Showing
19 changed files
with
411 additions
and
143 deletions
... | ... | @@ -92,7 +92,8 @@ export interface NodeDataDataSourceJsonType { |
92 | 92 | deviceId: string |
93 | 93 | deviceProfileId: string |
94 | 94 | deviceProfileTemplateId?: string |
95 | - attr: string | |
95 | + attr: string | string[] | |
96 | + deviceName?: string | |
96 | 97 | |
97 | 98 | enable: boolean |
98 | 99 | chartOption?: ChartOptionType | ... | ... |
... | ... | @@ -40,11 +40,10 @@ const contentDataStore = useContentDataStoreWithOut() |
40 | 40 | export const getFormSchemas = (event: EventTypeEnum): FormSchema[] => { |
41 | 41 | const { getNodeData, getCellInfo, getDeviceInfo } = usePublicFormContext() |
42 | 42 | const { dataSourceJson } = unref(getNodeData) || {} |
43 | - const { deviceProfileId, deviceId } = dataSourceJson || {} | |
43 | + const { deviceProfileId } = dataSourceJson || {} | |
44 | 44 | // transportType:判断是什么类型的设备 code:设备地址码 deviceType:设备类型 |
45 | - let codeType: string | null = '' | |
46 | - const { transportType, deviceType, codeType: deviceCodeType } = unref(getDeviceInfo) || {} | |
47 | - codeType = deviceCodeType || (deviceId ? contentDataStore.diveceDetailMap?.[deviceId]?.codeType : null) | |
45 | + const { transportType, deviceType, codeType } = unref(getDeviceInfo) || {} | |
46 | + | |
48 | 47 | const isTemplate = contentDataStore.isTemplate // 判断是否是模板 |
49 | 48 | |
50 | 49 | return [ | ... | ... |
... | ... | @@ -63,6 +63,7 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { |
63 | 63 | onSelect(value: string, option: DeviceItemType) { |
64 | 64 | formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value ? option.deviceProfileId : null |
65 | 65 | formModel[ContentDataFieldsEnum.ATTR] = null |
66 | + formModel[ContentDataFieldsEnum.DEVICE_NAME] = value ? option.alias || option.name : '' | |
66 | 67 | }, |
67 | 68 | filterOption: (inputValue: string, option: DeviceItemType) => { |
68 | 69 | return option.alias?.includes?.(inputValue) || option.name?.includes?.(inputValue) |
... | ... | @@ -71,6 +72,12 @@ export const formSchemas = (componentKey?: string): FormSchema[] => { |
71 | 72 | }, |
72 | 73 | }, |
73 | 74 | { |
75 | + field: ContentDataFieldsEnum.DEVICE_NAME, | |
76 | + label: ContentDataFieldsNameEnum.deviceName, | |
77 | + component: ComponentEnum.INPUT, | |
78 | + ifShow: false, | |
79 | + }, | |
80 | + { | |
74 | 81 | field: ContentDataFieldsEnum.ATTR, |
75 | 82 | label: ContentDataFieldsNameEnum.ATTR, |
76 | 83 | component: ComponentEnum.API_SELECT, | ... | ... |
... | ... | @@ -6,3 +6,10 @@ export function useLatestMessageValue(message: SubscriptionData, attr: string) { |
6 | 6 | const [ts, latestValue = null] = lateset || [] |
7 | 7 | return { ts, latestValue } |
8 | 8 | } |
9 | + | |
10 | +export function useLatestMultipleMessageValue(message: SubscriptionData, attr: string[], Fn: (attribute: string, value: any, timespan: number) => void) { | |
11 | + attr.forEach((item) => { | |
12 | + const [ts = null, latestValue = null] = message[item][0] || [] | |
13 | + Fn(item, ts, latestValue) | |
14 | + }) | |
15 | +} | ... | ... |
... | ... | @@ -34,13 +34,11 @@ function initChartInstance() { |
34 | 34 | const { onMessage } = useOnMessage({ |
35 | 35 | onReceiveDataSourceMessage(commandSource, message) { |
36 | 36 | const { data } = commandSource |
37 | - const { deviceInfo, attrInfo } = data || {} | |
38 | - const { deviceName } = deviceInfo || {} | |
39 | 37 | const { attr } = data as NodeDataDataSourceJsonType |
40 | 38 | const { latestValue } = useLatestMessageValue(message.data, attr) |
41 | 39 | unref(chartInstance)?.setOption({ |
42 | 40 | title: { |
43 | - text: `${deviceName || ''}-${attrInfo.name || ''}`, | |
41 | + // text: `${deviceName || ''}-${attrInfo.name || ''}`, | |
44 | 42 | }, |
45 | 43 | series: [{ |
46 | 44 | data: getSetValue(Number(latestValue)), | ... | ... |
... | ... | @@ -3,7 +3,8 @@ import { Button, Divider } from 'ant-design-vue' |
3 | 3 | import { nextTick, onMounted, ref, unref } from 'vue' |
4 | 4 | import { formSchemas } from '../config' |
5 | 5 | import { ChartComponentEnum } from '..' |
6 | -import { DataSourceForm } from '@/core/Library/components/PublicForm/components/DataSourceForm' | |
6 | +import { DataSourceForm } from '../component/index' | |
7 | +// import { DataSourceForm } from '@/core/Library/components/PublicForm/components/DataSourceForm' | |
7 | 8 | |
8 | 9 | import { BasicForm, useForm } from '@/components/Form' |
9 | 10 | import { FormLayoutEnum } from '@/components/Form/src/enum' |
... | ... | @@ -46,7 +47,7 @@ const handleSubmit = async () => { |
46 | 47 | if (contentDataStore.getIsTemplate) |
47 | 48 | dataSourceJson = { ...value, deviceProfileId: value?.deviceProfileTemplateId, deviceId: null } |
48 | 49 | |
49 | - await saveNodeAllData({ dataSourceJson: { ...dataSourceJson, chartOption: { ...values } } }) | |
50 | + await saveNodeAllData({ dataSourceJson: { ...dataSourceJson, attr: typeof value.attr == 'string' ? [value.attr] : value.attr, chartOption: { ...values } } }) | |
50 | 51 | savePageContent() |
51 | 52 | createMessage.success('操作成功~') |
52 | 53 | } |
... | ... | @@ -57,9 +58,9 @@ const handleSubmit = async () => { |
57 | 58 | |
58 | 59 | const handleSetFormValues = async () => { |
59 | 60 | const { dataSourceJson } = unref(getNodeData) || {} |
60 | - const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId } = dataSourceJson || {} | |
61 | + const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId, deviceName } = dataSourceJson || {} | |
61 | 62 | await nextTick() |
62 | - unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, deviceProfileTemplateId }) | |
63 | + unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceName, deviceProfileId, deviceProfileTemplateId }) | |
63 | 64 | setFieldsValue({ ...chartOption }) |
64 | 65 | } |
65 | 66 | ... | ... |
1 | 1 | import { isLightboxMode } from '@/utils/env' |
2 | 2 | |
3 | -const data = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] | |
4 | -const seriesList = [ | |
5 | - { value: 120, name: '123', itemStyle: { color: '#5470c6' } }, | |
6 | - { value: 150, name: '456', itemStyle: { color: '#5470c6' } }, | |
7 | - { value: 40, name: '789', itemStyle: { color: '#5470c6' } }, | |
8 | - { value: 139, name: '111', itemStyle: { color: '#5470c6' } }, | |
9 | - { value: 130, name: '222', itemStyle: { color: '#5470c6' } }, | |
10 | - { value: 125, name: '333', itemStyle: { color: '#5470c6' } }, | |
11 | - { value: 110, name: '444', itemStyle: { color: '#5470c6' } }, | |
12 | -] | |
13 | 3 | export const defaultOption = { |
14 | - color: ['#3398DB'], | |
15 | 4 | tooltip: { |
16 | - // 提示框 | |
17 | - trigger: 'item', | |
18 | - confine: true, | |
19 | - axisPointer: { | |
20 | - type: 'shadow', | |
21 | - }, | |
22 | 5 | }, |
23 | - xAxis: { | |
24 | - type: 'category', | |
25 | - data: isLightboxMode() ? [] : data, | |
26 | - axisTick: { | |
27 | - alignWithLabel: true, | |
6 | + calculable: true, | |
7 | + xAxis: [ | |
8 | + { | |
9 | + type: 'category', | |
10 | + // prettier-ignore | |
11 | + data: isLightboxMode() ? [] : ['Jan', 'Feb', 'Mar', 'Apr', 'May'], | |
28 | 12 | }, |
13 | + ], | |
14 | + legend: { | |
15 | + top: '8%', | |
16 | + left: 'center', | |
17 | + data: [''], | |
29 | 18 | }, |
19 | + | |
30 | 20 | grid: { |
31 | - top: '10%', | |
32 | - left: '10%', | |
33 | - bottom: '10%', | |
34 | - }, | |
35 | - yAxis: { | |
36 | - type: 'value', | |
21 | + top: '25%', | |
22 | + left: '6%', | |
23 | + right: '10%', | |
24 | + bottom: '8%', | |
25 | + containLabel: true, | |
37 | 26 | }, |
27 | + yAxis: [ | |
28 | + { | |
29 | + type: 'value', | |
30 | + }, | |
31 | + ], | |
38 | 32 | series: [ |
39 | 33 | { |
40 | - name: '', | |
34 | + name: 'Rainfall', | |
35 | + type: 'bar', | |
36 | + data: [ | |
37 | + 25.6, 76.7, 42.0, 27.0, 23.2, | |
38 | + ], | |
39 | + }, | |
40 | + { | |
41 | + name: 'Evaporation', | |
41 | 42 | type: 'bar', |
42 | - barWidth: '60%', | |
43 | - data: isLightboxMode () ? [] : seriesList, | |
43 | + data: isLightboxMode() | |
44 | + ? [] | |
45 | + : [ | |
46 | + 28.7, 32.6, 70.7, 29.0, 26.4, | |
47 | + ], | |
44 | 48 | }, |
45 | 49 | ], |
46 | 50 | } | ... | ... |
1 | 1 | <script lang="ts" setup> |
2 | -import { computed, onMounted, onUnmounted, ref, unref } from 'vue' | |
2 | +import { computed, onMounted, onUnmounted, ref, toRaw, unref } from 'vue' | |
3 | 3 | import { init } from 'echarts' |
4 | 4 | import type { ECharts, EChartsOption } from 'echarts' |
5 | 5 | import { defaultOption } from './index.config' |
... | ... | @@ -10,6 +10,7 @@ import { SocketSubscriberEnum } from '@/enums/datasource' |
10 | 10 | import type { SubscriptionData } from '@/core/websocket/type/message' |
11 | 11 | import { formatToDateTime } from '@/utils/dateUtil' |
12 | 12 | import type { CommandSource } from '@/core/websocket/processor' |
13 | +import { useProductsStore } from '@/store/modules/products' | |
13 | 14 | |
14 | 15 | const props = defineProps<{ |
15 | 16 | config: CreateComponentType |
... | ... | @@ -21,40 +22,80 @@ const chartElRef = ref<Nullable<HTMLDivElement>>() |
21 | 22 | |
22 | 23 | const chartInstance = ref<Nullable<ECharts>>() |
23 | 24 | |
25 | +const seriesData = ref<any>([]) | |
26 | +const XAxisData = ref<any>([]) | |
27 | +const titleATTR = ref<string[] | any>([]) | |
28 | + | |
24 | 29 | function initChartInstance() { |
25 | 30 | chartInstance.value = init(unref(chartElRef)) |
26 | 31 | chartInstance.value.setOption(defaultOption) |
27 | 32 | } |
28 | 33 | |
34 | +const productsStore = useProductsStore() | |
35 | + | |
29 | 36 | const handleHistoryData = (commandSource: CommandSource, message: SubscriptionData) => { |
30 | 37 | const { data } = commandSource |
31 | - const { attrInfo, deviceInfo, attr } = data as NodeDataDataSourceJsonType | |
32 | - const { deviceName } = deviceInfo || {} | |
38 | + const { attr, deviceName } = data as NodeDataDataSourceJsonType as any | |
33 | 39 | |
34 | - const historyData = message[attr] | |
35 | - const xAxisData: string[] = [] | |
36 | - const seriesData: number[] = [] | |
40 | + attr.forEach((item: any) => { // 得到所有时间 | |
41 | + XAxisData.value.push(...message[item].map((item1) => { | |
42 | + const [ts] = item1 | |
43 | + return ts | |
44 | + })) | |
45 | + }) | |
46 | + XAxisData.value = Array.from(new Set(XAxisData.value)).sort((a: any, b: any) => a - b)// 去重获取到的时间 | |
47 | + // 初始化有几个柱 | |
48 | + seriesData.value.forEach((item: any) => { | |
49 | + item.data = unref(XAxisData).map((time: any) => ({ name: time, value: '' })) | |
50 | + }) | |
37 | 51 | |
38 | - for (const item of historyData) { | |
39 | - const [ts, value] = item | |
40 | - xAxisData.push(formatToDateTime(ts)) | |
41 | - seriesData.push(value) | |
42 | - } | |
52 | + // 跟每个柱添加数据 | |
53 | + seriesData.value.forEach((item: any) => { | |
54 | + item.data.forEach((item1: any) => { | |
55 | + message[item.attribute].forEach((messageItem) => { | |
56 | + const [ts, value] = messageItem | |
57 | + if (item1.name === ts) | |
58 | + item1.value = value || '' | |
59 | + // item1.name = formatToDateTime(item1.name) | |
60 | + }) | |
61 | + }) | |
62 | + }) | |
43 | 63 | |
44 | 64 | unref(chartInstance)?.setOption({ |
45 | 65 | title: { |
46 | - text: `${deviceName || ''}-${attrInfo.name || ''}`, | |
66 | + text: `${deviceName || ''}`, | |
67 | + }, | |
68 | + xAxis: [ | |
69 | + { | |
70 | + type: 'category', | |
71 | + data: toRaw(unref(XAxisData.value.map((item: string | number) => formatToDateTime(item)))), | |
72 | + }, | |
73 | + ], | |
74 | + legend: { | |
75 | + data: unref(titleATTR), | |
47 | 76 | }, |
48 | - xAxis: { data: xAxisData }, | |
49 | - series: { data: seriesData }, | |
77 | + series: toRaw(unref(seriesData).map((item: { type: string; name: string; data: any; attribute: string }) => { | |
78 | + const { type, name, data } = item | |
79 | + return { | |
80 | + type, | |
81 | + name, | |
82 | + data: data.map((item: any) => item.value), | |
83 | + } | |
84 | + })), | |
50 | 85 | } as EChartsOption) |
51 | 86 | } |
52 | 87 | |
53 | 88 | const { onMessage } = useOnMessage({ |
54 | 89 | onReceiveDataSourceMessage(commandSource, message) { |
55 | 90 | const { data } = commandSource |
56 | - const { chartOption } = data as NodeDataDataSourceJsonType | |
91 | + const { chartOption, attr, deviceProfileId } = data as NodeDataDataSourceJsonType | |
57 | 92 | const { queryType } = chartOption || {} |
93 | + if (!seriesData.value.length) { | |
94 | + (attr as string[]).forEach((item: string) => { | |
95 | + titleATTR.value.push(productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, item)?.functionName) | |
96 | + seriesData.value.push({ attribute: item, data: [], type: 'bar', name: productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, item)?.functionName }) | |
97 | + }) | |
98 | + } | |
58 | 99 | |
59 | 100 | if (queryType === SocketSubscriberEnum.HISTORY_CMDS) |
60 | 101 | handleHistoryData(commandSource, message.data) | ... | ... |
... | ... | @@ -36,8 +36,6 @@ onMounted(() => { |
36 | 36 | |
37 | 37 | const onReceiveDataSourceMessage = (commandSource: CommandSource, message: SubscriptionUpdateMsg) => { |
38 | 38 | const { data } = commandSource |
39 | - const { deviceInfo, attrInfo } = data || {} | |
40 | - const { deviceName } = deviceInfo || {} | |
41 | 39 | const { attr } = data as NodeDataDataSourceJsonType |
42 | 40 | const { latestValue } = useLatestMessageValue(message.data, attr) |
43 | 41 | unref(chartInstance)?.setOption({ |
... | ... | @@ -46,7 +44,7 @@ const onReceiveDataSourceMessage = (commandSource: CommandSource, message: Subsc |
46 | 44 | |
47 | 45 | }], |
48 | 46 | title: { |
49 | - text: `${deviceName || ''}-${attrInfo.name || ''}`, | |
47 | + // text: `${deviceName || ''}-${attrInfo.name || ''}`, | |
50 | 48 | }, |
51 | 49 | } as EChartsOption) |
52 | 50 | } | ... | ... |
... | ... | @@ -3,7 +3,9 @@ import { Button, Divider } from 'ant-design-vue' |
3 | 3 | import { nextTick, onMounted, ref, unref } from 'vue' |
4 | 4 | import { formSchemas } from '../config' |
5 | 5 | import { ChartComponentEnum } from '..' |
6 | -import { DataSourceForm } from '@/core/Library/components/PublicForm/components/DataSourceForm' | |
6 | + | |
7 | +import { DataSourceForm } from '../component/index' | |
8 | +// import { DataSourceForm } from '@/core/Library/components/PublicForm/components/DataSourceForm' | |
7 | 9 | |
8 | 10 | import { BasicForm, useForm } from '@/components/Form' |
9 | 11 | import { FormLayoutEnum } from '@/components/Form/src/enum' |
... | ... | @@ -33,9 +35,9 @@ const dataSourceElRef = ref<Nullable<InstanceType<typeof DataSourceForm>>>() |
33 | 35 | |
34 | 36 | const handleSetFormValues = async () => { |
35 | 37 | const { dataSourceJson } = unref(getNodeData) || {} |
36 | - const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId } = dataSourceJson || {} | |
38 | + const { deviceId, attr, chartOption, deviceProfileId, deviceProfileTemplateId, deviceName } = dataSourceJson || {} | |
37 | 39 | await nextTick() |
38 | - unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, deviceProfileTemplateId }) | |
40 | + unref(dataSourceElRef)?.setFieldsValue({ deviceId, attr, deviceProfileId, deviceProfileTemplateId, deviceName }) | |
39 | 41 | setFieldsValue({ ...chartOption }) |
40 | 42 | } |
41 | 43 | |
... | ... | @@ -54,7 +56,7 @@ const handleSubmit = async () => { |
54 | 56 | if (contentDataStore.getIsTemplate) |
55 | 57 | dataSourceJson = { ...value, deviceProfileId: value?.deviceProfileTemplateId, deviceId: null } |
56 | 58 | |
57 | - saveNodeAllData({ dataSourceJson: { ...dataSourceJson, chartOption: { ...values } } }) | |
59 | + saveNodeAllData({ dataSourceJson: { ...dataSourceJson, attr: typeof value.attr == 'string' ? [value.attr] : value.attr, chartOption: { ...values } } }) | |
58 | 60 | createMessage.success('操作成功~') |
59 | 61 | savePageContent() |
60 | 62 | } | ... | ... |
... | ... | @@ -3,31 +3,35 @@ import { isLightboxMode } from '@/utils/env' |
3 | 3 | |
4 | 4 | export const getDefaultOption = (): EChartsOption => { |
5 | 5 | return { |
6 | - tooltip: { | |
7 | - trigger: 'item', | |
8 | - confine: true, | |
6 | + legend: { | |
7 | + top: '10%', | |
8 | + left: 'center', | |
9 | + data: [''], | |
9 | 10 | }, |
10 | 11 | grid: { |
11 | - left: '3%', | |
12 | - right: '4%', | |
13 | - bottom: '3%', | |
12 | + top: '30%', | |
13 | + left: '6%', | |
14 | + right: '10%', | |
15 | + bottom: '8%', | |
14 | 16 | containLabel: true, |
15 | 17 | }, |
16 | - dataset: { | |
17 | - source: isLightboxMode() | |
18 | + xAxis: { | |
19 | + type: 'category', | |
20 | + }, | |
21 | + yAxis: { | |
22 | + type: 'value', | |
23 | + boundaryGap: [0, '100%'], | |
24 | + }, | |
25 | + series: [{ | |
26 | + type: 'line', | |
27 | + name: '温度', | |
28 | + data: isLightboxMode() | |
18 | 29 | ? [] |
19 | 30 | : [ |
20 | - ['product', '2015', '2016', '2017'], | |
21 | - ['Matcha Latte', 43.3, 85.8, 93.7], | |
22 | - ['Milk Tea', 83.1, 73.4, 55.1], | |
23 | - ['Cheese Cocoa', 86.4, 65.2, 82.5], | |
24 | - ['Walnut Brownie', 72.4, 53.9, 39.1], | |
25 | - ], | |
26 | - }, | |
27 | - xAxis: { type: 'category' }, | |
28 | - yAxis: {}, | |
29 | - series: [ | |
30 | - { type: 'line' }, | |
31 | - ], | |
31 | + ['Matcha Latte', 43.3], | |
32 | + ['Milk Tea', 83.1], | |
33 | + ['Cheese Cocoa', 86.4], | |
34 | + ['Walnut Brownie', 72.4]], | |
35 | + }], | |
32 | 36 | } |
33 | 37 | } | ... | ... |
1 | 1 | <script lang="ts" setup> |
2 | -import { computed, onMounted, onUnmounted, ref, unref } from 'vue' | |
3 | -import type { DatasetComponentOption, ECharts, EChartsOption } from 'echarts' | |
2 | +import { computed, onMounted, onUnmounted, ref, toRaw, unref } from 'vue' | |
3 | +import type { ECharts, EChartsOption } from 'echarts' | |
4 | 4 | import { init } from 'echarts' |
5 | 5 | import { getDefaultOption } from './index.config' |
6 | 6 | import type { CreateComponentType, RenderComponentExposeType } from '@/core/Library/types' |
... | ... | @@ -9,8 +9,12 @@ import type { NodeDataDataSourceJsonType } from '@/api/node/model' |
9 | 9 | import type { SubscriptionData } from '@/core/websocket/type/message' |
10 | 10 | import { SocketSubscriberEnum } from '@/enums/datasource' |
11 | 11 | import { formatToDateTime } from '@/utils/dateUtil' |
12 | -import { useLatestMessageValue } from '@/core/Library/hook/useLatestMessageValue' | |
13 | 12 | import type { CommandSource } from '@/core/websocket/processor' |
13 | +import { useLatestMultipleMessageValue } from '@/core/Library/hook/useLatestMessageValue' | |
14 | +import { useProductsStore } from '@/store/modules/products' | |
15 | +interface IList { | |
16 | + time: string | number | |
17 | +} | |
14 | 18 | |
15 | 19 | const props = defineProps<{ |
16 | 20 | config: CreateComponentType |
... | ... | @@ -22,67 +26,126 @@ const chartElRef = ref<Nullable<HTMLDivElement>>() |
22 | 26 | |
23 | 27 | const chartInstance = ref<Nullable<ECharts>>() |
24 | 28 | |
29 | +const maxLength = ref<number>(20) | |
30 | + | |
31 | +const timeList = ref<number[] | string[] | any>([])// 存储XAxis的值 | |
32 | +const seriesData = ref<any>([])// 存储series的值 | |
33 | +const titleATTR = ref<string[] | any>([]) | |
34 | + | |
35 | +const productsStore = useProductsStore() | |
36 | + | |
25 | 37 | function initChartInstance() { |
26 | 38 | chartInstance.value = init(unref(chartElRef)) |
27 | 39 | chartInstance.value.setOption(getDefaultOption()) |
28 | 40 | } |
29 | 41 | |
30 | -const handleHistoryData = (message: SubscriptionData, attr: string) => { | |
31 | - const data = message[attr] | |
32 | - const xAxisData: string[] = [] | |
33 | - const seriesData: number[] = [] | |
34 | - | |
35 | - for (const item of data) { | |
36 | - const [ts, value] = item | |
37 | - xAxisData.push(formatToDateTime(ts)) | |
38 | - seriesData.push(value) | |
39 | - } | |
42 | +const handleHistoryData = (commandSource: CommandSource, message: SubscriptionData, attr: string[] | string) => { | |
43 | + const { data } = commandSource || {} | |
44 | + const { deviceName } = data as NodeDataDataSourceJsonType | |
40 | 45 | |
46 | + (attr as string[]).forEach((item) => { | |
47 | + timeList.value.push(...message[item].map((item1) => { | |
48 | + const [ts] = item1 | |
49 | + return ts | |
50 | + })) | |
51 | + }) | |
52 | + timeList.value = Array.from(new Set(timeList.value))?.sort((a: any, b: any) => a - b)// 去重获取到的时间 | |
53 | + seriesData.value.forEach((item: any) => { | |
54 | + item.data = unref(timeList).map((time: any) => ({ name: time, value: null })) | |
55 | + }) | |
56 | + seriesData.value.forEach((item: any) => { | |
57 | + item.data.forEach((item1: any) => { | |
58 | + message[item.attribute].forEach((messageItem) => { | |
59 | + const [ts, value] = messageItem | |
60 | + if (item1.name === ts) { | |
61 | + item1.value = value || undefined | |
62 | + item1.name = formatToDateTime(item1.name) | |
63 | + } | |
64 | + }) | |
65 | + }) | |
66 | + }) | |
41 | 67 | unref(chartInstance)?.setOption({ |
42 | - xAxis: { data: xAxisData }, | |
43 | - series: { data: seriesData }, | |
68 | + title: { | |
69 | + text: `${deviceName || ''}`, | |
70 | + }, | |
71 | + xAxis: { | |
72 | + data: toRaw(unref(timeList.value.map((item: string | number) => formatToDateTime(item)))), | |
73 | + }, | |
74 | + series: toRaw( | |
75 | + unref(seriesData).map((item: { type: string; name: string; data: any }) => { | |
76 | + const { type, name, data } = item | |
77 | + return { | |
78 | + type, | |
79 | + name, | |
80 | + data, | |
81 | + } | |
82 | + }), | |
83 | + ), | |
44 | 84 | } as EChartsOption) |
45 | 85 | } |
46 | 86 | |
47 | -function sliceData(data: any[], maxLength = 20) { | |
48 | - if (data.length > maxLength) | |
49 | - return data.slice(1) | |
50 | - return data | |
51 | -} | |
87 | +// const contentDataStore = useContentDataStoreWithOut() | |
52 | 88 | |
53 | -const handlerTimeSeriesData = (commandSource: CommandSource, message: SubscriptionData, attr: string) => { | |
89 | +const handlerTimeSeriesData = (commandSource: CommandSource, message: SubscriptionData, attr: string[] | string) => { | |
54 | 90 | const { data } = commandSource |
55 | - const { attrInfo, deviceInfo } = data as NodeDataDataSourceJsonType | |
56 | - const { deviceName } = deviceInfo || {} | |
57 | - const { ts, latestValue } = useLatestMessageValue(message, attr) | |
58 | - const option = unref(chartInstance)?.getOption() as EChartsOption | |
59 | - const oldDataset = (option.dataset as DatasetComponentOption[])?.[0].source as Recordable[] | |
60 | - oldDataset.push({ | |
61 | - ts: formatToDateTime(ts), | |
62 | - [attr]: latestValue, | |
91 | + const { deviceName } = data as NodeDataDataSourceJsonType | |
92 | + const list: IList | any = {}// 记录时间 | |
93 | + useLatestMultipleMessageValue(message, attr as any, (attribute, timespan, value) => { | |
94 | + list.time = timespan || list.time | |
95 | + seriesData.value.forEach((item: any) => { | |
96 | + if (item.attribute === attribute) { | |
97 | + item.data.push({ | |
98 | + name: formatToDateTime(list.time), | |
99 | + value: value || undefined, | |
100 | + }) | |
101 | + } | |
102 | + if (item.data.length > unref(maxLength)) | |
103 | + item.data.shift() | |
104 | + }) | |
63 | 105 | }) |
106 | + list.time && timeList.value.push(formatToDateTime(list.time)) | |
107 | + if (unref(timeList).length > unref(maxLength)) | |
108 | + timeList.value.shift() | |
64 | 109 | |
65 | 110 | unref(chartInstance)?.setOption({ |
66 | 111 | title: { |
67 | - text: `${deviceName || ''}-${attrInfo.name || ''}`, | |
112 | + text: `${deviceName || ''}`, | |
68 | 113 | }, |
69 | - dataset: { | |
70 | - dimensions: ['ts', attr], | |
71 | - source: sliceData(oldDataset), | |
114 | + xAxis: { | |
115 | + data: toRaw(unref(timeList)), | |
72 | 116 | }, |
117 | + legend: { | |
118 | + data: unref(titleATTR), | |
119 | + } as any, | |
120 | + series: toRaw( | |
121 | + unref(seriesData).map((item: { type: string; name: string; data: any }) => { | |
122 | + const { type, name, data } = item | |
123 | + return { | |
124 | + type, | |
125 | + name, | |
126 | + data, | |
127 | + } | |
128 | + }), | |
129 | + ), | |
73 | 130 | } as EChartsOption) |
74 | 131 | } |
75 | 132 | |
76 | 133 | const { onMessage } = useOnMessage({ |
77 | 134 | onReceiveDataSourceMessage(commandSource, message) { |
78 | 135 | const { data } = commandSource |
79 | - const { chartOption, attr } = data as NodeDataDataSourceJsonType | |
136 | + const { chartOption, attr, deviceProfileId } = data as NodeDataDataSourceJsonType | |
80 | 137 | const { queryType } = chartOption || {} |
138 | + if (!seriesData.value.length) { | |
139 | + (attr as string[]).forEach((item: string) => { | |
140 | + titleATTR.value.push(productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, item)?.functionName) | |
141 | + seriesData.value.push({ attribute: item, data: [], type: 'line', name: productsStore.getObjectModelByIdWithIdentifier(deviceProfileId, item)?.functionName }) | |
142 | + }) | |
143 | + } | |
81 | 144 | |
82 | 145 | if (queryType === SocketSubscriberEnum.TS_SUB_CMDS) |
83 | 146 | handlerTimeSeriesData(commandSource, message.data, attr) |
84 | 147 | else if (queryType === SocketSubscriberEnum.HISTORY_CMDS) |
85 | - handleHistoryData(message.data, attr) | |
148 | + handleHistoryData(commandSource, message.data, attr) | |
86 | 149 | }, |
87 | 150 | }) |
88 | 151 | ... | ... |
... | ... | @@ -34,13 +34,11 @@ function initChartInstance() { |
34 | 34 | const { onMessage } = useOnMessage({ |
35 | 35 | onReceiveDataSourceMessage(commandSource, message) { |
36 | 36 | const { data } = commandSource |
37 | - const { deviceInfo, attrInfo } = data || {} | |
38 | - const { deviceName } = deviceInfo || {} | |
39 | 37 | const { attr } = data as NodeDataDataSourceJsonType |
40 | 38 | const { latestValue } = useLatestMessageValue(message.data, attr) |
41 | 39 | unref(chartInstance)?.setOption({ |
42 | 40 | title: { |
43 | - text: `${deviceName || ''}-${attrInfo.name || ''}`, | |
41 | + // text: `${deviceName || ''}-${attrInfo.name || ''}`, | |
44 | 42 | }, |
45 | 43 | series: [{ |
46 | 44 | data: getSetValue(Number(latestValue)), | ... | ... |
... | ... | @@ -26,13 +26,11 @@ function initChartInstance() { |
26 | 26 | const { onMessage } = useOnMessage({ |
27 | 27 | onReceiveDataSourceMessage(commandSource, message) { |
28 | 28 | const { data } = commandSource |
29 | - const { deviceInfo, attrInfo } = (data || {}) as NodeDataDataSourceJsonType | |
30 | - const { deviceName } = deviceInfo || {} | |
31 | 29 | const { attr } = data as NodeDataDataSourceJsonType |
32 | 30 | const { latestValue } = useLatestMessageValue(message.data, attr) |
33 | 31 | unref(chartInstance)?.setOption({ |
34 | 32 | title: { |
35 | - text: `${deviceName || ''}-${attrInfo.name || ''}`, | |
33 | + // text: `${deviceName || ''}-${attrInfo.name || ''}`, | |
36 | 34 | }, |
37 | 35 | series: [{ |
38 | 36 | data: [{ | ... | ... |
1 | +import { unref } from 'vue' | |
2 | +import { getDeviceAttributes, getListByConfigurationId, getListByDeviceProfileIds } from '@/api/device' | |
3 | +import type { DeviceItemType, ThingsModelItemType } from '@/api/device/model' | |
4 | +import type { FormSchema } from '@/components/Form' | |
5 | +import { ComponentEnum } from '@/components/Form/src/enum' | |
6 | +import { ContentDataFieldsEnum, ContentDataFieldsNameEnum, DataTypeEnum } from '@/enums/datasource' | |
7 | +import { useContentDataStoreWithOut } from '@/store/modules/contentData' | |
8 | +import type { ProductAndDevice } from '@/api/content/model' | |
9 | +import { ControlComponentEnum } from '@/core/Library/packages/Control' | |
10 | +import { useParseParams } from '@/core/LoadData' | |
11 | + | |
12 | +const contentDataStore = useContentDataStoreWithOut() | |
13 | +export const formSchemas = (componentKey?: string): FormSchema[] => { | |
14 | + const isTemplate = contentDataStore.isTemplate // 判断是否是模板组态 | |
15 | + const isTemplateLink = contentDataStore.getIsTemplateLink | |
16 | + const params = useParseParams() | |
17 | + const { configurationId } = params | |
18 | + return [ | |
19 | + { | |
20 | + field: ContentDataFieldsEnum.DEVICE_PROFILE_ID, | |
21 | + label: ContentDataFieldsNameEnum.DEVICE_PROFILE_ID, | |
22 | + component: ComponentEnum.INPUT, | |
23 | + ifShow: false, | |
24 | + }, | |
25 | + { | |
26 | + field: ContentDataFieldsEnum.DEVICE_PROFILE_TEMPLATE_ID, // 模板产品id | |
27 | + label: ContentDataFieldsNameEnum.DEVICE_PROFILE_ID, | |
28 | + component: ComponentEnum.API_SELECT, | |
29 | + ifShow: !!isTemplate, | |
30 | + required: !!isTemplate, | |
31 | + componentProps: ({ formModel }) => { | |
32 | + return { | |
33 | + options: (unref(contentDataStore.getProductAndDevice) || []).map((item: ProductAndDevice) => ({ label: item.profileName || item.name, value: item.profileId, transportType: item?.transportType, deviceType: item?.deviceType })), | |
34 | + placeholder: '请选择产品', | |
35 | + onSelect(value: string) { | |
36 | + formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value | |
37 | + formModel[ContentDataFieldsEnum.ATTR] = [] | |
38 | + }, | |
39 | + getPopupContainer: () => document.body, | |
40 | + } | |
41 | + }, | |
42 | + }, | |
43 | + { | |
44 | + field: ContentDataFieldsEnum.DEVICE_ID, | |
45 | + label: ContentDataFieldsNameEnum.DEVICE_ID, | |
46 | + component: ComponentEnum.API_SELECT, | |
47 | + ifShow: !isTemplate, | |
48 | + required: !isTemplate, | |
49 | + componentProps: ({ formModel }) => { | |
50 | + const organizationId = window.useParseParams().organizationId | |
51 | + if (!organizationId) return | |
52 | + return { | |
53 | + showSearch: true, | |
54 | + api: async (params: Recordable) => { | |
55 | + if (isTemplateLink) return await getListByConfigurationId(configurationId!) | |
56 | + return await getListByDeviceProfileIds(params) | |
57 | + }, | |
58 | + params: { | |
59 | + deviceProfileIds: unref(contentDataStore.getProductIds), | |
60 | + organizationId, | |
61 | + }, | |
62 | + aliasField: 'alias', | |
63 | + fieldNames: { label: 'name', value: 'tbDeviceId' }, | |
64 | + onSelect(value: string, option: DeviceItemType) { | |
65 | + formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] = value ? option.deviceProfileId : null | |
66 | + formModel[ContentDataFieldsEnum.ATTR] = [] | |
67 | + formModel[ContentDataFieldsEnum.DEVICE_NAME] = value ? option.alias || option.name : '' | |
68 | + }, | |
69 | + filterOption: (inputValue: string, option: DeviceItemType) => { | |
70 | + return option.alias?.includes?.(inputValue) || option.name?.includes?.(inputValue) | |
71 | + }, | |
72 | + } | |
73 | + }, | |
74 | + }, | |
75 | + { | |
76 | + field: ContentDataFieldsEnum.DEVICE_NAME, | |
77 | + label: ContentDataFieldsNameEnum.deviceName, | |
78 | + component: ComponentEnum.INPUT, | |
79 | + ifShow: false, | |
80 | + }, | |
81 | + { | |
82 | + field: ContentDataFieldsEnum.ATTR, | |
83 | + label: ContentDataFieldsNameEnum.ATTR, | |
84 | + component: ComponentEnum.API_SELECT, | |
85 | + required: true, | |
86 | + componentProps: ({ formModel }) => { | |
87 | + const deviceProfileId = isTemplate ? formModel[ContentDataFieldsEnum.DEVICE_PROFILE_TEMPLATE_ID] : formModel[ContentDataFieldsEnum.DEVICE_PROFILE_ID] | |
88 | + return { | |
89 | + showSearch: true, | |
90 | + mode: 'multiple', | |
91 | + api: async (params: string) => { | |
92 | + if (!deviceProfileId) return [] | |
93 | + const options = await getDeviceAttributes(params) | |
94 | + if (componentKey === ControlComponentEnum.SWITCH) { // 开关只返回bool | |
95 | + return options.filter((item) => { | |
96 | + return item.detail.dataType.type === DataTypeEnum.BOOL | |
97 | + }) | |
98 | + } | |
99 | + return options | |
100 | + }, | |
101 | + params: deviceProfileId, | |
102 | + fieldNames: { label: 'name', value: 'identifier' }, | |
103 | + filterOption: (inputValue: string, option: ThingsModelItemType) => { | |
104 | + return option.name.includes(inputValue) | |
105 | + }, | |
106 | + } | |
107 | + }, | |
108 | + }, | |
109 | + ] | |
110 | +} | ... | ... |
1 | +export { default as DataSourceForm } from './index.vue' | ... | ... |
1 | +<script setup lang="ts"> | |
2 | +import { computed, unref } from 'vue' | |
3 | +import type { RuleError } from 'ant-design-vue/lib/form/interface' | |
4 | +import { formSchemas } from './config' | |
5 | +import { FormLayoutEnum } from '@/components/Form/src/enum' | |
6 | +import { BasicForm, useForm } from '@/components/Form' | |
7 | + | |
8 | +export interface ComponentExposeType { | |
9 | + getFieldsValue: () => any | |
10 | + setFieldsValue: (value: any) => void | |
11 | + validate: () => Promise<RuleError | any> | |
12 | +} | |
13 | + | |
14 | +const props = defineProps<{ componentKey?: string }>() | |
15 | +defineEmits(['fieldValueChange']) | |
16 | + | |
17 | +const getComponentKey = computed(() => { | |
18 | + return props.componentKey | |
19 | +}) | |
20 | + | |
21 | +const [registerForm, formActionType] = useForm({ | |
22 | + schemas: formSchemas(unref(getComponentKey)), | |
23 | + showActionButtonGroup: false, | |
24 | + layout: FormLayoutEnum.HORIZONTAL, | |
25 | + labelWidth: 70, | |
26 | +}) | |
27 | + | |
28 | +const getFieldsValue = () => { | |
29 | + return formActionType.getFieldsValue() | |
30 | +} | |
31 | + | |
32 | +const validate = async () => { | |
33 | + return await formActionType.validate() | |
34 | +} | |
35 | + | |
36 | +const setFieldsValue = (value: Recordable) => { | |
37 | + formActionType.setFieldsValue(value) | |
38 | + formActionType.clearValidate() | |
39 | +} | |
40 | + | |
41 | +defineExpose<ComponentExposeType>({ | |
42 | + getFieldsValue, | |
43 | + setFieldsValue, | |
44 | + validate, | |
45 | +}) | |
46 | +</script> | |
47 | + | |
48 | +<template> | |
49 | + <BasicForm @register="registerForm" @field-value-change="$emit('fieldValueChange')" /> | |
50 | +</template> | ... | ... |
... | ... | @@ -238,6 +238,7 @@ export enum ContentDataFieldsEnum { |
238 | 238 | DEVICE_ID = 'deviceId', |
239 | 239 | ORG_ID = 'orgId', |
240 | 240 | ATTR = 'attr', |
241 | + DEVICE_NAME = 'deviceName', | |
241 | 242 | VIDEO_FILTER = 'videoFilter', |
242 | 243 | VIDEO_ID = 'id', |
243 | 244 | VIDEO_URL = 'videoUrl', |
... | ... | @@ -253,6 +254,7 @@ export enum ContentDataFieldsNameEnum { |
253 | 254 | ORG_ID = '组织', |
254 | 255 | DEVICE_ID = '设备', |
255 | 256 | ATTR = '属性', |
257 | + deviceName = '设备名称', | |
256 | 258 | VIDEO_ID = '视频流', |
257 | 259 | ACCESS_MODE = 'ACCESS_MODE', |
258 | 260 | VIDEO_URL = '视频地址', | ... | ... |
1 | 1 | import { defineStore } from 'pinia' |
2 | -import { toRaw, unref } from 'vue' | |
2 | +import { toRaw } from 'vue' | |
3 | 3 | import { store } from '..' |
4 | 4 | import type { NodeDataType } from '@/api/node/model' |
5 | 5 | import type { ProductAndDevice } from '@/api/content/model' |
... | ... | @@ -7,12 +7,6 @@ import type { CreateComponentType } from '@/core/Library/types' |
7 | 7 | import { isShareMode } from '@/utils/env' |
8 | 8 | import { ConfigurationAuthEnum, ConfigurationTemplateAuthEnum } from '@/enums/authEnum' |
9 | 9 | |
10 | -interface DeviceList { | |
11 | - deviceId: string | |
12 | - name: string | |
13 | - codeType: string | |
14 | -} | |
15 | - | |
16 | 10 | interface ContentDataStoreType { |
17 | 11 | contentData: NodeDataType[] |
18 | 12 | configurationId: Nullable<string> |
... | ... | @@ -20,7 +14,6 @@ interface ContentDataStoreType { |
20 | 14 | isTemplate?: number | null | string |
21 | 15 | configurationContentList?: any |
22 | 16 | configurationContentId: Nullable<string> |
23 | - diveceDetailMap: Record<string, DeviceList> | |
24 | 17 | hasDesignAuth?: boolean |
25 | 18 | hasPerviewAuth?: boolean |
26 | 19 | isTemplateLink?: boolean |
... | ... | @@ -35,7 +28,6 @@ export const useContentDataStore = defineStore('app-content-data', { |
35 | 28 | productAndDevice: [], |
36 | 29 | configurationContentList: [], |
37 | 30 | configurationContentId: null, |
38 | - diveceDetailMap: {}, | |
39 | 31 | hasDesignAuth: true, |
40 | 32 | hasPerviewAuth: true, |
41 | 33 | isTemplateLink: false, |
... | ... | @@ -70,17 +62,9 @@ export const useContentDataStore = defineStore('app-content-data', { |
70 | 62 | |
71 | 63 | setProductAndDevice(list: ProductAndDevice[]) { |
72 | 64 | this.productAndDevice = list |
73 | - this.doBuildDeviceMap() | |
74 | 65 | return list |
75 | 66 | }, |
76 | 67 | |
77 | - doBuildDeviceMap() { | |
78 | - const list = this.productAndDevice.map(item => toRaw(unref(item.deviceList))).flat(1) | |
79 | - this.diveceDetailMap = list.reduce((prev, next) => { | |
80 | - return { ...prev, [next?.deviceId]: next } | |
81 | - }, {} as Record<'string', DeviceList>) | |
82 | - }, | |
83 | - | |
84 | 68 | getCurrentNodeDataById(config: CreateComponentType): Nullable<NodeDataType> { |
85 | 69 | const { cellInfo } = config |
86 | 70 | const { id } = cellInfo || {} | ... | ... |