diff --git a/config/label.json b/config/label.json index 0bdcad9..e9f9195 100644 --- a/config/label.json +++ b/config/label.json @@ -8,10 +8,11 @@ }, "endpoint": "/qr", "method": "GET", + "queryParams": ["qrcode"], "fields": [ { "id": "qrcode", - "label": "二维码内容", + "label": "SGWCMAID0000...", "type": "text", "required": true } @@ -26,6 +27,7 @@ }, "endpoint": "/ticket", "method": "GET", + "queryParams": ["userid"], "fields": [ { "id": "userid", @@ -44,6 +46,7 @@ }, "endpoint": "/mapstock", "method": "GET", + "queryParams": ["userid"], "fields": [ { "id": "userid", @@ -62,6 +65,7 @@ }, "endpoint": "/unlock", "method": "GET", + "queryParams": ["userid"], "fields": [ { "id": "userid", @@ -80,6 +84,7 @@ }, "endpoint": "/music", "method": "POST", + "requestFormat": "json", "fields": [ { "id": "userId", diff --git a/favicon.png b/favicon.png new file mode 100644 index 0000000..8b6ba7a Binary files /dev/null and b/favicon.png differ diff --git a/index.html b/index.html index 163274a..7a8f114 100644 --- a/index.html +++ b/index.html @@ -1,35 +1,11 @@ - - - - - - - - - - - - - - - TsumugiBoshi|纺星 - - - - + - + @@ -39,16 +15,15 @@ - - + + @@ -248,7 +266,8 @@ Alert, Menu, IconButton, - Link + Link, + Collapse } = MaterialUI; // 创建主题 @@ -262,9 +281,16 @@ ]; // 通用表单组件 - const DynamicForm = ({ formConfig, onSubmit }) => { + const DynamicForm = React.forwardRef(({ formConfig, onSubmit }, ref) => { const [formData, setFormData] = React.useState({}); + // 暴露更新表单数据的方法给父组件 + React.useImperativeHandle(ref, () => ({ + updateField: (fieldId, value) => { + handleChange(fieldId, value); + } + })); + const handleChange = (fieldId, value) => { const keys = fieldId.split('.'); setFormData(prev => { @@ -340,7 +366,7 @@ ); - }; + }); function App() { const [tabValue, setTabValue] = React.useState(0); @@ -372,6 +398,11 @@ const [snackbarOpen, setSnackbarOpen] = React.useState(false); const [snackbarMessage, setSnackbarMessage] = React.useState(''); const [menuAnchorEl, setMenuAnchorEl] = React.useState(null); + const [logs, setLogs] = React.useState( + JSON.parse(localStorage.getItem('apiLogs') || '[]') + ); + const [isLogExpanded, setIsLogExpanded] = React.useState(false); + const formRefs = React.useRef({}); // Cookie操作函数 function setCookie(name, value, days) { @@ -390,19 +421,76 @@ if (userId) setCookie('userId', userId, 7); }, [userId]); - const handleApiCall = async (endpoint, method = 'GET', body = null) => { + const saveLog = (endpoint, request, response) => { + const newLog = { + timestamp: Date.now(), + endpoint, + request, + response, + date: new Date().toLocaleString() + }; + + const updatedLogs = [newLog, ...logs].slice(0, 50); // 只保留最近50条记录 + setLogs(updatedLogs); + localStorage.setItem('apiLogs', JSON.stringify(updatedLogs)); + }; + + const handleApiCall = async (endpoint, method = 'GET', formData = null, config) => { try { const options = { method, headers: { 'Content-Type': 'application/json' } }; - if (body) options.body = JSON.stringify(body); + + let url = `${apiBase}${endpoint}`; - const res = await fetch(`${apiBase}${endpoint}`, options); + // 处理GET请求参数 + if (method === 'GET' && config.queryParams) { + const params = new URLSearchParams(); + config.queryParams.forEach(param => { + if (formData[param]) params.append(param, formData[param]); + }); + url += `?${params.toString()}`; + } + + // 处理POST请求体 + if (method === 'POST') { + if (config.requestFormat === 'json') { + options.body = JSON.stringify(formData); + } + } + + const res = await fetch(url, options); const data = await res.json(); setResponse(JSON.stringify(data, null, 2)); + + // 保存日志 + saveLog(endpoint, formData, data); + + // 处理登录响应 + if (config.endpoint === '/qr' && data.userId) { + setUserId(data.userId.toString()); + setCookie('userId', data.userId.toString(), 7); + + // 遍历所有表单配置,更新包含userid/userId字段的表单 + Object.entries(formConfigs).forEach(([key, formConfig]) => { + const userIdField = formConfig.fields.find( + field => field.id.toLowerCase().includes('userid') + ); + if (userIdField && formRefs.current[key]) { + formRefs.current[key].updateField(userIdField.id, data.userId.toString()); + } + }); + } + + // 根据状态设置提示消息 + setSnackbarMessage(data.info || '操作成功'); + setSnackbarOpen(true); + } catch (error) { setResponse(`Error: ${error.message}`); + setSnackbarMessage(`错误: ${error.message}`); + setSnackbarOpen(true); } }; @@ -468,7 +556,7 @@ )) ); - setSnackbarMessage('后端URL已删除'); + setSnackbarMessage('后端已删除'); setSnackbarOpen(true); }; @@ -558,11 +646,13 @@ tabValue === config.tab.index && ( formRefs.current[key] = el} formConfig={config} onSubmit={(data) => handleApiCall( config.endpoint, config.method, - config.method === 'POST' ? data : null + data, + config )} /> ) @@ -585,7 +675,7 @@ )} - {/* 后端选择器 */} + {/* 移动后端选择器到底部 */} 后端设置 @@ -642,7 +732,78 @@ + + {/* 日志卡片 */} + +
setIsLogExpanded(!isLogExpanded)} + > + + 📝 API调用日志 + + + + {isLogExpanded ? 'expand_less' : 'expand_more'} + + +
+ +
+ {logs.map((log, index) => ( +
+ + {log.date} - {log.endpoint} + + + 请求: {JSON.stringify(log.request, null, 2)} + + + 响应: {JSON.stringify(log.response, null, 2)} + +
+ ))} +
+
+
+ + {/* 添加自定义后端的对话框 */} + + 添加自定义后端 + + setNewBackend(prev => ({...prev, label: e.target.value}))} + /> + setNewBackend(prev => ({...prev, value: e.target.value}))} + /> + + + + + + + + {/* 提示消息 */} + + + {snackbarMessage} + + ); }