Commit 95403b74f74061acfee77997164af33afc9dfb7a
Merge branch 'main_dev' of http://git.yunteng.com/yunteng/thingskit-front into f…
…ix/board-style-issues
Showing
16 changed files
with
187 additions
and
49 deletions
@@ -28,6 +28,8 @@ enum DeviceManagerApi { | @@ -28,6 +28,8 @@ enum DeviceManagerApi { | ||
28 | 28 | ||
29 | DEVICE_ALARM_URL = '/alarm', | 29 | DEVICE_ALARM_URL = '/alarm', |
30 | 30 | ||
31 | + ALARM_BATCH_ACK = '/alarm/batch/ack', | ||
32 | + | ||
31 | DEVICE_CREDENTIALS = '/device/credentials', | 33 | DEVICE_CREDENTIALS = '/device/credentials', |
32 | 34 | ||
33 | COMMAND_ISSUANCE = '/rpc', | 35 | COMMAND_ISSUANCE = '/rpc', |
@@ -346,3 +348,13 @@ export const getDevicesByDeviceIds = (ids: string[]) => { | @@ -346,3 +348,13 @@ export const getDevicesByDeviceIds = (ids: string[]) => { | ||
346 | data: ids, | 348 | data: ids, |
347 | }); | 349 | }); |
348 | }; | 350 | }; |
351 | + | ||
352 | +export const doBatchAckAlarm = (ids: string[]) => { | ||
353 | + return defHttp.post( | ||
354 | + { | ||
355 | + url: DeviceManagerApi.ALARM_BATCH_ACK, | ||
356 | + data: { alarmIds: ids }, | ||
357 | + }, | ||
358 | + { joinPrefix: false } | ||
359 | + ); | ||
360 | +}; |
@@ -18,6 +18,9 @@ | @@ -18,6 +18,9 @@ | ||
18 | 查看告警详情 | 18 | 查看告警详情 |
19 | </a-button> | 19 | </a-button> |
20 | </template> | 20 | </template> |
21 | + <template #toolbar> | ||
22 | + <Button @click="handleBatchAck" type="primary">批量处理</Button> | ||
23 | + </template> | ||
21 | </BasicTable> | 24 | </BasicTable> |
22 | <AlarmDetailDrawer @register="registerDetailDrawer" @success="handleSuccess" /> | 25 | <AlarmDetailDrawer @register="registerDetailDrawer" @success="handleSuccess" /> |
23 | </div> | 26 | </div> |
@@ -26,10 +29,10 @@ | @@ -26,10 +29,10 @@ | ||
26 | import { defineComponent, nextTick, h } from 'vue'; | 29 | import { defineComponent, nextTick, h } from 'vue'; |
27 | import { BasicTable, useTable, TableAction } from '/@/components/Table'; | 30 | import { BasicTable, useTable, TableAction } from '/@/components/Table'; |
28 | import { alarmColumns, alarmSearchSchemas } from './config/detail.config'; | 31 | import { alarmColumns, alarmSearchSchemas } from './config/detail.config'; |
29 | - import { getDeviceAlarm } from '/@/api/device/deviceManager'; | 32 | + import { doBatchAckAlarm, getDeviceAlarm } from '/@/api/device/deviceManager'; |
30 | import { useDrawer } from '/@/components/Drawer'; | 33 | import { useDrawer } from '/@/components/Drawer'; |
31 | import AlarmDetailDrawer from './cpns/AlarmDetailDrawer.vue'; | 34 | import AlarmDetailDrawer from './cpns/AlarmDetailDrawer.vue'; |
32 | - import { Modal } from 'ant-design-vue'; | 35 | + import { Modal, Button } from 'ant-design-vue'; |
33 | import { JsonPreview } from '/@/components/CodeEditor'; | 36 | import { JsonPreview } from '/@/components/CodeEditor'; |
34 | import { getDeviceDetail } from '/@/api/device/deviceManager'; | 37 | import { getDeviceDetail } from '/@/api/device/deviceManager'; |
35 | import { getAttribute } from '/@/api/ruleengine/ruleengineApi'; | 38 | import { getAttribute } from '/@/api/ruleengine/ruleengineApi'; |
@@ -38,17 +41,21 @@ | @@ -38,17 +41,21 @@ | ||
38 | operationString, | 41 | operationString, |
39 | operationBoolean, | 42 | operationBoolean, |
40 | } from '/@/views/rule/linkedge/config/formatData'; | 43 | } from '/@/views/rule/linkedge/config/formatData'; |
44 | + import { AlarmLogItem } from '/@/api/device/model/deviceConfigModel'; | ||
45 | + import { AlarmStatus } from '/@/enums/alarmEnum'; | ||
46 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
41 | 47 | ||
42 | export default defineComponent({ | 48 | export default defineComponent({ |
43 | name: 'AlarmCenter', | 49 | name: 'AlarmCenter', |
44 | components: { | 50 | components: { |
51 | + Button, | ||
45 | BasicTable, | 52 | BasicTable, |
46 | TableAction, | 53 | TableAction, |
47 | AlarmDetailDrawer, | 54 | AlarmDetailDrawer, |
48 | }, | 55 | }, |
49 | 56 | ||
50 | setup() { | 57 | setup() { |
51 | - const [registerTable, { reload }] = useTable({ | 58 | + const [registerTable, { reload, getSelectRows, clearSelectedRowKeys }] = useTable({ |
52 | title: '告警记录列表', | 59 | title: '告警记录列表', |
53 | api: getDeviceAlarm, | 60 | api: getDeviceAlarm, |
54 | columns: alarmColumns, | 61 | columns: alarmColumns, |
@@ -61,6 +68,14 @@ | @@ -61,6 +68,14 @@ | ||
61 | bordered: true, | 68 | bordered: true, |
62 | showIndexColumn: false, | 69 | showIndexColumn: false, |
63 | showTableSetting: true, | 70 | showTableSetting: true, |
71 | + rowSelection: { | ||
72 | + type: 'checkbox', | ||
73 | + getCheckboxProps: (record: AlarmLogItem) => { | ||
74 | + return { | ||
75 | + disabled: record.status === AlarmStatus.CLEARED_ACK, | ||
76 | + }; | ||
77 | + }, | ||
78 | + }, | ||
64 | actionColumn: { | 79 | actionColumn: { |
65 | width: 200, | 80 | width: 200, |
66 | title: '操作', | 81 | title: '操作', |
@@ -148,12 +163,24 @@ | @@ -148,12 +163,24 @@ | ||
148 | attribute, | 163 | attribute, |
149 | }; | 164 | }; |
150 | }; | 165 | }; |
166 | + | ||
167 | + const { createMessage } = useMessage(); | ||
168 | + const handleBatchAck = async () => { | ||
169 | + const ids = getSelectRows<AlarmLogItem>().map((item) => item.id); | ||
170 | + | ||
171 | + await doBatchAckAlarm(ids); | ||
172 | + createMessage.success('操作成功'); | ||
173 | + clearSelectedRowKeys(); | ||
174 | + reload(); | ||
175 | + }; | ||
176 | + | ||
151 | return { | 177 | return { |
152 | registerTable, | 178 | registerTable, |
153 | registerDetailDrawer, | 179 | registerDetailDrawer, |
154 | handleDetail, | 180 | handleDetail, |
155 | handleSuccess, | 181 | handleSuccess, |
156 | handleViewAlarmDetails, | 182 | handleViewAlarmDetails, |
183 | + handleBatchAck, | ||
157 | }; | 184 | }; |
158 | }, | 185 | }, |
159 | }); | 186 | }); |
@@ -91,7 +91,7 @@ export const formSchema: FormSchema[] = [ | @@ -91,7 +91,7 @@ export const formSchema: FormSchema[] = [ | ||
91 | valueField: 'fileList', | 91 | valueField: 'fileList', |
92 | componentProps: () => { | 92 | componentProps: () => { |
93 | return { | 93 | return { |
94 | - // listType: 'picture-card', | 94 | + listType: 'picture-card', |
95 | maxFileLimit: 1, | 95 | maxFileLimit: 1, |
96 | api: async (file: File) => { | 96 | api: async (file: File) => { |
97 | try { | 97 | try { |
@@ -110,10 +110,10 @@ export const formSchema: FormSchema[] = [ | @@ -110,10 +110,10 @@ export const formSchema: FormSchema[] = [ | ||
110 | onPreview: (fileList: FileItem) => { | 110 | onPreview: (fileList: FileItem) => { |
111 | createImgPreview({ imageList: [fileList.url!] }); | 111 | createImgPreview({ imageList: [fileList.url!] }); |
112 | }, | 112 | }, |
113 | - showUploadList: { | ||
114 | - showDownloadIcon: true, | ||
115 | - showRemoveIcon: true, | ||
116 | - }, | 113 | + // showUploadList: { |
114 | + // showDownloadIcon: true, | ||
115 | + // showRemoveIcon: true, | ||
116 | + // }, | ||
117 | }; | 117 | }; |
118 | }, | 118 | }, |
119 | }, | 119 | }, |
@@ -22,7 +22,7 @@ | @@ -22,7 +22,7 @@ | ||
22 | watch( | 22 | watch( |
23 | () => props.customerList, | 23 | () => props.customerList, |
24 | (newValue) => { | 24 | (newValue) => { |
25 | - const transferResult = newValue.map((item) => [item.ts, item.value]); | 25 | + const transferResult = newValue.map((item) => [item.date, item.value]); |
26 | 26 | ||
27 | setOptions({ | 27 | setOptions({ |
28 | tooltip: { | 28 | tooltip: { |
@@ -35,7 +35,7 @@ | @@ -35,7 +35,7 @@ | ||
35 | }, | 35 | }, |
36 | }, | 36 | }, |
37 | xAxis: { | 37 | xAxis: { |
38 | - type: 'time', | 38 | + type: 'category', |
39 | splitLine: { | 39 | splitLine: { |
40 | show: true, | 40 | show: true, |
41 | lineStyle: { | 41 | lineStyle: { |
@@ -90,7 +90,7 @@ | @@ -90,7 +90,7 @@ | ||
90 | trend: props.type, | 90 | trend: props.type, |
91 | }); | 91 | }); |
92 | 92 | ||
93 | - const transferResult = res.map((item) => [item.ts, item.value]); | 93 | + const transferResult = res.map((item) => [item.date, item.value]); |
94 | setOptions({ | 94 | setOptions({ |
95 | tooltip: { | 95 | tooltip: { |
96 | trigger: 'axis', | 96 | trigger: 'axis', |
@@ -31,7 +31,7 @@ | @@ -31,7 +31,7 @@ | ||
31 | }, | 31 | }, |
32 | }, | 32 | }, |
33 | xAxis: { | 33 | xAxis: { |
34 | - type: 'time', | 34 | + type: 'category', |
35 | splitLine: { | 35 | splitLine: { |
36 | show: true, | 36 | show: true, |
37 | lineStyle: { | 37 | lineStyle: { |
@@ -79,11 +79,11 @@ | @@ -79,11 +79,11 @@ | ||
79 | const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); | 79 | const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
80 | onMounted(async () => { | 80 | onMounted(async () => { |
81 | const res = await getTrendData({ | 81 | const res = await getTrendData({ |
82 | - ...getDateByShortcutQueryKey(ShortcutQueryKeyEnum.LATEST_30_DAY), | 82 | + ...getDateByShortcutQueryKey(ShortcutQueryKeyEnum.LATEST_1_MONTH), |
83 | trend: 'CUSTOMER_TREND', | 83 | trend: 'CUSTOMER_TREND', |
84 | }); | 84 | }); |
85 | 85 | ||
86 | - const transferResult = res.map((item) => [item.ts, item.value]); | 86 | + const transferResult = res.map((item) => [item.date, item.value]); |
87 | setOptions({ | 87 | setOptions({ |
88 | tooltip: { | 88 | tooltip: { |
89 | trigger: 'axis', | 89 | trigger: 'axis', |
@@ -95,7 +95,7 @@ | @@ -95,7 +95,7 @@ | ||
95 | }, | 95 | }, |
96 | }, | 96 | }, |
97 | xAxis: { | 97 | xAxis: { |
98 | - type: 'time', | 98 | + type: 'category', |
99 | splitLine: { | 99 | splitLine: { |
100 | show: true, | 100 | show: true, |
101 | lineStyle: { | 101 | lineStyle: { |
@@ -37,7 +37,15 @@ | @@ -37,7 +37,15 @@ | ||
37 | <DatePicker | 37 | <DatePicker |
38 | @change="(_, DateString) => onDateChange(_, DateString, role === RoleEnum.CUSTOMER_USER)" | 38 | @change="(_, DateString) => onDateChange(_, DateString, role === RoleEnum.CUSTOMER_USER)" |
39 | v-model:value="dateValue" | 39 | v-model:value="dateValue" |
40 | - /> | 40 | + > |
41 | + <Button | ||
42 | + type="link" | ||
43 | + class="!px-0" | ||
44 | + :style="dateValue ? { color: '#0960bd', fontWeight: 500 } : { color: '#000000d9' }" | ||
45 | + > | ||
46 | + {{ dateValue ? (dateValue as moment.Moment).format('YYYY-MM-DD') : '自定义' }} | ||
47 | + </Button> | ||
48 | + </DatePicker> | ||
41 | </div> | 49 | </div> |
42 | </template> | 50 | </template> |
43 | <div v-if="activeKey === '1'"> | 51 | <div v-if="activeKey === '1'"> |
@@ -99,13 +107,13 @@ | @@ -99,13 +107,13 @@ | ||
99 | <Tooltip :overlayStyle="{ maxWidth: '340px' }"> | 107 | <Tooltip :overlayStyle="{ maxWidth: '340px' }"> |
100 | <template #title> | 108 | <template #title> |
101 | <section> | 109 | <section> |
102 | - <div>30天: 查询最近30天的数据,间隔时间为1天.</div> | 110 | + <div>最近一个月: 查询最近一个月的数据,间隔时间为1天.</div> |
103 | <div>最近三个月: 查询最近三个月的数据,间隔时间为1天.</div> | 111 | <div>最近三个月: 查询最近三个月的数据,间隔时间为1天.</div> |
104 | - <div>最近一年: 查询最近一年的数据,间隔时间为30天.</div> | 112 | + <div>最近一年: 查询最近一年的数据,间隔时间为1月.</div> |
105 | <div> | 113 | <div> |
106 | 间隔时间: | 114 | 间隔时间: |
107 | <span class="font-bold underline"> 以当天的(最后时间)作为结束时间 </span> | 115 | <span class="font-bold underline"> 以当天的(最后时间)作为结束时间 </span> |
108 | - ,往前推移对应天数或小时的时间作为开始时间,然后在此时间区间内进行分组聚合查询. | 116 | + ,往前推移对应天数或月的时间作为开始时间,然后在此时间区间内进行分组聚合查询. |
109 | </div> | 117 | </div> |
110 | </section> | 118 | </section> |
111 | </template> | 119 | </template> |
@@ -120,9 +128,28 @@ | @@ -120,9 +128,28 @@ | ||
120 | </template> | 128 | </template> |
121 | <DatePicker.RangePicker | 129 | <DatePicker.RangePicker |
122 | @change="onDateCustomerChange" | 130 | @change="onDateCustomerChange" |
131 | + :disabledDate="handleDisableDate" | ||
132 | + @calendarChange="handleCalendarChange" | ||
123 | size="small" | 133 | size="small" |
124 | v-model:value="customerDateValue" | 134 | v-model:value="customerDateValue" |
125 | - /> | 135 | + > |
136 | + <Button | ||
137 | + type="link" | ||
138 | + class="!px-0" | ||
139 | + :style=" | ||
140 | + customerDateValue && customerDateValue.length | ||
141 | + ? { color: '#0960bd', fontWeight: 500 } | ||
142 | + : { color: '#000000d9' } | ||
143 | + " | ||
144 | + >{{ | ||
145 | + customerDateValue && customerDateValue.length | ||
146 | + ? `${(customerDateValue?.[0] as moment.Moment)?.format('YYYY-MM-DD')} | ||
147 | + ~ | ||
148 | + ${(customerDateValue?.[1] as moment.Moment)?.format('YYYY-MM-DD')}` | ||
149 | + : '自定义' | ||
150 | + }}</Button | ||
151 | + > | ||
152 | + </DatePicker.RangePicker> | ||
126 | </div> | 153 | </div> |
127 | </template> | 154 | </template> |
128 | <CustomerTrend :customerTrendList="customerTrendList" /> | 155 | <CustomerTrend :customerTrendList="customerTrendList" /> |
@@ -130,8 +157,8 @@ | @@ -130,8 +157,8 @@ | ||
130 | </div> | 157 | </div> |
131 | </template> | 158 | </template> |
132 | <script lang="ts" setup> | 159 | <script lang="ts" setup> |
133 | - import { ref, reactive } from 'vue'; | ||
134 | - import { Card, DatePicker, Tooltip } from 'ant-design-vue'; | 160 | + import { ref, reactive, unref } from 'vue'; |
161 | + import { Card, DatePicker, Tooltip, Button } from 'ant-design-vue'; | ||
135 | import VisitAnalysis from './VisitAnalysis.vue'; | 162 | import VisitAnalysis from './VisitAnalysis.vue'; |
136 | import VisitAnalysisBar from './VisitAnalysisBar.vue'; | 163 | import VisitAnalysisBar from './VisitAnalysisBar.vue'; |
137 | import { RoleEnum, isAdmin } from '/@/enums/roleEnum'; | 164 | import { RoleEnum, isAdmin } from '/@/enums/roleEnum'; |
@@ -146,6 +173,7 @@ | @@ -146,6 +173,7 @@ | ||
146 | import { getTrendData } from '/@/api/dashboard'; | 173 | import { getTrendData } from '/@/api/dashboard'; |
147 | import { useGlobSetting } from '/@/hooks/setting'; | 174 | import { useGlobSetting } from '/@/hooks/setting'; |
148 | import { QuestionCircleOutlined } from '@ant-design/icons-vue'; | 175 | import { QuestionCircleOutlined } from '@ant-design/icons-vue'; |
176 | + import { RangePickerValue } from 'ant-design-vue/lib/date-picker/interface'; | ||
149 | 177 | ||
150 | defineExpose({ | 178 | defineExpose({ |
151 | isAdmin, | 179 | isAdmin, |
@@ -496,6 +524,18 @@ | @@ -496,6 +524,18 @@ | ||
496 | // onDateTenantChange, | 524 | // onDateTenantChange, |
497 | onDateCustomerChange, | 525 | onDateCustomerChange, |
498 | } = useDate(); | 526 | } = useDate(); |
527 | + | ||
528 | + const handleCalendarChange = (val: RangePickerValue) => { | ||
529 | + customerDateValue.value = val as any; | ||
530 | + }; | ||
531 | + | ||
532 | + const handleDisableDate = (current: moment.Moment) => { | ||
533 | + if (!unref(customerDateValue) || unref(customerDateValue).length === 0) { | ||
534 | + return false; | ||
535 | + } | ||
536 | + const diffDate = current.diff(unref(customerDateValue)[0], 'days'); | ||
537 | + return Math.abs(diffDate) > 30; | ||
538 | + }; | ||
499 | </script> | 539 | </script> |
500 | 540 | ||
501 | <style lang="less"> | 541 | <style lang="less"> |
@@ -47,7 +47,15 @@ | @@ -47,7 +47,15 @@ | ||
47 | <DatePicker | 47 | <DatePicker |
48 | @change="(_, DateString) => onDateChange(_, DateString, role === RoleEnum.CUSTOMER_USER)" | 48 | @change="(_, DateString) => onDateChange(_, DateString, role === RoleEnum.CUSTOMER_USER)" |
49 | v-model:value="dateValue" | 49 | v-model:value="dateValue" |
50 | - /> | 50 | + > |
51 | + <Button | ||
52 | + type="link" | ||
53 | + class="!px-0" | ||
54 | + :style="dateValue ? { color: '#0960bd', fontWeight: 500 } : { color: '#000000d9' }" | ||
55 | + > | ||
56 | + {{ dateValue ? (dateValue as moment.Moment).format('YYYY-MM-DD') : '自定义' }} | ||
57 | + </Button> | ||
58 | + </DatePicker> | ||
51 | </div> | 59 | </div> |
52 | </template> | 60 | </template> |
53 | <!-- <div v-if="activeKey === '1'"> | 61 | <!-- <div v-if="activeKey === '1'"> |
@@ -84,13 +92,13 @@ | @@ -84,13 +92,13 @@ | ||
84 | <Tooltip :overlayStyle="{ maxWidth: '340px' }"> | 92 | <Tooltip :overlayStyle="{ maxWidth: '340px' }"> |
85 | <template #title> | 93 | <template #title> |
86 | <section> | 94 | <section> |
87 | - <div>30天: 查询最近30天的数据,间隔时间为1天.</div> | 95 | + <div>最近一个月: 查询最近一个月的数据,间隔时间为1天.</div> |
88 | <div>最近三个月: 查询最近三个月的数据,间隔时间为1天.</div> | 96 | <div>最近三个月: 查询最近三个月的数据,间隔时间为1天.</div> |
89 | - <div>最近一年: 查询最近一年的数据,间隔时间为30天.</div> | 97 | + <div>最近一年: 查询最近一年的数据,间隔时间为1月.</div> |
90 | <div> | 98 | <div> |
91 | 间隔时间: | 99 | 间隔时间: |
92 | <span class="font-bold underline"> 以当天的(最后时间)作为结束时间 </span> | 100 | <span class="font-bold underline"> 以当天的(最后时间)作为结束时间 </span> |
93 | - ,往前推移对应天数或小时的时间作为开始时间,然后在此时间区间内进行分组聚合查询. | 101 | + ,往前推移对应天数或月的时间作为开始时间,然后在此时间区间内进行分组聚合查询. |
94 | </div> | 102 | </div> |
95 | </section> | 103 | </section> |
96 | </template> | 104 | </template> |
@@ -106,8 +114,27 @@ | @@ -106,8 +114,27 @@ | ||
106 | <DatePicker.RangePicker | 114 | <DatePicker.RangePicker |
107 | @change="onDateTenantChange" | 115 | @change="onDateTenantChange" |
108 | size="small" | 116 | size="small" |
117 | + @calendarChange="handleCalendarChange" | ||
118 | + :disabledDate="handleDisableDate" | ||
109 | v-model:value="tenantDateValue" | 119 | v-model:value="tenantDateValue" |
110 | - /> | 120 | + > |
121 | + <Button | ||
122 | + type="link" | ||
123 | + class="!px-0" | ||
124 | + :style=" | ||
125 | + tenantDateValue && tenantDateValue.length | ||
126 | + ? { color: '#0960bd', fontWeight: 500 } | ||
127 | + : { color: '#000000d9' } | ||
128 | + " | ||
129 | + >{{ | ||
130 | + tenantDateValue && tenantDateValue.length | ||
131 | + ? `${(tenantDateValue?.[0] as moment.Moment)?.format('YYYY-MM-DD')} | ||
132 | + ~ | ||
133 | + ${(tenantDateValue?.[1] as moment.Moment)?.format('YYYY-MM-DD')}` | ||
134 | + : '自定义' | ||
135 | + }}</Button | ||
136 | + > | ||
137 | + </DatePicker.RangePicker> | ||
111 | </div> | 138 | </div> |
112 | </template> | 139 | </template> |
113 | <TenantTrend :tenantTrendList="tenantTrendList" /> | 140 | <TenantTrend :tenantTrendList="tenantTrendList" /> |
@@ -139,8 +166,8 @@ | @@ -139,8 +166,8 @@ | ||
139 | </div> | 166 | </div> |
140 | </template> | 167 | </template> |
141 | <script lang="ts" setup> | 168 | <script lang="ts" setup> |
142 | - import { ref, reactive } from 'vue'; | ||
143 | - import { Card, DatePicker, Tooltip } from 'ant-design-vue'; | 169 | + import { ref, reactive, unref } from 'vue'; |
170 | + import { Card, DatePicker, Tooltip, Button } from 'ant-design-vue'; | ||
144 | import { QuestionCircleOutlined } from '@ant-design/icons-vue'; | 171 | import { QuestionCircleOutlined } from '@ant-design/icons-vue'; |
145 | // import VisitAnalysis from './VisitAnalysis.vue'; | 172 | // import VisitAnalysis from './VisitAnalysis.vue'; |
146 | import VisitAnalysisBar from './VisitAnalysisBar.vue'; | 173 | import VisitAnalysisBar from './VisitAnalysisBar.vue'; |
@@ -155,6 +182,7 @@ | @@ -155,6 +182,7 @@ | ||
155 | import { useDate } from '../hooks/useDate'; | 182 | import { useDate } from '../hooks/useDate'; |
156 | import { getTrendData } from '/@/api/dashboard'; | 183 | import { getTrendData } from '/@/api/dashboard'; |
157 | import { useGlobSetting } from '/@/hooks/setting'; | 184 | import { useGlobSetting } from '/@/hooks/setting'; |
185 | + import { RangePickerValue } from 'ant-design-vue/lib/date-picker/interface'; | ||
158 | 186 | ||
159 | defineExpose({ | 187 | defineExpose({ |
160 | isAdmin, | 188 | isAdmin, |
@@ -514,6 +542,18 @@ | @@ -514,6 +542,18 @@ | ||
514 | onDateTenantChange, | 542 | onDateTenantChange, |
515 | // onDateCustomerChange, | 543 | // onDateCustomerChange, |
516 | } = useDate(); | 544 | } = useDate(); |
545 | + | ||
546 | + const handleCalendarChange = (val: RangePickerValue) => { | ||
547 | + tenantDateValue.value = val as any; | ||
548 | + }; | ||
549 | + | ||
550 | + const handleDisableDate = (current: moment.Moment) => { | ||
551 | + if (!unref(tenantDateValue) || unref(tenantDateValue).length === 0) { | ||
552 | + return false; | ||
553 | + } | ||
554 | + const diffDate = current.diff(unref(tenantDateValue)[0], 'days'); | ||
555 | + return Math.abs(diffDate) > 30; | ||
556 | + }; | ||
517 | </script> | 557 | </script> |
518 | 558 | ||
519 | <style lang="less"> | 559 | <style lang="less"> |
@@ -31,7 +31,7 @@ | @@ -31,7 +31,7 @@ | ||
31 | }, | 31 | }, |
32 | }, | 32 | }, |
33 | xAxis: { | 33 | xAxis: { |
34 | - type: 'time', | 34 | + type: 'category', |
35 | splitLine: { | 35 | splitLine: { |
36 | show: true, | 36 | show: true, |
37 | lineStyle: { | 37 | lineStyle: { |
@@ -79,10 +79,10 @@ | @@ -79,10 +79,10 @@ | ||
79 | const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); | 79 | const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
80 | onMounted(async () => { | 80 | onMounted(async () => { |
81 | const res = await getTrendData({ | 81 | const res = await getTrendData({ |
82 | - ...getDateByShortcutQueryKey(ShortcutQueryKeyEnum.LATEST_30_DAY), | 82 | + ...getDateByShortcutQueryKey(ShortcutQueryKeyEnum.LATEST_1_MONTH), |
83 | trend: 'TENANT_TREND', | 83 | trend: 'TENANT_TREND', |
84 | }); | 84 | }); |
85 | - const transferResult = res.map((item) => [item.ts, item.value]); | 85 | + const transferResult = res.map((item) => [item.date, item.value]); |
86 | setOptions({ | 86 | setOptions({ |
87 | tooltip: { | 87 | tooltip: { |
88 | trigger: 'axis', | 88 | trigger: 'axis', |
@@ -94,7 +94,7 @@ | @@ -94,7 +94,7 @@ | ||
94 | }, | 94 | }, |
95 | }, | 95 | }, |
96 | xAxis: { | 96 | xAxis: { |
97 | - type: 'time', | 97 | + type: 'category', |
98 | splitLine: { | 98 | splitLine: { |
99 | show: true, | 99 | show: true, |
100 | lineStyle: { | 100 | lineStyle: { |
1 | import { dateUtil, formatToDateTime } from '/@/utils/dateUtil'; | 1 | import { dateUtil, formatToDateTime } from '/@/utils/dateUtil'; |
2 | import { ref } from 'vue'; | 2 | import { ref } from 'vue'; |
3 | import { getTrendData } from '/@/api/dashboard'; | 3 | import { getTrendData } from '/@/api/dashboard'; |
4 | +import { RangePickerValue } from 'ant-design-vue/lib/date-picker/interface'; | ||
4 | 5 | ||
5 | export enum ShortcutQueryKeyEnum { | 6 | export enum ShortcutQueryKeyEnum { |
6 | - LATEST_30_DAY = 'LATEST_30_DAY', | 7 | + LATEST_1_MONTH = 'LATEST_1_MONTH', |
7 | LATEST_3_MONTH = 'LATEST_3_MONTH', | 8 | LATEST_3_MONTH = 'LATEST_3_MONTH', |
8 | LATEST_1_YEAR = 'LATEST_1_YEAR', | 9 | LATEST_1_YEAR = 'LATEST_1_YEAR', |
9 | } | 10 | } |
10 | 11 | ||
11 | export function getDateByShortcutQueryKey(value: ShortcutQueryKeyEnum) { | 12 | export function getDateByShortcutQueryKey(value: ShortcutQueryKeyEnum) { |
12 | const mapping = { | 13 | const mapping = { |
13 | - [ShortcutQueryKeyEnum.LATEST_30_DAY]: () => { | 14 | + [ShortcutQueryKeyEnum.LATEST_1_MONTH]: () => { |
14 | return { | 15 | return { |
15 | - startTs: dateUtil().subtract(30, 'day').startOf('day').valueOf(), | 16 | + startTs: dateUtil().subtract(1, 'month').startOf('day').valueOf(), |
16 | interval: 24 * 60 * 60 * 1000, | 17 | interval: 24 * 60 * 60 * 1000, |
17 | }; | 18 | }; |
18 | }, | 19 | }, |
@@ -40,13 +41,13 @@ export function getDateByShortcutQueryKey(value: ShortcutQueryKeyEnum) { | @@ -40,13 +41,13 @@ export function getDateByShortcutQueryKey(value: ShortcutQueryKeyEnum) { | ||
40 | 41 | ||
41 | export function useDate() { | 42 | export function useDate() { |
42 | const tenantDateValue = ref([]); | 43 | const tenantDateValue = ref([]); |
43 | - const customerDateValue = ref([]); | 44 | + const customerDateValue = ref<RangePickerValue>([]); |
44 | const tenantTrendList = ref([]); | 45 | const tenantTrendList = ref([]); |
45 | const customerTrendList = ref([]); | 46 | const customerTrendList = ref([]); |
46 | const activeTenantIndex = ref(0); | 47 | const activeTenantIndex = ref(0); |
47 | const activeCustomerIndex = ref(0); | 48 | const activeCustomerIndex = ref(0); |
48 | const TenantOrCustomerDateList = ref([ | 49 | const TenantOrCustomerDateList = ref([ |
49 | - { label: '30天', value: ShortcutQueryKeyEnum.LATEST_30_DAY }, | 50 | + { label: '最近一个月', value: ShortcutQueryKeyEnum.LATEST_1_MONTH }, |
50 | { label: '最近三个月', value: ShortcutQueryKeyEnum.LATEST_3_MONTH }, | 51 | { label: '最近三个月', value: ShortcutQueryKeyEnum.LATEST_3_MONTH }, |
51 | { label: '最近一年', value: ShortcutQueryKeyEnum.LATEST_1_YEAR }, | 52 | { label: '最近一年', value: ShortcutQueryKeyEnum.LATEST_1_YEAR }, |
52 | ]); | 53 | ]); |
@@ -65,7 +66,7 @@ export function useDate() { | @@ -65,7 +66,7 @@ export function useDate() { | ||
65 | ...getDateByShortcutQueryKey(value), | 66 | ...getDateByShortcutQueryKey(value), |
66 | trend: 'TENANT_TREND', | 67 | trend: 'TENANT_TREND', |
67 | }); | 68 | }); |
68 | - tenantTrendList.value = res.map((item) => [item.ts, item.value]); | 69 | + tenantTrendList.value = res.map((item) => [item.date, item.value]); |
69 | } else { | 70 | } else { |
70 | if (activeCustomerIndex.value === index) return; | 71 | if (activeCustomerIndex.value === index) return; |
71 | activeCustomerIndex.value = index; | 72 | activeCustomerIndex.value = index; |
@@ -74,7 +75,7 @@ export function useDate() { | @@ -74,7 +75,7 @@ export function useDate() { | ||
74 | ...getDateByShortcutQueryKey(value), | 75 | ...getDateByShortcutQueryKey(value), |
75 | trend: 'CUSTOMER_TREND', | 76 | trend: 'CUSTOMER_TREND', |
76 | }); | 77 | }); |
77 | - customerTrendList.value = res.map((item) => [item.ts, item.value]); | 78 | + customerTrendList.value = res.map((item) => [item.date, item.value]); |
78 | } | 79 | } |
79 | } | 80 | } |
80 | 81 |
@@ -103,7 +103,9 @@ | @@ -103,7 +103,9 @@ | ||
103 | const value = { | 103 | const value = { |
104 | ['触发属性']: findName, | 104 | ['触发属性']: findName, |
105 | ['触发条件']: `${findLogin}${curr.value.logicValue}`, | 105 | ['触发条件']: `${findLogin}${curr.value.logicValue}`, |
106 | - ['触发值']: `${curr.value.realValue}${findAttribute.detail?.dataType?.specs?.unit?.key}`, | 106 | + ['触发值']: `${curr.value.realValue}${ |
107 | + findAttribute.detail?.dataType?.specs?.unit?.key ?? '' | ||
108 | + }`, | ||
107 | }; | 109 | }; |
108 | const data = { | 110 | const data = { |
109 | [item.name]: value, | 111 | [item.name]: value, |
@@ -60,7 +60,7 @@ | @@ -60,7 +60,7 @@ | ||
60 | span: 12, | 60 | span: 12, |
61 | }, | 61 | }, |
62 | componentProps: { | 62 | componentProps: { |
63 | - maxLength: 255, | 63 | + maxLength: 64, |
64 | placeholder: '请输入邮件主题', | 64 | placeholder: '请输入邮件主题', |
65 | }, | 65 | }, |
66 | }, | 66 | }, |
@@ -72,7 +72,7 @@ | @@ -72,7 +72,7 @@ | ||
72 | colProps: { | 72 | colProps: { |
73 | span: 12, | 73 | span: 12, |
74 | }, | 74 | }, |
75 | - rules: [...emailRule, { required: true ,message:'主送人不能为空'}], | 75 | + rules: [...emailRule, { required: true, message: '主送人不能为空' }], |
76 | componentProps: { | 76 | componentProps: { |
77 | maxLength: 255, | 77 | maxLength: 255, |
78 | placeholder: '请输入主送', | 78 | placeholder: '请输入主送', |
@@ -43,7 +43,7 @@ | @@ -43,7 +43,7 @@ | ||
43 | <div | 43 | <div |
44 | v-show="!item.notFoundData" | 44 | v-show="!item.notFoundData" |
45 | :id="`chart-${item.device}`" | 45 | :id="`chart-${item.device}`" |
46 | - style="width: 100%; height: 400px" | 46 | + class="w-full h-96" |
47 | ></div> | 47 | ></div> |
48 | </div> | 48 | </div> |
49 | </div> | 49 | </div> |
@@ -273,6 +273,9 @@ | @@ -273,6 +273,9 @@ | ||
273 | series, | 273 | series, |
274 | xAxis: { data: xAxisData }, | 274 | xAxis: { data: xAxisData }, |
275 | } as echarts.EChartsOption); | 275 | } as echarts.EChartsOption); |
276 | + | ||
277 | + await nextTick(); | ||
278 | + resize(); | ||
276 | } catch (error) { | 279 | } catch (error) { |
277 | } finally { | 280 | } finally { |
278 | loading.value = false; | 281 | loading.value = false; |
@@ -67,7 +67,7 @@ | @@ -67,7 +67,7 @@ | ||
67 | if (credentials) { | 67 | if (credentials) { |
68 | for (let i in credentials) Reflect.set(credentialsFile, i, credentials[i]); | 68 | for (let i in credentials) Reflect.set(credentialsFile, i, credentials[i]); |
69 | } | 69 | } |
70 | - setFieldsValue(value); | 70 | + setFieldsValue({ ...value, type: credentials.type }); |
71 | }; | 71 | }; |
72 | 72 | ||
73 | const resetValue = () => resetFields(); | 73 | const resetValue = () => resetFields(); |
@@ -576,7 +576,17 @@ export const actionSchema: FormSchema[] = [ | @@ -576,7 +576,17 @@ export const actionSchema: FormSchema[] = [ | ||
576 | const { setFieldsValue } = formActionType; | 576 | const { setFieldsValue } = formActionType; |
577 | return { | 577 | return { |
578 | placeholder: '请选择类型', | 578 | placeholder: '请选择类型', |
579 | - api: findDictItemByCode, | 579 | + api: async (parmas: Recordable) => { |
580 | + try { | ||
581 | + const record = await findDictItemByCode(parmas); | ||
582 | + return record.filter( | ||
583 | + (item) => | ||
584 | + item.itemValue !== CommandTypeEnum.ATTRIBUTE.toString() || item.itemText != '属性' | ||
585 | + ); | ||
586 | + } catch (error) { | ||
587 | + return []; | ||
588 | + } | ||
589 | + }, | ||
580 | labelField: 'itemText', | 590 | labelField: 'itemText', |
581 | valueField: 'itemValue', | 591 | valueField: 'itemValue', |
582 | params: { | 592 | params: { |
@@ -267,8 +267,10 @@ | @@ -267,8 +267,10 @@ | ||
267 | const validate = getFieldsValue(); | 267 | const validate = getFieldsValue(); |
268 | //ft-add-2022-11-22 | 268 | //ft-add-2022-11-22 |
269 | const type = validate?.outTarget; | 269 | const type = validate?.outTarget; |
270 | - if (type === 'DEVICE_OUT' && validate.device == 'PART') { | ||
271 | - if (validate.deviceId == undefined) return createMessage.error('请选择设备'); | 270 | + if (type === 'DEVICE_OUT') { |
271 | + if (!validate.deviceProfileId) return createMessage.error('请选择产品'); | ||
272 | + if (validate.device == 'PART' && validate.deviceId == undefined) | ||
273 | + return createMessage.error('请选择设备'); | ||
272 | } | 274 | } |
273 | //ft-add-2022-11-22 | 275 | //ft-add-2022-11-22 |
274 | //TODO-fengtao-设备验证 | 276 | //TODO-fengtao-设备验证 |
@@ -191,7 +191,8 @@ | @@ -191,7 +191,8 @@ | ||
191 | const updateFn: DataFetchUpdateFn = (message, attribute) => { | 191 | const updateFn: DataFetchUpdateFn = (message, attribute) => { |
192 | const { data = {} } = message; | 192 | const { data = {} } = message; |
193 | const [latest] = data[attribute] || []; | 193 | const [latest] = data[attribute] || []; |
194 | - const [_, value] = latest; | 194 | + const [name, value] = latest; |
195 | + if (!name && !value) return; | ||
195 | noSendValue.value = getNumberValue(value); | 196 | noSendValue.value = getNumberValue(value); |
196 | sliderValue.value = getNumberValue(value); | 197 | sliderValue.value = getNumberValue(value); |
197 | }; | 198 | }; |