@@ -132,6 +135,23 @@ const fetchDateTransactions = async (date) => {
}
};
+const getBalance = (transactions) => {
+ let balance = 0;
+ transactions.forEach(tx => {
+ if(tx.type === 1) {
+ balance += tx.amount;
+ } else if(tx.type === 0) {
+ balance -= tx.amount;
+ }
+ });
+
+ if(balance >= 0) {
+ return `结余
`;
+ }
+};
+
// 当月份显示时触发
const onMonthShow = ({ date }) => {
const year = date.getFullYear();
diff --git a/Web/src/views/PeriodicRecord.vue b/Web/src/views/PeriodicRecord.vue
new file mode 100644
index 0000000..f2ee956
--- /dev/null
+++ b/Web/src/views/PeriodicRecord.vue
@@ -0,0 +1,848 @@
+
+
+
+
+
diff --git a/Web/src/views/SettingView.vue b/Web/src/views/SettingView.vue
index 9e2aacf..7a148b4 100644
--- a/Web/src/views/SettingView.vue
+++ b/Web/src/views/SettingView.vue
@@ -3,18 +3,19 @@
+
@@ -22,6 +23,7 @@
+
@@ -53,6 +55,10 @@ const handleImportClick = (type) => {
fileInputRef.value?.click()
}
+const handlePeriodicRecord = () => {
+ router.push({ name: 'periodic-record' })
+}
+
/**
* 处理文件选择
*/
@@ -168,6 +174,7 @@ const handleLogout = async () => {
.detail-header {
padding: 16px 16px 5px 16px;
+ margin-bottom: 5px;
}
.detail-header p {
diff --git a/WebApi/Controllers/JobController.cs b/WebApi/Controllers/JobController.cs
new file mode 100644
index 0000000..82fb95d
--- /dev/null
+++ b/WebApi/Controllers/JobController.cs
@@ -0,0 +1,175 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Quartz;
+
+namespace WebApi.Controllers;
+
+///
+/// 定时任务管理控制器
+///
+[ApiController]
+[Route("api/[controller]")]
+public class JobController(
+ ISchedulerFactory schedulerFactory,
+ ILogger
logger) : ControllerBase
+{
+ ///
+ /// 手动触发邮件同步任务
+ ///
+ [HttpPost("sync-email")]
+ [Authorize]
+ public async Task TriggerEmailSync()
+ {
+ try
+ {
+ var scheduler = await schedulerFactory.GetScheduler();
+ var jobKey = new JobKey("EmailSyncJob");
+
+ // 立即触发任务
+ await scheduler.TriggerJob(jobKey);
+
+ logger.LogInformation("手动触发邮件同步任务成功");
+ return Ok(new { message = "邮件同步任务已触发" });
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "触发邮件同步任务失败");
+ return StatusCode(500, new { message = "触发任务失败", error = ex.Message });
+ }
+ }
+
+ ///
+ /// 手动触发周期性账单任务
+ ///
+ [HttpPost("periodic-bill")]
+ [Authorize]
+ public async Task TriggerPeriodicBill()
+ {
+ try
+ {
+ var scheduler = await schedulerFactory.GetScheduler();
+ var jobKey = new JobKey("PeriodicBillJob");
+
+ // 立即触发任务
+ await scheduler.TriggerJob(jobKey);
+
+ logger.LogInformation("手动触发周期性账单任务成功");
+ return Ok(new { message = "周期性账单任务已触发" });
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "触发周期性账单任务失败");
+ return StatusCode(500, new { message = "触发任务失败", error = ex.Message });
+ }
+ }
+
+ ///
+ /// 获取所有任务的状态
+ ///
+ [HttpGet("status")]
+ [Authorize]
+ public async Task GetJobStatus()
+ {
+ try
+ {
+ var scheduler = await schedulerFactory.GetScheduler();
+ var jobGroups = await scheduler.GetJobGroupNames();
+ var jobStatuses = new List