mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
refactor: webui network
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { OneBotConfig } from '../../../src/onebot/config/config';
|
||||
|
||||
export class QQLoginManager {
|
||||
private retCredential: string;
|
||||
private readonly apiPrefix: string;
|
||||
@@ -9,7 +11,7 @@ export class QQLoginManager {
|
||||
}
|
||||
|
||||
// TODO:
|
||||
public async GetOB11Config(): Promise<any> {
|
||||
public async GetOB11Config(): Promise<OneBotConfig> {
|
||||
try {
|
||||
const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/GetConfig`, {
|
||||
method: 'POST',
|
||||
@@ -21,16 +23,16 @@ export class QQLoginManager {
|
||||
if (ConfigResponse.status == 200) {
|
||||
const ConfigResponseJson = await ConfigResponse.json();
|
||||
if (ConfigResponseJson.code == 0) {
|
||||
return ConfigResponseJson?.data;
|
||||
return ConfigResponseJson?.data as OneBotConfig;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting OB11 config:', error);
|
||||
}
|
||||
return {};
|
||||
return {} as OneBotConfig;
|
||||
}
|
||||
|
||||
public async SetOB11Config(config: any): Promise<boolean> {
|
||||
public async SetOB11Config(config: OneBotConfig): Promise<boolean> {
|
||||
try {
|
||||
const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/SetConfig`, {
|
||||
method: 'POST',
|
||||
|
@@ -1,70 +1,104 @@
|
||||
<template>
|
||||
<t-space>
|
||||
<t-tabs v-model="value" :addable="true" theme="card" @add="showAddTabDialog" @remove="removeTab">
|
||||
<t-tabs v-model="activeTab" :addable="true" theme="card" @add="showAddTabDialog" @remove="removeTab">
|
||||
<t-tab-panel
|
||||
v-for="data in panelData"
|
||||
:key="data.value"
|
||||
:label="data.label"
|
||||
:removable="data.removable"
|
||||
:value="data.value"
|
||||
v-for="(config, idx) in clientPanelData"
|
||||
:key="idx"
|
||||
:label="config.name"
|
||||
:removable="true"
|
||||
:value="idx"
|
||||
>
|
||||
<component :is="data.component" :config="data.config" />
|
||||
<component :is="resolveDynamicComponent(getComponent(config.key))" :config="config.data" />
|
||||
<t-button @click="saveConfig">保存</t-button>
|
||||
</t-tab-panel>
|
||||
</t-tabs>
|
||||
<t-dialog
|
||||
v-model:visible="isDialogVisible"
|
||||
header="添加新选项卡"
|
||||
@close="isDialogVisible = false"
|
||||
@confirm="addTab"
|
||||
>
|
||||
<t-form ref="form" :model="newTab">
|
||||
<t-form-item :rules="[{ required: true, message: '请输入名称' }]" label="名称" name="name">
|
||||
<t-input v-model="newTab.name" />
|
||||
</t-form-item>
|
||||
<t-form-item :rules="[{ required: true, message: '请选择类型' }]" label="类型" name="type">
|
||||
<t-select v-model="newTab.type">
|
||||
<t-option value="httpServers">HTTP 服务器</t-option>
|
||||
<t-option value="httpClients">HTTP 客户端</t-option>
|
||||
<t-option value="websocketServers">WebSocket 服务器</t-option>
|
||||
<t-option value="websocketClients">WebSocket 客户端</t-option>
|
||||
</t-select>
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</t-dialog>
|
||||
</t-space>
|
||||
<t-dialog
|
||||
v-model:visible="isDialogVisible"
|
||||
header="添加新选项卡"
|
||||
@close="isDialogVisible = false"
|
||||
@confirm="addTab"
|
||||
>
|
||||
<t-form ref="form" :model="newTab">
|
||||
<t-form-item :rules="[{ required: true, message: '请输入名称' }]" label="名称" name="name">
|
||||
<t-input v-model="newTab.name" />
|
||||
</t-form-item>
|
||||
<t-form-item :rules="[{ required: true, message: '请选择类型' }]" label="类型" name="type">
|
||||
<t-select v-model="newTab.type">
|
||||
<t-option value="httpServers">HTTP 服务器</t-option>
|
||||
<t-option value="httpClients">HTTP 客户端</t-option>
|
||||
<t-option value="websocketServers">WebSocket 服务器</t-option>
|
||||
<t-option value="websocketClients">WebSocket 客户端</t-option>
|
||||
</t-select>
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</t-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onMounted, ref, shallowRef, watch } from 'vue';
|
||||
import { defaultOnebotConfig, mergeOnebotConfigs } from '../../../src/onebot/config/config';
|
||||
import { ref, resolveDynamicComponent, nextTick, Ref, onMounted, reactive, Reactive } from 'vue';
|
||||
import {
|
||||
httpServerDefaultConfigs,
|
||||
httpClientDefaultConfigs,
|
||||
websocketServerDefaultConfigs,
|
||||
websocketClientDefaultConfigs,
|
||||
HttpClientConfig,
|
||||
HttpServerConfig,
|
||||
WebsocketClientConfig,
|
||||
WebsocketServerConfig,
|
||||
NetworkConfig,
|
||||
OneBotConfig,
|
||||
defaultOneBotConfigs,
|
||||
mergeOneBotConfigs,
|
||||
} from '../../../src/onebot/config/config';
|
||||
import { QQLoginManager } from '@/backend/shell';
|
||||
import HttpServerComponent from './network/HttpServerComponent.vue';
|
||||
import HttpClientComponent from './network/HttpClientComponent.vue';
|
||||
import WebsocketServerComponent from './network/WebsocketServerComponent.vue';
|
||||
import WebsocketClientComponent from './network/WebsocketClientComponent.vue';
|
||||
import HttpServerComponent from '@/pages/network/HttpServerComponent.vue';
|
||||
import HttpClientComponent from '@/pages/network/HttpClientComponent.vue';
|
||||
import WebsocketServerComponent from '@/pages/network/WebsocketServerComponent.vue';
|
||||
import WebsocketClientComponent from '@/pages/network/WebsocketClientComponent.vue';
|
||||
|
||||
interface PanelData {
|
||||
value: string;
|
||||
label: string;
|
||||
removable: boolean;
|
||||
component: any;
|
||||
config: { name: string };
|
||||
}
|
||||
type ConfigKey = 'httpServers' | 'httpClients' | 'websocketServers' | 'websocketClients';
|
||||
|
||||
let id = 0;
|
||||
const value = ref<string>('first');
|
||||
const panelData = ref<PanelData[]>([]);
|
||||
const isDialogVisible = ref<boolean>(false);
|
||||
const newTab = ref<{ name: string; type: string }>({ name: '', type: '' });
|
||||
type ConfigUnion = HttpClientConfig | HttpServerConfig | WebsocketServerConfig | WebsocketClientConfig;
|
||||
|
||||
const componentMap: Record<string, any> = {
|
||||
httpServers: shallowRef(HttpServerComponent),
|
||||
httpClients: shallowRef(HttpClientComponent),
|
||||
websocketServers: shallowRef(WebsocketServerComponent),
|
||||
websocketClients: shallowRef(WebsocketClientComponent),
|
||||
const defaultConfigs: Record<ConfigKey, ConfigUnion> = {
|
||||
httpServers: httpServerDefaultConfigs,
|
||||
httpClients: httpClientDefaultConfigs,
|
||||
websocketServers: websocketServerDefaultConfigs,
|
||||
websocketClients: websocketClientDefaultConfigs,
|
||||
};
|
||||
|
||||
const getOB11Config = async (): Promise<any | undefined> => {
|
||||
const componentMap: Record<
|
||||
ConfigKey,
|
||||
| typeof HttpServerComponent
|
||||
| typeof HttpClientComponent
|
||||
| typeof WebsocketServerComponent
|
||||
| typeof WebsocketClientComponent
|
||||
> = {
|
||||
httpServers: HttpServerComponent,
|
||||
httpClients: HttpClientComponent,
|
||||
websocketServers: WebsocketServerComponent,
|
||||
websocketClients: WebsocketClientComponent,
|
||||
};
|
||||
|
||||
interface ClientPanel {
|
||||
name: string;
|
||||
key: ConfigKey;
|
||||
data: Ref<ConfigUnion>;
|
||||
}
|
||||
|
||||
type ComponentKey = keyof typeof componentMap;
|
||||
|
||||
const activeTab = ref<number>(0);
|
||||
const isDialogVisible = ref(false);
|
||||
const newTab = ref<{ name: string; type: ComponentKey }>({ name: '', type: 'httpServers' });
|
||||
const clientPanelData: Reactive<Array<ClientPanel>> = reactive([]);
|
||||
|
||||
const getComponent = (type: ComponentKey) => {
|
||||
return componentMap[type];
|
||||
};
|
||||
|
||||
const getOB11Config = async (): Promise<OneBotConfig | undefined> => {
|
||||
const storedCredential = localStorage.getItem('auth');
|
||||
if (!storedCredential) {
|
||||
console.error('No stored credential found');
|
||||
@@ -74,7 +108,7 @@ const getOB11Config = async (): Promise<any | undefined> => {
|
||||
return await loginManager.GetOB11Config();
|
||||
};
|
||||
|
||||
const setOB11Config = async (config: any): Promise<boolean> => {
|
||||
const setOB11Config = async (config: OneBotConfig): Promise<boolean> => {
|
||||
const storedCredential = localStorage.getItem('auth');
|
||||
if (!storedCredential) {
|
||||
console.error('No stored credential found');
|
||||
@@ -84,100 +118,74 @@ const setOB11Config = async (config: any): Promise<boolean> => {
|
||||
return await loginManager.SetOB11Config(config);
|
||||
};
|
||||
|
||||
const log = (message: string, data: any) => {
|
||||
console.log(message, data);
|
||||
const addToPanel = <T extends ConfigUnion>(configs: T[], key: ConfigKey) => {
|
||||
configs.forEach((config) => clientPanelData.push({ name: config.name, data: config, key: key }));
|
||||
};
|
||||
|
||||
const createPanel = (type: string, name: string, id: number): PanelData => {
|
||||
return {
|
||||
value: `${type}-${id}`,
|
||||
label: name,
|
||||
removable: true,
|
||||
component: componentMap[type],
|
||||
config: { name: name },
|
||||
};
|
||||
};
|
||||
|
||||
const generatePanels = (networkConfig: any): PanelData[] => {
|
||||
const panels: PanelData[] = [];
|
||||
Object.keys(networkConfig).forEach((key) => {
|
||||
networkConfig[key].forEach((config: any, index: number) => {
|
||||
const component = componentMap[key];
|
||||
if (!component) {
|
||||
console.error(`No component found for key: ${key}`);
|
||||
return;
|
||||
}
|
||||
panels.push(createPanel(key, config.name, index));
|
||||
});
|
||||
const addConfigDataToPanel = (data: NetworkConfig) => {
|
||||
Object.entries(data).forEach(([key, configs]) => {
|
||||
if (key in defaultConfigs) {
|
||||
addToPanel(configs as ConfigUnion[], key as ConfigKey);
|
||||
}
|
||||
});
|
||||
return panels;
|
||||
};
|
||||
|
||||
const parsePanelData = (): NetworkConfig => {
|
||||
return {
|
||||
websocketClients: clientPanelData
|
||||
.filter((panel) => panel.key === 'websocketClients')
|
||||
.map((panel) => panel.data as WebsocketClientConfig),
|
||||
websocketServers: clientPanelData
|
||||
.filter((panel) => panel.key === 'websocketServers')
|
||||
.map((panel) => panel.data as WebsocketServerConfig),
|
||||
httpClients: clientPanelData
|
||||
.filter((panel) => panel.key === 'httpClients')
|
||||
.map((panel) => panel.data as HttpClientConfig),
|
||||
httpServers: clientPanelData
|
||||
.filter((panel) => panel.key === 'httpServers')
|
||||
.map((panel) => panel.data as HttpServerConfig),
|
||||
};
|
||||
};
|
||||
|
||||
const loadConfig = async () => {
|
||||
try {
|
||||
const userConfig = await getOB11Config();
|
||||
if (!userConfig) return;
|
||||
const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig);
|
||||
const networkConfig = mergedConfig.network;
|
||||
log('networkConfig:', networkConfig);
|
||||
const panels = generatePanels(networkConfig);
|
||||
log('panels:', panels);
|
||||
panelData.value = panels;
|
||||
if (panels.length > 0) {
|
||||
value.value = panels[0].value;
|
||||
}
|
||||
const mergedConfig = mergeOneBotConfigs(defaultOneBotConfigs, userConfig);
|
||||
addConfigDataToPanel(mergedConfig.network);
|
||||
} catch (error) {
|
||||
console.error('Error loading config:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// It's better to "saveConfig" instead of using deep watch
|
||||
const saveConfig = async () => {
|
||||
const config = parsePanelData();
|
||||
const userConfig = await getOB11Config();
|
||||
if (!userConfig) return;
|
||||
userConfig.network = config;
|
||||
await setOB11Config(userConfig);
|
||||
};
|
||||
|
||||
const showAddTabDialog = () => {
|
||||
newTab.value = { name: '', type: '' };
|
||||
newTab.value = { name: '', type: 'httpServers' };
|
||||
isDialogVisible.value = true;
|
||||
};
|
||||
|
||||
const addTab = async () => {
|
||||
const { name, type } = newTab.value;
|
||||
if (!name || !type) return;
|
||||
const newPanel = createPanel(type, name, id);
|
||||
panelData.value.push(newPanel);
|
||||
id += 1;
|
||||
const defaultConfig = structuredClone(defaultConfigs[type]);
|
||||
clientPanelData.push({ name, data: defaultConfig, key: type });
|
||||
isDialogVisible.value = false;
|
||||
await nextTick(); // 确保 DOM 更新完成
|
||||
value.value = newPanel.value; // 强制重新渲染选项卡
|
||||
await nextTick();
|
||||
activeTab.value = clientPanelData.length - 1;
|
||||
};
|
||||
|
||||
const closeDialog = () => {
|
||||
isDialogVisible.value = false;
|
||||
const removeTab = (payload: { value: string; index: number; e: PointerEvent }) => {
|
||||
clientPanelData.splice(payload.index, 1);
|
||||
activeTab.value = Math.max(0, activeTab.value - 1);
|
||||
};
|
||||
|
||||
const removeTab = ({ value: val, index }: { value: string; index: number }) => {
|
||||
if (index < 0) return false;
|
||||
panelData.value.splice(index, 1);
|
||||
if (panelData.value.length === 0) return;
|
||||
if (value.value === val) {
|
||||
value.value = panelData.value[Math.max(index - 1, 0)].value;
|
||||
}
|
||||
};
|
||||
|
||||
const syncConfig = async () => {
|
||||
const networkConfig: Record<string, any[]> = {};
|
||||
panelData.value.forEach((panel) => {
|
||||
const key = panel.value.split('-')[0];
|
||||
if (!networkConfig[key]) {
|
||||
networkConfig[key] = [];
|
||||
}
|
||||
networkConfig[key].push(panel.config);
|
||||
});
|
||||
const userConfig = await getOB11Config();
|
||||
if (!userConfig) return;
|
||||
const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig);
|
||||
mergedConfig.network = networkConfig;
|
||||
await setOB11Config(mergedConfig);
|
||||
};
|
||||
|
||||
watch(panelData, syncConfig, { deep: true });
|
||||
|
||||
onMounted(() => {
|
||||
loadConfig();
|
||||
});
|
||||
|
@@ -22,21 +22,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
interface HttpClientConfig {
|
||||
url: string;
|
||||
messagePostFormat: string;
|
||||
reportSelfMessage: boolean;
|
||||
token: string;
|
||||
debug: boolean;
|
||||
}
|
||||
|
||||
const config = ref<HttpClientConfig>({
|
||||
url: '',
|
||||
messagePostFormat: '',
|
||||
reportSelfMessage: false,
|
||||
token: '',
|
||||
debug: false,
|
||||
});
|
||||
import { defineProps } from 'vue';
|
||||
import { HttpClientConfig } from '../../../../src/onebot/config/config';
|
||||
defineProps<{
|
||||
config: HttpClientConfig;
|
||||
}>();
|
||||
</script>
|
||||
|
@@ -31,27 +31,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
interface HttpServerConfig {
|
||||
port: number;
|
||||
host: string;
|
||||
enableCors: boolean;
|
||||
enableWebsocket: boolean;
|
||||
messagePostFormat: string;
|
||||
reportSelfMessage: boolean;
|
||||
token: string;
|
||||
debug: boolean;
|
||||
}
|
||||
|
||||
const config = ref<HttpServerConfig>({
|
||||
port: 8080,
|
||||
host: '',
|
||||
enableCors: false,
|
||||
enableWebsocket: false,
|
||||
messagePostFormat: '',
|
||||
reportSelfMessage: false,
|
||||
token: '',
|
||||
debug: false,
|
||||
});
|
||||
import { defineProps } from 'vue';
|
||||
import { HttpServerConfig } from '../../../../src/onebot/config/config';
|
||||
defineProps<{
|
||||
config: HttpServerConfig;
|
||||
}>();
|
||||
</script>
|
||||
|
@@ -25,23 +25,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
interface WsClientConfig {
|
||||
url: string;
|
||||
messagePostFormat: string;
|
||||
reportSelfMessage: boolean;
|
||||
token: string;
|
||||
debug: boolean;
|
||||
heartInterval: number;
|
||||
}
|
||||
|
||||
const config = ref<WsClientConfig>({
|
||||
url: '',
|
||||
messagePostFormat: '',
|
||||
reportSelfMessage: false,
|
||||
token: '',
|
||||
debug: false,
|
||||
heartInterval: 0,
|
||||
});
|
||||
import { defineProps } from 'vue';
|
||||
import { WebsocketClientConfig } from '../../../../src/onebot/config/config';
|
||||
defineProps<{
|
||||
config: WebsocketClientConfig;
|
||||
}>();
|
||||
</script>
|
||||
|
@@ -31,27 +31,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
interface WsServerConfig {
|
||||
host: string;
|
||||
port: number;
|
||||
messagePostFormat: string;
|
||||
reportSelfMessage: boolean;
|
||||
token: string;
|
||||
enablePushEvent: boolean;
|
||||
debug: boolean;
|
||||
heartInterval: number;
|
||||
}
|
||||
|
||||
const config = ref<WsServerConfig>({
|
||||
host: '',
|
||||
port: 8080,
|
||||
messagePostFormat: '',
|
||||
reportSelfMessage: false,
|
||||
token: '',
|
||||
enablePushEvent: false,
|
||||
debug: false,
|
||||
heartInterval: 0,
|
||||
});
|
||||
import { defineProps } from 'vue';
|
||||
import { WebsocketServerConfig } from '../../../../src/onebot/config/config';
|
||||
defineProps<{
|
||||
config: WebsocketServerConfig;
|
||||
}>();
|
||||
</script>
|
||||
|
Reference in New Issue
Block a user