Skip to content

Commit 000a854

Browse files
committed
refactor: restructure page layout for better readability and maintainability
1 parent 1428a9e commit 000a854

File tree

5 files changed

+196
-193
lines changed

5 files changed

+196
-193
lines changed

frontend/src/assets/main.less

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ a {
8989
padding-top: 4px !important;
9090
}
9191

92+
.py-2 {
93+
padding-top: 2px !important;
94+
padding-bottom: 2px !important;
95+
}
96+
97+
.px-8 {
98+
padding-left: 8px !important;
99+
padding-right: 8px !important;
100+
}
101+
92102
.mt-4 {
93103
margin-top: 4px !important;
94104
}
@@ -133,6 +143,10 @@ a {
133143
width: 100% !important;
134144
}
135145

146+
.rounded-8 {
147+
border-radius: 8px;
148+
}
149+
136150
.flex-1 {
137151
flex: 1;
138152
}

frontend/src/components/Modal/index.vue

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -64,30 +64,32 @@ provide('submit', handleSubmit)
6464
</script>
6565

6666
<template>
67-
<Transition name="modal" :duration="200">
68-
<div v-if="open" @click.self="onMaskClick" class="mask" style="--wails-draggable: drag">
69-
<div :style="contentStyle" class="modal" style="--wails-draggable: false">
70-
<div
71-
v-if="title"
72-
@dblclick="WindowToggleMaximise"
73-
class="title"
74-
style="--wails-draggable: drag"
75-
>
76-
{{ t(title) }}
77-
</div>
78-
<div class="content">
79-
<slot />
80-
</div>
81-
<div v-if="footer" class="action">
82-
<slot name="action" />
83-
<Button v-if="cancel" @click="handleCancel" :type="maskClosable ? 'text' : 'normal'">
84-
{{ t(cancelText) }}
85-
</Button>
86-
<Button v-if="submit" @click="handleSubmit" type="primary">{{ t(submitText) }}</Button>
67+
<Teleport to="body">
68+
<Transition name="modal" :duration="200">
69+
<div v-if="open" @click.self="onMaskClick" class="mask" style="--wails-draggable: drag">
70+
<div :style="contentStyle" class="modal" style="--wails-draggable: false">
71+
<div
72+
v-if="title"
73+
@dblclick="WindowToggleMaximise"
74+
class="title"
75+
style="--wails-draggable: drag"
76+
>
77+
{{ t(title) }}
78+
</div>
79+
<div class="content">
80+
<slot />
81+
</div>
82+
<div v-if="footer" class="action">
83+
<slot name="action" />
84+
<Button v-if="cancel" @click="handleCancel" :type="maskClosable ? 'text' : 'normal'">
85+
{{ t(cancelText) }}
86+
</Button>
87+
<Button v-if="submit" @click="handleSubmit" type="primary">{{ t(submitText) }}</Button>
88+
</div>
8789
</div>
8890
</div>
89-
</div>
90-
</Transition>
91+
</Transition>
92+
</Teleport>
9193
</template>
9294

9395
<style lang="less" scoped>

