Commit 7439b689b74a46e298c53851af53a8e947c7c8dc

Authored by sqy
1 parent 8141b0f4

feat:历史数据图标

... ... @@ -6,3 +6,15 @@ export const getDeviceProfile = () => {
6 6 url: '/deviceProfile/me/list',
7 7 });
8 8 };
  9 +
  10 +export const getDeviceHistoryInfo = (params) => {
  11 + return defHttp.get(
  12 + {
  13 + url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`,
  14 + params: { ...params, entityId: null },
  15 + },
  16 + {
  17 + joinPrefix: false,
  18 + }
  19 + );
  20 +};
... ...
... ... @@ -14,7 +14,6 @@ import { setupStore } from '/@/store';
14 14 import { setupGlobDirectives } from '/@/directives';
15 15 import { setupI18n } from '/@/locales/setupI18n';
16 16 import { registerGlobComp } from '/@/components/registerGlobComp';
17   -
18 17 // Do not introduce on-demand in local development?
19 18 // In the local development for introduce on-demand, the number of browser requests will increase by about 20%.
20 19 // Which may slow down the browser refresh.
... ...
... ... @@ -92,89 +92,302 @@ export const columns: BasicColumn[] = [
92 92 },
93 93 ];
94 94
  95 +// 动态生成options
  96 +function generateOptions(value: number) {
  97 + if (value === 3600000) {
  98 + return [
  99 + {
  100 + label: '10秒',
  101 + value: 10000,
  102 + },
  103 + {
  104 + label: '15秒',
  105 + value: 15000,
  106 + },
  107 + {
  108 + label: '30秒',
  109 + value: 300000,
  110 + },
  111 + {
  112 + label: '1分钟',
  113 + value: 600000,
  114 + },
  115 + {
  116 + label: '2分钟',
  117 + value: 1200000,
  118 + },
  119 + {
  120 + label: '5分钟',
  121 + value: 3600000,
  122 + },
  123 + ];
  124 + } else if (value === 7200000) {
  125 + return [
  126 + {
  127 + label: '15秒',
  128 + value: 15000,
  129 + },
  130 + {
  131 + label: '30秒',
  132 + value: 300000,
  133 + },
  134 + {
  135 + label: '1分钟',
  136 + value: 600000,
  137 + },
  138 + {
  139 + label: '2分钟',
  140 + value: 1200000,
  141 + },
  142 + {
  143 + label: '5分钟',
  144 + value: 3600000,
  145 + },
  146 + {
  147 + label: '10分钟',
  148 + value: 6000000,
  149 + },
  150 + {
  151 + label: '15分钟',
  152 + value: 9000000,
  153 + },
  154 + ];
  155 + } else if (value === 18000000) {
  156 + return [
  157 + {
  158 + label: '1分钟',
  159 + value: 60000,
  160 + },
  161 + {
  162 + label: '2分钟',
  163 + value: 120000,
  164 + },
  165 + {
  166 + label: '5分钟',
  167 + value: 360000,
  168 + },
  169 + {
  170 + label: '10分钟',
  171 + value: 600000,
  172 + },
  173 + {
  174 + label: '15分钟',
  175 + value: 900000,
  176 + },
  177 + {
  178 + label: '30分钟',
  179 + value: 1800000,
  180 + },
  181 + ];
  182 + } else if (value === 36000000) {
  183 + return [
  184 + {
  185 + label: '2分钟',
  186 + value: 120000,
  187 + },
  188 + {
  189 + label: '5分钟',
  190 + value: 360000,
  191 + },
  192 + {
  193 + label: '10分钟',
  194 + value: 600000,
  195 + },
  196 + {
  197 + label: '15分钟',
  198 + value: 900000,
  199 + },
  200 + {
  201 + label: '30分钟',
  202 + value: 1800000,
  203 + },
  204 + {
  205 + label: '1小时',
  206 + value: 3600000,
  207 + },
  208 + ];
  209 + } else if (value === 43200000) {
  210 + return [
  211 + {
  212 + label: '2分钟',
  213 + value: 120000,
  214 + },
  215 + {
  216 + label: '5分钟',
  217 + value: 360000,
  218 + },
  219 + {
  220 + label: '10分钟',
  221 + value: 600000,
  222 + },
  223 + {
  224 + label: '15分钟',
  225 + value: 900000,
  226 + },
  227 + {
  228 + label: '30分钟',
  229 + value: 1800000,
  230 + },
  231 + {
  232 + label: '1小时',
  233 + value: 3600000,
  234 + },
  235 + ];
  236 + } else if (value === 86400000) {
  237 + return [
  238 + {
  239 + label: '5分钟',
  240 + value: 360000,
  241 + },
  242 + {
  243 + label: '10分钟',
  244 + value: 600000,
  245 + },
  246 + {
  247 + label: '15分钟',
  248 + value: 900000,
  249 + },
  250 + {
  251 + label: '30分钟',
  252 + value: 1800000,
  253 + },
  254 + {
  255 + label: '1小时',
  256 + value: 3600000,
  257 + },
  258 + {
  259 + label: '2小时',
  260 + value: 7200000,
  261 + },
  262 + ];
  263 + } else if (value === 604800000) {
  264 + return [
  265 + {
  266 + label: '30分钟',
  267 + value: 1800000,
  268 + },
  269 + {
  270 + label: '1小时',
  271 + value: 3600000,
  272 + },
  273 + {
  274 + label: '2小时',
  275 + value: 7200000,
  276 + },
  277 + {
  278 + label: '5小时',
  279 + value: 18000000,
  280 + },
  281 + {
  282 + label: '10小时',
  283 + value: 36000000,
  284 + },
  285 + {
  286 + label: '12小时',
  287 + value: 43200000,
  288 + },
  289 + {
  290 + label: '1天',
  291 + value: 86400000,
  292 + },
  293 + ];
  294 + } else {
  295 + return [
  296 + {
  297 + label: '2小时',
  298 + value: 7200000,
  299 + },
  300 + {
  301 + label: '5小时',
  302 + value: 18000000,
  303 + },
  304 + {
  305 + label: '10小时',
  306 + value: 36000000,
  307 + },
  308 + {
  309 + label: '12小时',
  310 + value: 43200000,
  311 + },
  312 + {
  313 + label: '1天',
  314 + value: 86400000,
  315 + },
  316 + ];
  317 + }
  318 +}
95 319 export const schemas: FormSchema[] = [
96 320 {
97   - field: 'name',
  321 + field: 'endTs',
98 322 label: '最后数据',
99 323 component: 'Select',
100   - componentProps: {
101   - options: [
102   - {
103   - label: '最近1小时',
104   - value: '1',
105   - },
106   - {
107   - label: '最近2小时',
108   - value: '2',
  324 + required: true,
  325 + componentProps({ formModel, formActionType }) {
  326 + return {
  327 + onChange(value) {
  328 + const { updateSchema } = formActionType;
  329 + formModel.interval = '';
  330 + updateSchema({
  331 + field: 'interval',
  332 + componentProps: {
  333 + placeholder: '请选择分组间隔',
  334 + options: generateOptions(value),
  335 + },
  336 + });
109 337 },
110   - {
111   - label: '最近5小时',
112   - value: '5',
113   - },
114   - {
115   - label: '最近12小时',
116   - value: '12',
117   - },
118   - {
119   - label: '最近1天',
120   - value: '1天',
121   - },
122   - {
123   - label: '最近7天',
124   - value: '7天',
125   - },
126   - {
127   - label: '最近30天',
128   - value: '30天',
129   - },
130   - ],
  338 + options: [
  339 + {
  340 + label: '最近1小时',
  341 + value: 3600000,
  342 + },
  343 + {
  344 + label: '最近2小时',
  345 + value: 7200000,
  346 + },
  347 + {
  348 + label: '最近5小时',
  349 + value: 18000000,
  350 + },
  351 + {
  352 + label: '最近10小时',
  353 + value: 36000000,
  354 + },
  355 + {
  356 + label: '最近12小时',
  357 + value: 43200000,
  358 + },
  359 + {
  360 + label: '最近1天',
  361 + value: 86400000,
  362 + },
  363 + {
  364 + label: '最近7天',
  365 + value: 604800000,
  366 + },
  367 + {
  368 + label: '最近30天',
  369 + value: 2592000000,
  370 + },
  371 + ],
  372 + };
131 373 },
132 374 colProps: {
133 375 span: 6,
134 376 },
135 377 },
136 378 {
137   - field: 'name',
  379 + field: 'interval',
138 380 label: '分组间隔',
139 381 component: 'Select',
140   - componentProps: {
141   - options: [
142   - {
143   - label: '15秒',
144   - value: '15',
145   - },
146   - {
147   - label: '30秒',
148   - value: '30',
149   - },
150   - {
151   - label: '1分钟',
152   - value: '60',
153   - },
154   - {
155   - label: '2分钟',
156   - value: '120',
157   - },
158   - {
159   - label: '5分钟',
160   - value: '360',
161   - },
162   - {
163   - label: '10分钟',
164   - value: '600',
165   - },
166   - {
167   - label: '15分钟',
168   - value: '900',
169   - },
170   - ],
171   - },
172 382 colProps: {
173 383 span: 6,
174 384 },
  385 + componentProps: {
  386 + placeholder: '请选择分组间隔',
  387 + },
175 388 },
176 389 {
177   - field: 'name',
  390 + field: 'agg',
178 391 label: '数据聚合功能',
179 392 component: 'Select',
180 393 componentProps: {
... ...
1   -export const getLineData = (() => {
2   - const category: any[] = [];
3   - let dottedBase = +new Date();
4   - const lineData: any[] = [];
5   -
6   - for (let i = 0; i < 20; i++) {
7   - const date = new Date((dottedBase += 1000 * 3600 * 24));
8   - category.push([date.getFullYear(), date.getMonth() + 1, date.getDate()].join('-'));
9   - const b = Math.random() * 200;
10   - const d = Math.random() * 200;
11   - lineData.push(d + b);
12   - }
13   - console.log(lineData);
14   - return { category, lineData };
15   -})();
1 1 <template>
2   - <div class="wrapper">
  2 + <div class="wrapper123">
3 3 <div ref="wrapRef" :style="{ height, width }"> </div>
4 4 <div class="right-wrap">
5 5 <BasicTable @register="registerTable" @rowClick="deviceRowClick">
... ... @@ -25,16 +25,16 @@
25 25 </template>
26 26 </BasicTable>
27 27 </div>
28   - <a-button type="primary" @click="openHistoryModal">打开弹窗</a-button>
29 28 <BasicModal
30 29 @register="registerModal"
31 30 title="历史数据"
32 31 width="70%"
33 32 :footer="null"
34 33 @cancel="cancelHistoryModal"
  34 + :canFullscreen="false"
35 35 >
36 36 <BasicForm @register="registerForm" />
37   - <div ref="chartRef" :style="{ height: '300px', width }"></div>
  37 + <div ref="chartRef" :style="{ height: '400px', width }"></div>
38 38 </BasicModal>
39 39 </div>
40 40 </template>
... ... @@ -50,10 +50,9 @@
50 50 import { useModal, BasicModal } from '/@/components/Modal';
51 51 import { BasicForm, useForm } from '/@/components/Form';
52 52 import { schemas } from './config.data';
53   -
54 53 import { useECharts } from '/@/hooks/web/useECharts';
55   - import { getLineData } from './data';
56   -
  54 + import { getDeviceHistoryInfo } from '/@/api/alarm/position';
  55 + import moment from 'moment';
57 56 export default defineComponent({
58 57 name: 'BaiduMap',
59 58 components: {
... ... @@ -75,7 +74,7 @@
75 74 setup() {
76 75 const wrapRef = ref<HTMLDivElement | null>(null);
77 76 const { toPromise } = useScript({ src: BAI_DU_MAP_URL });
78   -
  77 + const entityId = ref('');
79 78 async function initMap() {
80 79 await toPromise();
81 80 await nextTick();
... ... @@ -101,6 +100,7 @@
101 100 });
102 101 // 点击表格某一行触发
103 102 const deviceRowClick = (record) => {
  103 + entityId.value = record.tbDeviceId;
104 104 const BMap = (window as any).BMap;
105 105 const wrapEl = unref(wrapRef);
106 106 const map = new BMap.Map(wrapEl);
... ... @@ -117,28 +117,29 @@
117 117 // 创建信息窗口对象
118 118 let infoWindow = new BMap.InfoWindow(
119 119 `
120   - <div style="display:flex;justify-content:space-between; margin:20px 0px;">
121   - <div style="font-size:16px;font-weight:bold">${name}</div>
122   - ${
123   - deviceState === 'INACTIVE'
124   - ? '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/djh.png">待激活</div>'
125   - : deviceState === 'ONLINE'
126   - ? '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/online1.png">在线</div>'
127   - : '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/lx1.png">离线</div>'
128   - }
129   - </div>
130   - <div>所属组织:${organizationDTO.name}</div>
131   - <div style="margin-top:6px;">接入协议:${deviceProfile.transportType}</div>
132   - <div style="margin-top:6px;">设备位置:${address}</div>
133   - <div style="margin-top:6px;">下线时间:${updateTime}</div>
134   - <div style="display:flex;justify-content:space-between; margin-top:10px">
135   - <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">设备信息</button>
136   - <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">报警记录</button>
137   - <button onclick="historyClick()"style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">历史数据</button>
138   - </div>
139   - `,
  120 + <div style="display:flex;justify-content:space-between; margin:20px 0px;">
  121 + <div style="font-size:16px;font-weight:bold">${name}</div>
  122 + ${
  123 + deviceState === 'INACTIVE'
  124 + ? '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/djh.png">待激活</div>'
  125 + : deviceState === 'ONLINE'
  126 + ? '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/online1.png">在线</div>'
  127 + : '<div style="display:flex;align-items:center"><img style="width:15px;height:15px" src="/src/assets/images/lx1.png">离线</div>'
  128 + }
  129 + </div>
  130 + <div>所属组织:${organizationDTO.name}</div>
  131 + <div style="margin-top:6px;">接入协议:${deviceProfile.transportType}</div>
  132 + <div style="margin-top:6px;">设备位置:${address}</div>
  133 + <div style="margin-top:6px;">下线时间:${updateTime}</div>
  134 + <div style="display:flex;justify-content:space-between; margin-top:10px">
  135 + <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">设备信息</button>
  136 + <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">报警记录</button>
  137 + <button onclick="openHistoryModal()" style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">历史数据</button>
  138 + </div>
  139 + `,
140 140 options
141 141 );
  142 +
142 143 map.openInfoWindow(infoWindow, map.getCenter());
143 144 let preMarker = null;
144 145 const rivet =
... ... @@ -170,35 +171,168 @@
170 171 };
171 172
172 173 const [registerModal, { openModal }] = useModal();
173   - const [registerForm, { resetFields }] = useForm({
  174 + const [registerForm, { resetFields, getFieldsValue, validate }] = useForm({
174 175 labelWidth: 120,
175 176 schemas,
  177 + async submitFunc() {
  178 + await validate();
  179 + let { endTs, interval, agg } = getFieldsValue();
  180 + if (!endTs) return;
  181 + const startTs = Date.now() - endTs;
  182 + endTs = Date.now();
  183 + console.log(startTs, endTs);
  184 + const res = await getDeviceHistoryInfo({
  185 + entityId: entityId.value,
  186 + keys: 'co,co2',
  187 + interval,
  188 + startTs,
  189 + endTs,
  190 + agg,
  191 + });
  192 + const dateArray: { key: string; time: string }[] = [];
  193 + const valueArray: { key: string; value: string }[] = [];
  194 + for (const key in res) {
  195 + for (const item1 of res[key]) {
  196 + let { ts, value } = item1;
  197 + const time = moment(ts).format('YYYY-MM-DD');
  198 + dateArray.push({
  199 + key,
  200 + time,
  201 + });
  202 + valueArray.push({
  203 + key,
  204 + value,
  205 + });
  206 + }
  207 + }
  208 + console.log(dateArray, valueArray);
  209 + const keys = ['co', 'co2'];
  210 + const series: any = keys.map((item) => {
  211 + return {
  212 + name: item,
  213 + type: 'line',
  214 + smooth: true,
  215 + showAllSymbol: 'auto',
  216 + symbol: 'emptyCircle',
  217 + symbolSize: 15,
  218 + data: valueArray.filter((item1) => item1.key === item).map((item1) => item1.value),
  219 + };
  220 + });
  221 + setOptions({
  222 + tooltip: {
  223 + trigger: 'axis',
  224 + axisPointer: {
  225 + lineStyle: {
  226 + width: 1,
  227 + color: '#019680',
  228 + },
  229 + },
  230 + },
  231 + xAxis: {
  232 + type: 'category',
  233 + boundaryGap: false,
  234 + data: dateArray.map((item) => item.time),
  235 + splitLine: {
  236 + show: true,
  237 + lineStyle: {
  238 + width: 1,
  239 + type: 'solid',
  240 + color: 'rgba(226,226,226,0.5)',
  241 + },
  242 + },
  243 + axisTick: {
  244 + show: false,
  245 + },
  246 + },
  247 + yAxis: [
  248 + {
  249 + type: 'value',
  250 + max: 1000,
  251 + splitNumber: 4,
  252 + axisTick: {
  253 + show: false,
  254 + },
  255 + splitArea: {
  256 + show: true,
  257 + areaStyle: {
  258 + color: ['rgba(255,255,255,0.2)', 'rgba(226,226,226,0.2)'],
  259 + },
  260 + },
  261 + },
  262 + ],
  263 + grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true },
  264 + series,
  265 + });
  266 + },
176 267 });
177 268
178 269 const chartRef = ref<HTMLDivElement | null>(null);
179   - const { setOptions, echarts } = useECharts(chartRef as Ref<HTMLDivElement>);
180   - const { lineData, category } = getLineData;
181   - const openHistoryModal = () => {
182   - openModal(true);
  270 + const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
  271 +
  272 + const openHistoryModal = async () => {
  273 + const startTs = Date.now() - 86400000;
  274 + const endTs = Date.now();
  275 + console.log(startTs, endTs);
  276 + const res = await getDeviceHistoryInfo({
  277 + entityId: entityId.value,
  278 + keys: 'co,co2',
  279 + startTs,
  280 + endTs,
  281 + });
  282 + const dateArray: { key: string; time: string }[] = [];
  283 + const valueArray: { key: string; value: string }[] = [];
  284 + for (const key in res) {
  285 + for (const item1 of res[key]) {
  286 + let { ts, value } = item1;
  287 + const time = moment(ts).format('YYYY-MM-DD HH:mm:ss');
  288 + dateArray.push({
  289 + key,
  290 + time,
  291 + });
  292 + valueArray.push({
  293 + key,
  294 + value,
  295 + });
  296 + }
  297 + }
  298 +
  299 + const keys = ['co', 'co2'];
  300 + const series: any = keys.map((item) => {
  301 + return {
  302 + smooth: true,
  303 + data: valueArray.filter((item1) => item1.key === item).map((item1) => item1.value),
  304 + type: 'line',
  305 + name: item,
  306 + itemStyle: {
  307 + color: '#5ab1ef',
  308 + },
  309 + };
  310 + });
183 311 setOptions({
184   - backgroundColor: '#0f375f',
185 312 tooltip: {
186 313 trigger: 'axis',
187 314 axisPointer: {
188   - type: 'shadow',
189   - label: {
190   - show: true,
191   - backgroundColor: '#333',
  315 + lineStyle: {
  316 + width: 1,
  317 + color: '#019680',
192 318 },
193 319 },
194 320 },
195 321 xAxis: {
196   - data: category,
197   - axisLine: {
  322 + type: 'category',
  323 + boundaryGap: false,
  324 + data: dateArray.map((item) => item.time),
  325 + splitLine: {
  326 + show: true,
198 327 lineStyle: {
199   - color: '#ccc',
  328 + width: 1,
  329 + type: 'solid',
  330 + color: 'rgba(226,226,226,0.5)',
200 331 },
201 332 },
  333 + axisTick: {
  334 + show: false,
  335 + },
202 336 },
203 337 yAxis: {
204 338 splitLine: { show: false },
... ... @@ -208,41 +342,18 @@
208 342 },
209 343 },
210 344 },
211   - series: [
212   - {
213   - name: '当日数据',
214   - type: 'line',
215   - smooth: true,
216   - showAllSymbol: 'auto',
217   - symbol: 'emptyCircle',
218   - symbolSize: 15,
219   - data: lineData,
220   - },
221   - {
222   - name: 'line',
223   - type: 'bar',
224   - barGap: '-100%',
225   - barWidth: 10,
226   - itemStyle: {
227   - color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
228   - { offset: 0, color: 'rgba(20,200,212,0.5)' },
229   - { offset: 0.2, color: 'rgba(20,200,212,0.2)' },
230   - { offset: 1, color: 'rgba(20,200,212,0)' },
231   - ]),
232   - },
233   - z: -12,
234   - data: lineData,
235   - },
236   - ],
  345 + grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true },
  346 + series,
237 347 });
  348 + openModal(true);
238 349 };
239 350 const cancelHistoryModal = () => {
240 351 resetFields();
241 352 };
242 353 onMounted(() => {
243 354 initMap();
  355 + (window as any).openHistoryModal = openHistoryModal;
244 356 });
245   -
246 357 return {
247 358 wrapRef,
248 359 registerTable,
... ... @@ -251,7 +362,6 @@
251 362 registerModal,
252 363 registerForm,
253 364 chartRef,
254   - openHistoryModal,
255 365 cancelHistoryModal,
256 366 };
257 367 },
... ...
... ... @@ -54,14 +54,14 @@
54 54 dataSource: state.recordList,
55 55 });
56 56
57   - const { send } = useWebSocket(state.server, {
  57 + const { send, close } = useWebSocket(state.server, {
58 58 onConnected() {
59 59 send(state.sendValue);
60 60 console.log('建立连接了');
61 61 },
62 62 onMessage(_, e) {
63 63 const { data } = JSON.parse(e.data);
64   - console.log('来新消息了', 'data---', data);
  64 + console.log('来新消息了', '---data---', data);
65 65 const newArray: socketDataType[] = [];
66 66 for (const key in data) {
67 67 const [time, value] = data[key].flat(1);
... ... @@ -73,15 +73,15 @@
73 73 if (state.recordList.length === 0) {
74 74 state.recordList.unshift(obj);
75 75 } else {
76   - newArray.unshift(obj);
  76 + newArray.push(obj);
77 77 }
78 78 }
79 79 newArray.forEach((item) => {
80 80 let flag = false;
81 81 state.recordList.forEach((item1) => {
82   - if (item.key === item1.key) {
83   - item1.time = item.time;
  82 + if (item1.key === item.key) {
84 83 item1.value = item.value;
  84 + item1.time = item.time;
85 85 flag = true;
86 86 }
87 87 });
... ... @@ -90,6 +90,10 @@
90 90 }
91 91 });
92 92 },
  93 + onDisconnected() {
  94 + console.log('断开连接了');
  95 + close();
  96 + },
93 97 onError() {
94 98 createMessage.error('webSocket连接超时,请联系管理员');
95 99 },
... ...