Files
IM/frontend/web/src/components/Window.vue
T

166 lines
4.3 KiB
Vue

<template>
<div class="desktop-container">
<div class="app-window" :class="{ 'is-maximized': isMaximized }">
<header class="window-header" @dblclick="toggleMaximize">
<div class="header-left">
<div class="app-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
</svg>
</div>
<span class="app-title">IM Connect</span>
</div>
<div class="window-controls">
<button class="control-btn minimize" @click="minimize" title="最小化">
<svg width="10" height="10" viewBox="0 0 10 1">
<rect width="10" height="1" fill="currentColor" />
</svg>
</button>
<button class="control-btn maximize" @click="toggleMaximize" title="最大化/还原">
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.2">
<rect x="1.5" y="1.5" width="7" height="7" v-if="!isMaximized"/>
<path d="M3 1h6v6H3z M1 3h6v6H1z" v-else fill="currentColor" fill-opacity="0.5"/>
</svg>
</button>
<button class="control-btn close" @click="close" title="关闭">
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linecap="round">
<path d="M1 1L9 9M9 1L1 9" />
</svg>
</button>
</div>
</header>
<main class="window-body">
<slot></slot>
</main>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const isMaximized = ref(false)
function minimize() {
console.log('Window: Minimize')
}
function toggleMaximize() {
isMaximized.value = !isMaximized.value
console.log('Window: Maximize/Restore')
}
function close() {
console.log('Window: Close')
}
</script>
<style scoped>
/* 模拟桌面背景 */
.desktop-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
/* 一个高级的模糊背景,衬托窗口 */
background: radial-gradient(circle at 50% 30%, #eef2ff, #e2e8f0);
padding: 20px;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
/* 窗口主体 */
.app-window {
width: 960px;
height: 600px;
/* 响应式限制 */
max-width: 95vw;
max-height: 90vh;
display: flex;
flex-direction: column;
background: #ffffff;
border-radius: 10px; /* 现代圆角 */
/* 精致的窗口阴影:一层边框 + 两层阴影模拟浮起 */
box-shadow:
0 0 0 1px rgba(0, 0, 0, 0.06),
0 8px 20px rgba(0, 0, 0, 0.08),
0 20px 50px rgba(0, 0, 0, 0.12);
overflow: hidden;
transition: width 0.3s, height 0.3s, border-radius 0.3s;
}
/* 最大化状态 */
.app-window.is-maximized {
width: 100vw;
height: 100vh;
max-width: none;
max-height: none;
border-radius: 0;
}
/* 头部 Header */
.window-header {
height: 38px; /* 经典桌面应用高度 */
background: #f8fafc; /* 极浅的灰,区分内容区 */
border-bottom: 1px solid rgba(0,0,0,0.05);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 0 0 12px;
user-select: none;
-webkit-app-region: drag; /* Electron/Tauri 拖拽支持 */
}
.header-left {
display: flex;
align-items: center;
gap: 10px;
color: #475569;
}
.app-icon {
width: 16px;
height: 16px;
color: #6366f1; /* 品牌色 */
}
.app-title {
font-size: 13px;
font-weight: 600;
}
/* 窗口控制按钮 */
.window-controls {
display: flex;
height: 100%;
-webkit-app-region: no-drag;
}
.control-btn {
width: 46px; /* 较宽的点击区域,类似 Win10/11 */
height: 100%;
border: none;
background: transparent;
display: flex;
align-items: center;
justify-content: center;
color: #64748b;
cursor: default;
transition: all 0.2s;
}
.control-btn:hover {
background: rgba(0, 0, 0, 0.05);
color: #1e293b;
}
.control-btn.close:hover {
background: #ef4444;
color: white;
}
/* 内容区 */
.window-body {
flex: 1;
position: relative;
overflow: hidden; /* 确保内容不溢出圆角 */
background: #fff;
}
</style>