Commit 72f924ee95ccddd3f39aa45e5cc9fd84d98ae65b

Authored by xp.Huang
2 parents fa8080ad b1cb3fac

Merge branch 'ww' into 'main'

fix: BUG in teambition

See merge request huang/yun-teng-iot-front!410
@@ -9,7 +9,7 @@ export const getDeviceProfile = () => { @@ -9,7 +9,7 @@ export const getDeviceProfile = () => {
9 }; 9 };
10 10
11 // 获取历史数据 11 // 获取历史数据
12 -export const getDeviceHistoryInfo = (params) => { 12 +export const getDeviceHistoryInfo = (params: Recordable) => {
13 return defHttp.get<HistoryData>( 13 return defHttp.get<HistoryData>(
14 { 14 {
15 url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`, 15 url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`,
@@ -10,6 +10,7 @@ import { @@ -10,6 +10,7 @@ import {
10 } from './tenantInfo'; 10 } from './tenantInfo';
11 import { defHttp } from '/@/utils/http/axios'; 11 import { defHttp } from '/@/utils/http/axios';
12 import { BasicPageParams } from '/@/api/model/baseModel'; 12 import { BasicPageParams } from '/@/api/model/baseModel';
  13 +import { PaginationResult } from '/#/axios';
13 export type QueryTenantProfilesParam = BasicPageParams & OtherParams; 14 export type QueryTenantProfilesParam = BasicPageParams & OtherParams;
14 export type DeleteTenantProfilesParam = OtherParams; 15 export type DeleteTenantProfilesParam = OtherParams;
15 export type OtherParams = { 16 export type OtherParams = {
@@ -47,7 +48,7 @@ export async function deleteTenantProfileApi(ids: string) { @@ -47,7 +48,7 @@ export async function deleteTenantProfileApi(ids: string) {
47 } 48 }
48 49
49 export const getTableTenantProfileApi = (params?: QueryTenantProfilesParam) => { 50 export const getTableTenantProfileApi = (params?: QueryTenantProfilesParam) => {
50 - return defHttp.get({ 51 + return defHttp.get<PaginationResult>({
51 url: Api.getTenantProfile, 52 url: Api.getTenantProfile,
52 params, 53 params,
53 }); 54 });
@@ -67,12 +68,12 @@ export const setTenantProfileIsDefaultApi = (id: string, v, params?: {}) => { @@ -67,12 +68,12 @@ export const setTenantProfileIsDefaultApi = (id: string, v, params?: {}) => {
67 }; 68 };
68 69
69 export const selectTenantProfileApi = async (params?: QueryTenantProfilesParam) => { 70 export const selectTenantProfileApi = async (params?: QueryTenantProfilesParam) => {
70 - const { items } = await getTableTenantProfileApi(params); 71 + const { items, total } = await getTableTenantProfileApi(params);
71 items.forEach((item) => { 72 items.forEach((item) => {
72 item.label = item.name; 73 item.label = item.name;
73 item.value = item.id.id; 74 item.value = item.id.id;
74 }); 75 });
75 - return Promise.resolve<any[]>(items); 76 + return { items, total };
76 }; 77 };
77 78
78 export async function saveTenantProfileApi(params: tenantProfileDTO) { 79 export async function saveTenantProfileApi(params: tenantProfileDTO) {
@@ -37,6 +37,7 @@ import ApiUpload from './components/ApiUpload.vue'; @@ -37,6 +37,7 @@ import ApiUpload from './components/ApiUpload.vue';
37 import ApiSearchSelect from './components/ApiSearchSelect.vue'; 37 import ApiSearchSelect from './components/ApiSearchSelect.vue';
38 import CustomMinMaxInput from './externalCompns/components/CustomMinMaxInput.vue'; 38 import CustomMinMaxInput from './externalCompns/components/CustomMinMaxInput.vue';
39 import StructForm from './externalCompns/components/StructForm/StructForm.vue'; 39 import StructForm from './externalCompns/components/StructForm/StructForm.vue';
  40 +import ApiSelectScrollLoad from './components/ApiSelectScrollLoad.vue';
40 41
41 const componentMap = new Map<ComponentType, Component>(); 42 const componentMap = new Map<ComponentType, Component>();
42 43
@@ -81,6 +82,7 @@ componentMap.set('ApiUpload', ApiUpload); @@ -81,6 +82,7 @@ componentMap.set('ApiUpload', ApiUpload);
81 componentMap.set('ApiSearchSelect', ApiSearchSelect); 82 componentMap.set('ApiSearchSelect', ApiSearchSelect);
82 componentMap.set('CustomMinMaxInput', CustomMinMaxInput); 83 componentMap.set('CustomMinMaxInput', CustomMinMaxInput);
83 componentMap.set('StructForm', StructForm); 84 componentMap.set('StructForm', StructForm);
  85 +componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad);
84 86
85 export function add(compName: ComponentType, component: Component) { 87 export function add(compName: ComponentType, component: Component) {
86 componentMap.set(compName, component); 88 componentMap.set(compName, component);
  1 +<script lang="ts">
  2 + export default {
  3 + inheritAttrs: true,
  4 + };
  5 +</script>
  6 +<script lang="ts" setup>
  7 + import { ref, watchEffect, computed, unref, watch, reactive } from 'vue';
  8 + import { Select, Spin } from 'ant-design-vue';
  9 + import { isFunction } from '/@/utils/is';
  10 + import { useRuleFormItem } from '/@/hooks/component/useFormItem';
  11 + import { useAttrs } from '/@/hooks/core/useAttrs';
  12 + import { get, omit } from 'lodash-es';
  13 + import { LoadingOutlined } from '@ant-design/icons-vue';
  14 + import { useI18n } from '/@/hooks/web/useI18n';
  15 + import { PaginationResult } from '/#/axios';
  16 + import { useDebounceFn } from '@vueuse/core';
  17 +
  18 + type OptionsItem = { label: string; value: string; disabled?: boolean };
  19 +
  20 + type Pagination = Record<'page' | 'pageSize', number>;
  21 +
  22 + const emit = defineEmits(['options-change', 'change']);
  23 + const props = withDefaults(
  24 + defineProps<{
  25 + value?: Recordable | number | string;
  26 + numberToString?: boolean;
  27 + api?: (arg?: Recordable) => Promise<PaginationResult<OptionsItem>>;
  28 + params?: Recordable;
  29 + resultField?: string;
  30 + labelField?: string;
  31 + valueField?: string;
  32 + immediate?: boolean;
  33 + pagenation?: Pagination;
  34 + queryEmptyDataAgin?: boolean;
  35 + }>(),
  36 + {
  37 + resultField: '',
  38 + labelField: 'label',
  39 + valueField: 'value',
  40 + immediate: true,
  41 + queryEmptyDataAgin: true,
  42 + pagenation: () => ({ page: 1, pageSize: 10 }),
  43 + }
  44 + );
  45 +
  46 + const OptionsItem = (_, { attrs }: { attrs: { vNode: any } }) => attrs.vNode;
  47 +
  48 + const options = ref<OptionsItem[]>([]);
  49 + const pagination = reactive(Object.assign({ total: 0, page: 1, pageSize: 10 }, props.pagenation));
  50 + const scrollLoading = ref(false);
  51 + const lock = ref(false);
  52 + const loading = ref(false);
  53 + const isFirstLoad = ref(true);
  54 + const emitData = ref<any[]>([]);
  55 + const attrs = useAttrs();
  56 + const { t } = useI18n();
  57 +
  58 + // Embedded in the form, just use the hook binding to perform form verification
  59 + const [state] = useRuleFormItem(props, 'value', 'change', emitData);
  60 +
  61 + const getOptions = computed(() => {
  62 + const { labelField, valueField, numberToString } = props;
  63 +
  64 + return unref(options).reduce((prev, next: Recordable) => {
  65 + if (next) {
  66 + const value = get(next, valueField);
  67 + prev.push({
  68 + label: next[labelField],
  69 + value: numberToString ? `${value}` : value,
  70 + ...omit(next, [labelField, valueField]),
  71 + });
  72 + }
  73 + return prev;
  74 + }, [] as OptionsItem[]);
  75 + });
  76 +
  77 + watchEffect(() => {
  78 + props.immediate && isFirstLoad.value && fetch();
  79 + });
  80 +
  81 + watch(
  82 + () => props.params,
  83 + () => {
  84 + !unref(isFirstLoad) && fetch();
  85 + },
  86 + { deep: true }
  87 + );
  88 +
  89 + async function fetch() {
  90 + const api = props.api;
  91 + if (!api || !isFunction(api)) return;
  92 + try {
  93 + !unref(getOptions).length ? (loading.value = true) : (scrollLoading.value = true);
  94 + lock.value = true;
  95 + const { total, items } = await api({
  96 + ...props.params,
  97 + page: pagination.page,
  98 + pageSize: pagination.pageSize,
  99 + });
  100 +
  101 + pagination.total = total;
  102 + if (Array.isArray(items)) {
  103 + options.value = [...options.value, ...items];
  104 + emitChange();
  105 + return;
  106 + }
  107 + if (props.resultField) {
  108 + options.value = [...options.value, ...(get(items, props.resultField) || [])];
  109 + }
  110 + emitChange();
  111 + } catch (error) {
  112 + pagination.page = Math.ceil(unref(getOptions).length / pagination.pageSize);
  113 + console.warn(error);
  114 + } finally {
  115 + isFirstLoad.value = false;
  116 + loading.value = false;
  117 + scrollLoading.value = false;
  118 + lock.value = false;
  119 + }
  120 + }
  121 +
  122 + async function handleFetch() {
  123 + if (!props.immediate && unref(isFirstLoad)) {
  124 + await fetch();
  125 + isFirstLoad.value = false;
  126 + }
  127 + }
  128 +
  129 + function emitChange() {
  130 + emit('options-change', unref(getOptions));
  131 + }
  132 +
  133 + function handleChange(_, ...args) {
  134 + emitData.value = args;
  135 + }
  136 +
  137 + async function handlePopupScroll(event: MouseEvent) {
  138 + const { scrollHeight, scrollTop, clientHeight } = event.target as HTMLDivElement;
  139 + if (scrollTop + clientHeight >= scrollHeight) {
  140 + if (unref(getOptions).length < pagination.total && !unref(lock)) {
  141 + pagination.page = pagination.page + 1;
  142 + await fetch();
  143 + }
  144 + }
  145 + }
  146 +
  147 + const debounceHandlePopupScroll = useDebounceFn(handlePopupScroll, 100);
  148 +</script>
  149 +
  150 +<template>
  151 + <Select
  152 + @dropdownVisibleChange="handleFetch"
  153 + v-bind="attrs"
  154 + @change="handleChange"
  155 + :options="getOptions"
  156 + v-model:value="state"
  157 + @popup-scroll="debounceHandlePopupScroll"
  158 + >
  159 + <template #[item]="data" v-for="item in Object.keys($slots)">
  160 + <slot :name="item" v-bind="data || {}"></slot>
  161 + </template>
  162 + <template #suffixIcon v-if="loading">
  163 + <LoadingOutlined spin />
  164 + </template>
  165 + <template #notFoundContent v-if="loading">
  166 + <span>
  167 + <LoadingOutlined spin class="mr-1" />
  168 + {{ t('component.form.apiSelectNotFound') }}
  169 + </span>
  170 + </template>
  171 + <template #dropdownRender="{ menuNode }">
  172 + <OptionsItem :vNode="menuNode" />
  173 + <div v-show="scrollLoading" class="flex justify-center">
  174 + <Spin size="small" />
  175 + </div>
  176 + </template>
  177 + </Select>
  178 +</template>
@@ -118,4 +118,5 @@ export type ComponentType = @@ -118,4 +118,5 @@ export type ComponentType =
118 | 'IconDrawer' 118 | 'IconDrawer'
119 | 'ApiUpload' 119 | 'ApiUpload'
120 | 'ApiSearchSelect' 120 | 'ApiSearchSelect'
121 - | 'StructForm'; 121 + | 'StructForm'
  122 + | 'ApiSelectScrollLoad';
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import moment from 'moment';  
3 import { nextTick, onMounted, onUnmounted, Ref, ref, unref } from 'vue'; 2 import { nextTick, onMounted, onUnmounted, Ref, ref, unref } from 'vue';
4 - import { getDeviceDataKeys, getDeviceHistoryInfo } from '/@/api/alarm/position'; 3 + import { getDeviceHistoryInfo } from '/@/api/alarm/position';
5 import { Empty, Spin } from 'ant-design-vue'; 4 import { Empty, Spin } from 'ant-design-vue';
6 import { useECharts } from '/@/hooks/web/useECharts'; 5 import { useECharts } from '/@/hooks/web/useECharts';
7 - import { dateUtil } from '/@/utils/dateUtil';  
8 - import {  
9 - AggregateDataEnum,  
10 - eChartOptions,  
11 - selectDeviceAttrSchema,  
12 - } from '/@/views/device/localtion/config.data'; 6 + import { AggregateDataEnum, selectDeviceAttrSchema } from '/@/views/device/localtion/config.data';
13 import { useTimePeriodForm } from '/@/views/device/localtion/cpns/TimePeriodForm'; 7 import { useTimePeriodForm } from '/@/views/device/localtion/cpns/TimePeriodForm';
14 - import { defaultSchemas, QueryWay } from '/@/views/device/localtion/cpns/TimePeriodForm/config'; 8 + import { defaultSchemas } from '/@/views/device/localtion/cpns/TimePeriodForm/config';
15 import TimePeriodForm from '/@/views/device/localtion/cpns/TimePeriodForm/TimePeriodForm.vue'; 9 import TimePeriodForm from '/@/views/device/localtion/cpns/TimePeriodForm/TimePeriodForm.vue';
16 import { SchemaFiled } from '/@/views/report/config/config.data'; 10 import { SchemaFiled } from '/@/views/report/config/config.data';
17 import { useGridLayout } from '/@/hooks/component/useGridLayout'; 11 import { useGridLayout } from '/@/hooks/component/useGridLayout';
18 import { ColEx } from '/@/components/Form/src/types'; 12 import { ColEx } from '/@/components/Form/src/types';
  13 + import { useHistoryData } from '../../hook/useHistoryData';
19 14
20 interface DeviceDetail { 15 interface DeviceDetail {
21 tbDeviceId: string; 16 tbDeviceId: string;
  17 + deviceProfileId: string;
22 } 18 }
23 19
24 const props = defineProps<{ 20 const props = defineProps<{
@@ -28,36 +24,12 @@ @@ -28,36 +24,12 @@
28 24
29 const chartRef = ref(); 25 const chartRef = ref();
30 26
31 - const deviceAttrs = ref<string[]>([]);  
32 -  
33 const loading = ref(false); 27 const loading = ref(false);
34 28
35 const isNull = ref(false); 29 const isNull = ref(false);
36 30
37 - function getSearchParams(value: Recordable) {  
38 - const { startTs, endTs, interval, agg, limit, keys, way } = value;  
39 - if (way === QueryWay.LATEST) {  
40 - return {  
41 - entityId: props.deviceDetail.tbDeviceId,  
42 - keys: keys ? keys : unref(deviceAttrs).join(),  
43 - startTs: moment().subtract(startTs, 'ms').valueOf(),  
44 - endTs: Date.now(),  
45 - interval,  
46 - agg,  
47 - limit,  
48 - };  
49 - } else {  
50 - return {  
51 - entityId: props.deviceDetail.tbDeviceId,  
52 - keys: keys ? keys : unref(deviceAttrs).join(),  
53 - startTs: moment(startTs).valueOf(),  
54 - endTs: moment(endTs).valueOf(),  
55 - interval,  
56 - agg,  
57 - limit,  
58 - };  
59 - }  
60 - } 31 + const { deviceAttrs, getDeviceKeys, getSearchParams, setChartOptions, getDeviceAttribute } =
  32 + useHistoryData();
61 33
62 function hasDeviceAttr() { 34 function hasDeviceAttr() {
63 if (!unref(deviceAttrs).length) { 35 if (!unref(deviceAttrs).length) {
@@ -69,28 +41,6 @@ @@ -69,28 +41,6 @@
69 41
70 const { setOptions, getInstance } = useECharts(chartRef as Ref<HTMLDivElement>); 42 const { setOptions, getInstance } = useECharts(chartRef as Ref<HTMLDivElement>);
71 43
72 - function setChartOptions(data, keys?) {  
73 - const dataArray: any[] = [];  
74 - for (const key in data) {  
75 - for (const item1 of data[key]) {  
76 - let { ts, value } = item1;  
77 - const time = dateUtil(ts).format('YYYY-MM-DD HH:mm:ss');  
78 - value = Number(value).toFixed(2);  
79 - dataArray.push([time, value, key]);  
80 - }  
81 - }  
82 - keys = keys ? [keys] : unref(deviceAttrs);  
83 - const series: any = keys.map((item) => {  
84 - return {  
85 - name: item,  
86 - type: 'line',  
87 - data: dataArray.filter((item1) => item1[2] === item),  
88 - };  
89 - });  
90 - // 设置数据  
91 - setOptions(eChartOptions(series, keys));  
92 - }  
93 -  
94 const [register, method] = useTimePeriodForm({ 44 const [register, method] = useTimePeriodForm({
95 schemas: [...defaultSchemas, ...selectDeviceAttrSchema], 45 schemas: [...defaultSchemas, ...selectDeviceAttrSchema],
96 baseColProps: useGridLayout(2, 3, 4) as unknown as ColEx, 46 baseColProps: useGridLayout(2, 3, 4) as unknown as ColEx,
@@ -103,7 +53,10 @@ @@ -103,7 +53,10 @@
103 if (!hasDeviceAttr()) return; 53 if (!hasDeviceAttr()) return;
104 // 发送请求 54 // 发送请求
105 loading.value = true; 55 loading.value = true;
106 - const res = await getDeviceHistoryInfo(searchParams); 56 + const res = await getDeviceHistoryInfo({
  57 + ...searchParams,
  58 + entityId: props.deviceDetail.tbDeviceId,
  59 + });
107 // 判断数据对象是否为空 60 // 判断数据对象是否为空
108 if (!Object.keys(res).length) { 61 if (!Object.keys(res).length) {
109 isNull.value = false; 62 isNull.value = false;
@@ -111,16 +64,19 @@ @@ -111,16 +64,19 @@
111 } else { 64 } else {
112 isNull.value = true; 65 isNull.value = true;
113 } 66 }
114 - setChartOptions(res, value.keys); 67 +
  68 + const selectedKeys = unref(deviceAttrs).find(
  69 + (item) => item.identifier === value[SchemaFiled.KEYS]
  70 + );
  71 +
  72 + setOptions(setChartOptions(res, selectedKeys));
115 loading.value = false; 73 loading.value = false;
116 }, 74 },
117 }); 75 });
118 76
119 const getDeviceDataKey = async () => { 77 const getDeviceDataKey = async () => {
120 - const { tbDeviceId } = props.deviceDetail || {};  
121 try { 78 try {
122 - deviceAttrs.value = (await getDeviceDataKeys(tbDeviceId)) || [];  
123 - 79 + await getDeviceAttribute(props.deviceDetail);
124 if (props.attr) { 80 if (props.attr) {
125 method.setFieldsValue({ keys: props.attr }); 81 method.setFieldsValue({ keys: props.attr });
126 } 82 }
@@ -132,7 +88,7 @@ @@ -132,7 +88,7 @@
132 method.updateSchema({ 88 method.updateSchema({
133 field: 'keys', 89 field: 'keys',
134 componentProps: { 90 componentProps: {
135 - options: unref(deviceAttrs).map((item) => ({ label: item, value: item })), 91 + options: unref(deviceAttrs).map((item) => ({ label: item.name, value: item.identifier })),
136 }, 92 },
137 }); 93 });
138 94
@@ -144,7 +100,7 @@ @@ -144,7 +100,7 @@
144 100
145 if (!hasDeviceAttr()) return; 101 if (!hasDeviceAttr()) return;
146 102
147 - const keys = props.attr ? props.attr : unref(deviceAttrs).join(); 103 + const keys = props.attr ? props.attr : unref(getDeviceKeys).join();
148 104
149 const res = await getDeviceHistoryInfo({ 105 const res = await getDeviceHistoryInfo({
150 entityId: props.deviceDetail.tbDeviceId, 106 entityId: props.deviceDetail.tbDeviceId,
@@ -162,7 +118,9 @@ @@ -162,7 +118,9 @@
162 } else { 118 } else {
163 isNull.value = true; 119 isNull.value = true;
164 } 120 }
165 - setChartOptions(res, props.attr); 121 + const selectedKeys = unref(deviceAttrs).find((item) => item.identifier === props.attr);
  122 +
  123 + setOptions(setChartOptions(res, selectedKeys));
166 }; 124 };
167 125
168 onMounted(async () => { 126 onMounted(async () => {
@@ -233,7 +233,9 @@ @@ -233,7 +233,9 @@
233 <div>{{ item.value || '--' }}</div> 233 <div>{{ item.value || '--' }}</div>
234 <div>{{ item.unit }}</div> 234 <div>{{ item.unit }}</div>
235 </div> 235 </div>
236 - <div class="text-dark-800 text-xs">{{ formatToDateTime(item.time) || '--' }}</div> 236 + <div class="text-dark-800 text-xs">
  237 + {{ item.value ? formatToDateTime(item.time) : '--' }}
  238 + </div>
237 </section> 239 </section>
238 </Card> 240 </Card>
239 </List.Item> 241 </List.Item>
  1 +import { EChartsOption } from 'echarts';
  2 +import moment from 'moment';
  3 +import { computed, ref, unref } from 'vue';
  4 +import { eChartOptions } from '../../localtion/config.data';
  5 +import { HistoryData } from '/@/api/alarm/position/model';
  6 +import { getDeviceAttributes } from '/@/api/dataBoard';
  7 +import { DeviceAttributeRecord } from '/@/api/dataBoard/model';
  8 +import { dateUtil } from '/@/utils/dateUtil';
  9 +import { QueryWay } from '/@/views/report/config/config.data';
  10 +import { SchemaFiled } from '/@/views/visual/board/detail/config/historyTrend.config';
  11 +import { DEFAULT_DATE_FORMAT } from '/@/views/visual/board/detail/config/util';
  12 +
  13 +interface DeviceOption {
  14 + deviceProfileId: string;
  15 +}
  16 +
  17 +export function useHistoryData() {
  18 + const deviceAttrs = ref<DeviceAttributeRecord[]>([]);
  19 +
  20 + const getDeviceKeys = computed(() => {
  21 + return unref(deviceAttrs).map((item) => item.identifier);
  22 + });
  23 +
  24 + const getDeviceAttribute = async (record: DeviceOption) => {
  25 + try {
  26 + const { deviceProfileId } = record;
  27 + deviceAttrs.value = (await getDeviceAttributes({ deviceProfileId })) || [];
  28 + } catch (error) {
  29 + throw error;
  30 + }
  31 + };
  32 +
  33 + function getSearchParams(value: Partial<Record<SchemaFiled, string>>) {
  34 + const { startTs, endTs, interval, agg, limit, keys, way, deviceId } = value;
  35 + const basicRecord = {
  36 + entityId: deviceId,
  37 + keys: keys ? keys : unref(getDeviceKeys).join(),
  38 + interval,
  39 + agg,
  40 + limit,
  41 + };
  42 + if (way === QueryWay.LATEST) {
  43 + return Object.assign(basicRecord, {
  44 + startTs: moment().subtract(startTs, 'ms').valueOf(),
  45 + endTs: Date.now(),
  46 + });
  47 + } else {
  48 + return Object.assign(basicRecord, {
  49 + startTs: moment(startTs).valueOf(),
  50 + endTs: moment(endTs).valueOf(),
  51 + });
  52 + }
  53 + }
  54 +
  55 + function setChartOptions(
  56 + data: HistoryData,
  57 + keys?: DeviceAttributeRecord | DeviceAttributeRecord[]
  58 + ) {
  59 + const dataArray: [string, string, string][] = [];
  60 + for (const key in data) {
  61 + for (const item of data[key]) {
  62 + let { value } = item;
  63 + const { ts } = item;
  64 + const time = dateUtil(ts).format(DEFAULT_DATE_FORMAT);
  65 + value = Number(value).toFixed(2);
  66 + dataArray.push([time, value, key as string]);
  67 + }
  68 + }
  69 +
  70 + keys = keys ? [keys as DeviceAttributeRecord] : unref(deviceAttrs);
  71 + const legend = keys.map((item) => item.name);
  72 +
  73 + const series: EChartsOption['series'] = (keys as DeviceAttributeRecord[]).map((item) => {
  74 + return {
  75 + name: item.name,
  76 + type: 'line',
  77 + data: dataArray.filter((temp) => temp[2] === item.identifier),
  78 + };
  79 + });
  80 +
  81 + return eChartOptions(series, legend);
  82 + }
  83 +
  84 + return {
  85 + deviceAttrs,
  86 + getDeviceKeys,
  87 + getDeviceAttribute,
  88 + getSearchParams,
  89 + setChartOptions,
  90 + };
  91 +}
@@ -78,11 +78,7 @@ @@ -78,11 +78,7 @@
78 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils'; 78 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
79 import { useModal, BasicModal } from '/@/components/Modal'; 79 import { useModal, BasicModal } from '/@/components/Modal';
80 import { useECharts } from '/@/hooks/web/useECharts'; 80 import { useECharts } from '/@/hooks/web/useECharts';
81 - import {  
82 - getDeviceHistoryInfo,  
83 - getDeviceDataKeys,  
84 - getDeviceActiveTime,  
85 - } from '/@/api/alarm/position'; 81 + import { getDeviceHistoryInfo, getDeviceActiveTime } from '/@/api/alarm/position';
86 import { useDrawer } from '/@/components/Drawer'; 82 import { useDrawer } from '/@/components/Drawer';
87 import DeviceDetailDrawer from '/@/views/device/list/cpns/modal/DeviceDetailDrawer.vue'; 83 import DeviceDetailDrawer from '/@/views/device/list/cpns/modal/DeviceDetailDrawer.vue';
88 import moment from 'moment'; 84 import moment from 'moment';
@@ -96,12 +92,12 @@ @@ -96,12 +92,12 @@
96 import lx1 from '/@/assets/images/lx1.png'; 92 import lx1 from '/@/assets/images/lx1.png';
97 import Loading from '/@/components/Loading/src/Loading.vue'; 93 import Loading from '/@/components/Loading/src/Loading.vue';
98 import { TimePeriodForm, useTimePeriodForm } from './cpns/TimePeriodForm'; 94 import { TimePeriodForm, useTimePeriodForm } from './cpns/TimePeriodForm';
99 - import { selectDeviceAttrSchema, eChartOptions } from './config.data'; 95 + import { selectDeviceAttrSchema } from './config.data';
100 import { defaultSchemas } from './cpns/TimePeriodForm/config'; 96 import { defaultSchemas } from './cpns/TimePeriodForm/config';
101 import { QueryWay, SchemaFiled, AggregateDataEnum } from './cpns/TimePeriodForm/config'; 97 import { QueryWay, SchemaFiled, AggregateDataEnum } from './cpns/TimePeriodForm/config';
102 - import { dateUtil } from '/@/utils/dateUtil';  
103 import { Spin } from 'ant-design-vue'; 98 import { Spin } from 'ant-design-vue';
104 import { useAsyncQueue } from './useAsyncQueue'; 99 import { useAsyncQueue } from './useAsyncQueue';
  100 + import { useHistoryData } from '../list/hook/useHistoryData';
105 101
106 interface DeviceInfo { 102 interface DeviceInfo {
107 alarmStatus: 0 | 1; 103 alarmStatus: 0 | 1;
@@ -112,6 +108,7 @@ @@ -112,6 +108,7 @@
112 deviceProfile: { default: boolean; enabled: boolean; name: string; transportType: string }; 108 deviceProfile: { default: boolean; enabled: boolean; name: string; transportType: string };
113 deviceInfo: { longitude: string; latitude: string; address: string }; 109 deviceInfo: { longitude: string; latitude: string; address: string };
114 deviceType?: string; 110 deviceType?: string;
  111 + deviceProfileId: string;
115 } 112 }
116 type MarkerList = DeviceInfo & { marker: any; label: any }; 113 type MarkerList = DeviceInfo & { marker: any; label: any };
117 114
@@ -142,8 +139,7 @@ @@ -142,8 +139,7 @@
142 let globalRecord: any = {}; 139 let globalRecord: any = {};
143 const wrapRef = ref<HTMLDivElement | null>(null); 140 const wrapRef = ref<HTMLDivElement | null>(null);
144 const chartRef = ref<HTMLDivElement | null>(null); 141 const chartRef = ref<HTMLDivElement | null>(null);
145 - const deviceAttrs = ref<string[]>([]);  
146 - const { setOptions, getInstance, resize } = useECharts(chartRef as Ref<HTMLDivElement>); 142 + const { setOptions, getInstance } = useECharts(chartRef as Ref<HTMLDivElement>);
147 const isNull = ref(true); 143 const isNull = ref(true);
148 const { toPromise } = useScript({ src: BAI_DU_MAP_URL }); 144 const { toPromise } = useScript({ src: BAI_DU_MAP_URL });
149 const [registerDetailDrawer, { openDrawer }] = useDrawer(); 145 const [registerDetailDrawer, { openDrawer }] = useDrawer();
@@ -154,6 +150,9 @@ @@ -154,6 +150,9 @@
154 150
155 const loading = ref(false); 151 const loading = ref(false);
156 152
  153 + const { deviceAttrs, getDeviceKeys, getDeviceAttribute, getSearchParams, setChartOptions } =
  154 + useHistoryData();
  155 +
157 const [registerTable] = useTable({ 156 const [registerTable] = useTable({
158 api: devicePage, 157 api: devicePage,
159 columns, 158 columns,
@@ -259,7 +258,7 @@ @@ -259,7 +258,7 @@
259 entityId = record.tbDeviceId; 258 entityId = record.tbDeviceId;
260 globalRecord = record; 259 globalRecord = record;
261 try { 260 try {
262 - deviceAttrs.value = (await getDeviceDataKeys(entityId)) || []; 261 + await getDeviceAttribute(record);
263 } catch (error) {} 262 } catch (error) {}
264 263
265 const options = { 264 const options = {
@@ -350,7 +349,7 @@ @@ -350,7 +349,7 @@
350 if (!hasDeviceAttr()) return; 349 if (!hasDeviceAttr()) return;
351 // 发送请求 350 // 发送请求
352 loading.value = true; 351 loading.value = true;
353 - const res = await getDeviceHistoryInfo(searchParams); 352 + const res = await getDeviceHistoryInfo({ ...searchParams, entityId });
354 loading.value = false; 353 loading.value = false;
355 // 判断数据对象是否为空 354 // 判断数据对象是否为空
356 if (!Object.keys(res).length) { 355 if (!Object.keys(res).length) {
@@ -359,35 +358,13 @@ @@ -359,35 +358,13 @@
359 } else { 358 } else {
360 isNull.value = true; 359 isNull.value = true;
361 } 360 }
362 - setChartOptions(res, value.keys); 361 + const selectedKeys = unref(deviceAttrs).find(
  362 + (item) => item.identifier === value[SchemaFiled.KEYS]
  363 + );
  364 + setOptions(setChartOptions(res, selectedKeys));
363 }, 365 },
364 }); 366 });
365 367
366 - function getSearchParams(value: Recordable) {  
367 - const { startTs, endTs, interval, agg, limit, keys, way } = value;  
368 - if (way === QueryWay.LATEST) {  
369 - return {  
370 - entityId,  
371 - keys: keys ? keys : unref(deviceAttrs).join(),  
372 - startTs: moment().subtract(startTs, 'ms').valueOf(),  
373 - endTs: Date.now(),  
374 - interval,  
375 - agg,  
376 - limit,  
377 - };  
378 - } else {  
379 - return {  
380 - entityId,  
381 - keys: keys ? keys : unref(deviceAttrs).join(),  
382 - startTs: moment(startTs).valueOf(),  
383 - endTs: moment(endTs).valueOf(),  
384 - interval,  
385 - agg,  
386 - limit,  
387 - };  
388 - }  
389 - }  
390 -  
391 const openHistoryModal = async () => { 368 const openHistoryModal = async () => {
392 openModal(true); 369 openModal(true);
393 370
@@ -395,7 +372,10 @@ @@ -395,7 +372,10 @@
395 method.updateSchema({ 372 method.updateSchema({
396 field: 'keys', 373 field: 'keys',
397 componentProps: { 374 componentProps: {
398 - options: unref(deviceAttrs).map((item) => ({ label: item, value: item })), 375 + options: unref(deviceAttrs).map((item) => ({
  376 + label: item.name,
  377 + value: item.identifier,
  378 + })),
399 }, 379 },
400 }); 380 });
401 381
@@ -409,7 +389,7 @@ @@ -409,7 +389,7 @@
409 389
410 const res = await getDeviceHistoryInfo({ 390 const res = await getDeviceHistoryInfo({
411 entityId, 391 entityId,
412 - keys: unref(deviceAttrs).join(), 392 + keys: unref(getDeviceKeys).join(),
413 startTs: Date.now() - 1 * 24 * 60 * 60 * 1000, 393 startTs: Date.now() - 1 * 24 * 60 * 60 * 1000,
414 endTs: Date.now(), 394 endTs: Date.now(),
415 agg: AggregateDataEnum.NONE, 395 agg: AggregateDataEnum.NONE,
@@ -423,7 +403,7 @@ @@ -423,7 +403,7 @@
423 } else { 403 } else {
424 isNull.value = true; 404 isNull.value = true;
425 } 405 }
426 - setChartOptions(res); 406 + setOptions(setChartOptions(res));
427 }; 407 };
428 function hasDeviceAttr() { 408 function hasDeviceAttr() {
429 if (!unref(deviceAttrs).length) { 409 if (!unref(deviceAttrs).length) {
@@ -435,29 +415,6 @@ @@ -435,29 +415,6 @@
435 } 415 }
436 } 416 }
437 417
438 - function setChartOptions(data, keys?) {  
439 - const dataArray: any[] = [];  
440 - for (const key in data) {  
441 - for (const item1 of data[key]) {  
442 - let { ts, value } = item1;  
443 - const time = dateUtil(ts).format('YYYY-MM-DD HH:mm:ss');  
444 - value = Number(value).toFixed(2);  
445 - dataArray.push([time, value, key]);  
446 - }  
447 - }  
448 - keys = keys ? [keys] : unref(deviceAttrs);  
449 - const series: any = keys.map((item) => {  
450 - return {  
451 - name: item,  
452 - type: 'line',  
453 - data: dataArray.filter((item1) => item1[2] === item),  
454 - };  
455 - });  
456 - resize();  
457 - // 设置数据  
458 - setOptions(eChartOptions(series, keys));  
459 - }  
460 -  
461 const handleCancelModal = () => { 418 const handleCancelModal = () => {
462 method.setFieldsValue({ 419 method.setFieldsValue({
463 [SchemaFiled.WAY]: QueryWay.LATEST, 420 [SchemaFiled.WAY]: QueryWay.LATEST,
1 import { BasicColumn } from '/@/components/Table/src/types/table'; 1 import { BasicColumn } from '/@/components/Table/src/types/table';
2 import { FormSchema } from '/@/components/Form'; 2 import { FormSchema } from '/@/components/Form';
3 import { getAllRoleList } from '/@/api/system/system'; 3 import { getAllRoleList } from '/@/api/system/system';
4 -import { selectTenantProfileApi } from '/@/api/tenant/tenantApi'; 4 +import { getTableTenantProfileApi, QueryTenantProfilesParam } from '/@/api/tenant/tenantApi';
5 import { RoleEnum } from '/@/enums/roleEnum'; 5 import { RoleEnum } from '/@/enums/roleEnum';
6 6
7 export function getBasicColumns(): BasicColumn[] { 7 export function getBasicColumns(): BasicColumn[] {
@@ -123,16 +123,26 @@ export const tenantFormSchema: FormSchema[] = [ @@ -123,16 +123,26 @@ export const tenantFormSchema: FormSchema[] = [
123 { 123 {
124 field: 'tenantProfileId', 124 field: 'tenantProfileId',
125 label: '租户配置', 125 label: '租户配置',
126 - component: 'ApiSelect', 126 + component: 'ApiSelectScrollLoad',
127 required: true, 127 required: true,
128 - defaultValue: 'Default',  
129 - componentProps: {  
130 - api: selectTenantProfileApi,  
131 - showSearch: true,  
132 - params: {  
133 - page: 1,  
134 - pageSize: 100,  
135 - }, 128 + componentProps: ({ formActionType }) => {
  129 + const { setFieldsValue } = formActionType;
  130 + return {
  131 + api: async (params: QueryTenantProfilesParam) => {
  132 + const { items, total } = await getTableTenantProfileApi(params);
  133 + const firstRecord = items.at(0);
  134 + if (firstRecord) {
  135 + setFieldsValue({ tenantProfileId: firstRecord.id.id });
  136 + }
  137 + return { items, total };
  138 + },
  139 + showSearch: true,
  140 + labelField: 'name',
  141 + valueField: 'id.id',
  142 + filterOption: (inputValue: string, options: Record<'label' | 'value', string>) => {
  143 + return options.label.toLowerCase().includes(inputValue.toLowerCase());
  144 + },
  145 + };
136 }, 146 },
137 }, 147 },
138 { 148 {
@@ -15,10 +15,10 @@ @@ -15,10 +15,10 @@
15 const emit = defineEmits(['register', 'ok']); 15 const emit = defineEmits(['register', 'ok']);
16 16
17 const [registerForm, { updateSchema, setFieldsValue, validate, getFieldsValue }] = useForm({ 17 const [registerForm, { updateSchema, setFieldsValue, validate, getFieldsValue }] = useForm({
18 - schemas: formSchema, 18 + schemas: formSchema(),
19 showActionButtonGroup: false, 19 showActionButtonGroup: false,
20 fieldMapToTime: [ 20 fieldMapToTime: [
21 - [SchemaFiled.DATE_RANGE, [SchemaFiled.START_TS, SchemaFiled.END_TS], 'YYYY-MM-DD HH:ss'], 21 + [SchemaFiled.DATE_RANGE, [SchemaFiled.START_TS, SchemaFiled.END_TS], 'YYYY-MM-DD HH:mm:ss'],
22 ], 22 ],
23 }); 23 });
24 24
@@ -214,7 +214,7 @@ @@ -214,7 +214,7 @@
214 </script> 214 </script>
215 215
216 <template> 216 <template>
217 - <div class="w-full h-full flex justify-center items-center flex-col p-2 no-drag"> 217 + <div class="w-full h-full flex justify-center items-center flex-col p-2">
218 <div 218 <div
219 class="w-full flex" 219 class="w-full flex"
220 v-if="props.layout?.componentType === FrontComponent.MAP_COMPONENT_TRACK_HISTORY" 220 v-if="props.layout?.componentType === FrontComponent.MAP_COMPONENT_TRACK_HISTORY"
@@ -241,8 +241,12 @@ @@ -241,8 +241,12 @@
241 </span> 241 </span>
242 </Button> 242 </Button>
243 </div> 243 </div>
244 - <Spin class="w-full h-full" :spinning="!prepare" tip="Loading..." />  
245 - <div ref="wrapRef" :id="wrapId" class="w-full h-full"></div> 244 + <Spin
  245 + class="w-full h-full !flex flex-col justify-center items-center pointer-events-none"
  246 + :spinning="!prepare"
  247 + tip="地图加载中..."
  248 + />
  249 + <div v-show="prepare" ref="wrapRef" :id="wrapId" class="w-full h-full no-drag"></div>
246 <HistoryDataModel @register="register" @ok="handleRenderHistroyData" /> 250 <HistoryDataModel @register="register" @ok="handleRenderHistroyData" />
247 </div> 251 </div>
248 </template> 252 </template>
@@ -267,12 +267,7 @@ @@ -267,12 +267,7 @@
267 <h3 class="w-24 flex-shrink-0 text-right pr-2 my-4">选择数据源</h3> 267 <h3 class="w-24 flex-shrink-0 text-right pr-2 my-4">选择数据源</h3>
268 <section ref="formListEl"> 268 <section ref="formListEl">
269 <div v-for="item in dataSource" :data-id="item.id" :key="item.id" class="flex bg-light-50"> 269 <div v-for="item in dataSource" :data-id="item.id" :key="item.id" class="flex bg-light-50">
270 - <div  
271 - class="w-24 text-right leading-30px pr-8px flex right"  
272 - style="flex: 0 0 96px; justify-content: right"  
273 - >  
274 - 选择设备  
275 - </div> 270 + <div class="w-24 text-right flex right justify-end"> 选择设备 </div>
276 <div class="pl-2 flex-auto"> 271 <div class="pl-2 flex-auto">
277 <component 272 <component
278 :frontId="$props.frontId" 273 :frontId="$props.frontId"
1 <script lang="ts" setup> 1 <script lang="ts" setup>
2 - import moment from 'moment';  
3 import { nextTick, Ref, ref, unref } from 'vue'; 2 import { nextTick, Ref, ref, unref } from 'vue';
4 import { getDeviceHistoryInfo } from '/@/api/alarm/position'; 3 import { getDeviceHistoryInfo } from '/@/api/alarm/position';
5 import { Empty } from 'ant-design-vue'; 4 import { Empty } from 'ant-design-vue';
6 import { useECharts } from '/@/hooks/web/useECharts'; 5 import { useECharts } from '/@/hooks/web/useECharts';
7 - import { dateUtil } from '/@/utils/dateUtil';  
8 - import { AggregateDataEnum, eChartOptions } from '/@/views/device/localtion/config.data'; 6 + import { AggregateDataEnum } from '/@/views/device/localtion/config.data';
9 import { useGridLayout } from '/@/hooks/component/useGridLayout'; 7 import { useGridLayout } from '/@/hooks/component/useGridLayout';
10 import { ColEx } from '/@/components/Form/src/types'; 8 import { ColEx } from '/@/components/Form/src/types';
11 - import { DataSource, DeviceAttributeRecord } from '/@/api/dataBoard/model'; 9 + import { DataSource } from '/@/api/dataBoard/model';
12 import { useForm, BasicForm } from '/@/components/Form'; 10 import { useForm, BasicForm } from '/@/components/Form';
13 - import { formSchema, QueryWay, SchemaFiled } from '../config/historyTrend.config';  
14 - import { DEFAULT_DATE_FORMAT } from '../config/util'; 11 + import { formSchema, SchemaFiled } from '../config/historyTrend.config';
15 import { Loading } from '/@/components/Loading'; 12 import { Loading } from '/@/components/Loading';
16 import BasicModal from '/@/components/Modal/src/BasicModal.vue'; 13 import BasicModal from '/@/components/Modal/src/BasicModal.vue';
17 import { useModalInner } from '/@/components/Modal'; 14 import { useModalInner } from '/@/components/Modal';
18 - import { getAllDeviceByOrg, getDeviceAttributes } from '/@/api/dataBoard';  
19 - import { HistoryData } from '/@/api/alarm/position/model';  
20 - import { EChartsOption } from 'echarts'; 15 + import { getAllDeviceByOrg } from '/@/api/dataBoard';
  16 + import { useHistoryData } from '/@/views/device/list/hook/useHistoryData';
21 17
22 type DeviceOption = Record<'label' | 'value' | 'organizationId', string>; 18 type DeviceOption = Record<'label' | 'value' | 'organizationId', string>;
23 19
@@ -25,37 +21,14 @@ @@ -25,37 +21,14 @@
25 21
26 const chartRef = ref(); 22 const chartRef = ref();
27 23
28 - const deviceAttrs = ref<DeviceAttributeRecord[]>([]);  
29 -  
30 const loading = ref(false); 24 const loading = ref(false);
31 25
32 const isNull = ref(false); 26 const isNull = ref(false);
33 27
34 - function getSearchParams(value: Recordable) {  
35 - const { startTs, endTs, interval, agg, limit, keys, way, deviceId } = value;  
36 - const basicRecord = {  
37 - entityId: deviceId,  
38 - keys: keys  
39 - ? keys  
40 - : unref(deviceAttrs)  
41 - .map((item) => item.identifier)  
42 - .join(),  
43 - interval,  
44 - agg,  
45 - limit,  
46 - };  
47 - if (way === QueryWay.LATEST) {  
48 - return Object.assign(basicRecord, {  
49 - startTs: moment().subtract(startTs, 'ms').valueOf(),  
50 - endTs: Date.now(),  
51 - });  
52 - } else {  
53 - return Object.assign(basicRecord, {  
54 - startTs: moment(startTs).valueOf(),  
55 - endTs: moment(endTs).valueOf(),  
56 - });  
57 - }  
58 - } 28 + const { deviceAttrs, getDeviceKeys, getSearchParams, setChartOptions, getDeviceAttribute } =
  29 + useHistoryData();
  30 +
  31 + const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
59 32
60 function hasDeviceAttr() { 33 function hasDeviceAttr() {
61 if (!unref(deviceAttrs).length) { 34 if (!unref(deviceAttrs).length) {
@@ -65,32 +38,8 @@ @@ -65,32 +38,8 @@
65 } 38 }
66 } 39 }
67 40
68 - function setChartOptions(data: HistoryData, keys?: string | string[]) {  
69 - const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);  
70 -  
71 - const dataArray: [string, string, string][] = [];  
72 - for (const key in data) {  
73 - for (const item1 of data[key]) {  
74 - let { ts, value } = item1;  
75 - const time = dateUtil(ts).format(DEFAULT_DATE_FORMAT);  
76 - value = Number(value).toFixed(2);  
77 - dataArray.push([time, value, key as string]);  
78 - }  
79 - }  
80 - keys = keys ? [keys as string] : unref(deviceAttrs).map((item) => item.identifier);  
81 - const series: EChartsOption['series'] = (keys as string[]).map((item) => {  
82 - return {  
83 - name: item,  
84 - type: 'line',  
85 - data: dataArray.filter((temp) => temp[2] === item),  
86 - };  
87 - });  
88 - // 设置数据  
89 - setOptions(eChartOptions(series, keys));  
90 - }  
91 -  
92 const [register, method] = useForm({ 41 const [register, method] = useForm({
93 - schemas: formSchema, 42 + schemas: formSchema(),
94 baseColProps: useGridLayout(2, 3, 4) as unknown as ColEx, 43 baseColProps: useGridLayout(2, 3, 4) as unknown as ColEx,
95 rowProps: { 44 rowProps: {
96 gutter: 10, 45 gutter: 10,
@@ -119,7 +68,11 @@ @@ -119,7 +68,11 @@
119 } else { 68 } else {
120 isNull.value = true; 69 isNull.value = true;
121 } 70 }
122 - setChartOptions(res, value.keys); 71 +
  72 + const selectedKeys = unref(deviceAttrs).find(
  73 + (item) => item.identifier === value[SchemaFiled.KEYS]
  74 + );
  75 + setOptions(setChartOptions(res, selectedKeys));
123 }, 76 },
124 }); 77 });
125 78
@@ -128,8 +81,7 @@ @@ -128,8 +81,7 @@
128 try { 81 try {
129 const options = await getAllDeviceByOrg(organizationId); 82 const options = await getAllDeviceByOrg(organizationId);
130 const record = options.find((item) => item.id === value); 83 const record = options.find((item) => item.id === value);
131 - const { deviceProfileId } = record!;  
132 - deviceAttrs.value = (await getDeviceAttributes({ deviceProfileId })) || []; 84 + await getDeviceAttribute(record!);
133 await nextTick(); 85 await nextTick();
134 method.updateSchema({ 86 method.updateSchema({
135 field: SchemaFiled.KEYS, 87 field: SchemaFiled.KEYS,
@@ -157,9 +109,7 @@ @@ -157,9 +109,7 @@
157 109
158 const res = await getDeviceHistoryInfo({ 110 const res = await getDeviceHistoryInfo({
159 entityId: deviceId, 111 entityId: deviceId,
160 - keys: unref(deviceAttrs)  
161 - .map((item) => item.identifier)  
162 - .join(), 112 + keys: unref(getDeviceKeys).join(),
163 startTs: Date.now() - 1 * 24 * 60 * 60 * 1000, 113 startTs: Date.now() - 1 * 24 * 60 * 60 * 1000,
164 endTs: Date.now(), 114 endTs: Date.now(),
165 agg: AggregateDataEnum.NONE, 115 agg: AggregateDataEnum.NONE,
@@ -173,7 +123,7 @@ @@ -173,7 +123,7 @@
173 } else { 123 } else {
174 isNull.value = true; 124 isNull.value = true;
175 } 125 }
176 - setChartOptions(res); 126 + setOptions(setChartOptions(res));
177 }; 127 };
178 128
179 const generateDeviceOptions = (dataSource: DataSource[]) => { 129 const generateDeviceOptions = (dataSource: DataSource[]) => {
@@ -35,171 +35,173 @@ export enum AggregateDataEnum { @@ -35,171 +35,173 @@ export enum AggregateDataEnum {
35 COUNT = 'COUNT', 35 COUNT = 'COUNT',
36 NONE = 'NONE', 36 NONE = 'NONE',
37 } 37 }
38 -export const formSchema: FormSchema[] = [  
39 - {  
40 - field: SchemaFiled.DEVICE_ID,  
41 - label: '设备名称',  
42 - component: 'Select',  
43 - rules: [{ required: true, message: '设备名称为必选项', type: 'string' }],  
44 - componentProps({ formActionType }) {  
45 - const { setFieldsValue } = formActionType;  
46 - return {  
47 - placeholder: '请选择设备',  
48 - onChange() {  
49 - setFieldsValue({ [SchemaFiled.KEYS]: null });  
50 - },  
51 - }; 38 +export const formSchema = (): FormSchema[] => {
  39 + return [
  40 + {
  41 + field: SchemaFiled.DEVICE_ID,
  42 + label: '设备名称',
  43 + component: 'Select',
  44 + rules: [{ required: true, message: '设备名称为必选项', type: 'string' }],
  45 + componentProps({ formActionType }) {
  46 + const { setFieldsValue } = formActionType;
  47 + return {
  48 + placeholder: '请选择设备',
  49 + onChange() {
  50 + setFieldsValue({ [SchemaFiled.KEYS]: null });
  51 + },
  52 + };
  53 + },
52 }, 54 },
53 - },  
54 - {  
55 - field: SchemaFiled.WAY,  
56 - label: '查询方式',  
57 - component: 'RadioGroup',  
58 - defaultValue: QueryWay.LATEST,  
59 - componentProps({ formActionType }) {  
60 - const { setFieldsValue } = formActionType;  
61 - return {  
62 - options: [  
63 - { label: '最后', value: QueryWay.LATEST },  
64 - { label: '时间段', value: QueryWay.TIME_PERIOD },  
65 - ],  
66 - onChange(event: ChangeEvent) {  
67 - (event.target as HTMLInputElement).value === QueryWay.LATEST  
68 - ? setFieldsValue({  
69 - [SchemaFiled.DATE_RANGE]: [],  
70 - [SchemaFiled.START_TS]: null,  
71 - [SchemaFiled.END_TS]: null,  
72 - })  
73 - : setFieldsValue({ [SchemaFiled.START_TS]: null });  
74 - },  
75 - getPopupContainer: () => document.body,  
76 - }; 55 + {
  56 + field: SchemaFiled.WAY,
  57 + label: '查询方式',
  58 + component: 'RadioGroup',
  59 + defaultValue: QueryWay.LATEST,
  60 + componentProps({ formActionType }) {
  61 + const { setFieldsValue } = formActionType;
  62 + return {
  63 + options: [
  64 + { label: '最后', value: QueryWay.LATEST },
  65 + { label: '时间段', value: QueryWay.TIME_PERIOD },
  66 + ],
  67 + onChange(event: ChangeEvent) {
  68 + (event.target as HTMLInputElement).value === QueryWay.LATEST
  69 + ? setFieldsValue({
  70 + [SchemaFiled.DATE_RANGE]: [],
  71 + [SchemaFiled.START_TS]: null,
  72 + [SchemaFiled.END_TS]: null,
  73 + })
  74 + : setFieldsValue({ [SchemaFiled.START_TS]: null });
  75 + },
  76 + getPopupContainer: () => document.body,
  77 + };
  78 + },
77 }, 79 },
78 - },  
79 - {  
80 - field: SchemaFiled.START_TS,  
81 - label: '最后数据',  
82 - component: 'Select',  
83 - ifShow({ values }) {  
84 - return values[SchemaFiled.WAY] === QueryWay.LATEST; 80 + {
  81 + field: SchemaFiled.START_TS,
  82 + label: '最后数据',
  83 + component: 'Select',
  84 + ifShow({ values }) {
  85 + return values[SchemaFiled.WAY] === QueryWay.LATEST;
  86 + },
  87 + componentProps({ formActionType }) {
  88 + const { setFieldsValue } = formActionType;
  89 + return {
  90 + options: intervalOption,
  91 + onChange() {
  92 + setFieldsValue({ [SchemaFiled.INTERVAL]: null });
  93 + },
  94 + getPopupContainer: () => document.body,
  95 + };
  96 + },
  97 + rules: [{ required: true, message: '最后数据为必选项', type: 'number' }],
85 }, 98 },
86 - componentProps({ formActionType }) {  
87 - const { setFieldsValue } = formActionType;  
88 - return {  
89 - options: intervalOption,  
90 - onChange() {  
91 - setFieldsValue({ [SchemaFiled.INTERVAL]: null });  
92 - },  
93 - getPopupContainer: () => document.body,  
94 - };  
95 - },  
96 - rules: [{ required: true, message: '最后数据为必选项', type: 'number' }],  
97 - },  
98 - {  
99 - field: SchemaFiled.DATE_RANGE,  
100 - label: '时间段',  
101 - component: 'RangePicker',  
102 - ifShow({ values }) {  
103 - return values[SchemaFiled.WAY] === QueryWay.TIME_PERIOD; 99 + {
  100 + field: SchemaFiled.DATE_RANGE,
  101 + label: '时间段',
  102 + component: 'RangePicker',
  103 + ifShow({ values }) {
  104 + return values[SchemaFiled.WAY] === QueryWay.TIME_PERIOD;
  105 + },
  106 + rules: [{ required: true, message: '时间段为必选项' }],
  107 + componentProps({ formActionType }) {
  108 + const { setFieldsValue } = formActionType;
  109 + let dates: Moment[] = [];
  110 + return {
  111 + showTime: {
  112 + defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')],
  113 + },
  114 + onCalendarChange(value: Moment[]) {
  115 + dates = value;
  116 + },
  117 + disabledDate(current: Moment) {
  118 + if (!dates || dates.length === 0 || !current) {
  119 + return false;
  120 + }
  121 + const diffDate = current.diff(dates[0], 'years', true);
  122 + return Math.abs(diffDate) > 1;
  123 + },
  124 + onChange() {
  125 + dates = [];
  126 + setFieldsValue({ [SchemaFiled.INTERVAL]: null });
  127 + },
  128 + getPopupContainer: () => document.body,
  129 + };
  130 + },
  131 + colProps: useGridLayout(2, 2, 2, 2, 2, 2) as unknown as ColEx,
104 }, 132 },
105 - rules: [{ required: true, message: '时间段为必选项' }],  
106 - componentProps({ formActionType }) {  
107 - const { setFieldsValue } = formActionType;  
108 - let dates: Moment[] = [];  
109 - return {  
110 - showTime: {  
111 - defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')],  
112 - },  
113 - onCalendarChange(value: Moment[]) {  
114 - dates = value;  
115 - },  
116 - disabledDate(current: Moment) {  
117 - if (!dates || dates.length === 0 || !current) {  
118 - return false;  
119 - }  
120 - const diffDate = current.diff(dates[0], 'years', true);  
121 - return Math.abs(diffDate) > 1;  
122 - },  
123 - onChange() {  
124 - dates = [];  
125 - setFieldsValue({ [SchemaFiled.INTERVAL]: null });  
126 - }, 133 + {
  134 + field: SchemaFiled.AGG,
  135 + label: '数据聚合功能',
  136 + component: 'Select',
  137 + componentProps: {
127 getPopupContainer: () => document.body, 138 getPopupContainer: () => document.body,
128 - };  
129 - },  
130 - colProps: useGridLayout(2, 2, 2, 2, 2, 2) as unknown as ColEx,  
131 - },  
132 - {  
133 - field: SchemaFiled.AGG,  
134 - label: '数据聚合功能',  
135 - component: 'Select',  
136 - componentProps: {  
137 - getPopupContainer: () => document.body,  
138 - options: [  
139 - { label: '最小值', value: AggregateDataEnum.MIN },  
140 - { label: '最大值', value: AggregateDataEnum.MAX },  
141 - { label: '平均值', value: AggregateDataEnum.AVG },  
142 - { label: '求和', value: AggregateDataEnum.SUM },  
143 - { label: '计数', value: AggregateDataEnum.COUNT },  
144 - { label: '空', value: AggregateDataEnum.NONE },  
145 - ],  
146 - },  
147 - },  
148 - {  
149 - field: SchemaFiled.INTERVAL,  
150 - label: '分组间隔',  
151 - component: 'Select',  
152 - dynamicRules: ({ model }) => {  
153 - return [  
154 - {  
155 - required: model[SchemaFiled.AGG] !== AggregateDataEnum.NONE,  
156 - message: '分组间隔为必填项',  
157 - type: 'number',  
158 - },  
159 - ];  
160 - },  
161 - ifShow({ values }) {  
162 - return values[SchemaFiled.AGG] !== AggregateDataEnum.NONE; 139 + options: [
  140 + { label: '最小值', value: AggregateDataEnum.MIN },
  141 + { label: '最大值', value: AggregateDataEnum.MAX },
  142 + { label: '平均值', value: AggregateDataEnum.AVG },
  143 + { label: '求和', value: AggregateDataEnum.SUM },
  144 + { label: '计数', value: AggregateDataEnum.COUNT },
  145 + { label: '空', value: AggregateDataEnum.NONE },
  146 + ],
  147 + },
163 }, 148 },
164 - componentProps({ formModel, formActionType }) {  
165 - const options =  
166 - formModel[SchemaFiled.WAY] === QueryWay.LATEST  
167 - ? getPacketIntervalByValue(formModel[SchemaFiled.START_TS])  
168 - : getPacketIntervalByRange(formModel[SchemaFiled.DATE_RANGE]);  
169 - if (formModel[SchemaFiled.AGG] !== AggregateDataEnum.NONE) {  
170 - formActionType.setFieldsValue({ [SchemaFiled.LIMIT]: null });  
171 - }  
172 - return {  
173 - options,  
174 - getPopupContainer: () => document.body,  
175 - }; 149 + {
  150 + field: SchemaFiled.INTERVAL,
  151 + label: '分组间隔',
  152 + component: 'Select',
  153 + dynamicRules: ({ model }) => {
  154 + return [
  155 + {
  156 + required: model[SchemaFiled.AGG] !== AggregateDataEnum.NONE,
  157 + message: '分组间隔为必填项',
  158 + type: 'number',
  159 + },
  160 + ];
  161 + },
  162 + ifShow({ values }) {
  163 + return values[SchemaFiled.AGG] !== AggregateDataEnum.NONE;
  164 + },
  165 + componentProps({ formModel, formActionType }) {
  166 + const options =
  167 + formModel[SchemaFiled.WAY] === QueryWay.LATEST
  168 + ? getPacketIntervalByValue(formModel[SchemaFiled.START_TS])
  169 + : getPacketIntervalByRange(formModel[SchemaFiled.DATE_RANGE]);
  170 + if (formModel[SchemaFiled.AGG] !== AggregateDataEnum.NONE) {
  171 + formActionType.setFieldsValue({ [SchemaFiled.LIMIT]: null });
  172 + }
  173 + return {
  174 + options,
  175 + getPopupContainer: () => document.body,
  176 + };
  177 + },
176 }, 178 },
177 - },  
178 - {  
179 - field: SchemaFiled.LIMIT,  
180 - label: '最大条数',  
181 - component: 'InputNumber',  
182 - ifShow({ values }) {  
183 - return values[SchemaFiled.AGG] === AggregateDataEnum.NONE; 179 + {
  180 + field: SchemaFiled.LIMIT,
  181 + label: '最大条数',
  182 + component: 'InputNumber',
  183 + ifShow({ values }) {
  184 + return values[SchemaFiled.AGG] === AggregateDataEnum.NONE;
  185 + },
  186 + helpMessage: ['根据查询条件,查出的数据条数不超过这个值'],
  187 + componentProps() {
  188 + return {
  189 + max: 50000,
  190 + min: 7,
  191 + getPopupContainer: () => document.body,
  192 + };
  193 + },
184 }, 194 },
185 - helpMessage: ['根据查询条件,查出的数据条数不超过这个值'],  
186 - componentProps() {  
187 - return {  
188 - max: 50000,  
189 - min: 7, 195 + {
  196 + field: SchemaFiled.KEYS,
  197 + label: '设备属性',
  198 + component: 'Select',
  199 + componentProps: {
190 getPopupContainer: () => document.body, 200 getPopupContainer: () => document.body,
191 - };  
192 - },  
193 - },  
194 - {  
195 - field: SchemaFiled.KEYS,  
196 - label: '设备属性',  
197 - component: 'Select',  
198 - componentProps: {  
199 - getPopupContainer: () => document.body, 201 + },
200 }, 202 },
201 - },  
202 -]; 203 + ];
  204 +};
203 205
204 export function getHistorySearchParams(value: Recordable) { 206 export function getHistorySearchParams(value: Recordable) {
205 const { startTs, endTs, interval, agg, limit, way, keys, deviceId } = value; 207 const { startTs, endTs, interval, agg, limit, way, keys, deviceId } = value;