Commit 36f46c76fc7269d90a6dac549076caee33489983

Authored by ww
1 parent 7b1f93b1

fix: report export trend charts not render multiple device charts

@@ -7,3 +7,43 @@ export type ExportParam = { @@ -7,3 +7,43 @@ export type ExportParam = {
7 orderFiled: string; 7 orderFiled: string;
8 orderType: string; 8 orderType: string;
9 }; 9 };
  10 +export interface ExecuteCondition2 {
  11 + interval: number;
  12 + limit: number;
  13 + agg: string;
  14 + orderBy: string;
  15 + useStrictDataTypes: boolean;
  16 + startTs: number;
  17 + endTs: number;
  18 + queryMode: number;
  19 +}
  20 +
  21 +export interface ExecuteAttribute {
  22 + device: string;
  23 + name: string;
  24 + attributes: string[];
  25 +}
  26 +
  27 +export interface ExecuteCondition {
  28 + executeCondition: ExecuteCondition2;
  29 + executeAttributes: ExecuteAttribute[];
  30 +}
  31 +
  32 +export interface ExecuteReportRecord {
  33 + id: string;
  34 + creator: string;
  35 + createTime: string;
  36 + updateTime: string;
  37 + enabled: boolean;
  38 + tenantId: string;
  39 + reportConfigName: string;
  40 + organizationName: string;
  41 + organizationId: string;
  42 + dataCompare: number;
  43 + executeWay: number;
  44 + executeStatus: number;
  45 + executeTime: string;
  46 + reportPath: string;
  47 + executeCondition: ExecuteCondition;
  48 + jobId: string;
  49 +}
@@ -7,38 +7,63 @@ @@ -7,38 +7,63 @@
7 :height="heightNum" 7 :height="heightNum"
8 @register="register" 8 @register="register"
9 title="报表趋势图" 9 title="报表趋势图"
  10 + :minHeight="500"
10 :showOkBtn="false" 11 :showOkBtn="false"
11 > 12 >
12 - <div :class="[initChartData.length % 2 !== 0 ? '' : 'wrapper']">  
13 - <div  
14 - v-if="initChartData.length > 0"  
15 - :class="[initChartData.length % 2 !== 0 ? '' : 'chart-style']"  
16 - > 13 + <div :class="[chartInstance.length % 2 !== 0 ? '' : 'wrapper']">
  14 + <Spin :spinning="loading">
17 <div 15 <div
18 - :class="[  
19 - initChartData.length % 2 !== 0 ? '' : 'inner',  
20 - initChartData.length % 2 !== 0 ? '' : 'item',  
21 - ]"  
22 - v-for="(item, index) in initChartData"  
23 - :key="index" 16 + v-if="chartInstance.length > 0"
  17 + :class="[chartInstance.length % 2 !== 0 ? '' : 'chart-style']"
24 > 18 >
25 - <p style="display: none">{{ item }}</p>  
26 - <div :id="`chart${index}`" :style="{ height, width }"></div> 19 + <div
  20 + :class="[
  21 + chartInstance.length % 2 !== 0 ? '' : 'inner',
  22 + chartInstance.length % 2 !== 0 ? '' : 'item',
  23 + ]"
  24 + v-for="item in chartInstance"
  25 + :key="item.device"
  26 + >
  27 + <p class="text-black text-lg">{{ item.name }}</p>
  28 + <div class="flex text-black items-center">
  29 + <div class="mr-4 text-sm">属性:</div>
  30 + <Select
  31 + class="min-w-25"
  32 + v-model:value="item.active"
  33 + @change="(value) => handleChangeChars(value, item.device)"
  34 + placeholder="请选择设备属性"
  35 + >
  36 + <Select.Option v-for="attr in item.attributes" :key="attr" :value="attr">
  37 + {{ attr }}
  38 + </Select.Option>
  39 + </Select>
  40 + </div>
  41 + <div>
  42 + <div :id="`chart-${item.device}`" :style="{ height, width }"></div>
  43 + </div>
  44 + </div>
27 </div> 45 </div>
28 - </div>  
29 - <div v-else style="display: flex; justify-content: center; align-items: center">  
30 - <div style="position: relative; left: 0rem; top: 3rem">暂无数据</div>  
31 - </div> 46 + <div v-else style="display: flex; justify-content: center; align-items: center">
  47 + <div style="position: relative; left: 0rem; top: 3rem">暂无数据</div>
  48 + </div>
  49 + </Spin>
