diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..304ac25
--- /dev/null
+++ b/.env.development
@@ -0,0 +1 @@
+VITE_APP_MODE=dev
\ No newline at end of file
diff --git a/.env.production b/.env.production
new file mode 100644
index 0000000..1714480
--- /dev/null
+++ b/.env.production
@@ -0,0 +1 @@
+VITE_APP_MODE=prod
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index a3842d4..a8fbe0c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,3 +29,8 @@ coverage
*.tsbuildinfo
package-lock.json
+components.d.ts
+auto-imports.d.ts
+
+# Personal
+**/_priv_*
\ No newline at end of file
diff --git a/auto-imports.d.ts b/auto-imports.d.ts
deleted file mode 100644
index 1d89ee8..0000000
--- a/auto-imports.d.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-/* eslint-disable */
-/* prettier-ignore */
-// @ts-nocheck
-// noinspection JSUnusedGlobalSymbols
-// Generated by unplugin-auto-import
-export {}
-declare global {
-
-}
diff --git a/components.d.ts b/components.d.ts
deleted file mode 100644
index 1a27d34..0000000
--- a/components.d.ts
+++ /dev/null
@@ -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']
- }
-}
diff --git a/env.d.ts b/env.d.ts
index 11f02fe..0fdf4a4 100644
--- a/env.d.ts
+++ b/env.d.ts
@@ -1 +1,3 @@
///
+declare const __APP_VERSION__: string; // defined in vite.config.ts, imported from package.json.version
+declare const __BUILD_TIME__: string;
\ No newline at end of file
diff --git a/index.html b/index.html
index a888544..ddb979e 100644
--- a/index.html
+++ b/index.html
@@ -2,7 +2,7 @@
-
+
Vite App
diff --git a/package.json b/package.json
index 47c463e..a481d8e 100644
--- a/package.json
+++ b/package.json
@@ -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"
}
diff --git a/src/App.vue b/src/App.vue
index 1189d23..5044a79 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,64 +1,63 @@
-
- This is body
- end
-
- test
-
-
-
diff --git a/src/api/apiWifi.ts b/src/api/apiWifi.ts
index 9ef8e61..a07ebf5 100644
--- a/src/api/apiWifi.ts
+++ b/src/api/apiWifi.ts
@@ -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;
}
diff --git a/src/api/index.ts b/src/api/index.ts
index 582c7df..faf7ad1 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -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));
}
\ No newline at end of file
diff --git a/src/assets/icon/favicon.svg b/src/assets/icon/favicon.svg
new file mode 100755
index 0000000..0144e92
--- /dev/null
+++ b/src/assets/icon/favicon.svg
@@ -0,0 +1,45 @@
+
+
diff --git a/src/assets/icon/view-hide.svg b/src/assets/icon/view-hide.svg
new file mode 100644
index 0000000..e4a56c7
--- /dev/null
+++ b/src/assets/icon/view-hide.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icon/view.svg b/src/assets/icon/view.svg
new file mode 100644
index 0000000..5c6e2da
--- /dev/null
+++ b/src/assets/icon/view.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icon/wifi-exclamation.svg b/src/assets/icon/wifi-exclamation.svg
new file mode 100644
index 0000000..3f55d61
--- /dev/null
+++ b/src/assets/icon/wifi-exclamation.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/src/assets/navigation.css b/src/assets/navigation.css
new file mode 100644
index 0000000..6d4563b
--- /dev/null
+++ b/src/assets/navigation.css
@@ -0,0 +1,3 @@
+.todo-menu-item {
+ @apply opacity-30
+}
\ No newline at end of file
diff --git a/src/assets/page.css b/src/assets/page.css
new file mode 100644
index 0000000..68acde3
--- /dev/null
+++ b/src/assets/page.css
@@ -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;
+}
diff --git a/src/assets/toggle_skewed.css b/src/assets/toggle_skewed.css
new file mode 100644
index 0000000..016683e
--- /dev/null
+++ b/src/assets/toggle_skewed.css
@@ -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);
+}
diff --git a/src/components/passwordViewer.vue b/src/components/passwordViewer.vue
new file mode 100644
index 0000000..440c877
--- /dev/null
+++ b/src/components/passwordViewer.vue
@@ -0,0 +1,25 @@
+
+
+
{{ password }}
+
••••••••
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/radioSkewed.vue b/src/components/radioSkewed.vue
new file mode 100644
index 0000000..fbe23a9
--- /dev/null
+++ b/src/components/radioSkewed.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
diff --git a/src/composables/broadcastChannelDef.ts b/src/composables/broadcastChannelDef.ts
index 2f90e6e..48cc7af 100644
--- a/src/composables/broadcastChannelDef.ts
+++ b/src/composables/broadcastChannelDef.ts
@@ -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));
-}
diff --git a/src/composables/buildMode.ts b/src/composables/buildMode.ts
new file mode 100644
index 0000000..840a858
--- /dev/null
+++ b/src/composables/buildMode.ts
@@ -0,0 +1,3 @@
+export function isDevMode() {
+ return import.meta.env.VITE_APP_MODE === 'dev';
+}
\ No newline at end of file
diff --git a/src/composables/importFavicon.ts b/src/composables/importFavicon.ts
new file mode 100644
index 0000000..d59ab4e
--- /dev/null
+++ b/src/composables/importFavicon.ts
@@ -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}`;
+ }
+}
+
+
diff --git a/src/composables/logConsoleMsg.ts b/src/composables/logConsoleMsg.ts
new file mode 100644
index 0000000..fd61683
--- /dev/null
+++ b/src/composables/logConsoleMsg.ts
@@ -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" +
+ "大概是一起去整点赛博薯条吧。-来自法国鸽子");
+}
diff --git a/src/composables/notification.ts b/src/composables/notification.ts
new file mode 100644
index 0000000..0ffe27a
--- /dev/null
+++ b/src/composables/notification.ts
@@ -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,
+ })
+}
diff --git a/src/composables/websocket/websocketService.ts b/src/composables/websocket/websocketService.ts
index c81de5e..910f7bc 100644
--- a/src/composables/websocket/websocketService.ts
+++ b/src/composables/websocket/websocketService.ts
@@ -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) {
+
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);
}
diff --git a/src/composables/websocket/websocketWrapper.ts b/src/composables/websocket/websocketWrapper.ts
index c7613b0..9f1a771 100644
--- a/src/composables/websocket/websocketWrapper.ts
+++ b/src/composables/websocket/websocketWrapper.ts
@@ -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)
}
}
diff --git a/src/composables/websocket/ws.sharedworker.ts b/src/composables/websocket/ws.sharedworker.ts
index c35d43f..b303a9e 100644
--- a/src/composables/websocket/ws.sharedworker.ts
+++ b/src/composables/websocket/ws.sharedworker.ts
@@ -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) {
- 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;
diff --git a/src/i18n.ts b/src/i18n.ts
index 2fd3a6a..45a36b8 100644
--- a/src/i18n.ts
+++ b/src/i18n.ts
@@ -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,
diff --git a/src/layouts/textLayout.vue b/src/layouts/textLayout.vue
new file mode 100644
index 0000000..8ebd00b
--- /dev/null
+++ b/src/layouts/textLayout.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/locales/index.ts b/src/locales/index.ts
new file mode 100644
index 0000000..0d4b347
--- /dev/null
+++ b/src/locales/index.ts
@@ -0,0 +1,14 @@
+import i18n from '@/i18n';
+import zh from '@/locales/zh';
+
+type NestedKeyOf = {
+ [Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object
+ ? `${Key}` | `${Key}.${NestedKeyOf}`
+ : `${Key}`
+}[keyof ObjectType & (string | number)];
+
+type TranslationKeys = NestedKeyOf;
+
+export function translate(key: K | string): string {
+ return i18n.global.t(key.toLowerCase());
+}
diff --git a/src/locales/zh.ts b/src/locales/zh.ts
index 80defde..17be1d1 100644
--- a/src/locales/zh.ts
+++ b/src/locales/zh.ts
@@ -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: "关闭",
},
}
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index e91a666..5841e0b 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -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')
diff --git a/src/router/index.ts b/src/router/index.ts
index f24dc5b..207b1f3 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -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
diff --git a/src/router/msgRouter.ts b/src/router/msgRouter.ts
new file mode 100644
index 0000000..d9edb39
--- /dev/null
+++ b/src/router/msgRouter.ts
@@ -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();
+
+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);
+ }
+}
diff --git a/src/stores/websocket.ts b/src/stores/websocket.ts
index 4a2f39f..ca669a6 100644
--- a/src/stores/websocket.ts
+++ b/src/stores/websocket.ts
@@ -1,5 +1,6 @@
import {defineStore} from "pinia";
-import {ControlEvent} from "@/composables/broadcastChannelDef";
+
+import {ControlEvent} from "@/api";
export const useWsStore = defineStore('websocket', {
state: () => {
diff --git a/src/utils/emitter.ts b/src/utils/emitter.ts
new file mode 100644
index 0000000..4b06ecd
--- /dev/null
+++ b/src/utils/emitter.ts
@@ -0,0 +1,5 @@
+import mitt from 'mitt';
+
+const emitter = mitt();
+
+export default emitter;
diff --git a/src/views/About.vue b/src/views/About.vue
index 7d99a3d..9e5c67e 100644
--- a/src/views/About.vue
+++ b/src/views/About.vue
@@ -1,12 +1,50 @@
+
+
- About Page
-
- About Page
+
+
+
关于上位机
+
+
+
+ {{version}}
+ {{compileTime}}
+ MIT
+
+
+
+ MIT
+ Apache 2.0
+ MIT
+ MIT
+ MIT
+ MIT
+ MIT
+ MIT
+ MIT
+ MPL-2.0 license
+
+
+
+ https://github.com/kerms
+ kerms@niazo.org
+ UID38669852
+ 642246000
+ 欢迎大家来打扰啊~
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/Feedback.vue b/src/views/Feedback.vue
new file mode 100644
index 0000000..e3757f1
--- /dev/null
+++ b/src/views/Feedback.vue
@@ -0,0 +1,20 @@
+
+
+
+
+ 反馈
+
+
+
+ 642246000
+ kerms@niazo.org
+
+
+
+
+
+
+
+
diff --git a/src/views/Home.vue b/src/views/Home.vue
index 50aff6c..50d46f2 100644
--- a/src/views/Home.vue
+++ b/src/views/Home.vue
@@ -1,20 +1,12 @@
- Home Page
-
- Home Page
-
+
diff --git a/src/views/Uart.vue b/src/views/Uart.vue
new file mode 100644
index 0000000..51dbd02
--- /dev/null
+++ b/src/views/Uart.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
尽请期待
+
+
diff --git a/src/views/Wifi.vue b/src/views/Wifi.vue
index eda250f..9c48909 100644
--- a/src/views/Wifi.vue
+++ b/src/views/Wifi.vue
@@ -1,7 +1,13 @@
-
-
Wifi View
-
+
+
+ Wi-Fi 配置
+
+
+
+
+
连接Wi-Fi
+
-
-
- 连接
+ 连接
+
+
+
+
+
+
+
+ 信号强度
+
+
+
+ {{ wifiStaApInfo.rssi }}
+
+
+
+
+
+ SSID
+
+
+ {{ wifiStaApInfo.ssid }}
+
+
+
+
+
+
+
+
+
+
+
+
+ IP
+
+
+ {{ wifiStaApInfo.ip }}
+
+
+
+
+ MAC
+
+
+ {{ wifiStaApInfo.mac }}
+
+
+
+
+ 网关
+
+
+ {{ wifiStaApInfo.gateway }}
+
+
+
+
+
+ 掩码
+
+
+ {{ wifiStaApInfo.netmask }}
+
+
+
+
+
+
+
+
+
+ SSID
+
+
+ {{ wifiApInfo.ssid }}
+
+
+
+
+
+
+
+
+
+
+
+
+ IP
+
+
+ {{ wifiApInfo.ip }}
+
+
+
+
+
+ MAC
+
+
+ {{ wifiApInfo.mac }}
+
+
+
+
+
+ 网关
+
+
+ {{ wifiApInfo.gateway }}
+
+
+
+
+
+ 掩码
+
+
+ {{ wifiApInfo.netmask }}
+
+
+
+
- 扫描
- 连接
+