This commit is contained in:
@@ -1,77 +0,0 @@
|
||||
/**
|
||||
* 高德地图 JSAPI 2.0 的唯一接入点。
|
||||
*
|
||||
* 两个热力图模块此前各自重复了:SDK 版本与插件列表、安全码注入、
|
||||
* 底图参数、控件位置以及整条渐变色带。这些必须保持一致,否则"同类地图
|
||||
* 长得不一样",因此收敛到这里。各页面只保留自己的热力图参数(半径/透明度)。
|
||||
*/
|
||||
|
||||
/** 最小结构类型:只声明我们实际用到的构造器。 */
|
||||
export type AmapInstance = {
|
||||
Map: new (container: HTMLElement, options: Record<string, unknown>) => any;
|
||||
HeatMap: new (map: any, options: Record<string, unknown>) => any;
|
||||
ToolBar: new (options?: Record<string, unknown>) => any;
|
||||
Scale: new (options?: Record<string, unknown>) => any;
|
||||
Bounds: new (southWest: [number, number], northEast: [number, number]) => any;
|
||||
};
|
||||
|
||||
export interface AmapClientConfig {
|
||||
key: string;
|
||||
securityCode: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
_AMapSecurityConfig?: { securityJsCode: string };
|
||||
}
|
||||
}
|
||||
|
||||
/** 热力图色带:冷→热,两处地图共用。 */
|
||||
export const HEATMAP_GRADIENT: Record<number, string> = {
|
||||
0.1: '#2563eb',
|
||||
0.3: '#0891b2',
|
||||
0.5: '#16a34a',
|
||||
0.68: '#eab308',
|
||||
0.84: '#f97316',
|
||||
1: '#dc2626',
|
||||
};
|
||||
|
||||
/** 与地图色带对应的 UI 图例渐变,避免图例与地图各写一份色值。 */
|
||||
export const HEATMAP_LEGEND_GRADIENT =
|
||||
'linear-gradient(90deg, #2563eb 0%, #0891b2 25%, #16a34a 45%, #eab308 65%, #f97316 82%, #dc2626 100%)';
|
||||
|
||||
/** 加载 JSAPI:版本、插件列表与安全码只在此处定义。 */
|
||||
export async function loadAmap(config: AmapClientConfig): Promise<AmapInstance> {
|
||||
window._AMapSecurityConfig = { securityJsCode: config.securityCode };
|
||||
const loaderModule = await import('@amap/amap-jsapi-loader');
|
||||
const loaded = await loaderModule.default.load({
|
||||
key: config.key,
|
||||
version: '2.0',
|
||||
plugins: ['AMap.HeatMap', 'AMap.ToolBar', 'AMap.Scale'],
|
||||
});
|
||||
return loaded as unknown as AmapInstance;
|
||||
}
|
||||
|
||||
/** 创建只读热力图底图:底图样式、控件位置与色带统一。 */
|
||||
export function createHeatmapMap(
|
||||
AMap: AmapInstance,
|
||||
container: HTMLElement,
|
||||
style: { radius: number; opacity: [number, number] },
|
||||
): { map: any; heatmap: any } {
|
||||
const map = new AMap.Map(container, {
|
||||
viewMode: '2D',
|
||||
zoom: 5,
|
||||
center: [105.4, 34.4],
|
||||
mapStyle: 'amap://styles/whitesmoke',
|
||||
resizeEnable: true,
|
||||
showLabel: true,
|
||||
});
|
||||
map.addControl(new AMap.ToolBar({ position: { right: '20px', bottom: '76px' } }));
|
||||
map.addControl(new AMap.Scale({ position: { right: '18px', bottom: '24px' } }));
|
||||
const heatmap = new AMap.HeatMap(map, {
|
||||
radius: style.radius,
|
||||
opacity: style.opacity,
|
||||
gradient: { ...HEATMAP_GRADIENT },
|
||||
});
|
||||
return { map, heatmap };
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* 自然日区间工具(按本地日历日,不使用 UTC)。
|
||||
* 前后端都要按"上海/本地自然日"取数,散落各处的实现容易在跨日与月末出错,
|
||||
* 因此收敛到 shared 这一层。
|
||||
*/
|
||||
|
||||
export interface DayRange {
|
||||
start: string;
|
||||
end: string;
|
||||
}
|
||||
|
||||
function formatYmd(date: Date): string {
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 近 N 天(含今天)的区间。
|
||||
*
|
||||
* 用于接口尚未返回数据水位(meta)时的兜底范围。此前代码把兜底写死成
|
||||
* `2026-01-01 ~ 2026-07-13`,会随着时间推移永久指向一段历史数据。
|
||||
*/
|
||||
export function recentDayRange(days = 30, now: Date = new Date()): DayRange {
|
||||
const span = Math.max(1, Math.floor(days));
|
||||
const end = new Date(now);
|
||||
end.setHours(0, 0, 0, 0);
|
||||
const start = new Date(end);
|
||||
start.setDate(start.getDate() - (span - 1));
|
||||
return { start: formatYmd(start), end: formatYmd(end) };
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* 核对功能上线期初(含当日)。
|
||||
*
|
||||
* 「待核对订单」只统计这一天及之后的加氢订单:起算日之前的历史订单视为核对范围之外,
|
||||
* 不计入待核对笔数与金额。该口径同时决定后端 SQL 过滤(energy/repository.ts)与前端
|
||||
* 展示文案(看板诊断卡、下钻弹窗提示),因此收敛到 shared 这一层,避免只改了一处导致
|
||||
* 口径与文案不一致。
|
||||
*/
|
||||
export const HYDROGEN_VERIFY_START_DATE = '2026-01-01';
|
||||
@@ -1,59 +0,0 @@
|
||||
import * as XLSX from 'xlsx';
|
||||
|
||||
/**
|
||||
* Excel 导出的唯一实现。
|
||||
*
|
||||
* 此前仓库里有 4 份各自拼装 workbook 的代码(assets 内联、mileage 内联、
|
||||
* hydrogen 的两份 aoa/json helper),产物后缀与 sheet 名截断规则也不一致。
|
||||
* 这里只收敛"组装与写出"这一层原语;各调用方仍自行决定列宽、冻结与数字格式,
|
||||
* 以免改变已验收的导出样式。
|
||||
*/
|
||||
|
||||
export type AoaRow = Array<string | number | boolean | null | undefined>;
|
||||
|
||||
/** 统一产物后缀:内部一律写出 .xlsx。 */
|
||||
export function ensureXlsxFilename(name: string | undefined): string {
|
||||
const raw = String(name || 'export').trim() || 'export';
|
||||
return `${raw.replace(/\.(csv|xls|xlsx)$/i, '')}.xlsx`;
|
||||
}
|
||||
|
||||
/** 由二维数组构建 sheet。 */
|
||||
export function buildAoaSheet(rows: AoaRow[]): XLSX.WorkSheet {
|
||||
return XLSX.utils.aoa_to_sheet(rows);
|
||||
}
|
||||
|
||||
/** 由对象数组构建 sheet(首行为字段名)。 */
|
||||
export function buildJsonSheet(rows: Record<string, unknown>[]): XLSX.WorkSheet {
|
||||
return XLSX.utils.json_to_sheet(rows);
|
||||
}
|
||||
|
||||
/** Excel 的 sheet 名上限是 31 字符,空名回退为 Sheet1。 */
|
||||
function safeSheetName(name: string): string {
|
||||
return (name || 'Sheet1').slice(0, 31) || 'Sheet1';
|
||||
}
|
||||
|
||||
/** 写出一本工作簿。调用方可先给 sheet 设置 `!cols` / `!freeze` / 单元格格式。 */
|
||||
export function writeWorkbook(
|
||||
sheets: Array<{ name: string; sheet: XLSX.WorkSheet }>,
|
||||
fileName: string,
|
||||
): void {
|
||||
const workbook = XLSX.utils.book_new();
|
||||
for (const { name, sheet } of sheets) {
|
||||
XLSX.utils.book_append_sheet(workbook, sheet, safeSheetName(name));
|
||||
}
|
||||
XLSX.writeFile(workbook, ensureXlsxFilename(fileName));
|
||||
}
|
||||
|
||||
/** 便捷:单个二维数组 sheet 直接下载。 */
|
||||
export function exportAoaSheet(rows: AoaRow[], fileName: string, sheetName = 'Sheet1'): void {
|
||||
writeWorkbook([{ name: sheetName, sheet: buildAoaSheet(rows) }], fileName);
|
||||
}
|
||||
|
||||
/** 便捷:单个对象数组 sheet 直接下载。 */
|
||||
export function exportJsonSheet(
|
||||
rows: Record<string, unknown>[],
|
||||
fileName: string,
|
||||
sheetName = 'Sheet1',
|
||||
): void {
|
||||
writeWorkbook([{ name: sheetName, sheet: buildJsonSheet(rows) }], fileName);
|
||||
}
|
||||
Reference in New Issue
Block a user