feat(energy): 用 truck_id 区分外部/我司,外部数据空时给友好提示
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- 后端 customerClause 改为基于 truck_id:外部=IS NULL,我司=IS NOT NULL - KPI 内联条件(ourYearKg/Fee、customerYearKg、lingniuBornKg/Fee)同步切换为 truck_id - 调用方 /hydrogen/daily 与 /electric/monthly 改传 b.truck_id / truck_id - 当前外部账单 truck_id 尚未对接,HydrogenDaily/ElectricDaily 在 customer=external 且无数据时 改展示「数据对接中…」友好状态(插头图标 + 蓝色脉冲),替代「暂无数据」 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { ChevronRight } from 'lucide-react';
|
import { ChevronRight, Plug } from 'lucide-react';
|
||||||
import { motion, AnimatePresence } from 'motion/react';
|
import { motion, AnimatePresence } from 'motion/react';
|
||||||
import TrendBadge from './TrendBadge';
|
import TrendBadge from './TrendBadge';
|
||||||
import { fetchElectricMonthly } from './api';
|
import { fetchElectricMonthly } from './api';
|
||||||
@@ -48,7 +48,27 @@ export default function ElectricDaily() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 外部数据对接中 友好空状态 */}
|
||||||
|
{customer === 'external' && months !== null && months.length === 0 && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 8 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
className="bg-white rounded-2xl border border-slate-100 shadow-sm px-6 py-12 flex flex-col items-center text-center"
|
||||||
|
>
|
||||||
|
<div className="w-14 h-14 rounded-2xl bg-blue-50 flex items-center justify-center mb-3 relative">
|
||||||
|
<Plug size={22} className="text-blue-500" />
|
||||||
|
<span className="absolute -top-0.5 -right-0.5 w-3 h-3 rounded-full bg-blue-400 animate-ping" />
|
||||||
|
<span className="absolute -top-0.5 -right-0.5 w-3 h-3 rounded-full bg-blue-500" />
|
||||||
|
</div>
|
||||||
|
<div className="text-sm font-bold text-slate-700 mb-1">数据对接中…</div>
|
||||||
|
<div className="text-[11px] text-slate-400 max-w-[260px] leading-relaxed">
|
||||||
|
外部充电账单正在与合作方系统打通,上线后此处将展示完整数据
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 月份分组表 */}
|
{/* 月份分组表 */}
|
||||||
|
{!(customer === 'external' && months !== null && months.length === 0) && (
|
||||||
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm overflow-hidden">
|
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm overflow-hidden">
|
||||||
<div className="grid grid-cols-[1fr_auto_auto_auto] md:grid-cols-[1fr_120px_140px_120px] gap-2 px-3 py-2 bg-slate-50 text-[11px] font-bold text-slate-500">
|
<div className="grid grid-cols-[1fr_auto_auto_auto] md:grid-cols-[1fr_120px_140px_120px] gap-2 px-3 py-2 bg-slate-50 text-[11px] font-bold text-slate-500">
|
||||||
<span>月份 / 日期</span>
|
<span>月份 / 日期</span>
|
||||||
@@ -122,6 +142,7 @@ export default function ElectricDaily() {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { ChevronRight } from 'lucide-react';
|
import { ChevronRight, Plug } from 'lucide-react';
|
||||||
import { motion, AnimatePresence } from 'motion/react';
|
import { motion, AnimatePresence } from 'motion/react';
|
||||||
import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Cell, Tooltip } from 'recharts';
|
import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Cell, Tooltip } from 'recharts';
|
||||||
import TrendBadge from './TrendBadge';
|
import TrendBadge from './TrendBadge';
|
||||||
@@ -75,6 +75,25 @@ export default function HydrogenDaily() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 外部数据对接中 友好空状态 */}
|
||||||
|
{customer === 'external' && rows !== null && rows.length === 0 && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 8 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
className="bg-white rounded-2xl border border-slate-100 shadow-sm px-6 py-12 flex flex-col items-center text-center"
|
||||||
|
>
|
||||||
|
<div className="w-14 h-14 rounded-2xl bg-blue-50 flex items-center justify-center mb-3 relative">
|
||||||
|
<Plug size={22} className="text-blue-500" />
|
||||||
|
<span className="absolute -top-0.5 -right-0.5 w-3 h-3 rounded-full bg-blue-400 animate-ping" />
|
||||||
|
<span className="absolute -top-0.5 -right-0.5 w-3 h-3 rounded-full bg-blue-500" />
|
||||||
|
</div>
|
||||||
|
<div className="text-sm font-bold text-slate-700 mb-1">数据对接中…</div>
|
||||||
|
<div className="text-[11px] text-slate-400 max-w-[260px] leading-relaxed">
|
||||||
|
外部加氢账单正在与合作方系统打通,上线后此处将展示完整数据
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 时段加氢量柱图 */}
|
{/* 时段加氢量柱图 */}
|
||||||
{trendData.length > 0 && (
|
{trendData.length > 0 && (
|
||||||
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm p-4">
|
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm p-4">
|
||||||
@@ -116,7 +135,8 @@ export default function HydrogenDaily() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 表格 */}
|
{/* 表格(外部空数据时不渲染,由上方友好空状态替代) */}
|
||||||
|
{!(customer === 'external' && rows !== null && rows.length === 0) && (
|
||||||
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm overflow-hidden">
|
<div className="bg-white rounded-2xl border border-slate-100 shadow-sm overflow-hidden">
|
||||||
{/* 表头 */}
|
{/* 表头 */}
|
||||||
<div className="grid grid-cols-[1fr_auto_auto] md:grid-cols-[1fr_140px_120px_120px] gap-2 px-3 py-2 bg-slate-50 text-[11px] font-bold text-slate-500">
|
<div className="grid grid-cols-[1fr_auto_auto] md:grid-cols-[1fr_140px_120px_120px] gap-2 px-3 py-2 bg-slate-50 text-[11px] font-bold text-slate-500">
|
||||||
@@ -193,6 +213,7 @@ export default function HydrogenDaily() {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ const ELECTRIC_LOCAL = `DATE_ADD(charging_start_time, INTERVAL 8 HOUR)`;
|
|||||||
|
|
||||||
type CustomerKind = 'external' | 'lingniu' | 'all';
|
type CustomerKind = 'external' | 'lingniu' | 'all';
|
||||||
|
|
||||||
|
// 外部/我司判定:truck_id 为空 = 外部;truck_id 非空 = 我司(羚牛车辆)
|
||||||
function customerClause(field: string, customer: CustomerKind): string {
|
function customerClause(field: string, customer: CustomerKind): string {
|
||||||
if (customer === 'lingniu') return `${field} IS NULL`;
|
if (customer === 'external') return `${field} IS NULL`;
|
||||||
if (customer === 'external') return `${field} IS NOT NULL`;
|
if (customer === 'lingniu') return `${field} IS NOT NULL`;
|
||||||
return '1=1';
|
return '1=1';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,11 +45,11 @@ app.get('/hydrogen/overview', async (c) => {
|
|||||||
THEN hydrogen_quantity ELSE 0 END) AS yearKg,
|
THEN hydrogen_quantity ELSE 0 END) AS yearKg,
|
||||||
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = YEAR(CURDATE())
|
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = YEAR(CURDATE())
|
||||||
THEN cost_expense ELSE 0 END) AS yearFee,
|
THEN cost_expense ELSE 0 END) AS yearFee,
|
||||||
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = YEAR(CURDATE()) AND customer_id IS NULL
|
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = YEAR(CURDATE()) AND truck_id IS NOT NULL
|
||||||
THEN hydrogen_quantity ELSE 0 END) AS ourYearKg,
|
THEN hydrogen_quantity ELSE 0 END) AS ourYearKg,
|
||||||
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = YEAR(CURDATE()) AND customer_id IS NULL
|
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = YEAR(CURDATE()) AND truck_id IS NOT NULL
|
||||||
THEN cost_expense ELSE 0 END) AS ourYearFee,
|
THEN cost_expense ELSE 0 END) AS ourYearFee,
|
||||||
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = YEAR(CURDATE()) AND customer_id IS NOT NULL
|
SUM(CASE WHEN YEAR(${HYDROGEN_LOCAL}) = YEAR(CURDATE()) AND truck_id IS NULL
|
||||||
THEN hydrogen_quantity ELSE 0 END) AS customerYearKg,
|
THEN hydrogen_quantity ELSE 0 END) AS customerYearKg,
|
||||||
SUM(CASE WHEN DATE_FORMAT(${HYDROGEN_LOCAL}, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
|
SUM(CASE WHEN DATE_FORMAT(${HYDROGEN_LOCAL}, '%Y-%m') = DATE_FORMAT(CURDATE(), '%Y-%m')
|
||||||
THEN hydrogen_quantity ELSE 0 END) AS monthKg,
|
THEN hydrogen_quantity ELSE 0 END) AS monthKg,
|
||||||
@@ -58,9 +59,9 @@ app.get('/hydrogen/overview', async (c) => {
|
|||||||
THEN hydrogen_quantity ELSE 0 END) AS todayKg,
|
THEN hydrogen_quantity ELSE 0 END) AS todayKg,
|
||||||
SUM(CASE WHEN DATE(${HYDROGEN_LOCAL}) = CURDATE()
|
SUM(CASE WHEN DATE(${HYDROGEN_LOCAL}) = CURDATE()
|
||||||
THEN cost_expense ELSE 0 END) AS todayFee,
|
THEN cost_expense ELSE 0 END) AS todayFee,
|
||||||
SUM(CASE WHEN customer_id IS NULL
|
SUM(CASE WHEN truck_id IS NOT NULL
|
||||||
THEN hydrogen_quantity ELSE 0 END) AS lingniuBornKg,
|
THEN hydrogen_quantity ELSE 0 END) AS lingniuBornKg,
|
||||||
SUM(CASE WHEN customer_id IS NULL
|
SUM(CASE WHEN truck_id IS NOT NULL
|
||||||
THEN cost_expense ELSE 0 END) AS lingniuBornFee
|
THEN cost_expense ELSE 0 END) AS lingniuBornFee
|
||||||
FROM tab_energy_hydrogen_bill
|
FROM tab_energy_hydrogen_bill
|
||||||
WHERE is_deleted = 0 AND hydrogen_time >= ?`,
|
WHERE is_deleted = 0 AND hydrogen_time >= ?`,
|
||||||
@@ -154,7 +155,7 @@ app.get('/hydrogen/daily', async (c) => {
|
|||||||
'b.is_deleted = 0',
|
'b.is_deleted = 0',
|
||||||
`b.hydrogen_time >= '${HYDROGEN_MIN_DATE}'`,
|
`b.hydrogen_time >= '${HYDROGEN_MIN_DATE}'`,
|
||||||
rangeClause(`b.hydrogen_time + INTERVAL 8 HOUR`, range),
|
rangeClause(`b.hydrogen_time + INTERVAL 8 HOUR`, range),
|
||||||
customerClause('b.customer_id', customer),
|
customerClause('b.truck_id', customer),
|
||||||
].join(' AND ');
|
].join(' AND ');
|
||||||
|
|
||||||
// 站点级聚合(每日 × 每站)。前端组装成 day → stations
|
// 站点级聚合(每日 × 每站)。前端组装成 day → stations
|
||||||
@@ -343,7 +344,7 @@ app.get('/electric/monthly', async (c) => {
|
|||||||
|
|
||||||
const where = [
|
const where = [
|
||||||
'is_deleted = 0',
|
'is_deleted = 0',
|
||||||
customerClause('customer_id', customer),
|
customerClause('truck_id', customer),
|
||||||
].join(' AND ');
|
].join(' AND ');
|
||||||
|
|
||||||
// 取最近 6 个月
|
// 取最近 6 个月
|
||||||
|
|||||||
Reference in New Issue
Block a user