Commit f23f07bc96c33212e0cf4218f074537c8a145225

Authored by fengtao
2 parents 6bf253e9 e569fd79

Merge branch 'main' into f-dev

... ... @@ -22,7 +22,7 @@ export const getDeviceHistoryInfo = (params) => {
22 22
23 23 // 获取设备数据的keys
24 24 export const getDeviceDataKeys = (id: string) => {
25   - return defHttp.get(
  25 + return defHttp.get<string[]>(
26 26 {
27 27 url: `/plugins/telemetry/DEVICE/${id}/keys/timeseries`,
28 28 },
... ...
... ... @@ -2,7 +2,8 @@ import type { BasicColumn } from '/@/components/Table';
2 2 import type { FormSchema } from '/@/components/Table';
3 3 import { getOrganizationList } from '/@/api/system/system';
4 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 8 export enum AggregateDataEnum {
8 9 MIN = 'MIN',
... ... @@ -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 387 field: 'interval',
390 388 label: '分组间隔',
391 389 component: 'Select',
392   - colProps: {
393   - span: 6,
394   - },
395 390 componentProps: {
396 391 placeholder: '请选择分组间隔',
397 392 getPopupContainer: () => document.body,
... ... @@ -427,6 +422,7 @@ export const schemas: FormSchema[] = [
427 422 field: 'agg',
428 423 label: '数据聚合功能',
429 424 component: 'Select',
  425 + defaultValue: AggregateDataEnum.NONE,
430 426 componentProps: {
431 427 getPopupContainer: () => document.body,
432 428 options: [
... ... @@ -446,10 +442,83 @@ export const schemas: FormSchema[] = [
446 442 label: '求和',
447 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 36 width="70%"
37 37 :minHeight="400"
38 38 :footer="null"
39   - @cancel="cancelHistoryModal"
40 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 45 <Loading :loading="loading" :absolute="true" />
45 46 </div>
46 47 <Empty v-show="!isNull" />
... ... @@ -49,17 +50,15 @@
49 50 </div>
50 51 </template>
51 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 54 import { useScript } from '/@/hooks/web/useScript';
54 55 import { formSchema, columns } from './config.data';
55 56 import { BasicTable, useTable } from '/@/components/Table';
56 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 59 import { DeviceState } from '/@/api/device/model/deviceModel';
59 60 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
60 61 import { useModal, BasicModal } from '/@/components/Modal';
61   - import { BasicForm, useForm } from '/@/components/Form';
62   - import { schemas } from './config.data';
63 62 import { useECharts } from '/@/hooks/web/useECharts';
64 63 import {
65 64 getDeviceHistoryInfo,
... ... @@ -77,6 +76,11 @@
77 76 import online from '/@/assets/images/online1.png';
78 77 import lx1 from '/@/assets/images/lx1.png';
79 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 84 export default defineComponent({
81 85 name: 'BaiduMap',
82 86 components: {
... ... @@ -84,9 +88,9 @@
84 88 Tag,
85 89 Empty,
86 90 BasicModal,
87   - BasicForm,
88 91 DeviceDetailDrawer,
89 92 Loading,
  93 + TimePeriodForm,
90 94 },
91 95 props: {
92 96 width: {
... ... @@ -100,103 +104,17 @@
100 104 },
101 105 setup() {
102 106 let entityId = '';
103   - let keys = [];
104 107 let globalRecord: any = {};
105 108 const wrapRef = ref<HTMLDivElement | null>(null);
106 109 const chartRef = ref<HTMLDivElement | null>(null);
  110 + const deviceAttrs = ref<string[]>([]);
107 111 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
108 112 const isNull = ref(true);
109 113 const { toPromise } = useScript({ src: BAI_DU_MAP_URL });
110 114 const [registerDetailDrawer, { openDrawer }] = useDrawer();
111 115 const [registerModal, { openModal }] = useModal();
112 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 118 const [registerTable] = useTable({
201 119 api: devicePage,
202 120 columns,
... ... @@ -231,7 +149,11 @@
231 149 const wrapEl = unref(wrapRef);
232 150 const map = new BMap.Map(wrapEl);
233 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 157 const { name, organizationDTO, deviceState, deviceProfile } = record;
236 158 const { longitude, latitude, address } = record.deviceInfo;
237 159 //这里如果没有地理位置 最好设置一个默认位置 不然地图会全蓝
... ... @@ -314,27 +236,81 @@
314 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 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 306 const res = await getDeviceHistoryInfo({
331 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 315 if (!Object.keys(res).length) {
340 316 isNull.value = false;
... ... @@ -342,118 +318,74 @@
342 318 } else {
343 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 337 let { ts, value } = item1;
349   - const time = moment(ts).format('YYYY-MM-DD HH:mm:ss');
  338 + const time = formatToDateTime(ts);
350 339 value = Number(value).toFixed(2);
351 340 dataArray.push([time, value, key]);
352 341 }
353 342 }
  343 + keys = keys ? [keys] : unref(deviceAttrs);
354 344 const series: any = keys.map((item) => {
355 345 return {
356 346 name: item,
357 347 type: 'line',
358   - // stack: 'Total',
359 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 366 onMounted(() => {
441 367 initMap();
442 368 (window as any).openDeviceInfoDrawer = openDeviceInfoDrawer;
443 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 377 return {
446 378 wrapRef,
447 379 registerTable,
448 380 deviceRowClick,
449 381 DeviceState,
450 382 registerModal,
451   - registerForm,
452 383 chartRef,
453 384 isNull,
454   - cancelHistoryModal,
455 385 registerDetailDrawer,
456 386 loading,
  387 + timePeriodRegister,
  388 + handleCancelModal,
457 389 };
458 390 },
459 391 });
... ...