mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-11-01 03:49:24 +08:00 
			
		
		
		
	Feat: Display recent ping chart
This commit is contained in:
		
							
								
								
									
										84
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										84
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1779,6 +1779,16 @@ | |||||||
|       "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", |       "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", | ||||||
|       "dev": true |       "dev": true | ||||||
|     }, |     }, | ||||||
|  |     "chart.js": { | ||||||
|  |       "version": "3.5.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.5.0.tgz", | ||||||
|  |       "integrity": "sha512-J1a4EAb1Gi/KbhwDRmoovHTRuqT8qdF0kZ4XgwxpGethJHUdDrkqyPYwke0a+BuvSeUxPf8Cos6AX2AB8H8GLA==" | ||||||
|  |     }, | ||||||
|  |     "chartjs-adapter-dayjs": { | ||||||
|  |       "version": "1.0.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/chartjs-adapter-dayjs/-/chartjs-adapter-dayjs-1.0.0.tgz", | ||||||
|  |       "integrity": "sha512-EnbVqTJGFKLpg1TROLdCEufrzbmIa2oeLGx8O2Wdjw2EoMudoOo9+YFu+6CM0Z0hQ/v3yq/e/Y6efQMu22n8Jg==" | ||||||
|  |     }, | ||||||
|     "chokidar": { |     "chokidar": { | ||||||
|       "version": "3.5.2", |       "version": "3.5.2", | ||||||
|       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", |       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", | ||||||
| @@ -6814,11 +6824,85 @@ | |||||||
|         "@vue/shared": "3.2.1" |         "@vue/shared": "3.2.1" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "vue-chart-3": { | ||||||
|  |       "version": "0.5.7", | ||||||
|  |       "resolved": "https://registry.npmjs.org/vue-chart-3/-/vue-chart-3-0.5.7.tgz", | ||||||
|  |       "integrity": "sha512-BccfPv2rodY6IOppYHvMluVmIJE1CHfp5uW2DXrHrm1kIzaafLwpQ5SwdrxuCevn/QhKoi7azzcxwRcoWbX9hg==", | ||||||
|  |       "requires": { | ||||||
|  |         "@vue/runtime-core": "^3.2.1", | ||||||
|  |         "@vue/runtime-dom": "^3.2.1", | ||||||
|  |         "csstype": "^3.0.8", | ||||||
|  |         "lodash": "^4.17.21", | ||||||
|  |         "nanoid": "^3.1.23", | ||||||
|  |         "vue-demi": "^0.10.1" | ||||||
|  |       }, | ||||||
|  |       "dependencies": { | ||||||
|  |         "@vue/reactivity": { | ||||||
|  |           "version": "3.2.1", | ||||||
|  |           "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.1.tgz", | ||||||
|  |           "integrity": "sha512-4Lja2KmyiKvuraDed6dXK2A6+r/7x7xGDA7vVR2Aqc8hQVu0+FWeVX+IBfiVOSpbZXFlHLNmCBFkbuWLQSlgxg==", | ||||||
|  |           "requires": { | ||||||
|  |             "@vue/shared": "3.2.1" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "@vue/runtime-core": { | ||||||
|  |           "version": "3.2.1", | ||||||
|  |           "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.1.tgz", | ||||||
|  |           "integrity": "sha512-IsgelRM/5hYeRhz5+ECi66XvYDdjG2t4lARjHvCXw5s9Q4N6uIbjLMwtLzAWRxYf3/y258BrD+ehxAi943ScJg==", | ||||||
|  |           "requires": { | ||||||
|  |             "@vue/reactivity": "3.2.1", | ||||||
|  |             "@vue/shared": "3.2.1" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "@vue/runtime-dom": { | ||||||
|  |           "version": "3.2.1", | ||||||
|  |           "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.1.tgz", | ||||||
|  |           "integrity": "sha512-bUAHUSe49A5wYdHQ8wsLU1CMPXaG2fRuv2661mx/6Q9+20QxglT3ss8ZeL6AVRu16JNJMcdvTTsNpbnMbVc/lQ==", | ||||||
|  |           "requires": { | ||||||
|  |             "@vue/runtime-core": "3.2.1", | ||||||
|  |             "@vue/shared": "3.2.1", | ||||||
|  |             "csstype": "^2.6.8" | ||||||
|  |           }, | ||||||
|  |           "dependencies": { | ||||||
|  |             "csstype": { | ||||||
|  |               "version": "2.6.17", | ||||||
|  |               "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz", | ||||||
|  |               "integrity": "sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A==" | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "@vue/shared": { | ||||||
|  |           "version": "3.2.1", | ||||||
|  |           "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.1.tgz", | ||||||
|  |           "integrity": "sha512-INN92dVBNgd0TW9BqfQQKx/HWGCHhUUbAV5EZ5FgSCiEdwuZsJbGt1mdnaD9IxGhpiyOjP2ClxGG8SFp7ELcWg==" | ||||||
|  |         }, | ||||||
|  |         "csstype": { | ||||||
|  |           "version": "3.0.8", | ||||||
|  |           "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.8.tgz", | ||||||
|  |           "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" | ||||||
|  |         }, | ||||||
|  |         "lodash": { | ||||||
|  |           "version": "4.17.21", | ||||||
|  |           "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", | ||||||
|  |           "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" | ||||||
|  |         }, | ||||||
|  |         "nanoid": { | ||||||
|  |           "version": "3.1.23", | ||||||
|  |           "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", | ||||||
|  |           "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "vue-confirm-dialog": { |     "vue-confirm-dialog": { | ||||||
|       "version": "1.0.2", |       "version": "1.0.2", | ||||||
|       "resolved": "https://registry.npmjs.org/vue-confirm-dialog/-/vue-confirm-dialog-1.0.2.tgz", |       "resolved": "https://registry.npmjs.org/vue-confirm-dialog/-/vue-confirm-dialog-1.0.2.tgz", | ||||||
|       "integrity": "sha512-gTo1bMDWOLd/6ihmWv8VlPxhc9QaKoE5YqlsKydUOfrrQ3Q3taljF6yI+1TMtAtJLrvZ8DYrePhgBhY1VCJzbQ==" |       "integrity": "sha512-gTo1bMDWOLd/6ihmWv8VlPxhc9QaKoE5YqlsKydUOfrrQ3Q3taljF6yI+1TMtAtJLrvZ8DYrePhgBhY1VCJzbQ==" | ||||||
|     }, |     }, | ||||||
|  |     "vue-demi": { | ||||||
|  |       "version": "0.10.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.10.1.tgz", | ||||||
|  |       "integrity": "sha512-L6Oi+BvmMv6YXvqv5rJNCFHEKSVu7llpWWJczqmAQYOdmPPw5PNYoz1KKS//Fxhi+4QP64dsPjtmvnYGo1jemA==" | ||||||
|  |     }, | ||||||
|     "vue-eslint-parser": { |     "vue-eslint-parser": { | ||||||
|       "version": "7.10.0", |       "version": "7.10.0", | ||||||
|       "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.10.0.tgz", |       "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.10.0.tgz", | ||||||
|   | |||||||
| @@ -31,11 +31,14 @@ | |||||||
|         "@fortawesome/free-regular-svg-icons": "^5.15.4", |         "@fortawesome/free-regular-svg-icons": "^5.15.4", | ||||||
|         "@fortawesome/free-solid-svg-icons": "^5.15.4", |         "@fortawesome/free-solid-svg-icons": "^5.15.4", | ||||||
|         "@fortawesome/vue-fontawesome": "^3.0.0-4", |         "@fortawesome/vue-fontawesome": "^3.0.0-4", | ||||||
|  |         "@louislam/sqlite3": "^5.0.3", | ||||||
|         "@popperjs/core": "^2.9.3", |         "@popperjs/core": "^2.9.3", | ||||||
|         "args-parser": "^1.3.0", |         "args-parser": "^1.3.0", | ||||||
|         "axios": "^0.21.1", |         "axios": "^0.21.1", | ||||||
|         "bcrypt": "^5.0.1", |         "bcrypt": "^5.0.1", | ||||||
|         "bootstrap": "^5.1.0", |         "bootstrap": "^5.1.0", | ||||||
|  |         "chart.js": "^3.5.0", | ||||||
|  |         "chartjs-adapter-dayjs": "^1.0.0", | ||||||
|         "command-exists": "^1.2.9", |         "command-exists": "^1.2.9", | ||||||
|         "dayjs": "^1.10.6", |         "dayjs": "^1.10.6", | ||||||
|         "express": "^4.17.1", |         "express": "^4.17.1", | ||||||
| @@ -50,10 +53,10 @@ | |||||||
|         "redbean-node": "0.0.21", |         "redbean-node": "0.0.21", | ||||||
|         "socket.io": "^4.1.3", |         "socket.io": "^4.1.3", | ||||||
|         "socket.io-client": "^4.1.3", |         "socket.io-client": "^4.1.3", | ||||||
|         "@louislam/sqlite3": "^5.0.3", |  | ||||||
|         "tcp-ping": "^0.1.1", |         "tcp-ping": "^0.1.1", | ||||||
|         "v-pagination-3": "^0.1.6", |         "v-pagination-3": "^0.1.6", | ||||||
|         "vue": "^3.2.1", |         "vue": "^3.2.1", | ||||||
|  |         "vue-chart-3": "^0.5.7", | ||||||
|         "vue-confirm-dialog": "^1.0.2", |         "vue-confirm-dialog": "^1.0.2", | ||||||
|         "vue-multiselect": "^3.0.0-alpha.2", |         "vue-multiselect": "^3.0.0-alpha.2", | ||||||
|         "vue-router": "^4.0.10", |         "vue-router": "^4.0.10", | ||||||
|   | |||||||
							
								
								
									
										91
									
								
								src/components/PingChart.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/components/PingChart.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | |||||||
|  | <template> | ||||||
|  |     <LineChart :chart-data="chartData" :height="100" :options="chartOptions" /> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  | import { Chart, registerables } from "chart.js"; | ||||||
|  | import dayjs from "dayjs"; | ||||||
|  | import utc from "dayjs/plugin/utc"; | ||||||
|  | import timezone from "dayjs/plugin/timezone"; | ||||||
|  | import "chartjs-adapter-dayjs"; | ||||||
|  | import { LineChart } from "vue-chart-3"; | ||||||
|  | dayjs.extend(utc); | ||||||
|  | dayjs.extend(timezone); | ||||||
|  |  | ||||||
|  | Chart.register(...registerables); | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |     components: { LineChart }, | ||||||
|  |     props: { | ||||||
|  |         monitorId: { | ||||||
|  |             type: Number, | ||||||
|  |             required: true, | ||||||
|  |         }, | ||||||
|  |     }, | ||||||
|  |     data() { | ||||||
|  |         return { | ||||||
|  |             chartPeriodHrs: 24, | ||||||
|  |         }; | ||||||
|  |     }, | ||||||
|  |     computed: { | ||||||
|  |         chartOptions() { | ||||||
|  |             return { | ||||||
|  |                 responsive: true, | ||||||
|  |                 layout: { | ||||||
|  |                     padding: { | ||||||
|  |                         left: 10, | ||||||
|  |                         right: 30, | ||||||
|  |                         top: 30, | ||||||
|  |                         bottom: 10, | ||||||
|  |                     }, | ||||||
|  |                 }, | ||||||
|  |                 scales: { | ||||||
|  |                     x: { | ||||||
|  |                         type: "time", | ||||||
|  |                         time: { | ||||||
|  |                             stepSize: 30, | ||||||
|  |                         }, | ||||||
|  |                     }, | ||||||
|  |                     y: { | ||||||
|  |                         title: { | ||||||
|  |                             display: true, | ||||||
|  |                             text: "Response Time (ms)", | ||||||
|  |                         }, | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 bounds: "ticks", | ||||||
|  |                 plugins: { | ||||||
|  |                     legend: { | ||||||
|  |                         display: false, | ||||||
|  |                     }, | ||||||
|  |                 }, | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         chartData() { | ||||||
|  |             let data = []; | ||||||
|  |             if (this.monitorId in this.$root.heartbeatList) { | ||||||
|  |                 data = this.$root.heartbeatList[this.monitorId] | ||||||
|  |                     .filter( | ||||||
|  |                         (beat) => dayjs.utc(beat.time).tz(this.$root.timezone).isAfter(dayjs().subtract(this.chartPeriodHrs, "hours"))) | ||||||
|  |                     .map((beat) => { | ||||||
|  |                         return { | ||||||
|  |                             x: dayjs.utc(beat.time).tz(this.$root.timezone).format("YYYY-MM-DD HH:mm:ss"), | ||||||
|  |                             y: beat.ping, | ||||||
|  |                         }; | ||||||
|  |                     }); | ||||||
|  |             } | ||||||
|  |             return { | ||||||
|  |                 datasets: [ | ||||||
|  |                     { | ||||||
|  |                         data: data, | ||||||
|  |                         fill: "origin", | ||||||
|  |                         tension: 0.2, | ||||||
|  |                         borderColor: "#5CDD8B", | ||||||
|  |                         backgroundColor: "#5CDD8B38", | ||||||
|  |                     }, | ||||||
|  |                 ], | ||||||
|  |             }; | ||||||
|  |         }, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  | </script> | ||||||
| @@ -42,7 +42,11 @@ | |||||||
|             <div class="col"> |             <div class="col"> | ||||||
|                 <h4>{{ pingTitle }}</h4> |                 <h4>{{ pingTitle }}</h4> | ||||||
|                 <p>(Current)</p> |                 <p>(Current)</p> | ||||||
|                 <span class="num"><CountUp :value="ping" /></span> |                 <span class="num"> | ||||||
|  |                     <a href="#" @click.prevent="showPingChartBox = !showPingChartBox"> | ||||||
|  |                         <CountUp :value="ping" /> | ||||||
|  |                     </a> | ||||||
|  |                 </span> | ||||||
|             </div> |             </div> | ||||||
|             <div class="col"> |             <div class="col"> | ||||||
|                 <h4>Avg. {{ pingTitle }}</h4> |                 <h4>Avg. {{ pingTitle }}</h4> | ||||||
| @@ -70,6 +74,14 @@ | |||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  |     <div v-if="showPingChartBox" class="shadow-box big-padding text-center"> | ||||||
|  |         <div class="row"> | ||||||
|  |             <div class="col"> | ||||||
|  |                 <PingChart :monitor-id="monitor.id" /> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|     <div v-if="showCertInfoBox" class="shadow-box big-padding text-center"> |     <div v-if="showCertInfoBox" class="shadow-box big-padding text-center"> | ||||||
|         <div class="row"> |         <div class="row"> | ||||||
|             <div class="col"> |             <div class="col"> | ||||||
| @@ -164,6 +176,7 @@ import Datetime from "../components/Datetime.vue"; | |||||||
| import CountUp from "../components/CountUp.vue"; | import CountUp from "../components/CountUp.vue"; | ||||||
| import Uptime from "../components/Uptime.vue"; | import Uptime from "../components/Uptime.vue"; | ||||||
| import Pagination from "v-pagination-3"; | import Pagination from "v-pagination-3"; | ||||||
|  | import PingChart from "../components/PingChart.vue"; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|     components: { |     components: { | ||||||
| @@ -174,6 +187,7 @@ export default { | |||||||
|         Confirm, |         Confirm, | ||||||
|         Status, |         Status, | ||||||
|         Pagination, |         Pagination, | ||||||
|  |         PingChart, | ||||||
|     }, |     }, | ||||||
|     data() { |     data() { | ||||||
|         return { |         return { | ||||||
| @@ -181,6 +195,7 @@ export default { | |||||||
|             perPage: 25, |             perPage: 25, | ||||||
|             heartBeatList: [], |             heartBeatList: [], | ||||||
|             toggleCertInfoBox: false, |             toggleCertInfoBox: false, | ||||||
|  |             showPingChartBox: false, | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
|     computed: { |     computed: { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user