mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-11-22 15:20:09 +01:00
Continue to work on v4 ;0
This commit is contained in:
parent
a950b80d5a
commit
0aa4c8af6f
99
src/static/app/package-lock.json
generated
99
src/static/app/package-lock.json
generated
@ -8,10 +8,13 @@
|
|||||||
"name": "app",
|
"name": "app",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vueuse/core": "^10.9.0",
|
||||||
|
"@vueuse/shared": "^10.9.0",
|
||||||
"bootstrap": "^5.3.2",
|
"bootstrap": "^5.3.2",
|
||||||
"bootstrap-icons": "^1.11.2",
|
"bootstrap-icons": "^1.11.2",
|
||||||
"cidr-tools": "^7.0.4",
|
"cidr-tools": "^7.0.4",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
|
"fuse.js": "^7.0.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
@ -593,6 +596,11 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/web-bluetooth": {
|
||||||
|
"version": "0.0.20",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
||||||
|
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="
|
||||||
|
},
|
||||||
"node_modules/@vitejs/plugin-vue": {
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
"version": "4.6.2",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz",
|
||||||
@ -701,6 +709,89 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.3.tgz",
|
||||||
"integrity": "sha512-rIwlkkP1n4uKrRzivAKPZIEkHiuwY5mmhMJ2nZKCBLz8lTUlE73rQh4n1OnnMurXt1vcUNyH4ZPfdh8QweTjpQ=="
|
"integrity": "sha512-rIwlkkP1n4uKrRzivAKPZIEkHiuwY5mmhMJ2nZKCBLz8lTUlE73rQh4n1OnnMurXt1vcUNyH4ZPfdh8QweTjpQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@vueuse/core": {
|
||||||
|
"version": "10.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.9.0.tgz",
|
||||||
|
"integrity": "sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/web-bluetooth": "^0.0.20",
|
||||||
|
"@vueuse/metadata": "10.9.0",
|
||||||
|
"@vueuse/shared": "10.9.0",
|
||||||
|
"vue-demi": ">=0.14.7"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vueuse/core/node_modules/vue-demi": {
|
||||||
|
"version": "0.14.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz",
|
||||||
|
"integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"bin": {
|
||||||
|
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||||
|
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.0.0-rc.1",
|
||||||
|
"vue": "^3.0.0-0 || ^2.6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vueuse/metadata": {
|
||||||
|
"version": "10.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.9.0.tgz",
|
||||||
|
"integrity": "sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vueuse/shared": {
|
||||||
|
"version": "10.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.9.0.tgz",
|
||||||
|
"integrity": "sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==",
|
||||||
|
"dependencies": {
|
||||||
|
"vue-demi": ">=0.14.7"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vueuse/shared/node_modules/vue-demi": {
|
||||||
|
"version": "0.14.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz",
|
||||||
|
"integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"bin": {
|
||||||
|
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||||
|
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.0.0-rc.1",
|
||||||
|
"vue": "^3.0.0-0 || ^2.6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-regex": {
|
"node_modules/ansi-regex": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
@ -926,6 +1017,14 @@
|
|||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fuse.js": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-caller-file": {
|
"node_modules/get-caller-file": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
|
@ -9,10 +9,13 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vueuse/core": "^10.9.0",
|
||||||
|
"@vueuse/shared": "^10.9.0",
|
||||||
"bootstrap": "^5.3.2",
|
"bootstrap": "^5.3.2",
|
||||||
"bootstrap-icons": "^1.11.2",
|
"bootstrap-icons": "^1.11.2",
|
||||||
"cidr-tools": "^7.0.4",
|
"cidr-tools": "^7.0.4",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
|
"fuse.js": "^7.0.0",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
|
@ -1,8 +1,26 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { onClickOutside } from '@vueuse/core'
|
||||||
export default {
|
export default {
|
||||||
name: "peer",
|
name: "peer",
|
||||||
props: {
|
props: {
|
||||||
Peer: Object
|
Peer: Object
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(){
|
||||||
|
const target = ref(null);
|
||||||
|
const subMenuOpened = ref(false)
|
||||||
|
onClickOutside(target, event => {
|
||||||
|
subMenuOpened.value = false;
|
||||||
|
});
|
||||||
|
return {target, subMenuOpened}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -34,14 +52,38 @@ export default {
|
|||||||
<small class="text-muted">Public Key</small>
|
<small class="text-muted">Public Key</small>
|
||||||
<p class="mb-0"><samp>{{Peer.id}}</samp></p>
|
<p class="mb-0"><samp>{{Peer.id}}</samp></p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="d-flex align-items-end">
|
||||||
<div>
|
<div>
|
||||||
<small class="text-muted">Allowed IP</small>
|
<small class="text-muted">Allowed IP</small>
|
||||||
<p class="mb-0"><samp>{{Peer.allowed_ip}}</samp></p>
|
<p class="mb-0"><samp>{{Peer.allowed_ip}}</samp></p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ms-auto px-2">
|
||||||
|
<a role="button" class="text-body" @click="this.subMenuOpened = true">
|
||||||
|
<h5 class="mb-0"><i class="bi bi-three-dots"></i></h5>
|
||||||
|
</a>
|
||||||
|
<Transition name="slide-fade">
|
||||||
|
<ul class="dropdown-menu mt-2 shadow-lg dropdown-menu-left d-block"
|
||||||
|
v-if="this.subMenuOpened"
|
||||||
|
ref="target">
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item d-flex" role="button" ></a></li>
|
||||||
|
</ul>
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
|
.slide-fade-leave-active, .slide-fade-enter-active {
|
||||||
|
transition: all 0.2s cubic-bezier(1, 0.5, 0.8, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-fade-enter-from,
|
||||||
|
.slide-fade-leave-to {
|
||||||
|
transform: translateY(20px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -0,0 +1,98 @@
|
|||||||
|
<script>
|
||||||
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
|
import {fetchPost} from "@/utilities/fetch.js";
|
||||||
|
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "peerSearch",
|
||||||
|
setup(){
|
||||||
|
const store = DashboardConfigurationStore();
|
||||||
|
const wireguardConfigurationStore = WireguardConfigurationsStore()
|
||||||
|
return {store, wireguardConfigurationStore}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
searchString: String
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
sort: {
|
||||||
|
status: "Status",
|
||||||
|
name: "Name",
|
||||||
|
allowed_ip: "Allowed IP"
|
||||||
|
},
|
||||||
|
interval: {
|
||||||
|
'5000': '5 Seconds',
|
||||||
|
'10000': '10 Seconds',
|
||||||
|
'30000': '30 Seconds',
|
||||||
|
'60000': '1 Minutes'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateSort(sort){
|
||||||
|
fetchPost("/api/updateDashboardConfigurationItem", {
|
||||||
|
section: "Server",
|
||||||
|
key: "dashboard_sort",
|
||||||
|
value: sort
|
||||||
|
}, (res) => {
|
||||||
|
if (res.status){
|
||||||
|
this.store.getConfiguration();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
updateRefreshInterval(refreshInterval){
|
||||||
|
fetchPost("/api/updateDashboardConfigurationItem", {
|
||||||
|
section: "Server",
|
||||||
|
key: "dashboard_refresh_interval",
|
||||||
|
value: refreshInterval
|
||||||
|
}, (res) => {
|
||||||
|
if (res.status){
|
||||||
|
this.store.getConfiguration();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="d-flex gap-2 mb-3 z-3">
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-outline-secondary btn-sm dropdown-toggle rounded-3" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i class="bi bi-filter-circle me-2"></i>
|
||||||
|
Sort
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu mt-2 shadow-lg">
|
||||||
|
<li v-for="(value, key) in this.sort">
|
||||||
|
<a class="dropdown-item d-flex" role="button" @click="this.updateSort(key)">
|
||||||
|
<span class="me-auto">{{value}}</span>
|
||||||
|
<i class="bi bi-check"
|
||||||
|
v-if="store.Configuration.Server.dashboard_sort === key"></i>
|
||||||
|
</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-outline-secondary btn-sm dropdown-toggle rounded-3" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i class="bi bi-arrow-repeat me-2"></i>Refresh Interval
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li v-for="(value, key) in this.interval">
|
||||||
|
<a class="dropdown-item d-flex" role="button" @click="updateRefreshInterval(key)">
|
||||||
|
<span class="me-auto">{{value}}</span>
|
||||||
|
<i class="bi bi-check"
|
||||||
|
v-if="store.Configuration.Server.dashboard_refresh_interval === key"></i>
|
||||||
|
</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ms-auto d-flex align-items-center">
|
||||||
|
<label class="d-flex me-2 text-muted" for="searchPeers"><i class="bi bi-search me-1"></i></label>
|
||||||
|
<input class="form-control form-control-sm rounded-3"
|
||||||
|
v-model="this.wireguardConfigurationStore.searchString">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -3,7 +3,8 @@ import {fetchGet} from "@/utilities/fetch.js";
|
|||||||
|
|
||||||
export const WireguardConfigurationsStore = defineStore('WireguardConfigurationsStore', {
|
export const WireguardConfigurationsStore = defineStore('WireguardConfigurationsStore', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
Configurations: undefined
|
Configurations: undefined,
|
||||||
|
searchString: ""
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
async getConfigurations(){
|
async getConfigurations(){
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import {fetchGet} from "@/utilities/fetch.js";
|
import {fetchGet} from "@/utilities/fetch.js";
|
||||||
import Peer from "@/components/configurationComponents/peer.vue";
|
import Peer from "@/components/configurationComponents/peer.vue";
|
||||||
import { Line, Bar } from 'vue-chartjs'
|
import { Line, Bar } from 'vue-chartjs'
|
||||||
|
import Fuse from "fuse.js";
|
||||||
import {
|
import {
|
||||||
Chart,
|
Chart,
|
||||||
ArcElement,
|
ArcElement,
|
||||||
@ -56,10 +57,19 @@ Chart.register(
|
|||||||
);
|
);
|
||||||
|
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import PeerSearch from "@/components/configurationComponents/peerSearch.vue";
|
||||||
|
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||||
|
import {ref} from "vue";
|
||||||
|
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "configuration",
|
name: "configuration",
|
||||||
components: {Peer, Line, Bar},
|
setup(){
|
||||||
|
const dashboardConfigurationStore = DashboardConfigurationStore();
|
||||||
|
const wireguardConfigurationStore = WireguardConfigurationsStore();
|
||||||
|
return {dashboardConfigurationStore, wireguardConfigurationStore}
|
||||||
|
},
|
||||||
|
components: {PeerSearch, Peer, Line, Bar},
|
||||||
data(){
|
data(){
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
@ -69,6 +79,7 @@ export default {
|
|||||||
historyDataSentDifference: [],
|
historyDataSentDifference: [],
|
||||||
historyDataReceivedDifference: [],
|
historyDataReceivedDifference: [],
|
||||||
|
|
||||||
|
|
||||||
historySentData: {
|
historySentData: {
|
||||||
labels: [],
|
labels: [],
|
||||||
datasets: [
|
datasets: [
|
||||||
@ -106,11 +117,13 @@ export default {
|
|||||||
this.configurationPeers = [];
|
this.configurationPeers = [];
|
||||||
if (id){
|
if (id){
|
||||||
this.getPeers(id)
|
this.getPeers(id)
|
||||||
// this.interval = setInterval(() => {
|
this.setInterval();
|
||||||
// this.getPeers(id)
|
|
||||||
// }, 2000)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'dashboardConfigurationStore.Configuration.Server.dashboard_refresh_interval'(){
|
||||||
|
clearInterval(this.interval);
|
||||||
|
this.setInterval();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeRouteLeave(){
|
beforeRouteLeave(){
|
||||||
@ -170,6 +183,12 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
setInterval(){
|
||||||
|
console.log('Set Interval');
|
||||||
|
this.interval = setInterval(() => {
|
||||||
|
this.getPeers(this.$route.params.id)
|
||||||
|
}, parseInt(this.dashboardConfigurationStore.Configuration.Server.dashboard_refresh_interval))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -275,6 +294,25 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
searchPeers(){
|
||||||
|
const fuse = new Fuse(this.configurationPeers, {
|
||||||
|
keys: ["name", "id", "allowed_ip"]
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = this.wireguardConfigurationStore.searchString ? fuse.search(this.wireguardConfigurationStore.searchString).map(x => x.item) : this.configurationPeers;
|
||||||
|
|
||||||
|
return result.slice().sort((a, b) => {
|
||||||
|
if ( a[this.dashboardConfigurationStore.Configuration.Server.dashboard_sort]
|
||||||
|
< b[this.dashboardConfigurationStore.Configuration.Server.dashboard_sort] ){
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ( a[this.dashboardConfigurationStore.Configuration.Server.dashboard_sort]
|
||||||
|
> b[this.dashboardConfigurationStore.Configuration.Server.dashboard_sort]){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -398,29 +436,22 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<nav class="navbar navbar-expand-lg bg-body mb-3 rounded-3 shadow-sm border">
|
<div class="d-flex align-items-center gap-3 mb-2 ">
|
||||||
<div class="container-fluid w-100">
|
<h3>Peers</h3>
|
||||||
<ul class="navbar-nav peerNav gap-1">
|
<a href="#"
|
||||||
<li class="nav-item">
|
class="ms-auto text-secondary text-decoration-none"><i class="bi bi-sliders2 me-2"></i>Peer Settings</a>
|
||||||
<a class="nav-link active rounded-3 px-3 w-100" aria-current="page" href="#">Peers</a>
|
<a href="#" class="text-decoration-none"><i class="bi bi-plus-circle-fill me-2"></i>Add Peer</a>
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link rounded-3 px-3" href="#">Manage Peers</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link rounded-3 px-3" href="#">Configuration Settings</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
<PeerSearch></PeerSearch>
|
||||||
<div class="row gx-2 gy-2">
|
|
||||||
<div class="col-12 col-lg-6 col-xl-4" v-for="peer in this.configurationPeers">
|
<TransitionGroup name="list" tag="div" class="row gx-2 gy-2 z-0">
|
||||||
|
<div class="col-12 col-lg-6 col-xl-4"
|
||||||
|
:key="peer.id"
|
||||||
|
v-for="peer in this.searchPeers">
|
||||||
<Peer :Peer="peer"></Peer>
|
<Peer :Peer="peer"></Peer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</TransitionGroup>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -428,8 +459,31 @@ export default {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.peerNav .nav-link{
|
.peerNav .nav-link{
|
||||||
&.active{
|
&.active{
|
||||||
background: linear-gradient(var(--degree), var(--brandColor1) var(--distance2), var(--brandColor2) 100%);
|
//background: linear-gradient(var(--degree), var(--brandColor1) var(--distance2), var(--brandColor2) 100%);
|
||||||
color: white;
|
//color: white;
|
||||||
|
background-color: #efefef;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-move, /* apply transition to moving elements */
|
||||||
|
.list-enter-active,
|
||||||
|
.list-leave-active {
|
||||||
|
transition: all 0.4s cubic-bezier(0.82, 0.58, 0.17, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-leave-active{
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-enter-from,
|
||||||
|
.list-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ensure leaving items are taken out of layout flow so that moving
|
||||||
|
animations can be calculated correctly. */
|
||||||
|
.list-leave-active {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
Loading…
Reference in New Issue
Block a user