mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
refactor: webui
This commit is contained in:
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",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"lint": "eslint . --fix",
|
||||
"dev": "vite --host 127.0.0.1",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"tdesign-vue-next": "^1.10.3",
|
||||
"vue": "^3.5.12",
|
||||
"vue-router": "^4.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.1.0",
|
||||
"@eslint/js": "^9.14.0",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@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",
|
||||
"vite": "^5.4.10",
|
||||
"vue-tsc": "^2.1.8"
|
||||
|
@@ -1,11 +1,7 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view />
|
||||
</div>
|
||||
<div id="app">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App'
|
||||
};
|
||||
</script>
|
||||
<script setup lang="ts"></script>
|
||||
|
@@ -1,14 +1,17 @@
|
||||
export class QQLoginManager {
|
||||
private retCredential: string;
|
||||
private apiprefix: string;
|
||||
private readonly apiPrefix: string;
|
||||
// TODO:
|
||||
//调试时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.apiprefix = apiprefix;
|
||||
this.apiPrefix = apiPrefix;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
public async GetOB11Config(): Promise<any> {
|
||||
try {
|
||||
const ConfigResponse = await fetch(`${this.apiprefix}/OB11Config/GetConfig`, {
|
||||
const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/GetConfig`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + this.retCredential,
|
||||
@@ -22,14 +25,14 @@ export class QQLoginManager {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error getting OB11 config:", error);
|
||||
console.error('Error getting OB11 config:', error);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
public async SetOB11Config(config: any): Promise<boolean> {
|
||||
try {
|
||||
const ConfigResponse = await fetch(`${this.apiprefix}/OB11Config/SetConfig`, {
|
||||
const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/SetConfig`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + this.retCredential,
|
||||
@@ -44,137 +47,137 @@ export class QQLoginManager {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error setting OB11 config:", error);
|
||||
console.error('Error setting OB11 config:', error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async checkQQLoginStatus(): Promise<boolean> {
|
||||
try {
|
||||
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/CheckLoginStatus`, {
|
||||
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/CheckLoginStatus`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': "Bearer " + this.retCredential,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
Authorization: 'Bearer ' + this.retCredential,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
if (QQLoginResponse.status == 200) {
|
||||
let QQLoginResponseJson = await QQLoginResponse.json();
|
||||
const QQLoginResponseJson = await QQLoginResponse.json();
|
||||
if (QQLoginResponseJson.code == 0) {
|
||||
return QQLoginResponseJson.data.isLogin;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error checking QQ login status:", error);
|
||||
console.error('Error checking QQ login status:', error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async checkWebUiLogined(): Promise<boolean> {
|
||||
try {
|
||||
let LoginResponse = await fetch(`${this.apiprefix}/auth/check`, {
|
||||
const LoginResponse = await fetch(`${this.apiPrefix}/auth/check`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': "Bearer " + this.retCredential,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
Authorization: 'Bearer ' + this.retCredential,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
if (LoginResponse.status == 200) {
|
||||
let LoginResponseJson = await LoginResponse.json();
|
||||
const LoginResponseJson = await LoginResponse.json();
|
||||
if (LoginResponseJson.code == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error checking web UI login status:", error);
|
||||
console.error('Error checking web UI login status:', error);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async loginWithToken(token: string): Promise<string | null> {
|
||||
try {
|
||||
let loginResponse = await fetch(`${this.apiprefix}/auth/login`, {
|
||||
const loginResponse = await fetch(`${this.apiPrefix}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ token: token })
|
||||
body: JSON.stringify({ token: token }),
|
||||
});
|
||||
const loginResponseJson = await loginResponse.json();
|
||||
let retCode = loginResponseJson.code;
|
||||
const retCode = loginResponseJson.code;
|
||||
if (retCode === 0) {
|
||||
this.retCredential = loginResponseJson.data.Credential;
|
||||
return this.retCredential;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error logging in with token:", error);
|
||||
console.error('Error logging in with token:', error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async getQQLoginQrcode(): Promise<string> {
|
||||
try {
|
||||
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/GetQQLoginQrcode`, {
|
||||
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/GetQQLoginQrcode`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': "Bearer " + this.retCredential,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
Authorization: 'Bearer ' + this.retCredential,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
if (QQLoginResponse.status == 200) {
|
||||
let QQLoginResponseJson = await QQLoginResponse.json();
|
||||
const QQLoginResponseJson = await QQLoginResponse.json();
|
||||
if (QQLoginResponseJson.code == 0) {
|
||||
return QQLoginResponseJson.data.qrcode || "";
|
||||
return QQLoginResponseJson.data.qrcode || '';
|
||||
}
|
||||
}
|
||||
} 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[]> {
|
||||
try {
|
||||
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/GetQuickLoginList`, {
|
||||
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/GetQuickLoginList`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': "Bearer " + this.retCredential,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
Authorization: 'Bearer ' + this.retCredential,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
if (QQLoginResponse.status == 200) {
|
||||
let QQLoginResponseJson = await QQLoginResponse.json();
|
||||
const QQLoginResponseJson = await QQLoginResponse.json();
|
||||
if (QQLoginResponseJson.code == 0) {
|
||||
return QQLoginResponseJson.data || [];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error getting QQ quick login list:", error);
|
||||
console.error('Error getting QQ quick login list:', error);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
public async setQuickLogin(uin: string): Promise<{ result: boolean, errMsg: string }> {
|
||||
public async setQuickLogin(uin: string): Promise<{ result: boolean; errMsg: string }> {
|
||||
try {
|
||||
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/SetQuickLogin`, {
|
||||
const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/SetQuickLogin`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': "Bearer " + this.retCredential,
|
||||
'Content-Type': 'application/json'
|
||||
Authorization: 'Bearer ' + this.retCredential,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ uin: uin })
|
||||
body: JSON.stringify({ uin: uin }),
|
||||
});
|
||||
if (QQLoginResponse.status == 200) {
|
||||
let QQLoginResponseJson = await QQLoginResponse.json();
|
||||
const QQLoginResponseJson = await QQLoginResponse.json();
|
||||
if (QQLoginResponseJson.code == 0) {
|
||||
return { result: true, errMsg: "" };
|
||||
return { result: true, errMsg: '' };
|
||||
} else {
|
||||
return { result: false, errMsg: QQLoginResponseJson.message };
|
||||
}
|
||||
}
|
||||
} 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>
|
||||
<div class="dashboard-container">
|
||||
<SidebarMenu :menuItems="menuItems" class="sidebar-menu" />
|
||||
<div class="content">
|
||||
<router-view />
|
||||
<div class="dashboard-container">
|
||||
<SidebarMenu :menu-items="menuItems" class="sidebar-menu" />
|
||||
<div class="content">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import SidebarMenu from './webui/Nav.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SidebarMenu
|
||||
},
|
||||
data() {
|
||||
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' }
|
||||
]
|
||||
};
|
||||
}
|
||||
interface MenuItem {
|
||||
value: string;
|
||||
icon: string;
|
||||
label: string;
|
||||
route: string;
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
<style scoped>
|
||||
.dashboard-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.sidebar-menu {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.content {
|
||||
padding: 10px;
|
||||
}
|
||||
.content {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@@ -2,67 +2,91 @@
|
||||
<div class="login-container">
|
||||
<h2 class="sotheby-font">QQ Login</h2>
|
||||
<div class="login-methods">
|
||||
<t-button id="quick-login" class="login-method" :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>
|
||||
<t-button
|
||||
id="quick-login"
|
||||
class="login-method"
|
||||
: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 id="quick-login-dropdown" class="login-form" v-show="loginMethod === 'quick'">
|
||||
<t-select id="quick-login-select" v-model="selectedAccount" @change="selectAccount"
|
||||
placeholder="Select Account">
|
||||
<div v-show="loginMethod === 'quick'" id="quick-login-dropdown" class="login-form">
|
||||
<t-select
|
||||
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-select>
|
||||
</div>
|
||||
<div id="qrcode" class="qrcode" v-show="loginMethod === 'qrcode'">
|
||||
<div v-show="loginMethod === 'qrcode'" id="qrcode" class="qrcode">
|
||||
<canvas ref="qrcodeCanvas"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import QRCode from 'qrcode';
|
||||
import * as QRCode from 'qrcode';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { QQLoginManager } from '../backend/shell.ts';
|
||||
import { QQLoginManager } from '@/backend/shell';
|
||||
|
||||
const router = useRouter();
|
||||
const loginMethod = ref('quick');
|
||||
const quickLoginList = ref([]);
|
||||
const selectedAccount = ref('');
|
||||
const qrcodeCanvas = ref(null);
|
||||
const qqLoginManager = new QQLoginManager(localStorage.getItem('auth'));
|
||||
let heartBeatTimer = null;
|
||||
const loginMethod = ref<'quick' | 'qrcode'>('quick');
|
||||
const quickLoginList = ref<string[]>([]);
|
||||
const selectedAccount = ref<string>('');
|
||||
const qrcodeCanvas = ref<HTMLCanvasElement | null>(null);
|
||||
const qqLoginManager = new QQLoginManager(localStorage.getItem('auth') || '');
|
||||
let heartBeatTimer: number | null = null;
|
||||
|
||||
const selectAccount = async (accountName) => {
|
||||
const selectAccount = async (accountName: string): Promise<void> => {
|
||||
const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName);
|
||||
if (result) {
|
||||
MessagePlugin.success("登录成功即将跳转");
|
||||
await MessagePlugin.success('登录成功即将跳转');
|
||||
await router.push({ path: '/dashboard/basic-info' });
|
||||
} else {
|
||||
MessagePlugin.error("登录失败," + errMsg);
|
||||
await MessagePlugin.error('登录失败,' + errMsg);
|
||||
}
|
||||
};
|
||||
|
||||
const generateQrCode = (data, canvas) => {
|
||||
QRCode.toCanvas(canvas, data, function (error) {
|
||||
if (error) console.log(error);
|
||||
console.log('QR Code generated!');
|
||||
const generateQrCode = (data: string, canvas: HTMLCanvasElement | null): void => {
|
||||
if (!canvas) {
|
||||
console.error('Canvas element not found');
|
||||
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 () => {
|
||||
let isLogined = await qqLoginManager.checkQQLoginStatus();
|
||||
const HeartBeat = async (): Promise<void> => {
|
||||
const isLogined = await qqLoginManager.checkQQLoginStatus();
|
||||
if (isLogined) {
|
||||
clearInterval(heartBeatTimer);
|
||||
if (heartBeatTimer) {
|
||||
clearInterval(heartBeatTimer);
|
||||
}
|
||||
await router.push({ path: '/dashboard/basic-info' });
|
||||
}
|
||||
};
|
||||
|
||||
const InitPages = async () => {
|
||||
const InitPages = async (): Promise<void> => {
|
||||
quickLoginList.value = await qqLoginManager.getQQQuickLoginList();
|
||||
generateQrCode(await qqLoginManager.getQQLoginQrcode(), qrcodeCanvas.value);
|
||||
heartBeatTimer = setInterval(HeartBeat, 3000);
|
||||
const qrcodeData = await qqLoginManager.getQQLoginQrcode();
|
||||
generateQrCode(qrcodeData, qrcodeCanvas.value);
|
||||
heartBeatTimer = window.setInterval(HeartBeat, 3000);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
@@ -103,7 +127,7 @@ onMounted(() => {
|
||||
|
||||
.login-method.active {
|
||||
background-color: #e6f0ff;
|
||||
color: #007BFF;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.login-form,
|
||||
@@ -125,7 +149,7 @@ onMounted(() => {
|
||||
font-family: Sotheby, Helvetica, monospace;
|
||||
font-size: 3.125rem;
|
||||
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 {
|
||||
@@ -140,4 +164,4 @@ onMounted(() => {
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="login-container">
|
||||
<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-input v-model="formData.token" type="password" clearable placeholder="请输入Token">
|
||||
<template #prefix-icon>
|
||||
@@ -14,32 +14,34 @@
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</div>
|
||||
<div class="footer">
|
||||
Power By NapCat.WebUi
|
||||
</div>
|
||||
<div class="footer">Power By NapCat.WebUi</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import '../css/style.css';
|
||||
import '../css/font.css';
|
||||
import { reactive, ref, onMounted } from 'vue';
|
||||
import { reactive, onMounted } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { LockOnIcon } from 'tdesign-icons-vue-next';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { QQLoginManager } from '../backend/shell';
|
||||
import { QQLoginManager } from '@/backend/shell';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const formData = reactive({
|
||||
interface FormData {
|
||||
token: string;
|
||||
}
|
||||
|
||||
const formData: FormData = reactive({
|
||||
token: '',
|
||||
});
|
||||
|
||||
const handleLoginSuccess = async (credential) => {
|
||||
const handleLoginSuccess = async (credential: string) => {
|
||||
localStorage.setItem('auth', credential);
|
||||
await checkLoginStatus();
|
||||
};
|
||||
|
||||
const handleLoginFailure = (message) => {
|
||||
const handleLoginFailure = (message: string) => {
|
||||
MessagePlugin.error(message);
|
||||
};
|
||||
|
||||
@@ -63,7 +65,7 @@ const checkLoginStatus = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const loginWithToken = async (token) => {
|
||||
const loginWithToken = async (token: string) => {
|
||||
const loginManager = new QQLoginManager('');
|
||||
const credential = await loginManager.loginWithToken(token);
|
||||
if (credential) {
|
||||
@@ -75,15 +77,15 @@ const loginWithToken = async (token) => {
|
||||
|
||||
onMounted(() => {
|
||||
const url = new URL(window.location.href);
|
||||
const token = url.searchParams.get("token");
|
||||
const token = url.searchParams.get('token');
|
||||
if (token) {
|
||||
loginWithToken(token);
|
||||
}
|
||||
checkLoginStatus();
|
||||
});
|
||||
|
||||
const onSubmit = async ({ validateResult }) => {
|
||||
if (validateResult === true) {
|
||||
const onSubmit = async ({ validateResult }: { validateResult: boolean }) => {
|
||||
if (validateResult) {
|
||||
await loginWithToken(formData.token);
|
||||
} else {
|
||||
handleLoginFailure('请填写Token');
|
||||
@@ -131,7 +133,7 @@ const onSubmit = async ({ validateResult }) => {
|
||||
font-family: Sotheby, Helvetica, monospace;
|
||||
font-size: 3.125rem;
|
||||
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 {
|
||||
@@ -146,4 +148,4 @@ const onSubmit = async ({ validateResult }) => {
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@@ -1,77 +1,71 @@
|
||||
<template>
|
||||
<t-menu theme="light" default-value="2-1" :collapsed="collapsed" class="sidebar-menu">
|
||||
<template #logo>
|
||||
</template>
|
||||
<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">
|
||||
<template #icon>
|
||||
<t-icon :name="item.icon" />
|
||||
<t-menu theme="light" default-value="2-1" :collapsed="collapsed" class="sidebar-menu">
|
||||
<template #logo> </template>
|
||||
<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">
|
||||
<template #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>
|
||||
{{ 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>
|
||||
</t-menu>
|
||||
</t-menu>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, ref, onMounted } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { ref, defineProps } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SidebarMenu',
|
||||
props: {
|
||||
menuItems: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const collapsed = ref(localStorage.getItem('sidebar-collapsed') === 'true');
|
||||
const iconName = ref(collapsed.value ? 'menu-unfold' : 'menu-fold');
|
||||
type MenuItem = {
|
||||
value: string;
|
||||
label: string;
|
||||
route: string;
|
||||
icon?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const changeCollapsed = () => {
|
||||
collapsed.value = !collapsed.value;
|
||||
iconName.value = collapsed.value ? 'menu-unfold' : 'menu-fold';
|
||||
localStorage.setItem('sidebar-collapsed', collapsed.value);
|
||||
};
|
||||
defineProps<{
|
||||
menuItems: MenuItem[];
|
||||
}>();
|
||||
|
||||
return {
|
||||
collapsed,
|
||||
iconName,
|
||||
changeCollapsed
|
||||
};
|
||||
}
|
||||
});
|
||||
const collapsed = ref<boolean>(localStorage.getItem('sidebar-collapsed') === 'true');
|
||||
const iconName = ref<string>(collapsed.value ? 'menu-unfold' : 'menu-fold');
|
||||
|
||||
const changeCollapsed = (): void => {
|
||||
collapsed.value = !collapsed.value;
|
||||
iconName.value = collapsed.value ? 'menu-unfold' : 'menu-fold';
|
||||
localStorage.setItem('sidebar-collapsed', collapsed.value.toString());
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sidebar-menu {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 200px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 200px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sidebar-menu {
|
||||
width: 100px; /* 移动端侧边栏宽度 */
|
||||
}
|
||||
.sidebar-menu {
|
||||
width: 100px; /* 移动端侧边栏宽度 */
|
||||
}
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import {
|
||||
Button as TButton,
|
||||
Input as TInput,
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
Space as TSpace,
|
||||
Checkbox as TCheckbox,
|
||||
Popup as TPopup,
|
||||
Dialog as TDialog
|
||||
Dialog as TDialog,
|
||||
} from 'tdesign-vue-next';
|
||||
import { router } from './router';
|
||||
import 'tdesign-vue-next/es/style/index.css';
|
||||
@@ -57,4 +57,4 @@ app.use(TSpace);
|
||||
app.use(TCheckbox);
|
||||
app.use(TPopup);
|
||||
app.use(TDialog);
|
||||
app.mount('#app');
|
||||
app.mount('#app');
|
||||
|
@@ -3,11 +3,12 @@
|
||||
<div>
|
||||
<t-divider content="面板关于信息" align="left" />
|
||||
<t-alert theme="success" message="NapCat.WebUi is running" />
|
||||
|
||||
<t-list class="list">
|
||||
<t-list-item class="list-item">
|
||||
<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 class="list-item">
|
||||
<span class="item-label">版本信息:</span>
|
||||
@@ -22,15 +23,11 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
joinQQGroup() {
|
||||
// 加入QQ群的逻辑
|
||||
window.open('https://jq.qq.com/?_wv=1027&k=123456789', '_blank');
|
||||
}
|
||||
}
|
||||
}
|
||||
<script setup lang="ts">
|
||||
const joinQQGroup = () => {
|
||||
// 加入QQ群的逻辑
|
||||
window.open('https://jq.qq.com/?_wv=1027&k=123456789', '_blank');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -59,11 +56,11 @@ export default {
|
||||
flex: 2;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end; /* 添加这一行 */
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
@@ -1,18 +1,28 @@
|
||||
<template>
|
||||
<t-space>
|
||||
<t-tabs v-model="value" theme="card" :addable="true" @add="showAddTabDialog" @remove="removeTab">
|
||||
<t-tab-panel v-for="data in panelData" :key="data.value" :value="data.value" :label="data.label"
|
||||
:removable="data.removable">
|
||||
<t-tabs v-model="value" :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"
|
||||
>
|
||||
<component :is="data.component" :config="data.config" />
|
||||
</t-tab-panel>
|
||||
</t-tabs>
|
||||
</t-space>
|
||||
<t-dialog :visible.sync="isDialogVisible" header="添加新选项卡" @confirm="addTab" @close="isDialogVisible = false">
|
||||
<t-form :model="newTab" ref="form">
|
||||
<t-form-item label="名称" name="name" :rules="[{ required: true, message: '请输入名称' }]">
|
||||
<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 label="类型" name="type" :rules="[{ required: true, message: '请选择类型' }]">
|
||||
<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>
|
||||
@@ -24,55 +34,61 @@
|
||||
</t-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, shallowRef, onMounted, watch, nextTick } from 'vue';
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onMounted, ref, shallowRef, watch } from 'vue';
|
||||
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 HttpClientComponent from './network/HttpClientComponent.vue';
|
||||
import WebsocketServerComponent from './network/WebsocketServerComponent.vue';
|
||||
import WebsocketClientComponent from './network/WebsocketClientComponent.vue';
|
||||
|
||||
let id = 0;
|
||||
const value = ref('first');
|
||||
const panelData = ref([]);
|
||||
const isDialogVisible = ref(false);
|
||||
const newTab = ref({ name: '', type: '' });
|
||||
interface PanelData {
|
||||
value: string;
|
||||
label: string;
|
||||
removable: boolean;
|
||||
component: any;
|
||||
config: { name: string };
|
||||
}
|
||||
|
||||
const componentMap = {
|
||||
'httpServers': shallowRef(HttpServerComponent),
|
||||
'httpClients': shallowRef(HttpClientComponent),
|
||||
'websocketServers': shallowRef(WebsocketServerComponent),
|
||||
'websocketClients': shallowRef(WebsocketClientComponent),
|
||||
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: '' });
|
||||
|
||||
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');
|
||||
if (!storedCredential) {
|
||||
console.error('No stored credential found');
|
||||
return;
|
||||
}
|
||||
const loginManager = new QQLoginManager(storedCredential);
|
||||
const config = await loginManager.GetOB11Config();
|
||||
return config;
|
||||
return await loginManager.GetOB11Config();
|
||||
};
|
||||
|
||||
const setOB11Config = async (config) => {
|
||||
const setOB11Config = async (config: any): Promise<boolean> => {
|
||||
const storedCredential = localStorage.getItem('auth');
|
||||
if (!storedCredential) {
|
||||
console.error('No stored credential found');
|
||||
return false;
|
||||
}
|
||||
const loginManager = new QQLoginManager(storedCredential);
|
||||
const result = await loginManager.SetOB11Config(config);
|
||||
return result;
|
||||
return await loginManager.SetOB11Config(config);
|
||||
};
|
||||
|
||||
const log = (message, data) => {
|
||||
const log = (message: string, data: any) => {
|
||||
console.log(message, data);
|
||||
};
|
||||
|
||||
const createPanel = (type, name, id) => {
|
||||
const createPanel = (type: string, name: string, id: number): PanelData => {
|
||||
return {
|
||||
value: `${type}-${id}`,
|
||||
label: name,
|
||||
@@ -82,10 +98,10 @@ const createPanel = (type, name, id) => {
|
||||
};
|
||||
};
|
||||
|
||||
const generatePanels = (networkConfig) => {
|
||||
const panels = [];
|
||||
const generatePanels = (networkConfig: any): PanelData[] => {
|
||||
const panels: PanelData[] = [];
|
||||
Object.keys(networkConfig).forEach((key) => {
|
||||
networkConfig[key].forEach((config, index) => {
|
||||
networkConfig[key].forEach((config: any, index: number) => {
|
||||
const component = componentMap[key];
|
||||
if (!component) {
|
||||
console.error(`No component found for key: ${key}`);
|
||||
@@ -100,6 +116,7 @@ const generatePanels = (networkConfig) => {
|
||||
const loadConfig = async () => {
|
||||
try {
|
||||
const userConfig = await getOB11Config();
|
||||
if (!userConfig) return;
|
||||
const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig);
|
||||
const networkConfig = mergedConfig.network;
|
||||
log('networkConfig:', networkConfig);
|
||||
@@ -130,7 +147,11 @@ const addTab = async () => {
|
||||
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;
|
||||
panelData.value.splice(index, 1);
|
||||
if (panelData.value.length === 0) return;
|
||||
@@ -140,8 +161,8 @@ const removeTab = ({ value: val, index }) => {
|
||||
};
|
||||
|
||||
const syncConfig = async () => {
|
||||
const networkConfig = {};
|
||||
panelData.value.forEach(panel => {
|
||||
const networkConfig: Record<string, any[]> = {};
|
||||
panelData.value.forEach((panel) => {
|
||||
const key = panel.value.split('-')[0];
|
||||
if (!networkConfig[key]) {
|
||||
networkConfig[key] = [];
|
||||
@@ -149,6 +170,7 @@ const syncConfig = async () => {
|
||||
networkConfig[key].push(panel.config);
|
||||
});
|
||||
const userConfig = await getOB11Config();
|
||||
if (!userConfig) return;
|
||||
const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig);
|
||||
mergedConfig.network = networkConfig;
|
||||
await setOB11Config(mergedConfig);
|
||||
@@ -159,4 +181,4 @@ watch(panelData, syncConfig, { deep: true });
|
||||
onMounted(() => {
|
||||
loadConfig();
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
@@ -4,4 +4,4 @@
|
||||
<t-divider content="其余配置" align="left" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
@@ -1,29 +1,42 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3>HTTP Client 配置</h3>
|
||||
<t-form>
|
||||
<t-form-item label="URL">
|
||||
<t-input v-model="config.url" />
|
||||
</t-form-item>
|
||||
<t-form-item label="消息格式">
|
||||
<t-input v-model="config.messagePostFormat" />
|
||||
</t-form-item>
|
||||
<t-form-item label="报告自身消息">
|
||||
<t-checkbox v-model="config.reportSelfMessage" />
|
||||
</t-form-item>
|
||||
<t-form-item label="Token">
|
||||
<t-input v-model="config.token" />
|
||||
</t-form-item>
|
||||
<t-form-item label="调试模式">
|
||||
<t-checkbox v-model="config.debug" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</div>
|
||||
<div>
|
||||
<h3>HTTP Client 配置</h3>
|
||||
<t-form>
|
||||
<t-form-item label="URL">
|
||||
<t-input v-model="config.url" />
|
||||
</t-form-item>
|
||||
<t-form-item label="消息格式">
|
||||
<t-input v-model="config.messagePostFormat" />
|
||||
</t-form-item>
|
||||
<t-form-item label="报告自身消息">
|
||||
<t-checkbox v-model="config.reportSelfMessage" />
|
||||
</t-form-item>
|
||||
<t-form-item label="Token">
|
||||
<t-input v-model="config.token" />
|
||||
</t-form-item>
|
||||
<t-form-item label="调试模式">
|
||||
<t-checkbox v-model="config.debug" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from 'vue';
|
||||
defineProps({
|
||||
config: Object,
|
||||
<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,
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
@@ -1,38 +1,57 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3>HTTP Server 配置</h3>
|
||||
<t-form>
|
||||
<t-form-item label="端口">
|
||||
<t-input v-model.number="config.port" type="number" />
|
||||
</t-form-item>
|
||||
<t-form-item label="主机">
|
||||
<t-input v-model="config.host" type="text" />
|
||||
</t-form-item>
|
||||
<t-form-item label="启用 CORS">
|
||||
<t-checkbox v-model="config.enableCors" />
|
||||
</t-form-item>
|
||||
<t-form-item label="启用 WebSocket">
|
||||
<t-checkbox v-model="config.enableWebsocket" />
|
||||
</t-form-item>
|
||||
<t-form-item label="消息格式">
|
||||
<t-input v-model="config.messagePostFormat" type="text" />
|
||||
</t-form-item>
|
||||
<t-form-item label="报告自身消息">
|
||||
<t-checkbox v-model="config.reportSelfMessage" />
|
||||
</t-form-item>
|
||||
<t-form-item label="Token">
|
||||
<t-input v-model="config.token" type="text" />
|
||||
</t-form-item>
|
||||
<t-form-item label="调试模式">
|
||||
<t-checkbox v-model="config.debug" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</div>
|
||||
<div>
|
||||
<h3>HTTP Server 配置</h3>
|
||||
<t-form>
|
||||
<t-form-item label="端口">
|
||||
<t-input v-model.number="config.port" type="number" />
|
||||
</t-form-item>
|
||||
<t-form-item label="主机">
|
||||
<t-input v-model="config.host" type="text" />
|
||||
</t-form-item>
|
||||
<t-form-item label="启用 CORS">
|
||||
<t-checkbox v-model="config.enableCors" />
|
||||
</t-form-item>
|
||||
<t-form-item label="启用 WebSocket">
|
||||
<t-checkbox v-model="config.enableWebsocket" />
|
||||
</t-form-item>
|
||||
<t-form-item label="消息格式">
|
||||
<t-input v-model="config.messagePostFormat" type="text" />
|
||||
</t-form-item>
|
||||
<t-form-item label="报告自身消息">
|
||||
<t-checkbox v-model="config.reportSelfMessage" />
|
||||
</t-form-item>
|
||||
<t-form-item label="Token">
|
||||
<t-input v-model="config.token" type="text" />
|
||||
</t-form-item>
|
||||
<t-form-item label="调试模式">
|
||||
<t-checkbox v-model="config.debug" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from 'vue';
|
||||
defineProps({
|
||||
config: Object,
|
||||
<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,
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
@@ -1,32 +1,47 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3>WebSocket Client 配置</h3>
|
||||
<t-form>
|
||||
<t-form-item label="URL">
|
||||
<t-input v-model="config.url" />
|
||||
</t-form-item>
|
||||
<t-form-item label="消息格式">
|
||||
<t-input v-model="config.messagePostFormat" />
|
||||
</t-form-item>
|
||||
<t-form-item label="报告自身消息">
|
||||
<t-checkbox v-model="config.reportSelfMessage" />
|
||||
</t-form-item>
|
||||
<t-form-item label="Token">
|
||||
<t-input v-model="config.token" />
|
||||
</t-form-item>
|
||||
<t-form-item label="调试模式">
|
||||
<t-checkbox v-model="config.debug" />
|
||||
</t-form-item>
|
||||
<t-form-item label="心跳间隔">
|
||||
<t-input v-model.number="config.heartInterval" type="number" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</div>
|
||||
<div>
|
||||
<h3>WebSocket Client 配置</h3>
|
||||
<t-form>
|
||||
<t-form-item label="URL">
|
||||
<t-input v-model="config.url" />
|
||||
</t-form-item>
|
||||
<t-form-item label="消息格式">
|
||||
<t-input v-model="config.messagePostFormat" />
|
||||
</t-form-item>
|
||||
<t-form-item label="报告自身消息">
|
||||
<t-checkbox v-model="config.reportSelfMessage" />
|
||||
</t-form-item>
|
||||
<t-form-item label="Token">
|
||||
<t-input v-model="config.token" />
|
||||
</t-form-item>
|
||||
<t-form-item label="调试模式">
|
||||
<t-checkbox v-model="config.debug" />
|
||||
</t-form-item>
|
||||
<t-form-item label="心跳间隔">
|
||||
<t-input v-model.number="config.heartInterval" type="number" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from 'vue';
|
||||
defineProps({
|
||||
config: Object,
|
||||
<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,
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
@@ -1,38 +1,57 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3>WebSocket Server 配置</h3>
|
||||
<t-form>
|
||||
<t-form-item label="主机">
|
||||
<t-input v-model="config.host" />
|
||||
</t-form-item>
|
||||
<t-form-item label="端口">
|
||||
<t-input v-model.number="config.port" type="number" />
|
||||
</t-form-item>
|
||||
<t-form-item label="消息格式">
|
||||
<t-input v-model="config.messagePostFormat" />
|
||||
</t-form-item>
|
||||
<t-form-item label="报告自身消息">
|
||||
<t-checkbox v-model="config.reportSelfMessage" />
|
||||
</t-form-item>
|
||||
<t-form-item label="Token">
|
||||
<t-input v-model="config.token" />
|
||||
</t-form-item>
|
||||
<t-form-item label="启用推送事件">
|
||||
<t-checkbox v-model="config.enablePushEvent" />
|
||||
</t-form-item>
|
||||
<t-form-item label="调试模式">
|
||||
<t-checkbox v-model="config.debug" />
|
||||
</t-form-item>
|
||||
<t-form-item label="心跳间隔">
|
||||
<t-input v-model.number="config.heartInterval" type="number" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</div>
|
||||
<div>
|
||||
<h3>WebSocket Server 配置</h3>
|
||||
<t-form>
|
||||
<t-form-item label="主机">
|
||||
<t-input v-model="config.host" />
|
||||
</t-form-item>
|
||||
<t-form-item label="端口">
|
||||
<t-input v-model.number="config.port" type="number" />
|
||||
</t-form-item>
|
||||
<t-form-item label="消息格式">
|
||||
<t-input v-model="config.messagePostFormat" />
|
||||
</t-form-item>
|
||||
<t-form-item label="报告自身消息">
|
||||
<t-checkbox v-model="config.reportSelfMessage" />
|
||||
</t-form-item>
|
||||
<t-form-item label="Token">
|
||||
<t-input v-model="config.token" />
|
||||
</t-form-item>
|
||||
<t-form-item label="启用推送事件">
|
||||
<t-checkbox v-model="config.enablePushEvent" />
|
||||
</t-form-item>
|
||||
<t-form-item label="调试模式">
|
||||
<t-checkbox v-model="config.debug" />
|
||||
</t-form-item>
|
||||
<t-form-item label="心跳间隔">
|
||||
<t-input v-model.number="config.heartInterval" type="number" />
|
||||
</t-form-item>
|
||||
</t-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps } from 'vue';
|
||||
defineProps({
|
||||
config: Object,
|
||||
<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,
|
||||
});
|
||||
</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 BasicInfo from '../pages/BasicInfo.vue';
|
||||
import AboutUs from '../pages/AboutUs.vue';
|
||||
@@ -8,25 +8,25 @@ import QQLogin from '../components/QQLogin.vue';
|
||||
import WebUiLogin from '../components/WebUiLogin.vue';
|
||||
import OtherConfig from '../pages/OtherConfig.vue';
|
||||
|
||||
const routes = [
|
||||
{ path: '/', redirect: '/webui' },
|
||||
{ path: '/webui', component: WebUiLogin, name: 'WebUiLogin' },
|
||||
{ path: '/qqlogin', component: QQLogin, name: 'QQLogin' },
|
||||
{
|
||||
path: '/dashboard',
|
||||
component: Dashboard,
|
||||
children: [
|
||||
{ path: '', redirect: 'basic-info' },
|
||||
{ path: 'basic-info', component: BasicInfo, name: 'BasicInfo' },
|
||||
{ path: 'network-config', component: NetWork, name: 'NetWork' },
|
||||
{ path: 'log-view', component: LogView, name: 'LogView' },
|
||||
{ path: 'other-config', component: OtherConfig, name: 'OtherConfig' },
|
||||
{ path: 'about-us', component: AboutUs, name: 'AboutUs' }
|
||||
]
|
||||
}
|
||||
]
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{ path: '/', redirect: '/webui' },
|
||||
{ path: '/webui', component: WebUiLogin, name: 'WebUiLogin' },
|
||||
{ path: '/qqlogin', component: QQLogin, name: 'QQLogin' },
|
||||
{
|
||||
path: '/dashboard',
|
||||
component: Dashboard,
|
||||
children: [
|
||||
{ path: '', redirect: 'basic-info' },
|
||||
{ path: 'basic-info', component: BasicInfo, name: 'BasicInfo' },
|
||||
{ path: 'network-config', component: NetWork, name: 'NetWork' },
|
||||
{ path: 'log-view', component: LogView, name: 'LogView' },
|
||||
{ path: 'other-config', component: OtherConfig, name: 'OtherConfig' },
|
||||
{ path: 'about-us', component: AboutUs, name: 'AboutUs' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes,
|
||||
})
|
||||
history: createWebHashHistory(),
|
||||
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": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"jsx": "preserve",
|
||||
"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": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "Bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { defineConfig } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
base: './'
|
||||
})
|
||||
plugins: [vue()],
|
||||
base: './',
|
||||
});
|
||||
|
Reference in New Issue
Block a user