本文档面向已经了解 面板小程序开发 的开发者,您需要充分的了解什么是面板小程序,什么是 DP 功能。

相关知识

在某些设备的面板中需要提供给 C 端用户查看设备数据的能力,并需要导出数据使用户能够进一步分析。这里一般会导出如:温度、湿度、PM2.5 等的数据,涂鸦提供了相关的接口,可以按小时、天、月等粒度导出数据,把数据生成 Excel 文件,并通过邮件的方式发送给用户。

按小时粒度导出数据接口

入参

参数名称

参数类型

是否必选

默认值

描述

email

string

是

无

接收导出文件的邮箱地址

devId

string

是

无

待操作的设备 ID

dpExcelQuery

string

是

无

具体 DP 点的查询条件

date

string

是

无

待导出数据的日期,格式:20230901

type

string

是

无

DP 点的统计类型,支持:sum/avg/minux/min/max/count 等

auto

number

否

0

补数据的类型, 0:代表无数据补 0,1:代表无数据时会拿上一个时间点的数据补到当前时间点,2:代表无数据时补#

keepScalaPoint

boolean

否

false

是否按照 dp 点上报扩大倍数保留小数点

lang

string

否

无

多语言 code

title

string

否

无

邮件标题

按天粒度导出数据接口

入参

参数名称

参数类型

是否必选

默认值

描述

email

string

是

无

接收导出文件的邮箱地址

devId

string

是

无

待操作的设备 ID

dpExcelQuery

string

是

无

具体 DP 点的查询条件

startDay

string

是

无

待导出数据的开始日期,格式:20230901

endDay

string

是

无

待导出数据的结束日期,格式:20230930

type

string

是

无

DP 点的统计类型,支持:sum/avg/minux/min/max/count 等

keepScalaPoint

boolean

否

false

是否按照 dp 点上报扩大倍数保留小数点

lang

string

否

无

多语言 code

title

string

否

无

邮件标题

按月粒度导出数据接口

入参

参数名称

参数类型

是否必选

默认值

描述

email

string

是

无

接收导出文件的邮箱地址

devId

string

是

无

待操作的设备 ID

dpExcelQuery

string

是

无

具体 DP 点的查询条件

startMonth

string

是

无

待导出数据的开始年月,格式:202309

endMonth

string

是

无

待导出数据的结束年月,格式:202309

type

string

是

无

DP 点的统计类型,支持:sum/avg/minux/min/max/count 等

keepScalaPoint

boolean

否

false

是否按照 dp 点上报扩大倍数保留小数点

lang

string

否

无

多语言 code

title

string

否

无

邮件标题

dpExcelQuery 的结构

此值为一个数组结构的 JSON 字符串,数组元素的结构为:

属性名称

类型

是否必须

描述

dpId

Number

是

DP 功能的 ID

name

String

是

DP 功能名称

handler

String

否

可支持温度的转换,值可为 temperatureF2C 或 temperatureC2F

示例

[{"dpId":"101","name":"华氏度转摄氏度","handler":"temperatureF2C"}]

示例为:需要导出设备的温度 (101)、湿度 (102)两个 DP 的数据,并且需要支持将温度的单位转为华氏度。

功能描述

UI 实现

import React, { FC, useState, useCallback, useRef } from "react";
import { View, Text, Button, Checkbox, showToast } from "@ray-js/ray";
import SwitchButton from "@ray-js/components-ty-switch";
import ActionSheet from "@ray-js/components-ty-actionsheet";
import List from "@ray-js/components-ty-cell";
import DatePicker from "@ray/components-ty-datetime-picker";
import Input from "@ray-js/components-ty-input";
import { hooks } from "@ray-js/panel-sdk";
import * as api from "./api";

const langs = {
	hour: "时",
	day: "天",
	month: "月",
};

const exportFn = {
	hour: api.exportHour,
	day: api.exportDay,
	month: api.exportMonth,
};

const formDate = (date, format = "YYYYMMDD") => {
	const year = date.getFullYear();
	const month = date.getMonth() + 1;
	const day = date.getDate();

	return format
		.replace("YYYY", year)
		.replace("MM", month.toString().padStart(2, "0"))
		.replace("DD", day.toString().padStart(2, "0"));
};

