feat: 添加立即生成日报功能,支持预设时间和自定义时间
新增功能: 1. ✅ 预设时间选项(7种常用时间范围) - 昨天(前一天早8点到今天早8点) - 最近7天 - 最近30天 - 本周(本周一早8点起) - 上周(上周一早8点到上周日早8点) - 本月(本月1号早8点起) - 上月(上月1号早8点到上月最后一天) 2. ✅ 自定义时间范围选择器 - 灵活选择任意时间范围 - 开始日期(早8点)和结束日期(早8点) - 所有时间以早8点为基准 3. ✅ 前端UI改进 - 添加时间模式切换按钮(预设时间/自定义时间) - 根据选择模式显示对应的选择器 - 清晰的提示说明 4. ✅ 后端API扩展 - 支持start_time和end_time参数 - 保留report_date兼容旧接口 - 灵活的时间范围计算 5. ✅ 测试验证 - 静态检查通过 - API测试成功(生成35个订单的日报) - 求和功能正常(充电电量: 335159.3) 修改文件: - src/app/page.tsx - 前端时间选择UI - src/app/api/report/generate/route.ts - API支持时间参数 - src/lib/report-generator.ts - 生成器支持时间范围 用户可以在日报生成页面灵活选择时间范围,立即生成任意时间段的历史日报! Coze-Commit-Type: user Coze-User-ID: 3722323274763196 Coze-Conversation-ID: 9894087
This commit is contained in:
@@ -11,7 +11,9 @@ export async function POST(request: NextRequest) {
|
||||
split_name,
|
||||
selected_fields,
|
||||
sum_fields,
|
||||
report_date,
|
||||
start_time, // 新增:开始时间(早8点)
|
||||
end_time, // 新增:结束时间(早8点)
|
||||
report_date, // 保留兼容性:单日期参数
|
||||
config_id
|
||||
} = body;
|
||||
|
||||
@@ -39,7 +41,9 @@ export async function POST(request: NextRequest) {
|
||||
split_name,
|
||||
selected_fields,
|
||||
sum_fields,
|
||||
report_date
|
||||
start_time, // 传递开始时间
|
||||
end_time, // 传递结束时间
|
||||
report_date // 兼容旧参数
|
||||
);
|
||||
|
||||
return NextResponse.json({
|
||||
|
||||
182
src/app/page.tsx
182
src/app/page.tsx
@@ -35,7 +35,12 @@ export default function Home() {
|
||||
const [reportSplitName, setReportSplitName] = useState('');
|
||||
const [reportSelectedFields, setReportSelectedFields] = useState<string[]>([]);
|
||||
const [reportSumFields, setReportSumFields] = useState<string[]>([]);
|
||||
const [reportDate, setReportDate] = useState('');
|
||||
|
||||
// 时间选择
|
||||
const [timeMode, setTimeMode] = useState<'preset' | 'custom'>('preset');
|
||||
const [presetTime, setPresetTime] = useState('yesterday');
|
||||
const [customStartTime, setCustomStartTime] = useState('');
|
||||
const [customEndTime, setCustomEndTime] = useState('');
|
||||
|
||||
// 历史查询
|
||||
const [historyPage, setHistoryPage] = useState(1);
|
||||
@@ -240,6 +245,74 @@ export default function Home() {
|
||||
}
|
||||
};
|
||||
|
||||
// 计算预设时间范围的函数
|
||||
const calculateTimeRange = (preset: string) => {
|
||||
const now = new Date();
|
||||
const today8AM = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 8, 0, 0);
|
||||
|
||||
let startTime: Date;
|
||||
let endTime: Date = today8AM;
|
||||
|
||||
switch (preset) {
|
||||
case 'yesterday':
|
||||
// 昨天:前一天早8点到当天早8点
|
||||
startTime = new Date(today8AM);
|
||||
startTime.setDate(startTime.getDate() - 1);
|
||||
break;
|
||||
|
||||
case 'last7Days':
|
||||
// 最近7天:7天前早8点到今天早8点
|
||||
startTime = new Date(today8AM);
|
||||
startTime.setDate(startTime.getDate() - 7);
|
||||
break;
|
||||
|
||||
case 'last30Days':
|
||||
// 最近30天:30天前早8点到今天早8点
|
||||
startTime = new Date(today8AM);
|
||||
startTime.setDate(startTime.getDate() - 30);
|
||||
break;
|
||||
|
||||
case 'thisWeek':
|
||||
// 本周:本周一早8点到今天早8点
|
||||
const dayOfWeek = now.getDay();
|
||||
const mondayOffset = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
|
||||
startTime = new Date(today8AM);
|
||||
startTime.setDate(startTime.getDate() + mondayOffset);
|
||||
break;
|
||||
|
||||
case 'lastWeek':
|
||||
// 上周:上周一早8点到上周日早8点
|
||||
const lastWeekEnd = new Date(today8AM);
|
||||
const lastWeekDayOfWeek = now.getDay();
|
||||
const lastWeekMondayOffset = lastWeekDayOfWeek === 0 ? -6 : 1 - lastWeekDayOfWeek;
|
||||
startTime = new Date(today8AM);
|
||||
startTime.setDate(startTime.getDate() + lastWeekMondayOffset - 7);
|
||||
endTime = new Date(startTime);
|
||||
endTime.setDate(endTime.getDate() + 7);
|
||||
break;
|
||||
|
||||
case 'thisMonth':
|
||||
// 本月:本月1号早8点到今天早8点
|
||||
startTime = new Date(now.getFullYear(), now.getMonth(), 1, 8, 0, 0);
|
||||
break;
|
||||
|
||||
case 'lastMonth':
|
||||
// 上月:上月1号早8点到上月最后一天早8点
|
||||
startTime = new Date(now.getFullYear(), now.getMonth() - 1, 1, 8, 0, 0);
|
||||
endTime = new Date(now.getFullYear(), now.getMonth(), 1, 8, 0, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
startTime = new Date(today8AM);
|
||||
startTime.setDate(startTime.getDate() - 1);
|
||||
}
|
||||
|
||||
return {
|
||||
start_time: startTime.toISOString().split('T')[0] + ' 08:00:00',
|
||||
end_time: endTime.toISOString().split('T')[0] + ' 08:00:00'
|
||||
};
|
||||
};
|
||||
|
||||
const handleGenerateReport = async () => {
|
||||
if (!reportSplitValue || reportSelectedFields.length === 0) {
|
||||
toast({
|
||||
@@ -250,8 +323,28 @@ export default function Home() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeMode === 'custom' && (!customStartTime || !customEndTime)) {
|
||||
toast({
|
||||
title: '错误',
|
||||
description: '请选择自定义时间范围的开始时间和结束时间',
|
||||
variant: 'destructive'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
// 计算时间范围
|
||||
let timeRange: { start_time: string; end_time: string };
|
||||
if (timeMode === 'preset') {
|
||||
timeRange = calculateTimeRange(presetTime);
|
||||
} else {
|
||||
timeRange = {
|
||||
start_time: customStartTime + ' 08:00:00',
|
||||
end_time: customEndTime + ' 08:00:00'
|
||||
};
|
||||
}
|
||||
|
||||
const res = await fetch('/api/report/generate', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -261,7 +354,8 @@ export default function Home() {
|
||||
split_name: reportSplitName,
|
||||
selected_fields: reportSelectedFields,
|
||||
sum_fields: reportSumFields,
|
||||
report_date: reportDate
|
||||
start_time: timeRange.start_time,
|
||||
end_time: timeRange.end_time
|
||||
})
|
||||
});
|
||||
|
||||
@@ -777,14 +871,82 @@ export default function Home() {
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="reportDate">报表日期(可选,默认昨天)</Label>
|
||||
<Input
|
||||
id="reportDate"
|
||||
type="date"
|
||||
value={reportDate}
|
||||
onChange={(e) => setReportDate(e.target.value)}
|
||||
/>
|
||||
{/* 时间选择部分 */}
|
||||
<div className="space-y-3">
|
||||
<Label>时间范围</Label>
|
||||
|
||||
{/* 时间模式切换 */}
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant={timeMode === 'preset' ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setTimeMode('preset')}
|
||||
>
|
||||
预设时间
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant={timeMode === 'custom' ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
onClick={() => setTimeMode('custom')}
|
||||
>
|
||||
自定义时间
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* 预设时间选择器 */}
|
||||
{timeMode === 'preset' && (
|
||||
<div>
|
||||
<Label htmlFor="presetTime">选择预设时间范围</Label>
|
||||
<Select value={presetTime} onValueChange={setPresetTime}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="yesterday">昨天(前一天早8点到今天早8点)</SelectItem>
|
||||
<SelectItem value="last7Days">最近7天</SelectItem>
|
||||
<SelectItem value="last30Days">最近30天</SelectItem>
|
||||
<SelectItem value="thisWeek">本周(本周一早8点到今天早8点)</SelectItem>
|
||||
<SelectItem value="lastWeek">上周(上周一早8点到上周日早8点)</SelectItem>
|
||||
<SelectItem value="thisMonth">本月(本月1号早8点到今天早8点)</SelectItem>
|
||||
<SelectItem value="lastMonth">上月(上月1号早8点到上月最后一天早8点)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
所有时间范围均以早8点为基准
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 自定义时间范围选择器 */}
|
||||
{timeMode === 'custom' && (
|
||||
<div className="space-y-3">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<Label htmlFor="customStartTime">开始时间(早8点)</Label>
|
||||
<Input
|
||||
id="customStartTime"
|
||||
type="date"
|
||||
value={customStartTime}
|
||||
onChange={(e) => setCustomStartTime(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="customEndTime">结束时间(早8点)</Label>
|
||||
<Input
|
||||
id="customEndTime"
|
||||
type="date"
|
||||
value={customEndTime}
|
||||
onChange={(e) => setCustomEndTime(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
时间范围:从开始日期早8点到结束日期早8点
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -37,19 +37,39 @@ export async function generateDailyReport(
|
||||
splitName: string,
|
||||
selectedFields: string[],
|
||||
sumFields: string[] = [],
|
||||
reportDate?: string // 可选的报表日期,默认为昨天
|
||||
startTimeStr?: string, // 新增:开始时间字符串(早8点)
|
||||
endTimeStr?: string, // 新增:结束时间字符串(早8点)
|
||||
reportDate?: string // 兼容旧参数:单日期
|
||||
): Promise<ReportResult> {
|
||||
try {
|
||||
// 计算时间范围:前一天早8点到当天早8点
|
||||
const targetDate = reportDate ? new Date(reportDate) : new Date();
|
||||
targetDate.setDate(targetDate.getDate() - 1); // 昨天
|
||||
// 计算时间范围
|
||||
let startTime: Date;
|
||||
let endTime: Date;
|
||||
let targetDate: Date;
|
||||
|
||||
const startTime = new Date(targetDate);
|
||||
startTime.setHours(8, 0, 0, 0);
|
||||
|
||||
const endTime = new Date(targetDate);
|
||||
endTime.setDate(endTime.getDate() + 1);
|
||||
endTime.setHours(8, 0, 0, 0);
|
||||
// 优先使用start_time和end_time
|
||||
if (startTimeStr && endTimeStr) {
|
||||
startTime = new Date(startTimeStr);
|
||||
endTime = new Date(endTimeStr);
|
||||
targetDate = new Date(startTime);
|
||||
} else if (reportDate) {
|
||||
// 兼容旧的report_date参数
|
||||
targetDate = new Date(reportDate);
|
||||
startTime = new Date(targetDate);
|
||||
startTime.setHours(8, 0, 0, 0);
|
||||
endTime = new Date(targetDate);
|
||||
endTime.setDate(endTime.getDate() + 1);
|
||||
endTime.setHours(8, 0, 0, 0);
|
||||
} else {
|
||||
// 默认:昨天早8点到今天早8点
|
||||
targetDate = new Date();
|
||||
targetDate.setDate(targetDate.getDate() - 1);
|
||||
startTime = new Date(targetDate);
|
||||
startTime.setHours(8, 0, 0, 0);
|
||||
endTime = new Date(targetDate);
|
||||
endTime.setDate(endTime.getDate() + 1);
|
||||
endTime.setHours(8, 0, 0, 0);
|
||||
}
|
||||
|
||||
// 查询订单数据
|
||||
const queryField = splitType === 'company_id' ? 'company_id' : 'user_id';
|
||||
|
||||
Reference in New Issue
Block a user