Commit f23f07bc96c33212e0cf4218f074537c8a145225

Authored by fengtao
2 parents 6bf253e9 e569fd79

Merge branch 'main' into f-dev

@@ -22,7 +22,7 @@ export const getDeviceHistoryInfo = (params) => { @@ -22,7 +22,7 @@ export const getDeviceHistoryInfo = (params) => {
22 22
23 // 获取设备数据的keys 23 // 获取设备数据的keys
24 export const getDeviceDataKeys = (id: string) => { 24 export const getDeviceDataKeys = (id: string) => {
25 - return defHttp.get( 25 + return defHttp.get<string[]>(
26 { 26 {
27 url: `/plugins/telemetry/DEVICE/${id}/keys/timeseries`, 27 url: `/plugins/telemetry/DEVICE/${id}/keys/timeseries`,
28 }, 28 },
@@ -2,7 +2,8 @@ import type { BasicColumn } from '/@/components/Table'; @@ -2,7 +2,8 @@ import type { BasicColumn } from '/@/components/Table';
2 import type { FormSchema } from '/@/components/Table'; 2 import type { FormSchema } from '/@/components/Table';
3 import { getOrganizationList } from '/@/api/system/system'; 3 import { getOrganizationList } from '/@/api/system/system';
4 import { copyTransFun } from '/@/utils/fnUtils'; 4 import { copyTransFun } from '/@/utils/fnUtils';
5 -import { getDeviceProfile } from '/@/api/alarm/position'; 5 +import { getDeviceDataKeys, getDeviceProfile } from '/@/api/alarm/position';
  6 +import { EChartsOption } from 'echarts';
6 7
7 export enum AggregateDataEnum { 8 export enum AggregateDataEnum {
8 MIN = 'MIN', 9 MIN = 'MIN',
@@ -381,17 +382,11 @@ export const schemas: FormSchema[] = [ @@ -381,17 +382,11 @@ export const schemas: FormSchema[] = [
381 ], 382 ],
382 }; 383 };
383 }, 384 },
384 - colProps: {  
385 - span: 6,  
386 - },  
387 }, 385 },
388 { 386 {
389 field: 'interval', 387 field: 'interval',
390 label: '分组间隔', 388 label: '分组间隔',
391 component: 'Select', 389 component: 'Select',
392 - colProps: {  
393 - span: 6,  
394 - },  
395 componentProps: { 390 componentProps: {
396 placeholder: '请选择分组间隔', 391 placeholder: '请选择分组间隔',
397 getPopupContainer: () => document.body, 392 getPopupContainer: () => document.body,
@@ -427,6 +422,7 @@ export const schemas: FormSchema[] = [ @@ -427,6 +422,7 @@ export const schemas: FormSchema[] = [
427 field: 'agg', 422 field: 'agg',
428 label: '数据聚合功能', 423 label: '数据聚合功能',
429 component: 'Select', 424 component: 'Select',
  425 + defaultValue: AggregateDataEnum.NONE,
430 componentProps: { 426 componentProps: {
431 getPopupContainer: () => document.body, 427 getPopupContainer: () => document.body,
432 options: [ 428 options: [
@@ -446,10 +442,83 @@ export const schemas: FormSchema[] = [ @@ -446,10 +442,83 @@ export const schemas: FormSchema[] = [
446 label: '求和', 442 label: '求和',
447 value: AggregateDataEnum.SUM, 443 value: AggregateDataEnum.SUM,
448 }, 444 },
  445 + {
  446 + label: '计数',
  447 + value: AggregateDataEnum.COUNT,
  448 + },
  449 + {
  450 + label: '空',
  451 + value: AggregateDataEnum.NONE,
  452 + },
449 ], 453 ],
450 }, 454 },
451 - colProps: {  
452 - span: 6, 455 + },
  456 + {
  457 + field: 'attr',
  458 + label: '设备属性',
  459 + component: 'Select',
  460 + componentProps: {
  461 + api: async (id: string) => {
  462 + try {
  463 + const res = await getDeviceDataKeys(id);
  464 + return res.map((item) => ({ label: item, value: item }));
  465 + } catch (error) {}
  466 + },
453 }, 467 },
454 }, 468 },
455 ]; 469 ];
  470 +
  471 +export const selectDeviceAttrSchema: FormSchema[] = [
  472 + {
  473 + field: 'keys',
  474 + label: '设备属性',
  475 + component: 'Select',
  476 + // componentProps: {
  477 + // api: async ({ entityId }: { entityId: string }) => {
  478 + // if (!entityId) return [];
  479 + // try {
  480 + // const res = await getDeviceDataKeys(entityId);
  481 + // return res.map((item) => ({ label: item, value: item }));
  482 + // } catch (error) {}
  483 + // },
  484 + // immediate: true,
  485 + // },
  486 + },
  487 +];
  488 +
  489 +export const eChartOptions = (series, keys): EChartsOption => {
  490 + return {
  491 + tooltip: {
  492 + trigger: 'axis',
  493 + },
  494 + legend: {
  495 + data: keys,
  496 + },
  497 + grid: {
  498 + left: '3%',
  499 + right: '4%',
  500 + bottom: '3%',
  501 + containLabel: true,
  502 + },
  503 + dataZoom: [
  504 + {
  505 + type: 'inside',
  506 + start: 0,
  507 + end: 50,
  508 + },
  509 + {
  510 + start: 20,
  511 + end: 40,
  512 + },
  513 + ],
  514 + xAxis: {
  515 + type: 'time',
  516 + boundaryGap: false,
  517 + },
  518 + yAxis: {
  519 + type: 'value',
  520 + boundaryGap: [0, '100%'],
  521 + },
  522 + series,
  523 + };
  524 +};
  1 +<script setup lang="ts">
  2 + import { BasicForm, useForm } from '/@/components/Form';
  3 + import { defaultSchemas, SchemaFiled } from './config';
  4 + import { onMounted } from 'vue';
  5 +
  6 + const emit = defineEmits(['register']);
  7 + const [register, method] = useForm({
  8 + schemas: defaultSchemas,
  9 + labelWidth: 120,
  10 + baseColProps: {
  11 + span: 5,
  12 + },
  13 + fieldMapToTime: [[SchemaFiled.DATE_RANGE, [SchemaFiled.START_TS, SchemaFiled.END_TS]]],
  14 + });
  15 + onMounted(() => {
  16 + emit('register', method);
  17 + });
  18 +</script>
  19 +
  20 +<template>
  21 + <BasicForm @register="register" />
  22 +</template>
  1 +import { Moment } from 'moment';
  2 +import { getPacketIntervalByRange, getPacketIntervalByValue, intervalOption } from './helper';
  3 +import { FormSchema } from '/@/components/Form';
  4 +export enum QueryWay {
  5 + LATEST = 'latest',
  6 + TIME_PERIOD = 'timePeriod',
  7 +}
  8 +
  9 +export enum SchemaFiled {
  10 + WAY = 'way',
  11 + TIME_PERIOD = 'timePeriod',
  12 + KEYS = 'keys',
  13 + DATE_RANGE = 'dataRange',
  14 + START_TS = 'startTs',
  15 + END_TS = 'endTs',
  16 + INTERVAL = 'interval',
  17 + LIMIT = 'limit',
  18 + AGG = 'agg',
  19 + ORDER_BY = 'orderBy',
  20 +}
  21 +
  22 +export enum AggregateDataEnum {
  23 + MIN = 'MIN',
  24 + MAX = 'MAX',
  25 + AVG = 'AVG',
  26 + SUM = 'SUM',
  27 + COUNT = 'COUNT',
  28 + NONE = 'NONE',
  29 +}
  30 +export const defaultSchemas: FormSchema[] = [
  31 + {
  32 + field: SchemaFiled.WAY,
  33 + label: '查询方式',
  34 + component: 'RadioGroup',
  35 + defaultValue: QueryWay.LATEST,
  36 + componentProps({ formActionType }) {
  37 + const { setFieldsValue } = formActionType;
  38 + return {
  39 + options: [
  40 + { label: '最后', value: QueryWay.LATEST },
  41 + { label: '时间段', value: QueryWay.TIME_PERIOD },
  42 + ],
  43 + onChange(value) {
  44 + value === QueryWay.LATEST
  45 + ? setFieldsValue({
  46 + [SchemaFiled.DATE_RANGE]: [],
  47 + [SchemaFiled.START_TS]: null,
  48 + [SchemaFiled.END_TS]: null,
  49 + })
  50 + : setFieldsValue({ [SchemaFiled.START_TS]: null });
  51 + },
  52 + };
  53 + },
  54 + },
  55 + {
  56 + field: SchemaFiled.START_TS,
  57 + label: '最后数据',
  58 + component: 'Select',
  59 + ifShow({ values }) {
  60 + return values[SchemaFiled.WAY] === QueryWay.LATEST;
  61 + },
  62 + componentProps({ formActionType }) {
  63 + const { setFieldsValue } = formActionType;
  64 + return {
  65 + options: intervalOption,
  66 + onChange() {
  67 + setFieldsValue({ [SchemaFiled.INTERVAL]: null });
  68 + },
  69 + };
  70 + },
  71 + rules: [{ required: true, message: '最后数据为必选项', type: 'number' }],
  72 + },
  73 + {
  74 + field: SchemaFiled.DATE_RANGE,
  75 + label: '时间段',
  76 + component: 'RangePicker',
  77 + ifShow({ values }) {
  78 + return values[SchemaFiled.WAY] === QueryWay.TIME_PERIOD;
  79 + },
  80 + rules: [{ required: true, message: '时间段为必选项' }],
  81 + componentProps({ formActionType }) {
  82 + const { setFieldsValue } = formActionType;
  83 + let dates: Moment[] = [];
  84 + return {
  85 + showTime: true,
  86 + onCalendarChange(value: Moment[]) {
  87 + console.log('enter ');
  88 + dates = value;
  89 + },
  90 + disabledDate(current: Moment) {
  91 + if (!dates || dates.length === 0 || !current) {
  92 + return false;
  93 + }
  94 + const diffDate = current.diff(dates[0], 'years', true);
  95 + return Math.abs(diffDate) > 1;
  96 + },
  97 + onChange() {
  98 + dates = [];
  99 + setFieldsValue({ [SchemaFiled.INTERVAL]: null });
  100 + },
  101 + };
  102 + },
  103 + colProps: {
  104 + span: 10,
  105 + },
  106 + },
  107 + {
  108 + field: SchemaFiled.AGG,
  109 + label: '数据聚合功能',
  110 + component: 'Select',
  111 + defaultValue: AggregateDataEnum.NONE,
  112 + componentProps: {
  113 + getPopupContainer: () => document.body,
  114 + options: [
  115 + { label: '最小值', value: AggregateDataEnum.MIN },
  116 + { label: '最大值', value: AggregateDataEnum.MAX },
  117 + { label: '平均值', value: AggregateDataEnum.AVG },
  118 + { label: '求和', value: AggregateDataEnum.SUM },
  119 + { label: '计数', value: AggregateDataEnum.COUNT },
  120 + { label: '空', value: AggregateDataEnum.NONE },
  121 + ],
  122 + },
  123 + },
  124 + {
  125 + field: SchemaFiled.INTERVAL,
  126 + label: '分组间隔',
  127 + component: 'Select',
  128 + ifShow({ values }) {
  129 + return values[SchemaFiled.AGG] !== AggregateDataEnum.NONE;
  130 + },
  131 + componentProps({ formModel }) {
  132 + const options =
  133 + formModel[SchemaFiled.WAY] === QueryWay.LATEST
  134 + ? getPacketIntervalByValue(formModel[SchemaFiled.START_TS])
  135 + : getPacketIntervalByRange(formModel[SchemaFiled.DATE_RANGE]);
  136 +
  137 + return {
  138 + options,
  139 + };
  140 + },
  141 + },
  142 + {
  143 + field: SchemaFiled.LIMIT,
  144 + label: '最大值',
  145 + component: 'InputNumber',
  146 + defaultValue: 7,
  147 + ifShow({ values }) {
  148 + return values[SchemaFiled.AGG] === AggregateDataEnum.NONE;
  149 + },
  150 + componentProps: {
  151 + max: 50000,
  152 + min: 7,
  153 + },
  154 + },
  155 +];
  1 +import { Moment } from 'moment';
  2 +
  3 +enum TimeUnit {
  4 + SECOND = 'second',
  5 + MINUTE = 'MINUTE',
  6 + HOUR = 'HOUR',
  7 + DAY = 'DAY',
  8 +}
  9 +
  10 +const unitMapping = {
  11 + [TimeUnit.SECOND]: '秒',
  12 + [TimeUnit.MINUTE]: '分',
  13 + [TimeUnit.HOUR]: '小时',
  14 + [TimeUnit.DAY]: '天',
  15 +};
  16 +
  17 +const unitConversion = {
  18 + [TimeUnit.SECOND]: 1 * 1000,
  19 + [TimeUnit.MINUTE]: 1 * 60 * 1000,
  20 + [TimeUnit.HOUR]: 1 * 60 * 60 * 1000,
  21 + [TimeUnit.DAY]: 1 * 60 * 60 * 24 * 1000,
  22 +};
  23 +
  24 +export const intervalOption = [
  25 + {
  26 + id: 1,
  27 + unit: TimeUnit.SECOND,
  28 + linkage: [{ id: 1, unit: TimeUnit.SECOND }],
  29 + },
  30 + {
  31 + id: 5,
  32 + unit: TimeUnit.SECOND,
  33 + linkage: [{ id: 1, unit: TimeUnit.SECOND }],
  34 + },
  35 + {
  36 + id: 10,
  37 + unit: TimeUnit.SECOND,
  38 + linkage: [{ id: 1, unit: TimeUnit.SECOND }],
  39 + },
  40 + {
  41 + id: 15,
  42 + unit: TimeUnit.SECOND,
  43 + linkage: [{ id: 1, unit: TimeUnit.SECOND }],
  44 + },
  45 + {
  46 + id: 30,
  47 + unit: TimeUnit.SECOND,
  48 + linkage: [{ id: 1, unit: TimeUnit.SECOND }],
  49 + },
  50 + {
  51 + id: 1,
  52 + unit: TimeUnit.MINUTE,
  53 + linkage: [
  54 + { id: 1, unit: TimeUnit.SECOND },
  55 + { id: 5, unit: TimeUnit.SECOND },
  56 + ],
  57 + },
  58 + {
  59 + id: 2,
  60 + unit: TimeUnit.MINUTE,
  61 + linkage: [
  62 + { id: 1, unit: TimeUnit.SECOND },
  63 + { id: 5, unit: TimeUnit.SECOND },
  64 + { id: 10, unit: TimeUnit.SECOND },
  65 + { id: 15, unit: TimeUnit.SECOND },
  66 + ],
  67 + },
  68 + {
  69 + id: 5,
  70 + unit: TimeUnit.MINUTE,
  71 + linkage: [
  72 + { id: 1, unit: TimeUnit.SECOND },
  73 + { id: 5, unit: TimeUnit.SECOND },
  74 + { id: 10, unit: TimeUnit.SECOND },
  75 + { id: 15, unit: TimeUnit.SECOND },
  76 + { id: 30, unit: TimeUnit.SECOND },
  77 + ],
  78 + },
  79 + {
  80 + id: 10,
  81 + unit: TimeUnit.MINUTE,
  82 + linkage: [
  83 + { id: 5, unit: TimeUnit.SECOND },
  84 + { id: 10, unit: TimeUnit.SECOND },
  85 + { id: 15, unit: TimeUnit.SECOND },
  86 + { id: 30, unit: TimeUnit.SECOND },
  87 + { id: 1, unit: TimeUnit.MINUTE },
  88 + ],
  89 + },
  90 + {
  91 + id: 15,
  92 + unit: TimeUnit.MINUTE,
  93 + linkage: [
  94 + { id: 5, unit: TimeUnit.SECOND },
  95 + { id: 10, unit: TimeUnit.SECOND },
  96 + { id: 15, unit: TimeUnit.SECOND },
  97 + { id: 30, unit: TimeUnit.SECOND },
  98 + { id: 1, unit: TimeUnit.MINUTE },
  99 + { id: 2, unit: TimeUnit.MINUTE },
  100 + ],
  101 + },
  102 + {
  103 + id: 30,
  104 + unit: TimeUnit.MINUTE,
  105 + linkage: [
  106 + { id: 5, unit: TimeUnit.SECOND },
  107 + { id: 10, unit: TimeUnit.SECOND },
  108 + { id: 15, unit: TimeUnit.SECOND },
  109 + { id: 30, unit: TimeUnit.SECOND },
  110 + { id: 1, unit: TimeUnit.MINUTE },
  111 + { id: 2, unit: TimeUnit.MINUTE },
  112 + ],
  113 + },
  114 + {
  115 + id: 1,
  116 + unit: TimeUnit.HOUR,
  117 + linkage: [
  118 + { id: 10, unit: TimeUnit.SECOND },
  119 + { id: 15, unit: TimeUnit.SECOND },
  120 + { id: 30, unit: TimeUnit.SECOND },
  121 + { id: 1, unit: TimeUnit.MINUTE },
  122 + { id: 2, unit: TimeUnit.MINUTE },
  123 + { id: 5, unit: TimeUnit.MINUTE },
  124 + ],
  125 + },
  126 + {
  127 + id: 2,
  128 + unit: TimeUnit.HOUR,
  129 + linkage: [
  130 + { id: 15, unit: TimeUnit.SECOND },
  131 + { id: 30, unit: TimeUnit.SECOND },
  132 + { id: 1, unit: TimeUnit.MINUTE },
  133 + { id: 2, unit: TimeUnit.MINUTE },
  134 + { id: 5, unit: TimeUnit.MINUTE },
  135 + { id: 10, unit: TimeUnit.MINUTE },
  136 + { id: 15, unit: TimeUnit.MINUTE },
  137 + ],
  138 + },
  139 + {
  140 + id: 5,
  141 + unit: TimeUnit.HOUR,
  142 + linkage: [
  143 + { id: 1, unit: TimeUnit.MINUTE },
  144 + { id: 2, unit: TimeUnit.MINUTE },
  145 + { id: 5, unit: TimeUnit.MINUTE },
  146 + { id: 10, unit: TimeUnit.MINUTE },
  147 + { id: 15, unit: TimeUnit.MINUTE },
  148 + { id: 30, unit: TimeUnit.MINUTE },
  149 + ],
  150 + },
  151 + {
  152 + id: 10,
  153 + unit: TimeUnit.HOUR,
  154 + linkage: [
  155 + { id: 2, unit: TimeUnit.MINUTE },
  156 + { id: 5, unit: TimeUnit.MINUTE },
  157 + { id: 10, unit: TimeUnit.MINUTE },
  158 + { id: 15, unit: TimeUnit.MINUTE },
  159 + { id: 30, unit: TimeUnit.MINUTE },
  160 + { id: 1, unit: TimeUnit.HOUR },
  161 + ],
  162 + },
  163 + {
  164 + id: 12,
  165 + unit: TimeUnit.HOUR,
  166 + linkage: [
  167 + { id: 2, unit: TimeUnit.MINUTE },
  168 + { id: 5, unit: TimeUnit.MINUTE },
  169 + { id: 10, unit: TimeUnit.MINUTE },
  170 + { id: 15, unit: TimeUnit.MINUTE },
  171 + { id: 30, unit: TimeUnit.MINUTE },
  172 + { id: 1, unit: TimeUnit.HOUR },
  173 + ],
  174 + },
  175 + {
  176 + id: 1,
  177 + unit: TimeUnit.DAY,
  178 + linkage: [
  179 + { id: 5, unit: TimeUnit.MINUTE },
  180 + { id: 10, unit: TimeUnit.MINUTE },
  181 + { id: 15, unit: TimeUnit.MINUTE },
  182 + { id: 30, unit: TimeUnit.MINUTE },
  183 + { id: 1, unit: TimeUnit.HOUR },
  184 + { id: 2, unit: TimeUnit.HOUR },
  185 + ],
  186 + },
  187 + {
  188 + id: 7,
  189 + unit: TimeUnit.DAY,
  190 + linkage: [
  191 + { id: 30, unit: TimeUnit.MINUTE },
  192 + { id: 1, unit: TimeUnit.HOUR },
  193 + { id: 2, unit: TimeUnit.HOUR },
  194 + { id: 5, unit: TimeUnit.HOUR },
  195 + { id: 10, unit: TimeUnit.HOUR },
  196 + { id: 12, unit: TimeUnit.HOUR },
  197 + { id: 1, unit: TimeUnit.DAY },
  198 + ],
  199 + },
  200 + {
  201 + id: 30,
  202 + unit: TimeUnit.DAY,
  203 + linkage: [
  204 + { id: 2, unit: TimeUnit.HOUR },
  205 + { id: 5, unit: TimeUnit.HOUR },
  206 + { id: 10, unit: TimeUnit.HOUR },
  207 + { id: 12, unit: TimeUnit.HOUR },
  208 + { id: 1, unit: TimeUnit.DAY },
  209 + ],
  210 + },
  211 +].map((item) => {
  212 + return {
  213 + value: item.id * unitConversion[item.unit],
  214 + label: item.id + unitMapping[item.unit],
  215 + linkage: item.linkage.map((item) => {
  216 + return {
  217 + value: item.id * unitConversion[item.unit],
  218 + label: item.id + unitMapping[item.unit],
  219 + };
  220 + }),
  221 + };
  222 +});
  223 +
  224 +const rangeIntervalOption = [
  225 + {
  226 + id: 90,
  227 + unit: TimeUnit.DAY,
  228 + linkage: [
  229 + { id: 5, unit: TimeUnit.HOUR },
  230 + { id: 10, unit: TimeUnit.HOUR },
  231 + { id: 12, unit: TimeUnit.HOUR },
  232 + { id: 1, unit: TimeUnit.DAY },
  233 + { id: 7, unit: TimeUnit.DAY },
  234 + ],
  235 + },
  236 + {
  237 + id: 180,
  238 + unit: TimeUnit.DAY,
  239 + linkage: [
  240 + { id: 10, unit: TimeUnit.HOUR },
  241 + { id: 12, unit: TimeUnit.HOUR },
  242 + { id: 1, unit: TimeUnit.DAY },
  243 + { id: 7, unit: TimeUnit.DAY },
  244 + ],
  245 + },
  246 + {
  247 + id: 360,
  248 + unit: TimeUnit.DAY,
  249 + linkage: [
  250 + { id: 1, unit: TimeUnit.DAY },
  251 + { id: 7, unit: TimeUnit.DAY },
  252 + { id: 30, unit: TimeUnit.DAY },
  253 + ],
  254 + },
  255 +].map((item) => {
  256 + return {
  257 + value: item.id * unitConversion[item.unit],
  258 + label: item.id + unitMapping[item.unit],
  259 + linkage: item.linkage.map((item) => {
  260 + return {
  261 + value: item.id * unitConversion[item.unit],
  262 + label: item.id + unitMapping[item.unit],
  263 + };
  264 + }),
  265 + };
  266 +});
  267 +
  268 +/**
  269 + * @description
  270 + * @param {number} value
  271 + * @returns
  272 + */
  273 +export function getPacketIntervalByValue(value: number) {
  274 + return intervalOption.find((item) => item.value === value)?.linkage || [];
  275 +}
  276 +
  277 +export function getPacketIntervalByRange(
  278 + [start, end] = [null, null] as [Nullable<Moment>, Nullable<Moment>]
  279 +) {
  280 + if (start && end) {
  281 + const value = end.diff(start, 'ms');
  282 + let options: { value: number; label: string }[] = [];
  283 + for (const item of [...intervalOption, ...rangeIntervalOption]) {
  284 + if (item.value <= value) continue;
  285 + if (item.value >= value) {
  286 + options = item.linkage;
  287 + break;
  288 + }
  289 + }
  290 + return options;
  291 + }
  292 + return [];
  293 +}
  1 +export { default as TimePeriodForm } from './TimePeriodForm.vue';
  2 +export { useTimePeriodForm } from './useTimePeriodForm';
  1 +import { nextTick, reactive, ref, unref, watch } from 'vue';
  2 +import { FormActionType, FormProps } from '/@/components/Form';
  3 +import { getDynamicProps } from '/@/utils';
  4 +
  5 +export function useTimePeriodForm(props: FormProps): [Fn, FormActionType] {
  6 + const method = reactive<Partial<FormActionType>>({} as any);
  7 + const isInitialize = ref(false);
  8 + async function register(instance: FormActionType) {
  9 + if (unref(isInitialize)) return;
  10 + await nextTick();
  11 + Object.assign(method, instance);
  12 + isInitialize.value = true;
  13 + watch(
  14 + () => props,
  15 + () => {
  16 + props && instance.setProps(getDynamicProps(props));
  17 + },
  18 + {
  19 + immediate: true,
  20 + deep: true,
  21 + }
  22 + );
  23 + }
  24 +
  25 + return [register, method as FormActionType];
  26 +}
@@ -36,11 +36,12 @@ @@ -36,11 +36,12 @@
36 width="70%" 36 width="70%"
37 :minHeight="400" 37 :minHeight="400"
38 :footer="null" 38 :footer="null"
39 - @cancel="cancelHistoryModal"  
40 :canFullscreen="false" 39 :canFullscreen="false"
  40 + @cancel="handleCancelModal"
41 > 41 >
42 - <BasicForm @register="registerForm" />  
43 - <div v-show="isNull" ref="chartRef" :style="{ height: '600px', width }"> 42 + <TimePeriodForm @register="timePeriodRegister" />
  43 + <!-- <BasicForm @register="registerForm" /> -->
  44 + <div v-show="isNull" ref="chartRef" :style="{ height: '550px', width }">
44 <Loading :loading="loading" :absolute="true" /> 45 <Loading :loading="loading" :absolute="true" />
45 </div> 46 </div>
46 <Empty v-show="!isNull" /> 47 <Empty v-show="!isNull" />
@@ -49,17 +50,15 @@ @@ -49,17 +50,15 @@
49 </div> 50 </div>
50 </template> 51 </template>
51 <script lang="ts"> 52 <script lang="ts">
52 - import { defineComponent, ref, nextTick, unref, onMounted, Ref } from 'vue'; 53 + import { defineComponent, ref, nextTick, unref, onMounted, Ref, onUnmounted } from 'vue';
53 import { useScript } from '/@/hooks/web/useScript'; 54 import { useScript } from '/@/hooks/web/useScript';
54 import { formSchema, columns } from './config.data'; 55 import { formSchema, columns } from './config.data';
55 import { BasicTable, useTable } from '/@/components/Table'; 56 import { BasicTable, useTable } from '/@/components/Table';
56 import { devicePage } from '/@/api/alarm/contact/alarmContact'; 57 import { devicePage } from '/@/api/alarm/contact/alarmContact';
57 - import { Tag, Empty } from 'ant-design-vue'; 58 + import { Tag, Empty, message } from 'ant-design-vue';
58 import { DeviceState } from '/@/api/device/model/deviceModel'; 59 import { DeviceState } from '/@/api/device/model/deviceModel';
59 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils'; 60 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
60 import { useModal, BasicModal } from '/@/components/Modal'; 61 import { useModal, BasicModal } from '/@/components/Modal';
61 - import { BasicForm, useForm } from '/@/components/Form';  
62 - import { schemas } from './config.data';  
63 import { useECharts } from '/@/hooks/web/useECharts'; 62 import { useECharts } from '/@/hooks/web/useECharts';
64 import { 63 import {
65 getDeviceHistoryInfo, 64 getDeviceHistoryInfo,
@@ -77,6 +76,11 @@ @@ -77,6 +76,11 @@
77 import online from '/@/assets/images/online1.png'; 76 import online from '/@/assets/images/online1.png';
78 import lx1 from '/@/assets/images/lx1.png'; 77 import lx1 from '/@/assets/images/lx1.png';
79 import Loading from '/@/components/Loading/src/Loading.vue'; 78 import Loading from '/@/components/Loading/src/Loading.vue';
  79 + import { TimePeriodForm, useTimePeriodForm } from './cpns/TimePeriodForm';
  80 + import { selectDeviceAttrSchema, eChartOptions } from './config.data';
  81 + import { defaultSchemas } from './cpns/TimePeriodForm/config';
  82 + import { QueryWay, SchemaFiled, AggregateDataEnum } from './cpns/TimePeriodForm/config';
  83 + import { formatToDateTime } from '/@/utils/dateUtil';
80 export default defineComponent({ 84 export default defineComponent({
81 name: 'BaiduMap', 85 name: 'BaiduMap',
82 components: { 86 components: {
@@ -84,9 +88,9 @@ @@ -84,9 +88,9 @@
84 Tag, 88 Tag,
85 Empty, 89 Empty,
86 BasicModal, 90 BasicModal,
87 - BasicForm,  
88 DeviceDetailDrawer, 91 DeviceDetailDrawer,
89 Loading, 92 Loading,
  93 + TimePeriodForm,
90 }, 94 },
91 props: { 95 props: {
92 width: { 96 width: {
@@ -100,103 +104,17 @@ @@ -100,103 +104,17 @@
100 }, 104 },
101 setup() { 105 setup() {
102 let entityId = ''; 106 let entityId = '';
103 - let keys = [];  
104 let globalRecord: any = {}; 107 let globalRecord: any = {};
105 const wrapRef = ref<HTMLDivElement | null>(null); 108 const wrapRef = ref<HTMLDivElement | null>(null);
106 const chartRef = ref<HTMLDivElement | null>(null); 109 const chartRef = ref<HTMLDivElement | null>(null);
  110 + const deviceAttrs = ref<string[]>([]);
107 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); 111 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
108 const isNull = ref(true); 112 const isNull = ref(true);
109 const { toPromise } = useScript({ src: BAI_DU_MAP_URL }); 113 const { toPromise } = useScript({ src: BAI_DU_MAP_URL });
110 const [registerDetailDrawer, { openDrawer }] = useDrawer(); 114 const [registerDetailDrawer, { openDrawer }] = useDrawer();
111 const [registerModal, { openModal }] = useModal(); 115 const [registerModal, { openModal }] = useModal();
112 const loading = ref(false); 116 const loading = ref(false);
113 - const [  
114 - registerForm,  
115 - { resetFields, getFieldsValue, setFieldsValue, validate, updateSchema },  
116 - ] = useForm({  
117 - labelWidth: 120,  
118 - schemas,  
119 - async submitFunc() {  
120 - // 表单验证  
121 - await validate();  
122 - let { endTs, interval, agg } = getFieldsValue();  
123 - if (!endTs || !keys.length) return;  
124 - // 数据收集  
125 - const dataArray: any[] = [];  
126 - const startTs = Date.now() - endTs;  
127 - endTs = Date.now();  
128 - // 发送请求  
129 - loading.value = true;  
130 - const res = await getDeviceHistoryInfo({  
131 - entityId,  
132 - keys: keys.join(),  
133 - startTs,  
134 - endTs,  
135 - interval,  
136 - agg,  
137 - });  
138 - // 判断数据对象是否为空  
139 - if (!Object.keys(res).length) {  
140 - isNull.value = false;  
141 - return;  
142 - } else {  
143 - isNull.value = true;  
144 - }  
145 - // 处理数据  
146 - for (const key in res) {  
147 - for (const item1 of res[key]) {  
148 - let { ts, value } = item1;  
149 - const time = moment(ts).format('YYYY-MM-DD HH:mm:ss');  
150 - value = Number(value).toFixed(2);  
151 - dataArray.push([time, value, key]);  
152 - }  
153 - }  
154 - const series: any = keys.map((item) => {  
155 - return {  
156 - name: item,  
157 - type: 'line',  
158 - // stack: 'Total',  
159 - data: dataArray.filter((item1) => item1[2] === item),  
160 - };  
161 - });  
162 - // 设置数据  
163 - setOptions({  
164 - tooltip: {  
165 - trigger: 'axis',  
166 - },  
167 - legend: {  
168 - data: keys,  
169 - },  
170 - grid: {  
171 - left: '3%',  
172 - right: '4%',  
173 - bottom: '3%',  
174 - containLabel: true,  
175 - },  
176 - dataZoom: [  
177 - {  
178 - type: 'inside',  
179 - start: 0,  
180 - end: 50,  
181 - },  
182 - {  
183 - start: 20,  
184 - end: 40,  
185 - },  
186 - ],  
187 - xAxis: {  
188 - type: 'time',  
189 - boundaryGap: false,  
190 - },  
191 - yAxis: {  
192 - type: 'value',  
193 - boundaryGap: [0, '100%'],  
194 - },  
195 - series,  
196 - });  
197 - loading.value = false;  
198 - },  
199 - }); 117 +
200 const [registerTable] = useTable({ 118 const [registerTable] = useTable({
201 api: devicePage, 119 api: devicePage,
202 columns, 120 columns,
@@ -231,7 +149,11 @@ @@ -231,7 +149,11 @@
231 const wrapEl = unref(wrapRef); 149 const wrapEl = unref(wrapRef);
232 const map = new BMap.Map(wrapEl); 150 const map = new BMap.Map(wrapEl);
233 // if (record.deviceInfo.address) { 151 // if (record.deviceInfo.address) {
234 - keys = await getDeviceDataKeys(entityId); 152 + // keys = await getDeviceDataKeys(entityId);
  153 + try {
  154 + deviceAttrs.value = (await getDeviceDataKeys(entityId)) || [];
  155 + } catch (error) {}
  156 +
235 const { name, organizationDTO, deviceState, deviceProfile } = record; 157 const { name, organizationDTO, deviceState, deviceProfile } = record;
236 const { longitude, latitude, address } = record.deviceInfo; 158 const { longitude, latitude, address } = record.deviceInfo;
237 //这里如果没有地理位置 最好设置一个默认位置 不然地图会全蓝 159 //这里如果没有地理位置 最好设置一个默认位置 不然地图会全蓝
@@ -314,27 +236,81 @@ @@ -314,27 +236,81 @@
314 tbDeviceId, 236 tbDeviceId,
315 }); 237 });
316 }; 238 };
317 - const openHistoryModal = async () => {  
318 - openModal(true);  
319 - // 收集参数  
320 - const dataArray: any[] = [];  
321 - const startTs = Date.now() - 86400000; //最近一天  
322 - const endTs = Date.now();  
323 - // 发送请求  
324 - if (!keys.length) {  
325 - isNull.value = false;  
326 - return; 239 +
  240 + const [timePeriodRegister, method] = useTimePeriodForm({
  241 + schemas: [...defaultSchemas, ...selectDeviceAttrSchema],
  242 + async submitFunc() {
  243 + // 表单验证
  244 + await method.validate();
  245 + const value = method.getFieldsValue();
  246 + const searchParams = getSearchParams(value);
  247 +
  248 + if (!hasDeviceAttr()) return;
  249 + // 发送请求
  250 + loading.value = true;
  251 + const res = await getDeviceHistoryInfo(searchParams);
  252 + // 判断数据对象是否为空
  253 + if (!Object.keys(res).length) {
  254 + isNull.value = false;
  255 + return;
  256 + } else {
  257 + isNull.value = true;
  258 + }
  259 + setChartOptions(res, value.keys);
  260 + loading.value = false;
  261 + },
  262 + });
  263 +
  264 + function getSearchParams(value: Recordable) {
  265 + const { startTs, endTs, interval, agg, limit, keys, way } = value;
  266 + if (way === QueryWay.LATEST) {
  267 + return {
  268 + entityId,
  269 + keys: keys ? keys : unref(deviceAttrs).join(),
  270 + startTs: moment().subtract(startTs, 'ms').valueOf(),
  271 + endTs: Date.now(),
  272 + interval,
  273 + agg,
  274 + limit,
  275 + };
327 } else { 276 } else {
328 - isNull.value = true; 277 + return {
  278 + entityId,
  279 + keys: keys ? keys : unref(deviceAttrs).join(),
  280 + startTs: moment(startTs).valueOf(),
  281 + endTs: moment(endTs).valueOf(),
  282 + interval,
  283 + agg,
  284 + limit,
  285 + };
329 } 286 }
  287 + }
  288 +
  289 + const openHistoryModal = async () => {
  290 + openModal(true);
  291 +
  292 + await nextTick();
  293 + method.updateSchema({
  294 + field: 'keys',
  295 + componentProps: {
  296 + options: unref(deviceAttrs).map((item) => ({ label: item, value: item })),
  297 + },
  298 + });
  299 +
  300 + method.setFieldsValue({
  301 + [SchemaFiled.START_TS]: 1 * 24 * 60 * 60 * 1000,
  302 + });
  303 +
  304 + if (!hasDeviceAttr()) return;
  305 +
330 const res = await getDeviceHistoryInfo({ 306 const res = await getDeviceHistoryInfo({
331 entityId, 307 entityId,
332 - keys: keys.join(),  
333 - startTs,  
334 - endTs,  
335 - interval: 7200000, //间隔两小时  
336 - agg: 'AVG', 308 + keys: unref(deviceAttrs).join(),
  309 + startTs: Date.now() - 1 * 24 * 60 * 60 * 1000,
  310 + endTs: Date.now(),
  311 + agg: AggregateDataEnum.NONE,
337 }); 312 });
  313 +
338 // 判断对象是否为空 314 // 判断对象是否为空
339 if (!Object.keys(res).length) { 315 if (!Object.keys(res).length) {
340 isNull.value = false; 316 isNull.value = false;
@@ -342,118 +318,74 @@ @@ -342,118 +318,74 @@
342 } else { 318 } else {
343 isNull.value = true; 319 isNull.value = true;
344 } 320 }
345 - // 处理数据  
346 - for (const key in res) {  
347 - for (const item1 of res[key]) { 321 + setChartOptions(res);
  322 + };
  323 + function hasDeviceAttr() {
  324 + if (!unref(deviceAttrs).length) {
  325 + isNull.value = false;
  326 + return false;
  327 + } else {
  328 + isNull.value = true;
  329 + return true;
  330 + }
  331 + }
  332 +
  333 + function setChartOptions(data, keys?) {
  334 + const dataArray: any[] = [];
  335 + for (const key in data) {
  336 + for (const item1 of data[key]) {
348 let { ts, value } = item1; 337 let { ts, value } = item1;
349 - const time = moment(ts).format('YYYY-MM-DD HH:mm:ss'); 338 + const time = formatToDateTime(ts);
350 value = Number(value).toFixed(2); 339 value = Number(value).toFixed(2);
351 dataArray.push([time, value, key]); 340 dataArray.push([time, value, key]);
352 } 341 }
353 } 342 }
  343 + keys = keys ? [keys] : unref(deviceAttrs);
354 const series: any = keys.map((item) => { 344 const series: any = keys.map((item) => {
355 return { 345 return {
356 name: item, 346 name: item,
357 type: 'line', 347 type: 'line',
358 - // stack: 'Total',  
359 data: dataArray.filter((item1) => item1[2] === item), 348 data: dataArray.filter((item1) => item1[2] === item),
360 }; 349 };
361 }); 350 });
362 - console.log(dataArray);  
363 - // 设置数据;  
364 - setOptions({  
365 - tooltip: {  
366 - trigger: 'axis',  
367 - },  
368 - legend: {  
369 - data: keys,  
370 - },  
371 - grid: {  
372 - left: '3%',  
373 - right: '4%',  
374 - bottom: '3%',  
375 - containLabel: true,  
376 - },  
377 - dataZoom: [  
378 - {  
379 - type: 'inside',  
380 - start: 0,  
381 - end: 50,  
382 - },  
383 - {  
384 - start: 0,  
385 - end: 20,  
386 - },  
387 - ],  
388 - xAxis: {  
389 - type: 'time',  
390 - boundaryGap: false,  
391 - },  
392 - yAxis: {  
393 - type: 'value',  
394 - boundaryGap: [0, '100%'],  
395 - },  
396 - series,  
397 - });  
398 - setFieldsValue({  
399 - endTs: 86400000,  
400 - interval: 7200000,  
401 - agg: 'AVG',  
402 - });  
403 - };  
404 - const cancelHistoryModal = () => {  
405 - resetFields();  
406 - updateSchema({  
407 - field: 'interval',  
408 - componentProps: {  
409 - placeholder: '请选择分组间隔',  
410 - options: [  
411 - {  
412 - label: '5分钟',  
413 - value: 300000,  
414 - },  
415 - {  
416 - label: '10分钟',  
417 - value: 600000,  
418 - },  
419 - {  
420 - label: '15分钟',  
421 - value: 900000,  
422 - },  
423 - {  
424 - label: '30分钟',  
425 - value: 1800000,  
426 - },  
427 - {  
428 - label: '1小时',  
429 - value: 3600000,  
430 - },  
431 - {  
432 - label: '2小时',  
433 - value: 7200000,  
434 - },  
435 - ],  
436 - }, 351 + // 设置数据
  352 + setOptions(eChartOptions(series, keys));
  353 + }
  354 +
  355 + const handleCancelModal = () => {
  356 + method.setFieldsValue({
  357 + [SchemaFiled.WAY]: QueryWay.LATEST,
  358 + [SchemaFiled.KEYS]: null,
  359 + [SchemaFiled.DATE_RANGE]: [],
  360 + [SchemaFiled.INTERVAL]: null,
  361 + [SchemaFiled.LIMIT]: 7,
  362 + [SchemaFiled.AGG]: AggregateDataEnum.NONE,
437 }); 363 });
438 - setOptions({});  
439 }; 364 };
  365 +
440 onMounted(() => { 366 onMounted(() => {
441 initMap(); 367 initMap();
442 (window as any).openDeviceInfoDrawer = openDeviceInfoDrawer; 368 (window as any).openDeviceInfoDrawer = openDeviceInfoDrawer;
443 (window as any).openHistoryModal = openHistoryModal; 369 (window as any).openHistoryModal = openHistoryModal;
444 }); 370 });
  371 +
  372 + onUnmounted(() => {
  373 + (window as any).openDeviceInfoDrawer = null;
  374 + (window as any).openHistoryModal = null;
  375 + });
  376 +
445 return { 377 return {
446 wrapRef, 378 wrapRef,
447 registerTable, 379 registerTable,
448 deviceRowClick, 380 deviceRowClick,
449 DeviceState, 381 DeviceState,
450 registerModal, 382 registerModal,
451 - registerForm,  
452 chartRef, 383 chartRef,
453 isNull, 384 isNull,
454 - cancelHistoryModal,  
455 registerDetailDrawer, 385 registerDetailDrawer,
456 loading, 386 loading,
  387 + timePeriodRegister,
  388 + handleCancelModal,
457 }; 389 };
458 }, 390 },
459 }); 391 });