212dbc4e1d
这是一次大型重构和功能新增: 1. 将原 zen_box 工具重命名为 web_zen_box,调整所有相关路径与配置 2. 新增 web_order_box 原生Web应用的完整资源与部署脚本 3. 新增私人剪贴板后端技能与配套测试脚本 4. 修复订单盒子APP的API地址配置,避免路径重复拼接 5. 新增本地开发跨域白名单支持localhost 6. 清理旧版zen_box的冗余文件
99 lines
3.1 KiB
TypeScript
99 lines
3.1 KiB
TypeScript
import axios from 'axios';
|
||
import { ApiEnvelope, MotionCard, MotionSession } from './types';
|
||
|
||
const API_BASE_URL = process.env.EXPO_PUBLIC_API_BASE_URL || '/api/app_order_box';
|
||
// 注意:MOTION_BASE 是相对于 axios baseURL 的路径,不要包含 API_BASE_URL 前缀
|
||
// 否则 axios 会双重拼接导致 404(如 /api/app_order_box/api/app_order_box/kits/...)
|
||
const MOTION_BASE = '/kits/motion_reminder';
|
||
|
||
const api = axios.create({
|
||
baseURL: API_BASE_URL,
|
||
timeout: 15000,
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
});
|
||
|
||
const formatError = (error: any): string => {
|
||
if (error?.response) {
|
||
return `服务器响应 ${error.response.status}: ${error.response.statusText}\n${JSON.stringify(error.response.data)}`;
|
||
}
|
||
if (error?.request) {
|
||
return `服务器无响应: 无法连接到 ${API_BASE_URL}`;
|
||
}
|
||
if (error?.message) {
|
||
return `请求错误: ${error.message}`;
|
||
}
|
||
return '未知错误';
|
||
};
|
||
|
||
export const motionReminderApi = {
|
||
async listCards(): Promise<MotionCard[]> {
|
||
try {
|
||
const response = await api.get<ApiEnvelope<MotionCard[]>>(`${MOTION_BASE}/cards`);
|
||
return response.data.data || [];
|
||
} catch (error) {
|
||
throw new Error(formatError(error));
|
||
}
|
||
},
|
||
|
||
async saveCard(card: Partial<MotionCard>): Promise<MotionCard> {
|
||
try {
|
||
const response = card.id
|
||
? await api.put<ApiEnvelope<MotionCard>>(`${MOTION_BASE}/cards`, card)
|
||
: await api.post<ApiEnvelope<MotionCard>>(`${MOTION_BASE}/cards`, card);
|
||
if (!response.data.ok || !response.data.data) {
|
||
throw new Error(response.data.error || '保存失败');
|
||
}
|
||
return response.data.data;
|
||
} catch (error) {
|
||
throw new Error(formatError(error));
|
||
}
|
||
},
|
||
|
||
async deleteCard(id: string): Promise<void> {
|
||
try {
|
||
await api.delete(`${MOTION_BASE}/cards`, { params: { id } });
|
||
} catch (error) {
|
||
throw new Error(formatError(error));
|
||
}
|
||
},
|
||
|
||
async listHistory(): Promise<MotionSession[]> {
|
||
try {
|
||
const response = await api.get<ApiEnvelope<MotionSession[]>>(`${MOTION_BASE}/history`);
|
||
return response.data.data || [];
|
||
} catch (error) {
|
||
throw new Error(formatError(error));
|
||
}
|
||
},
|
||
|
||
async startSession(card: MotionCard): Promise<MotionSession> {
|
||
try {
|
||
const response = await api.post<ApiEnvelope<MotionSession>>(`${MOTION_BASE}/sessions/start`, { card });
|
||
if (!response.data.ok || !response.data.data) {
|
||
throw new Error(response.data.error || '启动失败');
|
||
}
|
||
return response.data.data;
|
||
} catch (error) {
|
||
throw new Error(formatError(error));
|
||
}
|
||
},
|
||
|
||
async syncSession(payload: Partial<MotionSession> & { id: string }): Promise<void> {
|
||
try {
|
||
await api.post(`${MOTION_BASE}/sessions/sync`, payload);
|
||
} catch (error) {
|
||
throw new Error(formatError(error));
|
||
}
|
||
},
|
||
|
||
async finishSession(payload: Partial<MotionSession> & { id: string; status: 'completed' | 'stopped' }): Promise<void> {
|
||
try {
|
||
await api.post(`${MOTION_BASE}/sessions/finish`, payload);
|
||
} catch (error) {
|
||
throw new Error(formatError(error));
|
||
}
|
||
},
|
||
};
|