feat: change agent list and edit page style
parent
25221f7b5e
commit
9536909f53
|
|
@ -134,7 +134,7 @@ export default function ChatPreview({ agent, agentId }: Props) {
|
||||||
const isImageUrl = (url: string | undefined) => url?.startsWith('http') || url?.startsWith('/');
|
const isImageUrl = (url: string | undefined) => url?.startsWith('http') || url?.startsWith('/');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full bg-gray-50 border-l">
|
<div className="flex flex-col h-full bg-gray-50">
|
||||||
<div className="p-4 border-b bg-white flex items-center gap-2">
|
<div className="p-4 border-b bg-white flex items-center gap-2">
|
||||||
<div
|
<div
|
||||||
className="rounded-full text-white flex items-center justify-center font-bold overflow-hidden"
|
className="rounded-full text-white flex items-center justify-center font-bold overflow-hidden"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
.agent-editor-shell-container {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-editor-workbench-container {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-editor-workbench-container.is-mobile {
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 8px;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-editor-workbench-container.is-desktop {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-editor-mobile-footer-bar {
|
||||||
|
padding: 1rem;
|
||||||
|
border-top: 1px solid var(--color-border);
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-editor-footer-status {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--color-text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-editor-footer-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
@ -8,9 +8,10 @@ interface HeaderProps {
|
||||||
autoSaveStatus: 'saved' | 'dirty' | 'saving' | 'error';
|
autoSaveStatus: 'saved' | 'dirty' | 'saving' | 'error';
|
||||||
saving: boolean;
|
saving: boolean;
|
||||||
onSave: () => Promise<void>;
|
onSave: () => Promise<void>;
|
||||||
|
isMobile?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Header({ isNew, currentName, navigate, autoSaveStatus, saving, onSave }: HeaderProps) {
|
export default function Header({ isNew, currentName, navigate, autoSaveStatus, saving, onSave, isMobile }: HeaderProps) {
|
||||||
return (
|
return (
|
||||||
<header className="monica-header agent-editor-header">
|
<header className="monica-header agent-editor-header">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
|
|
@ -27,34 +28,38 @@ export default function Header({ isNew, currentName, navigate, autoSaveStatus, s
|
||||||
{isNew ? '创建新智能体' : currentName || ''}
|
{isNew ? '创建新智能体' : currentName || ''}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="agent-editor-header-subtitle">
|
{!isMobile && (
|
||||||
围绕个性化、能力配置与实时预览,搭建一个更完整的 AI 工作台。
|
<div className="agent-editor-header-subtitle">
|
||||||
</div>
|
围绕个性化、能力配置与实时预览,搭建一个更完整的 AI 工作台。
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
{!isMobile && (
|
||||||
<span className="agent-editor-save-state text-xs text-gray-400">
|
<div className="flex items-center gap-4">
|
||||||
{autoSaveStatus === 'saving'
|
<span className="agent-editor-save-state text-xs text-gray-400">
|
||||||
? '正在保存...'
|
{autoSaveStatus === 'saving'
|
||||||
: autoSaveStatus === 'dirty'
|
? '正在保存...'
|
||||||
? '有未保存更改'
|
: autoSaveStatus === 'dirty'
|
||||||
: autoSaveStatus === 'error'
|
? '有未保存更改'
|
||||||
? '保存失败'
|
: autoSaveStatus === 'error'
|
||||||
: '已保存'}
|
? '保存失败'
|
||||||
</span>
|
: '已保存'}
|
||||||
<Button icon={<FileTextOutlined />} className="agent-editor-header-action">
|
</span>
|
||||||
文档
|
<Button icon={<FileTextOutlined />} className="agent-editor-header-action">
|
||||||
</Button>
|
文档
|
||||||
<Button
|
</Button>
|
||||||
type="primary"
|
<Button
|
||||||
icon={<SaveOutlined />}
|
type="primary"
|
||||||
loading={saving}
|
icon={<SaveOutlined />}
|
||||||
onClick={onSave}
|
loading={saving}
|
||||||
className="agent-editor-save-action"
|
onClick={onSave}
|
||||||
>
|
className="agent-editor-save-action"
|
||||||
保存
|
>
|
||||||
</Button>
|
保存
|
||||||
</div>
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ interface PreviewPaneProps {
|
||||||
|
|
||||||
export default function PreviewPane({ liveAgent, agentId }: PreviewPaneProps) {
|
export default function PreviewPane({ liveAgent, agentId }: PreviewPaneProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 h-full agent-editor-pane">
|
<div className="min-h-0 agent-editor-pane">
|
||||||
<div className="agent-editor-pane-body agent-editor-preview-shell">
|
<div className="agent-editor-pane-body agent-editor-preview-shell flex flex-col h-full">
|
||||||
<div className="agent-editor-pane-header">
|
<div className="agent-editor-pane-header shrink-0">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="agent-editor-pane-title">预览</h3>
|
<h3 className="agent-editor-pane-title">预览</h3>
|
||||||
<p className="agent-editor-pane-subtitle">
|
<p className="agent-editor-pane-subtitle">
|
||||||
|
|
@ -23,13 +23,13 @@ export default function PreviewPane({ liveAgent, agentId }: PreviewPaneProps) {
|
||||||
Live Preview
|
Live Preview
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="agent-editor-intro agent-editor-preview-intro">
|
<div className="agent-editor-intro agent-editor-preview-intro shrink-0">
|
||||||
<div className="agent-editor-intro-title">当前预览角色</div>
|
<div className="agent-editor-intro-title">当前预览角色</div>
|
||||||
<div className="agent-editor-intro-text">
|
<div className="agent-editor-intro-text">
|
||||||
{liveAgent?.name || '未命名智能体'} 会基于左侧 Prompt 与中栏设置立即更新。你可以直接在这里检查语气、头像和整体观感。
|
{liveAgent?.name || '未命名智能体'} 会基于左侧 Prompt 与中栏设置立即更新。你可以直接在这里检查语气、头像和整体观感。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="agent-editor-surface agent-editor-preview-surface">
|
<div className="agent-editor-surface agent-editor-preview-surface min-h-0 overflow-auto">
|
||||||
<ChatPreview agent={liveAgent} agentId={agentId} />
|
<ChatPreview agent={liveAgent} agentId={agentId} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { Form } from 'antd';
|
import { Form, Button } from 'antd';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { App as AntApp } from 'antd';
|
import { App as AntApp } from 'antd';
|
||||||
|
import { FileTextOutlined, SaveOutlined } from '@ant-design/icons';
|
||||||
import SkillEditor from '../../components/SkillEditor';
|
import SkillEditor from '../../components/SkillEditor';
|
||||||
import { useAgentEditor } from './hooks/useAgentEditor';
|
import { useAgentEditor } from './hooks/useAgentEditor';
|
||||||
|
import { useIsMobile } from '../../hooks/useIsMobile';
|
||||||
import Header from './components/Header';
|
import Header from './components/Header';
|
||||||
import PromptEditor from './components/PromptEditor';
|
import PromptEditor from './components/PromptEditor';
|
||||||
import CapabilitySettings from './components/CapabilitySettings';
|
import CapabilitySettings from './components/CapabilitySettings';
|
||||||
import PreviewPane from './components/PreviewPane';
|
import PreviewPane from './components/PreviewPane';
|
||||||
import InitModal from './components/InitModal';
|
import InitModal from './components/InitModal';
|
||||||
import AvatarSelector from './components/AvatarSelector';
|
import AvatarSelector from './components/AvatarSelector';
|
||||||
|
import './AgentEditor.css';
|
||||||
import './styles/agent-editor-capability.css';
|
import './styles/agent-editor-capability.css';
|
||||||
import './styles/agent-editor-panels.css';
|
import './styles/agent-editor-panels.css';
|
||||||
import './styles/agent-editor-modal.css';
|
import './styles/agent-editor-modal.css';
|
||||||
|
|
@ -54,9 +57,11 @@ export default function AgentEditor() {
|
||||||
markDirty,
|
markDirty,
|
||||||
} = useAgentEditor({ id, isNew, form, message, navigate });
|
} = useAgentEditor({ id, isNew, form, message, navigate });
|
||||||
|
|
||||||
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="fixed inset-0 flex flex-col bg-white z-100 agent-editor-shell">
|
<div className="agent-editor-shell-container">
|
||||||
<Header
|
<Header
|
||||||
isNew={isNew}
|
isNew={isNew}
|
||||||
currentName={currentName}
|
currentName={currentName}
|
||||||
|
|
@ -64,9 +69,10 @@ export default function AgentEditor() {
|
||||||
autoSaveStatus={autoSaveStatus}
|
autoSaveStatus={autoSaveStatus}
|
||||||
saving={saving}
|
saving={saving}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
|
isMobile={isMobile}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="flex-1 overflow-hidden agent-editor-workbench">
|
<div className={`agent-editor-workbench-container ${isMobile ? 'is-mobile' : 'is-desktop'}`}>
|
||||||
<PromptEditor form={form} markDirty={markDirty} />
|
<PromptEditor form={form} markDirty={markDirty} />
|
||||||
<CapabilitySettings
|
<CapabilitySettings
|
||||||
form={form}
|
form={form}
|
||||||
|
|
@ -84,6 +90,34 @@ export default function AgentEditor() {
|
||||||
<PreviewPane liveAgent={liveAgent} agentId={id} />
|
<PreviewPane liveAgent={liveAgent} agentId={id} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{isMobile && (
|
||||||
|
<div className="agent-editor-mobile-footer-bar">
|
||||||
|
<span className="agent-editor-footer-status">
|
||||||
|
{autoSaveStatus === 'saving'
|
||||||
|
? '正在保存...'
|
||||||
|
: autoSaveStatus === 'dirty'
|
||||||
|
? '有未保存更改'
|
||||||
|
: autoSaveStatus === 'error'
|
||||||
|
? '保存失败'
|
||||||
|
: '已保存'}
|
||||||
|
</span>
|
||||||
|
<div className="agent-editor-footer-actions">
|
||||||
|
<Button icon={<FileTextOutlined />} size="middle">
|
||||||
|
文档
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<SaveOutlined />}
|
||||||
|
loading={saving}
|
||||||
|
onClick={() => handleSave()}
|
||||||
|
size="middle"
|
||||||
|
>
|
||||||
|
保存
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{!isNew && (
|
{!isNew && (
|
||||||
<SkillEditor
|
<SkillEditor
|
||||||
open={skillEditorOpen}
|
open={skillEditorOpen}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,312 @@
|
||||||
|
.h5-agent-list-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-stats-banner {
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 20px 16px 18px;
|
||||||
|
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: 1px solid rgba(8, 145, 178, 0.12);
|
||||||
|
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.06);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-stats-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-stats-header-info {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-stats-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255,255,255,0.78);
|
||||||
|
border: 1px solid rgba(8, 145, 178, 0.10);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-stats-title {
|
||||||
|
margin-bottom: 8px !important;
|
||||||
|
font-size: 22px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-stats-subtitle {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
line-height: 1.6 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-stats-marketplace-btn {
|
||||||
|
border-radius: 10px !important;
|
||||||
|
height: 40px !important;
|
||||||
|
padding: 0 16px !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 统计卡片网格 - 横向排列 */
|
||||||
|
.h5-stats-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-stats-card {
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 10px;
|
||||||
|
background: rgba(255,255,255,0.72);
|
||||||
|
border: 1px solid rgba(255,255,255,0.7);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-stats-card-label {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
margin-bottom: 6px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 统计数值与标签容器 - 换行显示 */
|
||||||
|
.h5-stats-value-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-stats-value {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--color-text);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-stats-tag {
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-agent-card-item {
|
||||||
|
border-radius: 12px;
|
||||||
|
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;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
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;
|
||||||
|
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: 13px;
|
||||||
|
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.4;
|
||||||
|
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;
|
||||||
|
padding-top: 8px;
|
||||||
|
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 {
|
||||||
|
margin-top: 16px;
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
background: var(--color-surface);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-footer-promo-info {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-footer-promo-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text);
|
||||||
|
margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-footer-promo-subtitle {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.h5-footer-promo-btn {
|
||||||
|
color: var(--color-brand) !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
text-align: left !important;
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import { Link, useNavigate } from 'react-router-dom';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import type { Agent } from '../../../api';
|
import type { Agent } from '../../../api';
|
||||||
import type { AgentListLogicOutput } from '../AgentListLogic';
|
import type { AgentListLogicOutput } from '../AgentListLogic';
|
||||||
|
import './AgentListH5.css';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
logic: AgentListLogicOutput;
|
logic: AgentListLogicOutput;
|
||||||
|
|
@ -22,52 +23,19 @@ export default function AgentListH5({ logic }: Props) {
|
||||||
const { list, loading, stats, handleDelete, isImageUrl, getModelLabel } = logic;
|
const { list, loading, stats, handleDelete, isImageUrl, getModelLabel } = logic;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-container h5-page-container">
|
<div className="page-container h5-page-container h5-agent-list-container">
|
||||||
<div
|
<div className="h5-stats-banner">
|
||||||
style={{
|
<div className="h5-stats-header">
|
||||||
borderRadius: 16,
|
<div className="h5-stats-header-info">
|
||||||
padding: '20px 16px 18px',
|
<div className="h5-stats-badge">
|
||||||
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: '1px solid rgba(8, 145, 178, 0.12)',
|
|
||||||
boxShadow: '0 10px 24px rgba(15, 23, 42, 0.06)',
|
|
||||||
marginBottom: 16,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
gap: 12,
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
marginBottom: 16,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ width: '100%' }}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: 'inline-flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: 6,
|
|
||||||
padding: '4px 10px',
|
|
||||||
borderRadius: 999,
|
|
||||||
background: 'rgba(255,255,255,0.78)',
|
|
||||||
border: '1px solid rgba(8, 145, 178, 0.10)',
|
|
||||||
color: 'var(--color-text-secondary)',
|
|
||||||
fontSize: 11,
|
|
||||||
fontWeight: 600,
|
|
||||||
marginBottom: 12,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RobotOutlined style={{ color: 'var(--color-brand)', fontSize: 12 }} />
|
<RobotOutlined style={{ color: 'var(--color-brand)', fontSize: 12 }} />
|
||||||
我的 Agent 资产
|
我的 Agent 资产
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 className="page-title h5-page-title" style={{ marginBottom: 8, fontSize: 22 }}>
|
<h2 className="page-title h5-page-title h5-stats-title">
|
||||||
我的智能体
|
我的智能体
|
||||||
</h2>
|
</h2>
|
||||||
<div className="page-subtitle h5-page-subtitle" style={{ marginTop: 0, fontSize: 13, lineHeight: 1.6 }}>
|
<div className="page-subtitle h5-page-subtitle h5-stats-subtitle">
|
||||||
把你的 AI 助手沉淀成一组可管理、可协作、可持续进化的能力单元。
|
把你的 AI 助手沉淀成一组可管理、可协作、可持续进化的能力单元。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -76,34 +44,23 @@ export default function AgentListH5({ logic }: Props) {
|
||||||
size="middle"
|
size="middle"
|
||||||
icon={<CompassOutlined />}
|
icon={<CompassOutlined />}
|
||||||
onClick={() => navigate('/marketplace')}
|
onClick={() => navigate('/marketplace')}
|
||||||
style={{ borderRadius: 10, height: 40, padding: '0 16px', fontWeight: 600, width: '100%' }}
|
className="h5-stats-marketplace-btn"
|
||||||
>
|
>
|
||||||
前往智能体广场
|
前往智能体广场
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(1, minmax(0, 1fr))', gap: 10 }}>
|
<div className="h5-stats-grid">
|
||||||
{stats.map((item) => (
|
{stats.map((item) => (
|
||||||
<div
|
<div key={item.label} className="h5-stats-card">
|
||||||
key={item.label}
|
<div className="h5-stats-card-label">{item.label}</div>
|
||||||
style={{
|
<div className="h5-stats-value-wrap">
|
||||||
borderRadius: 12,
|
<span className="h5-stats-value">{item.value}</span>
|
||||||
padding: '12px 14px',
|
|
||||||
background: 'rgba(255,255,255,0.72)',
|
|
||||||
border: '1px solid rgba(255,255,255,0.7)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ fontSize: 11, color: 'var(--color-text-secondary)', marginBottom: 6 }}>{item.label}</div>
|
|
||||||
<div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
|
|
||||||
<span style={{ fontSize: 22, fontWeight: 700, color: 'var(--color-text)' }}>{item.value}</span>
|
|
||||||
<span
|
<span
|
||||||
|
className="h5-stats-tag"
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 999,
|
|
||||||
padding: '3px 6px',
|
|
||||||
background: item.tone,
|
background: item.tone,
|
||||||
color: item.color,
|
color: item.color,
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: 600,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
实时统计
|
实时统计
|
||||||
|
|
@ -123,22 +80,33 @@ export default function AgentListH5({ logic }: Props) {
|
||||||
</Empty>
|
</Empty>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Row gutter={[12, 12]}>
|
<Row gutter={[8, 8]}>
|
||||||
{list.map((a) => (
|
{list.map((a) => (
|
||||||
<Col xs={24} sm={24} md={12} key={a.id}>
|
<Col xs={12} sm={12} md={12} key={a.id}>
|
||||||
<div
|
<div className="agent-card h5-agent-card h5-agent-card-item">
|
||||||
className="agent-card h5-agent-card"
|
{/* 右上角可见性标签 */}
|
||||||
style={{
|
<div className="h5-agent-card-badge">
|
||||||
borderRadius: 14,
|
{a.visibility === 'public' && (
|
||||||
padding: 16,
|
<Tag bordered={false} className="h5-agent-tag-item tag-public">
|
||||||
background: 'linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(252,252,253,1) 100%)',
|
公开
|
||||||
boxShadow: '0 6px 16px rgba(15, 23, 42, 0.045)',
|
</Tag>
|
||||||
}}
|
)}
|
||||||
>
|
{a.visibility === 'team' && (
|
||||||
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
|
<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
|
<div
|
||||||
className="avatar"
|
className="avatar h5-agent-avatar"
|
||||||
style={{ background: a.avatar || 'var(--gradient-brand)', borderRadius: '50%', overflow: 'hidden', width: 46, height: 46 }}
|
style={{ background: a.avatar || 'var(--gradient-brand)' }}
|
||||||
>
|
>
|
||||||
{isImageUrl(a.avatar) ? (
|
{isImageUrl(a.avatar) ? (
|
||||||
<img src={a.avatar} className="w-full h-full object-cover" alt="avatar" />
|
<img src={a.avatar} className="w-full h-full object-cover" alt="avatar" />
|
||||||
|
|
@ -146,72 +114,26 @@ export default function AgentListH5({ logic }: Props) {
|
||||||
(a.name?.charAt(0) || '?').toUpperCase()
|
(a.name?.charAt(0) || '?').toUpperCase()
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ flex: 1, minWidth: 0 }}>
|
<div className="h5-agent-info">
|
||||||
<div style={{ fontWeight: 700, fontSize: 15, color: 'var(--color-text)', marginBottom: 3 }}>{a.name}</div>
|
<div className="h5-agent-name">{a.name}</div>
|
||||||
<div style={{ fontSize: 11, color: 'var(--color-text-tertiary)' }}>
|
|
||||||
最近更新于 {dayjs(a.updated_at).format('YYYY-MM-DD')}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
{a.description && (
|
||||||
style={{
|
<div className="h5-agent-desc">
|
||||||
marginTop: 12,
|
{a.description}
|
||||||
padding: '12px 12px 14px',
|
|
||||||
borderRadius: 12,
|
|
||||||
background: 'linear-gradient(180deg, rgba(248,250,252,0.9) 0%, rgba(255,255,255,0.95) 100%)',
|
|
||||||
border: '1px solid rgba(148, 163, 184, 0.14)',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="desc" style={{ minHeight: 50, fontSize: 12.5, lineHeight: 1.6 }}>
|
|
||||||
{a.description || '还没有填写描述,可以补充这个智能体适合解决什么问题。'}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
<Space size={4} wrap style={{ marginTop: 10 }}>
|
<div className="h5-agent-actions">
|
||||||
{a.visibility === 'public' && (
|
<Link to={`/chat/${a.id}`} className="h5-agent-action-link">
|
||||||
<Tag bordered={false} style={{ background: 'var(--color-success-soft)', color: 'var(--color-success)', borderRadius: 999, margin: 0, fontSize: 11 }}>
|
<Button type="text" size="small" block icon={<MessageOutlined />} className="h5-agent-action-btn" />
|
||||||
公开
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
{a.visibility === 'team' && (
|
|
||||||
<Tag bordered={false} style={{ background: 'var(--color-info-soft)', color: 'var(--color-info)', borderRadius: 999, margin: 0, fontSize: 11 }}>
|
|
||||||
团队
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
{a.visibility === 'private' && (
|
|
||||||
<Tag bordered={false} style={{ background: 'var(--color-surface-2)', color: 'var(--color-text-secondary)', borderRadius: 999, margin: 0, fontSize: 11 }}>
|
|
||||||
私有
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
{getModelLabel(a.model) && (
|
|
||||||
<Tag bordered={false} style={{ background: 'var(--color-brand-soft)', color: 'var(--color-brand)', borderRadius: 999, margin: 0, maxWidth: '100%', fontSize: 11 }}>
|
|
||||||
<span style={{ display: 'inline-block', maxWidth: 150, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
|
||||||
{getModelLabel(a.model)}
|
|
||||||
</span>
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
{(a.fork_count ?? 0) > 0 && (
|
|
||||||
<Tag bordered={false} style={{ background: 'var(--color-surface-2)', color: 'var(--color-text-secondary)', borderRadius: 999, margin: 0, fontSize: 11 }}>
|
|
||||||
Fork {a.fork_count}
|
|
||||||
</Tag>
|
|
||||||
)}
|
|
||||||
</Space>
|
|
||||||
|
|
||||||
<div style={{ display: 'flex', gap: 6, marginTop: 'auto', paddingTop: 12, borderTop: '1px solid var(--color-border)' }}>
|
|
||||||
<Link to={`/chat/${a.id}`} style={{ flex: 1 }}>
|
|
||||||
<Button type="primary" block icon={<MessageOutlined />} style={{ borderRadius: 8, height: 36, fontWeight: 600, fontSize: 13 }}>
|
|
||||||
聊天
|
|
||||||
</Button>
|
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={`/agents/${a.id}`} style={{ flex: 1 }}>
|
<Link to={`/agents/${a.id}`} className="h5-agent-action-link">
|
||||||
<Button block icon={<EditOutlined />} style={{ borderRadius: 8, height: 36, fontSize: 13 }}>
|
<Button type="text" size="small" block icon={<EditOutlined />} className="h5-agent-action-btn" />
|
||||||
管理
|
|
||||||
</Button>
|
|
||||||
</Link>
|
</Link>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定删除该智能体?"
|
title="确定删除?"
|
||||||
description="将删除其知识库与对话记录"
|
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
handleDelete(a.id);
|
handleDelete(a.id);
|
||||||
message.success('已删除');
|
message.success('已删除');
|
||||||
|
|
@ -219,7 +141,7 @@ export default function AgentListH5({ logic }: Props) {
|
||||||
okText="删除"
|
okText="删除"
|
||||||
cancelText="取消"
|
cancelText="取消"
|
||||||
>
|
>
|
||||||
<Button danger icon={<DeleteOutlined />} style={{ borderRadius: 8, width: 36, height: 36, padding: 0 }} />
|
<Button type="text" size="small" icon={<DeleteOutlined />} className="h5-agent-action-btn h5-agent-delete-btn" />
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -229,29 +151,16 @@ export default function AgentListH5({ logic }: Props) {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{list.length > 0 && (
|
{list.length > 0 && (
|
||||||
<div
|
<div className="h5-footer-promo">
|
||||||
style={{
|
<div className="h5-footer-promo-info">
|
||||||
marginTop: 16,
|
<div className="h5-footer-promo-title">
|
||||||
borderRadius: 14,
|
|
||||||
padding: '14px 16px',
|
|
||||||
background: 'var(--color-surface)',
|
|
||||||
border: '1px solid var(--color-border)',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
gap: 12,
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ width: '100%' }}>
|
|
||||||
<div style={{ fontSize: 14, fontWeight: 600, color: 'var(--color-text)', marginBottom: 3 }}>
|
|
||||||
想创建新的智能体入口?
|
想创建新的智能体入口?
|
||||||
</div>
|
</div>
|
||||||
<div style={{ fontSize: 12, color: 'var(--color-text-secondary)' }}>
|
<div className="h5-footer-promo-subtitle">
|
||||||
统一从智能体广场进入,保证创建流程和发现体验保持一致。
|
统一从智能体广场进入,保证创建流程和发现体验保持一致。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button type="text" icon={<ArrowRightOutlined />} onClick={() => navigate('/marketplace')} style={{ color: 'var(--color-brand)', fontWeight: 600, width: '100%', textAlign: 'left' }}>
|
<Button type="text" icon={<ArrowRightOutlined />} onClick={() => navigate('/marketplace')} className="h5-footer-promo-btn">
|
||||||
去广场继续发现
|
去广场继续发现
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,35 @@
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-page-h5 .stats-page-cards-grid,
|
.stats-page-h5 .stats-page-cards-grid {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-page-h5 .stats-page-card {
|
||||||
|
padding: 10px 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-page-h5 .stats-page-card-label {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-page-h5 .stats-page-card-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats-page-h5 .stats-page-card-value {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
.stats-page-h5 .points-integration-row,
|
.stats-page-h5 .points-integration-row,
|
||||||
.stats-page-h5 .stats-page-token-cards-grid,
|
.stats-page-h5 .stats-page-token-cards-grid,
|
||||||
.stats-page-h5 .stats-page-token-charts-grid {
|
.stats-page-h5 .stats-page-token-charts-grid {
|
||||||
|
|
|
||||||
|
|
@ -1493,6 +1493,10 @@ body {
|
||||||
padding: 40px 32px 60px;
|
padding: 40px 32px 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-container.h5-page-container {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.feature-cover-container {
|
.feature-cover-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue