mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-12-19 05:05:44 +08:00
refactor: webui
This commit is contained in:
parent
1ec1040e43
commit
fe0bda11d3
53
napcat.webui/eslint.config.mjs
Normal file
53
napcat.webui/eslint.config.mjs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import globals from 'globals';
|
||||||
|
import ts from 'typescript-eslint';
|
||||||
|
import vue from 'eslint-plugin-vue';
|
||||||
|
import prettier from 'eslint-plugin-prettier/recommended';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
...globals.node,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...ts.configs.recommended,
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'warn',
|
||||||
|
'@typescript-eslint/no-var-requires': 'warn',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...vue.configs['flat/base'],
|
||||||
|
{
|
||||||
|
files: ['*.vue', '**/*.vue'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
parser: ts.parser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
indent: ['error', 4],
|
||||||
|
semi: ['error', 'always'],
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'warn',
|
||||||
|
'@typescript-eslint/no-var-requires': 'warn',
|
||||||
|
'object-curly-spacing': ['error', 'always'],
|
||||||
|
'vue/v-for-delimiter-style': ['error', 'in'],
|
||||||
|
'vue/require-name-property': 'warn',
|
||||||
|
'vue/prefer-true-attribute-shorthand': 'warn',
|
||||||
|
'prefer-arrow-callback': 'warn',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prettier,
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
'prettier/prettier': 'warn',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -4,18 +4,26 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"lint": "eslint . --fix",
|
||||||
|
"dev": "vite --host 127.0.0.1",
|
||||||
"build": "vue-tsc -b && vite build",
|
"build": "vue-tsc -b && vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"tdesign-vue-next": "^1.10.3",
|
"tdesign-vue-next": "^1.10.3",
|
||||||
"vue": "^3.5.12",
|
"vue": "^3.5.12",
|
||||||
"vue-router": "^4.4.5"
|
"vue-router": "^4.4.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/eslintrc": "^3.1.0",
|
||||||
|
"@eslint/js": "^9.14.0",
|
||||||
|
"@types/qrcode": "^1.5.5",
|
||||||
"@vitejs/plugin-vue": "^5.1.4",
|
"@vitejs/plugin-vue": "^5.1.4",
|
||||||
|
"eslint-config-prettier": "^9.1.0",
|
||||||
|
"eslint-plugin-vue": "^9.31.0",
|
||||||
|
"globals": "^15.12.0",
|
||||||
"typescript": "~5.6.2",
|
"typescript": "~5.6.2",
|
||||||
"vite": "^5.4.10",
|
"vite": "^5.4.10",
|
||||||
"vue-tsc": "^2.1.8"
|
"vue-tsc": "^2.1.8"
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<router-view />
|
<router-view />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts"></script>
|
||||||
export default {
|
|
||||||
name: 'App'
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|||||||
@ -1,14 +1,17 @@
|
|||||||
export class QQLoginManager {
|
export class QQLoginManager {
|
||||||
private retCredential: string;
|
private retCredential: string;
|
||||||
private apiprefix: string;
|
private readonly apiPrefix: string;
|
||||||
|
// TODO:
|
||||||
//调试时http://127.0.0.1:6099/api 打包时 ../api
|
//调试时http://127.0.0.1:6099/api 打包时 ../api
|
||||||
constructor(retCredential: string, apiprefix: string = 'http://127.0.0.1:6099/api') {
|
constructor(retCredential: string, apiPrefix: string = 'http://127.0.0.1:6099/api') {
|
||||||
this.retCredential = retCredential;
|
this.retCredential = retCredential;
|
||||||
this.apiprefix = apiprefix;
|
this.apiPrefix = apiPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
public async GetOB11Config(): Promise<any> {
|
public async GetOB11Config(): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const ConfigResponse = await fetch(`${this.apiprefix}/OB11Config/GetConfig`, {
|
const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/GetConfig`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + this.retCredential,
|
Authorization: 'Bearer ' + this.retCredential,
|
||||||
@ -22,14 +25,14 @@ export class QQLoginManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error getting OB11 config:", error);
|
console.error('Error getting OB11 config:', error);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async SetOB11Config(config: any): Promise<boolean> {
|
public async SetOB11Config(config: any): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const ConfigResponse = await fetch(`${this.apiprefix}/OB11Config/SetConfig`, {
|
const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/SetConfig`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + this.retCredential,
|
Authorization: 'Bearer ' + this.retCredential,
|
||||||
@ -44,137 +47,137 @@ export class QQLoginManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error setting OB11 config:", error);
|
console.error('Error setting OB11 config:', error);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async checkQQLoginStatus(): Promise<boolean> {
|
public async checkQQLoginStatus(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/CheckLoginStatus`, {
|
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/CheckLoginStatus`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': "Bearer " + this.retCredential,
|
Authorization: 'Bearer ' + this.retCredential,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
if (QQLoginResponse.status == 200) {
|
if (QQLoginResponse.status == 200) {
|
||||||
let QQLoginResponseJson = await QQLoginResponse.json();
|
const QQLoginResponseJson = await QQLoginResponse.json();
|
||||||
if (QQLoginResponseJson.code == 0) {
|
if (QQLoginResponseJson.code == 0) {
|
||||||
return QQLoginResponseJson.data.isLogin;
|
return QQLoginResponseJson.data.isLogin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error checking QQ login status:", error);
|
console.error('Error checking QQ login status:', error);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async checkWebUiLogined(): Promise<boolean> {
|
public async checkWebUiLogined(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
let LoginResponse = await fetch(`${this.apiprefix}/auth/check`, {
|
const LoginResponse = await fetch(`${this.apiPrefix}/auth/check`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': "Bearer " + this.retCredential,
|
Authorization: 'Bearer ' + this.retCredential,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
if (LoginResponse.status == 200) {
|
if (LoginResponse.status == 200) {
|
||||||
let LoginResponseJson = await LoginResponse.json();
|
const LoginResponseJson = await LoginResponse.json();
|
||||||
if (LoginResponseJson.code == 0) {
|
if (LoginResponseJson.code == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error checking web UI login status:", error);
|
console.error('Error checking web UI login status:', error);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async loginWithToken(token: string): Promise<string | null> {
|
public async loginWithToken(token: string): Promise<string | null> {
|
||||||
try {
|
try {
|
||||||
let loginResponse = await fetch(`${this.apiprefix}/auth/login`, {
|
const loginResponse = await fetch(`${this.apiPrefix}/auth/login`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ token: token })
|
body: JSON.stringify({ token: token }),
|
||||||
});
|
});
|
||||||
const loginResponseJson = await loginResponse.json();
|
const loginResponseJson = await loginResponse.json();
|
||||||
let retCode = loginResponseJson.code;
|
const retCode = loginResponseJson.code;
|
||||||
if (retCode === 0) {
|
if (retCode === 0) {
|
||||||
this.retCredential = loginResponseJson.data.Credential;
|
this.retCredential = loginResponseJson.data.Credential;
|
||||||
return this.retCredential;
|
return this.retCredential;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error logging in with token:", error);
|
console.error('Error logging in with token:', error);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getQQLoginQrcode(): Promise<string> {
|
public async getQQLoginQrcode(): Promise<string> {
|
||||||
try {
|
try {
|
||||||
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/GetQQLoginQrcode`, {
|
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/GetQQLoginQrcode`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': "Bearer " + this.retCredential,
|
Authorization: 'Bearer ' + this.retCredential,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
if (QQLoginResponse.status == 200) {
|
if (QQLoginResponse.status == 200) {
|
||||||
let QQLoginResponseJson = await QQLoginResponse.json();
|
const QQLoginResponseJson = await QQLoginResponse.json();
|
||||||
if (QQLoginResponseJson.code == 0) {
|
if (QQLoginResponseJson.code == 0) {
|
||||||
return QQLoginResponseJson.data.qrcode || "";
|
return QQLoginResponseJson.data.qrcode || '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error getting QQ login QR code:", error);
|
console.error('Error getting QQ login QR code:', error);
|
||||||
}
|
}
|
||||||
return "";
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getQQQuickLoginList(): Promise<string[]> {
|
public async getQQQuickLoginList(): Promise<string[]> {
|
||||||
try {
|
try {
|
||||||
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/GetQuickLoginList`, {
|
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/GetQuickLoginList`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': "Bearer " + this.retCredential,
|
Authorization: 'Bearer ' + this.retCredential,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
if (QQLoginResponse.status == 200) {
|
if (QQLoginResponse.status == 200) {
|
||||||
let QQLoginResponseJson = await QQLoginResponse.json();
|
const QQLoginResponseJson = await QQLoginResponse.json();
|
||||||
if (QQLoginResponseJson.code == 0) {
|
if (QQLoginResponseJson.code == 0) {
|
||||||
return QQLoginResponseJson.data || [];
|
return QQLoginResponseJson.data || [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error getting QQ quick login list:", error);
|
console.error('Error getting QQ quick login list:', error);
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public async setQuickLogin(uin: string): Promise<{ result: boolean, errMsg: string }> {
|
public async setQuickLogin(uin: string): Promise<{ result: boolean; errMsg: string }> {
|
||||||
try {
|
try {
|
||||||
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/SetQuickLogin`, {
|
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/SetQuickLogin`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': "Bearer " + this.retCredential,
|
Authorization: 'Bearer ' + this.retCredential,
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ uin: uin })
|
body: JSON.stringify({ uin: uin }),
|
||||||
});
|
});
|
||||||
if (QQLoginResponse.status == 200) {
|
if (QQLoginResponse.status == 200) {
|
||||||
let QQLoginResponseJson = await QQLoginResponse.json();
|
const QQLoginResponseJson = await QQLoginResponse.json();
|
||||||
if (QQLoginResponseJson.code == 0) {
|
if (QQLoginResponseJson.code == 0) {
|
||||||
return { result: true, errMsg: "" };
|
return { result: true, errMsg: '' };
|
||||||
} else {
|
} else {
|
||||||
return { result: false, errMsg: QQLoginResponseJson.message };
|
return { result: false, errMsg: QQLoginResponseJson.message };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error setting quick login:", error);
|
console.error('Error setting quick login:', error);
|
||||||
}
|
}
|
||||||
return { result: false, errMsg: "接口异常" };
|
return { result: false, errMsg: '接口异常' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,56 +1,55 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dashboard-container">
|
<div class="dashboard-container">
|
||||||
<SidebarMenu :menuItems="menuItems" class="sidebar-menu" />
|
<SidebarMenu :menu-items="menuItems" class="sidebar-menu" />
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<router-view />
|
<router-view />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
import SidebarMenu from './webui/Nav.vue';
|
import SidebarMenu from './webui/Nav.vue';
|
||||||
|
|
||||||
export default {
|
interface MenuItem {
|
||||||
components: {
|
value: string;
|
||||||
SidebarMenu
|
icon: string;
|
||||||
},
|
label: string;
|
||||||
data() {
|
route: string;
|
||||||
return {
|
|
||||||
menuItems: [
|
|
||||||
{ value: 'item1', icon: 'dashboard', label: '基础信息', route: '/dashboard/basic-info' },
|
|
||||||
{ value: 'item3', icon: 'wifi-1', label: '网络配置', route: '/dashboard/network-config' },
|
|
||||||
{ value: 'item4', icon: 'setting', label: '其余配置', route: '/dashboard/other-config' },
|
|
||||||
{ value: 'item5', icon: 'system-log', label: '日志查看', route: '/dashboard/log-view' },
|
|
||||||
{ value: 'item6', icon: 'info-circle', label: '关于我们', route: '/dashboard/about-us' }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const menuItems = ref<MenuItem[]>([
|
||||||
|
{ value: 'item1', icon: 'dashboard', label: '基础信息', route: '/dashboard/basic-info' },
|
||||||
|
{ value: 'item3', icon: 'wifi-1', label: '网络配置', route: '/dashboard/network-config' },
|
||||||
|
{ value: 'item4', icon: 'setting', label: '其余配置', route: '/dashboard/other-config' },
|
||||||
|
{ value: 'item5', icon: 'system-log', label: '日志查看', route: '/dashboard/log-view' },
|
||||||
|
{ value: 'item6', icon: 'info-circle', label: '关于我们', route: '/dashboard/about-us' },
|
||||||
|
]);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.dashboard-container {
|
.dashboard-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-menu {
|
.sidebar-menu {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.content {
|
.content {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -2,67 +2,91 @@
|
|||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<h2 class="sotheby-font">QQ Login</h2>
|
<h2 class="sotheby-font">QQ Login</h2>
|
||||||
<div class="login-methods">
|
<div class="login-methods">
|
||||||
<t-button id="quick-login" class="login-method" :class="{ active: loginMethod === 'quick' }"
|
<t-button
|
||||||
@click="loginMethod = 'quick'">Quick Login</t-button>
|
id="quick-login"
|
||||||
<t-button id="qrcode-login" class="login-method" :class="{ active: loginMethod === 'qrcode' }"
|
class="login-method"
|
||||||
@click="loginMethod = 'qrcode'">QR Code</t-button>
|
:class="{ active: loginMethod === 'quick' }"
|
||||||
|
@click="loginMethod = 'quick'"
|
||||||
|
>Quick Login</t-button
|
||||||
|
>
|
||||||
|
<t-button
|
||||||
|
id="qrcode-login"
|
||||||
|
class="login-method"
|
||||||
|
:class="{ active: loginMethod === 'qrcode' }"
|
||||||
|
@click="loginMethod = 'qrcode'"
|
||||||
|
>QR Code</t-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div id="quick-login-dropdown" class="login-form" v-show="loginMethod === 'quick'">
|
<div v-show="loginMethod === 'quick'" id="quick-login-dropdown" class="login-form">
|
||||||
<t-select id="quick-login-select" v-model="selectedAccount" @change="selectAccount"
|
<t-select
|
||||||
placeholder="Select Account">
|
id="quick-login-select"
|
||||||
|
v-model="selectedAccount"
|
||||||
|
placeholder="Select Account"
|
||||||
|
@change="selectAccount"
|
||||||
|
>
|
||||||
<t-option v-for="account in quickLoginList" :key="account" :value="account">{{ account }}</t-option>
|
<t-option v-for="account in quickLoginList" :key="account" :value="account">{{ account }}</t-option>
|
||||||
</t-select>
|
</t-select>
|
||||||
</div>
|
</div>
|
||||||
<div id="qrcode" class="qrcode" v-show="loginMethod === 'qrcode'">
|
<div v-show="loginMethod === 'qrcode'" id="qrcode" class="qrcode">
|
||||||
<canvas ref="qrcodeCanvas"></canvas>
|
<canvas ref="qrcodeCanvas"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import QRCode from 'qrcode';
|
import * as QRCode from 'qrcode';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { MessagePlugin } from 'tdesign-vue-next';
|
import { MessagePlugin } from 'tdesign-vue-next';
|
||||||
import { QQLoginManager } from '../backend/shell.ts';
|
import { QQLoginManager } from '@/backend/shell';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const loginMethod = ref('quick');
|
const loginMethod = ref<'quick' | 'qrcode'>('quick');
|
||||||
const quickLoginList = ref([]);
|
const quickLoginList = ref<string[]>([]);
|
||||||
const selectedAccount = ref('');
|
const selectedAccount = ref<string>('');
|
||||||
const qrcodeCanvas = ref(null);
|
const qrcodeCanvas = ref<HTMLCanvasElement | null>(null);
|
||||||
const qqLoginManager = new QQLoginManager(localStorage.getItem('auth'));
|
const qqLoginManager = new QQLoginManager(localStorage.getItem('auth') || '');
|
||||||
let heartBeatTimer = null;
|
let heartBeatTimer: number | null = null;
|
||||||
|
|
||||||
const selectAccount = async (accountName) => {
|
const selectAccount = async (accountName: string): Promise<void> => {
|
||||||
const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName);
|
const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName);
|
||||||
if (result) {
|
if (result) {
|
||||||
MessagePlugin.success("登录成功即将跳转");
|
await MessagePlugin.success('登录成功即将跳转');
|
||||||
await router.push({ path: '/dashboard/basic-info' });
|
await router.push({ path: '/dashboard/basic-info' });
|
||||||
} else {
|
} else {
|
||||||
MessagePlugin.error("登录失败," + errMsg);
|
await MessagePlugin.error('登录失败,' + errMsg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateQrCode = (data, canvas) => {
|
const generateQrCode = (data: string, canvas: HTMLCanvasElement | null): void => {
|
||||||
QRCode.toCanvas(canvas, data, function (error) {
|
if (!canvas) {
|
||||||
if (error) console.log(error);
|
console.error('Canvas element not found');
|
||||||
console.log('QR Code generated!');
|
return;
|
||||||
|
}
|
||||||
|
QRCode.toCanvas(canvas, data, function (error: Error | null | undefined) {
|
||||||
|
if (error) {
|
||||||
|
console.error('Error generating QR Code:', error);
|
||||||
|
} else {
|
||||||
|
console.log('QR Code generated!');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const HeartBeat = async () => {
|
const HeartBeat = async (): Promise<void> => {
|
||||||
let isLogined = await qqLoginManager.checkQQLoginStatus();
|
const isLogined = await qqLoginManager.checkQQLoginStatus();
|
||||||
if (isLogined) {
|
if (isLogined) {
|
||||||
clearInterval(heartBeatTimer);
|
if (heartBeatTimer) {
|
||||||
|
clearInterval(heartBeatTimer);
|
||||||
|
}
|
||||||
await router.push({ path: '/dashboard/basic-info' });
|
await router.push({ path: '/dashboard/basic-info' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const InitPages = async () => {
|
const InitPages = async (): Promise<void> => {
|
||||||
quickLoginList.value = await qqLoginManager.getQQQuickLoginList();
|
quickLoginList.value = await qqLoginManager.getQQQuickLoginList();
|
||||||
generateQrCode(await qqLoginManager.getQQLoginQrcode(), qrcodeCanvas.value);
|
const qrcodeData = await qqLoginManager.getQQLoginQrcode();
|
||||||
heartBeatTimer = setInterval(HeartBeat, 3000);
|
generateQrCode(qrcodeData, qrcodeCanvas.value);
|
||||||
|
heartBeatTimer = window.setInterval(HeartBeat, 3000);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@ -103,7 +127,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
.login-method.active {
|
.login-method.active {
|
||||||
background-color: #e6f0ff;
|
background-color: #e6f0ff;
|
||||||
color: #007BFF;
|
color: #007bff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-form,
|
.login-form,
|
||||||
@ -125,7 +149,7 @@ onMounted(() => {
|
|||||||
font-family: Sotheby, Helvetica, monospace;
|
font-family: Sotheby, Helvetica, monospace;
|
||||||
font-size: 3.125rem;
|
font-size: 3.125rem;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, .1);
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
@ -140,4 +164,4 @@ onMounted(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<h2 class="sotheby-font">WebUi Login</h2>
|
<h2 class="sotheby-font">WebUi Login</h2>
|
||||||
<t-form ref="form" :data="formData" :colon="true" :label-width="0" @submit="onSubmit">
|
<t-form ref="form" :data="formData" colon :label-width="0" @submit="onSubmit">
|
||||||
<t-form-item name="password">
|
<t-form-item name="password">
|
||||||
<t-input v-model="formData.token" type="password" clearable placeholder="请输入Token">
|
<t-input v-model="formData.token" type="password" clearable placeholder="请输入Token">
|
||||||
<template #prefix-icon>
|
<template #prefix-icon>
|
||||||
@ -14,32 +14,34 @@
|
|||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">Power By NapCat.WebUi</div>
|
||||||
Power By NapCat.WebUi
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import '../css/style.css';
|
import '../css/style.css';
|
||||||
import '../css/font.css';
|
import '../css/font.css';
|
||||||
import { reactive, ref, onMounted } from 'vue';
|
import { reactive, onMounted } from 'vue';
|
||||||
import { MessagePlugin } from 'tdesign-vue-next';
|
import { MessagePlugin } from 'tdesign-vue-next';
|
||||||
import { LockOnIcon } from 'tdesign-icons-vue-next';
|
import { LockOnIcon } from 'tdesign-icons-vue-next';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { QQLoginManager } from '../backend/shell';
|
import { QQLoginManager } from '@/backend/shell';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const formData = reactive({
|
interface FormData {
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData: FormData = reactive({
|
||||||
token: '',
|
token: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleLoginSuccess = async (credential) => {
|
const handleLoginSuccess = async (credential: string) => {
|
||||||
localStorage.setItem('auth', credential);
|
localStorage.setItem('auth', credential);
|
||||||
await checkLoginStatus();
|
await checkLoginStatus();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLoginFailure = (message) => {
|
const handleLoginFailure = (message: string) => {
|
||||||
MessagePlugin.error(message);
|
MessagePlugin.error(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -63,7 +65,7 @@ const checkLoginStatus = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loginWithToken = async (token) => {
|
const loginWithToken = async (token: string) => {
|
||||||
const loginManager = new QQLoginManager('');
|
const loginManager = new QQLoginManager('');
|
||||||
const credential = await loginManager.loginWithToken(token);
|
const credential = await loginManager.loginWithToken(token);
|
||||||
if (credential) {
|
if (credential) {
|
||||||
@ -75,15 +77,15 @@ const loginWithToken = async (token) => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
const token = url.searchParams.get("token");
|
const token = url.searchParams.get('token');
|
||||||
if (token) {
|
if (token) {
|
||||||
loginWithToken(token);
|
loginWithToken(token);
|
||||||
}
|
}
|
||||||
checkLoginStatus();
|
checkLoginStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = async ({ validateResult }) => {
|
const onSubmit = async ({ validateResult }: { validateResult: boolean }) => {
|
||||||
if (validateResult === true) {
|
if (validateResult) {
|
||||||
await loginWithToken(formData.token);
|
await loginWithToken(formData.token);
|
||||||
} else {
|
} else {
|
||||||
handleLoginFailure('请填写Token');
|
handleLoginFailure('请填写Token');
|
||||||
@ -131,7 +133,7 @@ const onSubmit = async ({ validateResult }) => {
|
|||||||
font-family: Sotheby, Helvetica, monospace;
|
font-family: Sotheby, Helvetica, monospace;
|
||||||
font-size: 3.125rem;
|
font-size: 3.125rem;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, .1);
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
@ -146,4 +148,4 @@ const onSubmit = async ({ validateResult }) => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,77 +1,71 @@
|
|||||||
<template>
|
<template>
|
||||||
<t-menu theme="light" default-value="2-1" :collapsed="collapsed" class="sidebar-menu">
|
<t-menu theme="light" default-value="2-1" :collapsed="collapsed" class="sidebar-menu">
|
||||||
<template #logo>
|
<template #logo> </template>
|
||||||
</template>
|
<router-link v-for="item in menuItems" :key="item.value" :to="item.route">
|
||||||
<router-link v-for="item in menuItems" :key="item.value" :to="item.route">
|
<t-menu-item :value="item.value" :disabled="item.disabled" class="menu-item">
|
||||||
<t-menu-item :value="item.value" :disabled="item.disabled" class="menu-item">
|
<template #icon>
|
||||||
<template #icon>
|
<t-icon :name="item.icon" />
|
||||||
<t-icon :name="item.icon" />
|
</template>
|
||||||
|
{{ item.label }}
|
||||||
|
</t-menu-item>
|
||||||
|
</router-link>
|
||||||
|
<template #operations>
|
||||||
|
<t-button class="t-demo-collapse-btn" variant="text" shape="square" @click="changeCollapsed">
|
||||||
|
<template #icon><t-icon :name="iconName" /></template>
|
||||||
|
</t-button>
|
||||||
</template>
|
</template>
|
||||||
{{ item.label }}
|
</t-menu>
|
||||||
</t-menu-item>
|
|
||||||
</router-link>
|
|
||||||
<template #operations>
|
|
||||||
<t-button class="t-demo-collapse-btn" variant="text" shape="square" @click="changeCollapsed">
|
|
||||||
<template #icon><t-icon :name="iconName" /></template>
|
|
||||||
</t-button>
|
|
||||||
</template>
|
|
||||||
</t-menu>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
import { defineComponent, ref, onMounted } from 'vue';
|
import { ref, defineProps } from 'vue';
|
||||||
|
|
||||||
export default defineComponent({
|
type MenuItem = {
|
||||||
name: 'SidebarMenu',
|
value: string;
|
||||||
props: {
|
label: string;
|
||||||
menuItems: {
|
route: string;
|
||||||
type: Array,
|
icon?: string;
|
||||||
required: true
|
disabled?: boolean;
|
||||||
}
|
};
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
const collapsed = ref(localStorage.getItem('sidebar-collapsed') === 'true');
|
|
||||||
const iconName = ref(collapsed.value ? 'menu-unfold' : 'menu-fold');
|
|
||||||
|
|
||||||
const changeCollapsed = () => {
|
defineProps<{
|
||||||
collapsed.value = !collapsed.value;
|
menuItems: MenuItem[];
|
||||||
iconName.value = collapsed.value ? 'menu-unfold' : 'menu-fold';
|
}>();
|
||||||
localStorage.setItem('sidebar-collapsed', collapsed.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
const collapsed = ref<boolean>(localStorage.getItem('sidebar-collapsed') === 'true');
|
||||||
collapsed,
|
const iconName = ref<string>(collapsed.value ? 'menu-unfold' : 'menu-fold');
|
||||||
iconName,
|
|
||||||
changeCollapsed
|
const changeCollapsed = (): void => {
|
||||||
};
|
collapsed.value = !collapsed.value;
|
||||||
}
|
iconName.value = collapsed.value ? 'menu-unfold' : 'menu-fold';
|
||||||
});
|
localStorage.setItem('sidebar-collapsed', collapsed.value.toString());
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sidebar-menu {
|
.sidebar-menu {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.sidebar-menu {
|
.sidebar-menu {
|
||||||
width: 100px; /* 移动端侧边栏宽度 */
|
width: 100px; /* 移动端侧边栏宽度 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-text {
|
.logo-text {
|
||||||
display: block;
|
display: block;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-item {
|
.menu-item {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue';
|
||||||
import App from './App.vue'
|
import App from './App.vue';
|
||||||
import {
|
import {
|
||||||
Button as TButton,
|
Button as TButton,
|
||||||
Input as TInput,
|
Input as TInput,
|
||||||
@ -25,7 +25,7 @@ import {
|
|||||||
Space as TSpace,
|
Space as TSpace,
|
||||||
Checkbox as TCheckbox,
|
Checkbox as TCheckbox,
|
||||||
Popup as TPopup,
|
Popup as TPopup,
|
||||||
Dialog as TDialog
|
Dialog as TDialog,
|
||||||
} from 'tdesign-vue-next';
|
} from 'tdesign-vue-next';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
import 'tdesign-vue-next/es/style/index.css';
|
import 'tdesign-vue-next/es/style/index.css';
|
||||||
@ -57,4 +57,4 @@ app.use(TSpace);
|
|||||||
app.use(TCheckbox);
|
app.use(TCheckbox);
|
||||||
app.use(TPopup);
|
app.use(TPopup);
|
||||||
app.use(TDialog);
|
app.use(TDialog);
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
|
|||||||
@ -3,11 +3,12 @@
|
|||||||
<div>
|
<div>
|
||||||
<t-divider content="面板关于信息" align="left" />
|
<t-divider content="面板关于信息" align="left" />
|
||||||
<t-alert theme="success" message="NapCat.WebUi is running" />
|
<t-alert theme="success" message="NapCat.WebUi is running" />
|
||||||
|
|
||||||
<t-list class="list">
|
<t-list class="list">
|
||||||
<t-list-item class="list-item">
|
<t-list-item class="list-item">
|
||||||
<span class="item-label">开发人员:</span>
|
<span class="item-label">开发人员:</span>
|
||||||
<span class="item-content"><t-link href="mailto:nanaeonn@outlook.com">Mlikiowa</t-link></span>
|
<span class="item-content">
|
||||||
|
<t-link href="mailto:nanaeonn@outlook.com">Mlikiowa</t-link>
|
||||||
|
</span>
|
||||||
</t-list-item>
|
</t-list-item>
|
||||||
<t-list-item class="list-item">
|
<t-list-item class="list-item">
|
||||||
<span class="item-label">版本信息:</span>
|
<span class="item-label">版本信息:</span>
|
||||||
@ -22,15 +23,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
export default {
|
const joinQQGroup = () => {
|
||||||
methods: {
|
// 加入QQ群的逻辑
|
||||||
joinQQGroup() {
|
window.open('https://jq.qq.com/?_wv=1027&k=123456789', '_blank');
|
||||||
// 加入QQ群的逻辑
|
};
|
||||||
window.open('https://jq.qq.com/?_wv=1027&k=123456789', '_blank');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -59,11 +56,11 @@ export default {
|
|||||||
flex: 2;
|
flex: 2;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: flex-end; /* 添加这一行 */
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-item {
|
.tag-item {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,18 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<t-space>
|
<t-space>
|
||||||
<t-tabs v-model="value" theme="card" :addable="true" @add="showAddTabDialog" @remove="removeTab">
|
<t-tabs v-model="value" :addable="true" theme="card" @add="showAddTabDialog" @remove="removeTab">
|
||||||
<t-tab-panel v-for="data in panelData" :key="data.value" :value="data.value" :label="data.label"
|
<t-tab-panel
|
||||||
:removable="data.removable">
|
v-for="data in panelData"
|
||||||
|
:key="data.value"
|
||||||
|
:label="data.label"
|
||||||
|
:removable="data.removable"
|
||||||
|
:value="data.value"
|
||||||
|
>
|
||||||
<component :is="data.component" :config="data.config" />
|
<component :is="data.component" :config="data.config" />
|
||||||
</t-tab-panel>
|
</t-tab-panel>
|
||||||
</t-tabs>
|
</t-tabs>
|
||||||
</t-space>
|
</t-space>
|
||||||
<t-dialog :visible.sync="isDialogVisible" header="添加新选项卡" @confirm="addTab" @close="isDialogVisible = false">
|
<t-dialog
|
||||||
<t-form :model="newTab" ref="form">
|
v-model:visible="isDialogVisible"
|
||||||
<t-form-item label="名称" name="name" :rules="[{ required: true, message: '请输入名称' }]">
|
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-input v-model="newTab.name" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="类型" name="type" :rules="[{ required: true, message: '请选择类型' }]">
|
<t-form-item :rules="[{ required: true, message: '请选择类型' }]" label="类型" name="type">
|
||||||
<t-select v-model="newTab.type">
|
<t-select v-model="newTab.type">
|
||||||
<t-option value="httpServers">HTTP 服务器</t-option>
|
<t-option value="httpServers">HTTP 服务器</t-option>
|
||||||
<t-option value="httpClients">HTTP 客户端</t-option>
|
<t-option value="httpClients">HTTP 客户端</t-option>
|
||||||
@ -24,55 +34,61 @@
|
|||||||
</t-dialog>
|
</t-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { ref, shallowRef, onMounted, watch, nextTick } from 'vue';
|
import { nextTick, onMounted, ref, shallowRef, watch } from 'vue';
|
||||||
import { defaultOnebotConfig, mergeOnebotConfigs } from '../../../src/onebot/config/config';
|
import { defaultOnebotConfig, mergeOnebotConfigs } from '../../../src/onebot/config/config';
|
||||||
import { QQLoginManager } from '../backend/shell';
|
import { QQLoginManager } from '@/backend/shell';
|
||||||
import HttpServerComponent from './network/HttpServerComponent.vue';
|
import HttpServerComponent from './network/HttpServerComponent.vue';
|
||||||
import HttpClientComponent from './network/HttpClientComponent.vue';
|
import HttpClientComponent from './network/HttpClientComponent.vue';
|
||||||
import WebsocketServerComponent from './network/WebsocketServerComponent.vue';
|
import WebsocketServerComponent from './network/WebsocketServerComponent.vue';
|
||||||
import WebsocketClientComponent from './network/WebsocketClientComponent.vue';
|
import WebsocketClientComponent from './network/WebsocketClientComponent.vue';
|
||||||
|
|
||||||
let id = 0;
|
interface PanelData {
|
||||||
const value = ref('first');
|
value: string;
|
||||||
const panelData = ref([]);
|
label: string;
|
||||||
const isDialogVisible = ref(false);
|
removable: boolean;
|
||||||
const newTab = ref({ name: '', type: '' });
|
component: any;
|
||||||
|
config: { name: string };
|
||||||
|
}
|
||||||
|
|
||||||
const componentMap = {
|
let id = 0;
|
||||||
'httpServers': shallowRef(HttpServerComponent),
|
const value = ref<string>('first');
|
||||||
'httpClients': shallowRef(HttpClientComponent),
|
const panelData = ref<PanelData[]>([]);
|
||||||
'websocketServers': shallowRef(WebsocketServerComponent),
|
const isDialogVisible = ref<boolean>(false);
|
||||||
'websocketClients': shallowRef(WebsocketClientComponent),
|
const newTab = ref<{ name: string; type: string }>({ name: '', type: '' });
|
||||||
|
|
||||||
|
const componentMap: Record<string, any> = {
|
||||||
|
httpServers: shallowRef(HttpServerComponent),
|
||||||
|
httpClients: shallowRef(HttpClientComponent),
|
||||||
|
websocketServers: shallowRef(WebsocketServerComponent),
|
||||||
|
websocketClients: shallowRef(WebsocketClientComponent),
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOB11Config = async () => {
|
const getOB11Config = async (): Promise<any | undefined> => {
|
||||||
const storedCredential = localStorage.getItem('auth');
|
const storedCredential = localStorage.getItem('auth');
|
||||||
if (!storedCredential) {
|
if (!storedCredential) {
|
||||||
console.error('No stored credential found');
|
console.error('No stored credential found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const loginManager = new QQLoginManager(storedCredential);
|
const loginManager = new QQLoginManager(storedCredential);
|
||||||
const config = await loginManager.GetOB11Config();
|
return await loginManager.GetOB11Config();
|
||||||
return config;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const setOB11Config = async (config) => {
|
const setOB11Config = async (config: any): Promise<boolean> => {
|
||||||
const storedCredential = localStorage.getItem('auth');
|
const storedCredential = localStorage.getItem('auth');
|
||||||
if (!storedCredential) {
|
if (!storedCredential) {
|
||||||
console.error('No stored credential found');
|
console.error('No stored credential found');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const loginManager = new QQLoginManager(storedCredential);
|
const loginManager = new QQLoginManager(storedCredential);
|
||||||
const result = await loginManager.SetOB11Config(config);
|
return await loginManager.SetOB11Config(config);
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const log = (message, data) => {
|
const log = (message: string, data: any) => {
|
||||||
console.log(message, data);
|
console.log(message, data);
|
||||||
};
|
};
|
||||||
|
|
||||||
const createPanel = (type, name, id) => {
|
const createPanel = (type: string, name: string, id: number): PanelData => {
|
||||||
return {
|
return {
|
||||||
value: `${type}-${id}`,
|
value: `${type}-${id}`,
|
||||||
label: name,
|
label: name,
|
||||||
@ -82,10 +98,10 @@ const createPanel = (type, name, id) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const generatePanels = (networkConfig) => {
|
const generatePanels = (networkConfig: any): PanelData[] => {
|
||||||
const panels = [];
|
const panels: PanelData[] = [];
|
||||||
Object.keys(networkConfig).forEach((key) => {
|
Object.keys(networkConfig).forEach((key) => {
|
||||||
networkConfig[key].forEach((config, index) => {
|
networkConfig[key].forEach((config: any, index: number) => {
|
||||||
const component = componentMap[key];
|
const component = componentMap[key];
|
||||||
if (!component) {
|
if (!component) {
|
||||||
console.error(`No component found for key: ${key}`);
|
console.error(`No component found for key: ${key}`);
|
||||||
@ -100,6 +116,7 @@ const generatePanels = (networkConfig) => {
|
|||||||
const loadConfig = async () => {
|
const loadConfig = async () => {
|
||||||
try {
|
try {
|
||||||
const userConfig = await getOB11Config();
|
const userConfig = await getOB11Config();
|
||||||
|
if (!userConfig) return;
|
||||||
const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig);
|
const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig);
|
||||||
const networkConfig = mergedConfig.network;
|
const networkConfig = mergedConfig.network;
|
||||||
log('networkConfig:', networkConfig);
|
log('networkConfig:', networkConfig);
|
||||||
@ -130,7 +147,11 @@ const addTab = async () => {
|
|||||||
value.value = newPanel.value; // 强制重新渲染选项卡
|
value.value = newPanel.value; // 强制重新渲染选项卡
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeTab = ({ value: val, index }) => {
|
const closeDialog = () => {
|
||||||
|
isDialogVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeTab = ({ value: val, index }: { value: string; index: number }) => {
|
||||||
if (index < 0) return false;
|
if (index < 0) return false;
|
||||||
panelData.value.splice(index, 1);
|
panelData.value.splice(index, 1);
|
||||||
if (panelData.value.length === 0) return;
|
if (panelData.value.length === 0) return;
|
||||||
@ -140,8 +161,8 @@ const removeTab = ({ value: val, index }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const syncConfig = async () => {
|
const syncConfig = async () => {
|
||||||
const networkConfig = {};
|
const networkConfig: Record<string, any[]> = {};
|
||||||
panelData.value.forEach(panel => {
|
panelData.value.forEach((panel) => {
|
||||||
const key = panel.value.split('-')[0];
|
const key = panel.value.split('-')[0];
|
||||||
if (!networkConfig[key]) {
|
if (!networkConfig[key]) {
|
||||||
networkConfig[key] = [];
|
networkConfig[key] = [];
|
||||||
@ -149,6 +170,7 @@ const syncConfig = async () => {
|
|||||||
networkConfig[key].push(panel.config);
|
networkConfig[key].push(panel.config);
|
||||||
});
|
});
|
||||||
const userConfig = await getOB11Config();
|
const userConfig = await getOB11Config();
|
||||||
|
if (!userConfig) return;
|
||||||
const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig);
|
const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig);
|
||||||
mergedConfig.network = networkConfig;
|
mergedConfig.network = networkConfig;
|
||||||
await setOB11Config(mergedConfig);
|
await setOB11Config(mergedConfig);
|
||||||
@ -159,4 +181,4 @@ watch(panelData, syncConfig, { deep: true });
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadConfig();
|
loadConfig();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -4,4 +4,4 @@
|
|||||||
<t-divider content="其余配置" align="left" />
|
<t-divider content="其余配置" align="left" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,29 +1,42 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h3>HTTP Client 配置</h3>
|
<h3>HTTP Client 配置</h3>
|
||||||
<t-form>
|
<t-form>
|
||||||
<t-form-item label="URL">
|
<t-form-item label="URL">
|
||||||
<t-input v-model="config.url" />
|
<t-input v-model="config.url" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="消息格式">
|
<t-form-item label="消息格式">
|
||||||
<t-input v-model="config.messagePostFormat" />
|
<t-input v-model="config.messagePostFormat" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="报告自身消息">
|
<t-form-item label="报告自身消息">
|
||||||
<t-checkbox v-model="config.reportSelfMessage" />
|
<t-checkbox v-model="config.reportSelfMessage" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="Token">
|
<t-form-item label="Token">
|
||||||
<t-input v-model="config.token" />
|
<t-input v-model="config.token" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="调试模式">
|
<t-form-item label="调试模式">
|
||||||
<t-checkbox v-model="config.debug" />
|
<t-checkbox v-model="config.debug" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { defineProps } from 'vue';
|
import { ref } from 'vue';
|
||||||
defineProps({
|
|
||||||
config: Object,
|
interface HttpClientConfig {
|
||||||
|
url: string;
|
||||||
|
messagePostFormat: string;
|
||||||
|
reportSelfMessage: boolean;
|
||||||
|
token: string;
|
||||||
|
debug: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = ref<HttpClientConfig>({
|
||||||
|
url: '',
|
||||||
|
messagePostFormat: '',
|
||||||
|
reportSelfMessage: false,
|
||||||
|
token: '',
|
||||||
|
debug: false,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,38 +1,57 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h3>HTTP Server 配置</h3>
|
<h3>HTTP Server 配置</h3>
|
||||||
<t-form>
|
<t-form>
|
||||||
<t-form-item label="端口">
|
<t-form-item label="端口">
|
||||||
<t-input v-model.number="config.port" type="number" />
|
<t-input v-model.number="config.port" type="number" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="主机">
|
<t-form-item label="主机">
|
||||||
<t-input v-model="config.host" type="text" />
|
<t-input v-model="config.host" type="text" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="启用 CORS">
|
<t-form-item label="启用 CORS">
|
||||||
<t-checkbox v-model="config.enableCors" />
|
<t-checkbox v-model="config.enableCors" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="启用 WebSocket">
|
<t-form-item label="启用 WebSocket">
|
||||||
<t-checkbox v-model="config.enableWebsocket" />
|
<t-checkbox v-model="config.enableWebsocket" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="消息格式">
|
<t-form-item label="消息格式">
|
||||||
<t-input v-model="config.messagePostFormat" type="text" />
|
<t-input v-model="config.messagePostFormat" type="text" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="报告自身消息">
|
<t-form-item label="报告自身消息">
|
||||||
<t-checkbox v-model="config.reportSelfMessage" />
|
<t-checkbox v-model="config.reportSelfMessage" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="Token">
|
<t-form-item label="Token">
|
||||||
<t-input v-model="config.token" type="text" />
|
<t-input v-model="config.token" type="text" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="调试模式">
|
<t-form-item label="调试模式">
|
||||||
<t-checkbox v-model="config.debug" />
|
<t-checkbox v-model="config.debug" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { defineProps } from 'vue';
|
import { ref } from 'vue';
|
||||||
defineProps({
|
|
||||||
config: Object,
|
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,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,32 +1,47 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h3>WebSocket Client 配置</h3>
|
<h3>WebSocket Client 配置</h3>
|
||||||
<t-form>
|
<t-form>
|
||||||
<t-form-item label="URL">
|
<t-form-item label="URL">
|
||||||
<t-input v-model="config.url" />
|
<t-input v-model="config.url" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="消息格式">
|
<t-form-item label="消息格式">
|
||||||
<t-input v-model="config.messagePostFormat" />
|
<t-input v-model="config.messagePostFormat" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="报告自身消息">
|
<t-form-item label="报告自身消息">
|
||||||
<t-checkbox v-model="config.reportSelfMessage" />
|
<t-checkbox v-model="config.reportSelfMessage" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="Token">
|
<t-form-item label="Token">
|
||||||
<t-input v-model="config.token" />
|
<t-input v-model="config.token" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="调试模式">
|
<t-form-item label="调试模式">
|
||||||
<t-checkbox v-model="config.debug" />
|
<t-checkbox v-model="config.debug" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="心跳间隔">
|
<t-form-item label="心跳间隔">
|
||||||
<t-input v-model.number="config.heartInterval" type="number" />
|
<t-input v-model.number="config.heartInterval" type="number" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { defineProps } from 'vue';
|
import { ref } from 'vue';
|
||||||
defineProps({
|
|
||||||
config: Object,
|
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,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,38 +1,57 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h3>WebSocket Server 配置</h3>
|
<h3>WebSocket Server 配置</h3>
|
||||||
<t-form>
|
<t-form>
|
||||||
<t-form-item label="主机">
|
<t-form-item label="主机">
|
||||||
<t-input v-model="config.host" />
|
<t-input v-model="config.host" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="端口">
|
<t-form-item label="端口">
|
||||||
<t-input v-model.number="config.port" type="number" />
|
<t-input v-model.number="config.port" type="number" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="消息格式">
|
<t-form-item label="消息格式">
|
||||||
<t-input v-model="config.messagePostFormat" />
|
<t-input v-model="config.messagePostFormat" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="报告自身消息">
|
<t-form-item label="报告自身消息">
|
||||||
<t-checkbox v-model="config.reportSelfMessage" />
|
<t-checkbox v-model="config.reportSelfMessage" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="Token">
|
<t-form-item label="Token">
|
||||||
<t-input v-model="config.token" />
|
<t-input v-model="config.token" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="启用推送事件">
|
<t-form-item label="启用推送事件">
|
||||||
<t-checkbox v-model="config.enablePushEvent" />
|
<t-checkbox v-model="config.enablePushEvent" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="调试模式">
|
<t-form-item label="调试模式">
|
||||||
<t-checkbox v-model="config.debug" />
|
<t-checkbox v-model="config.debug" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item label="心跳间隔">
|
<t-form-item label="心跳间隔">
|
||||||
<t-input v-model.number="config.heartInterval" type="number" />
|
<t-input v-model.number="config.heartInterval" type="number" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
</t-form>
|
</t-form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import { defineProps } from 'vue';
|
import { ref } from 'vue';
|
||||||
defineProps({
|
|
||||||
config: Object,
|
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,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
|
||||||
import Dashboard from '../components/Dashboard.vue';
|
import Dashboard from '../components/Dashboard.vue';
|
||||||
import BasicInfo from '../pages/BasicInfo.vue';
|
import BasicInfo from '../pages/BasicInfo.vue';
|
||||||
import AboutUs from '../pages/AboutUs.vue';
|
import AboutUs from '../pages/AboutUs.vue';
|
||||||
@ -8,25 +8,25 @@ import QQLogin from '../components/QQLogin.vue';
|
|||||||
import WebUiLogin from '../components/WebUiLogin.vue';
|
import WebUiLogin from '../components/WebUiLogin.vue';
|
||||||
import OtherConfig from '../pages/OtherConfig.vue';
|
import OtherConfig from '../pages/OtherConfig.vue';
|
||||||
|
|
||||||
const routes = [
|
const routes: Array<RouteRecordRaw> = [
|
||||||
{ path: '/', redirect: '/webui' },
|
{ path: '/', redirect: '/webui' },
|
||||||
{ path: '/webui', component: WebUiLogin, name: 'WebUiLogin' },
|
{ path: '/webui', component: WebUiLogin, name: 'WebUiLogin' },
|
||||||
{ path: '/qqlogin', component: QQLogin, name: 'QQLogin' },
|
{ path: '/qqlogin', component: QQLogin, name: 'QQLogin' },
|
||||||
{
|
{
|
||||||
path: '/dashboard',
|
path: '/dashboard',
|
||||||
component: Dashboard,
|
component: Dashboard,
|
||||||
children: [
|
children: [
|
||||||
{ path: '', redirect: 'basic-info' },
|
{ path: '', redirect: 'basic-info' },
|
||||||
{ path: 'basic-info', component: BasicInfo, name: 'BasicInfo' },
|
{ path: 'basic-info', component: BasicInfo, name: 'BasicInfo' },
|
||||||
{ path: 'network-config', component: NetWork, name: 'NetWork' },
|
{ path: 'network-config', component: NetWork, name: 'NetWork' },
|
||||||
{ path: 'log-view', component: LogView, name: 'LogView' },
|
{ path: 'log-view', component: LogView, name: 'LogView' },
|
||||||
{ path: 'other-config', component: OtherConfig, name: 'OtherConfig' },
|
{ path: 'other-config', component: OtherConfig, name: 'OtherConfig' },
|
||||||
{ path: 'about-us', component: AboutUs, name: 'AboutUs' }
|
{ path: 'about-us', component: AboutUs, name: 'AboutUs' },
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
export const router = createRouter({
|
export const router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
routes,
|
routes,
|
||||||
})
|
});
|
||||||
|
|||||||
5
napcat.webui/src/shims-vue.d.ts
vendored
5
napcat.webui/src/shims-vue.d.ts
vendored
@ -1,5 +0,0 @@
|
|||||||
declare module '*.vue' {
|
|
||||||
import { DefineComponent } from 'vue';
|
|
||||||
const component: DefineComponent<{}, {}, any>;
|
|
||||||
export default component;
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
||||||
"target": "ES2020",
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
||||||
"skipLibCheck": true,
|
|
||||||
|
|
||||||
/* Bundler mode */
|
|
||||||
"moduleResolution": "Bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"moduleDetection": "force",
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
|
|
||||||
/* Linting */
|
|
||||||
"strict": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"noUncheckedSideEffectImports": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
|
||||||
}
|
|
||||||
@ -1,7 +1,34 @@
|
|||||||
{
|
{
|
||||||
"files": [],
|
"compilerOptions": {
|
||||||
"references": [
|
"target": "ESNext",
|
||||||
{ "path": "./tsconfig.app.json" },
|
"jsx": "preserve",
|
||||||
{ "path": "./tsconfig.node.json" }
|
"jsxImportSource": "vue",
|
||||||
]
|
"lib": [
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable"
|
||||||
|
],
|
||||||
|
"baseUrl": ".",
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"types": [
|
||||||
|
"vite/client"
|
||||||
|
],
|
||||||
|
"strict": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"useDefineForClassFields": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules"],
|
||||||
|
"references": [{"path": "./tsconfig.node.json"}]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,11 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
"composite": true,
|
||||||
"target": "ES2022",
|
"skipLibCheck": true,
|
||||||
"lib": ["ES2023"],
|
"module": "ESNext",
|
||||||
"module": "ESNext",
|
"moduleResolution": "bundler",
|
||||||
"skipLibCheck": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strictNullChecks": true
|
||||||
/* Bundler mode */
|
},
|
||||||
"moduleResolution": "Bundler",
|
"include": ["vite.config.ts"]
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"moduleDetection": "force",
|
|
||||||
"noEmit": true,
|
|
||||||
|
|
||||||
/* Linting */
|
|
||||||
"strict": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"noUncheckedSideEffectImports": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite';
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
base: './'
|
base: './',
|
||||||
})
|
});
|
||||||
|
|||||||
@ -48,9 +48,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^5.0.0",
|
"express": "^5.0.0",
|
||||||
"silk-wasm": "^3.6.1",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
"ws": "^8.18.0",
|
|
||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"fluent-ffmpeg": "^2.1.2"
|
"silk-wasm": "^3.6.1",
|
||||||
|
"ws": "^8.18.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -300,18 +300,18 @@ export class NTQQFileApi {
|
|||||||
element.elementType === ElementType.FILE
|
element.elementType === ElementType.FILE
|
||||||
) {
|
) {
|
||||||
switch (element.elementType) {
|
switch (element.elementType) {
|
||||||
case ElementType.PIC:
|
case ElementType.PIC:
|
||||||
element.picElement!.sourcePath = elementResults[elementIndex];
|
element.picElement!.sourcePath = elementResults[elementIndex];
|
||||||
break;
|
break;
|
||||||
case ElementType.VIDEO:
|
case ElementType.VIDEO:
|
||||||
element.videoElement!.filePath = elementResults[elementIndex];
|
element.videoElement!.filePath = elementResults[elementIndex];
|
||||||
break;
|
break;
|
||||||
case ElementType.PTT:
|
case ElementType.PTT:
|
||||||
element.pttElement!.filePath = elementResults[elementIndex];
|
element.pttElement!.filePath = elementResults[elementIndex];
|
||||||
break;
|
break;
|
||||||
case ElementType.FILE:
|
case ElementType.FILE:
|
||||||
element.fileElement!.filePath = elementResults[elementIndex];
|
element.fileElement!.filePath = elementResults[elementIndex];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
elementIndex++;
|
elementIndex++;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
|
|||||||
await Promise.all(msgList.map(async msg => {
|
await Promise.all(msgList.map(async msg => {
|
||||||
msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||||
}));
|
}));
|
||||||
let network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
|
const network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
|
||||||
//烘焙消息
|
//烘焙消息
|
||||||
const ob11MsgList = (await Promise.all(
|
const ob11MsgList = (await Promise.all(
|
||||||
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array')))
|
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array')))
|
||||||
|
|||||||
@ -43,9 +43,9 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
|
|||||||
await Promise.all(msgList.map(async msg => {
|
await Promise.all(msgList.map(async msg => {
|
||||||
msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
|
||||||
}));
|
}));
|
||||||
let network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
|
const network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
|
||||||
//烘焙消息
|
//烘焙消息
|
||||||
let msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array';
|
const msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array';
|
||||||
const ob11MsgList = (await Promise.all(
|
const ob11MsgList = (await Promise.all(
|
||||||
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, msgFormat)))
|
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, msgFormat)))
|
||||||
).filter(msg => msg !== undefined);
|
).filter(msg => msg !== undefined);
|
||||||
|
|||||||
@ -31,8 +31,8 @@ export class GetGroupEssence extends BaseAction<Payload, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _handle(payload: Payload, adapter: string) {
|
async _handle(payload: Payload, adapter: string) {
|
||||||
let network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
|
const network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
|
||||||
let msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array';
|
const msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array';
|
||||||
const msglist = (await this.core.apis.WebApi.getGroupEssenceMsgAll(payload.group_id.toString())).flatMap((e) => e.data.msg_list);
|
const msglist = (await this.core.apis.WebApi.getGroupEssenceMsgAll(payload.group_id.toString())).flatMap((e) => e.data.msg_list);
|
||||||
if (!msglist) {
|
if (!msglist) {
|
||||||
throw new Error('获取失败');
|
throw new Error('获取失败');
|
||||||
|
|||||||
@ -24,8 +24,8 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
|
|||||||
|
|
||||||
async _handle(payload: Payload, adapter: string) {
|
async _handle(payload: Payload, adapter: string) {
|
||||||
// log("history msg ids", Object.keys(msgHistory));
|
// log("history msg ids", Object.keys(msgHistory));
|
||||||
let network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
|
const network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
|
||||||
let msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array';
|
const msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array';
|
||||||
if (!payload.message_id) {
|
if (!payload.message_id) {
|
||||||
throw Error('参数message_id不能为空');
|
throw Error('参数message_id不能为空');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,9 +17,9 @@ export default class GetRecentContact extends BaseAction<Payload, any> {
|
|||||||
|
|
||||||
async _handle(payload: Payload, adapter: string) {
|
async _handle(payload: Payload, adapter: string) {
|
||||||
const ret = await this.core.apis.UserApi.getRecentContactListSnapShot(+(payload.count || 10));
|
const ret = await this.core.apis.UserApi.getRecentContactListSnapShot(+(payload.count || 10));
|
||||||
let network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
|
const network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
|
||||||
//烘焙消息
|
//烘焙消息
|
||||||
let msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array';
|
const msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array';
|
||||||
return await Promise.all(ret.info.changedList.map(async (t) => {
|
return await Promise.all(ret.info.changedList.map(async (t) => {
|
||||||
const FastMsg = await this.core.apis.MsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
|
const FastMsg = await this.core.apis.MsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
|
||||||
if (FastMsg.msgList.length > 0) {
|
if (FastMsg.msgList.length > 0) {
|
||||||
|
|||||||
@ -85,7 +85,7 @@ export const defaultOnebotConfig = createDefaultConfig<OnebotConfig>({
|
|||||||
},
|
},
|
||||||
musicSignUrl: "",
|
musicSignUrl: "",
|
||||||
enableLocalFile2Url: false
|
enableLocalFile2Url: false
|
||||||
})
|
});
|
||||||
export const mergeNetworkDefaultConfig = {
|
export const mergeNetworkDefaultConfig = {
|
||||||
httpServers: httpServerDefaultConfigs,
|
httpServers: httpServerDefaultConfigs,
|
||||||
httpClients: httpClientDefaultConfigs,
|
httpClients: httpClientDefaultConfigs,
|
||||||
|
|||||||
@ -100,7 +100,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
this.context.logger.setLogSelfInfo(selfInfo);
|
this.context.logger.setLogSelfInfo(selfInfo);
|
||||||
}).catch(this.context.logger.logError.bind(this.context.logger));
|
}).catch(this.context.logger.logError.bind(this.context.logger));
|
||||||
|
|
||||||
let serviceInfo = await this.creatOneBotLog(ob11Config);
|
const serviceInfo = await this.creatOneBotLog(ob11Config);
|
||||||
this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`);
|
this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`);
|
||||||
|
|
||||||
// //创建NetWork服务
|
// //创建NetWork服务
|
||||||
@ -497,14 +497,14 @@ export class NapCatOneBot11Adapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async emitMsg(message: RawMessage) {
|
private async emitMsg(message: RawMessage) {
|
||||||
let network = Object.values(this.configLoader.configData.network) as Array<typeof this.configLoader.configData.network[keyof typeof this.configLoader.configData.network]>;
|
const network = Object.values(this.configLoader.configData.network) as Array<typeof this.configLoader.configData.network[keyof typeof this.configLoader.configData.network]>;
|
||||||
this.context.logger.logDebug('收到新消息 RawMessage', message);
|
this.context.logger.logDebug('收到新消息 RawMessage', message);
|
||||||
this.apis.MsgApi.parseMessageV2(message).then((ob11Msg) => {
|
this.apis.MsgApi.parseMessageV2(message).then((ob11Msg) => {
|
||||||
if (!ob11Msg) return;
|
if (!ob11Msg) return;
|
||||||
const isSelfMsg = ob11Msg.stringMsg.user_id.toString() == this.core.selfInfo.uin || ob11Msg.arrayMsg.user_id.toString() == this.core.selfInfo.uin;
|
const isSelfMsg = ob11Msg.stringMsg.user_id.toString() == this.core.selfInfo.uin || ob11Msg.arrayMsg.user_id.toString() == this.core.selfInfo.uin;
|
||||||
this.context.logger.logDebug('转化为 OB11Message', ob11Msg);
|
this.context.logger.logDebug('转化为 OB11Message', ob11Msg);
|
||||||
let msgMap: Map<string, OB11Message> = new Map();
|
const msgMap: Map<string, OB11Message> = new Map();
|
||||||
let enable_client: string[] = [];
|
const enable_client: string[] = [];
|
||||||
network.flat().filter(e => e.enable).map(e => {
|
network.flat().filter(e => e.enable).map(e => {
|
||||||
enable_client.push(e.name);
|
enable_client.push(e.name);
|
||||||
if (e.messagePostFormat == 'string') {
|
if (e.messagePostFormat == 'string') {
|
||||||
@ -518,7 +518,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let debug_network = network.flat().filter(e => e.enable && e.debug);
|
const debug_network = network.flat().filter(e => e.enable && e.debug);
|
||||||
if (debug_network.length > 0) {
|
if (debug_network.length > 0) {
|
||||||
for (const adapter of debug_network) {
|
for (const adapter of debug_network) {
|
||||||
if (adapter.name) {
|
if (adapter.name) {
|
||||||
@ -534,7 +534,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
let notreportSelf_network = network.flat().filter(e => e.enable && !e.reportSelfMessage);
|
const notreportSelf_network = network.flat().filter(e => e.enable && !e.reportSelfMessage);
|
||||||
if (isSelfMsg) {
|
if (isSelfMsg) {
|
||||||
for (const adapter of notreportSelf_network) {
|
for (const adapter of notreportSelf_network) {
|
||||||
msgMap.delete(adapter.name);
|
msgMap.delete(adapter.name);
|
||||||
|
|||||||
@ -38,7 +38,7 @@ export class OB11NetworkManager {
|
|||||||
return Promise.all(Array.from(map.entries()).map(([name, event]) => {
|
return Promise.all(Array.from(map.entries()).map(([name, event]) => {
|
||||||
const adapter = this.adapters.get(name);
|
const adapter = this.adapters.get(name);
|
||||||
if (adapter) {
|
if (adapter) {
|
||||||
return adapter.onEvent(event);
|
return adapter.onEvent(event);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,7 @@ export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapp
|
|||||||
app.use(config.prefix + '/webui', express.static(pathWrapper.staticPath));
|
app.use(config.prefix + '/webui', express.static(pathWrapper.staticPath));
|
||||||
//挂载API接口
|
//挂载API接口
|
||||||
// 添加CORS支持
|
// 添加CORS支持
|
||||||
|
// TODO:
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
res.header('Access-Control-Allow-Origin', '*');
|
res.header('Access-Control-Allow-Origin', '*');
|
||||||
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||||
@ -47,22 +48,22 @@ export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapp
|
|||||||
app.listen(config.port, config.host, async () => {
|
app.listen(config.port, config.host, async () => {
|
||||||
log(`[NapCat] [WebUi] Current WebUi is running at http://${config.host}:${config.port}${config.prefix}`);
|
log(`[NapCat] [WebUi] Current WebUi is running at http://${config.host}:${config.port}${config.prefix}`);
|
||||||
log(`[NapCat] [WebUi] Login Token is ${config.token}`);
|
log(`[NapCat] [WebUi] Login Token is ${config.token}`);
|
||||||
log(`[NapCat] [WebUi] WebUi User Panel Url: http://${config.host}:${config.port}${config.prefix}/webui?token=${config.token}`);
|
log(
|
||||||
log(`[NapCat] [WebUi] WebUi Local Panel Url: http://127.0.0.1:${config.port}${config.prefix}/webui?token=${config.token}`);
|
`[NapCat] [WebUi] WebUi User Panel Url: http://${config.host}:${config.port}${config.prefix}/webui?token=${config.token}`
|
||||||
|
);
|
||||||
|
log(
|
||||||
|
`[NapCat] [WebUi] WebUi Local Panel Url: http://127.0.0.1:${config.port}${config.prefix}/webui?token=${config.token}`
|
||||||
|
);
|
||||||
//获取上网Ip
|
//获取上网Ip
|
||||||
//https://www.ip.cn/api/index?ip&type=0
|
//https://www.ip.cn/api/index?ip&type=0
|
||||||
RequestUtil.HttpGetJson<{ IP: {IP:string} }>(
|
RequestUtil.HttpGetJson<{ IP: { IP: string } }>('https://ip.011102.xyz/', 'GET', {}, {}, true, true)
|
||||||
'https://ip.011102.xyz/',
|
.then((data) => {
|
||||||
'GET',
|
log(
|
||||||
{},
|
`[NapCat] [WebUi] WebUi Publish Panel Url: http://${data.IP.IP}:${config.port}${config.prefix}/webui/?token=${config.token}`
|
||||||
{},
|
);
|
||||||
true,
|
})
|
||||||
true
|
.catch((err) => {
|
||||||
).then((data) => {
|
logger.logError.bind(logger)(`[NapCat] [WebUi] Get Publish Panel Url Error: ${err}`);
|
||||||
log(`[NapCat] [WebUi] WebUi Publish Panel Url: http://${data.IP.IP}:${config.port}${config.prefix}/webui/?token=${config.token}`);
|
});
|
||||||
}).catch((err) => {
|
|
||||||
logger.logError.bind(logger)(`[NapCat] [WebUi] Get Publish Panel Url Error: ${err}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user