diff --git a/src/MilitaryTrainingManagement/Controllers/AllocationsController.cs b/src/MilitaryTrainingManagement/Controllers/AllocationsController.cs index 66c80a5..29ab980 100644 --- a/src/MilitaryTrainingManagement/Controllers/AllocationsController.cs +++ b/src/MilitaryTrainingManagement/Controllers/AllocationsController.cs @@ -507,7 +507,7 @@ public class AllocationsController : BaseApiController /// 获取按单位汇总的上报数据 /// [HttpGet("distributions/{distributionId}/summary")] - public async Task GetReportSummaryByUnit(int distributionId, [FromQuery] string? period = null) + public async Task GetReportSummaryByUnit(int distributionId, [FromQuery] string? period = null, [FromQuery] string? startDate = null, [FromQuery] string? endDate = null) { var unitId = GetCurrentUnitId(); var unitLevel = GetCurrentUnitLevel(); @@ -521,23 +521,30 @@ public class AllocationsController : BaseApiController return NotFound(new { message = "配额分配记录不存在" }); // 计算时间范围 - DateTime? startDate = null; - DateTime? endDate = null; - if (!string.IsNullOrEmpty(period) && period != "all") + DateTime? queryStartDate = null; + DateTime? queryEndDate = null; + + if (period == "custom" && !string.IsNullOrEmpty(startDate) && !string.IsNullOrEmpty(endDate)) { - endDate = DateTime.Now; - startDate = period switch + // 自定义时间范围 + queryStartDate = DateTime.Parse(startDate); + queryEndDate = DateTime.Parse(endDate).AddDays(1).AddSeconds(-1); + } + else if (!string.IsNullOrEmpty(period) && period != "all") + { + queryEndDate = DateTime.Now; + queryStartDate = period switch { - "week" => endDate.Value.AddDays(-7), - "month" => endDate.Value.AddMonths(-1), - "halfYear" => endDate.Value.AddMonths(-6), - "year" => endDate.Value.AddYears(-1), + "week" => queryEndDate.Value.AddDays(-7), + "month" => queryEndDate.Value.AddMonths(-1), + "halfYear" => queryEndDate.Value.AddMonths(-6), + "year" => queryEndDate.Value.AddYears(-1), _ => null }; } // 获取按单位汇总的上报数据 - var summaries = await _allocationService.GetReportSummaryByUnitAsync(distributionId, unitId.Value, unitLevel.Value, startDate, endDate); + var summaries = await _allocationService.GetReportSummaryByUnitAsync(distributionId, unitId.Value, unitLevel.Value, queryStartDate, queryEndDate); return Ok(summaries); } @@ -548,7 +555,7 @@ public class AllocationsController : BaseApiController /// [HttpGet("{id}/consumption-stats")] [Authorize(Policy = "DivisionLevel")] - public async Task GetConsumptionStats(int id, [FromQuery] string period = "month") + public async Task GetConsumptionStats(int id, [FromQuery] string period = "month", [FromQuery] string? startDate = null, [FromQuery] string? endDate = null) { var unitId = GetCurrentUnitId(); var unitLevel = GetCurrentUnitLevel(); @@ -562,18 +569,30 @@ public class AllocationsController : BaseApiController return NotFound(new { message = "配额不存在" }); // 计算时间范围 - var endDate = DateTime.Now; - var startDate = period switch + DateTime queryEndDate; + DateTime queryStartDate; + + if (period == "custom" && !string.IsNullOrEmpty(startDate) && !string.IsNullOrEmpty(endDate)) { - "week" => endDate.AddDays(-7), - "month" => endDate.AddMonths(-1), - "halfYear" => endDate.AddMonths(-6), - "year" => endDate.AddYears(-1), - _ => endDate.AddMonths(-1) - }; + // 自定义时间范围 + queryStartDate = DateTime.Parse(startDate); + queryEndDate = DateTime.Parse(endDate).AddDays(1).AddSeconds(-1); // 包含结束日期当天 + } + else + { + queryEndDate = DateTime.Now; + queryStartDate = period switch + { + "week" => queryEndDate.AddDays(-7), + "month" => queryEndDate.AddMonths(-1), + "halfYear" => queryEndDate.AddMonths(-6), + "year" => queryEndDate.AddYears(-1), + _ => queryEndDate.AddMonths(-1) + }; + } // 获取按单位汇总的消耗统计 - var stats = await _allocationService.GetConsumptionStatsByPeriodAsync(id, startDate, endDate); + var stats = await _allocationService.GetConsumptionStatsByPeriodAsync(id, queryStartDate, queryEndDate); return Ok(new { @@ -582,8 +601,8 @@ public class AllocationsController : BaseApiController unit = allocation.Unit, totalQuota = allocation.TotalQuota, period, - startDate, - endDate, + startDate = queryStartDate, + endDate = queryEndDate, unitStats = stats }); } diff --git a/src/frontend/src/api/allocations.ts b/src/frontend/src/api/allocations.ts index baf88a6..9d1bf65 100644 --- a/src/frontend/src/api/allocations.ts +++ b/src/frontend/src/api/allocations.ts @@ -53,16 +53,23 @@ export const allocationsApi = { return response.data }, - async getReportSummaryByUnit(distributionId: number, period?: string): Promise { + async getReportSummaryByUnit(distributionId: number, period?: string, startDate?: string, endDate?: string): Promise { + const params: Record = {} + if (period) params.period = period + if (startDate) params.startDate = startDate + if (endDate) params.endDate = endDate const response = await apiClient.get(`/allocations/distributions/${distributionId}/summary`, { - params: period ? { period } : undefined + params: Object.keys(params).length > 0 ? params : undefined }) return response.data }, - async getConsumptionStats(allocationId: number, period: string): Promise { + async getConsumptionStats(allocationId: number, period: string, startDate?: string, endDate?: string): Promise { + const params: Record = { period } + if (startDate) params.startDate = startDate + if (endDate) params.endDate = endDate const response = await apiClient.get(`/allocations/${allocationId}/consumption-stats`, { - params: { period } + params }) return response.data } diff --git a/src/frontend/src/views/allocations/AllocationList.vue b/src/frontend/src/views/allocations/AllocationList.vue index 6fd5ae5..075ef98 100644 --- a/src/frontend/src/views/allocations/AllocationList.vue +++ b/src/frontend/src/views/allocations/AllocationList.vue @@ -258,13 +258,51 @@
查看时间范围: - - 本周 - 本月 - 半年 - 年度 + + 按周 + 按月 + 按半年 + 按年 全部 + + + +
@@ -382,13 +420,51 @@
查看时间范围: - - 本周 - 本月 - 半年 - 年度 + + 按周 + 按月 + 按半年 + 按年 全部 + + + +
@@ -505,6 +581,31 @@ const selectedDistribution = ref(null) const selectedPeriod = ref('all') const reportsPeriod = ref('all') +// 时间范围筛选相关 +const periodType = ref('all') +const selectedWeek = ref(null) +const selectedMonth = ref(null) +const selectedYear = ref(null) +const selectedHalfYear = ref('') + +// 上报记录时间范围筛选相关 +const reportsPeriodType = ref('all') +const reportsSelectedWeek = ref(null) +const reportsSelectedMonth = ref(null) +const reportsSelectedYear = ref(null) +const reportsSelectedHalfYear = ref('') + +// 生成半年选项(最近3年的上半年和下半年) +const halfYearOptions = computed(() => { + const options = [] + const currentYear = new Date().getFullYear() + for (let year = currentYear; year >= currentYear - 2; year--) { + options.push({ label: `${year}年下半年`, value: `${year}-H2` }) + options.push({ label: `${year}年上半年`, value: `${year}-H1` }) + } + return options +}) + const pagination = reactive({ pageNumber: 1, pageSize: 10, @@ -633,7 +734,7 @@ function getTotalDistributed(): number { function getTotalConsumed(): number { // 如果有按时间范围筛选的统计数据,使用统计数据 - if (authStore.canCreateAllocations && selectedPeriod.value !== 'all' && consumptionStats.value.length > 0) { + if (authStore.canCreateAllocations && consumptionStats.value.length > 0) { return consumptionStats.value.reduce((sum: number, s) => sum + s.totalConsumed, 0) } // 营部及以下级别使用后端计算的可见范围内的上报总和 @@ -656,23 +757,106 @@ function getTotalProgressPercentage(): number { // 根据时间范围筛选后的分配数据 const filteredDistributions = computed(() => { - if (!authStore.canCreateAllocations || selectedPeriod.value === 'all') { + if (!authStore.canCreateAllocations || (selectedPeriod.value === 'all' && periodType.value === 'all')) { return distributions.value } // 使用统计数据更新分配记录的消耗数据 - return distributions.value.map(d => { - const stat = consumptionStats.value.find((s: UnitConsumptionStat) => s.unitId === d.targetUnitId) - if (stat) { - return { - ...d, - actualCompletion: stat.totalConsumed, - completionRate: stat.completionRate + if (consumptionStats.value.length > 0) { + return distributions.value.map(d => { + const stat = consumptionStats.value.find((s: UnitConsumptionStat) => s.unitId === d.targetUnitId) + if (stat) { + return { + ...d, + actualCompletion: stat.totalConsumed, + completionRate: stat.completionRate + } } - } - return { ...d, actualCompletion: 0, completionRate: 0 } - }) + return { ...d, actualCompletion: 0, completionRate: 0 } + }) + } + return distributions.value }) +// 处理时间类型切换 +function handlePeriodTypeChange() { + // 重置选择 + selectedWeek.value = null + selectedMonth.value = null + selectedYear.value = null + selectedHalfYear.value = '' + + if (periodType.value === 'all') { + selectedPeriod.value = 'all' + consumptionStats.value = [] + } +} + +// 处理日期选择变化 +async function handleDateChange() { + if (!selectedAllocation.value) return + + let startDate: string | null = null + let endDate: string | null = null + + if (periodType.value === 'week' && selectedWeek.value) { + // 获取选中周的起止日期 + const date = new Date(selectedWeek.value) + const day = date.getDay() + const diff = date.getDate() - day + (day === 0 ? -6 : 1) // 周一 + const monday = new Date(date.setDate(diff)) + const sunday = new Date(monday) + sunday.setDate(monday.getDate() + 6) + startDate = formatDateParam(monday) + endDate = formatDateParam(sunday) + } else if (periodType.value === 'month' && selectedMonth.value) { + const date = new Date(selectedMonth.value) + const firstDay = new Date(date.getFullYear(), date.getMonth(), 1) + const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0) + startDate = formatDateParam(firstDay) + endDate = formatDateParam(lastDay) + } else if (periodType.value === 'halfYear' && selectedHalfYear.value) { + const [year, half] = selectedHalfYear.value.split('-') + if (half === 'H1') { + startDate = `${year}-01-01` + endDate = `${year}-06-30` + } else { + startDate = `${year}-07-01` + endDate = `${year}-12-31` + } + } else if (periodType.value === 'year' && selectedYear.value) { + const year = selectedYear.value.getFullYear() + startDate = `${year}-01-01` + endDate = `${year}-12-31` + } + + if (!startDate || !endDate) return + + selectedPeriod.value = 'custom' + loadingStats.value = true + try { + const response = await allocationsApi.getConsumptionStats( + selectedAllocation.value.id, + 'custom', + startDate, + endDate + ) + consumptionStats.value = response.unitStats + } catch (error) { + console.error('加载消耗统计失败', error) + ElMessage.error('加载消耗统计失败') + consumptionStats.value = [] + } finally { + loadingStats.value = false + } +} + +function formatDateParam(date: Date): string { + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + return `${year}-${month}-${day}` +} + async function handlePeriodChange() { if (!selectedAllocation.value || selectedPeriod.value === 'all') { consumptionStats.value = [] @@ -732,6 +916,11 @@ async function handleViewReports(distribution: AllocationDistribution) { showReportsDialog.value = true reportsTabActive.value = 'summary' reportsPeriod.value = 'all' + reportsPeriodType.value = 'all' + reportsSelectedWeek.value = null + reportsSelectedMonth.value = null + reportsSelectedYear.value = null + reportsSelectedHalfYear.value = '' loadingReports.value = true try { // 同时加载汇总数据和明细数据 @@ -756,6 +945,76 @@ function getReportsTotalConsumed(): number { return unitSummaries.value.reduce((sum, s) => sum + s.totalReported, 0) } +// 上报记录时间类型切换 +function handleReportsPeriodTypeChange() { + reportsSelectedWeek.value = null + reportsSelectedMonth.value = null + reportsSelectedYear.value = null + reportsSelectedHalfYear.value = '' + + if (reportsPeriodType.value === 'all') { + reportsPeriod.value = 'all' + handleReportsPeriodChange() + } +} + +// 上报记录日期选择变化 +async function handleReportsDateChange() { + if (!selectedDistribution.value) return + + let startDate: string | null = null + let endDate: string | null = null + + if (reportsPeriodType.value === 'week' && reportsSelectedWeek.value) { + const date = new Date(reportsSelectedWeek.value) + const day = date.getDay() + const diff = date.getDate() - day + (day === 0 ? -6 : 1) + const monday = new Date(date.setDate(diff)) + const sunday = new Date(monday) + sunday.setDate(monday.getDate() + 6) + startDate = formatDateParam(monday) + endDate = formatDateParam(sunday) + } else if (reportsPeriodType.value === 'month' && reportsSelectedMonth.value) { + const date = new Date(reportsSelectedMonth.value) + const firstDay = new Date(date.getFullYear(), date.getMonth(), 1) + const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0) + startDate = formatDateParam(firstDay) + endDate = formatDateParam(lastDay) + } else if (reportsPeriodType.value === 'halfYear' && reportsSelectedHalfYear.value) { + const [year, half] = reportsSelectedHalfYear.value.split('-') + if (half === 'H1') { + startDate = `${year}-01-01` + endDate = `${year}-06-30` + } else { + startDate = `${year}-07-01` + endDate = `${year}-12-31` + } + } else if (reportsPeriodType.value === 'year' && reportsSelectedYear.value) { + const year = reportsSelectedYear.value.getFullYear() + startDate = `${year}-01-01` + endDate = `${year}-12-31` + } + + if (!startDate || !endDate) return + + reportsPeriod.value = 'custom' + loadingReports.value = true + try { + const summaries = await allocationsApi.getReportSummaryByUnit( + selectedDistribution.value.id, + 'custom', + startDate, + endDate + ) + unitSummaries.value = summaries + } catch (error) { + console.error('加载上报记录失败', error) + ElMessage.error('加载上报记录失败') + } finally { + loadingReports.value = false + } +} + // 上报记录时间范围筛选变化 async function handleReportsPeriodChange() { if (!selectedDistribution.value) return @@ -807,6 +1066,11 @@ async function loadAllocations() { async function handleViewDistribution(allocation: MaterialAllocation) { selectedAllocation.value = allocation selectedPeriod.value = 'all' + periodType.value = 'all' + selectedWeek.value = null + selectedMonth.value = null + selectedYear.value = null + selectedHalfYear.value = '' consumptionStats.value = [] // 对于非师团级账号,只显示本单位及下级单位的分配记录 if (authStore.canCreateAllocations) {