2025-09-10 23:43:41 +00:00
|
|
|
<template>
|
|
|
|
<div class="taskbar">
|
2025-09-11 17:01:54 +00:00
|
|
|
<div class="start-section" ref="startMenuRef">
|
2025-09-10 23:43:41 +00:00
|
|
|
<button class="start-button" @click="toggleStartMenu">
|
2025-09-11 16:50:28 +00:00
|
|
|
<img src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIGZpbGw9IiNjMGMwYzAiLz48cmVjdCB4PSIyIiB5PSIyIiB3aWR0aD0iNyIgaGVpZ2h0PSI3IiBmaWxsPSIjMDAwMDgwIi8+PHJlY3QgeD0iMiIgeT0iMTEiIHdpZHRoPSI3IiBoZWlnaHQ9IjciIGZpbGw9IiMwMDAwODAiLz48cmVjdCB4PSIxMSIgeT0iMiIgd2lkdGg9IjciIGhlaWdodD0iNyIgZmlsbD0iIzAwMDA4MCIvPjxyZWN0IHg9IjExIiB5PSIxMSIgd2lkdGg9IjciIGhlaWdodD0iNyIgZmlsbD0iIzAwMDA4MCIvPjwvc3ZnPg==" alt="Start" class="start-icon" />
|
2025-09-11 17:01:54 +00:00
|
|
|
<span>{{ t('start') }}</span>
|
2025-09-10 23:43:41 +00:00
|
|
|
</button>
|
2025-09-11 17:01:54 +00:00
|
|
|
|
2025-09-10 23:43:41 +00:00
|
|
|
<div v-if="showStartMenu" class="start-menu">
|
2025-09-11 16:50:28 +00:00
|
|
|
<div
|
|
|
|
v-for="item in menuItems"
|
|
|
|
:key="item.labelKey"
|
|
|
|
class="menu-item"
|
|
|
|
@click="handleMenuClick(item.action)"
|
|
|
|
>
|
|
|
|
<img :src="item.icon" class="menu-icon" />
|
|
|
|
<span>{{ t(item.labelKey) }}</span>
|
2025-09-10 23:43:41 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="task-buttons">
|
|
|
|
<button
|
|
|
|
v-for="window in minimizedWindows"
|
|
|
|
:key="window.id"
|
|
|
|
class="task-button"
|
|
|
|
@click="restoreMinimizedWindow(window.id)"
|
|
|
|
>
|
|
|
|
{{ t(window.title) }}
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<div class="right-section">
|
|
|
|
<div class="language-toggle" @click="toggleLanguage">
|
2025-09-11 11:35:49 +00:00
|
|
|
<span>{{ locale === 'zh-tw' ? '注音' : 'ABC' }}</span>
|
2025-09-10 23:43:41 +00:00
|
|
|
</div>
|
|
|
|
<div class="clock">
|
2025-09-11 11:35:49 +00:00
|
|
|
<span v-if="isMounted">{{ currentTime }}</span>
|
2025-09-10 23:43:41 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
2025-09-11 14:22:41 +00:00
|
|
|
import { ref, onMounted, onUnmounted, watch } from 'vue';
|
2025-09-10 23:43:41 +00:00
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
|
2025-09-11 11:35:49 +00:00
|
|
|
const { t, locale, setLocale } = useI18n();
|
2025-09-11 16:50:28 +00:00
|
|
|
|
2025-09-11 11:35:49 +00:00
|
|
|
defineProps({
|
2025-09-11 16:50:28 +00:00
|
|
|
minimizedWindows: { type: Array, default: () => [] },
|
|
|
|
menuItems: { type: Array, default: () => [] },
|
2025-09-10 23:43:41 +00:00
|
|
|
});
|
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
const emit = defineEmits(['restore-window', 'menu-action']);
|
|
|
|
|
2025-09-11 11:45:25 +00:00
|
|
|
const currentTime = ref('');
|
2025-09-11 11:35:49 +00:00
|
|
|
const isMounted = ref(false);
|
2025-09-10 23:43:41 +00:00
|
|
|
let timerId = null;
|
|
|
|
const showStartMenu = ref(false);
|
2025-09-11 17:01:54 +00:00
|
|
|
const startMenuRef = ref(null); // Ref for the start-section div
|
2025-09-10 23:43:41 +00:00
|
|
|
|
|
|
|
const toggleStartMenu = () => {
|
|
|
|
showStartMenu.value = !showStartMenu.value;
|
|
|
|
};
|
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
const handleMenuClick = (action) => {
|
|
|
|
emit('menu-action', action);
|
2025-09-10 23:43:41 +00:00
|
|
|
showStartMenu.value = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
const toggleLanguage = () => {
|
2025-09-11 11:35:49 +00:00
|
|
|
const newLocale = locale.value === 'zh-tw' ? 'en' : 'zh-tw';
|
|
|
|
locale.value = newLocale;
|
|
|
|
setLocale(newLocale);
|
2025-09-10 23:43:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const restoreMinimizedWindow = (id) => {
|
|
|
|
emit('restore-window', id);
|
|
|
|
};
|
|
|
|
|
2025-09-11 11:45:25 +00:00
|
|
|
const updateCurrentTime = () => {
|
|
|
|
const timeLocale = locale.value === 'zh-tw' ? 'zh-TW' : 'en-US';
|
|
|
|
currentTime.value = new Date().toLocaleTimeString(timeLocale);
|
|
|
|
};
|
2025-09-11 16:50:28 +00:00
|
|
|
|
2025-09-11 17:01:54 +00:00
|
|
|
// Function to handle clicks outside the start menu
|
|
|
|
const handleClickOutside = (event) => {
|
|
|
|
if (startMenuRef.value && !startMenuRef.value.contains(event.target)) {
|
|
|
|
if (showStartMenu.value) {
|
|
|
|
showStartMenu.value = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2025-09-11 11:45:25 +00:00
|
|
|
watch(locale, updateCurrentTime);
|
|
|
|
|
2025-09-10 23:43:41 +00:00
|
|
|
onMounted(() => {
|
2025-09-11 11:35:49 +00:00
|
|
|
isMounted.value = true;
|
2025-09-11 16:50:28 +00:00
|
|
|
updateCurrentTime();
|
2025-09-11 11:45:25 +00:00
|
|
|
timerId = setInterval(updateCurrentTime, 1000);
|
2025-09-11 17:01:54 +00:00
|
|
|
document.addEventListener('click', handleClickOutside); // Add event listener
|
2025-09-10 23:43:41 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
if (timerId) {
|
|
|
|
clearInterval(timerId);
|
|
|
|
}
|
2025-09-11 17:01:54 +00:00
|
|
|
document.removeEventListener('click', handleClickOutside); // Remove event listener
|
2025-09-10 23:43:41 +00:00
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
.taskbar {
|
|
|
|
display: flex;
|
|
|
|
justify-content: space-between;
|
|
|
|
align-items: center;
|
2025-09-11 16:50:28 +00:00
|
|
|
background-color: #c0c0c0;
|
2025-09-10 23:43:41 +00:00
|
|
|
padding: 4px 6px;
|
2025-09-11 14:22:41 +00:00
|
|
|
border-top: 2px solid #ffffff;
|
|
|
|
border-bottom: 2px solid #808080;
|
2025-09-10 23:43:41 +00:00
|
|
|
width: 100%;
|
|
|
|
box-sizing: border-box;
|
|
|
|
height: 40px;
|
2025-09-11 16:50:28 +00:00
|
|
|
position: relative;
|
2025-09-10 23:43:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.start-section {
|
|
|
|
position: relative;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
}
|
|
|
|
|
|
|
|
.start-button {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
padding: 4px 8px;
|
|
|
|
border: 2px solid;
|
|
|
|
border-color: #ffffff #808080 #808080 #ffffff;
|
|
|
|
background-color: #c0c0c0;
|
|
|
|
font-weight: bold;
|
|
|
|
font-size: 1rem;
|
|
|
|
cursor: pointer;
|
|
|
|
color: black;
|
|
|
|
}
|
|
|
|
|
|
|
|
.start-button:active {
|
|
|
|
border-color: #808080 #ffffff #ffffff #808080;
|
|
|
|
}
|
|
|
|
|
|
|
|
.start-icon {
|
|
|
|
margin-right: 8px;
|
2025-09-11 16:50:28 +00:00
|
|
|
width: 20px;
|
|
|
|
height: 20px;
|
2025-09-10 23:43:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.start-menu {
|
|
|
|
position: absolute;
|
2025-09-11 16:50:28 +00:00
|
|
|
top: 100%;
|
2025-09-10 23:43:41 +00:00
|
|
|
left: 0;
|
|
|
|
background-color: #c0c0c0;
|
|
|
|
border: 2px solid;
|
|
|
|
border-color: #ffffff #808080 #808080 #ffffff;
|
|
|
|
padding: 4px;
|
2025-09-11 16:50:28 +00:00
|
|
|
min-width: 200px;
|
2025-09-10 23:43:41 +00:00
|
|
|
z-index: 1001;
|
|
|
|
}
|
|
|
|
|
|
|
|
.menu-item {
|
2025-09-11 16:50:28 +00:00
|
|
|
padding: 8px 12px;
|
2025-09-10 23:43:41 +00:00
|
|
|
cursor: pointer;
|
|
|
|
white-space: nowrap;
|
|
|
|
border: 1px solid transparent;
|
|
|
|
color: black;
|
2025-09-11 16:50:28 +00:00
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
gap: 10px;
|
2025-09-10 23:43:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
.menu-item:hover {
|
|
|
|
background-color: #000080;
|
|
|
|
color: white;
|
|
|
|
border-color: #000080;
|
|
|
|
}
|
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
.menu-icon {
|
|
|
|
width: 24px;
|
|
|
|
height: 24px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.task-buttons {
|
|
|
|
display: flex;
|
|
|
|
flex-grow: 1;
|
|
|
|
margin-left: 10px;
|
|
|
|
gap: 6px;
|
|
|
|
overflow-x: auto;
|
|
|
|
}
|
|
|
|
|
|
|
|
.task-button {
|
2025-09-10 23:43:41 +00:00
|
|
|
padding: 4px 8px;
|
|
|
|
border: 2px solid;
|
|
|
|
border-color: #808080 #ffffff #ffffff #808080;
|
2025-09-11 16:50:28 +00:00
|
|
|
background-color: #c0c0c0;
|
|
|
|
font-weight: bold;
|
2025-09-10 23:43:41 +00:00
|
|
|
font-size: 0.9rem;
|
|
|
|
cursor: pointer;
|
2025-09-11 16:50:28 +00:00
|
|
|
color: black;
|
|
|
|
white-space: nowrap;
|
|
|
|
flex-shrink: 0;
|
2025-09-10 23:43:41 +00:00
|
|
|
}
|
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
.task-button:active {
|
2025-09-10 23:43:41 +00:00
|
|
|
border-color: #ffffff #808080 #808080 #ffffff;
|
|
|
|
}
|
|
|
|
|
2025-09-11 16:50:28 +00:00
|
|
|
.right-section {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
gap: 6px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.language-toggle, .clock {
|
2025-09-10 23:43:41 +00:00
|
|
|
padding: 4px 8px;
|
|
|
|
border: 2px solid;
|
|
|
|
border-color: #808080 #ffffff #ffffff #808080;
|
|
|
|
font-size: 0.9rem;
|
|
|
|
color: black;
|
2025-09-11 16:50:28 +00:00
|
|
|
cursor: pointer;
|
2025-09-10 23:43:41 +00:00
|
|
|
background-color: #c0c0c0;
|
|
|
|
}
|
2025-09-11 16:50:28 +00:00
|
|
|
|
|
|
|
.language-toggle:active {
|
|
|
|
border-color: #ffffff #808080 #808080 #ffffff;
|
|
|
|
}
|
2025-09-10 23:43:41 +00:00
|
|
|
</style>
|