184 lines
6.7 KiB
Vue
184 lines
6.7 KiB
Vue
<script setup lang="ts">
|
|
import Loading from '@/components/Loading.vue';
|
|
import {getPressureTrend} from '@/services/units.ts';
|
|
import {inject, onMounted, ref} from 'vue';
|
|
import {api, BASE} from '../services/api';
|
|
|
|
const data = ref<any>();
|
|
const openGraph = inject<(key: string) => void>('openGraph');
|
|
|
|
onMounted(async () => {
|
|
data.value = await api.current();
|
|
});
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.today-card {
|
|
position: relative;
|
|
border-radius: 12px;
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
padding: 14px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
.today-icon {
|
|
width: 52px;
|
|
height: 52px;
|
|
object-fit: contain;
|
|
flex-shrink: 0;
|
|
}
|
|
.today-label {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: var(--text-muted);
|
|
}
|
|
.hi-lo {
|
|
display: flex;
|
|
gap: 8px;
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
margin-top: 2px;
|
|
.hi { color: var(--text); }
|
|
.lo { color: var(--text-muted); font-weight: 400; }
|
|
}
|
|
.divider {
|
|
height: 1px;
|
|
background: var(--border);
|
|
}
|
|
.stat-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
|
|
gap: 10px;
|
|
}
|
|
.stat {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
cursor: pointer;
|
|
border-radius: 8px;
|
|
padding: 4px 6px;
|
|
margin: -4px -6px;
|
|
transition: background 0.15s;
|
|
&:hover { background: var(--hover); }
|
|
}
|
|
.stat-label {
|
|
font-size: 10px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.06em;
|
|
color: var(--text-muted);
|
|
}
|
|
.stat-value {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: var(--text);
|
|
}
|
|
</style>
|
|
|
|
<template>
|
|
<div class="today-card">
|
|
<div class="flex-r align-items-center justify-between">
|
|
<div v-if="!data" class="pos-rel br-2 overflow-hidden" style="width: 75px; height: 50px"><Loading/></div>
|
|
<div v-else class="flex-c justify-center align-items-center mx-3">
|
|
<img :src="BASE + '/api/icon/' + data.icon" class="today-icon" alt="weather" />
|
|
<div class="today-label">{{ data.label }}</div>
|
|
</div>
|
|
<div class="flex-r align-items-center gap-2">
|
|
<div class="fs-6 m-0 p-0 flex-r align-items-center">
|
|
<div v-if="!data" class="d-inline-block pos-rel br-2 overflow-hidden" style="width: 50px; height: 1.25em"><Loading/></div>
|
|
<template v-else>{{ Math.round(data.temperature) }}°C</template>
|
|
</div>
|
|
<div class="flex-c fg-muted">
|
|
<div class="hi">
|
|
<div v-if="!data" class="d-inline-block pos-rel br-2 overflow-hidden" style="width: 25px; height: 1em"><Loading/></div>
|
|
<template v-else>↑ {{ ~~(data.temperature_max) }}°</template>
|
|
</div>
|
|
<div class="lo">
|
|
<div v-if="!data" class="d-inline-block pos-rel br-2 overflow-hidden" style="width: 25px; height: 1em"><Loading/></div>
|
|
<template v-else>↓ {{ ~~(data.temperature_min) }}°</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="divider" />
|
|
|
|
<div class="stat-grid">
|
|
<div class="stat" @click="openGraph?.('heat_index')">
|
|
<span class="stat-label">Feels Like</span>
|
|
<div v-if="!data" class="w-100 pos-rel br-2 overflow-hidden" style="height: 1.75em"><Loading/></div>
|
|
<span v-else class="stat-value">
|
|
<template v-if="data.heat_index == null">—</template>
|
|
<template v-else>{{ ~~(data.heat_index) }}<span class="fg-muted">°C</span></template>
|
|
</span>
|
|
</div>
|
|
<div class="stat" @click="openGraph?.('humidity')">
|
|
<span class="stat-label">Humidity</span>
|
|
<div v-if="!data" class="w-100 pos-rel br-2 overflow-hidden" style="height: 1.75em"><Loading/></div>
|
|
<span v-else class="stat-value">
|
|
<template v-if="data.humidity == null">—</template>
|
|
<template v-else>{{ Math.round(data.humidity) }}<span class="fg-muted">%</span></template>
|
|
</span>
|
|
</div>
|
|
<div class="stat" @click="openGraph?.('precipitation_chance')">
|
|
<span class="stat-label">Rain</span>
|
|
<div v-if="!data" class="w-100 pos-rel br-2 overflow-hidden" style="height: 1.75em"><Loading /></div>
|
|
<span v-else class="stat-value">
|
|
<template v-if="data.raining == null">—</template>
|
|
<template v-else-if="data.raining">Raining</template>
|
|
<template v-else>{{data.precipitation_chance}}<span class="fg-muted">%</span></template>
|
|
</span>
|
|
</div>
|
|
<div class="stat" @click="openGraph?.('clouds')">
|
|
<span class="stat-label">Cloud Cover</span>
|
|
<div v-if="!data" class="w-100 pos-rel br-2 overflow-hidden" style="height: 1.75em"><Loading/></div>
|
|
<span v-else class="stat-value">
|
|
<template v-if="data.clouds == null">—</template>
|
|
<template v-else>{{ Math.round(data.clouds) }}<span class="fg-muted">%</span></template>
|
|
</span>
|
|
</div>
|
|
<div class="stat" @click="openGraph?.('pressure_slp')">
|
|
<span class="stat-label">Sea Level</span>
|
|
<div v-if="!data" class="w-100 pos-rel br-2 overflow-hidden" style="height: 1.75em"><Loading/></div>
|
|
<span v-else class="stat-value">
|
|
<template v-if="data.pressure_slp == null">—</template>
|
|
<template v-else>{{ data.pressure_slp?.toFixed(1) }} <span class="fg-muted">({{ getPressureTrend(data.pressure_trend) }})</span></template>
|
|
</span>
|
|
</div>
|
|
<div class="stat" @click="openGraph?.('vapor_pressure_deficit')">
|
|
<span class="stat-label">VPD KPA</span>
|
|
<div v-if="!data" class="w-100 pos-rel br-2 overflow-hidden" style="height: 1.75em"><Loading/></div>
|
|
<span v-else class="stat-value">
|
|
<template v-if="data.vapor_pressure_deficit == null">—</template>
|
|
<template v-else>{{ data.vapor_pressure_deficit?.toFixed(2) }} <span class="fg-muted">kPa</span></template>
|
|
</span>
|
|
</div>
|
|
<div class="stat" @click="openGraph?.('air_quality')">
|
|
<span class="stat-label">Air Quality</span>
|
|
<div v-if="!data" class="w-100 pos-rel br-2 overflow-hidden" style="height: 1.75em"><Loading/></div>
|
|
<span v-else class="stat-value">
|
|
<template v-if="data.air_quality == null">—</template>
|
|
<template v-else>{{ data.air_quality }} <span class="fg-muted">/ 500</span></template>
|
|
</span>
|
|
</div>
|
|
<div class="stat" @click="openGraph?.('air_quality_ohms')">
|
|
<span class="stat-label">Gas Resistance</span>
|
|
<div v-if="!data" class="w-100 pos-rel br-2 overflow-hidden" style="height: 1.75em"><Loading/></div>
|
|
<span v-else class="stat-value">
|
|
<template v-if="data.air_quality_ohms == null">—</template>
|
|
<template v-else>{{ data.air_quality_ohms }} <span class="fg-muted">Ω</span></template>
|
|
</span>
|
|
</div>
|
|
<div class="stat" @click="openGraph?.('visibility')">
|
|
<span class="stat-label">Visibility</span>
|
|
<div v-if="!data" class="w-100 pos-rel br-2 overflow-hidden" style="height: 1.75em"><Loading/></div>
|
|
<span v-else class="stat-value">
|
|
<template v-if="data.visibility == null">—</template>
|
|
<template v-else>{{ data.visibility?.toFixed(1) }} <span class="fg-muted">km</span></template>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|