d4bf3627fd
新增运动节拍RepClock全套小程序页面、后端API与数据库服务,新增subhub子工具汇总页面,优化全局主页样式与配置,为web静态资源添加版本缓存参数,补充配套调试脚本
161 lines
5.7 KiB
JavaScript
161 lines
5.7 KiB
JavaScript
(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: Object.assign(
|
|
{
|
|
'Accept': 'application/json; charset=utf-8',
|
|
'X-Mp-Timeassistant': 'mp_web_time_assistant_v1_' + Math.random().toString(36).slice(2, 10),
|
|
},
|
|
opts.headers || {}
|
|
),
|
|
credentials: 'same-origin'
|
|
}
|
|
if (body !== null && body !== undefined) {
|
|
if (body instanceof FormData) {
|
|
init.body = body
|
|
} else {
|
|
init.headers['Content-Type'] = 'application/json; charset=utf-8'
|
|
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 : (payload || {})
|
|
},
|
|
async getState() {
|
|
const res = await request('GET', '/api/mp_time_assistant/state')
|
|
const payload = (res && res.data) || {}
|
|
return (payload && payload.ok) ? payload : (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 : (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_personal_collect/kits/personal_collect/items' + suffix)
|
|
const payload = (res && res.data) || {}
|
|
if (payload && payload.ok) return payload.items || payload.data || []
|
|
return Array.isArray(payload) ? payload : []
|
|
},
|
|
async get(id) {
|
|
const res = await request('GET', '/api/mp_personal_collect/kits/personal_collect/items/' + encodeURIComponent(id))
|
|
const payload = (res && res.data) || {}
|
|
return (payload && payload.ok) ? (payload.item || payload.data || null) : (payload || null)
|
|
},
|
|
async create(payload) {
|
|
const res = await request('POST', '/api/mp_personal_collect/kits/personal_collect/items', payload)
|
|
const body = (res && res.data) || {}
|
|
if (body && body.ok) return body.item || body.data || body
|
|
throw new Error((body && body.error) || '创建失败')
|
|
},
|
|
async update(payload) {
|
|
const res = await request('PUT', '/api/mp_personal_collect/kits/personal_collect/items', payload)
|
|
const body = (res && res.data) || {}
|
|
if (body && body.ok) return body.item || body.data || body
|
|
throw new Error((body && body.error) || '更新失败')
|
|
},
|
|
async remove(id) {
|
|
const q = new URLSearchParams({ id })
|
|
const res = await request('DELETE', '/api/mp_personal_collect/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_personal_collect/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
|
|
}
|
|
})()
|