Commit 7b757491c5e96dc344de09a289f7d04ad7b44d6c
Merge branch 'f-dev' into 'main'
fix:修改报表配置相关 See merge request huang/yun-teng-iot-front!279
Showing
9 changed files
with
211 additions
and
76 deletions
... | ... | @@ -4,6 +4,7 @@ import { ExportQueryParam } from './model/exportModel'; |
4 | 4 | enum ReportManagerApi { |
5 | 5 | GET_EXPORT_API = '/report/generate/record', |
6 | 6 | DELETE_EXPORT_API = '/report/generate/record', |
7 | + GET_TELEMETRY_API = '/plugins/telemetry/', | |
7 | 8 | } |
8 | 9 | |
9 | 10 | //报表导出分页 |
... | ... | @@ -23,3 +24,17 @@ export const deleteExportManage = (ids: string[]) => { |
23 | 24 | }, |
24 | 25 | }); |
25 | 26 | }; |
27 | + | |
28 | +//报表查看趋势图 | |
29 | +export const exportViewChartApi = (entityId, params) => { | |
30 | + return defHttp.get<ExportQueryParam>( | |
31 | + { | |
32 | + url: | |
33 | + ReportManagerApi.GET_TELEMETRY_API + 'DEVICE' + '/' + entityId + '/values' + '/timeseries', | |
34 | + params, | |
35 | + }, | |
36 | + { | |
37 | + joinPrefix: false, | |
38 | + } | |
39 | + ); | |
40 | +}; | ... | ... |
... | ... | @@ -12,6 +12,8 @@ enum ReportManagerApi { |
12 | 12 | JOB_LOG_PAGE_API = '/monitor/jobLog/page', |
13 | 13 | DELETE_LOG_API = '/monitor/jobLog', |
14 | 14 | POST_LOG_CLEAN_API = '/monitor/jobLog/clean', |
15 | + QUERY_CORN_API = '/monitor/job/queryCronExpression/', | |
16 | + CHECK_CORN_API = '/monitor/job/checkCron/', | |
15 | 17 | } |
16 | 18 | |
17 | 19 | //分页 |
... | ... | @@ -57,7 +59,21 @@ export const putSchedueByidAndStatusManage = (id, status) => { |
57 | 59 | //执行一次 |
58 | 60 | export const postRunSchedueConfigManage = (id) => { |
59 | 61 | return defHttp.post<ReportModel>({ |
60 | - url: ReportManagerApi.RUN_REPORT_API+'/'+id, | |
62 | + url: ReportManagerApi.RUN_REPORT_API + '/' + id, | |
63 | + }); | |
64 | +}; | |
65 | + | |
66 | +// 查询cron表达式近5次的执行时间 | |
67 | +export const schedueQueryCornTimeApi = (corn) => { | |
68 | + return defHttp.get({ | |
69 | + url: ReportManagerApi.QUERY_CORN_API + corn, | |
70 | + }); | |
71 | +}; | |
72 | + | |
73 | +// 检查cron表达式是否有效 | |
74 | +export const schedueCheckCornApi = (corn) => { | |
75 | + return defHttp.get({ | |
76 | + url: ReportManagerApi.CHECK_CORN_API + corn, | |
61 | 77 | }); |
62 | 78 | }; |
63 | 79 | ... | ... |
... | ... | @@ -23,7 +23,6 @@ |
23 | 23 | @deselect="handleDeselect" |
24 | 24 | mode="multiple" |
25 | 25 | labelInValue |
26 | - allowClear | |
27 | 26 | notFoundContent="请选择设备" |
28 | 27 | /> |
29 | 28 | <div style="margin-top: 1.5vh"></div> |
... | ... | @@ -66,6 +65,7 @@ |
66 | 65 | attribute?: string; |
67 | 66 | device?: string; |
68 | 67 | name?: string; |
68 | + attributes?: string | undefined; | |
69 | 69 | }; |
70 | 70 | const emit = defineEmits(['success', 'register']); |
71 | 71 | const bindDeviceRefObj = { |
... | ... | @@ -105,8 +105,10 @@ |
105 | 105 | //设备Select选中 |
106 | 106 | const handleDeviceChange = (e) => { |
107 | 107 | if (unref(isUpdate)) { |
108 | + //编辑 | |
109 | + let temp: any = []; | |
108 | 110 | editDeviceAttr.value.forEach((f) => { |
109 | - deviceList.value = [f, ...e]; | |
111 | + temp = [f, ...e]; | |
110 | 112 | }); |
111 | 113 | let deWeightThree = () => { |
112 | 114 | let map = new Map(); |
... | ... | @@ -117,7 +119,11 @@ |
117 | 119 | } |
118 | 120 | return [...map.values()]; |
119 | 121 | }; |
120 | - deviceList.value = deWeightThree(); | |
122 | + temp = deWeightThree(); | |
123 | + deviceList.value = temp; | |
124 | + if (e.length !== 0) { | |
125 | + deviceList.value = e; | |
126 | + } | |
121 | 127 | } else { |
122 | 128 | deviceList.value = e; |
123 | 129 | } |
... | ... | @@ -125,7 +131,7 @@ |
125 | 131 | //设备取消删除 |
126 | 132 | const handleDeselect = (e) => { |
127 | 133 | if (unref(isUpdate)) { |
128 | - const eEditDevice = e.key || e.value; | |
134 | + //编辑 | |
129 | 135 | let deWeightThree = () => { |
130 | 136 | let map = new Map(); |
131 | 137 | for (let item of deviceList.value) { |
... | ... | @@ -136,8 +142,13 @@ |
136 | 142 | return [...map.values()]; |
137 | 143 | }; |
138 | 144 | deviceList.value = deWeightThree(); |
139 | - const findEditValue = deviceList.value.findIndex((f) => f.value == eEditDevice); | |
145 | + const findEditValue = deviceList.value.findIndex((f) => f.value == e.value); | |
140 | 146 | if (findEditValue !== -1) deviceList.value.splice(findEditValue, 1); |
147 | + // try { | |
148 | + // if (deviceList.value[0]?.attributes.length > 0) { | |
149 | + // deviceList.value.splice(0, 1); | |
150 | + // } | |
151 | + // } catch {} | |
141 | 152 | } else { |
142 | 153 | const eDevice = e.key || e.value; |
143 | 154 | const findValue = deviceList.value.findIndex((f) => f.value == eDevice); |
... | ... | @@ -179,12 +190,29 @@ |
179 | 190 | await setFieldsValue({ |
180 | 191 | agg: editResData.data.queryCondition?.agg, |
181 | 192 | interval: editResData.data.queryCondition?.interval, |
182 | - limit1: editResData.data.queryCondition?.limit, | |
193 | + limit: editResData.data.queryCondition?.limit, | |
183 | 194 | orderBy: editResData.data.queryCondition?.orderBy, |
184 | 195 | useStrictDataTypes: editResData.data.queryCondition?.useStrictDataTypes, |
185 | 196 | startTs: editResData.data.queryCondition?.startTs, |
186 | 197 | endTs: editResData.data.queryCondition?.endTs, |
198 | + way: editResData.data?.way, | |
199 | + queryMode: editResData.data.queryCondition?.queryMode === 0 ? 'latest' : 'timePeriod', | |
200 | + }); | |
201 | + const endTsTime = editResData.data.queryCondition?.endTs; | |
202 | + const startTsTime = editResData.data.queryCondition?.startTs; | |
203 | + const mathFloor = (endTsTime - startTsTime) / 10; | |
204 | + const multTen = Math.floor(mathFloor) * 10; | |
205 | + await setFieldsValue({ | |
206 | + startTs: multTen, | |
187 | 207 | }); |
208 | + if (editResData.data.queryCondition?.queryMode == 1) { | |
209 | + await setFieldsValue({ | |
210 | + dataRange: [ | |
211 | + editResData.data.queryCondition?.startTs, | |
212 | + editResData.data.queryCondition?.endTs, | |
213 | + ], | |
214 | + }); | |
215 | + } | |
188 | 216 | //回显聚合条件 |
189 | 217 | const dataCompareOpions = [ |
190 | 218 | { label: '最小值', value: AggregateDataEnum.MIN }, |
... | ... | @@ -248,6 +276,10 @@ |
248 | 276 | deviceList.value = editDeviceAttr.value; |
249 | 277 | editDeviceList.value = editResData.data.executeAttributes; |
250 | 278 | } else { |
279 | + setFieldsValue({ | |
280 | + startTs: 1000, | |
281 | + interval: 1000, | |
282 | + }); | |
251 | 283 | editId.value = ''; |
252 | 284 | orgId.value = ''; |
253 | 285 | selectDevice.value = []; |
... | ... | @@ -312,13 +344,17 @@ |
312 | 344 | if (getAttrDevice.value.length === 0) { |
313 | 345 | return createMessage.error('请选择设备及其属性'); |
314 | 346 | } |
347 | + } else { | |
348 | + if (getAttrDevice.value.length === 0) { | |
349 | + return createMessage.error('请选择设备及其属性'); | |
350 | + } | |
315 | 351 | } |
316 | 352 | if (values.executeWay == 0) { |
317 | 353 | executeContent = null; |
318 | 354 | } else { |
319 | 355 | executeContent = values.cronTime; |
320 | 356 | } |
321 | - if (values.way === QueryWay.LATEST) { | |
357 | + if (values.queryMode === QueryWay.LATEST) { | |
322 | 358 | startTs.value = moment().subtract(values.startTs, 'ms').valueOf(); |
323 | 359 | endTs.value = Date.now(); |
324 | 360 | } else { |
... | ... | @@ -335,6 +371,7 @@ |
335 | 371 | ...{ |
336 | 372 | endTs: endTs.value, |
337 | 373 | }, |
374 | + queryMode: values?.queryMode === 'latest' ? 0 : 1, | |
338 | 375 | }; |
339 | 376 | |
340 | 377 | delete values.devices; |
... | ... | @@ -347,6 +384,7 @@ |
347 | 384 | delete values.cronYear; |
348 | 385 | delete values.limit1; |
349 | 386 | delete values.startTs; |
387 | + delete values.queryMode; | |
350 | 388 | postObj = { |
351 | 389 | ...values, |
352 | 390 | ...{ | ... | ... |
... | ... | @@ -21,7 +21,7 @@ export enum QueryWay { |
21 | 21 | TIME_PERIOD = 'timePeriod', |
22 | 22 | } |
23 | 23 | export enum SchemaFiled { |
24 | - WAY = 'way', | |
24 | + WAY = 'queryMode', | |
25 | 25 | TIME_PERIOD = 'timePeriod', |
26 | 26 | KEYS = 'keys', |
27 | 27 | DATE_RANGE = 'dataRange', |
... | ... | @@ -214,8 +214,12 @@ export const formSchema: QFormSchema[] = [ |
214 | 214 | placeholder: '请选择执行方式', |
215 | 215 | onChange(e) { |
216 | 216 | let dataCompareOpions: any = []; |
217 | + setFieldsValue({ | |
218 | + startTs: 1000, | |
219 | + interval: 1000, | |
220 | + }); | |
217 | 221 | if (e.target.value == 0) { |
218 | - setFieldsValue({ way: QueryWay.LATEST }); | |
222 | + setFieldsValue({ queryMode: QueryWay.LATEST }); | |
219 | 223 | dataCompareOpions = [ |
220 | 224 | { label: '固定周期', value: QueryWay.LATEST }, |
221 | 225 | { label: '自定义周期', value: QueryWay.TIME_PERIOD }, |
... | ... | @@ -227,7 +231,7 @@ export const formSchema: QFormSchema[] = [ |
227 | 231 | }, |
228 | 232 | }); |
229 | 233 | } else { |
230 | - setFieldsValue({ way: QueryWay.LATEST }); | |
234 | + setFieldsValue({ queryMode: QueryWay.LATEST }); | |
231 | 235 | dataCompareOpions = [{ label: '固定周期', value: QueryWay.LATEST }]; |
232 | 236 | updateSchema({ |
233 | 237 | defaultValue: QueryWay.LATEST, |
... | ... | @@ -492,7 +496,6 @@ export const formSchema: QFormSchema[] = [ |
492 | 496 | formActionType.setFieldsValue({ [SchemaFiled.LIMIT]: null }); |
493 | 497 | } |
494 | 498 | return { |
495 | - // defaultValue: 1000, | |
496 | 499 | placeholder: '请选择间隔时间', |
497 | 500 | options, |
498 | 501 | }; | ... | ... |
... | ... | @@ -10,7 +10,7 @@ |
10 | 10 | :showOkBtn="false" |
11 | 11 | > |
12 | 12 | <div class="wrapper"> |
13 | - <div class="inner item" v-for="(item, index) in chartData" :key="index"> | |
13 | + <div class="inner item" v-for="(item, index) in initChartData" :key="index"> | |
14 | 14 | <p style="display: none">{{ item }}</p> |
15 | 15 | <div :id="`chart${index}`" :style="{ height, width }"></div> |
16 | 16 | </div> |
... | ... | @@ -22,6 +22,8 @@ |
22 | 22 | import { ref, PropType, nextTick } from 'vue'; |
23 | 23 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
24 | 24 | import * as echarts from 'echarts'; |
25 | + import { exportViewChartApi } from '/@/api/export/exportManager'; | |
26 | + import moment from 'moment'; | |
25 | 27 | |
26 | 28 | defineProps({ |
27 | 29 | width: { |
... | ... | @@ -34,72 +36,120 @@ |
34 | 36 | }, |
35 | 37 | }); |
36 | 38 | defineEmits(['register']); |
37 | - | |
38 | 39 | const heightNum = ref(800); |
39 | - const chartData = ref([1, 2, 3, 4]); | |
40 | - const [register] = useModalInner((data) => { | |
41 | - console.log(data); | |
42 | - //TODO待服务端返回值 | |
43 | - nextTick(() => { | |
44 | - chartData.value.forEach((_, index) => { | |
45 | - let myChart = echarts.init(document.getElementById(`chart${index}`) as HTMLElement); | |
46 | - let myOption = { | |
47 | - title: { | |
48 | - text: '报表趋势图', | |
49 | - subtext: `图${index + 1}`, | |
50 | - left: 'left', | |
51 | - }, | |
52 | - tooltip: { | |
53 | - trigger: 'axis', | |
54 | - }, | |
55 | - legend: { | |
56 | - data: ['CO', 'CO2', 'Temp'], | |
57 | - top: '20px', | |
58 | - }, | |
59 | - toolbox: {}, | |
60 | - grid: { | |
61 | - left: '3%', | |
62 | - right: '4%', | |
63 | - bottom: '3%', | |
64 | - containLabel: true, | |
65 | - }, | |
66 | - xAxis: { | |
67 | - type: 'category', | |
68 | - data: ['1', '2', '3', '4', '5', '6', '7'], | |
69 | - boundaryGap: false, | |
70 | - }, | |
71 | - yAxis: { | |
72 | - type: 'value', | |
73 | - boundaryGap: false, | |
74 | - }, | |
75 | - series: [ | |
76 | - { | |
77 | - name: 'CO', | |
78 | - type: 'line', | |
79 | - stack: 'Total', | |
80 | - data: [150, 230, 224, 218, 135, 147, 260], | |
81 | - }, | |
82 | - { | |
83 | - name: 'CO2', | |
84 | - type: 'line', | |
85 | - stack: 'Total', | |
86 | - data: [15, 23, 22, 28, 15, 17, 20], | |
87 | - }, | |
40 | + const getItem: any = ref([]); | |
41 | + const initChartData: any = ref([]); | |
42 | + //生成随机颜色 | |
43 | + let getRandomColor = function () { | |
44 | + return ( | |
45 | + 'rgb(' + | |
46 | + Math.round(Math.random() * 255) + | |
47 | + ',' + | |
48 | + Math.round(Math.random() * 255) + | |
49 | + ',' + | |
50 | + Math.round(Math.random() * 10) + | |
51 | + ')' | |
52 | + ); | |
53 | + }; | |
54 | + const [register, { setModalProps }] = useModalInner(async (data) => { | |
55 | + setModalProps({ loading: true }); | |
56 | + try { | |
57 | + const entityId = data.record.executeCondition?.executeAttributes.map((m) => m?.device); | |
58 | + let key = data.record.executeCondition?.executeAttributes.map((m) => m?.attributes); | |
59 | + let params: any = {}; | |
60 | + params = { | |
61 | + ...data.record.executeCondition?.executeCondition, | |
62 | + ...{ | |
63 | + keys: key.length !== 0 ? key.join(',') : '', | |
64 | + }, | |
65 | + }; | |
66 | + const result = await exportViewChartApi(entityId[0], params); | |
67 | + getItem.value = Object.entries(result).map((item) => { | |
68 | + const [attr, val] = item; | |
69 | + return { attr, val }; | |
70 | + }); | |
71 | + //服务端返回值 | |
72 | + initChartData.value = getItem.value.map((item): any => { | |
73 | + const seriesData = item.val.map((m1) => Number(m1.value)); | |
74 | + return { | |
75 | + attr: item.attr, | |
76 | + lengendData: [item.attr], | |
77 | + xAxisData: item.val.map((m) => moment(m.ts).format('YYYY-MM-DD HH:mm:ss')), | |
78 | + seriesData: [ | |
88 | 79 | { |
89 | - name: 'Temp', | |
80 | + name: item.attr, | |
90 | 81 | type: 'line', |
91 | - stack: 'Total', | |
92 | - data: [15, 23, 22, 28, 15, 17, 20], | |
82 | + data: seriesData, | |
83 | + lineStyle: { | |
84 | + color: getRandomColor(), | |
85 | + }, | |
93 | 86 | }, |
94 | 87 | ], |
95 | 88 | }; |
96 | - myChart.setOption(myOption); | |
97 | - //自适应 | |
98 | - window.addEventListener('resize', () => { | |
99 | - myChart.resize(); | |
89 | + }); | |
90 | + nextTick(() => { | |
91 | + initChartData.value.forEach((item, index) => { | |
92 | + let myChart = echarts.init(document.getElementById(`chart${index}`) as HTMLElement); | |
93 | + let myOption = { | |
94 | + title: { | |
95 | + text: `${item.attr}趋势图`, | |
96 | + subtext: `${item.attr}`, | |
97 | + left: 'left', | |
98 | + }, | |
99 | + tooltip: { | |
100 | + trigger: 'axis', | |
101 | + }, | |
102 | + legend: { | |
103 | + data: item.lengendData, | |
104 | + top: '20px', | |
105 | + }, | |
106 | + toolbox: {}, | |
107 | + grid: { | |
108 | + left: '3%', | |
109 | + right: '4%', | |
110 | + bottom: '3%', | |
111 | + containLabel: true, | |
112 | + }, | |
113 | + dataZoom: [ | |
114 | + { | |
115 | + type: 'slider', | |
116 | + show: true, | |
117 | + start: 0, | |
118 | + end: 30, | |
119 | + xAxisIndex: [0], | |
120 | + }, | |
121 | + ], | |
122 | + xAxis: { | |
123 | + type: 'category', | |
124 | + data: item.xAxisData, | |
125 | + boundaryGap: false, | |
126 | + axisPointer: { type: 'shadow' }, | |
127 | + axisLabel: { | |
128 | + color: '#333', | |
129 | + // 让x轴文字方向为竖向 | |
130 | + interval: 0, | |
131 | + rotate: 90, | |
132 | + // formatter: function (value) { | |
133 | + // return value.split('').join('\n'); | |
134 | + // }, | |
135 | + }, | |
136 | + }, | |
137 | + yAxis: { | |
138 | + type: 'value', | |
139 | + boundaryGap: false, | |
140 | + }, | |
141 | + series: item.seriesData, | |
142 | + }; | |
143 | + myChart.setOption(myOption); | |
144 | + //自适应 | |
145 | + window.addEventListener('resize', () => { | |
146 | + myChart.resize(); | |
147 | + }); | |
100 | 148 | }); |
101 | 149 | }); |
102 | - }); | |
150 | + } finally { | |
151 | + setModalProps({ loading: false }); | |
152 | + } | |
103 | 153 | }); |
104 | 154 | </script> |
105 | 155 | <style lang="less" scoped> | ... | ... |
... | ... | @@ -20,8 +20,11 @@ |
20 | 20 | label: '报表导出', |
21 | 21 | icon: 'ant-design:dot-chart-outlined', |
22 | 22 | auth: 'api:yt:reportExport:export', |
23 | - onClick: handleExport.bind(null, record), | |
24 | 23 | ifShow: record.executeStatus === 1, |
24 | + popConfirm: { | |
25 | + title: '是否需要导出', | |
26 | + confirm: handleExport.bind(null, record), | |
27 | + }, | |
25 | 28 | }, |
26 | 29 | { |
27 | 30 | label: '报表查看', |
... | ... | @@ -99,7 +102,7 @@ |
99 | 102 | }); |
100 | 103 | |
101 | 104 | const [registerModal, { openModal }] = useModal(); |
102 | - const handleView = (_, record) => { | |
105 | + const handleView = (record) => { | |
103 | 106 | openModal(true, { |
104 | 107 | isUpdate: true, |
105 | 108 | record, | ... | ... |
... | ... | @@ -59,6 +59,7 @@ |
59 | 59 | @register="registerTable" |
60 | 60 | > |
61 | 61 | <template #toolbar> |
62 | + <a-button type="primary" @click="handleClearData"> 刷新 </a-button> | |
62 | 63 | <Popconfirm |
63 | 64 | title="您确定要清空全部数据" |
64 | 65 | ok-text="确定" |
... | ... | @@ -261,6 +262,7 @@ |
261 | 262 | await schedueLogCleanPage(); |
262 | 263 | createMessage.success(`清空成功`); |
263 | 264 | handleSuccess(); |
265 | + handleClearData(); | |
264 | 266 | }; |
265 | 267 | const handleSearchInfo = async () => { |
266 | 268 | setProps({ | ... | ... |
... | ... | @@ -2,7 +2,7 @@ |
2 | 2 | <div> |
3 | 3 | <BasicModal |
4 | 4 | v-bind="$attrs" |
5 | - width="62rem" | |
5 | + width="80rem" | |
6 | 6 | :height="heightNum" |
7 | 7 | @register="register" |
8 | 8 | title="任务详细信息" |
... | ... | @@ -22,21 +22,30 @@ |
22 | 22 | import { personSchema } from './config.data'; |
23 | 23 | import { Description } from '/@/components/Description/index'; |
24 | 24 | import { useDescription } from '/@/components/Description'; |
25 | + import { schedueQueryCornTimeApi, schedueCheckCornApi } from '/@/api/schedue/schedueManager'; | |
25 | 26 | |
26 | 27 | const heightNum = ref(800); |
28 | + const timeData: any = ref([]); | |
27 | 29 | let personData = reactive({}); |
28 | 30 | const [registeDesc, { setDescProps }] = useDescription({ |
29 | 31 | data: personData, |
30 | 32 | schema: personSchema, |
31 | 33 | column: 3, |
32 | 34 | }); |
33 | - const [register] = useModalInner((data) => { | |
35 | + const [register] = useModalInner(async (data) => { | |
36 | + console.log(data.record.cronExpression); | |
37 | + const resuCheck = await schedueCheckCornApi(encodeURIComponent(data.record.cronExpression)); | |
38 | + if (resuCheck) { | |
39 | + const resu = await schedueQueryCornTimeApi(encodeURIComponent(data.record.cronExpression)); | |
40 | + timeData.value = resu.data; | |
41 | + } | |
34 | 42 | nextTick(() => { |
35 | 43 | //回显 |
36 | 44 | for (let i in data.record) { |
37 | 45 | Reflect.set(personData, i, data.record[i]); |
38 | 46 | } |
39 | 47 | setDescProps(personData); |
48 | + Reflect.set(personData, 'updateTime', timeData.value.join(',')); | |
40 | 49 | }); |
41 | 50 | }); |
42 | 51 | const handleCancel = () => {}; | ... | ... |