mirror of
https://github.com/KingHWJ/WeReadBoard.git
synced 2026-11-03 18:24:58 +08:00
153 lines
4.6 KiB
JavaScript
153 lines
4.6 KiB
JavaScript
/* ==========================================================================
|
||
阅间 · 数据层
|
||
--------------------------------------------------------------------------
|
||
把后端 /api/reading/* 接口封装成前端可直接使用的数据结构。
|
||
设计稿(Design Demo)原本使用 demo-data.js 里的静态示例数据,
|
||
这里替换为真实接口调用,字段名与设计稿保持一致。
|
||
========================================================================== */
|
||
(function (global) {
|
||
'use strict';
|
||
|
||
const BASE = '/api/reading';
|
||
const FALLBACK_BASE = '/api/' + 'v' + '2';
|
||
|
||
async function request(path, params, method = 'GET', body) {
|
||
const makeUrl = base => {
|
||
const url = new URL(base + path, global.location.origin);
|
||
Object.entries(params || {}).forEach(([key, value]) => {
|
||
if (value !== undefined && value !== null && value !== '') url.searchParams.set(key, value);
|
||
});
|
||
return url;
|
||
};
|
||
const send = base => fetch(makeUrl(base).toString(), {
|
||
method,
|
||
headers: { Accept: 'application/json', ...(body ? { 'Content-Type': 'application/json' } : {}) },
|
||
...(body ? { body: JSON.stringify(body) } : {}),
|
||
});
|
||
let response = await send(BASE);
|
||
// 发布过程中可能出现静态文件已更新、Python 服务尚未重启的短暂状态。
|
||
if (response.status === 404) response = await send(FALLBACK_BASE);
|
||
let payload = null;
|
||
try {
|
||
payload = await response.json();
|
||
} catch (error) {
|
||
payload = null;
|
||
}
|
||
if (!response.ok) {
|
||
const message = (payload && (payload.error || payload.detail)) || `请求失败(${response.status})`;
|
||
const err = new Error(message);
|
||
err.status = response.status;
|
||
err.payload = payload;
|
||
throw err;
|
||
}
|
||
return payload;
|
||
}
|
||
|
||
/* ------------------------------ 工具函数 ------------------------------ */
|
||
|
||
function durationText(seconds) {
|
||
const total = Math.max(0, Math.round(Number(seconds) || 0));
|
||
const hours = Math.floor(total / 3600);
|
||
const minutes = Math.round((total % 3600) / 60);
|
||
return hours ? `${hours}小时${minutes}分钟` : `${minutes}分钟`;
|
||
}
|
||
|
||
function percentText(value) {
|
||
const num = Number(value) || 0;
|
||
return `${num}%`;
|
||
}
|
||
|
||
/* ------------------------------ 接口封装 ------------------------------ */
|
||
|
||
const WeApi = {
|
||
durationText,
|
||
percentText,
|
||
|
||
status() {
|
||
return request('/status');
|
||
},
|
||
|
||
goals() {
|
||
return request('/goals');
|
||
},
|
||
|
||
saveGoal(goal, editing = false) {
|
||
return request(editing ? `/goals/${encodeURIComponent(goal.id)}` : '/goals', null, editing ? 'PUT' : 'POST', goal);
|
||
},
|
||
|
||
deleteGoal(id) {
|
||
return request(`/goals/${encodeURIComponent(id)}`, null, 'DELETE');
|
||
},
|
||
|
||
importGoals(goals) {
|
||
return request('/goals/import', null, 'POST', { goals });
|
||
},
|
||
|
||
/**
|
||
* 书架数据
|
||
* @param {string} tab all|reading|done|wish
|
||
* @param {{keyword?:string, limit?:number, offset?:number, progress?:boolean}} options
|
||
*/
|
||
shelf(tab, options) {
|
||
const opts = options || {};
|
||
return request('/shelf', {
|
||
tab: tab || 'all',
|
||
keyword: opts.keyword || '',
|
||
limit: opts.limit,
|
||
offset: opts.offset,
|
||
progress: opts.progress ? 1 : 0,
|
||
});
|
||
},
|
||
|
||
dashboard(period) {
|
||
return request('/dashboard', { period: period || 'week' });
|
||
},
|
||
|
||
notebooks() {
|
||
return request('/notebooks');
|
||
},
|
||
|
||
/** bookId 传 'all' 时聚合笔记最多的前 limit 本 */
|
||
notes(bookId, limit) {
|
||
return request('/notes', { book_id: bookId, limit: limit });
|
||
},
|
||
|
||
book(bookId) {
|
||
return request('/book/' + encodeURIComponent(bookId));
|
||
},
|
||
|
||
reviews(bookId, type, count) {
|
||
return request(`/book/${encodeURIComponent(bookId)}/reviews`, { type: type || 0, count: count || 10 });
|
||
},
|
||
|
||
highlights(bookId, chapterUid) {
|
||
return request(`/book/${encodeURIComponent(bookId)}/highlights`, { chapter_uid: chapterUid || 0 });
|
||
},
|
||
|
||
underlineThoughts(bookId, chapterUid, range) {
|
||
return request(`/book/${encodeURIComponent(bookId)}/underline-thoughts`, {
|
||
chapter_uid: chapterUid,
|
||
range: range,
|
||
});
|
||
},
|
||
|
||
thought(reviewId) {
|
||
return request('/thought/' + encodeURIComponent(reviewId));
|
||
},
|
||
|
||
archives() {
|
||
return request('/archives');
|
||
},
|
||
|
||
recommend(maxIdx, refresh = false) {
|
||
return request('/recommend', { maxIdx: maxIdx || 0, refresh: refresh ? 1 : 0 });
|
||
},
|
||
|
||
search(keyword, scope) {
|
||
return request('/search', { keyword: keyword, scope: scope || 'books' });
|
||
},
|
||
};
|
||
|
||
global.WeApi = WeApi;
|
||
})(window);
|