32 </div> 50 </div>
33 </BasicModal> 51 </BasicModal>
34 </div> 52 </div>
35 </template> 53 </template>
36 <script setup lang="ts"> 54 <script setup lang="ts">
37 - import { ref, PropType, nextTick } from 'vue'; 55 + import { ref, PropType, nextTick, shallowReactive, onMounted, onUnmounted } from 'vue';
38 import { BasicModal, useModalInner } from '/@/components/Modal'; 56 import { BasicModal, useModalInner } from '/@/components/Modal';
39 import * as echarts from 'echarts'; 57 import * as echarts from 'echarts';
40 import { exportViewChartApi } from '/@/api/export/exportManager'; 58 import { exportViewChartApi } from '/@/api/export/exportManager';
41 import moment from 'moment'; 59 import moment from 'moment';
  60 + import { ExecuteReportRecord } from '/@/api/export/model/exportModel';
  61 + import { Select, Spin } from 'ant-design-vue';
  62 +
  63 + interface ResponsData {
  64 + attr: string;
  65 + val: { ts: number; value: string }[];
  66 + }
42 67
43 defineProps({ 68 defineProps({
44 width: { 69 width: {
@@ -52,8 +77,14 @@ @@ -52,8 +77,14 @@
52 }); 77 });
53 defineEmits(['register']); 78 defineEmits(['register']);
54 const heightNum = ref(800); 79 const heightNum = ref(800);
55 - const getItem: any = ref([]);  
56 - const initChartData: any = ref([]); 80 +
  81 + let currentRecord: ExecuteReportRecord = {};
  82 +
  83 + const chartInstance = ref<
  84 + { device: string; name: string; attributes: string[]; active?: string }[]
  85 + >([]);
  86 +
  87 + const chartsInstance = shallowReactive<{ [key: string]: echarts.ECharts }>({});
57 //生成随机颜色 88 //生成随机颜色
58 let getRandomColor = function () { 89 let getRandomColor = function () {
59 return ( 90 return (
@@ -66,55 +97,72 @@ @@ -66,55 +97,72 @@
66 ')' 97 ')'
67 ); 98 );
68 }; 99 };
69 - const [register, { setModalProps }] = useModalInner(async (data) => {  
70 - setModalProps({ loading: true });  
71 - try {  
72 - const entityId = data.record.executeCondition?.executeAttributes.map((m) => m?.device);  
73 - let key = data.record.executeCondition?.executeAttributes.map((m) => m?.attributes);  
74 - let params: any = {};  
75 - params = {  
76 - ...data.record.executeCondition?.executeCondition,  
77 - ...{  
78 - keys: key.length !== 0 ? key.join(',') : '',  
79 - },  
80 - };  
81 - const result = await exportViewChartApi(entityId[0], params);  
82 - getItem.value = Object.entries(result).map((item) => {  
83 - const [attr, val] = item;  
84 - return { attr, val };  
85 - });  
86 - //服务端返回值  
87 - initChartData.value = getItem.value.map((item): any => { 100 +
  101 + const getChartsOption = (result: ResponsData) => {
  102 + const generateInfo = Object.entries(result).map((item) => {
  103 + const [attr, val] = item;
  104 + return { attr, val } as { attr: string; val: { ts: number; value: string }[] };
  105 + });
  106 + const chartDataConfig =
  107 + generateInfo.map((item) => {
88 const seriesData = item.val.map((m1) => Number(m1.value)); 108 const seriesData = item.val.map((m1) => Number(m1.value));
89 return { 109 return {
90 - attr: item.attr,  
91 - lengendData: [item.attr],  
92 - xAxisData: item.val.map((m) => moment(m.ts).format('YYYY-MM-DD HH:mm:ss')),  
93 - seriesData: [  
94 - {  
95 - name: item.attr,  
96 - type: 'line',  
97 - data: seriesData,  
98 - lineStyle: {  
99 - color: getRandomColor(),  
100 - }, 110 + xAxis: item.val.map((m) => moment(m.ts).format('YYYY-MM-DD HH:mm:ss')),
  111 + series: {
  112 + name: item.attr,
  113 + type: 'line',
  114 + data: seriesData,
  115 + lineStyle: {
  116 + color: getRandomColor(),
101 }, 117 },
102 - ],  
103 - };  
104 - });  
105 - nextTick(() => {  
106 - initChartData.value.forEach((item, index) => {  
107 - let myChart = echarts.init(document.getElementById(`chart${index}`) as HTMLElement);  
108 - let myOption = { 118 + },
  119 + } as echarts.EChartsOption;
  120 + }) || ([] as echarts.EChartsOption[]);
  121 + const chartOption = {
  122 + xAxisData: chartDataConfig.at(0)?.xAxis,
  123 + series: [chartDataConfig.at(0)?.series],
  124 + };
  125 +
  126 + return chartOption;
  127 + };
  128 +
  129 + const [register, { setModalProps }] = useModalInner(
  130 + async (data: { record: ExecuteReportRecord }) => {
  131 + setModalProps({ loading: true });
  132 + try {
  133 + currentRecord = data.record;
  134 + const deviceInfo = data.record.executeCondition.executeAttributes || [];
  135 + chartInstance.value = deviceInfo.map((item) => ({
  136 + ...item,
  137 + active: item.attributes.at(0),
  138 + }));
  139 + for (const item of deviceInfo) {
  140 + const { attributes, device } = item;
  141 + const keys = attributes.length ? attributes.at(0) : '';
  142 + const sendParams = {
  143 + ...data.record.executeCondition.executeCondition,
  144 + ...{
  145 + keys,
  146 + },
  147 + };
  148 +
  149 + const result = await exportViewChartApi(device, sendParams);
  150 + const { xAxisData, series } = getChartsOption(result as unknown as ResponsData);
  151 +
  152 + await nextTick();
  153 + chartsInstance[device] = echarts.init(
  154 + document.getElementById(`chart-${device}`) as HTMLElement
  155 + );
  156 +
  157 + const chartOption = {
109 title: { 158 title: {
110 - text: `${item.attr}趋势图`,  
111 left: 'center', 159 left: 'center',
112 }, 160 },
113 tooltip: { 161 tooltip: {
114 trigger: 'axis', 162 trigger: 'axis',
115 }, 163 },
116 legend: { 164 legend: {
117 - data: item.lengendData, 165 + data: attributes,
118 top: '20px', 166 top: '20px',
119 }, 167 },
120 toolbox: {}, 168 toolbox: {},
@@ -136,11 +184,11 @@ @@ -136,11 +184,11 @@
136 ], 184 ],
137 xAxis: { 185 xAxis: {
138 type: 'category', 186 type: 'category',
139 - data: item.xAxisData, 187 + data: xAxisData,
140 boundaryGap: false, 188 boundaryGap: false,
141 axisPointer: { type: 'shadow' }, 189 axisPointer: { type: 'shadow' },
142 axisLabel: { 190 axisLabel: {
143 - interval: 0, 191 + interval: 0,
144 rotate: 65, 192 rotate: 65,
145 textStyle: { 193 textStyle: {
146 color: '#000', 194 color: '#000',
@@ -159,18 +207,60 @@ @@ -159,18 +207,60 @@
159 type: 'value', 207 type: 'value',
160 boundaryGap: false, 208 boundaryGap: false,
161 }, 209 },
162 - series: item.seriesData, 210 + series,
163 }; 211 };
164 - myChart.setOption(myOption); 212 + chartsInstance[device].setOption(chartOption);
165 //自适应 213 //自适应
166 - window.addEventListener('resize', () => {  
167 - myChart.resize();  
168 - });  
169 - });  
170 - }); 214 + // window.addEventListener('resize', () => {
  215 + // chartsInstance[device].resize();
  216 + // });
  217 + }
  218 + } catch (error) {
  219 + throw error;
  220 + } finally {
  221 + setModalProps({ loading: false });
  222 + }
  223 + }
  224 + );
  225 + const loading = ref(false);
  226 + const renderCharts = async (device: string, keys: string) => {
  227 + const sendParams = {
  228 + ...currentRecord.executeCondition.executeCondition,
  229 + ...{
  230 + keys,
  231 + },
  232 + };
  233 + try {
  234 + loading.value = true;
  235 + const result = await exportViewChartApi(device, sendParams);
  236 + const { xAxisData, series } = getChartsOption(result as unknown as ResponsData);
  237 +
  238 + chartsInstance[device].setOption({
  239 + series,
  240 + xAxis: { data: xAxisData },
  241 + } as echarts.EChartsOption);
  242 + } catch (error) {
171 } finally { 243 } finally {
172 - setModalProps({ loading: false }); 244 + loading.value = false;
173 } 245 }
  246 + };
  247 +
  248 + const handleChangeChars = (value: string, device: string) => {
  249 + renderCharts(device, value);
  250 + };
  251 +
  252 + const resize = () => {
  253 + Object.keys(chartsInstance).forEach((key) => {
  254 + chartsInstance[key].resize();
  255 + });
  256 + };
  257 +
  258 + onMounted(() => {
  259 + window.addEventListener('resize', resize);
  260 + });
  261 +
  262 + onUnmounted(() => {
  263 + window.removeEventListener('resize', resize);
174 }); 264 });
175 </script> 265 </script>
176 <style lang="less" scoped> 266 <style lang="less" scoped>