frontend/src/views/HomeView/components/GroupsController.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ onActivated(() => {
192192
</script>
193193

194194
<template>
195-
<div class="groups" style="margin-top: 0">
195+
<div class="groups groups-fixed" style="margin-top: 0">
196196
<div class="header">
197197
<Switch v-model="appSettings.app.kernel.autoClose">
198198
{{ t('home.controller.autoClose') }}
@@ -318,13 +318,18 @@ onActivated(() => {
318318
transform: scaleY(0);
319319
}
320320
321+
.groups-fixed {
322+
z-index: 2;
323+
position: sticky;
324+
top: 0;
325+
}
321326
.groups {
322327
margin: 8px;
323328
324329
.header {
325330
position: sticky;
326331
z-index: 1;
327-
top: 0;
332+
top: 56px;
328333
display: flex;
329334
align-items: center;
330335
padding: 8px;

frontend/src/views/HomeView/components/OverView.vue

Lines changed: 142 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
<script setup lang="ts">
22
import { useI18n } from 'vue-i18n'
3-
import { ref, onUnmounted } from 'vue'
3+
import { ref, onUnmounted, h } from 'vue'
44
5-
import { useBool } from '@/hooks'
65
import { getKernelWS } from '@/api/kernel'
7-
import { useKernelApiStore } from '@/stores'
6+
import { useModal } from '@/components/Modal'
7+
import { useEnvStore, useKernelApiStore } from '@/stores'
88
import { ModeOptions } from '@/constant/kernel'
9-
import { formatBytes, handleChangeMode, setIntervalImmediately } from '@/utils'
9+
import { formatBytes, handleChangeMode, message, setIntervalImmediately } from '@/utils'
1010
11+
import LogsController from './LogsController.vue'
12+
import CommonController from './CommonController.vue'
1113
import ConnectionsController from './ConnectionsController.vue'
1214
1315
const trafficHistory = ref<[number[], number[]]>([[], []])
@@ -21,10 +23,88 @@ const statistics = ref({
2123
})
2224
2325
const { t } = useI18n()
26+
const [Modal, modalApi] = useModal({})
27+
const envStore = useEnvStore()
2428
const kernelApiStore = useKernelApiStore()
25-
const [showKernelConnections, toggleKernelConnections] = useBool(false)
2629
27-
const isActiveMode = (mode: string) => kernelApiStore.config.mode === mode
30+
const handleRestartKernel = async () => {
31+
try {
32+
await kernelApiStore.restartKernel()
33+
} catch (error: any) {
34+
console.error(error)
35+
message.error(error)
36+
}
37+
}
38+
39+
const handleStopKernel = async () => {
40+
try {
41+
await kernelApiStore.stopKernel()
42+
} catch (error: any) {
43+
console.error(error)
44+
message.error(error)
45+
}
46+
}
47+
48+
const handleShowApiLogs = () => {
49+
modalApi
50+
.setProps({
51+
title: 'Logs',
52+
cancelText: 'common.close',
53+
width: '90',
54+
height: '90',
55+
submit: false,
56+
maskClosable: true,
57+
})
58+
.setComponent(h(LogsController))
59+
.open()
60+
}
61+
62+
const handleShowApiConnections = () => {
63+
modalApi
64+
.setProps({
65+
title: 'home.overview.connections',
66+
cancelText: 'common.close',
67+
width: '90',
68+
height: '90',
69+
submit: false,
70+
maskClosable: true,
71+
})
72+
.setComponent(h(ConnectionsController))
73+
.open()
74+
}
75+
76+
const handleShowSettings = () => {
77+
modalApi
78+
.setProps({
79+
title: 'home.overview.settings',
80+
cancelText: 'common.close',
81+
width: '90',
82+
submit: false,
83+
maskClosable: true,
84+
})
85+
.setComponent(h(CommonController))
86+
.open()
87+
}
88+
89+
const onTunSwitchChange = async (enable: boolean) => {
90+
try {
91+
await kernelApiStore.updateConfig('tun', { enable })
92+
} catch (error: any) {
93+
kernelApiStore.config.tun.enable = !kernelApiStore.config.tun.enable
94+
console.error(error)
95+
message.error(error)
96+
}
97+
}
98+
99+
const onSystemProxySwitchChange = async (enable: boolean) => {
100+
try {
101+
await envStore.switchSystemProxy(enable)
102+
} catch (error: any) {
103+
console.error(error)
104+
message.error(error)
105+
envStore.systemProxy = !envStore.systemProxy
106+
}
107+
}
28108
29109
const onConnections = (data: any) => {
30110
statistics.value.downloadTotal = data.downloadTotal
@@ -61,7 +141,50 @@ onUnmounted(() => {
61141

62142
<template>
63143
<div class="overview">
64-
<div class="statistics">
144+
<div class="flex items-center rounded-8 px-8 py-2" style="background-color: var(--card-bg)">
145+
<Button @click="handleShowSettings" type="text" size="small" icon="settings" />
146+
<Switch
147+
v-model="envStore.systemProxy"
148+
@change="onSystemProxySwitchChange"
149+
size="small"
150+
border="square"
151+
class="ml-4"
152+
>
153+
{{ t('home.overview.systemProxy') }}
154+
</Switch>
155+
<Switch
156+
v-model="kernelApiStore.config.tun.enable"
157+
@change="onTunSwitchChange"
158+
size="small"
159+
border="square"
160+
class="ml-8"
161+
>
162+
{{ t('home.overview.tunMode') }}
163+
</Switch>
164+
<Button
165+
@click="handleShowApiLogs"
166+
v-tips="'home.overview.viewlog'"
167+
type="text"
168+
size="small"
169+
icon="log"
170+
class="ml-auto"
171+
/>
172+
<Button
173+
@click="handleRestartKernel"
174+
v-tips="'home.overview.restart'"
175+
type="text"
176+
size="small"
177+
icon="restart"
178+
/>
179+
<Button
180+
@click="handleStopKernel"
181+
v-tips="'home.overview.stop'"
182+
type="text"
183+
size="small"
184+
icon="stop"
185+
/>
186+
</div>
187+
<div class="flex justify-between" style="margin-top: 20px; gap: 12px">
65188
<Card :title="t('home.overview.realtimeTraffic')" class="statistics-card">
66189
<div class="detail">
67190
↑ {{ formatBytes(statistics.upload) }}/s ↓ {{ formatBytes(statistics.download) }}/s
@@ -73,9 +196,9 @@ onUnmounted(() => {
73196
</div>
74197
</Card>
75198
<Card
76-
@click="toggleKernelConnections"
199+
@click="handleShowApiConnections"
77200
:title="t('home.overview.connections')"
78-
class="statistics-card"
201+
class="statistics-card cursor-pointer"
79202
>
80203
<div class="detail">
81204
{{ statistics.connections.length }}
@@ -100,48 +223,28 @@ onUnmounted(() => {
100223
<Card
101224
v-for="mode in ModeOptions"
102225
:key="mode.value"
103-
:selected="isActiveMode(mode.value)"
226+
:selected="kernelApiStore.config.mode === mode.value"
104227
@click="handleChangeMode(mode.value as any)"
105228
:title="t(mode.label)"
106229
class="mode-card"
107230
>
108-
<div class="desp">{{ t(mode.desc) }}</div>
231+
<div class="desc">{{ t(mode.desc) }}</div>
109232
</Card>
110233
</div>
111234
</div>
112235
</div>
113236

114-
<Modal
115-
v-model:open="showKernelConnections"
116-
:title="t('home.overview.connections')"
117-
:submit="false"
118-
mask-closable
119-
cancel-text="common.close"
120-
width="90"
121-
height="90"
122-
>
123-
<div @wheel.stop="($event) => 0" style="height: 100%">
124-
<ConnectionsController />
125-
</div>
126-
</Modal>
237+
<Modal />
127238
</template>
128239

129240
<style lang="less" scoped>
130241
.overview {
131-
margin-top: 20px;
132-
.statistics {
133-
display: flex;
134-
justify-content: space-between;
135-
&-card {
136-
width: calc(100% / 4 - 8px);
137-
&:nth-child(3) {
138-
cursor: pointer;
139-
}
140-
.detail {
141-
padding: 4px 0;
142-
font-size: 12px;
143-
line-height: 2;
144-
}
242+
.statistics-card {
243+
flex: 1;
244+
.detail {
245+
padding: 4px 0;
246+
font-size: 12px;
247+
line-height: 2;
145248
}
146249
}
147250
@@ -163,7 +266,7 @@ onUnmounted(() => {
163266
&:nth-child(3) {
164267
margin: 12px 0;
165268
}
166-
.desp {
269+
.desc {
167270
line-height: 20px;
168271
font-size: 12px;
169272
}

0 commit comments

Comments
 (0)