Commit 7b757491c5e96dc344de09a289f7d04ad7b44d6c

Authored by xp.Huang
2 parents c0f86739 b93d637f

Merge branch 'f-dev' into 'main'

fix:修改报表配置相关

See merge request huang/yun-teng-iot-front!279
... ... @@ -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
... ...
... ... @@ -9,7 +9,6 @@ export const formSchema: FormSchema[] = [
9 9 required: true,
10 10 component: 'Input',
11 11 componentProps: {
12   - maxLength: 10,
13 12 placeholder: '请输入用户昵称',
14 13 },
15 14 rules: chineseAndEnlishRule,
... ...
... ... @@ -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 = () => {};
... ...