feat: 技能市场Hub导入MCP/Skills添加名称说明输入;MCP传输代码验证后独立导入按钮
This commit is contained in:
1 parent
c0ec9e0e88
commit
62631a3b95
1 file changed
+57
-5
@@ -870,7 +870,7 @@ interface HubImportModalProps {
|
|||||||
appType: 'skill' | 'mcp';
|
appType: 'skill' | 'mcp';
|
||||||
platforms: typeof skillsHubPlatforms;
|
platforms: typeof skillsHubPlatforms;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onImport: (platformName: string, url: string, skillName: string, price?: number) => void;
|
onImport: (platformName: string, url: string, skillName: string, price?: number, description?: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const HubImportModal: React.FC<HubImportModalProps> = ({ appType, platforms, onClose, onImport }) => {
|
const HubImportModal: React.FC<HubImportModalProps> = ({ appType, platforms, onClose, onImport }) => {
|
||||||
@@ -884,6 +884,8 @@ const HubImportModal: React.FC<HubImportModalProps> = ({ appType, platforms, onC
|
|||||||
const [hubCode, setHubCode] = useState('');
|
const [hubCode, setHubCode] = useState('');
|
||||||
const [hubCodeVerifyResult, setHubCodeVerifyResult] = useState<'idle' | 'checking' | 'pass' | 'fail'>('idle');
|
const [hubCodeVerifyResult, setHubCodeVerifyResult] = useState<'idle' | 'checking' | 'pass' | 'fail'>('idle');
|
||||||
const [hubCodeVerifyMsg, setHubCodeVerifyMsg] = useState('');
|
const [hubCodeVerifyMsg, setHubCodeVerifyMsg] = useState('');
|
||||||
|
const [hubName, setHubName] = useState('');
|
||||||
|
const [hubDescription, setHubDescription] = useState('');
|
||||||
|
|
||||||
const handleVisit = (url: string) => {
|
const handleVisit = (url: string) => {
|
||||||
window.open(url, '_blank');
|
window.open(url, '_blank');
|
||||||
@@ -908,9 +910,10 @@ const HubImportModal: React.FC<HubImportModalProps> = ({ appType, platforms, onC
|
|||||||
|
|
||||||
const handleImport = (platformId: string) => {
|
const handleImport = (platformId: string) => {
|
||||||
const url = importUrl[platformId];
|
const url = importUrl[platformId];
|
||||||
// For Skills: require URL; for MCP: require code verification pass
|
// For Skills: require URL; for MCP: require code verification pass + name
|
||||||
if (appType === 'skill' && !url?.trim()) return;
|
if (appType === 'skill' && !url?.trim()) return;
|
||||||
if (appType === 'mcp' && hubCodeVerifyResult !== 'pass') return;
|
if (appType === 'mcp' && hubCodeVerifyResult !== 'pass') return;
|
||||||
|
if (appType === 'mcp' && !hubName.trim()) return;
|
||||||
setImportingId(platformId);
|
setImportingId(platformId);
|
||||||
setImportStatus('verifying');
|
setImportStatus('verifying');
|
||||||
setStatusMsg(appType === 'mcp' ? '正在验证代码...' : '正在验证URL链接...');
|
setStatusMsg(appType === 'mcp' ? '正在验证代码...' : '正在验证URL链接...');
|
||||||
@@ -930,13 +933,16 @@ const HubImportModal: React.FC<HubImportModalProps> = ({ appType, platforms, onC
|
|||||||
setStatusMsg(step.msg);
|
setStatusMsg(step.msg);
|
||||||
if (step.status === 'published') {
|
if (step.status === 'published') {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
onImport(platformId, appType === 'mcp' ? hubCode : url, extractSkillName(url, platformId), importPrice);
|
const name = appType === 'skill' ? (hubName || extractSkillName(url || '', platformId)) : hubName;
|
||||||
|
onImport(platformId, appType === 'mcp' ? hubCode : (url || ''), name, importPrice, hubDescription);
|
||||||
setImportingId(null);
|
setImportingId(null);
|
||||||
setImportStatus('idle');
|
setImportStatus('idle');
|
||||||
setImportUrl(prev => ({ ...prev, [platformId]: '' }));
|
setImportUrl(prev => ({ ...prev, [platformId]: '' }));
|
||||||
setHubCode('');
|
setHubCode('');
|
||||||
setHubCodeVerifyResult('idle');
|
setHubCodeVerifyResult('idle');
|
||||||
setHubCodeVerifyMsg('');
|
setHubCodeVerifyMsg('');
|
||||||
|
setHubName('');
|
||||||
|
setHubDescription('');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
}, delay);
|
}, delay);
|
||||||
@@ -974,6 +980,28 @@ const HubImportModal: React.FC<HubImportModalProps> = ({ appType, platforms, onC
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Name & Description */}
|
||||||
|
<div className="form-section">
|
||||||
|
<label className="form-label">名称 <span className="required">*</span></label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form-input"
|
||||||
|
placeholder={appType === 'skill' ? '请输入Skills工具名称' : '请输入MCP应用名称'}
|
||||||
|
value={hubName}
|
||||||
|
onChange={e => setHubName(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-section">
|
||||||
|
<label className="form-label">说明</label>
|
||||||
|
<textarea
|
||||||
|
className="form-textarea"
|
||||||
|
placeholder="请描述功能、使用场景等..."
|
||||||
|
value={hubDescription}
|
||||||
|
onChange={e => setHubDescription(e.target.value)}
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* MCP Hub Import: Code Editor + AI验证 */}
|
{/* MCP Hub Import: Code Editor + AI验证 */}
|
||||||
{appType === 'mcp' && (
|
{appType === 'mcp' && (
|
||||||
<div className="hub-code-section">
|
<div className="hub-code-section">
|
||||||
@@ -1013,6 +1041,29 @@ const mcpClient = new MCPClient({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* MCP 独立导入按钮:直接导入(不依赖选择平台卡片) */}
|
||||||
|
{appType === 'mcp' && hubCodeVerifyResult === 'pass' && (
|
||||||
|
<div className="hub-standalone-import" style={{ textAlign: 'center', marginBottom: 20, padding: '16px', background: 'var(--color-bg-secondary)', borderRadius: 8 }}>
|
||||||
|
<button
|
||||||
|
className="hub-import-btn"
|
||||||
|
style={{ padding: '10px 40px', fontSize: 15, fontWeight: 600 }}
|
||||||
|
onClick={() => handleImport('custom-import')}
|
||||||
|
disabled={!hubName.trim()}
|
||||||
|
>
|
||||||
|
{importingId === 'custom-import' ? (
|
||||||
|
<span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||||
|
<div className="audit-spinner tiny" />
|
||||||
|
导入中...
|
||||||
|
</span>
|
||||||
|
) : '导入'}
|
||||||
|
</button>
|
||||||
|
{!hubName.trim() && <div style={{ color: '#f85149', fontSize: 12, marginTop: 6 }}>请先输入名称</div>}
|
||||||
|
<div style={{ fontSize: 12, color: 'var(--color-text-tertiary)', marginTop: 6 }}>
|
||||||
|
直接从传输代码导入,无需选择平台
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="hub-platforms">
|
<div className="hub-platforms">
|
||||||
{platforms.map(p => (
|
{platforms.map(p => (
|
||||||
<div key={p.id} className="hub-platform-card">
|
<div key={p.id} className="hub-platform-card">
|
||||||
@@ -1317,12 +1368,13 @@ const MarketPage: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Handle hub import - 调用后端 API 创建应用
|
// Handle hub import - 调用后端 API 创建应用
|
||||||
const handleHubImport = async (platformName: string, url: string, skillName: string, price?: number) => {
|
const handleHubImport = async (platformName: string, url: string, skillName: string, price?: number, description?: string) => {
|
||||||
try {
|
try {
|
||||||
|
const desc = description || `从 ${platformName} 导入的${showHubImport === 'skill' ? 'Skills工具' : 'MCP应用'} (来源: ${url})`;
|
||||||
await marketAPI.createApp({
|
await marketAPI.createApp({
|
||||||
name: skillName,
|
name: skillName,
|
||||||
type: showHubImport || 'skill',
|
type: showHubImport || 'skill',
|
||||||
description: `从 ${platformName} 导入的${showHubImport === 'skill' ? 'Skills工具' : 'MCP应用'} (来源: ${url})`,
|
description: desc,
|
||||||
price: price || 0,
|
price: price || 0,
|
||||||
is_member_free: (price || 0) === 0,
|
is_member_free: (price || 0) === 0,
|
||||||
join_promotion: false,
|
join_promotion: false,
|
||||||
|
|||||||
Reference in new issue
Block a user