Files
WeChatDataAnalysis/frontend/components/DesktopTitleBar.vue
2977094657 6eb161c726 feat(desktop): 新增 Electron 桌面端壳与自绘标题栏
- 新增 desktop/ Electron 工程:启动后端并等待 /api/health,就绪后加载页面;打包模式从 extraResources 读取 UI/后端
- 新增 DesktopTitleBar 组件,适配 frame:false 自绘标题栏,并修复桌面端 100vh 布局导致的外层滚动条
- chat 页面右侧布局调整更接近原生微信;detection-result 调试输出仅在 dev 环境启用
- .gitignore 忽略 desktop 构建产物/依赖,保留 .gitkeep 占位文件
- README 补充 Windows 桌面端 EXE 打包(npm run dist)与产物路径说明
2026-01-17 18:23:52 +08:00

164 lines
3.4 KiB
Vue

<template>
<div v-if="isDesktop" class="desktop-titlebar" @dblclick="toggleMaximize">
<div class="flex-1" />
<div class="desktop-titlebar-controls">
<button
class="desktop-titlebar-btn"
type="button"
aria-label="最小化"
title="最小化"
@click="minimize"
>
<span class="desktop-titlebar-icon desktop-titlebar-icon-minimize" />
</button>
<button
class="desktop-titlebar-btn"
type="button"
aria-label="最大化"
title="最大化"
@click="toggleMaximize"
>
<span class="desktop-titlebar-icon desktop-titlebar-icon-maximize" />
</button>
<button
class="desktop-titlebar-btn desktop-titlebar-btn-close"
type="button"
aria-label="关闭"
title="关闭"
@click="closeWindow"
>
<span class="desktop-titlebar-icon desktop-titlebar-icon-close" />
</button>
</div>
</div>
</template>
<script setup>
// Keep SSR/client initial DOM consistent; enable desktop titlebar after mount.
const isDesktop = ref(false)
onMounted(() => {
isDesktop.value = !!window?.wechatDesktop
})
const minimize = () => {
window.wechatDesktop?.minimize?.()
}
const toggleMaximize = () => {
window.wechatDesktop?.toggleMaximize?.()
}
const closeWindow = () => {
window.wechatDesktop?.close?.()
}
</script>
<style scoped>
.desktop-titlebar {
height: var(--desktop-titlebar-height, 32px);
background: #ededed;
display: flex;
align-items: stretch;
flex-shrink: 0;
/* Allow dragging the window from the title bar area */
-webkit-app-region: drag;
user-select: none;
}
.desktop-titlebar-controls {
display: flex;
align-items: stretch;
/* Ensure buttons remain clickable */
-webkit-app-region: no-drag;
}
.desktop-titlebar-btn {
width: var(--desktop-titlebar-btn-width, 46px);
height: var(--desktop-titlebar-height, 32px);
display: inline-flex;
align-items: center;
justify-content: center;
background: transparent;
border: 0;
padding: 0;
margin: 0;
cursor: default;
}
.desktop-titlebar-btn:hover {
background: rgba(0, 0, 0, 0.06);
}
.desktop-titlebar-btn:active {
background: rgba(0, 0, 0, 0.1);
}
.desktop-titlebar-btn-close:hover {
background: #e81123;
}
.desktop-titlebar-btn-close:active {
background: #c50f1f;
}
.desktop-titlebar-icon {
display: inline-block;
width: 12px;
height: 12px;
position: relative;
}
.desktop-titlebar-icon-minimize::before {
content: "";
position: absolute;
left: 1px;
right: 1px;
/* Optical centering: the glyph was anchored to the bottom, so it looked low. */
top: 5px;
height: 1px;
background: #111;
}
.desktop-titlebar-icon-maximize::before {
content: "";
position: absolute;
left: 2px;
top: 2px;
right: 2px;
bottom: 2px;
border: 1px solid #111;
box-sizing: border-box;
}
.desktop-titlebar-icon-close::before,
.desktop-titlebar-icon-close::after {
content: "";
position: absolute;
left: 1px;
right: 1px;
top: 50%;
height: 1px;
background: #111;
transform-origin: center;
}
.desktop-titlebar-icon-close::before {
transform: translateY(-50%) rotate(45deg);
}
.desktop-titlebar-icon-close::after {
transform: translateY(-50%) rotate(-45deg);
}
.desktop-titlebar-btn-close:hover .desktop-titlebar-icon-close::before,
.desktop-titlebar-btn-close:hover .desktop-titlebar-icon-close::after {
background: #fff;
}
</style>