optimize: api resp size.
Some checks failed
Build and Push Docker Image / buildx (push) Has been cancelled
Some checks failed
Build and Push Docker Image / buildx (push) Has been cancelled
This commit is contained in:
@@ -53,14 +53,22 @@ export async function fetchCurrentUser(server, token) {
|
|||||||
return request(server, '/api/auth/me', { token });
|
return request(server, '/api/auth/me', { token });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchCalendar(server, token) {
|
export async function fetchCalendar(server, token, year, month) {
|
||||||
return request(server, '/api/calendar', { token });
|
const params = new URLSearchParams();
|
||||||
|
if (year !== undefined && month !== undefined) {
|
||||||
|
params.append('year', year);
|
||||||
|
params.append('month', month);
|
||||||
|
}
|
||||||
|
const queryString = params.toString();
|
||||||
|
const path = queryString ? `/api/calendar?${queryString}` : '/api/calendar';
|
||||||
|
return request(server, path, { token });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveCalendar(server, token, markedDates) {
|
|
||||||
|
export async function saveCalendar(server, token, markedDates, deletedDates) {
|
||||||
return request(server, '/api/calendar', {
|
return request(server, '/api/calendar', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
token,
|
token,
|
||||||
body: { markedDates }
|
body: { markedDates, deletedDates }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export default class CalendarView {
|
|||||||
this.prevBtn.addEventListener('click', () => {
|
this.prevBtn.addEventListener('click', () => {
|
||||||
state.currentDate.setMonth(state.currentDate.getMonth() - 1);
|
state.currentDate.setMonth(state.currentDate.getMonth() - 1);
|
||||||
this.renderCalendar();
|
this.renderCalendar();
|
||||||
|
// Load data for the new month
|
||||||
|
this.loadData();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,6 +35,8 @@ export default class CalendarView {
|
|||||||
this.nextBtn.addEventListener('click', () => {
|
this.nextBtn.addEventListener('click', () => {
|
||||||
state.currentDate.setMonth(state.currentDate.getMonth() + 1);
|
state.currentDate.setMonth(state.currentDate.getMonth() + 1);
|
||||||
this.renderCalendar();
|
this.renderCalendar();
|
||||||
|
// Load data for the new month
|
||||||
|
this.loadData();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +44,8 @@ export default class CalendarView {
|
|||||||
this.todayBtn.addEventListener('click', () => {
|
this.todayBtn.addEventListener('click', () => {
|
||||||
state.currentDate = new Date();
|
state.currentDate = new Date();
|
||||||
this.renderCalendar();
|
this.renderCalendar();
|
||||||
|
// Load data for the current month
|
||||||
|
this.loadData();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,21 +57,12 @@ export default class CalendarView {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
showToast('正在加载...', 'info');
|
showToast('正在加载...', 'info');
|
||||||
const result = await fetchCalendar(state.currentServer, state.token);
|
// Pass current year and month to only fetch data for the displayed month
|
||||||
// Convert the new data structure to the old one for backward compatibility
|
const year = state.currentDate.getFullYear();
|
||||||
const markedDates = {};
|
const month = state.currentDate.getMonth() + 1; // getMonth() returns 0-11, need 1-12
|
||||||
for (const [date, data] of Object.entries(result.markedDates || {})) {
|
const result = await fetchCalendar(state.currentServer, state.token, year, month);
|
||||||
if (typeof data === 'string') {
|
// Merge with existing data
|
||||||
// Old format - just status
|
Object.assign(state.markedDates, result.markedDates || {});
|
||||||
markedDates[date] = data;
|
|
||||||
} else if (data && typeof data === 'object') {
|
|
||||||
// New format - object with status and updatedAt
|
|
||||||
markedDates[date] = data.status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.markedDates = markedDates;
|
|
||||||
// Store the full data including timestamps
|
|
||||||
state.markedDatesWithTimestamps = result.markedDates || {};
|
|
||||||
this.renderCalendar();
|
this.renderCalendar();
|
||||||
showToast('数据加载成功', 'success');
|
showToast('数据加载成功', 'success');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -82,7 +79,7 @@ export default class CalendarView {
|
|||||||
|
|
||||||
clearData() {
|
clearData() {
|
||||||
state.markedDates = {};
|
state.markedDates = {};
|
||||||
state.markedDatesWithTimestamps = {};
|
state.deletedDates = [];
|
||||||
this.renderCalendar();
|
this.renderCalendar();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +304,8 @@ export default class CalendarView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const dateKey = this.formatDate(date);
|
const dateKey = this.formatDate(date);
|
||||||
const currentStatus = state.markedDates[dateKey] || 'none';
|
const dateData = state.markedDates[dateKey];
|
||||||
|
const currentStatus = (dateData && dateData.status) || 'none';
|
||||||
let nextStatus;
|
let nextStatus;
|
||||||
|
|
||||||
switch (currentStatus) {
|
switch (currentStatus) {
|
||||||
@@ -325,11 +323,21 @@ export default class CalendarView {
|
|||||||
|
|
||||||
if (nextStatus === 'none') {
|
if (nextStatus === 'none') {
|
||||||
delete state.markedDates[dateKey];
|
delete state.markedDates[dateKey];
|
||||||
|
// Track this deletion
|
||||||
|
if (!state.deletedDates.includes(dateKey)) {
|
||||||
|
state.deletedDates.push(dateKey);
|
||||||
|
}
|
||||||
// Clear pending tooltip for removed marks
|
// Clear pending tooltip for removed marks
|
||||||
this.pendingTooltipDate = null;
|
this.pendingTooltipDate = null;
|
||||||
this.hideTooltip();
|
this.hideTooltip();
|
||||||
} else {
|
} else {
|
||||||
state.markedDates[dateKey] = nextStatus;
|
// Store status, timestamp will be updated by server on save
|
||||||
|
state.markedDates[dateKey] = { status: nextStatus };
|
||||||
|
// Remove from deleted dates if it was there
|
||||||
|
const deleteIndex = state.deletedDates.indexOf(dateKey);
|
||||||
|
if (deleteIndex !== -1) {
|
||||||
|
state.deletedDates.splice(deleteIndex, 1);
|
||||||
|
}
|
||||||
// Store the date for tooltip after save completes
|
// Store the date for tooltip after save completes
|
||||||
this.pendingTooltipDate = dateKey;
|
this.pendingTooltipDate = dateKey;
|
||||||
}
|
}
|
||||||
@@ -353,7 +361,17 @@ export default class CalendarView {
|
|||||||
async persistMarkedDates() {
|
async persistMarkedDates() {
|
||||||
state.saveTimeoutId = null;
|
state.saveTimeoutId = null;
|
||||||
try {
|
try {
|
||||||
await saveCalendar(state.currentServer, state.token, state.markedDates);
|
// Convert state format { date: { status, updatedAt } } to API format { date: status }
|
||||||
|
const markedDatesForAPI = {};
|
||||||
|
for (const [date, data] of Object.entries(state.markedDates)) {
|
||||||
|
if (data && typeof data === 'object' && data.status) {
|
||||||
|
markedDatesForAPI[date] = data.status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await saveCalendar(state.currentServer, state.token, markedDatesForAPI, state.deletedDates);
|
||||||
|
// Clear the deleted dates array after successful save
|
||||||
|
state.deletedDates = [];
|
||||||
showToast('数据已保存', 'success');
|
showToast('数据已保存', 'success');
|
||||||
// Reload data to get updated timestamps
|
// Reload data to get updated timestamps
|
||||||
await this.loadData();
|
await this.loadData();
|
||||||
@@ -384,12 +402,16 @@ export default class CalendarView {
|
|||||||
|
|
||||||
getDateStatus(date) {
|
getDateStatus(date) {
|
||||||
const dateKey = this.formatDate(date);
|
const dateKey = this.formatDate(date);
|
||||||
return state.markedDates[dateKey] || 'none';
|
const dateData = state.markedDates[dateKey];
|
||||||
|
if (dateData && typeof dateData === 'object') {
|
||||||
|
return dateData.status || 'none';
|
||||||
|
}
|
||||||
|
return 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
getDateTimestamp(date) {
|
getDateTimestamp(date) {
|
||||||
const dateKey = this.formatDate(date);
|
const dateKey = this.formatDate(date);
|
||||||
const dateData = state.markedDatesWithTimestamps[dateKey];
|
const dateData = state.markedDates[dateKey];
|
||||||
if (dateData && typeof dateData === 'object' && dateData.updatedAt) {
|
if (dateData && typeof dateData === 'object' && dateData.updatedAt) {
|
||||||
return dateData.updatedAt;
|
return dateData.updatedAt;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const state = {
|
const state = {
|
||||||
currentDate: new Date(),
|
currentDate: new Date(),
|
||||||
markedDates: {},
|
markedDates: {}, // Now stores objects with { status, updatedAt }
|
||||||
markedDatesWithTimestamps: {},
|
deletedDates: [], // Track dates that have been explicitly deleted
|
||||||
currentServer: '',
|
currentServer: '',
|
||||||
token: '',
|
token: '',
|
||||||
user: null,
|
user: null,
|
||||||
|
|||||||
@@ -5,11 +5,39 @@ const router = express.Router();
|
|||||||
|
|
||||||
router.get('/', (req, res) => {
|
router.get('/', (req, res) => {
|
||||||
const userId = req.user.userId;
|
const userId = req.user.userId;
|
||||||
|
const { year, month } = req.query;
|
||||||
|
|
||||||
db.all(
|
// Build query based on whether year and month are provided
|
||||||
'SELECT date, status, updated_at FROM calendar_marks WHERE user_id = ?',
|
let query = 'SELECT date, status, updated_at FROM calendar_marks WHERE user_id = ?';
|
||||||
[userId],
|
const params = [userId];
|
||||||
(err, rows) => {
|
|
||||||
|
// If year and month are provided, filter by that month plus adjacent months
|
||||||
|
if (year && month) {
|
||||||
|
const yearNum = parseInt(year, 10);
|
||||||
|
const monthNum = parseInt(month, 10);
|
||||||
|
|
||||||
|
// Validate year and month
|
||||||
|
if (isNaN(yearNum) || isNaN(monthNum) || monthNum < 1 || monthNum > 12) {
|
||||||
|
return res.status(400).json({ success: false, error: '无效的年份或月份' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the date range: previous month to next month
|
||||||
|
// This covers all dates that might be visible in the calendar view
|
||||||
|
const prevMonth = monthNum === 1 ? 12 : monthNum - 1;
|
||||||
|
const prevYear = monthNum === 1 ? yearNum - 1 : yearNum;
|
||||||
|
const nextMonth = monthNum === 12 ? 1 : monthNum + 1;
|
||||||
|
const nextYear = monthNum === 12 ? yearNum + 1 : yearNum;
|
||||||
|
|
||||||
|
const startDate = `${prevYear}-${String(prevMonth).padStart(2, '0')}-01`;
|
||||||
|
// Get the last day of next month
|
||||||
|
const lastDayOfNextMonth = new Date(nextYear, nextMonth, 0).getDate();
|
||||||
|
const endDate = `${nextYear}-${String(nextMonth).padStart(2, '0')}-${String(lastDayOfNextMonth).padStart(2, '0')}`;
|
||||||
|
|
||||||
|
query += ' AND date >= ? AND date <= ?';
|
||||||
|
params.push(startDate, endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
db.all(query, params, (err, rows) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return res.status(500).json({ success: false, error: '获取数据失败' });
|
return res.status(500).json({ success: false, error: '获取数据失败' });
|
||||||
}
|
}
|
||||||
@@ -23,13 +51,12 @@ router.get('/', (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
res.json({ success: true, markedDates });
|
res.json({ success: true, markedDates });
|
||||||
}
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/', (req, res) => {
|
router.post('/', (req, res) => {
|
||||||
const userId = req.user.userId;
|
const userId = req.user.userId;
|
||||||
const { markedDates } = req.body || {};
|
const { markedDates, deletedDates } = req.body || {};
|
||||||
|
|
||||||
if (!markedDates || typeof markedDates !== 'object') {
|
if (!markedDates || typeof markedDates !== 'object') {
|
||||||
return res.status(400).json({ success: false, error: '无效的数据格式' });
|
return res.status(400).json({ success: false, error: '无效的数据格式' });
|
||||||
@@ -40,8 +67,12 @@ router.post('/', (req, res) => {
|
|||||||
return new Date().toISOString();
|
return new Date().toISOString();
|
||||||
};
|
};
|
||||||
|
|
||||||
const dbOperations = Object.entries(markedDates).map(([date, status]) => {
|
const dbOperations = [];
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
|
// Update or insert marked dates
|
||||||
|
Object.entries(markedDates).forEach(([date, status]) => {
|
||||||
|
dbOperations.push(
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
// First check if this date exists and if status has changed
|
// First check if this date exists and if status has changed
|
||||||
db.get(
|
db.get(
|
||||||
'SELECT status FROM calendar_marks WHERE user_id = ? AND date = ?',
|
'SELECT status FROM calendar_marks WHERE user_id = ? AND date = ?',
|
||||||
@@ -85,30 +116,18 @@ router.post('/', (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const dates = Object.keys(markedDates);
|
|
||||||
if (dates.length > 0) {
|
|
||||||
const placeholders = dates.map(() => '?').join(',');
|
|
||||||
dbOperations.push(
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
db.run(
|
|
||||||
`DELETE FROM calendar_marks WHERE user_id = ? AND date NOT IN (${placeholders})`,
|
|
||||||
[userId, ...dates],
|
|
||||||
(err) => {
|
|
||||||
if (err) reject(err);
|
|
||||||
else resolve();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else {
|
});
|
||||||
|
|
||||||
|
// Delete explicitly removed dates
|
||||||
|
if (deletedDates && Array.isArray(deletedDates) && deletedDates.length > 0) {
|
||||||
|
const placeholders = deletedDates.map(() => '?').join(',');
|
||||||
dbOperations.push(
|
dbOperations.push(
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
db.run(
|
db.run(
|
||||||
'DELETE FROM calendar_marks WHERE user_id = ?',
|
`DELETE FROM calendar_marks WHERE user_id = ? AND date IN (${placeholders})`,
|
||||||
[userId],
|
[userId, ...deletedDates],
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err) reject(err);
|
if (err) reject(err);
|
||||||
else resolve();
|
else resolve();
|
||||||
|
|||||||
Reference in New Issue
Block a user