whistle.mock-plugins 1.2.2-beta.7 → 1.2.2-beta.9
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 +0 -34
- package/app/public/js/index.js +1 -1
- package/app/public/js/index.js.LICENSE.txt +2 -0
- package/lib/ruleManager.js +55 -23
- package/package.json +1 -2
- package/ui/src/components/interface-management/ResponseContentEditor.js +191 -30
- package/ui/src/components/interface-management/index.js +0 -1
- package/ui/src/pages/InterfaceManagementV2.js +213 -985
- package/ui/src/pages/Settings.js +6 -75
- package/ui/src/styles/interface-management.css +1 -37
- package/ui/src/styles/settings.css +0 -29
- package/COLUMN_RESIZE_IMPLEMENTATION.md +0 -162
- package/TEST_GUIDE.md +0 -156
- package/ui/src/components/interface-management/DataManagementDrawer.js +0 -115
package/ui/src/pages/Settings.js
CHANGED
|
@@ -1,45 +1,11 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import AppLayout from '../components/AppLayout';
|
|
3
|
-
import { Card, Typography,
|
|
3
|
+
import { Card, Typography, Empty } from 'antd';
|
|
4
4
|
import '../styles/settings.css';
|
|
5
5
|
|
|
6
6
|
const { Title, Text, Paragraph } = Typography;
|
|
7
7
|
|
|
8
8
|
const Settings = () => {
|
|
9
|
-
const [displayMode, setDisplayMode] = useState('modal');
|
|
10
|
-
const [interfaceFormDisplayMode, setInterfaceFormDisplayMode] = useState('modal');
|
|
11
|
-
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
// 从 localStorage 读取配置
|
|
14
|
-
const savedMode = localStorage.getItem('dataManagementDisplayMode');
|
|
15
|
-
if (savedMode) {
|
|
16
|
-
setDisplayMode(savedMode);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const savedInterfaceFormMode = localStorage.getItem('interfaceFormDisplayMode');
|
|
20
|
-
if (savedInterfaceFormMode) {
|
|
21
|
-
setInterfaceFormDisplayMode(savedInterfaceFormMode);
|
|
22
|
-
}
|
|
23
|
-
}, []);
|
|
24
|
-
|
|
25
|
-
const handleDisplayModeChange = (checked) => {
|
|
26
|
-
const newMode = checked ? 'drawer' : 'modal';
|
|
27
|
-
setDisplayMode(newMode);
|
|
28
|
-
localStorage.setItem('dataManagementDisplayMode', newMode);
|
|
29
|
-
|
|
30
|
-
// 触发 storage 事件,通知其他页面
|
|
31
|
-
window.dispatchEvent(new Event('storage'));
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const handleInterfaceFormDisplayModeChange = (checked) => {
|
|
35
|
-
const newMode = checked ? 'drawer' : 'modal';
|
|
36
|
-
setInterfaceFormDisplayMode(newMode);
|
|
37
|
-
localStorage.setItem('interfaceFormDisplayMode', newMode);
|
|
38
|
-
|
|
39
|
-
// 触发 storage 事件,通知其他页面
|
|
40
|
-
window.dispatchEvent(new Event('storage'));
|
|
41
|
-
};
|
|
42
|
-
|
|
43
9
|
return (
|
|
44
10
|
<AppLayout>
|
|
45
11
|
<div className="page-container">
|
|
@@ -53,45 +19,10 @@ const Settings = () => {
|
|
|
53
19
|
</div>
|
|
54
20
|
|
|
55
21
|
<Card title="界面设置" className="settings-card">
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<div className="settings-item-description">
|
|
61
|
-
选择数据管理的显示方式
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
<Space>
|
|
65
|
-
<Text type="secondary">
|
|
66
|
-
{displayMode === 'drawer' ? '抽屉模式' : '弹窗模式'}
|
|
67
|
-
</Text>
|
|
68
|
-
<Switch
|
|
69
|
-
checked={displayMode === 'drawer'}
|
|
70
|
-
onChange={handleDisplayModeChange}
|
|
71
|
-
/>
|
|
72
|
-
</Space>
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
|
|
76
|
-
<div className="settings-item">
|
|
77
|
-
<div className="settings-item-header">
|
|
78
|
-
<div>
|
|
79
|
-
<div className="settings-item-title">接口表单交互</div>
|
|
80
|
-
<div className="settings-item-description">
|
|
81
|
-
选择添加/编辑接口的显示方式
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
<Space>
|
|
85
|
-
<Text type="secondary">
|
|
86
|
-
{interfaceFormDisplayMode === 'drawer' ? '抽屉模式' : '弹窗模式'}
|
|
87
|
-
</Text>
|
|
88
|
-
<Switch
|
|
89
|
-
checked={interfaceFormDisplayMode === 'drawer'}
|
|
90
|
-
onChange={handleInterfaceFormDisplayModeChange}
|
|
91
|
-
/>
|
|
92
|
-
</Space>
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
22
|
+
<Empty
|
|
23
|
+
description="暂无可配置项"
|
|
24
|
+
style={{ padding: '40px 0' }}
|
|
25
|
+
/>
|
|
95
26
|
</Card>
|
|
96
27
|
</div>
|
|
97
28
|
</AppLayout>
|
|
@@ -295,11 +295,7 @@
|
|
|
295
295
|
padding: 12px 16px !important;
|
|
296
296
|
white-space: nowrap;
|
|
297
297
|
overflow: hidden;
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
/* 当前响应列需要 overflow: visible 以显示完整的下拉菜单 */
|
|
301
|
-
.interface-table td.current-response-cell {
|
|
302
|
-
overflow: visible !important;
|
|
298
|
+
text-overflow: ellipsis;
|
|
303
299
|
}
|
|
304
300
|
|
|
305
301
|
/* 卡片视图样式 */
|
|
@@ -1287,35 +1283,3 @@
|
|
|
1287
1283
|
.interface-table .ant-pagination-options {
|
|
1288
1284
|
margin-right: 16px;
|
|
1289
1285
|
}
|
|
1290
|
-
|
|
1291
|
-
/* 可调整列宽样式 */
|
|
1292
|
-
.column-resize-handle {
|
|
1293
|
-
position: absolute;
|
|
1294
|
-
right: -5px;
|
|
1295
|
-
top: 0;
|
|
1296
|
-
bottom: 0;
|
|
1297
|
-
width: 10px;
|
|
1298
|
-
cursor: col-resize;
|
|
1299
|
-
z-index: 1;
|
|
1300
|
-
user-select: none;
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
.column-resize-handle:hover {
|
|
1304
|
-
background-color: rgba(24, 144, 255, 0.2);
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
.column-resize-handle::after {
|
|
1308
|
-
content: '';
|
|
1309
|
-
position: absolute;
|
|
1310
|
-
right: 4px;
|
|
1311
|
-
top: 50%;
|
|
1312
|
-
transform: translateY(-50%);
|
|
1313
|
-
width: 2px;
|
|
1314
|
-
height: 20px;
|
|
1315
|
-
background-color: #d9d9d9;
|
|
1316
|
-
transition: background-color 0.3s;
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
|
-
.column-resize-handle:hover::after {
|
|
1320
|
-
background-color: #1890ff;
|
|
1321
|
-
}
|
|
@@ -33,35 +33,6 @@
|
|
|
33
33
|
font-weight: 500;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/* 设置项样式 */
|
|
37
|
-
.settings-item {
|
|
38
|
-
padding: 16px 0;
|
|
39
|
-
border-bottom: 1px solid #f0f0f0;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.settings-item:last-child {
|
|
43
|
-
border-bottom: none;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
.settings-item-header {
|
|
47
|
-
display: flex;
|
|
48
|
-
justify-content: space-between;
|
|
49
|
-
align-items: center;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
.settings-item-title {
|
|
53
|
-
font-size: 14px;
|
|
54
|
-
font-weight: 500;
|
|
55
|
-
color: #262626;
|
|
56
|
-
margin-bottom: 4px;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.settings-item-description {
|
|
60
|
-
font-size: 12px;
|
|
61
|
-
color: #8c8c8c;
|
|
62
|
-
line-height: 1.5;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
36
|
.settings-section {
|
|
66
37
|
margin-bottom: 32px;
|
|
67
38
|
}
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
# 列宽拖动调整功能实现说明
|
|
2
|
-
|
|
3
|
-
## 实现日期
|
|
4
|
-
2025-11-06
|
|
5
|
-
|
|
6
|
-
## 功能概述
|
|
7
|
-
为接口管理页面的"当前响应"列添加了拖动调整宽度的功能,列内的下拉框(Select)会自动根据列宽进行自适应。
|
|
8
|
-
|
|
9
|
-
## 技术方案
|
|
10
|
-
使用 Ant Design + react-resizable 的组合方案实现列宽拖动调整。
|
|
11
|
-
|
|
12
|
-
## 修改文件清单
|
|
13
|
-
|
|
14
|
-
### 1. package.json
|
|
15
|
-
**修改内容:**
|
|
16
|
-
- 添加 `react-resizable: ^3.0.5` 依赖到 devDependencies
|
|
17
|
-
|
|
18
|
-
**位置:** 第42行
|
|
19
|
-
|
|
20
|
-
### 2. InterfaceManagementV2.js
|
|
21
|
-
**修改内容:**
|
|
22
|
-
|
|
23
|
-
#### 2.1 导入模块(第18-19行)
|
|
24
|
-
```javascript
|
|
25
|
-
import { Resizable } from 'react-resizable';
|
|
26
|
-
import 'react-resizable/css/styles.css';
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
#### 2.2 扩展 tableConfig 状态(第98-123行)
|
|
30
|
-
- 添加 `columnWidths` 字段到状态中
|
|
31
|
-
- 为"当前响应"列设置默认宽度 180px
|
|
32
|
-
- 添加兼容性处理,确保旧缓存数据能正确加载
|
|
33
|
-
|
|
34
|
-
#### 2.3 创建 ResizableTitle 组件(第714-740行)
|
|
35
|
-
- 封装可调整大小的表头组件
|
|
36
|
-
- 集成 react-resizable 的 Resizable 组件
|
|
37
|
-
- 添加拖动手柄和事件处理
|
|
38
|
-
|
|
39
|
-
#### 2.4 创建 handleResize 函数(第742-749行)
|
|
40
|
-
- 处理列宽调整事件
|
|
41
|
-
- 更新 tableConfig.columnWidths
|
|
42
|
-
- 自动保存到 localStorage
|
|
43
|
-
|
|
44
|
-
#### 2.5 修改"当前响应"列配置(第844-881行)
|
|
45
|
-
- 将固定宽度改为动态读取:`tableConfig.columnWidths?.currentResponse || 180`
|
|
46
|
-
- 添加 `onHeaderCell` 属性,配置可调整大小的表头
|
|
47
|
-
- 为 Select 组件添加 `getPopupContainer` 属性,确保下拉菜单正确定位
|
|
48
|
-
|
|
49
|
-
#### 2.6 修改 Table 组件(第1383-1387行)
|
|
50
|
-
- 添加 `components` 属性
|
|
51
|
-
- 配置自定义表头组件:`{ header: { cell: ResizableTitle } }`
|
|
52
|
-
|
|
53
|
-
#### 2.7 更新 handleResetColumns 函数(第680-689行)
|
|
54
|
-
- 重置列配置时同时重置列宽到默认值 180px
|
|
55
|
-
|
|
56
|
-
### 3. interface-management.css
|
|
57
|
-
**修改内容:**
|
|
58
|
-
|
|
59
|
-
添加拖动手柄样式(第1287-1321行):
|
|
60
|
-
- `.react-resizable` - 可调整大小容器的基础样式
|
|
61
|
-
- `.react-resizable-handle` - 拖动手柄的样式和定位
|
|
62
|
-
- `.react-resizable-handle:hover` - 悬停时的视觉反馈
|
|
63
|
-
- `.react-resizable-handle::after` - 拖动指示线的样式
|
|
64
|
-
|
|
65
|
-
## 功能特性
|
|
66
|
-
|
|
67
|
-
### 1. 列宽拖动
|
|
68
|
-
- 鼠标悬停在"当前响应"列的右边缘时,光标变为调整大小的图标
|
|
69
|
-
- 拖动边缘可以调整列宽
|
|
70
|
-
- 拖动时有视觉反馈(蓝色高亮)
|
|
71
|
-
|
|
72
|
-
### 2. 自动保存
|
|
73
|
-
- 调整后的列宽自动保存到 localStorage
|
|
74
|
-
- 页面刷新后保持用户设置的列宽
|
|
75
|
-
|
|
76
|
-
### 3. Select 自适应
|
|
77
|
-
- 列内的下拉框宽度设置为 100%
|
|
78
|
-
- 随列宽变化自动调整
|
|
79
|
-
- 下拉菜单正确定位,不会超出容器
|
|
80
|
-
|
|
81
|
-
### 4. 重置功能
|
|
82
|
-
- 点击"重置"按钮可以恢复默认列宽(180px)
|
|
83
|
-
|
|
84
|
-
### 5. 兼容性处理
|
|
85
|
-
- 兼容旧版本的缓存数据
|
|
86
|
-
- 如果缓存中没有 columnWidths,自动初始化为默认值
|
|
87
|
-
|
|
88
|
-
## 使用说明
|
|
89
|
-
|
|
90
|
-
### 安装依赖
|
|
91
|
-
```bash
|
|
92
|
-
cd whistle.mock-plugin
|
|
93
|
-
npm install
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### 构建项目
|
|
97
|
-
```bash
|
|
98
|
-
npm run build
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### 测试功能
|
|
102
|
-
1. 启动 whistle 服务
|
|
103
|
-
2. 打开接口管理页面
|
|
104
|
-
3. 找到"当前响应"列
|
|
105
|
-
4. 将鼠标移动到列标题的右边缘
|
|
106
|
-
5. 当光标变为调整大小图标时,拖动调整列宽
|
|
107
|
-
6. 刷新页面,验证列宽是否保持
|
|
108
|
-
|
|
109
|
-
## 技术细节
|
|
110
|
-
|
|
111
|
-
### 最小/最大宽度
|
|
112
|
-
当前实现没有设置硬性的最小/最大宽度限制,但可以通过以下方式添加:
|
|
113
|
-
|
|
114
|
-
```javascript
|
|
115
|
-
onResize: (e, { size }) => {
|
|
116
|
-
const minWidth = 120;
|
|
117
|
-
const maxWidth = 400;
|
|
118
|
-
const newWidth = Math.max(minWidth, Math.min(maxWidth, size.width));
|
|
119
|
-
// 更新列宽...
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### 扩展到其他列
|
|
124
|
-
如果需要为其他列也添加拖动调整功能:
|
|
125
|
-
|
|
126
|
-
1. 在 `tableConfig.columnWidths` 中添加对应列的默认宽度
|
|
127
|
-
2. 在列配置中添加 `onHeaderCell` 属性
|
|
128
|
-
3. 使用动态宽度替代固定宽度
|
|
129
|
-
|
|
130
|
-
示例:
|
|
131
|
-
```javascript
|
|
132
|
-
{
|
|
133
|
-
title: '延迟(毫秒)',
|
|
134
|
-
key: 'responseDelay',
|
|
135
|
-
width: tableConfig.columnWidths?.responseDelay || 100,
|
|
136
|
-
onHeaderCell: (column) => ({
|
|
137
|
-
width: column.width,
|
|
138
|
-
onResize: handleResize('responseDelay'),
|
|
139
|
-
}),
|
|
140
|
-
// ...
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
## 注意事项
|
|
145
|
-
|
|
146
|
-
1. **性能考虑**:拖动时会频繁更新状态和 localStorage,对于大量数据的表格可能需要添加防抖处理
|
|
147
|
-
2. **浏览器兼容性**:react-resizable 依赖现代浏览器的 API,IE11 可能需要 polyfill
|
|
148
|
-
3. **移动端支持**:当前实现主要针对桌面端,移动端的触摸拖动可能需要额外配置
|
|
149
|
-
|
|
150
|
-
## 后续优化建议
|
|
151
|
-
|
|
152
|
-
1. 添加最小/最大宽度限制,防止列宽过小或过大
|
|
153
|
-
2. 为拖动操作添加防抖,减少状态更新频率
|
|
154
|
-
3. 支持双击列边缘自动调整到最适宽度
|
|
155
|
-
4. 支持更多列的宽度调整
|
|
156
|
-
5. 添加"恢复默认列宽"的快捷按钮
|
|
157
|
-
|
|
158
|
-
## 相关文档
|
|
159
|
-
|
|
160
|
-
- [react-resizable 官方文档](https://github.com/react-grid-layout/react-resizable)
|
|
161
|
-
- [Ant Design Table 可调整列宽示例](https://ant.design/components/table-cn#components-table-demo-resizable-column)
|
|
162
|
-
|
package/TEST_GUIDE.md
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
# 列宽拖动功能测试指南
|
|
2
|
-
|
|
3
|
-
## 测试环境
|
|
4
|
-
- ✅ 依赖已安装(react-resizable@3.0.5)
|
|
5
|
-
- ✅ 项目已构建(2025-11-06 15:03)
|
|
6
|
-
- ✅ Whistle 服务已重启
|
|
7
|
-
|
|
8
|
-
## 访问地址
|
|
9
|
-
打开浏览器访问:http://127.0.0.1:8899/ 或 http://10.181.134.20:8899/
|
|
10
|
-
|
|
11
|
-
## 测试步骤
|
|
12
|
-
|
|
13
|
-
### 1. 进入接口管理页面
|
|
14
|
-
1. 在 Whistle 管理界面中找到 mock-plugin 插件
|
|
15
|
-
2. 点击进入接口管理页面(InterfaceManagementV2)
|
|
16
|
-
|
|
17
|
-
### 2. 测试列宽拖动功能
|
|
18
|
-
|
|
19
|
-
#### 测试点 1:拖动调整列宽
|
|
20
|
-
1. 找到表格中的"当前响应"列
|
|
21
|
-
2. 将鼠标移动到"当前响应"列标题的右边缘
|
|
22
|
-
3. **预期结果**:光标应该变为调整大小的图标(↔️)
|
|
23
|
-
4. 按住鼠标左键并拖动
|
|
24
|
-
5. **预期结果**:
|
|
25
|
-
- 列宽随鼠标移动而变化
|
|
26
|
-
- 拖动手柄区域显示蓝色高亮
|
|
27
|
-
- 列内的下拉框(Select)宽度自动调整
|
|
28
|
-
|
|
29
|
-
#### 测试点 2:Select 下拉框自适应
|
|
30
|
-
1. 调整"当前响应"列的宽度(拖宽或拖窄)
|
|
31
|
-
2. 观察列内的下拉框
|
|
32
|
-
3. **预期结果**:
|
|
33
|
-
- 下拉框宽度始终占满整列
|
|
34
|
-
- 下拉框内容正常显示,不会溢出
|
|
35
|
-
- 点击下拉框,下拉菜单正确显示
|
|
36
|
-
|
|
37
|
-
#### 测试点 3:列宽持久化
|
|
38
|
-
1. 调整"当前响应"列的宽度到一个特定值(如 250px)
|
|
39
|
-
2. 刷新浏览器页面(F5 或 Cmd+R)
|
|
40
|
-
3. **预期结果**:
|
|
41
|
-
- 页面重新加载后,列宽保持为调整后的值
|
|
42
|
-
- 不会恢复到默认的 180px
|
|
43
|
-
|
|
44
|
-
#### 测试点 4:重置列宽
|
|
45
|
-
1. 调整"当前响应"列的宽度
|
|
46
|
-
2. 点击表格右上角的"列设置"按钮
|
|
47
|
-
3. 点击"重置"按钮
|
|
48
|
-
4. **预期结果**:
|
|
49
|
-
- 列宽恢复到默认值 180px
|
|
50
|
-
- 其他列配置也恢复到默认状态
|
|
51
|
-
|
|
52
|
-
#### 测试点 5:拖动视觉反馈
|
|
53
|
-
1. 将鼠标悬停在"当前响应"列的右边缘
|
|
54
|
-
2. **预期结果**:
|
|
55
|
-
- 显示一个灰色的竖线指示器
|
|
56
|
-
- 鼠标悬停时,指示器变为蓝色
|
|
57
|
-
- 拖动区域有半透明蓝色背景
|
|
58
|
-
|
|
59
|
-
### 3. 边界情况测试
|
|
60
|
-
|
|
61
|
-
#### 测试点 6:最小宽度
|
|
62
|
-
1. 尝试将列宽拖到非常窄
|
|
63
|
-
2. **预期结果**:
|
|
64
|
-
- 列宽可以调整到很小(当前没有硬性限制)
|
|
65
|
-
- Select 下拉框仍然可以正常点击和使用
|
|
66
|
-
|
|
67
|
-
#### 测试点 7:最大宽度
|
|
68
|
-
1. 尝试将列宽拖到非常宽
|
|
69
|
-
2. **预期结果**:
|
|
70
|
-
- 列宽可以调整到很宽(当前没有硬性限制)
|
|
71
|
-
- 不会影响其他列的显示
|
|
72
|
-
|
|
73
|
-
#### 测试点 8:多次调整
|
|
74
|
-
1. 多次来回拖动调整列宽
|
|
75
|
-
2. **预期结果**:
|
|
76
|
-
- 每次调整都流畅响应
|
|
77
|
-
- 没有卡顿或延迟
|
|
78
|
-
- localStorage 正确更新
|
|
79
|
-
|
|
80
|
-
### 4. 功能交互测试
|
|
81
|
-
|
|
82
|
-
#### 测试点 9:与其他功能的兼容性
|
|
83
|
-
1. 调整列宽后,测试以下功能:
|
|
84
|
-
- 切换响应数据(点击下拉框选择不同的响应)
|
|
85
|
-
- 排序功能(点击列标题排序)
|
|
86
|
-
- 分页功能(切换页码)
|
|
87
|
-
- 搜索功能(搜索接口)
|
|
88
|
-
- 分组筛选(选择不同分组)
|
|
89
|
-
2. **预期结果**:
|
|
90
|
-
- 所有功能正常工作
|
|
91
|
-
- 列宽保持不变
|
|
92
|
-
|
|
93
|
-
#### 测试点 10:兼容旧数据
|
|
94
|
-
1. 清除浏览器的 localStorage(开发者工具 > Application > Local Storage)
|
|
95
|
-
2. 刷新页面
|
|
96
|
-
3. **预期结果**:
|
|
97
|
-
- 页面正常加载
|
|
98
|
-
- "当前响应"列显示默认宽度 180px
|
|
99
|
-
- 没有控制台错误
|
|
100
|
-
|
|
101
|
-
## 已知问题和限制
|
|
102
|
-
|
|
103
|
-
1. **没有最小/最大宽度限制**:当前实现允许列宽调整到任意大小
|
|
104
|
-
2. **仅支持"当前响应"列**:其他列暂不支持拖动调整
|
|
105
|
-
3. **桌面端优化**:主要针对鼠标操作,触摸屏可能需要额外配置
|
|
106
|
-
|
|
107
|
-
## 测试结果记录
|
|
108
|
-
|
|
109
|
-
### 测试环境信息
|
|
110
|
-
- 浏览器:__________
|
|
111
|
-
- 操作系统:__________
|
|
112
|
-
- 测试日期:__________
|
|
113
|
-
|
|
114
|
-
### 测试结果
|
|
115
|
-
- [ ] 测试点 1:拖动调整列宽
|
|
116
|
-
- [ ] 测试点 2:Select 下拉框自适应
|
|
117
|
-
- [ ] 测试点 3:列宽持久化
|
|
118
|
-
- [ ] 测试点 4:重置列宽
|
|
119
|
-
- [ ] 测试点 5:拖动视觉反馈
|
|
120
|
-
- [ ] 测试点 6:最小宽度
|
|
121
|
-
- [ ] 测试点 7:最大宽度
|
|
122
|
-
- [ ] 测试点 8:多次调整
|
|
123
|
-
- [ ] 测试点 9:与其他功能的兼容性
|
|
124
|
-
- [ ] 测试点 10:兼容旧数据
|
|
125
|
-
|
|
126
|
-
### 发现的问题
|
|
127
|
-
(如有问题,请在此记录)
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
|
-
## 调试技巧
|
|
132
|
-
|
|
133
|
-
### 查看 localStorage 中的列宽配置
|
|
134
|
-
1. 打开浏览器开发者工具(F12)
|
|
135
|
-
2. 切换到 Console 标签
|
|
136
|
-
3. 输入以下命令查看配置:
|
|
137
|
-
```javascript
|
|
138
|
-
JSON.parse(localStorage.getItem('interface-table-config'))
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### 手动设置列宽
|
|
142
|
-
```javascript
|
|
143
|
-
const config = JSON.parse(localStorage.getItem('interface-table-config'));
|
|
144
|
-
config.columnWidths.currentResponse = 250;
|
|
145
|
-
localStorage.setItem('interface-table-config', JSON.stringify(config));
|
|
146
|
-
location.reload();
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### 清除列宽配置
|
|
150
|
-
```javascript
|
|
151
|
-
const config = JSON.parse(localStorage.getItem('interface-table-config'));
|
|
152
|
-
delete config.columnWidths;
|
|
153
|
-
localStorage.setItem('interface-table-config', JSON.stringify(config));
|
|
154
|
-
location.reload();
|
|
155
|
-
```
|
|
156
|
-
|
|
@@ -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
|
-
|