fix: 考核区间支持多批次显示

190辆冷链车有3个不同考核区间(40台、50台、100台),
恒运有2个。后端改为查询每个target的不同考核区间并返回
periods数组,前端换行显示每个区间及其车辆数。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
kkfluous
2026-04-01 21:43:17 +08:00
parent 2a10c5ae31
commit 152935819b
3 changed files with 35 additions and 10 deletions

View File

@@ -260,7 +260,9 @@ export default function StatisticsView() {
<div className="pt-3 mt-2 border-t border-slate-50 landscape:border-slate-800 grid grid-cols-2 gap-x-4 gap-y-3"> <div className="pt-3 mt-2 border-t border-slate-50 landscape:border-slate-800 grid grid-cols-2 gap-x-4 gap-y-3">
<div className="space-y-0.5"> <div className="space-y-0.5">
<p className="text-[8px] font-bold text-slate-400 uppercase tracking-wider"></p> <p className="text-[8px] font-bold text-slate-400 uppercase tracking-wider"></p>
<p className="text-[10px] font-black text-slate-700 landscape:text-slate-300">{target.period}</p> {target.periods.map((p, i) => (
<p key={i} className="text-[10px] font-black text-slate-700 landscape:text-slate-300">{p}</p>
))}
</div> </div>
<div className="space-y-0.5"> <div className="space-y-0.5">
<p className="text-[8px] font-bold text-slate-400 uppercase tracking-wider"></p> <p className="text-[8px] font-bold text-slate-400 uppercase tracking-wider"></p>
@@ -443,7 +445,7 @@ export default function StatisticsView() {
<span className="text-[10px] font-bold text-white">{target.avgCompletion.toFixed(1)}%</span> <span className="text-[10px] font-bold text-white">{target.avgCompletion.toFixed(1)}%</span>
</div> </div>
</td> </td>
<td className="p-4 text-[10px] text-slate-400">{target.period}</td> <td className="p-4 text-[10px] text-slate-400">{target.periods.join('\n')}</td>
<td className="p-4 text-xs text-slate-300">{target.annualMileagePerVehicle}</td> <td className="p-4 text-xs text-slate-300">{target.annualMileagePerVehicle}</td>
<td className="p-4 text-xs font-bold text-emerald-400">{target.yearQualifiedCount}</td> <td className="p-4 text-xs font-bold text-emerald-400">{target.yearQualifiedCount}</td>
<td className="p-4 text-xs font-bold text-blue-400">{target.halfQualifiedCount}</td> <td className="p-4 text-xs font-bold text-blue-400">{target.halfQualifiedCount}</td>

View File

@@ -23,7 +23,7 @@ export interface TargetSummary {
totalMileagePerVehicle: number; totalMileagePerVehicle: number;
annualMileagePerVehicle: number; annualMileagePerVehicle: number;
assessmentYears: number; assessmentYears: number;
period: string; periods: string[];
todayTotal: number; todayTotal: number;
cumulativeTotal: number; cumulativeTotal: number;
avgCompletion: number; avgCompletion: number;

View File

@@ -107,6 +107,25 @@ app.get('/targets', async (c) => {
statsMap.set(s.target_id, s); statsMap.set(s.target_id, s);
} }
// 查询每个 target 的不同考核区间
const [periodRows] = await pool.execute(`
SELECT target_id,
DATE_FORMAT(assessment_start_date, '%Y-%m-%d') as start_date,
DATE_FORMAT(assessment_end_date, '%Y-%m-%d') as end_date,
COUNT(*) as cnt
FROM tab_mileage_assessment_vehicle
WHERE is_deleted = 0
GROUP BY target_id, assessment_start_date, assessment_end_date
ORDER BY target_id, assessment_start_date
`) as any;
const periodsMap = new Map<number, string[]>();
for (const p of periodRows) {
const list = periodsMap.get(p.target_id) || [];
list.push(`${p.start_date} ~ ${p.end_date} (${p.cnt}台)`);
periodsMap.set(p.target_id, list);
}
const now = new Date(); const now = new Date();
const result = targets.map((t: any) => { const result = targets.map((t: any) => {
const s = statsMap.get(t.id) || {}; const s = statsMap.get(t.id) || {};
@@ -117,12 +136,16 @@ app.get('/targets', async (c) => {
const daysLeft = Math.max(1, Math.ceil((yearEnd.getTime() - now.getTime()) / 86400000)); const daysLeft = Math.max(1, Math.ceil((yearEnd.getTime() - now.getTime()) / 86400000));
const dailyTarget = remaining / daysLeft; const dailyTarget = remaining / daysLeft;
const periods = periodsMap.get(t.id) || [];
if (periods.length === 0) {
const startDate = t.default_start_date const startDate = t.default_start_date
? new Date(t.default_start_date).toISOString().split('T')[0] ? new Date(t.default_start_date).toISOString().split('T')[0]
: ''; : '';
const endDate = t.default_end_date const endDate = t.default_end_date
? new Date(t.default_end_date).toISOString().split('T')[0] ? new Date(t.default_end_date).toISOString().split('T')[0]
: ''; : '';
if (startDate || endDate) periods.push(`${startDate} ~ ${endDate}`);
}
return { return {
id: t.id, id: t.id,
@@ -131,7 +154,7 @@ app.get('/targets', async (c) => {
totalMileagePerVehicle: Number(t.total_mileage_per_vehicle), totalMileagePerVehicle: Number(t.total_mileage_per_vehicle),
annualMileagePerVehicle: Number(t.annual_mileage_per_vehicle), annualMileagePerVehicle: Number(t.annual_mileage_per_vehicle),
assessmentYears: t.assessment_years, assessmentYears: t.assessment_years,
period: `${startDate} ~ ${endDate}`, periods,
todayTotal: Number(s.today_total) || 0, todayTotal: Number(s.today_total) || 0,
cumulativeTotal: Number(s.cumulative_total) || 0, cumulativeTotal: Number(s.cumulative_total) || 0,
avgCompletion: (Number(s.avg_completion) || 0) * 100, avgCompletion: (Number(s.avg_completion) || 0) * 100,