const Page: FC = () => {
	const devInfo = hooks.useDevInfo();
	const [unit, setUnit] = useState(false);
	const [type, setType] = useState("hour");
	const [email, setEmail] = useState("");
	const isEnd = useRef(false);
	const [showList, setShowList] = useState(false);
	const [showTime, setShowTime] = useState(false);
	const [startTime, setStartTime] = useState(new Date());
	const [endTime, setEndTime] = useState(new Date());

	const handleShowEnd = useCallback(() => {
		isEnd.current = true;
		setShowTime(true);
	}, []);
	const handleShowStart = useCallback(() => {
		isEnd.current = false;
		setShowTime(true);
	}, []);
	const handleSelectTime = useCallback((v) => {
		if (isEnd.current) {
			setEndTime(v);
		} else {
			setStartTime(v);
		}
	}, []);

	const handleExport = useCallback(() => {
		if (!email) {
			showToast({
				title: "请输入邮箱",
				icon: "error",
			});
			return;
		}
		const format = type === "month" ? "YYYYMM" : "YYYYMMDD";
		let start = formDate(startTime, format);
		let end = formDate(startTime, format);
		const data = {
			title: "温湿度数据表",
			devId: devInfo.devId,
			email,
			dpExcelQuery: [
				{
					dpId: "101",
					name: "室内温度",
					handler: unit ? "temperatureC2F" : undefined,
				},
				{ dpId: "102", name: "室内湿度" },
			],
		};

		switch (type) {
			case "hour":
				data.date = start;
				break;
			case "day":
				data.startDay = start;
				data.endDay = end;
				break;
			case "month":
				data.startMonth = start;
				data.endMonth = end;
				break;
		}
		exportFn[type](data);
	}, [type, email, unit, startTime, endTime, devInfo?.devId]);

	return (
		<View>
			<List>
				<List.Item
					title="使用华氏度"
					content={<SwitchButton checked={unit} onChange={(v) => setUnit(v)} />}
				/>
				<List.Item
					title="粒度"
					onClick={() => setShowList(true)}
					content={<Text>{langs[type]}</Text>}
				/>
				<List.Item
					title={type === "hour" ? "时间" : "开始时间"}
					onClick={handleShowStart}
					content={
						<Text>
							{formDate(startTime, type === "month" ? "YYYYMM" : "YYYYMMDD")}
						</Text>
					}
				/>
				{type !== "hour" && (
					<List.Item
						title="结束时间"
						onClick={handleShowEnd}
						content={
							<Text>
								{formDate(endTime, type === "month" ? "YYYYMM" : "YYYYMMDD")}
							</Text>
						}
					/>
				)}

				<List.Item
					title="邮箱"
					content={
						<Input
							placeholder="请输入"
							value={email}
							onInput={(e) => setEmail(e.value)}
						/>
					}
				/>
			</List>
			<ActionSheet
				show={showList}
				header="选择粒度"
				onCancel={() => setShowList(false)}
				okText=""
			>
				<List.Row
					dataSource={Object.keys(langs).map((key) => {
						return {
							title: langs[key],
							content: type === key ? <Checkbox checked /> : null,
							onClick: () => {
								setType(key);
								setShowList(false);
							},
						};
					})}
				/>
			</ActionSheet>
			<ActionSheet
				show={showTime}
				header="选择时间"
				onCancel={() => setShowTime(false)}
				onOk={() => setShowTime(false)}
			>
				<DatePicker
					onChange={handleSelectTime}
					type={type === "month" ? "year-month" : "date"}
				/>
			</ActionSheet>
			<Button onClick={handleExport}>导出</Button>
		</View>
	);
};

export default Page;

API 封装

api.js 文件内容

import {
	exportStatisticsDay,
	exportStatisticsHour,
	exportStatisticsMonth,
} from "@ray-js/ray";

// 导出小时数据
export const exportHour = async (params) => {
	try {
		await exportStatisticsHour({
			email: params.email,
			devId: params.devId,
			dpExcelQuery: JSON.stringify(params.dpExcelQuery),
			date: params.date,
			type: "avg",
			auto: 2,
			keepScalaPoint: true,
			lang: "cn",
		});

		// 导出成功
	} catch {
		// 导出失败
	}
};

// 导出天数据
export const exportDay = async (params) => {
	try {
		await exportStatisticsDay({
			email: params.email,
			devId: params.devId,
			dpExcelQuery: JSON.stringify(params.dpExcelQuery),
			startDay: params.startDay,
			endDay: params.endDay,
			type: "avg",
			keepScalaPoint: true,
			lang: "cn",
		});

		// 导出成功
	} catch {
		// 导出失败
	}
};

// 导出月数据
export const exportMonth = async (params) => {
	try {
		await exportStatisticsMonth({
			email: params.email,
			devId: params.devId,
			dpExcelQuery: JSON.stringify(params.dpExcelQuery),
			startMonth: params.startMonth,
			endMonth: params.endMonth,
			type: "avg",
			keepScalaPoint: true,
			lang: "cn",
		});

		// 导出成功
	} catch {
		// 导出失败
	}
};