Feature: Added pull to refresh and websocket indicator
This commit is contained in:
parent
7b5d31efca
commit
024ee26705
@ -28,6 +28,7 @@
|
|||||||
"@tsconfig/node18": "^18.2.2",
|
"@tsconfig/node18": "^18.2.2",
|
||||||
"@types/bootstrap": "^5.2.10",
|
"@types/bootstrap": "^5.2.10",
|
||||||
"@types/node": "^20.10.6",
|
"@types/node": "^20.10.6",
|
||||||
|
"@types/pulltorefreshjs": "^0.1.7",
|
||||||
"@types/sortablejs": "^1.15.7",
|
"@types/sortablejs": "^1.15.7",
|
||||||
"@types/spark-md5": "^3.0.4",
|
"@types/spark-md5": "^3.0.4",
|
||||||
"@vitejs/plugin-vue": "^5.0.2",
|
"@vitejs/plugin-vue": "^5.0.2",
|
||||||
@ -36,6 +37,7 @@
|
|||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-plugin-vue": "^9.19.2",
|
"eslint-plugin-vue": "^9.19.2",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
|
"pulltorefreshjs": "^0.1.22",
|
||||||
"sass": "^1.69.7",
|
"sass": "^1.69.7",
|
||||||
"terser": "^5.26.0",
|
"terser": "^5.26.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
|
|||||||
@ -4,7 +4,12 @@
|
|||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-11">
|
<div class="col-sm-11">
|
||||||
<h1>{{ title }}</h1>
|
<h1>{{ title }}
|
||||||
|
<span v-if="showWebSocket" :class="{
|
||||||
|
'onlineMarker': isWebsocketConnected,
|
||||||
|
'offlineMarker': !isWebsocketConnected,
|
||||||
|
}"></span>
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-1" v-if="showReload">
|
<div class="col-sm-1" v-if="showReload">
|
||||||
<button type="button" class="float-end btn btn-outline-primary"
|
<button type="button" class="float-end btn btn-outline-primary"
|
||||||
@ -28,6 +33,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import { BIconArrowClockwise } from 'bootstrap-icons-vue';
|
import { BIconArrowClockwise } from 'bootstrap-icons-vue';
|
||||||
|
import PullToRefresh from 'pulltorefreshjs';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
@ -37,7 +43,81 @@ export default defineComponent({
|
|||||||
title: { type: String, required: true },
|
title: { type: String, required: true },
|
||||||
isLoading: { type: Boolean, required: false, default: false },
|
isLoading: { type: Boolean, required: false, default: false },
|
||||||
isWideScreen: { type: Boolean, required: false, default: false },
|
isWideScreen: { type: Boolean, required: false, default: false },
|
||||||
|
isWebsocketConnected: { type: Boolean, required: false, default: false },
|
||||||
|
showWebSocket: { type: Boolean, required: false, default: false },
|
||||||
showReload: { type: Boolean, required: false, default: false },
|
showReload: { type: Boolean, required: false, default: false },
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
var self = this;
|
||||||
|
console.log("init");
|
||||||
|
PullToRefresh.init({
|
||||||
|
mainElement: 'main', // above which element?
|
||||||
|
instructionsPullToRefresh: this.$t('base.Pull'),
|
||||||
|
instructionsReleaseToRefresh: this.$t('base.Release'),
|
||||||
|
instructionsRefreshing: this.$t('base.Refreshing'),
|
||||||
|
onRefresh: function() {
|
||||||
|
self.$emit('reload');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
unmounted() {
|
||||||
|
console.log("destroy");
|
||||||
|
PullToRefresh.destroyAll();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.ptr--text {
|
||||||
|
color: var(--bs-primary-text-emphasis) !important;
|
||||||
|
}
|
||||||
|
.ptr--icon {
|
||||||
|
color: var(--bs-primary-text-emphasis) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.offlineMarker:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: #ff0000;
|
||||||
|
border-color: #ff0000;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.onlineMarker:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: #00bb00;
|
||||||
|
border-color: #00bb00;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.onlineMarker:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
margin: -12px 0 0 -12px;
|
||||||
|
border: 1px solid #00bb00;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 0 4px #00bb00, inset 0 0 4px rgb(56, 111, 169);
|
||||||
|
transform: scale(0);
|
||||||
|
animation: online 2.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes online {
|
||||||
|
0% {
|
||||||
|
transform: scale(.1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
transform: scale(2.5);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -26,7 +26,10 @@
|
|||||||
"Loading": "Lade...",
|
"Loading": "Lade...",
|
||||||
"Reload": "Aktualisieren",
|
"Reload": "Aktualisieren",
|
||||||
"Cancel": "Abbrechen",
|
"Cancel": "Abbrechen",
|
||||||
"Save": "Speichern"
|
"Save": "Speichern",
|
||||||
|
"Refreshing": "Aktualisieren",
|
||||||
|
"Pull": "Zum Aktualisieren nach unten ziehen",
|
||||||
|
"Release": "Loslassen zum Aktualisieren"
|
||||||
},
|
},
|
||||||
"localeswitcher": {
|
"localeswitcher": {
|
||||||
"Dark": "Dunkel",
|
"Dark": "Dunkel",
|
||||||
|
|||||||
@ -26,7 +26,10 @@
|
|||||||
"Loading": "Loading...",
|
"Loading": "Loading...",
|
||||||
"Reload": "Reload",
|
"Reload": "Reload",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
"Save": "Save"
|
"Save": "Save",
|
||||||
|
"Refreshing": "Refreshing",
|
||||||
|
"Pull": "Pull down to refresh",
|
||||||
|
"Release": "Release to refresh"
|
||||||
},
|
},
|
||||||
"localeswitcher": {
|
"localeswitcher": {
|
||||||
"Dark": "Dark",
|
"Dark": "Dark",
|
||||||
|
|||||||
@ -26,7 +26,10 @@
|
|||||||
"Loading": "Chargement...",
|
"Loading": "Chargement...",
|
||||||
"Reload": "Reload",
|
"Reload": "Reload",
|
||||||
"Cancel": "Annuler",
|
"Cancel": "Annuler",
|
||||||
"Save": "Sauvegarder"
|
"Save": "Sauvegarder",
|
||||||
|
"Refreshing": "Refreshing",
|
||||||
|
"Pull": "Pull down to refresh",
|
||||||
|
"Release": "Release to refresh"
|
||||||
},
|
},
|
||||||
"localeswitcher": {
|
"localeswitcher": {
|
||||||
"Dark": "Sombre",
|
"Dark": "Sombre",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<BasePage :title="$t('home.LiveData')" :isLoading="dataLoading" :isWideScreen="true">
|
<BasePage :title="$t('home.LiveData')" :isLoading="dataLoading" :isWideScreen="true" :showWebSocket="true" :isWebsocketConnected="isWebsocketConnected" @reload="reloadData">
|
||||||
<HintView :hints="liveData.hints" />
|
<HintView :hints="liveData.hints" />
|
||||||
<InverterTotalInfo :totalData="liveData.total" /><br />
|
<InverterTotalInfo :totalData="liveData.total" /><br />
|
||||||
<div class="row gy-3">
|
<div class="row gy-3">
|
||||||
@ -448,6 +448,8 @@ export default defineComponent({
|
|||||||
alertTypePower: "info",
|
alertTypePower: "info",
|
||||||
showAlertPower: false,
|
showAlertPower: false,
|
||||||
successCommandPower: "",
|
successCommandPower: "",
|
||||||
|
|
||||||
|
isWebsocketConnected: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@ -475,17 +477,23 @@ export default defineComponent({
|
|||||||
this.closeSocket();
|
this.closeSocket();
|
||||||
},
|
},
|
||||||
updated() {
|
updated() {
|
||||||
|
console.log("Updated");
|
||||||
// Select first tab
|
// Select first tab
|
||||||
if (this.isFirstFetchAfterConnect) {
|
if (this.isFirstFetchAfterConnect) {
|
||||||
this.isFirstFetchAfterConnect = false;
|
console.log("isFirstFetchAfterConnect");
|
||||||
|
|
||||||
const firstTabEl = document.querySelector(
|
this.$nextTick(() => {
|
||||||
"#v-pills-tab:first-child button"
|
console.log("nextTick");
|
||||||
);
|
const firstTabEl = document.querySelector(
|
||||||
if (firstTabEl != null) {
|
"#v-pills-tab:first-child button"
|
||||||
const firstTab = new bootstrap.Tab(firstTabEl);
|
);
|
||||||
firstTab.show();
|
if (firstTabEl != null) {
|
||||||
}
|
this.isFirstFetchAfterConnect = false;
|
||||||
|
console.log("Show");
|
||||||
|
const firstTab = new bootstrap.Tab(firstTabEl);
|
||||||
|
firstTab.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -508,15 +516,27 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isLoggedIn,
|
isLoggedIn,
|
||||||
getInitialData() {
|
getInitialData(triggerLoading : boolean = true) {
|
||||||
this.dataLoading = true;
|
if (triggerLoading) {
|
||||||
|
this.dataLoading = true;
|
||||||
|
}
|
||||||
fetch("/api/livedata/status", { headers: authHeader() })
|
fetch("/api/livedata/status", { headers: authHeader() })
|
||||||
.then((response) => handleResponse(response, this.$emitter, this.$router))
|
.then((response) => handleResponse(response, this.$emitter, this.$router))
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
this.liveData = data;
|
this.liveData = data;
|
||||||
this.dataLoading = false;
|
if (triggerLoading) {
|
||||||
|
this.dataLoading = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
reloadData() {
|
||||||
|
this.closeSocket();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.getInitialData(false);
|
||||||
|
this.initSocket();
|
||||||
|
}, 1000);
|
||||||
|
},
|
||||||
initSocket() {
|
initSocket() {
|
||||||
console.log("Starting connection to WebSocket Server");
|
console.log("Starting connection to WebSocket Server");
|
||||||
|
|
||||||
@ -540,11 +560,19 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
this.socket.onopen = function (event) {
|
this.socket.onopen = function (event) {
|
||||||
console.log(event);
|
console.log(event);
|
||||||
console.log("Successfully connected to the echo websocket server...");
|
console.log("Successfully connected to the echo websocket server...");
|
||||||
|
self.isWebsocketConnected = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.socket.onclose = function() {
|
||||||
|
console.log("Connection to websocket closed...")
|
||||||
|
self.isWebsocketConnected = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Listen to window events , When the window closes , Take the initiative to disconnect websocket Connect
|
// Listen to window events , When the window closes , Take the initiative to disconnect websocket Connect
|
||||||
window.onbeforeunload = () => {
|
window.onbeforeunload = () => {
|
||||||
this.closeSocket();
|
this.closeSocket();
|
||||||
|
|||||||
@ -442,6 +442,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~5.26.4"
|
undici-types "~5.26.4"
|
||||||
|
|
||||||
|
"@types/pulltorefreshjs@^0.1.7":
|
||||||
|
version "0.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pulltorefreshjs/-/pulltorefreshjs-0.1.7.tgz#1948638b0c7071282e47bd236d2ccb88bdf66753"
|
||||||
|
integrity sha512-Y0g/yfuycIvpvUmP97n5NE2+HDAOwfREGVERjhMWw2Y0ODh5wvbflcQ5gXPZ+ihgoq+BQZjA1DL8apw2wAsJXA==
|
||||||
|
|
||||||
"@types/semver@^7.5.0":
|
"@types/semver@^7.5.0":
|
||||||
version "7.5.1"
|
version "7.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.1.tgz#0480eeb7221eb9bc398ad7432c9d7e14b1a5a367"
|
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.1.tgz#0480eeb7221eb9bc398ad7432c9d7e14b1a5a367"
|
||||||
@ -2142,6 +2147,11 @@ prelude-ls@~1.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
||||||
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
|
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
|
||||||
|
|
||||||
|
pulltorefreshjs@^0.1.22:
|
||||||
|
version "0.1.22"
|
||||||
|
resolved "https://registry.yarnpkg.com/pulltorefreshjs/-/pulltorefreshjs-0.1.22.tgz#ddb5e3feee0b2a49fd46e1b18e84fffef2c47ac0"
|
||||||
|
integrity sha512-haxNVEHnS4NCQA7NeG7TSV69z4uqy/N7nfPRuc4dPWe8H6ygUrMjdNeohE+6v0lVVX/ukSjbLYwPUGUYtFKfvQ==
|
||||||
|
|
||||||
punycode@^2.1.0:
|
punycode@^2.1.0:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user