whistle.mock-plugins
Version:
Whistle 插件,用于快速创建 API 模拟数据
342 lines (316 loc) • 9.5 kB
JavaScript
import React, { useState, useEffect } from 'react';
import { Card, Table, Button, Space, Select, Tabs, Form, Input, Modal, Divider, message } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined, SaveOutlined, ExclamationCircleOutlined, CopyOutlined, PlayCircleOutlined } from '@ant-design/icons';
import { FeatureAPI, InterfaceAPI } from '../services/api';
const { Option } = Select;
const { TabPane } = Tabs;
const { confirm } = Modal;
const { TextArea } = Input;
const InterfacePage = () => {
const [features, setFeatures] = useState([]);
const [interfaces, setInterfaces] = useState([]);
const [loading, setLoading] = useState(false);
const [selectedFeature, setSelectedFeature] = useState(null);
const [modalVisible, setModalVisible] = useState(false);
const [currentInterface, setCurrentInterface] = useState(null);
const [form] = Form.useForm();
useEffect(() => {
fetchFeatures();
}, []);
useEffect(() => {
if (selectedFeature) {
fetchInterfaces(selectedFeature);
}
}, [selectedFeature]);
const fetchFeatures = async () => {
setLoading(true);
try {
const data = await FeatureAPI.getAllFeatures();
setFeatures(data);
if (data.length > 0 && !selectedFeature) {
setSelectedFeature(data[0].id);
}
} catch (error) {
console.error("获取功能列表失败:", error);
} finally {
setLoading(false);
}
};
const fetchInterfaces = async (featureId) => {
setLoading(true);
try {
const data = await FeatureAPI.getFeatureInterfaces(featureId);
setInterfaces(data);
} catch (error) {
console.error("获取接口列表失败:", error);
} finally {
setLoading(false);
}
};
const handleFeatureChange = (value) => {
setSelectedFeature(value);
};
const handleAdd = () => {
form.resetFields();
form.setFieldsValue({
featureId: selectedFeature,
method: 'GET',
status: 200,
delay: 0,
headers: '{\n "Content-Type": "application/json"\n}',
responseBody: '{\n "code": 0,\n "message": "success",\n "data": {}\n}'
});
setCurrentInterface(null);
setModalVisible(true);
};
const handleEdit = (record) => {
form.setFieldsValue({
...record,
headers: typeof record.headers === 'object'
? JSON.stringify(record.headers, null, 2)
: record.headers || '{}',
responseBody: typeof record.responseBody === 'object'
? JSON.stringify(record.responseBody, null, 2)
: record.responseBody || '{}'
});
setCurrentInterface(record);
setModalVisible(true);
};
const handleDelete = (record) => {
confirm({
title: '确认删除',
icon: <ExclamationCircleOutlined />,
content: `确定要删除接口 "${record.path}" 吗?`,
okText: '确认',
cancelText: '取消',
onOk: async () => {
try {
await InterfaceAPI.deleteInterface(record.id);
fetchInterfaces(selectedFeature);
} catch (error) {
console.error("删除接口失败:", error);
}
},
});
};
const handleCancel = () => {
setModalVisible(false);
};
const handleOk = async () => {
try {
const values = await form.validateFields();
try {
// 尝试解析 JSON
if (values.headers) {
values.headers = JSON.parse(values.headers);
}
if (values.responseBody) {
values.responseBody = JSON.parse(values.responseBody);
}
} catch (e) {
message.error('JSON 格式错误,请检查');
return;
}
if (currentInterface) {
await InterfaceAPI.updateInterface(currentInterface.id, values);
} else {
await InterfaceAPI.createInterface(values);
}
setModalVisible(false);
fetchInterfaces(selectedFeature);
} catch (error) {
console.error("保存接口失败:", error);
}
};
const handleTest = async (record) => {
try {
const result = await InterfaceAPI.testInterface(record.id);
Modal.info({
title: '测试结果',
width: 800,
content: (
<div>
<pre style={{ maxHeight: '500px', overflow: 'auto' }}>
{JSON.stringify(result, null, 2)}
</pre>
</div>
),
okText: '关闭'
});
} catch (error) {
console.error("测试失败:", error);
message.error('测试失败: ' + error.message);
}
};
const columns = [
{
title: 'URL 路径',
dataIndex: 'path',
key: 'path',
ellipsis: true,
},
{
title: '请求方法',
dataIndex: 'method',
key: 'method',
width: 100,
},
{
title: '状态码',
dataIndex: 'status',
key: 'status',
width: 100,
},
{
title: '延迟(ms)',
dataIndex: 'delay',
key: 'delay',
width: 100,
},
{
title: '描述',
dataIndex: 'description',
key: 'description',
ellipsis: true,
},
{
title: '操作',
key: 'action',
width: 240,
render: (_, record) => (
<Space size="small">
<Button type="text" icon={<EditOutlined />} onClick={() => handleEdit(record)}>
编辑
</Button>
<Button type="text" icon={<PlayCircleOutlined />} onClick={() => handleTest(record)}>
测试
</Button>
<Button type="text" danger icon={<DeleteOutlined />} onClick={() => handleDelete(record)}>
删除
</Button>
</Space>
),
},
];
return (
<div className="interface-page">
<Card
title="接口管理"
extra={
<Space>
<span>功能:</span>
<Select
style={{ width: 200 }}
value={selectedFeature}
onChange={handleFeatureChange}
loading={loading}
>
{features.map(feature => (
<Option key={feature.id} value={feature.id}>{feature.name}</Option>
))}
</Select>
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
添加接口
</Button>
</Space>
}
>
<Table
columns={columns}
dataSource={interfaces}
rowKey="id"
loading={loading}
/>
</Card>
<Modal
title={currentInterface ? "编辑接口" : "添加接口"}
open={modalVisible}
onOk={handleOk}
onCancel={handleCancel}
width={800}
okText="保存"
cancelText="取消"
>
<Form
form={form}
layout="vertical"
>
<Form.Item
name="featureId"
label="所属功能"
rules={[{ required: true, message: '请选择所属功能!' }]}
>
<Select>
{features.map(feature => (
<Option key={feature.id} value={feature.id}>{feature.name}</Option>
))}
</Select>
</Form.Item>
<Form.Item
name="path"
label="URL 路径"
rules={[{ required: true, message: '请输入 URL 路径!' }]}
>
<Input placeholder="/api/your-path" />
</Form.Item>
<div style={{ display: 'flex', gap: '16px' }}>
<Form.Item
name="method"
label="请求方法"
rules={[{ required: true, message: '请选择请求方法!' }]}
style={{ flex: 1 }}
>
<Select>
<Option value="GET">GET</Option>
<Option value="POST">POST</Option>
<Option value="PUT">PUT</Option>
<Option value="DELETE">DELETE</Option>
<Option value="PATCH">PATCH</Option>
</Select>
</Form.Item>
<Form.Item
name="status"
label="状态码"
rules={[{ required: true, message: '请输入状态码!' }]}
style={{ flex: 1 }}
>
<Input type="number" placeholder="200" />
</Form.Item>
<Form.Item
name="delay"
label="延迟(ms)"
rules={[{ required: true, message: '请输入延迟时间!' }]}
style={{ flex: 1 }}
>
<Input type="number" placeholder="0" />
</Form.Item>
</div>
<Form.Item
name="description"
label="描述"
>
<Input placeholder="接口描述" />
</Form.Item>
<Tabs defaultActiveKey="1">
<TabPane tab="响应体" key="1">
<Form.Item
name="responseBody"
rules={[{ required: true, message: '请输入响应体!' }]}
>
<TextArea rows={10} placeholder="响应JSON数据" />
</Form.Item>
</TabPane>
<TabPane tab="响应头" key="2">
<Form.Item
name="headers"
>
<TextArea rows={10} placeholder="响应头JSON数据" />
</Form.Item>
</TabPane>
</Tabs>
</Form>
</Modal>
</div>
);
};
export default InterfacePage;