app ui / ux reworked #1

pull/1/head
Thomas Ballmann 4 years ago
parent f4eae5a164
commit 4147b65904

@ -24,6 +24,7 @@
"sass": "^1.19.0",
"sass-loader": "^8.0.0",
"vue-cli-plugin-vuetify": "~2.0.4",
"vue-moment": "^4.1.0",
"vue-router": "^3.1.5",
"vue-svg-loader": "^0.16.0",
"vue-template-compiler": "^2.6.11",

@ -1,5 +1,5 @@
<template>
<v-app style="_background: #e2e2e2">
<v-app _style="background: #e2e2e2">
<template v-if="isLoading">
<v-overlay :absolute="true" :value="true">
<v-progress-circular indeterminate size="64"></v-progress-circular>
@ -7,97 +7,29 @@
</template>
<template v-else>
<!--
<v-navigation-drawer
v-model="drawer"
app
>
<!-- https://cdn.vuetifyjs.com/images/parallax/material.jpg -->
<v-img class="device-screen-image" :aspect-ratio="16/9" :src="device_screen_src">
<v-row align="end" class="lightbox white--text pa-2 fill-height">
<!--
<v-col>
<div class="subheading">update in 2min</div>
<div class="body-1">heyfromjonathan@gmail.com</div>
</v-col>
-->
</v-row>
</v-img>
<v-list-item>
<v-list-item-icon class="mr-3">
<v-progress-circular
:rotate="-90"
:size="50"
:width="5"
:value="playlistProgress"
>
{{ playlistRemaining }}
</v-progress-circular>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title class="title">
{{ stats.playlist.current }}
</v-list-item-title>
<v-list-item-subtitle>
8. March 2020
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-divider></v-divider>
<v-list-item>
<v-list-item-content>
<v-list-item-title class="title">
Weather
</v-list-item-title>
<v-list-item-subtitle>
6° Salzburg
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-divider></v-divider>
<v-list-item>
<v-list-item-content>
<v-list-item-title class="title">
Calendar
</v-list-item-title>
<v-list-item-subtitle>
8. March 2020
</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-divider></v-divider>
<v-list-item>
<v-list-item-content>
Total: {{ stats.device.fs.total | prettyBytes }}<br/>
Free: {{ stats.device.fs.free | prettyBytes }}
<v-progress-linear :value="fsUsagePercent" height="25">
<template v-slot="{ value }">
<strong class="white--text">{{ Math.ceil(value) }}%</strong>
</template>
</v-progress-linear>
</v-list-item-content>
</v-list-item>
</v-navigation-drawer>
-->
<!--
<v-system-bar app dark color="primary">
<span>paperdash.io</span>
<v-spacer></v-spacer>
<span>boxxi</span>
<v-spacer></v-spacer>
<v-icon>mdi-wifi-strength-4</v-icon>
<v-icon>{{ stats.wifi.rssi | wifiIcon(stats.wifi.secure) }}</v-icon>
<!--
<v-icon>mdi-signal-cellular-outline</v-icon>
<v-icon>mdi-battery</v-icon>
<span>12:30</span>
-->
</v-system-bar>
-->
<v-app-bar app color="orange lighten-1" dark short>
<!--
<v-app-bar _bottom dense app color="orange lighten-1" dark _short>
<v-app-bar-nav-icon @click="drawer = !drawer">
<v-icon>$device</v-icon>
</v-app-bar-nav-icon>
@ -113,6 +45,26 @@
</template>
</v-app-bar>
-->
<!--
<v-bottom-navigation
v-model="bottomNav"
dark
shift
app
>
<v-btn>
<span>Device</span>
<v-icon>$device</v-icon>
</v-btn>
<v-btn>
<span>Settings</span>
<v-icon>$settings</v-icon>
</v-btn>
</v-bottom-navigation>
-->
<v-content>
<v-container fluid fill-height class="align-start">
@ -126,29 +78,20 @@
<script>
import apiDevice from './api/device'
import "@/assets/app.css"
// eslint-disable-next-line
//import imageBmp from "@/assets/black.bmp"
// eslint-disable-next-line
//import imageJpg from "@/assets/black.jpg"
export default {
name: 'App',
data: () => ({
isLoading: true,
stats: null,
bottomNav: 3,
drawer: true,
items: [
{title: 'Dashboard', icon: '$dashboard', to: '/'},
//{title: 'Sandbox', icon: '$sandbox', to: '/sandbox'},
{title: 'Wifi', icon: '$wifi', to: '/wifi'},
{title: 'Settings', icon: '$settings', to: '/settings'},
],
playlistRemaining: 0,
playlistTimerProgress: 30,
device_screen_src: null
}),
created () {
this.$vuetify.icons.values.device = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/cast/baseline.svg')}
@ -157,45 +100,27 @@
this.$vuetify.icons.values.wifi = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/wifi/baseline.svg')}
this.$vuetify.icons.values.sandbox = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/gesture/baseline.svg')} // gesture, brush, palette,
this.reloadStats(() => {
this.isLoading = false
})
this.autoReloadStats();
},
watch: {
playlistRemaining: {
handler(value) {
if (value > 0) {
setTimeout(() => {
this.playlistRemaining--
}, 1000)
} else {
this.reloadStats()
}
},
stats () {
this.isLoading = false
}
},
computed: {
fsUsagePercent () {
let fs = this.stats.device.fs
return parseInt(100 / fs.total * fs.used)
stats () {
return this.$root._data.stats;
},
playlistProgress () {
return parseInt(100 / 60 * this.playlistRemaining)
}
},
methods: {
reloadStats (cb) {
this.device_screen_src = "/current-image?" + Date.now()
autoReloadStats () {
apiDevice.getStats(stats => {
this.stats = stats
this.playlistRemaining = stats.playlist.remaining
this.$root._data.stats = stats
if (cb) {
cb()
}
setTimeout(() => {
this.autoReloadStats()
}, (stats.playlist.remaining + 2) * 1000)
})
}
}
@ -203,8 +128,4 @@
</script>
<style scoped>
.lightbox {
box-shadow: 0 0 20px inset rgba(0, 0, 0, 0.2);
background-image: linear-gradient(to top, rgba(0, 0, 0, 0.4) 0%, transparent 72px);
}
</style>

@ -144,15 +144,5 @@ export default {
return axios
.get('/stats')
.then(response => cb(response.data))
},
/**
* @param cb
* @returns {PromiseLike<any> | Promise<any>}
*/
updateWeather(cb) {
return axios
.get('/api/update?weather=1')
.then(response => cb(response.data))
},
}
}

@ -0,0 +1,196 @@
/**
* Mocking client-server processing
*/
// eslint-disable-next-line
const _weather = {
coord: { lon: 13.04, lat: 47.8 },
weather: [
{ id: 800, main: "Clear", description: "Klarer Himmel", icon: "01d" }
],
base: "stations",
main: {
temp: 2.75,
feels_like: 0.09,
temp_min: 0,
temp_max: 6.11,
pressure: 1023,
humidity: 69
},
visibility: 10000,
wind: { speed: 0.5 },
clouds: { all: 9 },
dt: 1584260150,
sys: {
type: 1,
id: 6877,
country: "AT",
sunrise: 1584249628,
sunset: 1584292379
},
timezone: 3600,
id: 2766824,
name: "Salzburg",
cod: 200
}
// eslint-disable-next-line
const _forecast = {
city: {
id: 2766824,
name: "Salzburg",
coord: { lon: 13.044, lat: 47.7994 },
country: "AT",
population: 0,
timezone: 3600
},
cod: "200",
message: 0.0878612,
cnt: 4,
list: [
{
dt: 1584270000,
sunrise: 1584249627,
sunset: 1584292378,
temp: {
day: 14.02,
min: 1.59,
max: 14.02,
night: 1.59,
eve: 6.4,
morn: 2.88
},
feels_like: { day: 9.75, night: -2.31, eve: 2.85, morn: -0.11 },
pressure: 1021,
humidity: 42,
weather: [
{
id: 801,
main: "Clouds",
description: "Ein paar Wolken",
icon: "02d"
}
],
speed: 3.55,
deg: 105,
clouds: 15
},
{
dt: 1584356400,
sunrise: 1584335904,
sunset: 1584378865,
temp: {
day: 13.15,
min: 0.88,
max: 13.15,
night: 4.3,
eve: 6.25,
morn: 0.88
},
feels_like: { day: 10.56, night: 1.77, eve: 4.16, morn: -2.11 },
pressure: 1022,
humidity: 46,
weather: [
{
id: 800,
main: "Clear",
description: "Klarer Himmel",
icon: "01d"
}
],
speed: 1.26,
deg: 349,
clouds: 0
},
{
dt: 1584442800,
sunrise: 1584422182,
sunset: 1584465351,
temp: {
day: 13.8,
min: 3.73,
max: 14.05,
night: 9.64,
eve: 9.75,
morn: 3.73
},
feels_like: { day: 11.71, night: 7.26, eve: 7.47, morn: 1.33 },
pressure: 1029,
humidity: 53,
weather: [
{ id: 804, main: "Clouds", description: "Bedeckt", icon: "04d" }
],
speed: 1.21,
deg: 331,
clouds: 100
},
{
dt: 1584529200,
sunrise: 1584508459,
sunset: 1584551838,
temp: {
day: 16.55,
min: 6.35,
max: 16.55,
night: 7.1,
eve: 9.94,
morn: 6.35
},
feels_like: { day: 14.61, night: 5.37, eve: 8.03, morn: 4.56 },
pressure: 1028,
humidity: 52,
weather: [
{
id: 800,
main: "Clear",
description: "Klarer Himmel",
icon: "01d"
}
],
speed: 1.67,
deg: 39,
clouds: 7
}
]
}
import axios from 'axios'
export default {
/**
* @param cb
* @returns {PromiseLike<any> | Promise<any>}
*/
getCurrent(cb) {
//return cb(_weather);
// eslint-disable-next-line
return axios
.get('/fs/weatherCurrent.json')
.then(response => cb(response.data))
},
/**
* @param cb
* @returns {PromiseLike<any> | Promise<any>}
*/
getForecast(cb) {
//return cb(_forecast);
// eslint-disable-next-line
return axios
.get('/fs/weatherForecast.json')
.then(response => cb(response.data))
},
/**
* @param cb
* @returns {PromiseLike<any> | Promise<any>}
*/
updateWeather(cb) {
return axios
.get('/api/update?weather=1')
.then(response => cb(response.data))
},
}

@ -1,3 +1,7 @@
.device-screen-image {
.device-screen-image > .v-image__image {
transform: scaleY(-1);
}
.v-icon {
fill: currentColor;
}

@ -0,0 +1,71 @@
<template>
<v-card outlined>
<v-list-item>
<v-list-item-icon class="mr-3">
<v-icon>{{ stats.wifi.rssi | wifiIcon(stats.wifi.secure) }}</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>{{ stats.wifi.ssid }}</v-list-item-title>
</v-list-item-content>
<v-list-item-avatar>
<v-avatar color="secondary lighten-3" size="24">
<span class="white--text headline caption">{{ stats.wifi.channel }}</span>
</v-avatar>
</v-list-item-avatar>
</v-list-item>
<v-divider class="mx-4"></v-divider>
<v-list dense>
<v-list-item v-for="key in wifiStats" :key="key">
<v-list-item-title>{{ key }}</v-list-item-title>
<v-list-item-subtitle class="text-right">{{ stats.wifi[key] }}</v-list-item-subtitle>
</v-list-item>
</v-list>
<v-list-item>
<v-list-item-icon class="mr-3">
<v-icon>$memory</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>i8n:Storage</v-list-item-title>
</v-list-item-content>
<v-list-item-avatar>
<v-progress-circular :rotate="-90" :value="fsUsage" class="caption">{{ fsUsage }}</v-progress-circular>
</v-list-item-avatar>
</v-list-item>
<v-divider class="mx-4"></v-divider>
<v-list dense class="pb-0">
<v-list-item>
<v-list-item-title>Total</v-list-item-title>
<v-list-item-subtitle class="text-right">1.86 MB</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<v-list-item-title>Free</v-list-item-title>
<v-list-item-subtitle class="text-right">770KB</v-list-item-subtitle>
</v-list-item>
</v-list>
</v-card>
</template>
<script>
export default {
data: () => ({
wifiStats: ["ip", "gateway", "dns", "mac"]
}),
computed: {
stats() {
return this.$root._data.stats;
},
fsUsage() {
return Math.round(
(100 / this.stats.device.fs.total) * this.stats.device.fs.used
);
}
}
};
</script>

@ -0,0 +1,100 @@
<template>
<v-card outlined>
<!--
<v-toolbar
extended
extension-height="100"
dark
prominent
src="https://cdn.vuetifyjs.com/images/backgrounds/vbanner.jpg"
>
<v-app-bar-nav-icon></v-app-bar-nav-icon>
<v-toolbar-title>Title</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon>
<v-icon>mdi-magnify</v-icon>
</v-btn>
<v-btn icon>
<v-icon>mdi-heart</v-icon>
</v-btn>
<v-btn icon>
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</v-toolbar>
-->
<!-- bug, border radius oben geht mit skeleton nicht -->
<v-skeleton-loader type="image" tile :loading="false">
<v-img class="device-screen-image" :aspect-ratio="16/9" :src="device_screen_src"></v-img>
</v-skeleton-loader>
<!--
<v-btn absolute dark fab top right color="white">
<v-icon>$settings</v-icon>
</v-btn>
-->
<!--
<v-card-text style="height: 100px; position: relative">
<v-btn color="pink" dark absolute top right fab>
<v-icon>mdi-plus</v-icon>
</v-btn>
</v-card-text>
-->
<v-card-actions style="position: relative">
<v-btn color="white" _dark absolute top right fab elevation="2">
<v-icon>$settings</v-icon>
</v-btn>
<v-progress-circular
:rotate="-90"
:size="50"
:width="5"
:value="playlistProgress"
>{{ playlistRemainingCountdown }}</v-progress-circular>
<span class="headline pl-3">{{ playlistCurrent }}</span>
</v-card-actions>
</v-card>
</template>
<script>
export default {
data: () => ({
device_screen_src: null,
playlistRemainingCountdown: 0
}),
created() {
this.playlistRemainingCountdown = this.playlistRemaining;
this.device_screen_src = "/current-image?" + Date.now();
},
computed: {
playlistProgress() {
return parseInt((100 / 60) * this.playlistRemainingCountdown);
},
playlistRemaining() {
return this.$root._data.stats.playlist.remaining;
},
playlistCurrent() {
return this.$root._data.stats.playlist.current;
}
},
watch: {
playlistRemaining(val) {
this.playlistRemainingCountdown = val;
this.device_screen_src = "/current-image?" + Date.now();
},
playlistRemainingCountdown(val) {
if (val > 0) {
setTimeout(() => {
this.playlistRemainingCountdown--;
}, 1000);
}
}
}
};
</script>

@ -0,0 +1,121 @@
<template>
<v-card outlined>
<v-skeleton-loader
type="card-heading, list-item-three-line, list-item-avatar, list-item-avatar, list-item-avatar, list-item-avatar"
:loading="isLoading"
>
<v-list-item two-line>
<v-list-item-content v-if="weather">
<v-list-item-title class="headline">{{ weather.name }}</v-list-item-title>
<v-list-item-subtitle>{{ weather.dt | moment("ddd, h A") }}, {{ weather.weather[0].description }}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<template v-if="weather">
<v-card-text>
<v-row align="center">
<v-col class="display-3" cols="6">{{ currentTemp }} &deg;C</v-col>
<v-col cols="6">
<v-img :src="currentConditionIcon" alt width="92"></v-img>
</v-col>
</v-row>
</v-card-text>
<v-list-item>
<v-list-item-icon>
<v-icon>mdi-send</v-icon>
</v-list-item-icon>
<v-list-item-subtitle>{{ weather.wind.speed | round }} km/h</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<v-list-item-icon>
<v-icon>mdi-cloud-download</v-icon>
</v-list-item-icon>
<v-list-item-subtitle>{{ weather.main.humidity }}%</v-list-item-subtitle>
</v-list-item>
<v-divider class="mx-4"></v-divider>
<template v-if="forecast">
<v-list class="transparent">
<v-list-item v-for="item in forecast.list" :key="item.dt">
<v-list-item-title>{{ item.dt | moment("dddd") }}</v-list-item-title>
<v-list-item-icon class="ma-0">
<v-img :src="getConditionIcon(item.weather[0].icon)"></v-img>
</v-list-item-icon>
<v-list-item-subtitle
class="text-right"
>{{ item.temp.max | round }}&deg; / {{ item.temp.min | round }}&deg;</v-list-item-subtitle>
</v-list-item>
</v-list>
</template>
</template>
<v-divider></v-divider>
<v-card-actions>
<v-btn :loading="isUpdatingData" @click="updateData" text>i8n:update weather data</v-btn>
</v-card-actions>
</v-skeleton-loader>
</v-card>
</template>
<script>
import weatherApi from "@/api/weather";
export default {
data: () => ({
isLoading: true,
isUpdatingData: false,
weather: null,
forecast: null
}),
created() {
this.loadData();
},
computed: {
currentTemp() {
return Math.round(this.weather.main.temp);
},
currentConditionIcon() {
return (
"http://openweathermap.org/img/wn/" +
this.weather.weather[0].icon +
"@2x.png"
);
}
},
methods: {
getConditionIcon(code) {
return "http://openweathermap.org/img/wn/" + code + "@2x.png";
},
loadData() {
this.isLoading = true;
weatherApi.getCurrent(weather => {
this.weather = weather;
weatherApi.getForecast(forecast => {
this.forecast = forecast;
this.isLoading = false;
});
});
},
updateData() {
this.isUpdatingData = true;
weatherApi.updateWeather(() => {
// TODO reload json
this.isUpdatingData = false;
this.loadData();
});
}
}
};
</script>

@ -5,8 +5,13 @@ import vuetify from './plugins/vuetify'
Vue.config.productionTip = false
Vue.use(require('vue-moment'));
new Vue({
vuetify,
router,
data: {
stats: null
},
render: h => h(App)
}).$mount('#app')

@ -1,6 +1,66 @@
import Vue from 'vue'
import Vuetify from 'vuetify/lib'
// enable system icons
// @see https://vuetifyjs.com/en/customization/icons#component-icons
// @see https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/services/icons/presets/mdi-svg.ts
const MY_ICONS = {
// system icons
//complete: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/check/baseline.svg')},
//cancel: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/cancel/baseline.svg')},
//close: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/close/baseline.svg')},
//delete: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/delete/baseline.svg')},
//clear: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/clear/baseline.svg')},
//success: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/check_circle/baseline.svg')},
//info: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/info/baseline.svg')},
//warning: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/priority_high/baseline.svg')},
//error: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/warning/baseline.svg')},
//prev: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/chevron_left/baseline.svg')},
//next: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/chevron_right/baseline.svg')},
//checkboxOn: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/check_box/baseline.svg')},
//checkboxOff: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/check_box_outline_blank/baseline.svg')},
//checkboxIndeterminate: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/indeterminate_check_box/baseline.svg')},
//delimiter: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/fiber_manual_record/baseline.svg')}, // for carousel
//sort: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/arrow_upward/baseline.svg')},
//expand: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/keyboard_arrow_down/baseline.svg')},
//menu: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/menu/baseline.svg')},
//subgroup: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/arrow_drop_down/baseline.svg')},
dropdown: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/arrow_drop_down/baseline.svg')},
//radioOn: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/radio_button_checked/baseline.svg')},
//radioOff: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/radio_button_unchecked/baseline.svg')},
//edit: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/edit/baseline.svg')},
//ratingEmpty: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/star_border/baseline.svg')},
//ratingFull: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/star/baseline.svg')},
//ratingHalf: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/star_half/baseline.svg')},
//loading: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/cached/baseline.svg')},
//first: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/first_page/baseline.svg')},
//last: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/last_page/baseline.svg')},
//unfold: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/unfold_more/baseline.svg')},
//file: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/attach_file/baseline.svg')},
//plus: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/add/baseline.svg')},
//minus: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/remove/baseline.svg')},
// app icons
//save: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/save/baseline.svg')},
//more_vert: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/more_vert/baseline.svg')},
//reorder: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/reorder/baseline.svg')},
//preview: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/remove_red_eye/baseline.svg')},
//search: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/search/baseline.svg')},
memory: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/memory/baseline.svg')},
// wifi
signalWifi0: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_0_bar/baseline.svg')},
signalWifi1: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_1_bar/baseline.svg')},
signalWifi1Lock: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_1_bar_lock/baseline.svg')},
signalWifi2: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_2_bar/baseline.svg')},
signalWifi2Lock: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_2_bar_lock/baseline.svg')},
signalWifi3: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_3_bar/baseline.svg')},
signalWifi3Lock: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_3_bar_lock/baseline.svg')},
signalWifi4: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_4_bar/baseline.svg')},
signalWifi4Lock: {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_4_bar_lock/baseline.svg')}
}
Vue.use(Vuetify)
// usage: {{ file.size | prettyBytes }}
@ -30,5 +90,65 @@ Vue.filter('prettyBytes', function (num) {
return (neg ? '-' : '') + num + ' ' + unit;
});
export default new Vuetify({
})
/**
* Vue filter to round the decimal to the given place.
* http://jsfiddle.net/bryan_k/3ova17y9/
*
* @param {String} value The value string.
* @param {Number} decimals The number of decimal places.
*/
Vue.filter('round', function (value, decimals) {
if (!value) {
value = 0;
}
if (!decimals) {
decimals = 0;
}
value = Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals);
return value;
});
/**
* usage: {{ wifi.rssi | wifiIcon(wifi.secure) }}
* @param {Number} rssi rssi value
* @param {Boolean} secure Is wifi protected
*/
Vue.filter('wifiIcon', function (rssi, secure) {
let icon = '$signalWifi'
// strength
if (rssi >= -67) {
icon += 4
}
else if (rssi >= -70) {
icon += 3
}
else if (rssi >= -80) {
icon += 2
}
else if (rssi >= -90) {
icon += 1
}
else {
icon += 0
}
// secure
if (secure !== 0 && rssi >= -90) {
icon += 'Lock'
}
return icon
});
const opts = {
icons: {
iconfont: '',
values: MY_ICONS
}
}
export default new Vuetify(opts)

@ -1,28 +1,33 @@
<template>
<v-container fluid grid-list-md pa-2>
<v-card class="mx-auto my-12" max-width="400" elevation="24">
<!--<v-img class="device-screen-image" :aspect-ratio="16/9" src="/fs/screen.bmp"></v-img>-->
</v-card>
<v-btn class="ma-2" :loading="isLoading" color="secondary" @click="updateData">load weather data</v-btn>
<v-container fluid _grid-list-md _pa-2>
<v-row>
<v-col cols="12" md="6">
<screen-card></screen-card>
</v-col>
<v-col cols="12" sm="6" md="4">
<weather-card></weather-card>
</v-col>
<v-col cols="12" sm="6" md="4">
<device-card></device-card>
</v-col>
</v-row>
</v-container>
</template>
<script>
import apiDevice from "@/api/device";
import screenCard from "@/components/ScreenCard";
import weatherCard from "@/components/WeatherCard";
import deviceCard from "@/components/DeviceCard";
export default {
components: {
screenCard,
weatherCard,
deviceCard
},
data: () => ({
isLoading: false
}),
methods: {
updateData() {
this.isLoading = true;
apiDevice.updateWeather(() => {
this.isLoading = false;
});
}
}
})
};
</script>

@ -173,15 +173,16 @@
show1: false,
}),
created () {
this.$vuetify.icons.values.signalWifi0 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_0_bar/baseline.svg')}
this.$vuetify.icons.values.signalWifi1 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_1_bar/baseline.svg')}
this.$vuetify.icons.values.signalWifi1Lock = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_1_bar_lock/baseline.svg')}
this.$vuetify.icons.values.signalWifi2 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_2_bar/baseline.svg')}
this.$vuetify.icons.values.signalWifi2Lock = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_2_bar_lock/baseline.svg')}
this.$vuetify.icons.values.signalWifi3 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_3_bar/baseline.svg')}
this.$vuetify.icons.values.signalWifi3Lock = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_3_bar_lock/baseline.svg')}
this.$vuetify.icons.values.signalWifi4 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_4_bar/baseline.svg')}
this.$vuetify.icons.values.signalWifi4Lock = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_4_bar_lock/baseline.svg')}
//this.$vuetify.icons.values.signalWifi0 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_0_bar/baseline.svg')}
//this.$vuetify.icons.values.signalWifi1 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_1_bar/baseline.svg')}
//this.$vuetify.icons.values.signalWifi1Lock = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_1_bar_lock/baseline.svg')}
//this.$vuetify.icons.values.signalWifi2 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_2_bar/baseline.svg')}
//this.$vuetify.icons.values.signalWifi2Lock = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_2_bar_lock/baseline.svg')}
//this.$vuetify.icons.values.signalWifi3 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_3_bar/baseline.svg')}
//this.$vuetify.icons.values.signalWifi3Lock = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_3_bar_lock/baseline.svg')}
//this.$vuetify.icons.values.signalWifi4 = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_4_bar/baseline.svg')}
//this.$vuetify.icons.values.signalWifi4Lock = {component: () => import(/* webpackChunkName: "icons" */'!vue-svg-loader!@material-icons/svg/svg/signal_wifi_4_bar_lock/baseline.svg')}
apiDevice.wifiScan(list => {
this.wifiAvailable = list

@ -5243,6 +5243,11 @@ mkdirp@0.5.1, mkdirp@^0.5.1, mkdirp@~0.5.1:
dependencies:
minimist "0.0.8"
moment@^2.19.2:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@ -8014,6 +8019,13 @@ vue-loader@^15.8.3:
vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0"
vue-moment@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/vue-moment/-/vue-moment-4.1.0.tgz#092a8ff723a96c6f85a0a8e23ad30f0bf320f3b0"
integrity sha512-Gzisqpg82ItlrUyiD9d0Kfru+JorW2o4mQOH06lEDZNgxci0tv/fua1Hl0bo4DozDV2JK1r52Atn/8QVCu8qQw==
dependencies:
moment "^2.19.2"
vue-router@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.1.5.tgz#ff29b8a1e1306c526b52d4dc0532109f16c41231"

Loading…
Cancel
Save