fix: fix agent list style

feat/unify-api-and-responsive-pages
yannyang 2026-07-09 22:50:05 +08:00
parent 6dc5f1f750
commit eea532371b
17 changed files with 423 additions and 403 deletions

View File

@ -0,0 +1,119 @@
.h5-agent-list-item {
display: flex;
align-items: flex-start;
padding: 12px;
background: #fff;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
gap: 12px;
position: relative;
transition: all 0.2s;
width: 100%;
box-sizing: border-box;
}
.h5-agent-list-item:active {
background: #f8fafc;
transform: scale(0.98);
}
.h5-agent-item-avatar {
width: 56px;
height: 56px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 24px;
font-weight: 600;
overflow: hidden;
flex-shrink: 0;
border: 2px solid #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.h5-agent-item-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.h5-agent-item-content {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
.h5-agent-item-title-row {
display: flex;
align-items: center;
gap: 6px;
}
.h5-agent-item-name {
font-size: 16px;
font-weight: 600;
color: #1a1a1a;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.h5-agent-item-tag {
font-size: 10px;
padding: 0 6px;
height: 18px;
line-height: 18px;
border-radius: 4px;
margin: 0;
}
.tag-public {
background: #e6f4ff;
color: #1677ff;
}
.tag-team {
background: #f6ffed;
color: #52c41a;
}
.tag-private {
background: #fff7e6;
color: #fa8c16;
}
.h5-agent-item-desc {
font-size: 13px;
color: #666;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.4;
margin: 2px 0;
}
.h5-agent-item-author {
font-size: 11px;
color: #999;
font-weight: 400;
}
.h5-agent-item-actions {
flex-shrink: 0;
align-self: center;
}
.h5-agent-item-more-btn {
color: #bfbfbf;
font-size: 20px;
}
.h5-agent-item-more-btn:hover {
color: #1677ff;
background: rgba(0, 0, 0, 0.04);
}

View File

@ -0,0 +1,146 @@
import React from 'react';
import {
EllipsisOutlined,
MessageOutlined,
EditOutlined,
DeleteOutlined,
} from '@ant-design/icons';
import { Button, Tag, Dropdown, Modal, App as AntApp } from 'antd';
import { useNavigate } from 'react-router-dom';
import type { Agent } from '../api';
import './AgentListItemH5.css';
export interface AgentListItemH5Props {
agent: Agent;
onDelete?: (id: string) => Promise<void>;
isImageUrl: (url?: string) => boolean;
authorName?: string;
extraActions?: {
key: string;
label: string;
icon: React.ReactNode;
onClick: (agent: Agent) => void;
danger?: boolean;
}[];
onClick?: (agent: Agent) => void;
}
const AgentListItemH5: React.FC<AgentListItemH5Props> = ({
agent,
onDelete,
isImageUrl,
authorName,
extraActions = [],
onClick,
}) => {
const { message } = AntApp.useApp();
const navigate = useNavigate();
const handleDefaultClick = () => {
if (onClick) {
onClick(agent);
} else {
navigate(`/chat/${agent.id}`);
}
};
const menuItems = [
{
key: 'chat',
label: '开始对话',
icon: <MessageOutlined />,
onClick: () => navigate(`/chat/${agent.id}`),
},
...extraActions.map(action => ({
key: action.key,
label: action.label,
icon: action.icon,
danger: action.danger,
onClick: () => action.onClick(agent),
})),
];
if (onDelete) {
menuItems.push({
key: 'edit',
label: '修改',
icon: <EditOutlined />,
onClick: () => navigate(`/agents/${agent.id}`),
});
menuItems.push({
key: 'delete',
label: '删除',
danger: true,
icon: <DeleteOutlined />,
onClick: () => {
Modal.confirm({
title: '确定删除?',
content: '删除后无法恢复',
okText: '删除',
okType: 'danger',
cancelText: '取消',
onOk: async () => {
await onDelete(agent.id);
message.success('已删除');
},
});
},
});
}
return (
<div className="h5-agent-list-item">
<div
className="h5-agent-item-avatar"
style={{ background: agent.avatar && !isImageUrl(agent.avatar) ? agent.avatar : 'var(--gradient-brand)' }}
onClick={handleDefaultClick}
>
{isImageUrl(agent.avatar) ? (
<img src={agent.avatar} alt="avatar" />
) : (
(agent.name?.charAt(0) || '?').toUpperCase()
)}
</div>
<div className="h5-agent-item-content" onClick={handleDefaultClick}>
<div className="h5-agent-item-title-row">
<span className="h5-agent-item-name">{agent.name}</span>
{agent.visibility === 'public' && !authorName && (
<Tag bordered={false} className="h5-agent-item-tag tag-public">
</Tag>
)}
{agent.visibility === 'team' && (
<Tag bordered={false} className="h5-agent-item-tag tag-team">
</Tag>
)}
{agent.visibility === 'private' && (
<Tag bordered={false} className="h5-agent-item-tag tag-private">
</Tag>
)}
</div>
<div className="h5-agent-item-desc">{agent.description || '暂无描述'}</div>
{authorName && <div className="h5-agent-item-author">by {authorName}</div>}
</div>
<div className="h5-agent-item-actions">
<Dropdown
menu={{ items: menuItems }}
placement="bottomRight"
trigger={['click']}
>
<Button
type="text"
shape="circle"
icon={<EllipsisOutlined />}
className="h5-agent-item-more-btn"
/>
</Dropdown>
</div>
</div>
);
};
export default AgentListItemH5;

View File

@ -3,6 +3,25 @@ import { KnowledgeStatus, SkillType } from '../../api';
export const DEFAULT_AVATAR = 'https://static.svipdata.com/hoyidata/materials/B7lNeTYQM1_0/materials.jpg'; export const DEFAULT_AVATAR = 'https://static.svipdata.com/hoyidata/materials/B7lNeTYQM1_0/materials.jpg';
export const PRESET_AVATARS: string[] = [ export const PRESET_AVATARS: string[] = [
'https://static.svipdata.com/images/mock-dev-user-id/a1ed5d166a9bcda1.png',
'https://static.svipdata.com/images/mock-dev-user-id/da4d3c387956cd91.png',
'https://static.svipdata.com/images/mock-dev-user-id/9f3eff849389bdd3.png',
'https://static.svipdata.com/images/mock-dev-user-id/0bdcc398dd0423ca.png',
'https://static.svipdata.com/images/mock-dev-user-id/18e35c04462fc56c.png',
'https://static.svipdata.com/images/mock-dev-user-id/7a4d55fc295e65ce.png',
'https://static.svipdata.com/images/mock-dev-user-id/5755088edf226f3c.png',
'https://static.svipdata.com/images/mock-dev-user-id/5ec020c29804a8cd.png',
'https://static.svipdata.com/images/mock-dev-user-id/31265f843ca7099b.png',
'https://static.svipdata.com/images/mock-dev-user-id/f4a2df94f05ed9f6.png',
'https://static.svipdata.com/images/mock-dev-user-id/dfec7aa91c1c5144.png',
'https://static.svipdata.com/images/mock-dev-user-id/fa8ca9d432b555bd.png',
'https://static.svipdata.com/images/mock-dev-user-id/796e8ba7f5781bb0.png',
'https://static.svipdata.com/images/mock-dev-user-id/12a397b565a1ce78.png',
'https://static.svipdata.com/images/mock-dev-user-id/15f78f9a3e0d120c.png',
'https://static.svipdata.com/images/mock-dev-user-id/3ec3ff2af8d61123.png',
'https://static.svipdata.com/images/mock-dev-user-id/ef90579ba571476e.png',
'https://static.svipdata.com/images/mock-dev-user-id/c555806777e127ec.png',
'https://static.svipdata.com/images/mock-dev-user-id/40778045ad8c2620.png',
'https://static.svipdata.com/hoyidata/materials/PkM0iQCaAY_0/materials.png', 'https://static.svipdata.com/hoyidata/materials/PkM0iQCaAY_0/materials.png',
'https://static.svipdata.com/hoyidata/materials/tmPDm2FhJY_1/materials.png', 'https://static.svipdata.com/hoyidata/materials/tmPDm2FhJY_1/materials.png',
'https://static.svipdata.com/hoyidata/materials/hNCyP4RvJL_2/materials.png', 'https://static.svipdata.com/hoyidata/materials/hNCyP4RvJL_2/materials.png',

View File

@ -255,6 +255,7 @@
width: 5rem; width: 5rem;
height: 5rem; height: 5rem;
margin: 0 auto; margin: 0 auto;
padding: 0;
overflow: hidden; overflow: hidden;
border: 2px solid transparent; border: 2px solid transparent;
border-radius: 999px; border-radius: 999px;

View File

@ -26,7 +26,7 @@ export function useAgentListLogic() {
load(); load();
}; };
const isImageUrl = (url: string) => url?.startsWith('http') || url?.startsWith('/'); const isImageUrl = (url?: string): boolean => !!(url?.startsWith('http') || url?.startsWith('/'));
const getModelLabel = (value: unknown): string => { const getModelLabel = (value: unknown): string => {
if (Array.isArray(value)) { if (Array.isArray(value)) {

View File

@ -105,173 +105,11 @@
white-space: nowrap; white-space: nowrap;
} }
.h5-agent-card-item { /* 智能体列表容器 */
border-radius: 12px; .h5-agent-list {
padding: 12px;
background: linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(252,252,253,1) 100%);
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.04);
height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 4px;
position: relative; /* 为右上角标签提供定位参考 */
overflow: hidden;
}
.h5-agent-card-badge {
position: absolute;
top: 0;
right: 0;
z-index: 1;
}
.h5-agent-card-badge .h5-agent-tag-item {
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
border-bottom-left-radius: 8px !important;
padding: 2px 8px !important;
height: auto !important;
font-size: 10px !important;
}
.h5-agent-card-header {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 8px; gap: 8px;
padding-top: 4px;
}
.h5-agent-avatar {
border-radius: 50%;
overflow: hidden;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: bold;
font-size: 16px;
flex-shrink: 0;
}
.h5-agent-info {
flex: 1;
min-width: 0;
width: 100%;
}
.h5-agent-name {
font-weight: 700;
font-size: 12px;
color: var(--color-text);
margin-bottom: 0;
/* 支持最多展示两行 */
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.3;
min-height: 1.4em; /* 调整为单行高度占位,因为下面加了描述 */
}
.h5-agent-desc {
font-size: 11px;
color: var(--color-text-tertiary);
margin-top: 4px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.4;
min-height: 2.8em;
text-align: center;
}
.h5-agent-tags {
display: flex;
flex-wrap: wrap;
gap: 4px;
justify-content: center;
min-height: 40px;
}
.h5-agent-tag-item {
border-radius: 999px !important;
margin: 0 !important;
font-size: 10px !important;
border: none !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
padding: 0 6px !important;
height: 18px !important;
line-height: 1 !important;
}
.tag-public {
background: var(--color-success-soft) !important;
color: var(--color-success) !important;
}
.tag-team {
background: var(--color-info-soft) !important;
color: var(--color-info) !important;
}
.tag-private {
background: var(--color-surface-2) !important;
color: var(--color-text-secondary) !important;
}
.tag-model {
background: var(--color-brand-soft) !important;
color: var(--color-brand) !important;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.h5-agent-actions {
display: flex;
gap: 4px;
margin-top: auto;
border-top: 1px solid var(--color-border);
justify-content: space-around;
}
.h5-agent-action-link {
flex: 0 0 auto;
}
.h5-agent-action-btn {
border-radius: 6px !important;
height: 28px !important;
width: 28px !important;
padding: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
color: var(--color-text-secondary) !important;
background: transparent !important;
}
.h5-agent-action-btn:hover {
background: var(--color-surface-2) !important;
}
.h5-agent-action-btn .anticon {
font-size: 16px;
}
.h5-agent-delete-btn {
flex-shrink: 0;
} }
.h5-footer-promo { .h5-footer-promo {

View File

@ -1,15 +1,11 @@
import { import {
ArrowRightOutlined,
CompassOutlined, CompassOutlined,
DeleteOutlined,
EditOutlined,
MessageOutlined,
RobotOutlined, RobotOutlined,
ArrowRightOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { Button, Col, Row, Empty, Popconfirm, App as AntApp, Tag, Space } from 'antd'; import { Button, Empty } from 'antd';
import { Link, useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import dayjs from 'dayjs'; import AgentListItemH5 from '../../../components/AgentListItemH5';
import type { Agent } from '../../../api';
import type { AgentListLogicOutput } from '../AgentListLogic'; import type { AgentListLogicOutput } from '../AgentListLogic';
import './AgentListH5.css'; import './AgentListH5.css';
@ -18,9 +14,8 @@ interface Props {
} }
export default function AgentListH5({ logic }: Props) { export default function AgentListH5({ logic }: Props) {
const { message } = AntApp.useApp();
const navigate = useNavigate(); const navigate = useNavigate();
const { list, loading, stats, handleDelete, isImageUrl, getModelLabel } = logic; const { list, loading, stats, handleDelete, isImageUrl } = logic;
return ( return (
<div className="page-container h5-page-container h5-agent-list-container"> <div className="page-container h5-page-container h5-agent-list-container">
@ -80,74 +75,16 @@ export default function AgentListH5({ logic }: Props) {
</Empty> </Empty>
</div> </div>
) : ( ) : (
<Row gutter={[8, 8]}> <div className="h5-agent-list">
{list.map((a) => ( {list.map((a) => (
<Col xs={12} sm={12} md={12} key={a.id}> <AgentListItemH5
<div className="agent-card h5-agent-card h5-agent-card-item"> key={a.id}
{/* 右上角可见性标签 */} agent={a}
<div className="h5-agent-card-badge"> onDelete={handleDelete}
{a.visibility === 'public' && ( isImageUrl={isImageUrl}
<Tag bordered={false} className="h5-agent-tag-item tag-public"> />
</Tag>
)}
{a.visibility === 'team' && (
<Tag bordered={false} className="h5-agent-tag-item tag-team">
</Tag>
)}
{a.visibility === 'private' && (
<Tag bordered={false} className="h5-agent-tag-item tag-private">
</Tag>
)}
</div>
<div className="h5-agent-card-header">
<div
className="avatar h5-agent-avatar"
style={{ background: a.avatar || 'var(--gradient-brand)' }}
>
{isImageUrl(a.avatar) ? (
<img src={a.avatar} className="w-full h-full object-cover" alt="avatar" />
) : (
(a.name?.charAt(0) || '?').toUpperCase()
)}
</div>
<div className="h5-agent-info">
<div className="h5-agent-name">{a.name}</div>
</div>
</div>
{a.description && (
<div className="h5-agent-desc">
{a.description}
</div>
)}
<div className="h5-agent-actions">
<Link to={`/chat/${a.id}`} className="h5-agent-action-link">
<Button type="text" size="small" block icon={<MessageOutlined />} className="h5-agent-action-btn" />
</Link>
<Link to={`/agents/${a.id}`} className="h5-agent-action-link">
<Button type="text" size="small" block icon={<EditOutlined />} className="h5-agent-action-btn" />
</Link>
<Popconfirm
title="确定删除?"
onConfirm={() => {
handleDelete(a.id);
message.success('已删除');
}}
okText="删除"
cancelText="取消"
>
<Button type="text" size="small" icon={<DeleteOutlined />} className="h5-agent-action-btn h5-agent-delete-btn" />
</Popconfirm>
</div>
</div>
</Col>
))} ))}
</Row> </div>
)} )}
{list.length > 0 && ( {list.length > 0 && (

View File

@ -79,7 +79,6 @@
.agent-card { .agent-card {
border-radius: 20px; border-radius: 20px;
padding: 20px; padding: 20px;
min-height: 292px;
background: linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(252,252,253,1) 100%); background: linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(252,252,253,1) 100%);
box-shadow: 0 12px 28px rgba(15, 23, 42, 0.045); box-shadow: 0 12px 28px rgba(15, 23, 42, 0.045);
display: flex; display: flex;

View File

@ -37,7 +37,14 @@ export default function LoginFormCard({ logic }: { logic: LoginPageLogic }) {
onFinish={onLogin} onFinish={onLogin}
className="login-form" className="login-form"
> >
<Form.Item name="phone" label="手机号" rules={[{ required: true, message: '请填写手机号' }]}> <Form.Item
name="phone"
label="手机号"
rules={[
{ required: true, message: '请填写手机号' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式' }
]}
>
<Input placeholder="13800138000" size="large" autoFocus /> <Input placeholder="13800138000" size="large" autoFocus />
</Form.Item> </Form.Item>
<Form.Item name="password" label="密码" rules={[{ required: true }]}> <Form.Item name="password" label="密码" rules={[{ required: true }]}>
@ -59,7 +66,14 @@ export default function LoginFormCard({ logic }: { logic: LoginPageLogic }) {
onFinish={handleRegister} onFinish={handleRegister}
className="login-form" className="login-form"
> >
<Form.Item name="phone" label="手机号" rules={[{ required: true, message: '请填写手机号' }]}> <Form.Item
name="phone"
label="手机号"
rules={[
{ required: true, message: '请填写手机号' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式' }
]}
>
<Input placeholder="13800138000" size="large" /> <Input placeholder="13800138000" size="large" />
</Form.Item> </Form.Item>
<Form.Item name="name" label="昵称" rules={[{ required: true }]}> <Form.Item name="name" label="昵称" rules={[{ required: true }]}>

View File

@ -42,7 +42,7 @@ export function useMarketplacePageLogic() {
} }
}; };
const isImageUrl = (url: string) => url?.startsWith('http') || url?.startsWith('/'); const isImageUrl = (url?: string): boolean => !!(url?.startsWith('http') || url?.startsWith('/'));
return { return {
list, list,

View File

@ -0,0 +1,37 @@
.h5-marketplace-page {
min-height: 100vh;
background: var(--color-bg);
}
.h5-page-hero {
background: linear-gradient(135deg, rgba(255,255,255,0.98) 0%, rgba(236,253,245,0.92) 48%, rgba(239,246,255,0.96) 100%);
border-bottom: 1px solid var(--color-border);
}
.h5-hero-title {
color: var(--color-text);
font-weight: 800;
letter-spacing: -0.02em;
}
.h5-hero-subtitle {
color: var(--color-text-secondary);
}
.h5-page-container {
padding-bottom: 40px;
}
.h5-marketplace-list {
margin-top: 8px;
}
.h5-create-card {
transition: all 0.2s ease;
cursor: pointer;
}
.h5-create-card:active {
transform: scale(0.98);
background: rgba(8, 145, 178, 0.05);
}

View File

@ -1,8 +1,9 @@
import { PlusOutlined, SearchOutlined, CompassOutlined, FireOutlined } from '@ant-design/icons'; import { PlusOutlined, SearchOutlined, CompassOutlined } from '@ant-design/icons';
import { Col, Row, Empty, Button, Tag, Space, Input, Spin } from 'antd'; import { Empty, Button, Input, Spin } from 'antd';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import type { MarketplaceAgent } from '../../../api'; import AgentListItemH5 from '../../../components/AgentListItemH5';
import type { MarketplacePageLogicOutput } from '../MarketplacePageLogic'; import type { MarketplacePageLogicOutput } from '../MarketplacePageLogic';
import './MarketplacePageH5.css';
interface Props { interface Props {
logic: MarketplacePageLogicOutput; logic: MarketplacePageLogicOutput;
@ -83,155 +84,24 @@ export default function MarketplacePageH5({ logic }: Props) {
<Spin size="large" /> <Spin size="large" />
</div> </div>
) : ( ) : (
<Row gutter={[12, 12]}> <div className="h5-marketplace-list" style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
{/* Create New Card - always show on H5 */}
<Col xs={24} sm={24} key="create-new">
<div onClick={() => navigate('/agents/new')} className="create-card h5-create-card" style={{ padding: 16 }}>
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between' }}>
<div className="create-icon" style={{ width: 44, height: 44 }}>
<PlusOutlined style={{ fontSize: 20, color: '#0891b2' }} />
</div>
</div>
<div style={{ marginTop: 12 }}>
<div style={{ fontWeight: 700, fontSize: 15, color: 'var(--color-text)', marginBottom: 4 }}>
</div>
<div style={{ fontSize: 12, color: 'var(--color-text-tertiary)', marginBottom: 8 }}>
</div>
<div className="desc" style={{ minHeight: 36, fontSize: 12 }}>
AI
</div>
</div>
<div style={{ marginTop: 'auto', paddingTop: 12 }}>
<Button
type="default"
block
style={{
height: 36,
borderRadius: 8,
fontWeight: 600,
borderStyle: 'dashed',
}}
>
</Button>
</div>
</div>
</Col>
{filtered.map((a) => ( {filtered.map((a) => (
<Col xs={24} sm={24} key={a.id}> <AgentListItemH5
<div className="agent-card h5-agent-card" style={{ padding: 14, borderRadius: 12 }}> key={a.id}
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 8 }}> agent={a}
<div isImageUrl={isImageUrl}
style={{ authorName={a.ownerName}
width: 44, extraActions={[
height: 44, {
borderRadius: '50%', key: 'fork',
background: a.avatar || 'var(--gradient-brand)', label: '复制到我的',
color: '#fff', icon: <PlusOutlined />,
display: 'flex', onClick: (agent) => handleFork(agent as any),
alignItems: 'center', }
justifyContent: 'center', ]}
fontWeight: 700, />
fontSize: 18,
boxShadow: 'var(--shadow-sm)',
overflow: 'hidden',
}}
>
{isImageUrl(a.avatar) ? (
<img src={a.avatar} className="w-full h-full object-cover" alt="avatar" />
) : (
(a.name?.charAt(0) || '?').toUpperCase()
)}
</div>
{a.fork_count > 10 && (
<Tag
bordered={false}
icon={<FireOutlined />}
style={{
borderRadius: 999,
margin: 0,
background: 'var(--color-warning-soft)',
color: 'var(--color-warning)',
fontSize: 11,
}}
>
</Tag>
)}
</div>
<div style={{ marginTop: 12 }}>
<div
style={{
fontWeight: 700,
fontSize: 15,
color: 'var(--color-text)',
marginBottom: 4,
letterSpacing: '-0.01em',
}}
>
{a.name}
</div>
<div style={{ fontSize: 12, color: 'var(--color-text-tertiary)', marginBottom: 8 }}>
by {a.ownerName || '匿名作者'}
</div>
<div className="desc" style={{ minHeight: 36, fontSize: 12 }}>{a.description || '暂无详细描述'}</div>
</div>
<div style={{ marginTop: 'auto', paddingTop: 12 }}>
<Space size={4} wrap style={{ marginBottom: 12 }}>
{a.kbCount > 0 && (
<Tag
bordered={false}
style={{
background: 'var(--color-info-soft)',
color: 'var(--color-info)',
borderRadius: 999,
fontSize: 11,
margin: 0,
}}
>
📚 {a.kbCount}
</Tag>
)}
{a.skillCount > 0 && (
<Tag
bordered={false}
style={{
background: 'var(--color-success-soft)',
color: 'var(--color-success)',
borderRadius: 999,
fontSize: 11,
margin: 0,
}}
>
🛠 {a.skillCount}
</Tag>
)}
</Space>
<Button
type="default"
block
onClick={() => handleFork(a)}
style={{
height: 36,
borderRadius: 8,
fontWeight: 600,
}}
>
📥
</Button>
</div>
</div>
</Col>
))} ))}
</Row> </div>
)} )}
{filtered.length === 0 && !loading && ( {filtered.length === 0 && !loading && (

View File

@ -1,7 +1,6 @@
import { PlusOutlined, SearchOutlined, CompassOutlined, FireOutlined } from '@ant-design/icons'; import { PlusOutlined, SearchOutlined, CompassOutlined, FireOutlined } from '@ant-design/icons';
import { Col, Row, Empty, Button, Tag, Space, Input, Spin } from 'antd'; import { Col, Row, Empty, Button, Tag, Space, Input, Spin } from 'antd';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import type { MarketplaceAgent } from '../../../api';
import type { MarketplacePageLogicOutput } from '../MarketplacePageLogic'; import type { MarketplacePageLogicOutput } from '../MarketplacePageLogic';
interface Props { interface Props {

View File

@ -114,9 +114,10 @@ function TokenCharts({ logic }: { logic: StatsPageLogic }) {
pagination={false} pagination={false}
rowKey={(row) => `${row.providerKind}:${row.model}`} rowKey={(row) => `${row.providerKind}:${row.model}`}
dataSource={tokenData.byModel || []} dataSource={tokenData.byModel || []}
scroll={{ x: 'max-content' }}
columns={[ columns={[
{ title: 'Provider', dataIndex: 'providerKind', width: 110 }, { title: 'Provider', dataIndex: 'providerKind', width: 110 },
{ title: 'Model', dataIndex: 'model' }, { title: 'Model', dataIndex: 'model', minWidth: 150 },
{ title: 'Calls', dataIndex: 'calls', width: 80 }, { title: 'Calls', dataIndex: 'calls', width: 80 },
{ title: 'Tokens', dataIndex: 'totalTokens', width: 110, render: (value) => Number(value || 0).toLocaleString() }, { title: 'Tokens', dataIndex: 'totalTokens', width: 110, render: (value) => Number(value || 0).toLocaleString() },
{ title: 'Cost', dataIndex: 'costUSD', width: 110, render: (value) => logic.formatUSD(value) } { title: 'Cost', dataIndex: 'costUSD', width: 110, render: (value) => logic.formatUSD(value) }

View File

@ -75,6 +75,12 @@
.stats-page-h5 .points-integration-row, .stats-page-h5 .points-integration-row,
.stats-page-h5 .stats-page-token-charts-grid { .stats-page-h5 .stats-page-token-charts-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
min-width: 0;
}
.stats-page-h5 .stats-page-token-charts-grid > * {
min-width: 0;
overflow: hidden;
} }
.stats-page-h5 .stats-page-token-cards-grid { .stats-page-h5 .stats-page-token-cards-grid {
@ -102,7 +108,12 @@
.stats-page-h5 .stats-page-chart-container, .stats-page-h5 .stats-page-chart-container,
.stats-page-h5 .stats-page-token-chart-container { .stats-page-h5 .stats-page-token-chart-container {
overflow-x: auto; overflow-x: auto;
padding-bottom: 0.25rem; padding-bottom: 0.75rem;
display: flex !important;
align-items: flex-end;
gap: 12px;
width: 100%;
-webkit-overflow-scrolling: touch;
} }
.stats-page-h5 .stats-page-chart-container { .stats-page-h5 .stats-page-chart-container {
@ -112,7 +123,12 @@
} }
.stats-page-h5 .stats-page-chart-bar-group { .stats-page-h5 .stats-page-chart-bar-group {
min-width: 1.625rem; min-width: 2rem;
flex: 0 0 auto;
}
.stats-page-h5 .stats-page-token-chart-bar {
width: 14px;
} }
.stats-page-h5 .stats-page-chart-card .ant-card-head { .stats-page-h5 .stats-page-chart-card .ant-card-head {
@ -143,3 +159,15 @@
.stats-page-h5 .ant-card-body { .stats-page-h5 .ant-card-body {
padding: 16px; padding: 16px;
} }
.stats-page-h5 .ant-table-wrapper {
margin-top: 12px;
max-width: 100%;
overflow: hidden;
}
.stats-page-h5 .ant-table-cell {
font-size: 12px;
padding: 8px 4px !important;
}

View File

@ -353,8 +353,14 @@ export default function TeamsPageH5({ logic }: Props) {
</div> </div>
) : ( ) : (
<Form layout="vertical" onFinish={logic.handleInvite}> <Form layout="vertical" onFinish={logic.handleInvite}>
<Form.Item name="email" label="限定邮箱(可选)"> <Form.Item
<Input placeholder="只允许该邮箱使用此邀请码" /> name="phone"
label="限定手机号(可选)"
rules={[
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式' }
]}
>
<Input placeholder="只允许该手机号使用此邀请码" />
</Form.Item> </Form.Item>
<Form.Item name="ttlHours" label="有效期(小时)" initialValue={168}> <Form.Item name="ttlHours" label="有效期(小时)" initialValue={168}>
<Input type="number" placeholder="168 = 7 天" /> <Input type="number" placeholder="168 = 7 天" />

View File

@ -355,8 +355,14 @@ export default function TeamsPageWeb({ logic }: Props) {
</div> </div>
) : ( ) : (
<Form layout="vertical" onFinish={logic.handleInvite}> <Form layout="vertical" onFinish={logic.handleInvite}>
<Form.Item name="email" label="限定邮箱(可选)"> <Form.Item
<Input placeholder="只允许该邮箱使用此邀请码" /> name="phone"
label="限定手机号(可选)"
rules={[
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式' }
]}
>
<Input placeholder="只允许该手机号使用此邀请码" />
</Form.Item> </Form.Item>
<Form.Item name="ttlHours" label="有效期(小时)" initialValue={168}> <Form.Item name="ttlHours" label="有效期(小时)" initialValue={168}>
<Input type="number" placeholder="168 = 7 天" /> <Input type="number" placeholder="168 = 7 天" />