fix: language

This commit is contained in:
王性驊 2025-09-11 22:22:41 +08:00
parent 9039fb1a57
commit 33835376ba
4 changed files with 170 additions and 20 deletions

View File

@ -1,6 +1,7 @@
<script setup>
import Taskbar from '~/components/Taskbar.vue';
import AboutMeWindow from '~/components/AboutMeWindow.vue';
import TerminalWindow from '~/components/TerminalWindow.vue';
import { ref, reactive, onMounted, onUnmounted, computed } from 'vue';
import { useI18n } from 'vue-i18n';
@ -37,7 +38,7 @@ const saveWindowsToLocalStorage = () => {
};
// Function to open a new window or bring an existing one to front
const openWindow = (type, titleKey, initialWidth = 400, initialHeight = 300) => {
const openWindow = (type, titleKey, initialWidth = 600, initialHeight = 400) => {
const existingWindow = windows.find(w => w.type === type);
if (existingWindow) {
bringWindowToFront(existingWindow.id);
@ -51,7 +52,7 @@ const openWindow = (type, titleKey, initialWidth = 400, initialHeight = 300) =>
const desktopWidth = window.innerWidth;
const desktopHeight = window.innerHeight - 40; // Minus taskbar height
const initialX = (desktopWidth - initialWidth) / 2;
const initialY = (desktopHeight - initialHeight) / 2 + 40; // Add taskbar height
const initialY = (desktopHeight - initialHeight) / 2 + 40; // Offset by taskbar height
const newWindow = {
id: generateUniqueId(),
@ -129,6 +130,10 @@ const openAboutMeWindow = () => {
openWindow('about-me', 'about_me'); // Pass the translation key
};
const openTerminalWindow = () => {
openWindow('terminal', 'terminal');
};
// Computed property for minimized windows
const minimizedWindows = computed(() => {
return windows.filter(win => win.isMinimized);
@ -145,6 +150,7 @@ onUnmounted(() => {
<Taskbar
@open-about-me="openAboutMeWindow"
@close-all-windows="closeAllWindows"
@open-terminal="openTerminalWindow"
:minimized-windows="minimizedWindows"
@restore-window="restoreWindow"
/>
@ -163,6 +169,15 @@ onUnmounted(() => {
@bring-to-front="bringWindowToFront(window.id)"
@update-position="updateWindowPosition(window.id, $event.x, $event.y)"
/>
<TerminalWindow
v-if="window.type === 'terminal' && window.isVisible && !window.isMinimized"
:window-data="window"
@close="closeWindow(window.id)"
@minimize="minimizeWindow(window.id)"
@restore="restoreWindow(window.id)"
@bring-to-front="bringWindowToFront(window.id)"
@update-position="updateWindowPosition(window.id, $event.x, $event.y)"
/>
</template>
</div>
</template>
@ -190,8 +205,7 @@ body {
}
.desktop-content {
/* Push content down by the height of the taskbar */
margin-top: 40px;
margin-top: 40px; /* Push content down by the height of the taskbar */
height: calc(100vh - 40px); /* Fill remaining height */
width: 100%;
box-sizing: border-box;

View File

@ -6,6 +6,9 @@
<span>{{ t('start') }}</span>
</button>
<div v-if="showStartMenu" class="start-menu">
<div class="menu-item" @click="openTerminal">
{{ t('terminal') }}
</div>
<div class="menu-item" @click="openAboutMe">
{{ t('about_me') }}
</div>
@ -37,7 +40,7 @@
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { ref, onMounted, onUnmounted, watch } from 'vue';
import { useI18n } from 'vue-i18n';
const { t, locale, setLocale } = useI18n();
@ -53,12 +56,17 @@ const isMounted = ref(false);
let timerId = null;
const showStartMenu = ref(false);
const emit = defineEmits(['open-about-me', 'restore-window', 'close-all-windows']);
const emit = defineEmits(['open-about-me', 'restore-window', 'close-all-windows', 'open-terminal']);
const toggleStartMenu = () => {
showStartMenu.value = !showStartMenu.value;
};
const openTerminal = () => {
emit('open-terminal');
showStartMenu.value = false;
};
const openAboutMe = () => {
emit('open-about-me');
showStartMenu.value = false;
@ -79,8 +87,6 @@ const restoreMinimizedWindow = (id) => {
emit('restore-window', id);
};
const updateCurrentTime = () => {
// Map i18n locale to a specific locale for time formatting
const timeLocale = locale.value === 'zh-tw' ? 'zh-TW' : 'en-US';
@ -109,8 +115,8 @@ onUnmounted(() => {
align-items: center;
background-color: #c0c0c0; /* Classic Windows gray */
padding: 4px 6px;
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff; /* 3D effect */
border-top: 2px solid #ffffff;
border-bottom: 2px solid #808080;
width: 100%;
box-sizing: border-box;
height: 40px;
@ -179,7 +185,7 @@ onUnmounted(() => {
.start-menu {
position: absolute;
top: 100%;
top: 100%; /* Opens downwards from the taskbar */
left: 0;
background-color: #c0c0c0;
border: 2px solid;

View File

@ -0,0 +1,136 @@
<template>
<div
class="window-wrapper"
:style="{ top: windowData.y + 'px', left: windowData.x + 'px', width: windowData.width + 'px', height: windowData.height + 'px', zIndex: windowData.zIndex }"
@mousedown="bringToFront"
>
<div class="title-bar" @mousedown.prevent="startDrag">
<span class="title">{{ t(windowData.title) }}</span>
<div class="window-controls">
<button @click.stop="minimize">_</button>
<button @click.stop="close">X</button>
</div>
</div>
<div class="content">
<div class="terminal-section">
<span class="prompt">guest@daniels-mac:~$</span>
<input
v-model="command"
type="text"
class="terminal-input"
@keydown.enter="runCommand"
placeholder="Terminal is for display only"
autofocus
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const props = defineProps({ windowData: Object });
const emit = defineEmits(['close', 'minimize', 'bring-to-front', 'update-position']);
const command = ref('');
const runCommand = () => {
console.log(`Command executed: ${command.value}`);
command.value = '';
};
const close = () => emit('close');
const minimize = () => emit('minimize');
const bringToFront = () => emit('bring-to-front');
const startDrag = (event) => {
const startX = event.clientX;
const startY = event.clientY;
const initialX = props.windowData.x;
const initialY = props.windowData.y;
const onMouseMove = (moveEvent) => {
const newX = initialX + (moveEvent.clientX - startX);
const newY = initialY + (moveEvent.clientY - startY);
emit('update-position', { x: newX, y: newY });
};
const onMouseUp = () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
};
</script>
<style scoped>
.window-wrapper {
position: absolute;
background-color: #c0c0c0; /* Windows 95 gray */
border: 2px solid;
border-color: #ffffff #808080 #808080 #ffffff;
box-shadow: 2px 2px 5px rgba(0,0,0,0.5);
display: flex;
flex-direction: column;
}
.title-bar {
background-color: #000080; /* Windows 95 blue */
color: white;
padding: 4px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
font-weight: bold;
}
.window-controls button {
background-color: #c0c0c0;
border: 1px solid;
border-color: #ffffff #808080 #808080 #ffffff;
font-weight: bold;
margin-left: 2px;
min-width: 20px;
}
.content {
padding: 10px;
flex-grow: 1;
background-color: black;
color: #34d399; /* green-400 */
font-family: 'Courier New', Courier, monospace;
}
.terminal-section {
display: flex;
align-items: center;
}
.prompt {
color: #fbbF24; /* yellow-400 */
margin-right: 8px;
white-space: nowrap;
}
.terminal-input {
background: transparent;
border: none;
color: #34d399; /* green-400 */
width: 100%;
font-family: 'Courier New', Courier, monospace;
}
.terminal-input:focus {
outline: none;
}
.terminal-input::placeholder {
color: #6b7280; /* gray-500 */
}
</style>

View File

@ -2,25 +2,19 @@
<div class="desktop">
<div class="icon">
<div class="icon-image">[PC]</div>
<div class="icon-label">{{ t('my_computer') }}</div>
<div class="icon-label">{{ $t('my_computer') }}</div>
</div>
<div class="icon">
<div class="icon-image">[BIN]</div>
<div class="icon-label">{{ t('recycle_bin') }}</div>
<div class="icon-label">{{ $t('recycle_bin') }}</div>
</div>
<div class="icon">
<div class="icon-image">[>]_</div>
<div class="icon-label">{{ t('terminal') }}</div>
<div class="icon-label">{{ $t('terminal') }}</div>
</div>
</div>
</template>
<script setup>
import { useI18n } from 'vue-i18n'; // Import useI18n
const { t } = useI18n(); // Use useI18n composable
</script>
<style scoped>
.desktop {
display: flex;