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:
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user