20260706170339
This commit is contained in:
1 parent
ae99f50f6d
commit
4d1e6c8b8f
50 files changed
+3045
-2249
No files matched your search
@@ -0,0 +1,111 @@
|
||||
import dayjs from "dayjs";
|
||||
import { FinanceAutoRepeatType } from "@/types/finance";
|
||||
|
||||
// 判断今天是否应该执行
|
||||
export function shouldRun(repeatType: number, repeatDate: string, lastRun: string | null | undefined): boolean {
|
||||
const today = dayjs();
|
||||
if (!lastRun) return true; // 从未执行过
|
||||
|
||||
const last = dayjs(lastRun);
|
||||
|
||||
switch (repeatType) {
|
||||
case FinanceAutoRepeatType.Daily:
|
||||
return !last.isSame(today, "day");
|
||||
|
||||
case FinanceAutoRepeatType.Weekly: {
|
||||
// repeatDate = "1,3,5" → 周一三五
|
||||
const days = (repeatDate || "").split(",").map(Number);
|
||||
if (days.length === 0 || !days.includes(today.day())) return false;
|
||||
return true; // 今天在指定周几中,且上次执行不是今天(由外层 last_run 检查保证)
|
||||
}
|
||||
|
||||
case FinanceAutoRepeatType.Monthly: {
|
||||
const dates = (repeatDate || "").split(",").map(Number);
|
||||
if (dates.length === 0 || !dates.includes(today.date())) return false;
|
||||
return !last.isSame(today, "month");
|
||||
}
|
||||
|
||||
case FinanceAutoRepeatType.Quarterly: {
|
||||
const dates = (repeatDate || "").split(",").map(Number);
|
||||
if (dates.length === 0 || !dates.includes(today.date())) return false;
|
||||
return last.quarter() !== today.quarter() || last.year() !== today.year();
|
||||
}
|
||||
|
||||
case FinanceAutoRepeatType.Yearly: {
|
||||
// repeatDate = "3,15" → 3月15日
|
||||
const parts = (repeatDate || "").split(",").map(Number);
|
||||
if (parts.length !== 2 || parts[0] !== today.month() + 1 || parts[1] !== today.date()) return false;
|
||||
return last.year() !== today.year();
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取应该补记账的日期列表(从上次执行到今天)
|
||||
export function getMissedDates(
|
||||
repeatType: number,
|
||||
repeatDate: string,
|
||||
lastRun: string | null | undefined,
|
||||
): string[] {
|
||||
const today = dayjs();
|
||||
if (!lastRun) return [today.format("YYYY-MM-DD")];
|
||||
|
||||
const last = dayjs(lastRun);
|
||||
const dates: string[] = [];
|
||||
let cursor = last.add(1, "day");
|
||||
|
||||
while (cursor.isBefore(today, "day") || cursor.isSame(today, "day")) {
|
||||
const d = cursor.format("YYYY-MM-DD");
|
||||
if (shouldRunOnDate(repeatType, repeatDate, cursor)) {
|
||||
dates.push(d);
|
||||
}
|
||||
cursor = cursor.add(1, "day");
|
||||
}
|
||||
|
||||
return dates;
|
||||
}
|
||||
|
||||
function shouldRunOnDate(repeatType: number, repeatDate: string, date: dayjs.Dayjs): boolean {
|
||||
switch (repeatType) {
|
||||
case FinanceAutoRepeatType.Daily:
|
||||
return true;
|
||||
case FinanceAutoRepeatType.Weekly: {
|
||||
const days = (repeatDate || "").split(",").map(Number);
|
||||
return days.includes(date.day());
|
||||
}
|
||||
case FinanceAutoRepeatType.Monthly: {
|
||||
const dates = (repeatDate || "").split(",").map(Number);
|
||||
return dates.includes(date.date());
|
||||
}
|
||||
case FinanceAutoRepeatType.Quarterly: {
|
||||
const dates = (repeatDate || "").split(",").map(Number);
|
||||
return dates.includes(date.date());
|
||||
}
|
||||
case FinanceAutoRepeatType.Yearly: {
|
||||
const parts = (repeatDate || "").split(",").map(Number);
|
||||
return parts.length === 2 && parts[0] === date.month() + 1 && parts[1] === date.date();
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const REPEAT_LABELS: Record<number, string> = {
|
||||
[FinanceAutoRepeatType.Daily]: "每天",
|
||||
[FinanceAutoRepeatType.Weekly]: "每周",
|
||||
[FinanceAutoRepeatType.Monthly]: "每月",
|
||||
[FinanceAutoRepeatType.Quarterly]: "每季度",
|
||||
[FinanceAutoRepeatType.Yearly]: "每年",
|
||||
};
|
||||
|
||||
export const WEEK_DAYS = [
|
||||
{ value: 1, label: "一" },
|
||||
{ value: 2, label: "二" },
|
||||
{ value: 3, label: "三" },
|
||||
{ value: 4, label: "四" },
|
||||
{ value: 5, label: "五" },
|
||||
{ value: 6, label: "六" },
|
||||
{ value: 0, label: "日" },
|
||||
];
|
||||
@@ -0,0 +1,52 @@
|
||||
import * as Notifications from "expo-notifications";
|
||||
|
||||
// -- 初始化配置 --
|
||||
Notifications.setNotificationHandler({
|
||||
handleNotification: async () => ({
|
||||
shouldShowAlert: true,
|
||||
shouldPlaySound: true,
|
||||
shouldSetBadge: false,
|
||||
}),
|
||||
});
|
||||
|
||||
// -- 请求权限 --
|
||||
export async function requestNotificationPermission(): Promise<boolean> {
|
||||
const { status } = await Notifications.requestPermissionsAsync();
|
||||
return status === "granted";
|
||||
}
|
||||
|
||||
// -- 预定提醒通知 --
|
||||
export async function scheduleTodoReminder(
|
||||
todoId: number,
|
||||
todoName: string,
|
||||
remindAt: string,
|
||||
) {
|
||||
if (!remindAt) return null;
|
||||
|
||||
const triggerDate = new Date(remindAt);
|
||||
if (triggerDate <= new Date()) return null; // 时间已过,不预定
|
||||
|
||||
return Notifications.scheduleNotificationAsync({
|
||||
content: {
|
||||
title: "⏰ 待办提醒",
|
||||
body: `「${todoName}」该处理了`,
|
||||
data: { todoId, type: "todo_reminder" },
|
||||
},
|
||||
trigger: { type: Notifications.SchedulableTriggerInputTypes.DATE, date: triggerDate },
|
||||
});
|
||||
}
|
||||
|
||||
// -- 取消提醒通知 --
|
||||
export async function cancelTodoReminder(identifier: string) {
|
||||
await Notifications.cancelScheduledNotificationAsync(identifier);
|
||||
}
|
||||
|
||||
// -- 取消某个todo的所有提醒 --
|
||||
export async function cancelTodoReminders(todoId: number) {
|
||||
const scheduled = await Notifications.getAllScheduledNotificationsAsync();
|
||||
for (const n of scheduled) {
|
||||
if ((n.content.data as any)?.todoId === todoId) {
|
||||
await Notifications.cancelScheduledNotificationAsync(n.identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user