db7648eb4d
- 新增微信小程序版个人收藏夹全套功能,包含聚合首页、收藏列表、编辑、详情页面与配套后端API - 新增web版时光助手门户与个人收藏夹工具,支持与小程序共享后端数据 - 优化日历提醒工具:替换本地bootstrap资源为公共CDN版本,新增批量生成循环提醒的前端界面与后端逻辑,新增数据库batch_id字段,更新鉴权配置文件 - 补充项目配置、站点地图、调试诊断脚本等配套文件 - 完善各模块的页面配置与样式文件
155 lines
5.5 KiB
JavaScript
155 lines
5.5 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: 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
|
|
}
|
|
})()
|