Skip to content

Commit 6cfa09d

Browse files
committed
feat: 支持拖拽调整窗口
1 parent 986a239 commit 6cfa09d

3 files changed

Lines changed: 181 additions & 36 deletions

File tree

src-tauri/src/config.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ impl Default for AppConfig {
5959
space_dot_omission: Some(false),
6060
}),
6161
environment_mirror: Some(EnvironmentMirrorConfig {
62-
enabled: Some(true),
62+
enabled: Some(false),
6363
base_url: Some("http://cdn.global.devlive.top".to_string()),
6464
fallback_enabled: Some(false),
6565
}),
@@ -129,7 +129,7 @@ impl ConfigManager {
129129
// 检查并设置 environment_mirror 默认配置
130130
if config.environment_mirror.is_none() {
131131
config.environment_mirror = Some(EnvironmentMirrorConfig {
132-
enabled: Some(true),
132+
enabled: Some(false),
133133
base_url: Some("http://cdn.global.devlive.top".to_string()),
134134
fallback_enabled: Some(false),
135135
});
@@ -242,7 +242,7 @@ impl ConfigManager {
242242
space_dot_omission: Some(false),
243243
}),
244244
environment_mirror: Some(EnvironmentMirrorConfig {
245-
enabled: Some(true),
245+
enabled: Some(false),
246246
base_url: Some("http://cdn.global.devlive.top".to_string()),
247247
fallback_enabled: Some(false),
248248
}),

src/App.vue

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,43 +12,49 @@
1212
@load-example="loadExample">
1313
</AppHeader>
1414

15-
<div class="flex-1 flex overflow-hidden">
16-
<!-- 代码编辑器 -->
17-
<div class="flex-1 flex flex-col overflow-hidden">
18-
<div class="bg-gray-100 px-4 py-2 border-b border-gray-200 flex items-center justify-between flex-shrink-0">
19-
<div class="flex items-center space-x-3">
20-
<img :src="`/icons/${currentLanguage.replace(/\d+$/, '')}.svg`" class="w-5 h-5" :alt="currentLanguage"/>
21-
<h2 class="text-sm font-medium text-gray-700">{{ getLanguageDisplayName(currentLanguage) }} 代码编辑器</h2>
15+
<div class="flex-1 overflow-hidden">
16+
<ResizablePanels :min-left-width="400" :min-right-width="300">
17+
<template #left>
18+
<!-- 代码编辑器 -->
19+
<div class="h-full flex flex-col overflow-hidden">
20+
<div class="bg-gray-100 px-4 py-2 border-b border-gray-200 flex items-center justify-between flex-shrink-0">
21+
<div class="flex items-center space-x-3">
22+
<img :src="`/icons/${currentLanguage.replace(/\d+$/, '')}.svg`" class="w-5 h-5" :alt="currentLanguage"/>
23+
<h2 class="text-sm font-medium text-gray-700">{{ getLanguageDisplayName(currentLanguage) }} 代码编辑器</h2>
24+
</div>
25+
26+
<div class="flex items-center space-x-2 text-xs text-gray-500">
27+
<span><strong>{{ (code || '').length }}</strong> 字符</span>
28+
<span><strong>{{ (code || '').split('\n').length }}</strong> 行</span>
29+
</div>
30+
</div>
31+
<div class="flex-1 overflow-hidden">
32+
<CodeEditor v-model="code" class="h-full" :language="currentLanguage" :editor-config="editorConfig" :key="editorConfigKey"/>
33+
</div>
2234
</div>
23-
24-
<div class="flex items-center space-x-2 text-xs text-gray-500">
25-
<span><strong>{{ (code || '').length }}</strong> 字符</span>
26-
<span><strong>{{ (code || '').split('\n').length }}</strong> 行</span>
27-
</div>
28-
</div>
29-
<div class="flex-1 overflow-hidden">
30-
<CodeEditor v-model="code" class="h-full" :language="currentLanguage" :editor-config="editorConfig" :key="editorConfigKey"/>
31-
</div>
32-
</div>
33-
34-
<!-- 输出 -->
35-
<div class="w-2/5 flex flex-col border-l border-gray-200">
36-
<ConsoleOutput v-if="consoleType === 'console'"
35+
</template>
36+
37+
<template #right>
38+
<!-- 输出 -->
39+
<div class="h-full flex flex-col border-l border-gray-200">
40+
<ConsoleOutput v-if="consoleType === 'console'"
41+
class="flex-1"
42+
:output="output"
43+
:is-running="isRunning"
44+
:is-success="isSuccess"
45+
:execution-time="lastExecutionTime">
46+
</ConsoleOutput>
47+
48+
<!-- Web输出组件 -->
49+
<WebOutput v-else-if="consoleType === 'web'"
3750
class="flex-1"
38-
:output="output"
51+
:web-content="output"
3952
:is-running="isRunning"
40-
:is-success="isSuccess"
4153
:execution-time="lastExecutionTime">
42-
</ConsoleOutput>
43-
44-
<!-- Web输出组件 -->
45-
<WebOutput v-else-if="consoleType === 'web'"
46-
class="flex-1"
47-
:web-content="output"
48-
:is-running="isRunning"
49-
:execution-time="lastExecutionTime">
50-
</WebOutput>
51-
</div>
54+
</WebOutput>
55+
</div>
56+
</template>
57+
</ResizablePanels>
5258
</div>
5359

5460
<!-- 状态栏 -->
@@ -78,6 +84,7 @@ import StatusBar from './components/StatusBar.vue'
7884
import About from './components/About.vue'
7985
import Settings from './components/Settings.vue'
8086
import Toast from './components/Toast.vue'
87+
import ResizablePanels from './components/ResizablePanels.vue'
8188
import {useToast} from './plugins/toast'
8289
8390
// Composables

src/components/ResizablePanels.vue

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<template>
2+
<div ref="containerRef" class="flex overflow-hidden h-full">
3+
<!-- 左侧面板 -->
4+
<div :style="{ width: `${leftWidth}px` }" class="overflow-hidden">
5+
<slot name="left"></slot>
6+
</div>
7+
8+
<!-- 拖拽分隔条 -->
9+
<div
10+
class="relative w-1 bg-gray-200 hover:bg-blue-500 cursor-col-resize transition-colors flex-shrink-0 group"
11+
@mousedown="startResize"
12+
@touchstart="startResize">
13+
<div class="absolute inset-y-0 -left-1 -right-1"></div>
14+
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity">
15+
<div class="w-1 h-8 bg-blue-500 rounded-full"></div>
16+
</div>
17+
</div>
18+
19+
<!-- 右侧面板 -->
20+
<div class="flex-1 overflow-hidden">
21+
<slot name="right"></slot>
22+
</div>
23+
</div>
24+
</template>
25+
26+
<script setup lang="ts">
27+
import { onMounted, onUnmounted, ref } from 'vue'
28+
29+
interface Props {
30+
minLeftWidth?: number
31+
minRightWidth?: number
32+
defaultLeftWidth?: number
33+
}
34+
35+
const props = withDefaults(defineProps<Props>(), {
36+
minLeftWidth: 300,
37+
minRightWidth: 300,
38+
defaultLeftWidth: 0
39+
})
40+
41+
const containerRef = ref<HTMLElement | null>(null)
42+
const leftWidth = ref(0)
43+
const isResizing = ref(false)
44+
45+
// 初始化左侧宽度
46+
onMounted(() => {
47+
if (containerRef.value) {
48+
const containerWidth = containerRef.value.clientWidth
49+
if (props.defaultLeftWidth > 0) {
50+
leftWidth.value = props.defaultLeftWidth
51+
} else {
52+
// 默认左侧占 60%
53+
leftWidth.value = Math.floor(containerWidth * 0.6)
54+
}
55+
56+
// 从 localStorage 读取保存的宽度
57+
const savedWidth = localStorage.getItem('resizable-panels-left-width')
58+
if (savedWidth) {
59+
const width = parseInt(savedWidth, 10)
60+
if (width >= props.minLeftWidth && width <= containerWidth - props.minRightWidth) {
61+
leftWidth.value = width
62+
}
63+
}
64+
}
65+
66+
window.addEventListener('resize', handleWindowResize)
67+
})
68+
69+
onUnmounted(() => {
70+
window.removeEventListener('resize', handleWindowResize)
71+
})
72+
73+
// 窗口大小改变时调整面板宽度
74+
const handleWindowResize = () => {
75+
if (containerRef.value) {
76+
const containerWidth = containerRef.value.clientWidth
77+
const maxLeftWidth = containerWidth - props.minRightWidth
78+
79+
if (leftWidth.value > maxLeftWidth) {
80+
leftWidth.value = maxLeftWidth
81+
} else if (leftWidth.value < props.minLeftWidth) {
82+
leftWidth.value = props.minLeftWidth
83+
}
84+
}
85+
}
86+
87+
// 开始调整大小
88+
const startResize = (e: MouseEvent | TouchEvent) => {
89+
e.preventDefault()
90+
isResizing.value = true
91+
92+
// 添加全局事件监听
93+
document.addEventListener('mousemove', handleResize)
94+
document.addEventListener('mouseup', stopResize)
95+
document.addEventListener('touchmove', handleResize)
96+
document.addEventListener('touchend', stopResize)
97+
98+
// 添加选择禁用样式
99+
document.body.style.userSelect = 'none'
100+
document.body.style.cursor = 'col-resize'
101+
}
102+
103+
// 调整大小
104+
const handleResize = (e: MouseEvent | TouchEvent) => {
105+
if (!isResizing.value || !containerRef.value) return
106+
107+
const containerRect = containerRef.value.getBoundingClientRect()
108+
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX
109+
110+
let newLeftWidth = clientX - containerRect.left
111+
112+
// 限制最小/最大宽度
113+
const maxLeftWidth = containerRect.width - props.minRightWidth
114+
newLeftWidth = Math.max(props.minLeftWidth, Math.min(newLeftWidth, maxLeftWidth))
115+
116+
leftWidth.value = newLeftWidth
117+
}
118+
119+
// 停止调整大小
120+
const stopResize = () => {
121+
if (isResizing.value) {
122+
isResizing.value = false
123+
124+
// 移除全局事件监听
125+
document.removeEventListener('mousemove', handleResize)
126+
document.removeEventListener('mouseup', stopResize)
127+
document.removeEventListener('touchmove', handleResize)
128+
document.removeEventListener('touchend', stopResize)
129+
130+
// 移除选择禁用样式
131+
document.body.style.userSelect = ''
132+
document.body.style.cursor = ''
133+
134+
// 保存宽度到 localStorage
135+
localStorage.setItem('resizable-panels-left-width', leftWidth.value.toString())
136+
}
137+
}
138+
</script>

0 commit comments

Comments
 (0)