feat(wifi,ws) view page
This commit is contained in:
parent
1bae314449
commit
c7d1dff0d0
|
@ -0,0 +1 @@
|
|||
VITE_APP_MODE=dev
|
|
@ -0,0 +1 @@
|
|||
VITE_APP_MODE=prod
|
|
@ -29,3 +29,8 @@ coverage
|
|||
|
||||
*.tsbuildinfo
|
||||
package-lock.json
|
||||
components.d.ts
|
||||
auto-imports.d.ts
|
||||
|
||||
# Personal
|
||||
**/_priv_*
|
|
@ -1,9 +0,0 @@
|
|||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
export {}
|
||||
declare global {
|
||||
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
export {}
|
||||
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
InlineSvg: typeof import('./src/components/InlineSvg.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
}
|
|
@ -1 +1,3 @@
|
|||
/// <reference types="vite/client" />
|
||||
declare const __APP_VERSION__: string; // defined in vite.config.ts, imported from package.json.version
|
||||
declare const __BUILD_TIME__: string;
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<link id="favicon" rel="icon" href="data:,">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"build": "run-p type-check \"build-only {@}\" --",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"build:dev": "vite build --mode development",
|
||||
"type-check": "vue-tsc --build --force",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"format": "prettier --write src/"
|
||||
|
@ -40,6 +41,9 @@
|
|||
"unplugin-auto-import": "^0.17.5",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.1.6",
|
||||
"vite-plugin-css-injected-by-js": "^3.5.0",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vite-plugin-singlefile": "^2.0.1",
|
||||
"vite-svg-loader": "^5.1.0",
|
||||
"vue-tsc": "^2.0.6"
|
||||
}
|
||||
|
|
57
src/App.vue
57
src/App.vue
|
@ -1,64 +1,63 @@
|
|||
<script setup lang="ts">
|
||||
import {useWsStore} from "@/stores/websocket";
|
||||
import type {ControlMsg, ServerMsg} from "@/composables/broadcastChannelDef";
|
||||
import type {IWebsocketService} from "@/composables/websocket/websocketService";
|
||||
import {ControlEvent, ControlMsgType} from "@/composables/broadcastChannelDef";
|
||||
import {getWebsocketService} from "@/composables/websocket/websocketService";
|
||||
import {onMounted, onUnmounted} from "vue";
|
||||
import {changeFavicon} from "@/composables/importFavicon";
|
||||
import {logHelloMessage} from "@/composables/logConsoleMsg";
|
||||
import NavBar from "@/views/navigation/NavBar.vue";
|
||||
import type {ControlMsg, ServerMsg} from "@/api";
|
||||
import {ControlEvent, ControlMsgType} from "@/api";
|
||||
import {routeCtrlMsg, routeModuleServerMsg} from "@/router/msgRouter";
|
||||
import {globalNotify} from "@/composables/notification";
|
||||
import {isDevMode} from "@/composables/buildMode";
|
||||
|
||||
const wsState = useWsStore();
|
||||
|
||||
const onClientCtrl = (msg: ControlMsg) => {
|
||||
console.log("App.vue:", msg);
|
||||
if (isDevMode()) {
|
||||
console.log("App.vue:", msg);
|
||||
}
|
||||
if (msg.type === ControlMsgType.WS_EVENT) {
|
||||
wsState.$patch({state: msg.data as ControlEvent})
|
||||
routeCtrlMsg(msg);
|
||||
if (msg.data === ControlEvent.CONNECTED) {
|
||||
globalNotify("调试器已连接", "success");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onServerMsg = (msg: ServerMsg) => {
|
||||
console.log("App.vue:", msg);
|
||||
if (isDevMode()) {
|
||||
console.log("App.vue:", msg);
|
||||
}
|
||||
routeModuleServerMsg(msg);
|
||||
};
|
||||
|
||||
let websocketService: IWebsocketService;
|
||||
onMounted(() => {
|
||||
// const host = window.location
|
||||
console.log("App.vue mounted")
|
||||
|
||||
const host = "192.168.43.61";
|
||||
logHelloMessage();
|
||||
|
||||
let host = "";
|
||||
if (isDevMode()) {
|
||||
host = "192.168.43.61";
|
||||
} else {
|
||||
host = window.location.host
|
||||
}
|
||||
websocketService = getWebsocketService();
|
||||
websocketService.init(host, onServerMsg, onClientCtrl);
|
||||
changeFavicon();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
});
|
||||
|
||||
import NavBar from "@/views/navigation/NavBar.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<nav-bar/>
|
||||
</header>
|
||||
|
||||
<p class="m-0">This is body</p>
|
||||
<RouterView/>
|
||||
<p>end</p>
|
||||
<!-- <p>{{ test }}</p>-->
|
||||
<el-button type="danger"><p>test</p></el-button>
|
||||
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.app {
|
||||
background-color: #ddd;
|
||||
box-shadow: 0 0 10px;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.router-active {
|
||||
background-color: #666;
|
||||
cursor: default;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import {sendJsonMsg} from '@/composables/broadcastChannelDef'
|
||||
import {type ApiJsonMsg, sendJsonMsg} from '@/api'
|
||||
|
||||
|
||||
import {type ApiJsonMsg} from '@/api'
|
||||
|
||||
const WifiModuleID = 1;
|
||||
enum WifiCmd {
|
||||
export const WifiModuleID = 1;
|
||||
export enum WifiCmd {
|
||||
UNKNOWN = 0,
|
||||
WIFI_API_JSON_GET_AP_INFO,
|
||||
WIFI_API_JSON_STA_GET_AP_INFO,
|
||||
WIFI_API_JSON_CONNECT,
|
||||
WIFI_API_JSON_GET_SCAN,
|
||||
WIFI_API_JSON_DISCONNECT,
|
||||
WIFI_API_JSON_AP_GET_INFO,
|
||||
}
|
||||
|
||||
interface WifiMsgOut extends ApiJsonMsg {
|
||||
|
@ -25,10 +23,18 @@ export function wifi_get_scan_list() {
|
|||
sendJsonMsg(msg);
|
||||
}
|
||||
|
||||
export function wifi_get_ap_info() {
|
||||
export function wifi_sta_get_ap_info() {
|
||||
const msg : WifiMsgOut = {
|
||||
module: WifiModuleID,
|
||||
cmd: WifiCmd.WIFI_API_JSON_GET_AP_INFO,
|
||||
cmd: WifiCmd.WIFI_API_JSON_STA_GET_AP_INFO,
|
||||
}
|
||||
sendJsonMsg(msg);
|
||||
}
|
||||
|
||||
export function wifi_ap_get_info() {
|
||||
const msg : WifiMsgOut = {
|
||||
module: WifiModuleID,
|
||||
cmd: WifiCmd.WIFI_API_JSON_AP_GET_INFO,
|
||||
}
|
||||
sendJsonMsg(msg);
|
||||
}
|
||||
|
@ -43,10 +49,13 @@ export function wifi_connect_to(ssid: string, password: string) {
|
|||
sendJsonMsg(msg);
|
||||
}
|
||||
|
||||
export interface WifiInfo {
|
||||
export interface WifiInfo extends ApiJsonMsg {
|
||||
rssi: number;
|
||||
ssid: string;
|
||||
gateway: string;
|
||||
ip: string;
|
||||
mac: string;
|
||||
netmask: string;
|
||||
wifiLogo?: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,43 @@
|
|||
import {getWebsocketService} from "@/composables/websocket/websocketService";
|
||||
|
||||
export interface ApiJsonMsg {
|
||||
module: number;
|
||||
cmd: number;
|
||||
}
|
||||
|
||||
export enum ControlMsgType {
|
||||
WS_EVENT = "WS_EVENT",
|
||||
WS_SET_HOST = "WS_SET_HOST",
|
||||
WS_GET_STATE = "WS_GET_STATE",
|
||||
}
|
||||
|
||||
export enum ControlEvent {
|
||||
DISCONNECTED = "DISCONNECTED",
|
||||
LOADED = "LOADED",
|
||||
CONNECTED = "CONNECTED",
|
||||
CONNECTING = "CONNECTING",
|
||||
BROKEN = "BROKEN",
|
||||
}
|
||||
|
||||
export interface ControlMsg {
|
||||
type: ControlMsgType,
|
||||
data: ControlEvent | string,
|
||||
}
|
||||
|
||||
export interface ServerMsg {
|
||||
type: "json" | "binary"
|
||||
data: ApiJsonMsg | object;
|
||||
}
|
||||
|
||||
export function sendJsonMsg(apiJsonMsg: ApiJsonMsg) {
|
||||
const msg: ServerMsg = {
|
||||
type: "json",
|
||||
data: apiJsonMsg,
|
||||
};
|
||||
getWebsocketService().send(msg);
|
||||
// toServer.postMessage(msg);
|
||||
}
|
||||
|
||||
export function sendBinMsg(msg: ApiJsonMsg) {
|
||||
// toServer.postMessage(JSON.stringify(msg));
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 170 170">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FEAD04;}
|
||||
.st1{fill:#032E3E;}
|
||||
.st2{fill:#FFFFFF;}
|
||||
.st3{fill:#FF0135;}
|
||||
.st4{fill:#FA7E0C;}
|
||||
</style>
|
||||
<path class="st0" d="M20.1,59.7h29.7V35.5H21.6c-0.8,0-1.3,0.2-1.8,0.7L9.5,45.4c-3,2.9-2.6,5.1,0.5,7.5l8.7,6.4
|
||||
C19.1,59.6,19.6,59.7,20.1,59.7L20.1,59.7z"/>
|
||||
<path class="st1" d="M27.1,50.5h23.4v-7H27.1c-2,0-3.6,1.6-3.6,3.5S25.1,50.5,27.1,50.5L27.1,50.5z"/>
|
||||
<path class="st1" d="M79.7,168c31.7,0.7,58.5-13.8,80.2-54.7c2.7-5.5-2.1-9.9-11-9.9c-13.7,0.1-35.1-17.6-36.6-26.4
|
||||
c-17.5,6.1-26.8,14-40.1,14c-11.1,0-15-0.8-22.3-4.9c-1.9,0.6-3.9,1-6.2,0.9c-8.3-0.3-22.9-7.6-34.4-24.1c-3.8-5.5-5.9-2.6-6.9,3.5
|
||||
C-6.4,116.8,29.1,166.3,79.7,168L79.7,168z"/>
|
||||
<path class="st2" d="M76.4,91.7c-11.1,0-19.5-4.5-23.6-7.1V36.3C57.5,28.1,67.7,23,79.5,23h0.6c9.6,0.1,28,8.6,29.3,28.4v24.5
|
||||
c-3.3,4-12.8,13.8-28.1,15.6C79.7,91.7,78,91.7,76.4,91.7z"/>
|
||||
<path class="st1" d="M79.5,26c0.2,0,0.4,0,0.6,0c4,0.1,10.5,2,15.9,6.1c6.4,4.8,9.9,11.3,10.4,19.4v23.3
|
||||
c-3.6,4.1-12.1,12.1-25.4,13.7c-1.5,0.2-3,0.3-4.5,0.3c-9.2,0-16.5-3.3-20.6-5.8V37.1C60.2,30.2,69.1,26,79.5,26L79.5,26 M79.5,20
|
||||
c-13.7,0-24.7,6.2-29.7,15.5v50.7c2.5,1.8,12.5,8.6,26.6,8.6c1.7,0,3.4-0.1,5.2-0.3c18.6-2.2,28.9-15.1,30.7-17.5
|
||||
c0-8.5,0-17.1,0-25.6C111,29,90.5,20.1,80.2,20C79.9,20,79.7,20,79.5,20L79.5,20z M49.8,86.2L49.8,86.2L49.8,86.2z M49.8,86.2
|
||||
L49.8,86.2L49.8,86.2L49.8,86.2z"/>
|
||||
<circle class="st1" cx="77.6" cy="47.9" r="7.5"/>
|
||||
<path class="st2" d="M112.4,76.9c-37.2-15.2-45.8-17.1-62.6,9.3c-10.3,16.3-10.5,31.2-6.4,45c3.3,11,11.6,24.1,27.9,27
|
||||
c0.3,0.1-6.7-13.9,1.6-35.5C78.4,108.6,92.3,88.1,112.4,76.9L112.4,76.9z"/>
|
||||
<path class="st3" d="M78.4,167.9c0.4,0,0.9,0,1.3,0.1c31.7,0.7,58.5-13.8,80.2-54.7c2.7-5.5-2.1-9.9-11-9.9
|
||||
c-11.3,0.1-28-12.1-34.2-21.3c-5.4,2.9-10.9,6.4-16,10.8c-11,9.3-20.9,20.3-24.9,34.6c-2.7,10-2.7,21.8,1.8,36.1L78.4,167.9
|
||||
L78.4,167.9z"/>
|
||||
<path class="st0" d="M156.7,43.3l-22.1,71.4c-0.2,0.7,0.2,1.9,0.9,1.8l6.5-0.9c0.8-0.1,1.5-0.2,1.8-0.9l25.1-64.5
|
||||
c0.3-0.7-0.2-1.5-0.9-1.8l-3.6-1.5l-5.8-4.5C157.9,41.9,156.9,42.6,156.7,43.3L156.7,43.3z"/>
|
||||
<path class="st4" d="M143.6,114.8c0-0.1,0.1-0.1,0.1-0.2l25.1-64.5c0.3-0.7-0.2-1.5-0.9-1.8l-3-1.2l-24.5,66.5L143.6,114.8
|
||||
L143.6,114.8z"/>
|
||||
<path class="st0" d="M139.3,46.2L126,117.5c-0.1,0.8,0.4,1.5,1.1,1.6l8.6,1.6c0.8,0.1,1.5-0.4,1.6-1.1l13.3-71.3
|
||||
c0.1-0.8-0.4-1.5-1.1-1.6l-8.6-1.6C140.2,44.9,139.4,45.4,139.3,46.2z"/>
|
||||
<path class="st0" d="M121,45.3l1.6,76.8c0,0.8,0.7,1.4,1.4,1.4l8.8-0.2c0.8,0,1.4-0.7,1.4-1.4l-1.6-76.8c0-0.8-0.7-1.4-1.4-1.4
|
||||
l-8.8,0.2C121.6,43.9,121,44.6,121,45.3z"/>
|
||||
<path class="st4" d="M132.9,123.3c0.8,0,1.4-0.7,1.4-1.4L133,58.5l-1,4.2L132.9,123.3z"/>
|
||||
<path class="st4" d="M138,111.1c0.8,0.1,1.5-0.4,1.6-1.1l11.1-62.4l-1.8,3.9L138,111.1z"/>
|
||||
<path class="st1" d="M163.3,70.5c-14.3,0.1-41.6,2.9-64.6,22.4c-11,9.3-20.9,20.3-24.9,34.6c-2.7,10-2.7,21.8,1.8,36.1l-9.4-1.8
|
||||
c-3.8-14.1-3.5-26.1-0.7-36.5C70,108.9,81,96.7,93.1,86.4c27.1-22.9,59.5-24.7,73.6-24.4L163.3,70.5L163.3,70.5z"/>
|
||||
<path class="st1" d="M79.7,168c22.2,0.5,42.1-6.5,59.5-24.9c-1.5-1.6-3.8-4.1-4.5-4.5c-0.9-0.5-2.8-1.9-6.6,2.1
|
||||
c-17.8,19.3-51.4,18.1-51.4,18.1c-1.1,0.1-2.2-0.1-3.2,0.1c-5.5,1.2-8.5,4.1-9.5,7C69.1,167,74.3,167.8,79.7,168L79.7,168z"/>
|
||||
<path class="st1" d="M94.2,132.9c-2.3-0.4-3.8-2.6-3.4-4.9c0.4-2.3,2.6-3.8,4.9-3.4c0.1,0,22.6,4.4,39.9-13.4c1.6-1.7,4.3-1.7,6-0.1
|
||||
s1.7,4.3,0.1,6C121.1,138.1,94.2,132.9,94.2,132.9z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.5 KiB |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M876.8 156.8c0-9.6-3.2-16-9.6-22.4-6.4-6.4-12.8-9.6-22.4-9.6-9.6 0-16 3.2-22.4 9.6L736 220.8c-64-32-137.6-51.2-224-60.8-160 16-288 73.6-377.6 176C44.8 438.4 0 496 0 512s48 73.6 134.4 176c22.4 25.6 44.8 48 73.6 67.2l-86.4 89.6c-6.4 6.4-9.6 12.8-9.6 22.4 0 9.6 3.2 16 9.6 22.4 6.4 6.4 12.8 9.6 22.4 9.6 9.6 0 16-3.2 22.4-9.6l704-710.4c3.2-6.4 6.4-12.8 6.4-22.4Zm-646.4 528c-76.8-70.4-128-128-153.6-172.8 28.8-48 80-105.6 153.6-172.8C304 272 400 230.4 512 224c64 3.2 124.8 19.2 176 44.8l-54.4 54.4C598.4 300.8 560 288 512 288c-64 0-115.2 22.4-160 64s-64 96-64 160c0 48 12.8 89.6 35.2 124.8L256 707.2c-9.6-6.4-19.2-16-25.6-22.4Zm140.8-96c-12.8-22.4-19.2-48-19.2-76.8 0-44.8 16-83.2 48-112 32-28.8 67.2-48 112-48 28.8 0 54.4 6.4 73.6 19.2zM889.599 336c-12.8-16-28.8-28.8-41.6-41.6l-48 48c73.6 67.2 124.8 124.8 150.4 169.6-28.8 48-80 105.6-153.6 172.8-73.6 67.2-172.8 108.8-284.8 115.2-51.2-3.2-99.2-12.8-140.8-28.8l-48 48c57.6 22.4 118.4 38.4 188.8 44.8 160-16 288-73.6 377.6-176C979.199 585.6 1024 528 1024 512s-48.001-73.6-134.401-176Z"></path><path fill="currentColor" d="M511.998 672c-12.8 0-25.6-3.2-38.4-6.4l-51.2 51.2c28.8 12.8 57.6 19.2 89.6 19.2 64 0 115.2-22.4 160-64 41.6-41.6 64-96 64-160 0-32-6.4-64-19.2-89.6l-51.2 51.2c3.2 12.8 6.4 25.6 6.4 38.4 0 44.8-16 83.2-48 112-32 28.8-67.2 48-112 48Z"></path></svg>
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="currentColor" d="M512 160c320 0 512 352 512 352S832 864 512 864 0 512 0 512s192-352 512-352m0 64c-225.28 0-384.128 208.064-436.8 288 52.608 79.872 211.456 288 436.8 288 225.28 0 384.128-208.064 436.8-288-52.608-79.872-211.456-288-436.8-288zm0 64a224 224 0 1 1 0 448 224 224 0 0 1 0-448m0 64a160.192 160.192 0 0 0-160 160c0 88.192 71.744 160 160 160s160-71.808 160-160-71.744-160-160-160"></path></svg>
|
After Width: | Height: | Size: 477 B |
|
@ -0,0 +1,3 @@
|
|||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 19.4998H12.01M2 8.81929C3.69692 7.30051 5.74166 6.16236 8 5.53906M5 12.8584C5.86251 12.0129 6.87754 11.3223 8 10.8319M16 5.53906C18.2583 6.16236 20.3031 7.30051 22 8.81929M16 10.8319C17.1225 11.3223 18.1375 12.0129 19 12.8584M12 4.5V15.4998" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 419 B |
|
@ -0,0 +1,3 @@
|
|||
.todo-menu-item {
|
||||
@apply opacity-30
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
.text-layout {
|
||||
@apply m-auto max-w-2xl min-w-min px-2
|
||||
}
|
||||
|
||||
.page-title {
|
||||
@apply text-center my-6 text-4xl font-bold tracking-tight md:text-5xl lg:text-6xl
|
||||
}
|
||||
|
||||
.router-link {
|
||||
@apply text-sm text-gray-500 hover:text-gray-500
|
||||
}
|
||||
|
||||
.router-link-active {
|
||||
@apply text-blue-600 font-bold;
|
||||
cursor: default;
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
.tgl {
|
||||
display: none;
|
||||
}
|
||||
.tgl, .tgl:after, .tgl:before, .tgl *, .tgl *:after, .tgl *:before, .tgl + .tgl-btn {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tgl + .tgl-btn {
|
||||
border-bottom: 2px ridge;
|
||||
display: block;
|
||||
width: 4em;
|
||||
height: 2em;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.tgl + .tgl-btn:after, .tgl + .tgl-btn:before {
|
||||
position: relative;
|
||||
display: block;
|
||||
content: "";
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
}
|
||||
.tgl + .tgl-btn:after {
|
||||
left: 0;
|
||||
}
|
||||
.tgl + .tgl-btn:before {
|
||||
display: none;
|
||||
}
|
||||
.tgl:checked + .tgl-btn:after {
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skewed switch
|
||||
*/
|
||||
.tgl-skewed + .tgl-btn {
|
||||
overflow: hidden;
|
||||
backface-visibility: hidden;
|
||||
transition: all 0.2s ease;
|
||||
font-family: sans-serif;
|
||||
background: #888;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.tgl-skewed + .tgl-btn:after, .tgl-skewed + .tgl-btn:before {
|
||||
display: inline-block;
|
||||
transition: all 0.1s ease;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
line-height: 2em;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.tgl-skewed + .tgl-btn:after {
|
||||
left: 100%;
|
||||
content: attr(data-tg-on);
|
||||
}
|
||||
.tgl-skewed + .tgl-btn:before {
|
||||
left: 0;
|
||||
content: attr(data-tg-off);
|
||||
}
|
||||
.tgl-skewed + .tgl-btn:active {
|
||||
background: #888;
|
||||
}
|
||||
.tgl-skewed + .tgl-btn:active:before {
|
||||
left: -10%;
|
||||
}
|
||||
.tgl-skewed:checked + .tgl-btn {
|
||||
background: #86d993;
|
||||
}
|
||||
.tgl-skewed:checked + .tgl-btn:before {
|
||||
left: -100%;
|
||||
}
|
||||
.tgl-skewed:checked + .tgl-btn:after {
|
||||
left: 0;
|
||||
}
|
||||
.tgl-skewed:checked + .tgl-btn:active:after {
|
||||
left: 10%;
|
||||
}
|
||||
|
||||
.tgl-skewed:disabled + .tgl-btn {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* FLIP */
|
||||
.tgl-flip + .tgl-btn {
|
||||
padding: 2px;
|
||||
transition: all 0.2s ease;
|
||||
font-family: sans-serif;
|
||||
perspective: 100px;
|
||||
}
|
||||
.tgl-flip + .tgl-btn:after, .tgl-flip + .tgl-btn:before {
|
||||
display: inline-block;
|
||||
transition: all 0.4s ease;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
line-height: 2em;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
top: 0;
|
||||
left: 0;
|
||||
backface-visibility: hidden;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.tgl-flip + .tgl-btn:after {
|
||||
content: attr(data-tg-on);
|
||||
background: #02C66F;
|
||||
transform: rotateY(-180deg);
|
||||
}
|
||||
.tgl-flip + .tgl-btn:before {
|
||||
background: #FF3A19;
|
||||
content: attr(data-tg-off);
|
||||
}
|
||||
.tgl-flip + .tgl-btn:active:before {
|
||||
transform: rotateY(-20deg);
|
||||
}
|
||||
.tgl-flip:checked + .tgl-btn:before {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
.tgl-flip:checked + .tgl-btn:after {
|
||||
transform: rotateY(0);
|
||||
left: 0;
|
||||
background: #7FC6A6;
|
||||
}
|
||||
.tgl-flip:checked + .tgl-btn:active:after {
|
||||
transform: rotateY(20deg);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<div class="flex justify-between">
|
||||
<p v-if="isPasswordVisible">{{ password }}</p>
|
||||
<p v-else>••••••••</p>
|
||||
<span @click="togglePasswordVisibility" class="flex items-center">
|
||||
<InlineSvg v-show="isPasswordVisible" name="view" width="16"></InlineSvg>
|
||||
<InlineSvg v-show="!isPasswordVisible" name="view-hide" width="16"></InlineSvg>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import InlineSvg from "@/components/InlineSvg.vue";
|
||||
|
||||
const isPasswordVisible = ref(false);
|
||||
|
||||
defineProps<{
|
||||
password: string
|
||||
}>()
|
||||
|
||||
const togglePasswordVisibility = () => {
|
||||
isPasswordVisible.value = !isPasswordVisible.value;
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,30 @@
|
|||
<script setup lang="ts">
|
||||
import {ref} from "vue";
|
||||
|
||||
const isChecked = ref(false);
|
||||
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
class: {
|
||||
type: String,
|
||||
required: false,
|
||||
}
|
||||
});
|
||||
|
||||
const clickEmit = defineEmits(['click']);
|
||||
|
||||
const handleClick = () => {
|
||||
clickEmit('click', props.id, !isChecked.value);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="props.class">
|
||||
<input :id="props.id" type="checkbox" class="tgl tgl-skewed" v-model="isChecked" @click="handleClick"/>
|
||||
<label data-tg-off="OFF" data-tg-on="ON" :for="props.id" class="tgl-btn"></label>
|
||||
</div>
|
||||
</template>
|
|
@ -1,43 +1,4 @@
|
|||
import {type ApiJsonMsg} from "@/api"
|
||||
import {getWebsocketService} from "@/composables/websocket/websocketService";
|
||||
|
||||
export const toServer = new BroadcastChannel("toServer");
|
||||
export const toClient = new BroadcastChannel("toClient");
|
||||
export const toWebsocketCtrl = new BroadcastChannel("toWebsocketCtrl");
|
||||
export const toClientCtrl = new BroadcastChannel("toClientCtrl");
|
||||
|
||||
export enum ControlMsgType {
|
||||
WS_EVENT = "WS_EVENT",
|
||||
WS_SET_HOST = "WS_SET_HOST",
|
||||
WS_GET_STATE = "WS_GET_STATE",
|
||||
}
|
||||
|
||||
export enum ControlEvent {
|
||||
DISCONNECTED = "DISCONNECTED",
|
||||
LOADED = "LOADED",
|
||||
CONNECTED = "CONNECTED",
|
||||
CONNECTING = "CONNECTING",
|
||||
}
|
||||
|
||||
export interface ControlMsg {
|
||||
type: ControlMsgType,
|
||||
data: ControlEvent | string,
|
||||
}
|
||||
|
||||
export interface ServerMsg {
|
||||
type: "json" | "binary"
|
||||
data: object
|
||||
}
|
||||
|
||||
export function sendJsonMsg(apiJsonMsg: ApiJsonMsg) {
|
||||
const msg: ServerMsg = {
|
||||
type: "json",
|
||||
data: apiJsonMsg,
|
||||
};
|
||||
getWebsocketService().send(msg);
|
||||
// toServer.postMessage(msg);
|
||||
}
|
||||
|
||||
export function sendBinMsg(msg: ApiJsonMsg) {
|
||||
// toServer.postMessage(JSON.stringify(msg));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export function isDevMode() {
|
||||
return import.meta.env.VITE_APP_MODE === 'dev';
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import {h, render} from "vue";
|
||||
import MYSVG from "@/assets/icon/favicon.svg";
|
||||
|
||||
export function changeFavicon() {
|
||||
const SVGComponent = MYSVG;
|
||||
const container = document.createElement('div');
|
||||
render(h(SVGComponent), container);
|
||||
|
||||
const svgElement = container.innerHTML;
|
||||
const svgEncoded = encodeURIComponent(svgElement)
|
||||
.replace(/'/g, '%27')
|
||||
.replace(/"/g, '%22');
|
||||
|
||||
const link = document.getElementById('favicon');
|
||||
if (link instanceof HTMLLinkElement) {
|
||||
link.href = `data:image/svg+xml,${svgEncoded}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
export function logHelloMessage() {
|
||||
console.log(
|
||||
" ███████\n" +
|
||||
" █▒ ██\n" +
|
||||
" ▒▒▒▒▒▒▒▒█ ██ ██ ▒\n" +
|
||||
" ▒▒▒▒▒▒▒▒▒█ ██░ ░█ ▒▒▒ ▒▒ ▒▒▒\n" +
|
||||
" █ ██ ░█ ▒▒▒▒▒▒▒▒▒\n" +
|
||||
"███ ██ ░█ ██████▓▓█\n" +
|
||||
"█████ █▒ ░███▒▒▒▒▒▒▒▒\n" +
|
||||
"█████████░ ▓██▓▓▓▓▒▒▒▒▒▒\n" +
|
||||
"████████▒ ▒██▓▓▓▓▓▓▒▒▒▒▒\n" +
|
||||
" ███████ ██▓▓▓▓▓▓▓▓▒▒▒▒▓▓▓\n" +
|
||||
" ███████ ██▓▓▓▓▓▓▓▓▓▒██▓▓▓▓\n" +
|
||||
" ██████ ░█▓▓▓▓███████▓▓▓▓▓\n" +
|
||||
" █████ ██▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n" +
|
||||
" █████ ██▓▓▓▓▓▓▓▓▓▓███\n" +
|
||||
" ████▒█▓▓▓▓▓█████\n" +
|
||||
" ██████████\n" +
|
||||
"\n" +
|
||||
"Logo是什么意义?答:意义就是...没有意义。\n" +
|
||||
"大概是一起去整点赛博薯条吧。-来自法国鸽子");
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import {ElMessage, ElNotification} from "element-plus";
|
||||
|
||||
type NotificationType = 'error' | 'warning' | 'info' | 'success' ;
|
||||
|
||||
export function globalNotify(msg: string, type: NotificationType) {
|
||||
ElMessage({
|
||||
message: msg,
|
||||
grouping: true,
|
||||
type: type,
|
||||
duration: 2000,
|
||||
showClose: true,
|
||||
offset: 50,
|
||||
})
|
||||
}
|
||||
|
||||
export function globalNotifyRightSide(msg: string, type: NotificationType) {
|
||||
ElNotification({
|
||||
message: msg,
|
||||
type: type,
|
||||
duration: 1500,
|
||||
})
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
import MyWorker from '@/composables/websocket/ws.sharedworker?sharedworker&inline'
|
||||
import MyWorker from '@/composables/websocket/ws.sharedworker?sharedworker'
|
||||
import {WebsocketWrapper} from "@/composables/websocket/websocketWrapper";
|
||||
import type {ControlMsg, ServerMsg} from "@/composables/broadcastChannelDef";
|
||||
import {ControlMsgType, toClient, toClientCtrl} from "@/composables/broadcastChannelDef";
|
||||
|
||||
const toServer = new BroadcastChannel("toServer");
|
||||
import {toClient, toClientCtrl, toServer} from "@/composables/broadcastChannelDef";
|
||||
import type {ControlMsg, ServerMsg} from "@/api";
|
||||
import {ControlEvent, ControlMsgType} from "@/api";
|
||||
import {isDevMode} from "@/composables/buildMode";
|
||||
|
||||
export interface IWebsocketService {
|
||||
init(host: string,
|
||||
|
@ -29,13 +29,18 @@ class WebsocketShared implements IWebsocketService{
|
|||
|
||||
public static getInstance(): IWebsocketService {
|
||||
if (!WebsocketShared.instance) {
|
||||
if (isDevMode()) {
|
||||
console.log("New Shared Worker");
|
||||
}
|
||||
WebsocketShared.instance = new WebsocketShared();
|
||||
}
|
||||
return WebsocketShared.instance;
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
console.log("Shared Websocket init")
|
||||
if (isDevMode()) {
|
||||
console.log("Shared Websocket init");
|
||||
}
|
||||
this.msgCallback = () => {}
|
||||
this.ctrlCallback = () => {}
|
||||
this.messageEventProxy = this.messageEventProxy.bind(this);
|
||||
|
@ -44,14 +49,15 @@ class WebsocketShared implements IWebsocketService{
|
|||
}
|
||||
|
||||
init(host: string, msgCallback: (msg: ServerMsg) => any, ctrlCallback: (msg: ControlMsg) => any): void {
|
||||
console.log("webworker init")
|
||||
if (isDevMode()) {
|
||||
console.log("webworker init");
|
||||
}
|
||||
toClient.removeEventListener("message", this.messageEventProxy);
|
||||
toClientCtrl.removeEventListener("message", this.controlEventProxy);
|
||||
this.msgCallback = msgCallback;
|
||||
this.ctrlCallback = ctrlCallback;
|
||||
toClient.addEventListener("message", this.messageEventProxy);
|
||||
toClientCtrl.addEventListener("message", this.controlEventProxy);
|
||||
// this.worker.port.onmessage = this.controlEventProxy;
|
||||
this.worker.port.postMessage({type: ControlMsgType.WS_SET_HOST, data: host} as ControlMsg)
|
||||
}
|
||||
|
||||
|
@ -60,9 +66,11 @@ class WebsocketShared implements IWebsocketService{
|
|||
toClientCtrl.removeEventListener("message", this.controlEventProxy);
|
||||
}
|
||||
|
||||
reload(): void {
|
||||
// this.worker.terminate();
|
||||
}
|
||||
|
||||
send(msg: ServerMsg): void {
|
||||
console.log("Websocket Service send (not really)", msg)
|
||||
toServer.postMessage(msg);
|
||||
}
|
||||
|
||||
|
@ -71,6 +79,7 @@ class WebsocketShared implements IWebsocketService{
|
|||
}
|
||||
|
||||
controlEventProxy(ev: MessageEvent<ControlMsg>) {
|
||||
|
||||
this.ctrlCallback(ev.data);
|
||||
}
|
||||
}
|
||||
|
@ -93,8 +102,9 @@ class WebsocketClassic implements IWebsocketService{
|
|||
}
|
||||
|
||||
init(host: string, msgCallback: (ev: ServerMsg) => any, ctrlCallback: (msg: ControlMsg) => any): void {
|
||||
console.log("Websocket Service INIT called", WebsocketClassic.count);
|
||||
|
||||
if (isDevMode()) {
|
||||
console.log("Websocket Service INIT called", WebsocketClassic.count);
|
||||
}
|
||||
this.socket.init(host, msgCallback, ctrlCallback);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import type {ServerMsg, ControlMsg} from "@/composables/broadcastChannelDef";
|
||||
import {ControlEvent, ControlMsgType} from "@/composables/broadcastChannelDef";
|
||||
|
||||
import type {ApiJsonMsg, ControlMsg, ServerMsg} from "@/api";
|
||||
import {ControlEvent, ControlMsgType} from "@/api";
|
||||
import {isDevMode} from "@/composables/buildMode";
|
||||
|
||||
|
||||
interface IWebsocket {
|
||||
|
@ -21,7 +23,7 @@ class WebsocketDummy implements IWebsocket {
|
|||
}
|
||||
|
||||
class OneTimeWebsocket implements IWebsocket {
|
||||
private readonly heartBeatTimeout: number = 2;
|
||||
private readonly heartBeatTimeout: number = 1;
|
||||
private readonly host: string;
|
||||
private readonly intervalId: number;
|
||||
private readonly msgCallback: (ev: ServerMsg) => any;
|
||||
|
@ -30,6 +32,8 @@ class OneTimeWebsocket implements IWebsocket {
|
|||
private socket: WebSocket;
|
||||
private heartBeatTimeCount: number;
|
||||
private stoped: boolean;
|
||||
private cleared: boolean;
|
||||
private hasBeenConnected: boolean;
|
||||
|
||||
constructor(host: string,
|
||||
msgCallback: (ev: ServerMsg) => any,
|
||||
|
@ -38,6 +42,8 @@ class OneTimeWebsocket implements IWebsocket {
|
|||
) {
|
||||
this.host = host;
|
||||
this.stoped = false;
|
||||
this.cleared = false;
|
||||
this.hasBeenConnected = false;
|
||||
this.msgCallback = msgCallback;
|
||||
this.ctrlCallback = ctrlCallback;
|
||||
this.closeCallback = closeCallback;
|
||||
|
@ -51,8 +57,13 @@ class OneTimeWebsocket implements IWebsocket {
|
|||
if (this.heartBeatTimeCount > this.heartBeatTimeout) {
|
||||
/* did not receive packet "heartBeatTimeout" times,
|
||||
* connection may be lost: close the socket */
|
||||
this.socket.close();
|
||||
console.log("interval: ", this.heartBeatTimeCount, "state: ", this.socket.readyState);
|
||||
if (this.socket.readyState === this.socket.OPEN) {
|
||||
this.close();
|
||||
this.clear();
|
||||
}
|
||||
if (isDevMode()) {
|
||||
console.log("interval: ", this.heartBeatTimeCount, "state: ", this.socket.readyState);
|
||||
}
|
||||
}
|
||||
|
||||
this.heartBeatTimeCount++;
|
||||
|
@ -72,48 +83,47 @@ class OneTimeWebsocket implements IWebsocket {
|
|||
data: {},
|
||||
type: "json",
|
||||
}
|
||||
this.msgCallback(msg);
|
||||
if (typeof ev.data === "string") {
|
||||
try {
|
||||
msg.data = JSON.parse(ev.data);
|
||||
this.msgCallback(msg);
|
||||
msg.data = JSON.parse(ev.data) as ApiJsonMsg;
|
||||
if ((msg.data as ApiJsonMsg).cmd === undefined ||
|
||||
(msg.data as ApiJsonMsg).module === undefined
|
||||
){
|
||||
console.log("Server msg has no cmd or module");
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
msg.type = "binary";
|
||||
msg.data = ev.data;
|
||||
console.log(typeof ev.data);
|
||||
}
|
||||
this.msgCallback(msg);
|
||||
}
|
||||
|
||||
this.socket.onclose = () => {
|
||||
console.log('WebSocket Disconnected');
|
||||
|
||||
clearInterval(this.intervalId);
|
||||
this.socket.onclose = null
|
||||
this.socket.onopen = null
|
||||
this.socket.onerror = null
|
||||
this.socket.onmessage = null;
|
||||
|
||||
const msg: ControlMsg = {
|
||||
type: ControlMsgType.WS_EVENT,
|
||||
data: ControlEvent.DISCONNECTED,
|
||||
}
|
||||
this.ctrlCallback(msg);
|
||||
this.closeCallback();
|
||||
this.clear();
|
||||
};
|
||||
|
||||
this.socket.onerror = (error) => {
|
||||
console.error('WebSocket Error', error);
|
||||
this.socket.close();
|
||||
this.close();
|
||||
};
|
||||
|
||||
this.socket.onopen = ev => {
|
||||
console.log('WebSocket Connected');
|
||||
// console.log('WebSocket Connected');
|
||||
if (this.stoped) {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
this.heartBeatTimeCount = 0;
|
||||
this.hasBeenConnected = true;
|
||||
const msg: ControlMsg = {
|
||||
type: ControlMsgType.WS_EVENT,
|
||||
data: ControlEvent.CONNECTED,
|
||||
|
@ -145,6 +155,21 @@ class OneTimeWebsocket implements IWebsocket {
|
|||
this.socket.send(JSON.stringify(msg.data));
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
if (this.cleared) {
|
||||
return;
|
||||
}
|
||||
this.cleared = true;
|
||||
clearInterval(this.intervalId);
|
||||
|
||||
const msg: ControlMsg = {
|
||||
type: ControlMsgType.WS_EVENT,
|
||||
data: ControlEvent.DISCONNECTED,
|
||||
}
|
||||
this.ctrlCallback(msg);
|
||||
this.closeCallback();
|
||||
}
|
||||
}
|
||||
|
||||
export class WebsocketWrapper {
|
||||
|
@ -185,7 +210,6 @@ export class WebsocketWrapper {
|
|||
this.msgCallback = msgCallback;
|
||||
this.ctrlCallback = ctrlCallback;
|
||||
this.socket = new OneTimeWebsocket(host, this.msgCallback, this.ctrlCallback, this.closeCallback);
|
||||
|
||||
}
|
||||
|
||||
private closeCallback() {
|
||||
|
@ -194,7 +218,7 @@ export class WebsocketWrapper {
|
|||
}
|
||||
this.timeoutId = setTimeout(() =>
|
||||
this.newConnection(this.host, this.msgCallback, this.ctrlCallback),
|
||||
2000);
|
||||
1000);
|
||||
}
|
||||
|
||||
deinit() {
|
||||
|
@ -204,7 +228,6 @@ export class WebsocketWrapper {
|
|||
}
|
||||
|
||||
send(msg: ServerMsg) {
|
||||
console.log('WebSocket send: not ready', msg);
|
||||
// this.socket.send(msg)
|
||||
this.socket.send(msg)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import type {ControlMsg, ServerMsg} from "@/api";
|
||||
|
||||
declare const self: SharedWorkerGlobalScope;
|
||||
|
||||
import {WebsocketWrapper} from "@/composables/websocket/websocketWrapper";
|
||||
import type {ControlMsg, ServerMsg} from "@/composables/broadcastChannelDef";
|
||||
import {ControlEvent, ControlMsgType, toClient, toClientCtrl, toServer} from "@/composables/broadcastChannelDef";
|
||||
import {toClient, toClientCtrl, toServer} from "@/composables/broadcastChannelDef";
|
||||
import {ControlEvent, ControlMsgType} from "@/api";
|
||||
import {isDevMode} from "@/composables/buildMode";
|
||||
|
||||
const websocket = new WebsocketWrapper();
|
||||
let host = "";
|
||||
|
@ -19,7 +22,9 @@ function msgBroadcast(msg: ServerMsg) {
|
|||
self.onconnect = function(event) {
|
||||
const port = event.ports[0];
|
||||
port.onmessage = function (e: MessageEvent<ControlMsg>) {
|
||||
console.log('Received message in SharedWorker:', e.data);
|
||||
if (isDevMode()) {
|
||||
console.log('Received message in SharedWorker:', e.data);
|
||||
}
|
||||
if (e.data.type === ControlMsgType.WS_SET_HOST) {
|
||||
if (host === "" && e.data.data !== "") {
|
||||
host = e.data.data;
|
||||
|
|
|
@ -3,9 +3,7 @@ import zh from '@/locales/zh'
|
|||
import en from '@/locales/en'
|
||||
|
||||
// const locale = localStorage.getItem('lang') || 'zh';
|
||||
const locale = 'zh';
|
||||
|
||||
console.log("langggggg:", locale);
|
||||
export const locale = 'zh';
|
||||
|
||||
const i18n = createI18n({
|
||||
globalInjection: true,
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="my-2">
|
||||
<slot/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,14 @@
|
|||
import i18n from '@/i18n';
|
||||
import zh from '@/locales/zh';
|
||||
|
||||
type NestedKeyOf<ObjectType extends object> = {
|
||||
[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object
|
||||
? `${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]>}`
|
||||
: `${Key}`
|
||||
}[keyof ObjectType & (string | number)];
|
||||
|
||||
type TranslationKeys = NestedKeyOf<typeof zh>;
|
||||
|
||||
export function translate<K extends TranslationKeys>(key: K | string): string {
|
||||
return i18n.global.t(key.toLowerCase());
|
||||
}
|
|
@ -1,17 +1,20 @@
|
|||
export default {
|
||||
DISCONNECTED: "未连接",
|
||||
CONNECTED: "已连接",
|
||||
CONNECTING: "连接中",
|
||||
disconnected: "未连接",
|
||||
connected: "已连接",
|
||||
connecting: "连接中",
|
||||
|
||||
WS: {
|
||||
DISCONNECTED: "未连接",
|
||||
CONNECTED: "已连接",
|
||||
CONNECTING: "连接中",
|
||||
ws: {
|
||||
disconnected: "未连接",
|
||||
connected: "已连接",
|
||||
connecting: "连接中",
|
||||
},
|
||||
|
||||
PAGE: {
|
||||
HOME: "主页",
|
||||
ABOUT: "关于",
|
||||
FEEDBACK: "反馈",
|
||||
page: {
|
||||
home: "主页",
|
||||
wifi: "Wi-Fi",
|
||||
about: "关于",
|
||||
uart: "UART透传",
|
||||
feedback: "反馈",
|
||||
close: "关闭",
|
||||
},
|
||||
}
|
|
@ -1,4 +1,9 @@
|
|||
import '@/assets/tailwind.css'
|
||||
import '@/assets/toggle_skewed.css'
|
||||
import '@/assets/page.css'
|
||||
import '@/assets/navigation.css'
|
||||
import 'element-plus/dist/index.css';
|
||||
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
@ -10,7 +15,7 @@ import router from './router'
|
|||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
app.use(i18n);
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
|
|
|
@ -1,29 +1,48 @@
|
|||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import {createRouter, createWebHistory} from 'vue-router'
|
||||
import Home from '@/views/Home.vue'
|
||||
import Wifi from '@/views/Wifi.vue'
|
||||
import Feedback from '@/views/Feedback.vue'
|
||||
import About from '@/views/About.vue'
|
||||
import Uart from '@/views/Uart.vue'
|
||||
import {translate} from "@/locales";
|
||||
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: Home
|
||||
}, {
|
||||
path: '/home:ext(.*)',
|
||||
component: Home,
|
||||
}, {
|
||||
path: '/wifi:ext(.*)',
|
||||
component: Wifi,
|
||||
}, {
|
||||
path: '/about:ext(.*)',
|
||||
name: 'about',
|
||||
// route level code-splitting
|
||||
// this generates a separate chunk (About.[hash].js) for this route
|
||||
// which is lazy-loaded when the route is visited.
|
||||
component: () => import('@/views/About.vue')
|
||||
}
|
||||
]
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
meta: {title: translate("page.home")},
|
||||
component: Home
|
||||
}, {
|
||||
path: '/home:ext(.*)',
|
||||
meta: {title: translate("page.home")},
|
||||
redirect: () => '/',
|
||||
}, {
|
||||
path: '/wifi:ext(.*)',
|
||||
meta: {title: translate('page.wifi')},
|
||||
component: Wifi,
|
||||
}, {
|
||||
path: '/about:ext(.*)',
|
||||
meta: {title: translate('page.about')},
|
||||
component: About,
|
||||
}, {
|
||||
path: '/uart:ext(.*)',
|
||||
meta: {title: translate('page.uart')},
|
||||
component: Uart,
|
||||
}, {
|
||||
path: '/feedback:ext(.*)',
|
||||
meta: {title: translate('page.feedback')},
|
||||
name: 'feedback',
|
||||
component: Feedback,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
document.title = typeof to.meta.title === 'string' ? to.meta.title + " | 允斯工作室" : '允斯调试器';
|
||||
next();
|
||||
});
|
||||
|
||||
export default router
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import type {ApiJsonMsg, ControlMsg, ServerMsg} from "@/api";
|
||||
|
||||
export interface IModuleCallback {
|
||||
ctrlCallback: (msg: ControlMsg) => void;
|
||||
serverMsgCallback: (msg: ServerMsg) => void;
|
||||
}
|
||||
|
||||
const moduleMap = new Map<number, IModuleCallback>();
|
||||
|
||||
export function registerModule(moduleId: number, moduleCallback: IModuleCallback): boolean {
|
||||
if (moduleMap.has(moduleId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
moduleMap.set(moduleId, moduleCallback);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function unregisterModule(moduleId: number) {
|
||||
moduleMap.delete(moduleId);
|
||||
}
|
||||
|
||||
export function routeModuleServerMsg(msg: ServerMsg) {
|
||||
if (msg.type == "json") {
|
||||
const module = (msg.data as ApiJsonMsg).module;
|
||||
const moduleHandler = moduleMap.get(module);
|
||||
if (moduleHandler) {
|
||||
moduleHandler.serverMsgCallback(msg);
|
||||
} else {
|
||||
console.log("routeModuleServerMsg module not loaded", module);
|
||||
}
|
||||
} else {
|
||||
console.log("routeModuleServerMsg ignored:", msg);
|
||||
}
|
||||
}
|
||||
|
||||
export function routeCtrlMsg(msg: ControlMsg) {
|
||||
for (const item of moduleMap) {
|
||||
item[1].ctrlCallback(msg);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import {defineStore} from "pinia";
|
||||
import {ControlEvent} from "@/composables/broadcastChannelDef";
|
||||
|
||||
import {ControlEvent} from "@/api";
|
||||
|
||||
export const useWsStore = defineStore('websocket', {
|
||||
state: () => {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import mitt from 'mitt';
|
||||
|
||||
const emitter = mitt();
|
||||
|
||||
export default emitter;
|
|
@ -1,12 +1,50 @@
|
|||
<script setup lang="ts">
|
||||
const version = __APP_VERSION__;
|
||||
const compileTime = __BUILD_TIME__;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h2>About Page</h2>
|
||||
<!-- Your about page content goes here -->
|
||||
<h2>About Page</h2>
|
||||
<div class="text-layout">
|
||||
<el-divider></el-divider>
|
||||
<el-divider>关于上位机</el-divider>
|
||||
<el-divider></el-divider>
|
||||
|
||||
<el-descriptions border :column="1" class="mt-5 description-style">
|
||||
<el-descriptions-item label="上位机版本">{{version}}</el-descriptions-item>
|
||||
<el-descriptions-item label="发布时间">{{compileTime}}</el-descriptions-item>
|
||||
<el-descriptions-item label="许可证">MIT</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions title="鸣谢" border :column="1" class="mt-5 description-style">
|
||||
<el-descriptions-item label="vuejs"><a href="https://github.com/vuejs/vue/blob/main/LICENSE">MIT</a></el-descriptions-item>
|
||||
<el-descriptions-item label="typescript"><a
|
||||
href="https://github.com/microsoft/TypeScript/blob/main/LICENSE.txt">Apache 2.0</a></el-descriptions-item>
|
||||
<el-descriptions-item label="vite"><a href="https://github.com/vitejs/vite/blob/main/LICENSE">MIT</a></el-descriptions-item>
|
||||
<el-descriptions-item label="tailwindcss"><a href="https://github.com/tailwindlabs/tailwindcss/blob/master/LICENSE">MIT</a></el-descriptions-item>
|
||||
<el-descriptions-item label="element-plus"><a
|
||||
href="https://github.com/element-plus/element-plus/blob/dev/LICENSE">MIT</a></el-descriptions-item>
|
||||
<el-descriptions-item label="pinia"><a href="https://github.com/vuejs/pinia/blob/v2/LICENSE">MIT</a></el-descriptions-item>
|
||||
<el-descriptions-item label="mitt"><a href="https://github.com/developit/mitt/blob/main/LICENSE">MIT</a></el-descriptions-item>
|
||||
<el-descriptions-item label="vue-router"><a href="https://github.com/vuejs/vue-router/blob/dev/LICENSE">MIT</a></el-descriptions-item>
|
||||
<el-descriptions-item label="vue-i18n"><a href="https://github.com/kazupon/vue-i18n?tab=MIT-1-ov-file#readme">MIT</a></el-descriptions-item>
|
||||
<el-descriptions-item label="lightningcss"><a href="https://github.com/parcel-bundler/lightningcss/blob/master/LICENSE">MPL-2.0 license</a></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions title="作者:空空(kerms)" border :column="1" class="mt-5 description-style">
|
||||
<el-descriptions-item label="github"><a href="https://github.com/kerms">https://github.com/kerms</a></el-descriptions-item>
|
||||
<el-descriptions-item label="邮箱">kerms@niazo.org</el-descriptions-item>
|
||||
<el-descriptions-item label="BiliBili"><a href="https://space.bilibili.com/38669852">UID38669852</a></el-descriptions-item>
|
||||
<el-descriptions-item label="QQ群">642246000</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">欢迎大家来打扰啊~</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
<el-divider></el-divider>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.description-style :deep(.el-descriptions__label) {
|
||||
@apply w-32
|
||||
}
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
let name = "about-a";
|
||||
|
||||
</script>
|
||||
</style>
|
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
|
||||
<div class="text-layout">
|
||||
<el-divider></el-divider>
|
||||
<el-divider>反馈</el-divider>
|
||||
<el-divider></el-divider>
|
||||
|
||||
<el-descriptions title="反馈/建议/需要新功能" border :column="1">
|
||||
<el-descriptions-item label="QQ群">642246000</el-descriptions-item>
|
||||
<el-descriptions-item label="作者邮箱">kerms@niazo.org</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
</div>
|
||||
<el-divider></el-divider>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
|
@ -1,20 +1,12 @@
|
|||
<template>
|
||||
<h2>Home Page</h2>
|
||||
<!-- Your home page content goes here -->
|
||||
<h2>Home Page</h2>
|
||||
<nav>
|
||||
<!-- <RouterLink to="/">Home</RouterLink>-->
|
||||
<!-- <RouterLink to="/wifi">Wifi</RouterLink>-->
|
||||
<!-- <RouterLink to="/about">About</RouterLink>-->
|
||||
<!-- <RouterLink to="/test">Test</RouterLink>-->
|
||||
<!-- <RouterLink to="/home">home</RouterLink>-->
|
||||
</nav>
|
||||
<div class="text-layout">
|
||||
<h2 class="page-title">主页</h2>
|
||||
<p>空空如也,暂不知道放什么。</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
document.title = "Home";
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="text-layout">
|
||||
<h2 class="page-title opacity-10">尽请期待</h2>
|
||||
</div>
|
||||
</template>
|
|
@ -1,7 +1,13 @@
|
|||
<template>
|
||||
<div class="wifiView">
|
||||
<h2>Wifi View</h2>
|
||||
<el-form label-width="auto" ref="formRef" :model="ssidValidateForm">
|
||||
<div class="text-layout">
|
||||
<h1 class="page-title">
|
||||
Wi-Fi 配置
|
||||
</h1>
|
||||
<el-divider></el-divider>
|
||||
|
||||
|
||||
<h2 class="mb-4 text-xl font-bold tracking-tight md:text-2xl lg:text-3xl">连接Wi-Fi</h2>
|
||||
<el-form label-width="auto" ref="formRef" :model="ssidValidateForm" class="m-auto">
|
||||
<el-form-item
|
||||
label="Wi-Fi名"
|
||||
prop="wifiSsid"
|
||||
|
@ -19,22 +25,21 @@
|
|||
value-key="ssid"
|
||||
>
|
||||
<template #default="{ item }">
|
||||
<div class="flex">
|
||||
<div class="flex items-center border-b">
|
||||
<InlineSvg :name="item.wifiLogo" class="h-6 pr-4"></InlineSvg>
|
||||
<!-- <span class="w-10">{{ item.rssi }}</span>-->
|
||||
<!-- <span class="w-10">{{ item.rssi }}</span>-->
|
||||
<div>{{ item.ssid }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-autocomplete>
|
||||
<div class="h-8">
|
||||
<el-button class="h-8" @click="onScanClick">扫描</el-button>
|
||||
<el-button class="h-8" @click="onScanClick">{{ scanText }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<el-input
|
||||
v-model="password"
|
||||
v-model="ssidValidateForm.password"
|
||||
show-password
|
||||
type="password"
|
||||
clearable
|
||||
|
@ -42,106 +47,296 @@
|
|||
</el-form-item>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<el-button @click="onConnect" type="primary">连接</el-button>
|
||||
<el-button @click="onConnectClick" type="primary">连接</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<el-divider></el-divider>
|
||||
|
||||
|
||||
<el-descriptions
|
||||
title="Wi-Fi终端信息"
|
||||
:column="1"
|
||||
border
|
||||
class="description-style"
|
||||
>
|
||||
<el-descriptions-item label="asd">
|
||||
<template #label >
|
||||
<div>
|
||||
信号强度
|
||||
</div>
|
||||
</template>
|
||||
<template #default >
|
||||
{{ wifiStaApInfo.rssi }}
|
||||
</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item span="1">
|
||||
<template #label>
|
||||
<div>
|
||||
SSID
|
||||
</div>
|
||||
</template>
|
||||
{{ wifiStaApInfo.ssid }}
|
||||
</el-descriptions-item>
|
||||
<!-- <el-descriptions-item span="6" >-->
|
||||
<!-- <template #label>-->
|
||||
<!-- <div>-->
|
||||
<!-- 密码-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- <password-viewer password="asdasdasd"></password-viewer>-->
|
||||
<!-- </el-descriptions-item>-->
|
||||
<el-descriptions-item span="4">
|
||||
<template #label>
|
||||
<div>
|
||||
IP
|
||||
</div>
|
||||
</template>
|
||||
{{ wifiStaApInfo.ip }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item span="4">
|
||||
<template #label>
|
||||
<div>
|
||||
MAC
|
||||
</div>
|
||||
</template>
|
||||
{{ wifiStaApInfo.mac }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item span="4">
|
||||
<template #label>
|
||||
<div>
|
||||
网关
|
||||
</div>
|
||||
</template>
|
||||
{{ wifiStaApInfo.gateway }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item span="4">
|
||||
<template #label>
|
||||
<div>
|
||||
掩码
|
||||
</div>
|
||||
</template>
|
||||
{{ wifiStaApInfo.netmask }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-divider></el-divider>
|
||||
|
||||
<el-descriptions
|
||||
title="Wi-Fi热点信息"
|
||||
:column="1"
|
||||
border
|
||||
class="description-style"
|
||||
>
|
||||
<el-descriptions-item span="6">
|
||||
<template #label>
|
||||
<div>
|
||||
SSID
|
||||
</div>
|
||||
</template>
|
||||
{{ wifiApInfo.ssid }}
|
||||
</el-descriptions-item>
|
||||
<!-- <el-descriptions-item span="6">-->
|
||||
<!-- <template #label>-->
|
||||
<!-- <div>-->
|
||||
<!-- 密码-->
|
||||
<!-- </div>-->
|
||||
<!-- </template>-->
|
||||
<!-- <password-viewer password="asdasdasd"></password-viewer>-->
|
||||
<!-- </el-descriptions-item>-->
|
||||
<el-descriptions-item span="4">
|
||||
<template #label>
|
||||
<div>
|
||||
IP
|
||||
</div>
|
||||
</template>
|
||||
{{ wifiApInfo.ip }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item span="4">
|
||||
<template #label>
|
||||
<div>
|
||||
MAC
|
||||
</div>
|
||||
</template>
|
||||
{{ wifiApInfo.mac }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item span="4">
|
||||
<template #label>
|
||||
<div>
|
||||
网关
|
||||
</div>
|
||||
</template>
|
||||
{{ wifiApInfo.gateway }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item span="4">
|
||||
<template #label>
|
||||
<div>
|
||||
掩码
|
||||
</div>
|
||||
</template>
|
||||
{{ wifiApInfo.netmask }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-divider></el-divider>
|
||||
</div>
|
||||
<el-button class="h-8">扫描</el-button>
|
||||
<el-button type="primary">连接</el-button>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {inject, onMounted, onUnmounted, reactive, ref} from "vue";
|
||||
import {wifi_get_ap_info, wifi_get_scan_list, type WifiInfo, type WifiList} from "@/api/apiWifi";
|
||||
import type {FormInstance} from "element-plus";
|
||||
import type {ServerMsg,ControlMsg} from "@/composables/broadcastChannelDef";
|
||||
|
||||
import {computed, onMounted, onUnmounted, reactive, ref} from "vue";
|
||||
import {
|
||||
ControlEvent,
|
||||
ControlMsgType
|
||||
} from "@/composables/broadcastChannelDef";
|
||||
import InlineSvg from "@/components/InlineSvg.vue";
|
||||
import {getWebsocketService} from "@/composables/websocket/websocketService";
|
||||
wifi_sta_get_ap_info,
|
||||
wifi_get_scan_list,
|
||||
WifiCmd,
|
||||
type WifiInfo,
|
||||
type WifiList,
|
||||
WifiModuleID,
|
||||
wifi_ap_get_info
|
||||
} from "@/api/apiWifi";
|
||||
import type {FormInstance} from "element-plus";
|
||||
|
||||
import InlineSvg from "@/components/InlineSvg.vue";
|
||||
import type {ApiJsonMsg, ControlMsg, ServerMsg} from "@/api";
|
||||
import {ControlEvent, ControlMsgType} from "@/api";
|
||||
import {registerModule, unregisterModule} from "@/router/msgRouter";
|
||||
import {useWsStore} from "@/stores/websocket";
|
||||
import {globalNotify, globalNotifyRightSide} from "@/composables/notification";
|
||||
|
||||
const formRef = ref<FormInstance>()
|
||||
let wifiListPlaceholder = ref("我的WIFI")
|
||||
let ssidValidateForm = reactive({
|
||||
wifiSsid: ""
|
||||
wifiSsid: "",
|
||||
password: "",
|
||||
})
|
||||
const password = ref('')
|
||||
|
||||
let scanning = false;
|
||||
|
||||
let wsStore = useWsStore();
|
||||
|
||||
const defWifiInfo: WifiInfo = {
|
||||
cmd: 1,
|
||||
module: 1,
|
||||
gateway: "未连接",
|
||||
ip: "未连接",
|
||||
mac: "未连接",
|
||||
rssi: 0,
|
||||
netmask: "未连接",
|
||||
ssid: "未连接",
|
||||
}
|
||||
|
||||
let wifiStaApInfo = reactive<WifiInfo>({...defWifiInfo});
|
||||
let wifiApInfo = reactive<WifiInfo>({...defWifiInfo});
|
||||
|
||||
let scanning = ref(false);
|
||||
let scan_cb: any;
|
||||
let options: Array<WifiInfo> = [
|
||||
|
||||
]
|
||||
let options: Array<WifiInfo> = [];
|
||||
const scanText = computed(() => {
|
||||
return scanning.value ? "扫描中" : "扫描";
|
||||
});
|
||||
|
||||
const querySearch = (queryString: string, cb: any) => {
|
||||
if (scanning) {
|
||||
if (scanning.value) {
|
||||
scan_cb = cb;
|
||||
} else {
|
||||
cb(options);
|
||||
}
|
||||
}
|
||||
|
||||
const onClientMsg = (ev: MessageEvent<ServerMsg>) => {
|
||||
if (ev.data.type !== "json") {
|
||||
const onClientMsg = (msg: ServerMsg) => {
|
||||
if (msg.type !== "json") {
|
||||
return;
|
||||
}
|
||||
|
||||
const json: object = ev.data.data;
|
||||
let json = msg.data as ApiJsonMsg;
|
||||
|
||||
let wifiList: WifiList;
|
||||
try {
|
||||
wifiList = ev.data.data as WifiList;
|
||||
console.log(wifiList);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
scanning = false;
|
||||
wifiList.scan_list.forEach(value => {
|
||||
if (value.rssi > -50) {
|
||||
value.wifiLogo = "wifi-3";
|
||||
} else if (value.rssi > -65) {
|
||||
value.wifiLogo = "wifi-2";
|
||||
} else {
|
||||
value.wifiLogo = "wifi-1";
|
||||
switch (json.cmd as WifiCmd) {
|
||||
case WifiCmd.UNKNOWN:
|
||||
break;
|
||||
case WifiCmd.WIFI_API_JSON_STA_GET_AP_INFO: {
|
||||
const info = msg.data as WifiInfo;
|
||||
Object.assign(wifiStaApInfo, info);
|
||||
break;
|
||||
}
|
||||
case WifiCmd.WIFI_API_JSON_CONNECT:
|
||||
break;
|
||||
case WifiCmd.WIFI_API_JSON_GET_SCAN: {
|
||||
const list = msg.data as WifiList;
|
||||
scanning.value = false;
|
||||
list.scan_list.forEach(value => {
|
||||
if (value.rssi > -50) {
|
||||
value.wifiLogo = "wifi-3";
|
||||
} else if (value.rssi > -65) {
|
||||
value.wifiLogo = "wifi-2";
|
||||
} else {
|
||||
value.wifiLogo = "wifi-1";
|
||||
}
|
||||
});
|
||||
options = list.scan_list;
|
||||
if (scan_cb) {
|
||||
scan_cb(options);
|
||||
scan_cb = null;
|
||||
}
|
||||
globalNotifyRightSide("扫描完成", "success");
|
||||
break;
|
||||
}
|
||||
case WifiCmd.WIFI_API_JSON_DISCONNECT:
|
||||
break;
|
||||
case WifiCmd.WIFI_API_JSON_AP_GET_INFO: {
|
||||
const info = msg.data as WifiInfo;
|
||||
Object.assign(wifiApInfo, info);
|
||||
break;
|
||||
}
|
||||
});
|
||||
options = wifiList.scan_list;
|
||||
if (scan_cb) {
|
||||
scan_cb(options);
|
||||
scan_cb = null;
|
||||
}
|
||||
};
|
||||
|
||||
const onClientCtrl = (ev: MessageEvent<ControlMsg>) => {
|
||||
if (ev.data.type !== ControlMsgType.WS_EVENT) {
|
||||
const onClientCtrl = (msg: ControlMsg) => {
|
||||
if (msg.type !== ControlMsgType.WS_EVENT) {
|
||||
return
|
||||
}
|
||||
|
||||
if (ev.data.data === ControlEvent.CONNECTED) {
|
||||
wifi_get_ap_info();
|
||||
if (msg.data === ControlEvent.DISCONNECTED) {
|
||||
Object.assign(wifiStaApInfo, defWifiInfo);
|
||||
Object.assign(wifiApInfo, defWifiInfo);
|
||||
}
|
||||
|
||||
if (msg.data === ControlEvent.CONNECTED) {
|
||||
wifi_sta_get_ap_info();
|
||||
wifi_ap_get_info();
|
||||
}
|
||||
};
|
||||
|
||||
function onScanClick() {
|
||||
scanning = true;
|
||||
if (wsStore.state !== ControlEvent.CONNECTED) {
|
||||
globalNotify("调试器未连接", 'error');
|
||||
return;
|
||||
}
|
||||
scanning.value = true;
|
||||
wifi_get_scan_list();
|
||||
}
|
||||
|
||||
function onConnect() {
|
||||
console.log(ssidValidateForm.wifiSsid, password.value);
|
||||
function onConnectClick() {
|
||||
if (wsStore.state !== ControlEvent.CONNECTED) {
|
||||
globalNotify("调试器未连接", 'error');
|
||||
return;
|
||||
}
|
||||
console.log(ssidValidateForm.wifiSsid, ssidValidateForm.password);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
registerModule(WifiModuleID, {
|
||||
ctrlCallback: onClientCtrl,
|
||||
serverMsgCallback: onClientMsg
|
||||
});
|
||||
wifi_sta_get_ap_info();
|
||||
wifi_ap_get_info();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
unregisterModule(WifiModuleID);
|
||||
});
|
||||
|
||||
|
||||
|
@ -149,10 +344,7 @@ onUnmounted(() => {
|
|||
|
||||
|
||||
<style scoped>
|
||||
.wifiView {
|
||||
background-color: bisque;
|
||||
border-radius: 5px;
|
||||
padding: 20px;
|
||||
.description-style :deep(.el-descriptions__label) {
|
||||
@apply w-32
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -1,44 +1,38 @@
|
|||
<template>
|
||||
<nav class="relative px-4 py-1 flex justify-between items-center border-b">
|
||||
<nav class="relative px-2 py-1 flex justify-between items-center border-b">
|
||||
<div class="flex">
|
||||
<button @click.prevent="sideMenuOpen=true" class="flex items-center hover:text-blue-600 p-3">
|
||||
<svg class="block h-4 w-4 fill-current" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<title>Mobile menu</title>
|
||||
<title>导航侧栏</title>
|
||||
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<router-link to="/" class="text-3xl px-4 font-bold leading-none">
|
||||
<InlineSvg name="home" class="h-10"></InlineSvg>
|
||||
<router-link to="/" class="text-3xl px-4 font-bold leading-none" title="走,去码头整点薯条">
|
||||
<InlineSvg name="favicon" class="h-10"></InlineSvg>
|
||||
</router-link>
|
||||
<!-- <a class="text-3xl px-4 font-bold leading-none" href="/">-->
|
||||
<!-- <InlineSvg name="home" class="h-10"></InlineSvg>-->
|
||||
<!-- </a>-->
|
||||
<router-link to="/" class="flex items-center text-sm text-blue-600 font-bold">主页</router-link>
|
||||
<!-- <router-link to="/" class="flex items-center text-sm text-blue-600 font-bold">主页</router-link>-->
|
||||
<!-- <a class="flex items-center text-sm text-blue-600 font-bold" href="/">主页6</a>-->
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<ul class="hidden absolute top-1/2 left-1/2 transform -translate-y-1/2 -translate-x-1/2 md:flex md:mx-auto md:items-center md:w-auto md:space-x-6">
|
||||
<li><router-link to="/wifi" title="Wifi" class="text-sm text-gray-400 hover:text-gray-800">wifi</router-link></li>
|
||||
<!-- <li><a class="text-sm text-gray-400 hover:text-gray-500" href="#">Home</a></li>-->
|
||||
<!-- <li><a class="text-sm text-blue-600 font-bold">About Us</a></li>-->
|
||||
<!-- <li><a class="text-sm text-gray-400 hover:text-gray-500" href="#">Services</a></li>-->
|
||||
<li v-for="(item, index) in menuItems" :key="index" class="router-link">
|
||||
<router-link :to="item.href" :class="item?.class">{{item.name}}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- <a class="md:ml-auto md:mr-3"></a>-->
|
||||
<div class="flex">
|
||||
<button @click="stateMenuOpen=true"
|
||||
class="py-2 px-6 bg-blue-500 hover:bg-blue-600 text-sm text-white font-bold rounded-xl transition duration-200">
|
||||
<span class="flex">
|
||||
<svg class="mr-2" width="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 19.4998H12.01M2 8.81929C3.69692 7.30051 5.74166 6.16236 8 5.53906M5 12.8584C5.86251 12.0129 6.87754 11.3223 8 10.8319M16 5.53906C18.2583 6.16236 20.3031 7.30051 22 8.81929M16 10.8319C17.1225 11.3223 18.1375 12.0129 19 12.8584M12 4.5V15.4998" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span>{{ wsState }}</span>
|
||||
</span>
|
||||
</button>
|
||||
<!-- <span>{{ $t("Disconnected") }}</span>-->
|
||||
<el-button @click="stateMenuOpen=true" :type="wsColor" size="large" class="transition duration-1000">
|
||||
<InlineSvg v-show="wsColor!=='success'" name="wifi-exclamation" class="mr-2" width="20"></InlineSvg>
|
||||
<InlineSvg v-show="wsColor==='success'" name="wifi-3" class="mr-2" width="20"></InlineSvg>
|
||||
{{ wsState }}
|
||||
</el-button>
|
||||
</div>
|
||||
</nav>
|
||||
<div :class='["custom-drawer", {open: sideMenuOpen}]'>
|
||||
|
@ -46,26 +40,30 @@
|
|||
v-model="sideMenuOpen"
|
||||
:with-header="false"
|
||||
size=""
|
||||
:direction="'ltr'">
|
||||
<div :class="[sideMenuItemClass]" class="px-6" @click="sideMenuOpen=false">
|
||||
<InlineSvg name="cross" class="w-6"></InlineSvg>
|
||||
</div>
|
||||
<div class="flex-col justify-between m-4 mt-0">
|
||||
:direction="'ltr'"
|
||||
>
|
||||
<div id="testborder" :class="[sideMenuItemClass]" class="pr-6 flex text-gray-500" @click="sideMenuOpen=false">
|
||||
<InlineSvg name="cross" class="h-6"></InlineSvg>
|
||||
<div>
|
||||
<ul>
|
||||
<li v-for="(item, index) in menuItems" class="mb-1" :key="index">
|
||||
<router-link @click="sideMenuOpen=false" :title="item.name" :to="item.href" :class="sideMenuItemClass">{{ item.name }}</router-link>
|
||||
<!-- <a :href="item.href" :class="sideMenuItemClass">{{ item.name }}</a>-->
|
||||
</li>
|
||||
</ul>
|
||||
<p class="h-6 flex items-center">{{ $t("page.close") }}</p>
|
||||
</div>
|
||||
<div class="mt-auto">
|
||||
</div>
|
||||
|
||||
<p class="my-4 text-xs text-center text-gray-400">
|
||||
<span>Copyright kerms 2024</span>
|
||||
<div class="flex flex-col justify-between m-4 mt-0">
|
||||
<ul>
|
||||
<li v-for="(item, index) in menuItems" class="mb-1" :key="index">
|
||||
<router-link @click="sideMenuOpen=false" :title="item.name" :to="item.href" :class="[sideMenuItemClass, item?.class]">{{ item.name }}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div>
|
||||
<p class="text-xs text-center text-gray-400">
|
||||
<span>Copyright <a href="http://github.com/kerms">kerms</a> 2024</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
|
||||
|
@ -79,12 +77,7 @@
|
|||
<div class="flex-col justify-between m-4 bg-white">
|
||||
|
||||
<div class="mt-auto">
|
||||
<div class="pt-6">
|
||||
<a class="block px-4 py-3 mb-3 leading-loose text-xs text-center font-semibold bg-gray-50 hover:bg-gray-100 rounded-xl"
|
||||
href="#">Sign in</a>
|
||||
<a class="block px-4 py-3 mb-2 leading-loose text-xs text-center text-white font-semibold bg-blue-600 hover:bg-blue-700 rounded-xl"
|
||||
href="#">Sign Up</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
@ -93,41 +86,55 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import InlineSvg from "@/components/InlineSvg.vue";
|
||||
import {computed, ref, toRef} from "vue";
|
||||
import {computed, ref} from "vue";
|
||||
import {useWsStore} from "@/stores/websocket";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {translate} from "@/locales";
|
||||
import {ControlEvent} from "@/api";
|
||||
|
||||
const { t } = useI18n()
|
||||
const wsStore = useWsStore();
|
||||
|
||||
const sideMenuItemClass = "block p-4 text-sm font-semibold text-gray-400 hover:bg-blue-50 hover:text-blue-600 rounded"
|
||||
const sideMenuItemClass = "block p-4 text-sm font-semibold hover:bg-blue-50 hover:text-blue-600 rounded"
|
||||
const sideMenuOpen = ref(false);
|
||||
const stateMenuOpen = ref(false)
|
||||
|
||||
const wsState = computed(() => {
|
||||
const wsColor = computed(() => {
|
||||
let ret = "danger";
|
||||
switch (wsStore.state) {
|
||||
case ControlEvent.DISCONNECTED:
|
||||
ret = "danger";
|
||||
break
|
||||
case ControlEvent.CONNECTED:
|
||||
ret = "success";
|
||||
break
|
||||
case ControlEvent.CONNECTING:
|
||||
ret = "warning";
|
||||
break
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
|
||||
return t(wsStore.state);
|
||||
const wsState = computed(() => {
|
||||
return translate(wsStore.state);
|
||||
});
|
||||
|
||||
const menuItems = ([
|
||||
{
|
||||
name: "Home",
|
||||
name: translate("page.home"),
|
||||
href: "/",
|
||||
|
||||
}, {
|
||||
name: "About Us",
|
||||
href: "/about",
|
||||
}, {
|
||||
name: "Services",
|
||||
href: "/",
|
||||
}, {
|
||||
name: "Wifi",
|
||||
name: translate("page.wifi"),
|
||||
href: "/wifi",
|
||||
}, {
|
||||
name: "Contact",
|
||||
href: "/",
|
||||
name: translate("page.about"),
|
||||
href: "/about",
|
||||
}, {
|
||||
name: "6",
|
||||
href: "/",
|
||||
name: translate("page.feedback"),
|
||||
href: "/feedback",
|
||||
}, {
|
||||
name: translate("page.uart"),
|
||||
href: "/uart",
|
||||
class: "todo-menu-item",
|
||||
},
|
||||
]);
|
||||
|
||||
|
@ -153,4 +160,5 @@ const menuItems = ([
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
|
@ -5,7 +5,9 @@ import Components from 'unplugin-vue-components/vite'
|
|||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import svgLoader from "vite-svg-loader";
|
||||
|
||||
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
|
||||
import { viteSingleFile } from 'vite-plugin-singlefile'
|
||||
import packageJson from "./package.json"
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
|
@ -18,18 +20,46 @@ export default defineConfig({
|
|||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
svgLoader(),
|
||||
cssInjectedByJsPlugin(),
|
||||
viteSingleFile(),
|
||||
],
|
||||
define: {
|
||||
'__APP_VERSION__': JSON.stringify(packageJson.version),
|
||||
'__BUILD_TIME__': JSON.stringify(new Date().toISOString()),
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
}
|
||||
},
|
||||
|
||||
cacheDir: "/tmp/zhuang/cache",
|
||||
worker: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
inlineDynamicImports: true,
|
||||
minifyInternalExports: true,
|
||||
// entryFileNames: (chunkInfo) => {
|
||||
// // console.log(chunkInfo)
|
||||
// if (chunkInfo.name.includes("shared")) {
|
||||
// console.log(chunkInfo.name);
|
||||
// }
|
||||
// return "worker.js";
|
||||
// },
|
||||
entryFileNames: "[name].js",
|
||||
}
|
||||
}
|
||||
},
|
||||
build: {
|
||||
// target: 'es2015',
|
||||
outDir: '/tmp/zhuang/dap-web-dist/',
|
||||
emptyOutDir: true,
|
||||
cssMinify: 'lightningcss',
|
||||
|
||||
rollupOptions: {
|
||||
output: {
|
||||
inlineDynamicImports: true,
|
||||
minifyInternalExports: true,
|
||||
assetFileNames: (assetInfo) => {
|
||||
if (!assetInfo || !assetInfo.name) {
|
||||
return 'default-filename.ext';
|
||||
|
@ -44,15 +74,25 @@ export default defineConfig({
|
|||
extType = "css";
|
||||
return "style.css"
|
||||
}
|
||||
// return `[name]-[hash][extname]`;
|
||||
return `[name][extname]`;
|
||||
console.log(assetInfo)
|
||||
return `[name]-[hash][extname]`;
|
||||
},
|
||||
// chunkFileNames: "[name]-[hash].js",
|
||||
chunkFileNames: "[name].js",
|
||||
// chunkFileNames: "[name][hash].js",
|
||||
chunkFileNames(chunkInfo) {
|
||||
// Check if this chunk is your SharedWorker
|
||||
// console.log(chunkInfo)
|
||||
|
||||
// For other chunks, use the default naming scheme
|
||||
return 'assets/[name]-[hash].js';
|
||||
},
|
||||
// entryFileNames: "[name]-[hash].js",
|
||||
entryFileNames: (chunkInfo) => {
|
||||
// console.log(chunkInfo)
|
||||
return "script.js"
|
||||
if (chunkInfo.name.includes("shared")) {
|
||||
console.log(chunkInfo.name);
|
||||
}
|
||||
return "script.js";
|
||||
},
|
||||
|
||||
sourcemapFileNames: "map-[name].js",
|
||||
|
@ -61,16 +101,17 @@ export default defineConfig({
|
|||
// console.log(chunkInfo)
|
||||
// return `${chunkInfo.name}.js`
|
||||
// },
|
||||
manualChunks(id) {
|
||||
/* if (id.match('.*!/src/.*shared[a-zA-Z0-9-_]*[.](ts|js).*')) {
|
||||
// Prevent bundling node_modules into common chunks
|
||||
return 'bundle-shared';
|
||||
}
|
||||
else */{
|
||||
// Prevent bundling node_modules into common chunks
|
||||
return 'script'
|
||||
}
|
||||
},
|
||||
// manualChunks(id) {
|
||||
// /* if (id.match('.*!/src/.*shared[a-zA-Z0-9-_]*[.](ts|js).*')) {
|
||||
// // Prevent bundling node_modules into common chunks
|
||||
// return 'bundle-shared';
|
||||
// }
|
||||
// else */{
|
||||
// // Prevent bundling node_modules into common chunks
|
||||
// return 'script'
|
||||
// }
|
||||
// },
|
||||
manualChunks: undefined,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue