mirror of
https://github.com/headporter81/specialsource-homepage-frontend.git
synced 2026-10-07 15:21:10 +09:00
This commit is contained in:
@@ -209,6 +209,13 @@ body, html { margin: 0; padding: 0; width: 100%; height: 100%; background-color:
|
|||||||
font-size: 11px; font-weight: bold; padding: 3px 8px; border: 1px solid currentColor;
|
font-size: 11px; font-weight: bold; padding: 3px 8px; border: 1px solid currentColor;
|
||||||
background: rgba(0, 0, 0, 0.5); letter-spacing: 1px; border-radius: 2px; display: inline-block;
|
background: rgba(0, 0, 0, 0.5); letter-spacing: 1px; border-radius: 2px; display: inline-block;
|
||||||
}
|
}
|
||||||
|
.weather-panel { text-align: center; }
|
||||||
|
.weather-ascii {
|
||||||
|
font-family: inherit; white-space: pre; text-align: left; display: inline-block;
|
||||||
|
font-size: 11px; line-height: 1.15; color: var(--tui-color); text-shadow: 0 0 4px var(--tui-glow);
|
||||||
|
margin: 0 0 8px; border: none; background: transparent;
|
||||||
|
}
|
||||||
|
.weather-loading { font-size: 11px; color: var(--tui-dim); letter-spacing: 0.05em; padding: 8px 0; }
|
||||||
|
|
||||||
/* 📺 유튜브 그리드 레이아웃 */
|
/* 📺 유튜브 그리드 레이아웃 */
|
||||||
#videos-container {
|
#videos-container {
|
||||||
|
|||||||
@@ -75,6 +75,17 @@
|
|||||||
<span :class="['dash-chip', currentServerSource]">[{{ currentServerSourceDisplay }}]</span>
|
<span :class="['dash-chip', currentServerSource]">[{{ currentServerSourceDisplay }}]</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="dash-panel weather-panel">
|
||||||
|
<h4>SEOUL WEATHER</h4>
|
||||||
|
<div v-if="weather.loading" class="weather-loading">SYNCING...</div>
|
||||||
|
<div v-else-if="weather.error" class="weather-loading">SYNC FAILED</div>
|
||||||
|
<template v-else>
|
||||||
|
<pre class="weather-ascii">{{ weatherIcon }}</pre>
|
||||||
|
<div class="dash-stat"><span>TEMP</span><b>{{ weather.temp }}°C</b></div>
|
||||||
|
<div class="dash-stat"><span>COND</span><b>{{ weatherLabel }}</b></div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="dash-panel">
|
<div class="dash-panel">
|
||||||
<h4>SESSION</h4>
|
<h4>SESSION</h4>
|
||||||
<div class="dash-stat"><span>USER</span><b>{{ isLoggedIn ? currentUsername : 'anonymous' }}</b></div>
|
<div class="dash-stat"><span>USER</span><b>{{ isLoggedIn ? currentUsername : 'anonymous' }}</b></div>
|
||||||
@@ -271,9 +282,77 @@ const showSystemAlert = (message) => {
|
|||||||
const todayUnique = ref(0);
|
const todayUnique = ref(0);
|
||||||
const activeNow = ref(0);
|
const activeNow = ref(0);
|
||||||
let analyticsTimer = null;
|
let analyticsTimer = null;
|
||||||
|
let weatherTimer = null;
|
||||||
|
|
||||||
const currentServerSource = ref('nas');
|
const currentServerSource = ref('nas');
|
||||||
|
|
||||||
|
const WEATHER_ICONS = {
|
||||||
|
clear: " \\ /\n .-.\n ― ( ) ―\n `-'\n / \\",
|
||||||
|
cloudy: " .--.\n .-( ).\n (___.__)__)",
|
||||||
|
overcast: " .--.\n .-( ).\n (___.__)__)",
|
||||||
|
fog: " _ - _ - _\n_ - _ - _ -\n _ - _ - _",
|
||||||
|
drizzle: " .-.\n ( ).\n (___(__)\n ' ' ' '",
|
||||||
|
rain: " .-.\n ( ).\n (___(__)\n ' ' ' ' '",
|
||||||
|
snow: " .-.\n ( ).\n (___(__)\n * * *",
|
||||||
|
thunder: " .-.\n ( ).\n (___(__)\n ⚡ ' ⚡"
|
||||||
|
};
|
||||||
|
|
||||||
|
const WEATHER_LABELS = {
|
||||||
|
clear: '맑음',
|
||||||
|
cloudy: '구름 조금',
|
||||||
|
overcast: '흐림',
|
||||||
|
fog: '안개',
|
||||||
|
drizzle: '이슬비',
|
||||||
|
rain: '비',
|
||||||
|
snow: '눈',
|
||||||
|
thunder: '뇌우'
|
||||||
|
};
|
||||||
|
|
||||||
|
const getWeatherIconKey = (code) => {
|
||||||
|
if (code === 0) return 'clear';
|
||||||
|
if (code === 1 || code === 2) return 'cloudy';
|
||||||
|
if (code === 3) return 'overcast';
|
||||||
|
if (code === 45 || code === 48) return 'fog';
|
||||||
|
if ([51, 53, 55, 56, 57].includes(code)) return 'drizzle';
|
||||||
|
if ([61, 63, 65, 66, 67, 80, 81, 82].includes(code)) return 'rain';
|
||||||
|
if ([71, 73, 75, 77, 85, 86].includes(code)) return 'snow';
|
||||||
|
if ([95, 96, 99].includes(code)) return 'thunder';
|
||||||
|
return 'cloudy';
|
||||||
|
};
|
||||||
|
|
||||||
|
const weather = ref({ loading: true, error: false, temp: null, iconKey: 'clear' });
|
||||||
|
const weatherIcon = computed(() => WEATHER_ICONS[weather.value.iconKey] || WEATHER_ICONS.cloudy);
|
||||||
|
const weatherLabel = computed(() => WEATHER_LABELS[weather.value.iconKey] || '');
|
||||||
|
|
||||||
|
const fetchWeatherData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await axios.get('https://api.open-meteo.com/v1/forecast', {
|
||||||
|
params: {
|
||||||
|
latitude: 37.5665,
|
||||||
|
longitude: 126.9780,
|
||||||
|
current_weather: true,
|
||||||
|
timezone: 'Asia/Seoul'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const current = res.data && res.data.current_weather;
|
||||||
|
if (current) {
|
||||||
|
weather.value = {
|
||||||
|
loading: false,
|
||||||
|
error: false,
|
||||||
|
temp: Math.round(current.temperature),
|
||||||
|
iconKey: getWeatherIconKey(current.weathercode)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
weather.value.loading = false;
|
||||||
|
weather.value.error = true;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("날씨 데이터 로드 실패:", err);
|
||||||
|
weather.value.loading = false;
|
||||||
|
weather.value.error = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ id: 'main', name: 'HOME' },
|
{ id: 'main', name: 'HOME' },
|
||||||
{ id: 'youtube', name: 'YOUTUBE' },
|
{ id: 'youtube', name: 'YOUTUBE' },
|
||||||
@@ -499,6 +578,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
fetchVideos();
|
fetchVideos();
|
||||||
fetchAnalyticsData();
|
fetchAnalyticsData();
|
||||||
|
fetchWeatherData();
|
||||||
|
|
||||||
detectRealtimeServerSource();
|
detectRealtimeServerSource();
|
||||||
|
|
||||||
@@ -507,6 +587,8 @@ onMounted(() => {
|
|||||||
detectRealtimeServerSource();
|
detectRealtimeServerSource();
|
||||||
}, 30000);
|
}, 30000);
|
||||||
|
|
||||||
|
weatherTimer = setInterval(fetchWeatherData, 600000);
|
||||||
|
|
||||||
const globaljQuery = window.$;
|
const globaljQuery = window.$;
|
||||||
if (globaljQuery && terminalRef.value) {
|
if (globaljQuery && terminalRef.value) {
|
||||||
termInstance.value = globaljQuery(terminalRef.value).terminal({
|
termInstance.value = globaljQuery(terminalRef.value).terminal({
|
||||||
@@ -595,5 +677,6 @@ onMounted(() => {
|
|||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (analyticsTimer) clearInterval(analyticsTimer);
|
if (analyticsTimer) clearInterval(analyticsTimer);
|
||||||
|
if (weatherTimer) clearInterval(weatherTimer);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user