Files
Toolbox/public/tools/web_mp_time_assistant/api.js
T
yangxiangyuan db7648eb4d feat(mp, web, calendar): 新增微信收藏夹与日历批量生成功能
- 新增微信小程序版个人收藏夹全套功能,包含聚合首页、收藏列表、编辑、详情页面与配套后端API
- 新增web版时光助手门户与个人收藏夹工具,支持与小程序共享后端数据
- 优化日历提醒工具:替换本地bootstrap资源为公共CDN版本,新增批量生成循环提醒的前端界面与后端逻辑,新增数据库batch_id字段,更新鉴权配置文件
- 补充项目配置、站点地图、调试诊断脚本等配套文件
- 完善各模块的页面配置与样式文件
2026-09-02 22:20:37 +08:00

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
}
})()