app ui / ux reworked #1
parent
f4eae5a164
commit
4147b65904
@ -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 }} °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 }}° / {{ item.temp.min | round }}°</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>
|
Loading…
Reference in New Issue