mirror of
https://github.com/Zhuym07/Tsumugiboshi.git
synced 2025-05-20 04:27:28 +08:00
Compare commits
7 Commits
bfbab6be5d
...
f8a7da1959
Author | SHA1 | Date | |
---|---|---|---|
|
f8a7da1959 | ||
|
7a28bdb453 | ||
|
efc0c7cb50 | ||
|
f923ed0695 | ||
|
9ca6cde4b0 | ||
|
fc7c93de91 | ||
|
c7cb992208 |
224
index.html
224
index.html
@ -405,6 +405,27 @@
|
||||
);
|
||||
});
|
||||
|
||||
// 在 App 组件外部添加地址格式化函数
|
||||
const formatApiUrl = (url) => {
|
||||
if (!url) return '';
|
||||
|
||||
// 移除末尾的斜杠
|
||||
url = url.replace(/\/+$/, '');
|
||||
|
||||
// 如果不包含协议,添加 http://
|
||||
if (!/^https?:\/\//i.test(url)) {
|
||||
url = 'http://' + url;
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用 URL API 解析和规范化地址
|
||||
const urlObj = new URL(url);
|
||||
return urlObj.toString().replace(/\/+$/, '');
|
||||
} catch (e) {
|
||||
return url;
|
||||
}
|
||||
};
|
||||
|
||||
function App() {
|
||||
const [tabValue, setTabValue] = React.useState(0);
|
||||
const [qrInput, setQrInput] = React.useState('');
|
||||
@ -431,7 +452,7 @@
|
||||
...JSON.parse(localStorage.getItem('customBackends') || '[]')
|
||||
]);
|
||||
const [isDialogOpen, setIsDialogOpen] = React.useState(false);
|
||||
const [newBackend, setNewBackend] = React.useState({ label: '', value: '' });
|
||||
const [newBackend, setNewBackend] = React.useState({ label: '', value: '', formattedUrl: '' });
|
||||
const [snackbarOpen, setSnackbarOpen] = React.useState(false);
|
||||
const [snackbarMessage, setSnackbarMessage] = React.useState('');
|
||||
const [menuAnchorEl, setMenuAnchorEl] = React.useState(null);
|
||||
@ -441,6 +462,98 @@
|
||||
const [isLogExpanded, setIsLogExpanded] = React.useState(false);
|
||||
const formRefs = React.useRef({});
|
||||
|
||||
const handleApiCall = async (endpoint, method = "GET", formData = null, config) => {
|
||||
try {
|
||||
const options = {
|
||||
method,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include"
|
||||
};
|
||||
|
||||
let url = `${apiBase}${endpoint}`;
|
||||
|
||||
if (method === "GET" && config?.queryParams) {
|
||||
const params = new URLSearchParams();
|
||||
config.queryParams.forEach(param => {
|
||||
if (formData?.[param]) params.append(param, formData[param]);
|
||||
});
|
||||
url += `?${params.toString()}`;
|
||||
}
|
||||
|
||||
if (method === "POST" && config?.requestFormat === "json") {
|
||||
options.body = JSON.stringify(formData);
|
||||
}
|
||||
|
||||
const res = await fetch(url, options);
|
||||
const rawText = await res.text();
|
||||
|
||||
let responseData;
|
||||
try {
|
||||
responseData = JSON.parse(rawText);
|
||||
} catch {
|
||||
responseData = {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
rawResponse: rawText
|
||||
};
|
||||
}
|
||||
|
||||
// 将状态码添加到显示结果中
|
||||
const displayResponse = {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
...responseData
|
||||
};
|
||||
|
||||
setResponse(JSON.stringify(displayResponse, null, 2));
|
||||
|
||||
// 保存日志
|
||||
saveLog(endpoint, {
|
||||
method,
|
||||
url,
|
||||
...formData,
|
||||
status: res.status
|
||||
}, displayResponse);
|
||||
|
||||
// 特殊处理登录成功的情况
|
||||
if (config?.endpoint === "/qr" && responseData.userId) {
|
||||
setUserId(responseData.userId.toString());
|
||||
setCookie("userId", responseData.userId.toString(), 7);
|
||||
|
||||
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, responseData.userId.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 显示消息提示
|
||||
setSnackbarMessage(responseData.info || `请求完成 (${res.status} ${res.statusText})`);
|
||||
setSnackbarOpen(true);
|
||||
|
||||
} catch (error) {
|
||||
const errorResponse = {
|
||||
error: true,
|
||||
message: error.message,
|
||||
type: error.name
|
||||
};
|
||||
|
||||
setResponse(JSON.stringify(errorResponse, null, 2));
|
||||
setSnackbarMessage(`请求发生错误: ${error.message}`);
|
||||
setSnackbarOpen(true);
|
||||
|
||||
// 保存错误日志
|
||||
saveLog(endpoint, {
|
||||
method,
|
||||
url: `${apiBase}${endpoint}`,
|
||||
...formData
|
||||
}, errorResponse);
|
||||
}
|
||||
};
|
||||
|
||||
// Cookie操作函数
|
||||
function setCookie(name, value, days) {
|
||||
const d = new Date();
|
||||
@ -472,65 +585,6 @@
|
||||
localStorage.setItem('apiLogs', JSON.stringify(updatedLogs));
|
||||
};
|
||||
|
||||
const handleApiCall = async (endpoint, method = 'GET', formData = null, config) => {
|
||||
try {
|
||||
const options = {
|
||||
method,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
};
|
||||
|
||||
let url = `${apiBase}${endpoint}`;
|
||||
|
||||
// 处理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);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMusicChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setMusicData(prev => ({
|
||||
@ -554,7 +608,7 @@
|
||||
|
||||
const handleDialogClose = () => {
|
||||
setIsDialogOpen(false);
|
||||
setNewBackend({ label: '', value: '' });
|
||||
setNewBackend({ label: '', value: '', formattedUrl: '' });
|
||||
};
|
||||
|
||||
const handleSaveBackend = () => {
|
||||
@ -610,6 +664,12 @@
|
||||
handleMenuClose();
|
||||
};
|
||||
|
||||
const handleNewBackendChange = (e) => {
|
||||
const value = e.target.value;
|
||||
const formattedUrl = formatApiUrl(value);
|
||||
setNewBackend(prev => ({...prev, value: value, formattedUrl: formattedUrl}));
|
||||
};
|
||||
|
||||
// 加载表单配置
|
||||
const [formConfigs, setFormConfigs] = React.useState(null);
|
||||
|
||||
@ -759,6 +819,7 @@
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
@ -766,7 +827,21 @@
|
||||
onClick={handleAddBackend}
|
||||
>
|
||||
添加自定义后端
|
||||
</Button>
|
||||
</Button>
|
||||
|
||||
{/* 新增认证提示区域 */}
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center" mt={2}>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
⚠️部分后端地址受认证保护 使用前可能需要身份认证
|
||||
</Typography>
|
||||
<Button
|
||||
variant="text"
|
||||
color="primary"
|
||||
onClick={() => window.open(apiBase, '_blank')}
|
||||
>
|
||||
认证
|
||||
</Button>
|
||||
</Box>
|
||||
</FormControl>
|
||||
</Paper>
|
||||
|
||||
@ -822,12 +897,33 @@
|
||||
label="后端地址"
|
||||
fullWidth
|
||||
value={newBackend.value}
|
||||
onChange={(e) => setNewBackend(prev => ({...prev, value: e.target.value}))}
|
||||
onChange={handleNewBackendChange}
|
||||
placeholder="example.com:2333"
|
||||
helperText={
|
||||
<div style={{fontSize: '0.75rem', color: '#666'}}>
|
||||
支持的格式:<br/>
|
||||
- example.com<br/>
|
||||
- example.com:2333<br/>
|
||||
- http(s)://example.com<br/>
|
||||
{newBackend.value && (
|
||||
<>
|
||||
<br/>
|
||||
将被格式化为: <span style={{color: '#1976d2'}}>{newBackend.formattedUrl}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleDialogClose}>取消</Button>
|
||||
<Button onClick={handleSaveBackend} color="primary">保存</Button>
|
||||
<Button
|
||||
onClick={() => handleSaveBackend()}
|
||||
color="primary"
|
||||
disabled={!newBackend.label || !newBackend.value}
|
||||
>
|
||||
保存
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user