(function () { const isHttps = location.protocol === 'https:' const API_BASE = isHttps ? location.origin : 'http://localhost:8976' function buildUrl(path) { if (/^https?:/.test(path)) return path const p = path.startsWith('/') ? path : '/' + path return API_BASE + p } async function request(method, path, body, opts) { opts = opts || {} const url = buildUrl(path) const init = { method: method || 'GET', headers: opts.headers || {}, credentials: 'same-origin' } if (body !== null && body !== undefined) { if (body instanceof FormData) { init.body = body } else { init.headers['Content-Type'] = 'application/json' init.body = JSON.stringify(body) } } try { const res = await fetch(url, init) const text = await res.text() let data = null try { data = text ? JSON.parse(text) : null } catch { data = { ok: res.ok, raw: text } } if (!res.ok) { const msg = (data && data.error) || ('HTTP ' + res.status) const err = new Error(msg) err.status = res.status err.data = data throw err } return { ok: true, status: res.status, data } } catch (e) { const err = new Error(e.message || '请求失败') err.data = e.data || null throw err } } const hub = { async meta() { const res = await request('GET', '/api/mp_time_assistant/meta') const payload = (res && res.data) || {} return (payload && payload.ok) ? payload.data || {} : (payload || {}) }, async getState() { const res = await request('GET', '/api/mp_time_assistant/state') const payload = (res && res.data) || {} return (payload && payload.ok) ? payload.data || {} : (payload || {}) }, async setActiveKit(kitId) { const res = await request('PUT', '/api/mp_time_assistant/state/active-kit', { kitId }) const payload = (res && res.data) || {} return (payload && payload.ok) ? payload.data || {} : (payload || {}) } } const personalCollect = { async list(keyword) { const q = new URLSearchParams() if (keyword) q.set('keyword', keyword) const suffix = q.toString() ? '?' + q.toString() : '' const res = await request('GET', '/api/mp_time_assistant/kits/personal_collect/items' + suffix) const payload = (res && res.data) || {} if (payload && payload.ok) return payload.data || [] return Array.isArray(payload) ? payload : [] }, async get(id) { const res = await request('GET', '/api/mp_time_assistant/kits/personal_collect/items/' + encodeURIComponent(id)) const payload = (res && res.data) || {} return (payload && payload.ok) ? (payload.data || null) : (payload || null) }, async create(payload) { const res = await request('POST', '/api/mp_time_assistant/kits/personal_collect/items', payload) const body = (res && res.data) || {} if (body && body.ok) return body.data || body throw new Error((body && body.error) || '创建失败') }, async update(payload) { const res = await request('PUT', '/api/mp_time_assistant/kits/personal_collect/items', payload) const body = (res && res.data) || {} if (body && body.ok) return body.data || body throw new Error((body && body.error) || '更新失败') }, async remove(id) { const q = new URLSearchParams({ id }) const res = await request('DELETE', '/api/mp_time_assistant/kits/personal_collect/items?' + q.toString()) const body = (res && res.data) || {} if (body && body.ok) return body throw new Error((body && body.error) || '删除失败') }, async uploadFile(file, groupId, onProgress) { const form = new FormData() form.append('file', file) if (groupId) form.append('groupId', groupId) const url = buildUrl('/api/mp_time_assistant/kits/personal_collect/upload') return new Promise((resolve, reject) => { try { const xhr = new XMLHttpRequest() xhr.open('POST', url) xhr.withCredentials = true if (onProgress && typeof onProgress === 'function' && xhr.upload) { xhr.upload.addEventListener('progress', (e) => { if (e.lengthComputable) { onProgress({ loaded: e.loaded, total: e.total, percent: Math.round(e.loaded * 100 / Math.max(1, e.total)) }) } }) } xhr.onload = () => { try { const data = JSON.parse(xhr.responseText || '{}') if (xhr.status >= 200 && xhr.status < 300 && data && data.ok) { resolve({ ok: true, status: xhr.status, data }) } else { const err = new Error((data && data.error) || ('HTTP ' + xhr.status)) err.data = data reject(err) } } catch (e) { reject(new Error('响应解析失败: ' + e.message)) } } xhr.onerror = () => reject(new Error('网络错误:上传失败')) xhr.ontimeout = () => reject(new Error('上传超时')) xhr.send(form) } catch (e) { reject(e) } }) } } const fileUrl = (relPath) => { if (!relPath) return '' if (/^https?:/.test(relPath)) return relPath const p = relPath.startsWith('/') ? relPath : '/' + relPath return buildUrl(p) } window.__MPTA_API__ = { API_BASE, request, hub, personalCollect, fileUrl } })()