Skip to content

Commit e1e39d0

Browse files
committed
feat: initialize Vue.js project with Element Plus and Vite
1 parent 78dc133 commit e1e39d0

7 files changed

Lines changed: 404 additions & 0 deletions

File tree

src/webui/App.vue

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
<template>
2+
<el-container class="main-container">
3+
<el-header class="header">
4+
<el-icon class="logo"><i-ep-setting /></el-icon>
5+
<h2>LLOneBot 配置可视化编辑器</h2>
6+
</el-header>
7+
<el-main>
8+
<el-row :gutter="24" justify="center">
9+
<el-col :xs="24" :sm="22" :md="20" :lg="16" :xl="12">
10+
<el-card shadow="hover" class="config-card">
11+
<el-form :model="form" label-width="160px" size="large" class="config-form">
12+
<el-divider content-position="left"><el-icon><i-ep-cpu /></el-icon> Satori 协议</el-divider>
13+
<el-row :gutter="16" class="satori-row">
14+
<el-col :span="6">
15+
<el-form-item>
16+
<template #label>启用 Satori</template>
17+
<el-switch v-model="form.satori.enable" style="width: 100%;" />
18+
</el-form-item>
19+
</el-col>
20+
<el-col :span="8">
21+
<el-form-item>
22+
<template #label>Satori 端口</template>
23+
<el-input-number v-model="form.satori.port" :min="1" :max="65535" style="width: 100%;" />
24+
</el-form-item>
25+
</el-col>
26+
<el-col :span="10">
27+
<el-form-item>
28+
<template #label>Satori Token</template>
29+
<el-input v-model="form.satori.token" placeholder="Satori Token" clearable style="width: 100%;" />
30+
</el-form-item>
31+
</el-col>
32+
</el-row>
33+
34+
<el-divider content-position="left"><el-icon><i-ep-connection /></el-icon> OneBot 11 协议</el-divider>
35+
<el-row :gutter="16">
36+
<el-col :span="12">
37+
<el-form-item label="启用 OneBot 11">
38+
<el-switch v-model="form.ob11.enable" />
39+
</el-form-item>
40+
</el-col>
41+
<el-col :span="12">
42+
<el-form-item label="OneBot 11 Token">
43+
<el-input v-model="form.ob11.token" placeholder="请输入 Token" clearable />
44+
</el-form-item>
45+
</el-col>
46+
</el-row>
47+
<el-row :gutter="16">
48+
<el-col :span="12">
49+
<el-form-item label="HTTP 端口">
50+
<el-input-number v-model="form.ob11.httpPort" :min="1" :max="65535" />
51+
</el-form-item>
52+
</el-col>
53+
<el-col :span="12">
54+
<el-form-item label="正向 WS 端口">
55+
<el-input-number v-model="form.ob11.wsPort" :min="1" :max="65535" />
56+
</el-form-item>
57+
</el-col>
58+
</el-row>
59+
<el-form-item label="HTTP 上报地址">
60+
<el-input
61+
v-model="httpPostUrlInput"
62+
placeholder="输入后回车添加"
63+
@keyup.enter="addHttpPostUrl"
64+
clearable
65+
/>
66+
<div class="tag-list">
67+
<el-tag
68+
v-for="(url, idx) in form.ob11.httpPostUrls"
69+
:key="url"
70+
closable
71+
@close="removeHttpPostUrl(idx)"
72+
style="margin-right: 4px; margin-top: 4px;"
73+
>{{ url }}</el-tag>
74+
</div>
75+
</el-form-item>
76+
<el-form-item label="HTTP 上报密钥">
77+
<el-input v-model="form.ob11.httpSecret" placeholder="请输入密钥" clearable />
78+
</el-form-item>
79+
<el-form-item label="反向 WS 地址">
80+
<el-input
81+
v-model="wsReverseUrlInput"
82+
placeholder="输入后回车添加"
83+
@keyup.enter="addWsReverseUrl"
84+
clearable
85+
/>
86+
<div class="tag-list">
87+
<el-tag
88+
v-for="(url, idx) in form.ob11.wsReverseUrls"
89+
:key="url"
90+
closable
91+
@close="removeWsReverseUrl(idx)"
92+
style="margin-right: 4px; margin-top: 4px;"
93+
>{{ url }}</el-tag>
94+
</div>
95+
</el-form-item>
96+
<el-row :gutter="16">
97+
<el-col :span="12">
98+
<el-form-item label="启用 HTTP 服务">
99+
<el-switch v-model="form.ob11.enableHttp" />
100+
</el-form-item>
101+
</el-col>
102+
<el-col :span="12">
103+
<el-form-item label="启用 HTTP 上报">
104+
<el-switch v-model="form.ob11.enableHttpPost" />
105+
</el-form-item>
106+
</el-col>
107+
</el-row>
108+
<el-row :gutter="16">
109+
<el-col :span="12">
110+
<el-form-item label="启用正向 WS 服务">
111+
<el-switch v-model="form.ob11.enableWs" />
112+
</el-form-item>
113+
</el-col>
114+
<el-col :span="12">
115+
<el-form-item label="启用反向 WS 服务">
116+
<el-switch v-model="form.ob11.enableWsReverse" />
117+
</el-form-item>
118+
</el-col>
119+
</el-row>
120+
<el-form-item label="消息上报格式">
121+
<el-radio-group v-model="form.ob11.messagePostFormat">
122+
<el-radio label="array">消息段</el-radio>
123+
<el-radio label="string">CQ码</el-radio>
124+
</el-radio-group>
125+
</el-form-item>
126+
<el-row :gutter="16">
127+
<el-col :span="12">
128+
<el-form-item label="启用 HTTP 心跳">
129+
<el-switch v-model="form.ob11.enableHttpHeart" />
130+
</el-form-item>
131+
</el-col>
132+
<el-col :span="12">
133+
<el-form-item label="上报自己消息">
134+
<el-switch v-model="form.ob11.reportSelfMessage" />
135+
</el-form-item>
136+
</el-col>
137+
</el-row>
138+
139+
<el-divider content-position="left"><el-icon><i-ep-setting /></el-icon> 全局设置</el-divider>
140+
<el-row :gutter="16">
141+
<el-col :span="12">
142+
<el-form-item label="心跳间隔 (ms)">
143+
<el-input-number v-model="form.heartInterval" :min="1000" :max="600000" />
144+
</el-form-item>
145+
</el-col>
146+
<el-col :span="12">
147+
<el-form-item label="本地文件转URL">
148+
<el-switch v-model="form.enableLocalFile2Url" />
149+
</el-form-item>
150+
</el-col>
151+
</el-row>
152+
<el-row :gutter="16">
153+
<el-col :span="12">
154+
<el-form-item label="调试模式">
155+
<el-switch v-model="form.debug" />
156+
</el-form-item>
157+
</el-col>
158+
<el-col :span="12">
159+
<el-form-item label="日志">
160+
<el-switch v-model="form.log" />
161+
</el-form-item>
162+
</el-col>
163+
</el-row>
164+
<el-row :gutter="16">
165+
<el-col :span="12">
166+
<el-form-item label="自动删除收到的文件">
167+
<el-switch v-model="form.autoDeleteFile" />
168+
</el-form-item>
169+
</el-col>
170+
<el-col :span="12">
171+
<el-form-item label="自动删除文件时间 (秒)">
172+
<el-input-number v-model="form.autoDeleteFileSecond" :min="1" :max="3600" />
173+
</el-form-item>
174+
</el-col>
175+
</el-row>
176+
<el-form-item label="音乐签名地址">
177+
<el-input v-model="form.musicSignUrl" placeholder="请输入音乐签名地址" clearable />
178+
</el-form-item>
179+
<el-form-item label="消息缓存过期 (秒)">
180+
<el-input-number v-model="form.msgCacheExpire" :min="1" :max="86400" />
181+
</el-form-item>
182+
<el-form-item label="只监听本地地址">
183+
<el-switch v-model="form.onlyLocalhost" />
184+
<el-tooltip content="取消则监听0.0.0.0" placement="top">
185+
<el-icon class="info-icon"><QuestionFilled /></el-icon>
186+
</el-tooltip>
187+
</el-form-item>
188+
<el-form-item class="form-actions">
189+
<el-button type="primary" @click="onSave" size="large" style="float: right;">保存配置</el-button>
190+
</el-form-item>
191+
</el-form>
192+
</el-card>
193+
</el-col>
194+
</el-row>
195+
</el-main>
196+
</el-container>
197+
</template>
198+
199+
<script setup lang="ts">
200+
import { ref } from 'vue'
201+
import { ElMessage } from 'element-plus'
202+
import { Setting, Cpu, Connection, QuestionFilled } from '@element-plus/icons-vue'
203+
204+
const defaultConfig = {
205+
satori: { enable: false, port: 5600, token: '' },
206+
ob11: {
207+
enable: true,
208+
token: '',
209+
httpPort: 3000,
210+
httpPostUrls: [],
211+
httpSecret: '',
212+
wsPort: 3001,
213+
wsReverseUrls: [],
214+
enableHttp: true,
215+
enableHttpPost: true,
216+
enableWs: true,
217+
enableWsReverse: false,
218+
messagePostFormat: 'array',
219+
enableHttpHeart: false,
220+
reportSelfMessage: true,
221+
},
222+
heartInterval: 60000,
223+
enableLocalFile2Url: false,
224+
debug: false,
225+
log: true,
226+
autoDeleteFile: false,
227+
autoDeleteFileSecond: 60,
228+
musicSignUrl: '',
229+
msgCacheExpire: 120,
230+
onlyLocalhost: true,
231+
}
232+
233+
const form = ref(JSON.parse(JSON.stringify(defaultConfig)))
234+
235+
const httpPostUrlInput = ref('')
236+
const wsReverseUrlInput = ref('')
237+
238+
function addHttpPostUrl() {
239+
const val = httpPostUrlInput.value.trim()
240+
if (val && !form.value.ob11.httpPostUrls.includes(val)) {
241+
form.value.ob11.httpPostUrls.push(val)
242+
}
243+
httpPostUrlInput.value = ''
244+
}
245+
function removeHttpPostUrl(idx: number) {
246+
form.value.ob11.httpPostUrls.splice(idx, 1)
247+
}
248+
249+
function addWsReverseUrl() {
250+
const val = wsReverseUrlInput.value.trim()
251+
if (val && !form.value.ob11.wsReverseUrls.includes(val)) {
252+
form.value.ob11.wsReverseUrls.push(val)
253+
}
254+
wsReverseUrlInput.value = ''
255+
}
256+
function removeWsReverseUrl(idx: number) {
257+
form.value.ob11.wsReverseUrls.splice(idx, 1)
258+
}
259+
260+
function onSave() {
261+
ElMessage.success('配置已保存(仅前端演示)')
262+
}
263+
</script>
264+
265+
<style scoped>
266+
.main-container {
267+
min-height: 100vh;
268+
background: linear-gradient(135deg, #f0f3fa 0%, #e8f0fe 100%);
269+
}
270+
.header {
271+
text-align: center;
272+
margin-bottom: 24px;
273+
background: transparent;
274+
padding-top: 32px;
275+
padding-bottom: 0;
276+
}
277+
.logo {
278+
font-size: 40px;
279+
color: #409eff;
280+
vertical-align: middle;
281+
margin-bottom: 8px;
282+
}
283+
.config-card {
284+
border-radius: 18px;
285+
box-shadow: 0 4px 24px 0 rgba(64,158,255,0.08);
286+
background: #fff;
287+
padding: 32px 24px 16px 24px;
288+
}
289+
.config-form {
290+
margin-top: 8px;
291+
}
292+
.el-divider {
293+
margin-top: 32px;
294+
margin-bottom: 18px;
295+
font-size: 18px;
296+
color: #409eff;
297+
font-weight: bold;
298+
}
299+
.form-actions {
300+
float: right;
301+
}
302+
.tag-list {
303+
margin-top: 4px;
304+
}
305+
.satori-row {
306+
margin-bottom: 18px;
307+
}
308+
.info-icon {
309+
margin-left: 8px;
310+
color: #909399;
311+
}
312+
@media (max-width: 600px) {
313+
.config-card {
314+
padding: 12px 2px 8px 2px;
315+
}
316+
.header {
317+
padding-top: 12px;
318+
}
319+
.el-divider {
320+
font-size: 15px;
321+
}
322+
}
323+
</style>

src/webui/env.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/// <reference types="vite/client" />
2+
declare module '*.vue' {
3+
import { DefineComponent } from 'vue'
4+
const component: DefineComponent<{}, {}, any>
5+
export default component
6+
}

src/webui/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="zh-CN">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>LLOneBot 配置可视化</title>
7+
</head>
8+
<body>
9+
<div id="app"></div>
10+
<script type="module" src="./main.ts"></script>
11+
</body>
12+
</html>

src/webui/main.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { createApp } from 'vue'
2+
import ElementPlus from 'element-plus'
3+
import 'element-plus/dist/index.css'
4+
import App from './App.vue'
5+
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
6+
7+
const app = createApp(App)
8+
9+
// 注册所有图标
10+
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
11+
app.component(key, component)
12+
}
13+
14+
app.use(ElementPlus)
15+
app.mount('#app')

src/webui/package.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "llonebot-webui",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"dev": "vite",
7+
"build": "vite build",
8+
"preview": "vite preview"
9+
},
10+
"dependencies": {
11+
"vue": "^3.4.0",
12+
"element-plus": "^2.4.0"
13+
},
14+
"devDependencies": {
15+
"typescript": "^5.0.0",
16+
"vite": "^4.0.0",
17+
"@vitejs/plugin-vue": "^4.0.0"
18+
}
19+
}

src/webui/tsconfig.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"composite": true,
5+
"baseUrl": ".",
6+
"paths": {
7+
"@/*": ["./*"]
8+
},
9+
"types": ["vite/client"]
10+
},
11+
"include": ["./**/*"],
12+
"exclude": ["node_modules"]
13+
}

0 commit comments

Comments
 (0)