whistle.mock-plugins 1.3.1 → 1.4.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION.md +29 -0
- package/app/cgi-bin/interfaces.js +2 -0
- package/app/cgi-bin/settings.js +1 -0
- package/app/cgi-bin/stats.js +22 -12
- package/app/cgi-bin/webdav.js +56 -0
- package/app/public/index.html +1 -1
- package/app/public/js/11.index.js +2 -0
- package/app/public/js/11.index.js.LICENSE.txt +5 -0
- package/app/public/js/704.index.js +2 -0
- package/app/public/js/704.index.js.LICENSE.txt +1 -0
- package/app/public/js/798.index.js +2 -0
- package/app/public/js/798.index.js.LICENSE.txt +53 -0
- package/app/public/js/807.index.js +2 -0
- package/app/public/js/807.index.js.LICENSE.txt +1 -0
- package/app/public/js/968.index.js +2 -0
- package/app/public/js/968.index.js.LICENSE.txt +12 -0
- package/app/public/js/index.js +1 -1
- package/app/public/js/index.js.LICENSE.txt +0 -173
- package/lib/routes/settings.js +1 -0
- package/lib/webdav-sync.js +806 -0
- package/package.json +9 -2
- package/API.md +0 -247
- package/COLUMN_RESIZE_IMPLEMENTATION.md +0 -162
- package/FEATURE_ENHANCEMENT.md +0 -157
- package/TEST_GUIDE.md +0 -156
- package/data/features.json +0 -1
- package/data/interfaces.json +0 -1
- package/index.html +0 -52
- package/initialize.js +0 -32
- package/public/bundle.js +0 -2
- package/public/bundle.js.LICENSE.txt +0 -38
- package/public/index.html +0 -1
- package/public/plugin.mock-plugin.html +0 -14
- package/rules.txt +0 -22
- package/ui/src/App.css +0 -173
- package/ui/src/components/AppLayout.js +0 -407
- package/ui/src/components/FeatureCard.js +0 -58
- package/ui/src/components/KeyboardShortcutsHelp.js +0 -58
- package/ui/src/components/Layout.js +0 -94
- package/ui/src/components/MainNav.js +0 -42
- package/ui/src/components/PageContainer.js +0 -16
- package/ui/src/components/VersionModal.js +0 -99
- package/ui/src/components/interface-management/DataManagementDrawer.js +0 -115
- package/ui/src/components/interface-management/DataManagementModal.js +0 -110
- package/ui/src/components/interface-management/InterfaceTestModal.js +0 -313
- package/ui/src/components/interface-management/PreviewModal.js +0 -48
- package/ui/src/components/interface-management/README.md +0 -184
- package/ui/src/components/interface-management/RealResponseRecorder.js +0 -165
- package/ui/src/components/interface-management/ResponseContentEditor.js +0 -645
- package/ui/src/components/interface-management/constants.js +0 -36
- package/ui/src/components/interface-management/index.js +0 -7
- package/ui/src/components/interface-management/utils.js +0 -103
- package/ui/src/hooks/useKeyboardShortcuts.js +0 -99
- package/ui/src/index.html +0 -14
- package/ui/src/index.js +0 -50
- package/ui/src/pages/InterfaceManagementV2.js +0 -2351
- package/ui/src/pages/MockDataV2.js +0 -1089
- package/ui/src/pages/Settings.js +0 -299
- package/ui/src/pages/Sniffer.js +0 -521
- package/ui/src/pages/Stats.js +0 -288
- package/ui/src/services/del_api.js +0 -192
- package/ui/src/styles/app-layout.css +0 -373
- package/ui/src/styles/cyber-theme.css +0 -1234
- package/ui/src/styles/earth-theme.css +0 -618
- package/ui/src/styles/global.css +0 -224
- package/ui/src/styles/interface-management.css +0 -1321
- package/ui/src/styles/mock-data.css +0 -1185
- package/ui/src/styles/settings.css +0 -252
- package/ui/src/styles/version-modal.css +0 -116
- package/webpack.config.js +0 -54
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import '../styles/feature-card.css';
|
|
3
|
-
|
|
4
|
-
const FeatureCard = ({ feature }) => {
|
|
5
|
-
const { name, description, active, interfaceCount, createdAt } = feature;
|
|
6
|
-
|
|
7
|
-
// 生成随机的主题色(根据当前主题选择色系)
|
|
8
|
-
const getRandomColor = () => {
|
|
9
|
-
const isEarth = document.body.classList.contains('earth-theme');
|
|
10
|
-
if (isEarth) {
|
|
11
|
-
const colors = ['#b87333', '#6b8e23', '#d4a017', '#8d6e63', '#c0392b'];
|
|
12
|
-
return colors[Math.floor(Math.random() * colors.length)];
|
|
13
|
-
}
|
|
14
|
-
const colors = ['#00f0ff', '#00ff41', '#f0e800', '#ff00a0', '#bd00ff'];
|
|
15
|
-
return colors[Math.floor(Math.random() * colors.length)];
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const themeColor = getRandomColor();
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<div className="feature-card">
|
|
22
|
-
<div className="feature-card-color-bar" style={{ backgroundColor: themeColor }}></div>
|
|
23
|
-
<div className="feature-card-header">
|
|
24
|
-
<h3 className="feature-card-title">{name}</h3>
|
|
25
|
-
<span className={`feature-card-badge ${active ? 'active' : 'inactive'}`}>
|
|
26
|
-
{active ? '已启用' : '未启用'}
|
|
27
|
-
</span>
|
|
28
|
-
</div>
|
|
29
|
-
<div className="feature-card-content">
|
|
30
|
-
<p className="feature-card-description">{description || '没有描述'}</p>
|
|
31
|
-
<div className="feature-card-stats">
|
|
32
|
-
<div className="feature-stat">
|
|
33
|
-
<span className="feature-stat-value">{interfaceCount}</span>
|
|
34
|
-
<span className="feature-stat-label">接口数量</span>
|
|
35
|
-
</div>
|
|
36
|
-
{createdAt && (
|
|
37
|
-
<div className="feature-stat">
|
|
38
|
-
<span className="feature-stat-label">创建于</span>
|
|
39
|
-
<span className="feature-stat-value feature-date">{createdAt}</span>
|
|
40
|
-
</div>
|
|
41
|
-
)}
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
<div className="feature-card-footer">
|
|
45
|
-
<button className="card-btn edit-btn">
|
|
46
|
-
<i className="icon-edit"></i>
|
|
47
|
-
编辑
|
|
48
|
-
</button>
|
|
49
|
-
<button className="card-btn primary-btn" style={{ borderColor: themeColor, color: themeColor }}>
|
|
50
|
-
<i className="icon-manage"></i>
|
|
51
|
-
管理接口
|
|
52
|
-
</button>
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
);
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export default FeatureCard;
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Modal, Table } from 'antd';
|
|
3
|
-
import { getShortcutsList } from '../hooks/useKeyboardShortcuts';
|
|
4
|
-
|
|
5
|
-
const KeyboardShortcutsHelp = ({ visible, onClose }) => {
|
|
6
|
-
const shortcuts = getShortcutsList();
|
|
7
|
-
|
|
8
|
-
const columns = [
|
|
9
|
-
{
|
|
10
|
-
title: '快捷键',
|
|
11
|
-
dataIndex: 'key',
|
|
12
|
-
key: 'key',
|
|
13
|
-
width: 180,
|
|
14
|
-
render: (text) => (
|
|
15
|
-
<kbd style={{
|
|
16
|
-
display: 'inline-block',
|
|
17
|
-
padding: '4px 8px',
|
|
18
|
-
fontSize: '12px',
|
|
19
|
-
fontFamily: 'monospace',
|
|
20
|
-
lineHeight: 1,
|
|
21
|
-
color: '#333',
|
|
22
|
-
background: '#f5f5f5',
|
|
23
|
-
border: '1px solid #d9d9d9',
|
|
24
|
-
borderRadius: '4px',
|
|
25
|
-
boxShadow: '0 1px 0 rgba(0,0,0,0.1)',
|
|
26
|
-
}}>
|
|
27
|
-
{text}
|
|
28
|
-
</kbd>
|
|
29
|
-
),
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
title: '功能',
|
|
33
|
-
dataIndex: 'desc',
|
|
34
|
-
key: 'desc',
|
|
35
|
-
},
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<Modal
|
|
40
|
-
title="⌨️ 键盘快捷键"
|
|
41
|
-
open={visible}
|
|
42
|
-
onCancel={onClose}
|
|
43
|
-
footer={null}
|
|
44
|
-
width={480}
|
|
45
|
-
>
|
|
46
|
-
<Table
|
|
47
|
-
dataSource={shortcuts.map((s, i) => ({ ...s, id: i }))}
|
|
48
|
-
columns={columns}
|
|
49
|
-
rowKey="id"
|
|
50
|
-
pagination={false}
|
|
51
|
-
size="small"
|
|
52
|
-
showHeader={false}
|
|
53
|
-
/>
|
|
54
|
-
</Modal>
|
|
55
|
-
);
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export default KeyboardShortcutsHelp;
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import '../styles/layout.css';
|
|
3
|
-
|
|
4
|
-
const Layout = ({ children }) => {
|
|
5
|
-
const [activeFeature, setActiveFeature] = useState(null);
|
|
6
|
-
const [features, setFeatures] = useState([
|
|
7
|
-
{ id: 1, name: '用户数据', active: true, count: 3 },
|
|
8
|
-
{ id: 2, name: '订单信息', active: false, count: 2 },
|
|
9
|
-
{ id: 3, name: '商品列表', active: false, count: 5 }
|
|
10
|
-
]);
|
|
11
|
-
|
|
12
|
-
const handleFeatureClick = (id) => {
|
|
13
|
-
setActiveFeature(id);
|
|
14
|
-
// 更新功能状态
|
|
15
|
-
setFeatures(features.map(feature => ({
|
|
16
|
-
...feature,
|
|
17
|
-
active: feature.id === id ? !feature.active : feature.active
|
|
18
|
-
})));
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const handleCreateFeature = () => {
|
|
22
|
-
// 模拟创建新功能
|
|
23
|
-
const newId = features.length > 0 ? Math.max(...features.map(f => f.id)) + 1 : 1;
|
|
24
|
-
const newFeature = {
|
|
25
|
-
id: newId,
|
|
26
|
-
name: `新功能 ${newId}`,
|
|
27
|
-
active: false,
|
|
28
|
-
count: 0
|
|
29
|
-
};
|
|
30
|
-
setFeatures([...features, newFeature]);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
return (
|
|
34
|
-
<div className="layout">
|
|
35
|
-
{/* 顶部导航栏 */}
|
|
36
|
-
<div className="header">
|
|
37
|
-
<div className="logo">
|
|
38
|
-
<div className="logo-icon">W</div>
|
|
39
|
-
Whistle 接口代理插件
|
|
40
|
-
</div>
|
|
41
|
-
<div className="search">
|
|
42
|
-
<input type="text" placeholder="搜索..." />
|
|
43
|
-
</div>
|
|
44
|
-
<div className="header-actions">
|
|
45
|
-
<button>
|
|
46
|
-
<i>🔄</i> 刷新
|
|
47
|
-
</button>
|
|
48
|
-
<button>
|
|
49
|
-
<i>⚙️</i> 设置
|
|
50
|
-
</button>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
{/* 主内容区域 */}
|
|
55
|
-
<div className="main-container">
|
|
56
|
-
{/* 左侧导航 */}
|
|
57
|
-
<div className="sidebar">
|
|
58
|
-
<div className="create-feature">
|
|
59
|
-
<button onClick={handleCreateFeature}>+ 创建新功能</button>
|
|
60
|
-
</div>
|
|
61
|
-
|
|
62
|
-
<div className="feature-list">
|
|
63
|
-
{features.map(feature => (
|
|
64
|
-
<div
|
|
65
|
-
key={feature.id}
|
|
66
|
-
className={`feature-item ${activeFeature === feature.id ? 'active' : ''}`}
|
|
67
|
-
onClick={() => handleFeatureClick(feature.id)}
|
|
68
|
-
>
|
|
69
|
-
<div className="feature-item-name">
|
|
70
|
-
<div className={`status-icon ${feature.active ? 'active' : ''}`}>
|
|
71
|
-
{feature.active ? '✓' : ''}
|
|
72
|
-
</div>
|
|
73
|
-
{feature.name}
|
|
74
|
-
</div>
|
|
75
|
-
<div className="feature-item-count">{feature.count}</div>
|
|
76
|
-
</div>
|
|
77
|
-
))}
|
|
78
|
-
</div>
|
|
79
|
-
|
|
80
|
-
<div className="sidebar-footer">
|
|
81
|
-
Whistle Mock Plugin v0.2.0
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
{/* 内容区 */}
|
|
86
|
-
<div className="content">
|
|
87
|
-
{children}
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
90
|
-
</div>
|
|
91
|
-
);
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
export default Layout;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Link, useLocation } from 'react-router-dom';
|
|
3
|
-
import '../styles/main-nav.css';
|
|
4
|
-
|
|
5
|
-
const MainNav = () => {
|
|
6
|
-
const location = useLocation();
|
|
7
|
-
const pathname = location.pathname;
|
|
8
|
-
|
|
9
|
-
// 检查当前路径是否匹配导航项
|
|
10
|
-
const isActive = (path) => {
|
|
11
|
-
if (path === '/' && pathname === '/') return true;
|
|
12
|
-
if (path !== '/' && pathname.startsWith(path)) return true;
|
|
13
|
-
return false;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<div className="main-nav">
|
|
18
|
-
<div className="main-header">
|
|
19
|
-
<h1>Whistle Mock 数据工厂</h1>
|
|
20
|
-
</div>
|
|
21
|
-
<nav className="nav-tabs">
|
|
22
|
-
<Link to="/" className={`nav-tab ${isActive('/') ? 'active' : ''}`}>
|
|
23
|
-
首页
|
|
24
|
-
</Link>
|
|
25
|
-
<Link to="/mock" className={`nav-tab ${isActive('/mock') ? 'active' : ''}`}>
|
|
26
|
-
Mock数据
|
|
27
|
-
</Link>
|
|
28
|
-
<Link to="/file-proxy" className={`nav-tab ${isActive('/file-proxy') ? 'active' : ''}`}>
|
|
29
|
-
接口配置
|
|
30
|
-
</Link>
|
|
31
|
-
<Link to="/url-redirect" className={`nav-tab ${isActive('/url-redirect') ? 'active' : ''}`}>
|
|
32
|
-
规则管理
|
|
33
|
-
</Link>
|
|
34
|
-
<Link to="/settings" className={`nav-tab ${isActive('/settings') ? 'active' : ''}`}>
|
|
35
|
-
设置
|
|
36
|
-
</Link>
|
|
37
|
-
</nav>
|
|
38
|
-
</div>
|
|
39
|
-
);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export default MainNav;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import MainNav from './MainNav';
|
|
3
|
-
import '../styles/page-container.css';
|
|
4
|
-
|
|
5
|
-
const PageContainer = ({ children }) => {
|
|
6
|
-
return (
|
|
7
|
-
<div className="page-container">
|
|
8
|
-
<MainNav />
|
|
9
|
-
<div className="page-content">
|
|
10
|
-
{children}
|
|
11
|
-
</div>
|
|
12
|
-
</div>
|
|
13
|
-
);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export default PageContainer;
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { Modal, Spin, Alert } from 'antd';
|
|
3
|
-
import ReactMarkdown from 'react-markdown';
|
|
4
|
-
import '../styles/version-modal.css';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 获取版本信息的请求函数
|
|
8
|
-
* @returns {Promise<Object>} 版本信息
|
|
9
|
-
*/
|
|
10
|
-
const getVersionInfo = async () => {
|
|
11
|
-
const response = await fetch('/cgi-bin/version', {
|
|
12
|
-
headers: {
|
|
13
|
-
'Content-Type': 'application/json'
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const data = await response.json();
|
|
18
|
-
|
|
19
|
-
if (!response.ok) {
|
|
20
|
-
throw new Error(data.message || '请求失败');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return data;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* 版本信息模态框组件
|
|
28
|
-
* @param {Object} props - 组件属性
|
|
29
|
-
* @param {boolean} props.visible - 是否显示模态框
|
|
30
|
-
* @param {Function} props.onCancel - 取消回调函数
|
|
31
|
-
*/
|
|
32
|
-
const VersionModal = ({ visible, onCancel }) => {
|
|
33
|
-
const [versionContent, setVersionContent] = useState('');
|
|
34
|
-
const [loading, setLoading] = useState(false);
|
|
35
|
-
const [error, setError] = useState(null);
|
|
36
|
-
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
if (visible) {
|
|
39
|
-
fetchVersionInfo();
|
|
40
|
-
}
|
|
41
|
-
}, [visible]);
|
|
42
|
-
|
|
43
|
-
const fetchVersionInfo = async () => {
|
|
44
|
-
setLoading(true);
|
|
45
|
-
setError(null);
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
const response = await getVersionInfo();
|
|
49
|
-
if (response.code === 0) {
|
|
50
|
-
setVersionContent(response.data.content);
|
|
51
|
-
} else {
|
|
52
|
-
setError(response.message);
|
|
53
|
-
}
|
|
54
|
-
} catch (err) {
|
|
55
|
-
setError(`获取版本信息失败: ${err.message}`);
|
|
56
|
-
} finally {
|
|
57
|
-
setLoading(false);
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<Modal
|
|
63
|
-
title="版本更新记录"
|
|
64
|
-
open={visible}
|
|
65
|
-
onCancel={onCancel}
|
|
66
|
-
footer={null}
|
|
67
|
-
width={800}
|
|
68
|
-
className="version-modal"
|
|
69
|
-
>
|
|
70
|
-
<div className="version-modal-content">
|
|
71
|
-
{loading && (
|
|
72
|
-
<div className="version-modal-loading">
|
|
73
|
-
<Spin size="large" />
|
|
74
|
-
<p>正在加载版本信息...</p>
|
|
75
|
-
</div>
|
|
76
|
-
)}
|
|
77
|
-
|
|
78
|
-
{error && (
|
|
79
|
-
<Alert
|
|
80
|
-
message="加载失败"
|
|
81
|
-
description={error}
|
|
82
|
-
type="error"
|
|
83
|
-
showIcon
|
|
84
|
-
/>
|
|
85
|
-
)}
|
|
86
|
-
|
|
87
|
-
{!loading && !error && versionContent && (
|
|
88
|
-
<div className="version-markdown-content">
|
|
89
|
-
<ReactMarkdown>
|
|
90
|
-
{versionContent.replace(/^#\s*🔄?\s*版本更新记录\s*\n+/, '')}
|
|
91
|
-
</ReactMarkdown>
|
|
92
|
-
</div>
|
|
93
|
-
)}
|
|
94
|
-
</div>
|
|
95
|
-
</Modal>
|
|
96
|
-
);
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
export default VersionModal;
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import { Drawer, Form, Input, message, Button, Space } from 'antd';
|
|
3
|
-
import axios from 'axios';
|
|
4
|
-
import { ResponseContentEditor, PreviewModal, formatResponseContent, refreshCacheAfterUpdate } from '.';
|
|
5
|
-
|
|
6
|
-
const DataManagementDrawer = ({ visible, onCancel, interfaceItem, onSaved }) => {
|
|
7
|
-
const [form] = Form.useForm();
|
|
8
|
-
const [previewVisible, setPreviewVisible] = useState(false);
|
|
9
|
-
const [previewContent, setPreviewContent] = useState(null);
|
|
10
|
-
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
if (visible && interfaceItem) {
|
|
13
|
-
let responses = Array.isArray(interfaceItem.responses) ? interfaceItem.responses : [];
|
|
14
|
-
let activeResponseId = interfaceItem.activeResponseId || (responses[0] && responses[0].id) || '';
|
|
15
|
-
form.setFieldsValue({ responses, activeResponseId });
|
|
16
|
-
}
|
|
17
|
-
}, [visible, interfaceItem]);
|
|
18
|
-
|
|
19
|
-
const handlePreview = () => {
|
|
20
|
-
const values = form.getFieldsValue(true);
|
|
21
|
-
const responses = Array.isArray(values.responses) ? values.responses : [];
|
|
22
|
-
const activeResponse = responses.find(r => r.id === values.activeResponseId) || responses[0];
|
|
23
|
-
if (!activeResponse) {
|
|
24
|
-
message.error('未找到有效的响应内容');
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
const contentType = 'application/json; charset=utf-8';
|
|
28
|
-
const formatted = formatResponseContent(activeResponse.content || '', contentType);
|
|
29
|
-
setPreviewContent({ title: `预览: ${activeResponse.name || '未命名响应'}`, content: formatted, contentType, description: '' });
|
|
30
|
-
setPreviewVisible(true);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const handleOk = async () => {
|
|
34
|
-
try {
|
|
35
|
-
await form.validateFields();
|
|
36
|
-
const values = form.getFieldsValue(true);
|
|
37
|
-
const payload = {
|
|
38
|
-
responses: Array.isArray(values.responses) ? values.responses : [],
|
|
39
|
-
activeResponseId: values.activeResponseId || (values.responses && values.responses[0] && values.responses[0].id) || ''
|
|
40
|
-
};
|
|
41
|
-
if (!interfaceItem || !interfaceItem.id) {
|
|
42
|
-
message.error('缺少接口ID,无法保存');
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const res = await axios.patch(`/cgi-bin/interfaces/${interfaceItem.id}`, payload);
|
|
46
|
-
if (res.data && res.data.code === 0) {
|
|
47
|
-
message.success('保存成功');
|
|
48
|
-
await refreshCacheAfterUpdate();
|
|
49
|
-
if (onSaved) onSaved();
|
|
50
|
-
if (onCancel) onCancel();
|
|
51
|
-
} else {
|
|
52
|
-
throw new Error(res.data?.message || '保存失败');
|
|
53
|
-
}
|
|
54
|
-
} catch (err) {
|
|
55
|
-
if (err && err.errorFields) return; // 表单校验错误
|
|
56
|
-
console.error('保存失败', err);
|
|
57
|
-
message.error(err.response?.data?.message || err.message || '保存失败');
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<>
|
|
63
|
-
<Drawer
|
|
64
|
-
title="数据管理"
|
|
65
|
-
placement="right"
|
|
66
|
-
width={1040}
|
|
67
|
-
open={visible}
|
|
68
|
-
onClose={onCancel}
|
|
69
|
-
destroyOnClose
|
|
70
|
-
bodyStyle={{ paddingBottom: 80 }}
|
|
71
|
-
footer={
|
|
72
|
-
<div style={{ textAlign: 'right' }}>
|
|
73
|
-
<Space>
|
|
74
|
-
<Button onClick={onCancel}>取消</Button>
|
|
75
|
-
<Button type="primary" onClick={handleOk}>保存</Button>
|
|
76
|
-
</Space>
|
|
77
|
-
</div>
|
|
78
|
-
}
|
|
79
|
-
>
|
|
80
|
-
<Form form={form} layout="vertical" initialValues={{ responses: [], activeResponseId: '' }}>
|
|
81
|
-
<Form.Item name="responses" hidden>
|
|
82
|
-
<Input />
|
|
83
|
-
</Form.Item>
|
|
84
|
-
<Form.Item name="activeResponseId" hidden>
|
|
85
|
-
<Input />
|
|
86
|
-
</Form.Item>
|
|
87
|
-
<Form.Item noStyle shouldUpdate>
|
|
88
|
-
{({ getFieldValue }) => {
|
|
89
|
-
const responses = getFieldValue('responses') || [];
|
|
90
|
-
const activeResponseId = getFieldValue('activeResponseId') || (responses[0] && responses[0].id) || '';
|
|
91
|
-
return (
|
|
92
|
-
<ResponseContentEditor
|
|
93
|
-
form={form}
|
|
94
|
-
responses={responses}
|
|
95
|
-
activeResponseId={activeResponseId}
|
|
96
|
-
onPreview={handlePreview}
|
|
97
|
-
enableSorting={true}
|
|
98
|
-
/>
|
|
99
|
-
);
|
|
100
|
-
}}
|
|
101
|
-
</Form.Item>
|
|
102
|
-
</Form>
|
|
103
|
-
</Drawer>
|
|
104
|
-
|
|
105
|
-
<PreviewModal
|
|
106
|
-
visible={previewVisible}
|
|
107
|
-
onCancel={() => setPreviewVisible(false)}
|
|
108
|
-
previewContent={previewContent}
|
|
109
|
-
/>
|
|
110
|
-
</>
|
|
111
|
-
);
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
export default DataManagementDrawer;
|
|
115
|
-
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import { Modal, Form, Input, message } from 'antd';
|
|
3
|
-
import axios from 'axios';
|
|
4
|
-
import { ResponseContentEditor, PreviewModal, formatResponseContent, refreshCacheAfterUpdate } from '.';
|
|
5
|
-
|
|
6
|
-
const DataManagementModal = ({ visible, onCancel, interfaceItem, onSaved }) => {
|
|
7
|
-
const [form] = Form.useForm();
|
|
8
|
-
const [previewVisible, setPreviewVisible] = useState(false);
|
|
9
|
-
const [previewContent, setPreviewContent] = useState(null);
|
|
10
|
-
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
if (visible && interfaceItem) {
|
|
13
|
-
let responses = Array.isArray(interfaceItem.responses) ? interfaceItem.responses : [];
|
|
14
|
-
let activeResponseId = interfaceItem.activeResponseId || (responses[0] && responses[0].id) || '';
|
|
15
|
-
form.setFieldsValue({ responses, activeResponseId });
|
|
16
|
-
}
|
|
17
|
-
}, [visible, interfaceItem]);
|
|
18
|
-
|
|
19
|
-
const handlePreview = () => {
|
|
20
|
-
const values = form.getFieldsValue(true);
|
|
21
|
-
const responses = Array.isArray(values.responses) ? values.responses : [];
|
|
22
|
-
const activeResponse = responses.find(r => r.id === values.activeResponseId) || responses[0];
|
|
23
|
-
if (!activeResponse) {
|
|
24
|
-
message.error('未找到有效的响应内容');
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
const contentType = 'application/json; charset=utf-8';
|
|
28
|
-
const formatted = formatResponseContent(activeResponse.content || '', contentType);
|
|
29
|
-
setPreviewContent({ title: `预览: ${activeResponse.name || '未命名响应'}`, content: formatted, contentType, description: '' });
|
|
30
|
-
setPreviewVisible(true);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const handleOk = async () => {
|
|
34
|
-
try {
|
|
35
|
-
await form.validateFields();
|
|
36
|
-
const values = form.getFieldsValue(true);
|
|
37
|
-
const payload = {
|
|
38
|
-
responses: Array.isArray(values.responses) ? values.responses : [],
|
|
39
|
-
activeResponseId: values.activeResponseId || (values.responses && values.responses[0] && values.responses[0].id) || ''
|
|
40
|
-
};
|
|
41
|
-
if (!interfaceItem || !interfaceItem.id) {
|
|
42
|
-
message.error('缺少接口ID,无法保存');
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const res = await axios.patch(`/cgi-bin/interfaces/${interfaceItem.id}`, payload);
|
|
46
|
-
if (res.data && res.data.code === 0) {
|
|
47
|
-
message.success('保存成功');
|
|
48
|
-
await refreshCacheAfterUpdate();
|
|
49
|
-
if (onSaved) onSaved();
|
|
50
|
-
if (onCancel) onCancel();
|
|
51
|
-
} else {
|
|
52
|
-
throw new Error(res.data?.message || '保存失败');
|
|
53
|
-
}
|
|
54
|
-
} catch (err) {
|
|
55
|
-
if (err && err.errorFields) return; // 表单校验错误
|
|
56
|
-
console.error('保存失败', err);
|
|
57
|
-
message.error(err.response?.data?.message || err.message || '保存失败');
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
return (
|
|
62
|
-
<>
|
|
63
|
-
<Modal
|
|
64
|
-
title="数据管理"
|
|
65
|
-
open={visible}
|
|
66
|
-
onOk={handleOk}
|
|
67
|
-
onCancel={onCancel}
|
|
68
|
-
width={1040}
|
|
69
|
-
destroyOnClose
|
|
70
|
-
okText="保存"
|
|
71
|
-
cancelText="取消"
|
|
72
|
-
bodyStyle={{ maxHeight: '80vh', overflow: 'auto', padding: '24px' }}
|
|
73
|
-
>
|
|
74
|
-
<Form form={form} layout="vertical" initialValues={{ responses: [], activeResponseId: '' }}>
|
|
75
|
-
<Form.Item name="responses" hidden>
|
|
76
|
-
<Input />
|
|
77
|
-
</Form.Item>
|
|
78
|
-
<Form.Item name="activeResponseId" hidden>
|
|
79
|
-
<Input />
|
|
80
|
-
</Form.Item>
|
|
81
|
-
<Form.Item noStyle shouldUpdate>
|
|
82
|
-
{({ getFieldValue }) => {
|
|
83
|
-
const responses = getFieldValue('responses') || [];
|
|
84
|
-
const activeResponseId = getFieldValue('activeResponseId') || (responses[0] && responses[0].id) || '';
|
|
85
|
-
return (
|
|
86
|
-
<ResponseContentEditor
|
|
87
|
-
form={form}
|
|
88
|
-
responses={responses}
|
|
89
|
-
activeResponseId={activeResponseId}
|
|
90
|
-
onPreview={handlePreview}
|
|
91
|
-
enableSorting={true}
|
|
92
|
-
/>
|
|
93
|
-
);
|
|
94
|
-
}}
|
|
95
|
-
</Form.Item>
|
|
96
|
-
</Form>
|
|
97
|
-
</Modal>
|
|
98
|
-
|
|
99
|
-
<PreviewModal
|
|
100
|
-
visible={previewVisible}
|
|
101
|
-
onCancel={() => setPreviewVisible(false)}
|
|
102
|
-
previewContent={previewContent}
|
|
103
|
-
/>
|
|
104
|
-
</>
|
|
105
|
-
);
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
export default DataManagementModal;
|
|
109
|
-
|
|
110
|
-
|