85 changed files with 4908 additions and 4900 deletions
@ -1,32 +1,32 @@ |
|||||
{ |
{ |
||||
"tabWidth": 2, |
"tabWidth": 2, |
||||
"useTabs": false, |
"useTabs": false, |
||||
"semi": false, |
"semi": false, |
||||
"singleQuote": false, |
"singleQuote": false, |
||||
"trailingComma": "all", |
"trailingComma": "all", |
||||
"bracketSpacing": true, |
"bracketSpacing": true, |
||||
"arrowParens": "avoid", |
"arrowParens": "avoid", |
||||
"printWidth": 140, |
"printWidth": 140, |
||||
"htmlWhitespaceSensitivity": "ignore", |
"htmlWhitespaceSensitivity": "ignore", |
||||
"proseWrap": "preserve", |
"proseWrap": "preserve", |
||||
"endOfLine": "auto", |
"endOfLine": "auto", |
||||
"vueIndentScriptAndStyle": true, |
"vueIndentScriptAndStyle": true, |
||||
"embeddedLanguageFormatting": "auto", |
"embeddedLanguageFormatting": "auto", |
||||
"jsxSingleQuote": false, |
"jsxSingleQuote": false, |
||||
"jsxBracketSameLine": false, |
"jsxBracketSameLine": false, |
||||
"quoteProps": "as-needed", |
"quoteProps": "as-needed", |
||||
"overrides": [ |
"overrides": [ |
||||
{ |
{ |
||||
"files": "*.json", |
"files": "*.json", |
||||
"options": { |
"options": { |
||||
"printWidth": 80 |
"printWidth": 80 |
||||
} |
} |
||||
}, |
}, |
||||
{ |
{ |
||||
"files": ["*.vue", "*.tsx"], |
"files": ["*.vue", "*.tsx"], |
||||
"options": { |
"options": { |
||||
"singleAttributePerLine": false |
"singleAttributePerLine": false |
||||
} |
} |
||||
} |
} |
||||
] |
] |
||||
} |
} |
||||
|
@ -1,3 +1,3 @@ |
|||||
{ |
{ |
||||
"recommendations": ["dbaeumer.vscode-eslint", "lokalise.i18n-ally"] |
"recommendations": ["dbaeumer.vscode-eslint", "lokalise.i18n-ally"] |
||||
} |
} |
||||
|
@ -1,39 +1,39 @@ |
|||||
{ |
{ |
||||
"version": "0.2.0", |
"version": "0.2.0", |
||||
"configurations": [ |
"configurations": [ |
||||
{ |
{ |
||||
"name": "Debug Main Process", |
"name": "Debug Main Process", |
||||
"type": "node", |
"type": "node", |
||||
"request": "launch", |
"request": "launch", |
||||
"cwd": "${workspaceRoot}", |
"cwd": "${workspaceRoot}", |
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite", |
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite", |
||||
"windows": { |
"windows": { |
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" |
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron-vite.cmd" |
||||
}, |
}, |
||||
"runtimeArgs": ["--sourcemap"], |
"runtimeArgs": ["--sourcemap"], |
||||
"env": { |
"env": { |
||||
"REMOTE_DEBUGGING_PORT": "9222" |
"REMOTE_DEBUGGING_PORT": "9222" |
||||
} |
} |
||||
}, |
}, |
||||
{ |
{ |
||||
"name": "Debug Renderer Process", |
"name": "Debug Renderer Process", |
||||
"port": 9222, |
"port": 9222, |
||||
"request": "attach", |
"request": "attach", |
||||
"type": "chrome", |
"type": "chrome", |
||||
"webRoot": "${workspaceFolder}/src/renderer", |
"webRoot": "${workspaceFolder}/src/renderer", |
||||
"timeout": 60000, |
"timeout": 60000, |
||||
"presentation": { |
"presentation": { |
||||
"hidden": true |
"hidden": true |
||||
} |
} |
||||
} |
} |
||||
], |
], |
||||
"compounds": [ |
"compounds": [ |
||||
{ |
{ |
||||
"name": "Debug All", |
"name": "Debug All", |
||||
"configurations": ["Debug Main Process", "Debug Renderer Process"], |
"configurations": ["Debug Main Process", "Debug Renderer Process"], |
||||
"presentation": { |
"presentation": { |
||||
"order": 1 |
"order": 1 |
||||
} |
} |
||||
} |
} |
||||
] |
] |
||||
} |
} |
||||
|
@ -1,18 +1,18 @@ |
|||||
{ |
{ |
||||
"[typescript]": { |
"[typescript]": { |
||||
"editor.defaultFormatter": "esbenp.prettier-vscode" |
"editor.defaultFormatter": "esbenp.prettier-vscode" |
||||
}, |
}, |
||||
"[javascript]": { |
"[javascript]": { |
||||
"editor.defaultFormatter": "esbenp.prettier-vscode" |
"editor.defaultFormatter": "esbenp.prettier-vscode" |
||||
}, |
}, |
||||
"[json]": { |
"[json]": { |
||||
"editor.defaultFormatter": "esbenp.prettier-vscode" |
"editor.defaultFormatter": "esbenp.prettier-vscode" |
||||
}, |
}, |
||||
"i18n-ally.localesPaths": ["packages/locales/languages"], |
"i18n-ally.localesPaths": ["packages/locales/languages"], |
||||
"i18n-ally.sourceLanguage": "zh", |
"i18n-ally.sourceLanguage": "zh", |
||||
"i18n-ally.displayLanguage": "zh", |
"i18n-ally.displayLanguage": "zh", |
||||
"i18n-ally.keystyle": "nested", |
"i18n-ally.keystyle": "nested", |
||||
"i18n-ally.extract.autoDetect": true, |
"i18n-ally.extract.autoDetect": true, |
||||
"i18n-ally.enabledFrameworks": ["vue"], |
"i18n-ally.enabledFrameworks": ["vue"], |
||||
"i18n-ally.enabledParsers": ["json"] |
"i18n-ally.enabledParsers": ["json"] |
||||
} |
} |
||||
|
@ -1,45 +1,45 @@ |
|||||
appId: com.zephyr.app |
appId: com.zephyr.app |
||||
productName: zephyr |
productName: zephyr |
||||
directories: |
directories: |
||||
buildResources: build |
buildResources: build |
||||
files: |
files: |
||||
- "!**/.vscode/*" |
- "!**/.vscode/*" |
||||
- "!src/*" |
- "!src/*" |
||||
- "!electron.vite.config.{js,ts,mjs,cjs}" |
- "!electron.vite.config.{js,ts,mjs,cjs}" |
||||
- "!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}" |
- "!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}" |
||||
- "!{.env,.env.*,.npmrc,pnpm-lock.yaml}" |
- "!{.env,.env.*,.npmrc,pnpm-lock.yaml}" |
||||
- "!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}" |
- "!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}" |
||||
asarUnpack: |
asarUnpack: |
||||
- resources/** |
- resources/** |
||||
win: |
win: |
||||
executableName: zephyr |
executableName: zephyr |
||||
nsis: |
nsis: |
||||
artifactName: ${name}-${version}-setup.${ext} |
artifactName: ${name}-${version}-setup.${ext} |
||||
shortcutName: ${productName} |
shortcutName: ${productName} |
||||
uninstallDisplayName: ${productName} |
uninstallDisplayName: ${productName} |
||||
createDesktopShortcut: always |
createDesktopShortcut: always |
||||
mac: |
mac: |
||||
entitlementsInherit: build/entitlements.mac.plist |
entitlementsInherit: build/entitlements.mac.plist |
||||
extendInfo: |
extendInfo: |
||||
- NSCameraUsageDescription: Application requests access to the device's camera. |
- NSCameraUsageDescription: Application requests access to the device's camera. |
||||
- NSMicrophoneUsageDescription: Application requests access to the device's microphone. |
- NSMicrophoneUsageDescription: Application requests access to the device's microphone. |
||||
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. |
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. |
||||
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. |
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. |
||||
notarize: false |
notarize: false |
||||
dmg: |
dmg: |
||||
artifactName: ${name}-${version}.${ext} |
artifactName: ${name}-${version}.${ext} |
||||
linux: |
linux: |
||||
target: |
target: |
||||
- AppImage |
- AppImage |
||||
- snap |
- snap |
||||
- deb |
- deb |
||||
maintainer: electronjs.org |
maintainer: electronjs.org |
||||
category: Utility |
category: Utility |
||||
appImage: |
appImage: |
||||
artifactName: ${name}-${version}.${ext} |
artifactName: ${name}-${version}.${ext} |
||||
npmRebuild: false |
npmRebuild: false |
||||
publish: |
publish: |
||||
provider: generic |
provider: generic |
||||
url: https://example.com/auto-updates |
url: https://example.com/auto-updates |
||||
electronDownload: |
electronDownload: |
||||
mirror: https://npmmirror.com/mirrors/electron/ |
mirror: https://npmmirror.com/mirrors/electron/ |
||||
|
@ -1,78 +1,78 @@ |
|||||
{ |
{ |
||||
"name": "zephyr", |
"name": "zephyr", |
||||
"type": "module", |
"type": "module", |
||||
"private": true, |
"private": true, |
||||
"version": "0.0.1", |
"version": "0.0.1", |
||||
"description": "An Electron application with Vue and TypeScript", |
"description": "An Electron application with Vue and TypeScript", |
||||
"main": "./out/main/index.js", |
"main": "./out/main/index.js", |
||||
"author": "example.com", |
"author": "example.com", |
||||
"homepage": "https://electron-vite.org", |
"homepage": "https://electron-vite.org", |
||||
"scripts": { |
"scripts": { |
||||
"runInstall": "node node_modules/electron/install.js", |
"runInstall": "node node_modules/electron/install.js", |
||||
"format": "prettier --write .", |
"format": "prettier --write .", |
||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix", |
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix", |
||||
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", |
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", |
||||
"typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false", |
"typecheck:web": "vue-tsc --noEmit -p tsconfig.web.json --composite false", |
||||
"typecheck": "npm run typecheck:node && npm run typecheck:web", |
"typecheck": "npm run typecheck:node && npm run typecheck:web", |
||||
"start": "electron-vite preview", |
"start": "electron-vite preview", |
||||
"dev": "chcp 65001 && set DEBUG=app:*&& electron-vite dev", |
"dev": "chcp 65001 && set DEBUG=app:*&& electron-vite dev", |
||||
"dev:watch": "chcp 65001 & set DEBUG=app:*& electron-vite dev --watch", |
"dev:watch": "chcp 65001 & set DEBUG=app:*& electron-vite dev --watch", |
||||
"build": "npm run typecheck && electron-vite build", |
"build": "npm run typecheck && electron-vite build", |
||||
"postinstall": "electron-builder install-app-deps", |
"postinstall": "electron-builder install-app-deps", |
||||
"build:unpack": "npm run build && electron-builder --dir", |
"build:unpack": "npm run build && electron-builder --dir", |
||||
"build:win": "npm run build && electron-builder --win", |
"build:win": "npm run build && electron-builder --win", |
||||
"build:mac": "npm run build && electron-builder --mac", |
"build:mac": "npm run build && electron-builder --mac", |
||||
"build:linux": "npm run build && electron-builder --linux" |
"build:linux": "npm run build && electron-builder --linux" |
||||
}, |
}, |
||||
"dependencies": { |
"dependencies": { |
||||
"@electron-toolkit/preload": "^3.0.1", |
"@electron-toolkit/preload": "^3.0.1", |
||||
"@electron-toolkit/utils": "^3.0.0", |
"@electron-toolkit/utils": "^3.0.0", |
||||
"electron-updater": "^6.3.9", |
"electron-updater": "^6.3.9", |
||||
"inversify": "^6.2.2", |
"inversify": "^6.2.2", |
||||
"lowdb": "^7.0.1", |
"lowdb": "^7.0.1", |
||||
"reflect-metadata": "^0.2.2" |
"reflect-metadata": "^0.2.2" |
||||
}, |
}, |
||||
"devDependencies": { |
"devDependencies": { |
||||
"@electron-toolkit/eslint-config": "^1.0.2", |
"@electron-toolkit/eslint-config": "^1.0.2", |
||||
"@electron-toolkit/eslint-config-ts": "^2.0.0", |
"@electron-toolkit/eslint-config-ts": "^2.0.0", |
||||
"@electron-toolkit/tsconfig": "^1.0.1", |
"@electron-toolkit/tsconfig": "^1.0.1", |
||||
"@intlify/unplugin-vue-i18n": "^6.0.3", |
"@intlify/unplugin-vue-i18n": "^6.0.3", |
||||
"@rushstack/eslint-patch": "^1.10.5", |
"@rushstack/eslint-patch": "^1.10.5", |
||||
"@types/debug": "^4.1.12", |
"@types/debug": "^4.1.12", |
||||
"@types/node": "^20.17.19", |
"@types/node": "^20.17.19", |
||||
"@unocss/preset-rem-to-px": "^0.64.1", |
"@unocss/preset-rem-to-px": "^0.64.1", |
||||
"@unocss/reset": "^0.64.1", |
"@unocss/reset": "^0.64.1", |
||||
"@vitejs/plugin-vue": "^5.2.1", |
"@vitejs/plugin-vue": "^5.2.1", |
||||
"@vitejs/plugin-vue-jsx": "^4.1.1", |
"@vitejs/plugin-vue-jsx": "^4.1.1", |
||||
"@vue/eslint-config-prettier": "^9.0.0", |
"@vue/eslint-config-prettier": "^9.0.0", |
||||
"@vue/eslint-config-typescript": "^13.0.0", |
"@vue/eslint-config-typescript": "^13.0.0", |
||||
"@vueuse/core": "^12.7.0", |
"@vueuse/core": "^12.7.0", |
||||
"debug": "^4.4.0", |
"debug": "^4.4.0", |
||||
"electron": "^31.7.7", |
"electron": "^31.7.7", |
||||
"electron-builder": "^24.13.3", |
"electron-builder": "^24.13.3", |
||||
"electron-vite": "^2.3.0", |
"electron-vite": "^2.3.0", |
||||
"eslint": "^8.57.1", |
"eslint": "^8.57.1", |
||||
"eslint-plugin-vue": "^9.32.0", |
"eslint-plugin-vue": "^9.32.0", |
||||
"extract-zip": "^2.0.1", |
"extract-zip": "^2.0.1", |
||||
"locales": "workspace:*", |
"locales": "workspace:*", |
||||
"lodash-es": "^4.17.21", |
"lodash-es": "^4.17.21", |
||||
"monaco-editor": "^0.52.2", |
"monaco-editor": "^0.52.2", |
||||
"prettier": "^3.5.1", |
"prettier": "^3.5.1", |
||||
"rotating-file-stream": "^3.2.6", |
"rotating-file-stream": "^3.2.6", |
||||
"sass": "^1.85.0", |
"sass": "^1.85.0", |
||||
"simplebar-vue": "^2.4.0", |
"simplebar-vue": "^2.4.0", |
||||
"typescript": "^5.7.3", |
"typescript": "^5.7.3", |
||||
"unocss": "^0.64.1", |
"unocss": "^0.64.1", |
||||
"unplugin-auto-import": "^19.1.0", |
"unplugin-auto-import": "^19.1.0", |
||||
"unplugin-vue-components": "^28.4.0", |
"unplugin-vue-components": "^28.4.0", |
||||
"unplugin-vue-macros": "^2.14.2", |
"unplugin-vue-macros": "^2.14.2", |
||||
"unplugin-vue-router": "^0.11.2", |
"unplugin-vue-router": "^0.11.2", |
||||
"vite": "^5.4.14", |
"vite": "^5.4.14", |
||||
"vite-plugin-monaco-editor": "^1.1.0", |
"vite-plugin-monaco-editor": "^1.1.0", |
||||
"vite-plugin-vue-layouts": "^0.11.0", |
"vite-plugin-vue-layouts": "^0.11.0", |
||||
"vue": "^3.5.13", |
"vue": "^3.5.13", |
||||
"vue-i18n": "^11.1.1", |
"vue-i18n": "^11.1.1", |
||||
"vue-router": "^4.5.0", |
"vue-router": "^4.5.0", |
||||
"vue-tsc": "^2.1.10" |
"vue-tsc": "^2.1.10" |
||||
} |
} |
||||
} |
} |
||||
|
@ -1,2 +1,2 @@ |
|||||
packages: |
packages: |
||||
- "packages/*" |
- "packages/*" |
||||
|
@ -1,11 +1,12 @@ |
|||||
<!DOCTYPE html> |
<!doctype html> |
||||
<html lang="en"> |
<html lang="en"> |
||||
<head> |
<head> |
||||
<meta charset="UTF-8"> |
<meta charset="UTF-8" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<title>Document</title> |
<title>Document</title> |
||||
</head> |
</head> |
||||
<body> |
<body> |
||||
前往 <a href="https://baidu.com" target="_blank">百度</a> |
前往 |
||||
</body> |
<a href="https://baidu.com" target="_blank">百度</a> |
||||
|
</body> |
||||
</html> |
</html> |
||||
|
@ -1,54 +1,54 @@ |
|||||
import { _Base } from "../../lib/_Base" |
import { _Base } from "../../lib/_Base" |
||||
|
|
||||
export class Tabs extends _Base { |
export class Tabs extends _Base { |
||||
constructor() { |
constructor() { |
||||
super() |
super() |
||||
} |
} |
||||
|
|
||||
private isListen: boolean = false |
private isListen: boolean = false |
||||
|
|
||||
private execUpdate = (...args) => { |
private execUpdate = (...args) => { |
||||
this.#fnList.forEach(v => v(...args)) |
this.#fnList.forEach(v => v(...args)) |
||||
} |
} |
||||
|
|
||||
#fnList: ((...args) => void)[] = [] |
#fnList: ((...args) => void)[] = [] |
||||
listenUpdate(cb: (...args) => void) { |
listenUpdate(cb: (...args) => void) { |
||||
if (!this.isListen) { |
if (!this.isListen) { |
||||
api.on("main:TabsCommand.update", this.execUpdate) |
api.on("main:TabsCommand.update", this.execUpdate) |
||||
this.isListen = true |
this.isListen = true |
||||
} |
} |
||||
this.#fnList.push(cb) |
this.#fnList.push(cb) |
||||
} |
} |
||||
|
|
||||
unListenUpdate(fn: (...args) => void) { |
unListenUpdate(fn: (...args) => void) { |
||||
this.#fnList = this.#fnList.filter(v => { |
this.#fnList = this.#fnList.filter(v => { |
||||
return v !== fn |
return v !== fn |
||||
}) |
}) |
||||
if (!this.#fnList.length) { |
if (!this.#fnList.length) { |
||||
api.off("main:TabsCommand.update", this.execUpdate) |
api.off("main:TabsCommand.update", this.execUpdate) |
||||
this.isListen = false |
this.isListen = false |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
bindPosition(data) { |
bindPosition(data) { |
||||
api.call("TabsCommand.bindElement", data) |
api.call("TabsCommand.bindElement", data) |
||||
} |
} |
||||
|
|
||||
closeAll() { |
closeAll() { |
||||
api.call("TabsCommand.closeAll") |
api.call("TabsCommand.closeAll") |
||||
} |
} |
||||
|
|
||||
sync() { |
sync() { |
||||
api.call("TabsCommand.sync") |
api.call("TabsCommand.sync") |
||||
} |
} |
||||
|
|
||||
unListenerAll() { |
unListenerAll() { |
||||
this.#fnList = [] |
this.#fnList = [] |
||||
api.offAll("main:TabsCommand.update") |
api.offAll("main:TabsCommand.update") |
||||
} |
} |
||||
|
|
||||
async getAllTabs() { |
async getAllTabs() { |
||||
const res = await api.call("TabsCommand.getAllTabs") |
const res = await api.call("TabsCommand.getAllTabs") |
||||
return res |
return res |
||||
} |
} |
||||
} |
} |
||||
|
@ -1,12 +1,12 @@ |
|||||
export abstract class _Base { |
export abstract class _Base { |
||||
static instance |
static instance |
||||
|
|
||||
static getInstance<T>(): T { |
static getInstance<T>(): T { |
||||
if (!this.instance) { |
if (!this.instance) { |
||||
// 如果实例不存在,则创建一个新的实例
|
// 如果实例不存在,则创建一个新的实例
|
||||
// @ts-ignore ...
|
// @ts-ignore ...
|
||||
this.instance = new this() |
this.instance = new this() |
||||
} |
|
||||
return this.instance |
|
||||
} |
} |
||||
|
return this.instance |
||||
|
} |
||||
} |
} |
||||
|
@ -1,29 +1,29 @@ |
|||||
import { IApiClient } from "./abstract" |
import { IApiClient } from "./abstract" |
||||
|
|
||||
export class BrowserApiClient implements IApiClient { |
export class BrowserApiClient implements IApiClient { |
||||
call<T = any>(command: string, ...args: any[]): Promise<T> { |
call<T = any>(command: string, ...args: any[]): Promise<T> { |
||||
// 浏览器特定实现,可能使用 fetch 或其他方式
|
// 浏览器特定实现,可能使用 fetch 或其他方式
|
||||
const [service, method] = command.split(".") |
const [service, method] = command.split(".") |
||||
return fetch(`/api/${service}/${method}`, { |
return fetch(`/api/${service}/${method}`, { |
||||
method: "POST", |
method: "POST", |
||||
body: JSON.stringify(args), |
body: JSON.stringify(args), |
||||
headers: { "Content-Type": "application/json" }, |
headers: { "Content-Type": "application/json" }, |
||||
}).then(res => res.json()) |
}).then(res => res.json()) |
||||
} |
} |
||||
|
|
||||
// 实现其他方法...
|
// 实现其他方法...
|
||||
on<K extends string>(channel: K, callback: (...args: any[]) => void): void { |
on<K extends string>(channel: K, callback: (...args: any[]) => void): void { |
||||
// 浏览器中可能使用 WebSocket 或其他方式
|
// 浏览器中可能使用 WebSocket 或其他方式
|
||||
console.log("不支持 on 方法", channel, callback) |
console.log("不支持 on 方法", channel, callback) |
||||
} |
} |
||||
|
|
||||
off<K extends string>(channel: K, callback: (...args: any[]) => void): void { |
off<K extends string>(channel: K, callback: (...args: any[]) => void): void { |
||||
// 相应的解绑实现
|
// 相应的解绑实现
|
||||
console.log("不支持 on 方法", channel, callback) |
console.log("不支持 on 方法", channel, callback) |
||||
} |
} |
||||
|
|
||||
offAll<K extends string>(channel: K): void { |
offAll<K extends string>(channel: K): void { |
||||
// 相应的全部解绑实现
|
// 相应的全部解绑实现
|
||||
console.log("不支持 on 方法", channel) |
console.log("不支持 on 方法", channel) |
||||
} |
} |
||||
} |
} |
||||
|
@ -1,20 +1,20 @@ |
|||||
import { IApiClient } from "./abstract" |
import { IApiClient } from "./abstract" |
||||
|
|
||||
export class ElectronApiClient implements IApiClient { |
export class ElectronApiClient implements IApiClient { |
||||
call<T = any>(command: string, ...args: any[]): Promise<T> { |
call<T = any>(command: string, ...args: any[]): Promise<T> { |
||||
// Electron 特定实现
|
// Electron 特定实现
|
||||
return window.api.call(command, ...args) |
return window.api.call(command, ...args) |
||||
} |
} |
||||
|
|
||||
on<K extends string>(channel: K, callback: (...args: any[]) => void): void { |
on<K extends string>(channel: K, callback: (...args: any[]) => void): void { |
||||
window.api.on(channel, callback) |
window.api.on(channel, callback) |
||||
} |
} |
||||
|
|
||||
off<K extends string>(channel: K, callback: (...args: any[]) => void): void { |
off<K extends string>(channel: K, callback: (...args: any[]) => void): void { |
||||
window.api.off(channel, callback) |
window.api.off(channel, callback) |
||||
} |
} |
||||
|
|
||||
offAll<K extends string>(channel: K): void { |
offAll<K extends string>(channel: K): void { |
||||
window.api.offAll(channel) |
window.api.offAll(channel) |
||||
} |
} |
||||
} |
} |
||||
|
@ -1,6 +1,6 @@ |
|||||
export function Layout(width, height) { |
export function Layout(width, height) { |
||||
// Tab布局位置
|
// Tab布局位置
|
||||
const NavbarHeight = 30 |
const NavbarHeight = 30 |
||||
const OffsetHeight = NavbarHeight + 100 |
const OffsetHeight = NavbarHeight + 100 |
||||
return { x: 0, y: OffsetHeight, width: width, height: height - OffsetHeight } |
return { x: 0, y: OffsetHeight, width: width, height: height - OffsetHeight } |
||||
} |
} |
||||
|
@ -1,28 +1,28 @@ |
|||||
<!doctype html> |
<!doctype html> |
||||
<html lang="en"> |
<html lang="en"> |
||||
<head> |
<head> |
||||
<meta charset="UTF-8" /> |
<meta charset="UTF-8" /> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
<title>关于我</title> |
<title>关于我</title> |
||||
<style> |
<style> |
||||
html, |
html, |
||||
body { |
body { |
||||
height: 100%; |
height: 100%; |
||||
width: 100%; |
width: 100%; |
||||
margin: 0; |
margin: 0; |
||||
padding: 0; |
padding: 0; |
||||
outline: none; |
outline: none; |
||||
border: 0; |
border: 0; |
||||
display: flex; |
display: flex; |
||||
justify-content: center; |
justify-content: center; |
||||
align-items: center; |
align-items: center; |
||||
} |
} |
||||
</style> |
</style> |
||||
</head> |
</head> |
||||
|
|
||||
<body> |
<body> |
||||
<article> |
<article> |
||||
<h1 id="demo" style="text-align: center">您好,亲爱的冒险者!</h1> |
<h1 id="demo" style="text-align: center">您好,亲爱的冒险者!</h1> |
||||
</article> |
</article> |
||||
</body> |
</body> |
||||
</html> |
</html> |
||||
|
File diff suppressed because it is too large
@ -1,26 +1,27 @@ |
|||||
<!doctype html> |
<!doctype html> |
||||
<html> |
<html> |
||||
|
<head> |
||||
<head> |
|
||||
<meta charset="UTF-8" /> |
<meta charset="UTF-8" /> |
||||
<title>Electron</title> |
<title>Electron</title> |
||||
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> |
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> |
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' api: 'unsafe-inline'; |
<meta |
||||
|
http-equiv="Content-Security-Policy" |
||||
|
content="default-src 'self' api: 'unsafe-inline'; |
||||
script-src 'self' api:; |
script-src 'self' api:; |
||||
style-src 'self' 'unsafe-inline'; |
style-src 'self' 'unsafe-inline'; |
||||
img-src 'self' data: *;" /> |
img-src 'self' data: *;" |
||||
</head> |
/> |
||||
|
</head> |
||||
|
|
||||
<body> |
<body> |
||||
<div id="app"></div> |
<div id="app"></div> |
||||
<noscript> |
<noscript> |
||||
<style> |
<style> |
||||
[data-simplebar] { |
[data-simplebar] { |
||||
overflow: auto; |
overflow: auto; |
||||
} |
} |
||||
</style> |
</style> |
||||
</noscript> |
</noscript> |
||||
<script type="module" src="/src/main.ts"></script> |
<script type="module" src="/src/main.ts"></script> |
||||
</body> |
</body> |
||||
|
|
||||
</html> |
</html> |
||||
|
@ -1,30 +1,30 @@ |
|||||
<script setup lang="ts"></script> |
<script setup lang="ts"></script> |
||||
|
|
||||
<template> |
<template> |
||||
<div h-full flex flex-col overflow-hidden> |
<div h-full flex flex-col overflow-hidden> |
||||
<NavBar></NavBar> |
<NavBar></NavBar> |
||||
<div flex-1 h-0 overflow-hidden flex flex-col> |
<div flex-1 h-0 overflow-hidden flex flex-col> |
||||
<router-view v-slot="{ Component, route }"> |
<router-view v-slot="{ Component, route }"> |
||||
<Transition name="slide-fade" mode="out-in"> |
<Transition name="slide-fade" mode="out-in"> |
||||
<component :is="Component" :key="route.fullPath" /> |
<component :is="Component" :key="route.fullPath" /> |
||||
</Transition> |
</Transition> |
||||
</router-view> |
</router-view> |
||||
</div> |
|
||||
</div> |
</div> |
||||
|
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
.slide-fade-enter-active { |
.slide-fade-enter-active { |
||||
transition: all 0.2s ease-out; |
transition: all 0.2s ease-out; |
||||
} |
} |
||||
|
|
||||
.slide-fade-leave-active { |
.slide-fade-leave-active { |
||||
transition: all 0.2s cubic-bezier(1, 0.5, 0.8, 1); |
transition: all 0.2s cubic-bezier(1, 0.5, 0.8, 1); |
||||
} |
} |
||||
|
|
||||
.slide-fade-enter-from, |
.slide-fade-enter-from, |
||||
.slide-fade-leave-to { |
.slide-fade-leave-to { |
||||
transform: translateX(20px); |
transform: translateX(20px); |
||||
opacity: 0; |
opacity: 0; |
||||
} |
} |
||||
</style> |
</style> |
||||
|
@ -1,246 +1,245 @@ |
|||||
interface ScrollStyle { |
interface ScrollStyle { |
||||
[key: string]: string |
[key: string]: string |
||||
} |
} |
||||
|
|
||||
class Scrollbot { |
class Scrollbot { |
||||
private orgPar!: HTMLElement |
private orgPar!: HTMLElement |
||||
private sbw: number = 5 |
private sbw: number = 5 |
||||
private scrollSpeed: number = 200 |
private scrollSpeed: number = 200 |
||||
private parContent!: string |
private parContent!: string |
||||
private newPar!: HTMLDivElement |
private newPar!: HTMLDivElement |
||||
// private sbContainer!: HTMLDivElement
|
// private sbContainer!: HTMLDivElement
|
||||
private scrollBarHolder!: HTMLDivElement |
private scrollBarHolder!: HTMLDivElement |
||||
private scrollBar!: HTMLDivElement |
private scrollBar!: HTMLDivElement |
||||
private inP!: HTMLDivElement |
private inP!: HTMLDivElement |
||||
private sbHeight: number = 0 |
private sbHeight: number = 0 |
||||
private mdown: boolean = false |
private mdown: boolean = false |
||||
private customHeight: boolean = false |
private customHeight: boolean = false |
||||
// private scrollElement!: HTMLElement
|
// private scrollElement!: HTMLElement
|
||||
private onScrollF?: () => void |
private onScrollF?: () => void |
||||
private sB: ScrollStyle = {} |
private sB: ScrollStyle = {} |
||||
private sBH: ScrollStyle = {} |
private sBH: ScrollStyle = {} |
||||
private posCorrection: number = 0 |
private posCorrection: number = 0 |
||||
private btmCorrection: number = 0 |
private btmCorrection: number = 0 |
||||
private relY: number = 0 |
private relY: number = 0 |
||||
private pC: number = 0 |
private pC: number = 0 |
||||
|
|
||||
getDom(selector: string | HTMLElement) { |
getDom(selector: string | HTMLElement) { |
||||
if (typeof selector === "string") { |
if (typeof selector === "string") { |
||||
return document.querySelector<HTMLElement>(selector) |
return document.querySelector<HTMLElement>(selector) |
||||
} |
|
||||
return selector |
|
||||
} |
} |
||||
|
return selector |
||||
|
} |
||||
|
|
||||
constructor(selector: string | HTMLElement, width?: number) { |
constructor(selector: string | HTMLElement, width?: number) { |
||||
const element = this.getDom(selector) |
const element = this.getDom(selector) |
||||
if (!element) throw new Error("Element not found") |
if (!element) throw new Error("Element not found") |
||||
this.orgPar = element |
this.orgPar = element |
||||
|
|
||||
const ieVersion = this.isIE() |
const ieVersion = this.isIE() |
||||
if (!ieVersion || (ieVersion && ieVersion < 9)) { |
if (!ieVersion || (ieVersion && ieVersion < 9)) { |
||||
this.init(width) |
this.init(width) |
||||
} |
|
||||
} |
} |
||||
|
} |
||||
private init(width?: number): void { |
|
||||
this.sbw = width ?? 5 |
private init(width?: number): void { |
||||
this.parContent = this.orgPar.innerHTML |
this.sbw = width ?? 5 |
||||
this.orgPar.innerHTML = "" |
this.parContent = this.orgPar.innerHTML |
||||
|
this.orgPar.innerHTML = "" |
||||
this.setupElements() |
|
||||
this.setupStyles() |
this.setupElements() |
||||
this.setupEventListeners() |
this.setupStyles() |
||||
this.refresh() |
this.setupEventListeners() |
||||
|
this.refresh() |
||||
|
} |
||||
|
|
||||
|
private setupElements(): void { |
||||
|
this.newPar = document.createElement("div") |
||||
|
// this.sbContainer = document.createElement("div")
|
||||
|
this.scrollBarHolder = document.createElement("div") |
||||
|
this.scrollBar = document.createElement("div") |
||||
|
this.inP = document.createElement("div") |
||||
|
|
||||
|
this.newPar.className = "scrollbot-outer-parent" |
||||
|
this.scrollBarHolder.className = "scrollbot-scrollbar-holder" |
||||
|
this.scrollBar.className = "scrollbot-scrollbar" |
||||
|
this.inP.className = "scrollbot-inner-parent" |
||||
|
|
||||
|
this.inP.innerHTML = this.parContent |
||||
|
this.newPar.appendChild(this.inP) |
||||
|
this.scrollBarHolder.appendChild(this.scrollBar) |
||||
|
this.newPar.appendChild(this.scrollBarHolder) |
||||
|
this.orgPar.appendChild(this.newPar) |
||||
|
} |
||||
|
|
||||
|
private setupStyles(): void { |
||||
|
this.newPar.style.position = "relative" |
||||
|
this.newPar.style.paddingRight = `${this.sbw}px` |
||||
|
this.newPar.style.zIndex = "9999999" |
||||
|
this.newPar.style.height = "100%" |
||||
|
this.newPar.style.overflow = "hidden" |
||||
|
|
||||
|
this.inP.style.cssText = `height:100%;overflow-y:auto;overflow-x:hidden;padding-right:${ |
||||
|
this.sbw + 20 |
||||
|
}px;width:100%;box-sizing:content-box;` |
||||
|
|
||||
|
this.sbHeight = (this.inP.clientHeight * 100) / this.inP.scrollHeight |
||||
|
// this.scrollElement = this.inP
|
||||
|
|
||||
|
this.updateScrollbarStyles() |
||||
|
} |
||||
|
|
||||
|
private updateScrollbarStyles(): void { |
||||
|
this.sB = { |
||||
|
width: `${this.sbw}px`, |
||||
|
height: `${this.sbHeight}%`, |
||||
|
position: "absolute", |
||||
|
right: "0", |
||||
|
top: "0", |
||||
|
backgroundColor: "#444444", |
||||
|
borderRadius: "15px", |
||||
} |
} |
||||
|
|
||||
private setupElements(): void { |
this.sBH = { |
||||
this.newPar = document.createElement("div") |
width: `${this.sbw}px`, |
||||
// this.sbContainer = document.createElement("div")
|
height: "100%", |
||||
this.scrollBarHolder = document.createElement("div") |
position: "absolute", |
||||
this.scrollBar = document.createElement("div") |
right: "0", |
||||
this.inP = document.createElement("div") |
top: "0", |
||||
|
backgroundColor: "#ADADAD", |
||||
this.newPar.className = "scrollbot-outer-parent" |
borderRadius: "15px", |
||||
this.scrollBarHolder.className = "scrollbot-scrollbar-holder" |
|
||||
this.scrollBar.className = "scrollbot-scrollbar" |
|
||||
this.inP.className = "scrollbot-inner-parent" |
|
||||
|
|
||||
this.inP.innerHTML = this.parContent |
|
||||
this.newPar.appendChild(this.inP) |
|
||||
this.scrollBarHolder.appendChild(this.scrollBar) |
|
||||
this.newPar.appendChild(this.scrollBarHolder) |
|
||||
this.orgPar.appendChild(this.newPar) |
|
||||
} |
|
||||
|
|
||||
private setupStyles(): void { |
|
||||
this.newPar.style.position = "relative" |
|
||||
this.newPar.style.paddingRight = `${this.sbw}px` |
|
||||
this.newPar.style.zIndex = "9999999" |
|
||||
this.newPar.style.height = "100%" |
|
||||
this.newPar.style.overflow = "hidden" |
|
||||
|
|
||||
this.inP.style.cssText = `height:100%;overflow-y:auto;overflow-x:hidden;padding-right:${ |
|
||||
this.sbw + 20 |
|
||||
}px;width:100%;box-sizing:content-box;` |
|
||||
|
|
||||
this.sbHeight = (this.inP.clientHeight * 100) / this.inP.scrollHeight |
|
||||
// this.scrollElement = this.inP
|
|
||||
|
|
||||
this.updateScrollbarStyles() |
|
||||
} |
} |
||||
|
|
||||
private updateScrollbarStyles(): void { |
Object.assign(this.scrollBar.style, this.sB) |
||||
this.sB = { |
Object.assign(this.scrollBarHolder.style, this.sBH) |
||||
width: `${this.sbw}px`, |
} |
||||
height: `${this.sbHeight}%`, |
|
||||
position: "absolute", |
|
||||
right: "0", |
|
||||
top: "0", |
|
||||
backgroundColor: "#444444", |
|
||||
borderRadius: "15px", |
|
||||
} |
|
||||
|
|
||||
this.sBH = { |
public refresh(): void { |
||||
width: `${this.sbw}px`, |
this.sbHeight = (this.inP.clientHeight * 100) / this.inP.scrollHeight |
||||
height: "100%", |
this.scrollBarHolder.style.display = this.sbHeight >= 100 ? "none" : "block" |
||||
position: "absolute", |
|
||||
right: "0", |
|
||||
top: "0", |
|
||||
backgroundColor: "#ADADAD", |
|
||||
borderRadius: "15px", |
|
||||
} |
|
||||
|
|
||||
Object.assign(this.scrollBar.style, this.sB) |
if (this.inP.scrollHeight > this.inP.clientHeight) { |
||||
Object.assign(this.scrollBarHolder.style, this.sBH) |
this.scrollBar.style.height = this.customHeight ? this.sB.height : `${this.sbHeight}%` |
||||
} |
} |
||||
|
} |
||||
public refresh(): void { |
|
||||
this.sbHeight = (this.inP.clientHeight * 100) / this.inP.scrollHeight |
public destroy(): void { |
||||
this.scrollBarHolder.style.display = this.sbHeight >= 100 ? "none" : "block" |
this.orgPar.innerHTML = this.parContent |
||||
|
this.orgPar.style.overflow = "auto" |
||||
if (this.inP.scrollHeight > this.inP.clientHeight) { |
} |
||||
this.scrollBar.style.height = this.customHeight ? this.sB.height : `${this.sbHeight}%` |
|
||||
} |
private isIE(): number | false { |
||||
|
const userAgent = navigator.userAgent.toLowerCase() |
||||
|
const msie = userAgent.indexOf("msie") |
||||
|
return msie !== -1 ? parseInt(userAgent.split("msie")[1]) : false |
||||
|
} |
||||
|
|
||||
|
public onScroll(callback: () => void): void { |
||||
|
this.onScrollF = callback |
||||
|
} |
||||
|
|
||||
|
private setupEventListeners(): void { |
||||
|
this.setupScrollListener() |
||||
|
this.setupMouseEvents() |
||||
|
} |
||||
|
|
||||
|
private setupScrollListener(): void { |
||||
|
this.inP.addEventListener("scroll", () => { |
||||
|
const scrollPercentage = (this.inP.scrollTop * 100) / this.inP.scrollHeight |
||||
|
const correction = |
||||
|
((this.sbHeight - parseFloat(this.sB.height)) * this.inP.scrollTop) / (this.inP.scrollHeight - this.inP.clientHeight) |
||||
|
|
||||
|
this.scrollBar.style.top = `${scrollPercentage + correction}%` |
||||
|
|
||||
|
if (this.onScrollF) { |
||||
|
this.onScrollF() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
private setScroll(position: number, duration: number = 500): void { |
||||
|
if (position >= this.inP.scrollHeight - this.inP.clientHeight) { |
||||
|
position = this.inP.scrollHeight - this.inP.clientHeight |
||||
} |
} |
||||
|
|
||||
public destroy(): void { |
const difference = position - this.inP.scrollTop |
||||
this.orgPar.innerHTML = this.parContent |
const perTick = (difference / duration) * 10 |
||||
this.orgPar.style.overflow = "auto" |
|
||||
|
setTimeout(() => { |
||||
|
this.inP.scrollTop += perTick |
||||
|
if (Math.abs(position - this.inP.scrollTop) < 5) return |
||||
|
this.setScroll(position, duration - 10) |
||||
|
}, 10) |
||||
|
} |
||||
|
|
||||
|
private setupMouseEvents(): void { |
||||
|
// 滚动条容器点击事件
|
||||
|
this.scrollBarHolder.onmousedown = (e: MouseEvent) => { |
||||
|
if (e.target !== this.scrollBarHolder) return |
||||
|
const relPos = ((e.pageY - this.scrollBarHolder.getBoundingClientRect().top) * 100) / this.scrollBarHolder.clientHeight |
||||
|
this.setScroll((this.inP.scrollHeight * relPos) / 100, this.scrollSpeed) |
||||
} |
} |
||||
|
|
||||
private isIE(): number | false { |
// 滚动条拖动事件
|
||||
const userAgent = navigator.userAgent.toLowerCase() |
this.scrollBar.onmousedown = (e: MouseEvent) => { |
||||
const msie = userAgent.indexOf("msie") |
this.mdown = true |
||||
return msie !== -1 ? parseInt(userAgent.split("msie")[1]) : false |
this.posCorrection = e.pageY - this.scrollBar.getBoundingClientRect().top |
||||
|
this.btmCorrection = (this.scrollBar.clientHeight * 100) / this.newPar.clientHeight |
||||
|
return false |
||||
} |
} |
||||
|
|
||||
public onScroll(callback: () => void): void { |
// 全局鼠标事件
|
||||
this.onScrollF = callback |
document.onmouseup = () => { |
||||
|
this.mdown = false |
||||
} |
} |
||||
|
|
||||
private setupEventListeners(): void { |
document.onmousemove = (e: MouseEvent) => { |
||||
this.setupScrollListener() |
if (this.mdown) { |
||||
this.setupMouseEvents() |
// 清除文本选择
|
||||
} |
window.getSelection()?.removeAllRanges() |
||||
|
|
||||
private setupScrollListener(): void { |
this.relY = e.pageY - this.newPar.getBoundingClientRect().top |
||||
this.inP.addEventListener("scroll", () => { |
this.pC = ((this.relY - this.posCorrection) * 100) / this.newPar.clientHeight |
||||
const scrollPercentage = (this.inP.scrollTop * 100) / this.inP.scrollHeight |
|
||||
const correction = |
if (this.pC >= 0 && this.pC + this.btmCorrection <= 100) { |
||||
((this.sbHeight - parseFloat(this.sB.height)) * this.inP.scrollTop) / (this.inP.scrollHeight - this.inP.clientHeight) |
this.scrollBar.style.top = `${this.pC}%` |
||||
|
this.inP.scrollTop = |
||||
this.scrollBar.style.top = `${scrollPercentage + correction}%` |
((parseFloat(this.scrollBar.style.top) - |
||||
|
((this.sbHeight - parseFloat(this.sB.height)) * this.inP.scrollTop) / (this.inP.scrollHeight - this.inP.clientHeight)) * |
||||
if (this.onScrollF) { |
this.inP.scrollHeight) / |
||||
this.onScrollF() |
100 |
||||
} |
} else if (this.pC < 0 && parseFloat(this.scrollBar.style.top) > 0) { |
||||
}) |
this.scrollBar.style.top = "0%" |
||||
} |
this.inP.scrollTop = 0 |
||||
|
|
||||
private setScroll(position: number, duration: number = 500): void { |
|
||||
if (position >= this.inP.scrollHeight - this.inP.clientHeight) { |
|
||||
position = this.inP.scrollHeight - this.inP.clientHeight |
|
||||
} |
|
||||
|
|
||||
const difference = position - this.inP.scrollTop |
|
||||
const perTick = (difference / duration) * 10 |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
this.inP.scrollTop += perTick |
|
||||
if (Math.abs(position - this.inP.scrollTop) < 5) return |
|
||||
this.setScroll(position, duration - 10) |
|
||||
}, 10) |
|
||||
} |
|
||||
|
|
||||
private setupMouseEvents(): void { |
|
||||
// 滚动条容器点击事件
|
|
||||
this.scrollBarHolder.onmousedown = (e: MouseEvent) => { |
|
||||
if (e.target !== this.scrollBarHolder) return |
|
||||
const relPos = ((e.pageY - this.scrollBarHolder.getBoundingClientRect().top) * 100) / this.scrollBarHolder.clientHeight |
|
||||
this.setScroll((this.inP.scrollHeight * relPos) / 100, this.scrollSpeed) |
|
||||
} |
|
||||
|
|
||||
// 滚动条拖动事件
|
|
||||
this.scrollBar.onmousedown = (e: MouseEvent) => { |
|
||||
this.mdown = true |
|
||||
this.posCorrection = e.pageY - this.scrollBar.getBoundingClientRect().top |
|
||||
this.btmCorrection = (this.scrollBar.clientHeight * 100) / this.newPar.clientHeight |
|
||||
return false |
|
||||
} |
} |
||||
|
|
||||
// 全局鼠标事件
|
if (this.onScrollF) { |
||||
document.onmouseup = () => { |
this.onScrollF() |
||||
this.mdown = false |
|
||||
} |
|
||||
|
|
||||
document.onmousemove = (e: MouseEvent) => { |
|
||||
if (this.mdown) { |
|
||||
// 清除文本选择
|
|
||||
window.getSelection()?.removeAllRanges() |
|
||||
|
|
||||
this.relY = e.pageY - this.newPar.getBoundingClientRect().top |
|
||||
this.pC = ((this.relY - this.posCorrection) * 100) / this.newPar.clientHeight |
|
||||
|
|
||||
if (this.pC >= 0 && this.pC + this.btmCorrection <= 100) { |
|
||||
this.scrollBar.style.top = `${this.pC}%` |
|
||||
this.inP.scrollTop = |
|
||||
((parseFloat(this.scrollBar.style.top) - |
|
||||
((this.sbHeight - parseFloat(this.sB.height)) * this.inP.scrollTop) / |
|
||||
(this.inP.scrollHeight - this.inP.clientHeight)) * |
|
||||
this.inP.scrollHeight) / |
|
||||
100 |
|
||||
} else if (this.pC < 0 && parseFloat(this.scrollBar.style.top) > 0) { |
|
||||
this.scrollBar.style.top = "0%" |
|
||||
this.inP.scrollTop = 0 |
|
||||
} |
|
||||
|
|
||||
if (this.onScrollF) { |
|
||||
this.onScrollF() |
|
||||
} |
|
||||
} |
|
||||
return false |
|
||||
} |
} |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public setStyle(scrollbar?: ScrollStyle, scrollbarHolder?: ScrollStyle): Scrollbot { |
||||
|
if (scrollbar) { |
||||
|
scrollbar.width = `${this.sbw}px` |
||||
|
if ("height" in scrollbar) { |
||||
|
this.customHeight = true |
||||
|
scrollbar.height = `${(parseFloat(scrollbar.height) * 100) / this.newPar.clientHeight}%` |
||||
|
} |
||||
|
Object.assign(this.sB, scrollbar) |
||||
|
Object.assign(this.scrollBar.style, scrollbar) |
||||
} |
} |
||||
|
|
||||
public setStyle(scrollbar?: ScrollStyle, scrollbarHolder?: ScrollStyle): Scrollbot { |
if (scrollbarHolder) { |
||||
if (scrollbar) { |
scrollbarHolder.width = `${this.sbw}px` |
||||
scrollbar.width = `${this.sbw}px` |
Object.assign(this.sBH, scrollbarHolder) |
||||
if ("height" in scrollbar) { |
Object.assign(this.scrollBarHolder.style, scrollbarHolder) |
||||
this.customHeight = true |
|
||||
scrollbar.height = `${(parseFloat(scrollbar.height) * 100) / this.newPar.clientHeight}%` |
|
||||
} |
|
||||
Object.assign(this.sB, scrollbar) |
|
||||
Object.assign(this.scrollBar.style, scrollbar) |
|
||||
} |
|
||||
|
|
||||
if (scrollbarHolder) { |
|
||||
scrollbarHolder.width = `${this.sbw}px` |
|
||||
Object.assign(this.sBH, scrollbarHolder) |
|
||||
Object.assign(this.scrollBarHolder.style, scrollbarHolder) |
|
||||
} |
|
||||
|
|
||||
return this |
|
||||
} |
} |
||||
|
|
||||
|
return this |
||||
|
} |
||||
} |
} |
||||
|
|
||||
export default Scrollbot |
export default Scrollbot |
||||
|
@ -1,44 +1,44 @@ |
|||||
*, |
*, |
||||
*::before, |
*::before, |
||||
*::after { |
*::after { |
||||
box-sizing: border-box; |
box-sizing: border-box; |
||||
margin: 0; |
margin: 0; |
||||
font-weight: normal; |
font-weight: normal; |
||||
} |
} |
||||
|
|
||||
html { |
html { |
||||
--text-normal: #6b6b6b; |
--text-normal: #6b6b6b; |
||||
--text-hover: #000000; |
--text-hover: #000000; |
||||
height: 100%; |
height: 100%; |
||||
} |
} |
||||
|
|
||||
body { |
body { |
||||
--at-apply: text-normal; |
--at-apply: text-normal; |
||||
height: 100%; |
height: 100%; |
||||
} |
} |
||||
|
|
||||
#app { |
#app { |
||||
height: 100%; |
height: 100%; |
||||
} |
} |
||||
|
|
||||
* { |
* { |
||||
user-select: none; |
user-select: none; |
||||
outline: none; |
outline: none; |
||||
} |
} |
||||
|
|
||||
.simplebar-scrollbar::before { |
.simplebar-scrollbar::before { |
||||
background-color: #bdbdbd; |
background-color: #bdbdbd; |
||||
border-radius: 0; |
border-radius: 0; |
||||
left: 0; |
left: 0; |
||||
right: 0; |
right: 0; |
||||
bottom: 0; |
bottom: 0; |
||||
top: 0; |
top: 0; |
||||
} |
} |
||||
|
|
||||
.simplebar-hover .simplebar-scrollbar::before { |
.simplebar-hover .simplebar-scrollbar::before { |
||||
background-color: #909090; |
background-color: #909090; |
||||
} |
} |
||||
|
|
||||
.simplebar-wrapper:hover ~ .simplebar-track > .simplebar-scrollbar:before { |
.simplebar-wrapper:hover ~ .simplebar-track > .simplebar-scrollbar:before { |
||||
opacity: 0.5 !important; |
opacity: 0.5 !important; |
||||
} |
} |
||||
|
@ -1,300 +1,299 @@ |
|||||
<script lang="ts" setup> |
<script lang="ts" setup> |
||||
import { judgeFile } from "./utils" |
import { judgeFile } from "./utils" |
||||
import { monaco } from "./monaco" |
import { monaco } from "./monaco" |
||||
import { computed, getCurrentScope, onBeforeUnmount, onMounted, onScopeDispose, ref, watch } from "vue" |
import { computed, getCurrentScope, onBeforeUnmount, onMounted, onScopeDispose, ref, watch } from "vue" |
||||
import DefaultLogo from "./120x120.png" |
import DefaultLogo from "./120x120.png" |
||||
import { PlaceholderContentWidget } from "./PlaceholderContentWidget" |
import { PlaceholderContentWidget } from "./PlaceholderContentWidget" |
||||
const editorRef = ref<HTMLDivElement>() |
const editorRef = ref<HTMLDivElement>() |
||||
let editor: monaco.editor.IStandaloneCodeEditor | null = null |
let editor: monaco.editor.IStandaloneCodeEditor | null = null |
||||
let placeholderWidget: PlaceholderContentWidget | null = null |
let placeholderWidget: PlaceholderContentWidget | null = null |
||||
const props = withDefaults( |
const props = withDefaults( |
||||
defineProps<{ |
defineProps<{ |
||||
modelValue?: string |
modelValue?: string |
||||
name?: string |
name?: string |
||||
logoType?: "bg" | "logo" |
logoType?: "bg" | "logo" |
||||
logo?: string |
logo?: string |
||||
placeholder?: string |
placeholder?: string |
||||
fontFamily?: string |
fontFamily?: string |
||||
readonly?: boolean |
readonly?: boolean |
||||
}>(), |
}>(), |
||||
{ |
{ |
||||
logo: DefaultLogo, |
logo: DefaultLogo, |
||||
readonly: false, |
readonly: false, |
||||
logoType: "logo", |
logoType: "logo", |
||||
modelValue: "", |
modelValue: "", |
||||
name: "", |
name: "", |
||||
}, |
}, |
||||
) |
) |
||||
const emit = defineEmits<{ |
const emit = defineEmits<{ |
||||
(e: "update:modelValue", code: string): void |
(e: "update:modelValue", code: string): void |
||||
(e: "change", code: string): void |
(e: "change", code: string): void |
||||
(e: "cursor:position", position: [number, number]): void |
(e: "cursor:position", position: [number, number]): void |
||||
}>() |
}>() |
||||
defineExpose({ |
defineExpose({ |
||||
scrollTop() { |
scrollTop() { |
||||
editor?.setScrollTop(0) |
editor?.setScrollTop(0) |
||||
}, |
}, |
||||
insertText(text: string, type = "cursor") { |
insertText(text: string, type = "cursor") { |
||||
if (editor) { |
if (editor) { |
||||
const m = editor.getModel() |
const m = editor.getModel() |
||||
const currentPosition = editor.getPosition() |
const currentPosition = editor.getPosition() |
||||
if (m) { |
if (m) { |
||||
console.log(currentPosition) |
console.log(currentPosition) |
||||
if (type === "cursor" && currentPosition) { |
if (type === "cursor" && currentPosition) { |
||||
m.pushEditOperations( |
m.pushEditOperations( |
||||
[], |
[], |
||||
[ |
[ |
||||
{ |
{ |
||||
range: new monaco.Range( |
range: new monaco.Range( |
||||
currentPosition.lineNumber, |
currentPosition.lineNumber, |
||||
currentPosition.column, |
currentPosition.column, |
||||
currentPosition.lineNumber, |
currentPosition.lineNumber, |
||||
currentPosition.column, |
currentPosition.column, |
||||
), |
), |
||||
text, |
text, |
||||
}, |
}, |
||||
], |
], |
||||
() => [ |
() => [ |
||||
new monaco.Selection( |
new monaco.Selection( |
||||
currentPosition.lineNumber, |
currentPosition.lineNumber, |
||||
currentPosition.column, |
currentPosition.column, |
||||
currentPosition.lineNumber, |
currentPosition.lineNumber, |
||||
currentPosition.column, |
currentPosition.column, |
||||
), |
), |
||||
], |
], |
||||
) |
) |
||||
} else { |
} else { |
||||
const lineCount = m.getLineCount() |
const lineCount = m.getLineCount() |
||||
const lastLineLength = m.getLineLength(lineCount) |
const lastLineLength = m.getLineLength(lineCount) |
||||
const range = new monaco.Selection(lineCount, lastLineLength + 1, lineCount, lastLineLength + 1) |
const range = new monaco.Selection(lineCount, lastLineLength + 1, lineCount, lastLineLength + 1) |
||||
const text = "your text" |
const text = "your text" |
||||
const op = { |
const op = { |
||||
range: range, |
range: range, |
||||
text: text, |
text: text, |
||||
} |
|
||||
m.pushEditOperations([], [op], () => [range]) |
|
||||
} |
|
||||
} |
} |
||||
|
m.pushEditOperations([], [op], () => [range]) |
||||
|
} |
||||
} |
} |
||||
|
} |
||||
}, |
}, |
||||
setContent(content: string) { |
setContent(content: string) { |
||||
if (editorRef.value && editor) { |
if (editorRef.value && editor) { |
||||
editor.setValue(content) |
editor.setValue(content) |
||||
} |
} |
||||
}, |
}, |
||||
}) |
}) |
||||
|
|
||||
let isInnerChange = false |
let isInnerChange = false |
||||
function updateModel(name: string, content: string) { |
function updateModel(name: string, content: string) { |
||||
if (editor) { |
if (editor) { |
||||
const oldModel = editor.getModel() //获取旧模型 |
const oldModel = editor.getModel() //获取旧模型 |
||||
const file = judgeFile(name) |
const file = judgeFile(name) |
||||
// 这样定义的话model无法清除 |
// 这样定义的话model无法清除 |
||||
// monaco.editor.createModel("const a = 111","typescript", monaco.Uri.parse('file://root/file3.ts')) |
// monaco.editor.createModel("const a = 111","typescript", monaco.Uri.parse('file://root/file3.ts')) |
||||
const model: monaco.editor.ITextModel = monaco.editor.createModel(content ?? "", file?.language ?? "txt") |
const model: monaco.editor.ITextModel = monaco.editor.createModel(content ?? "", file?.language ?? "txt") |
||||
model.onDidChangeContent(() => { |
model.onDidChangeContent(() => { |
||||
if (model) { |
if (model) { |
||||
isInnerChange = true |
isInnerChange = true |
||||
const code = model.getValue() |
const code = model.getValue() |
||||
emit("update:modelValue", code) |
emit("update:modelValue", code) |
||||
emit("change", code) |
emit("change", code) |
||||
} |
|
||||
}) |
|
||||
if (oldModel) { |
|
||||
oldModel.dispose() |
|
||||
} |
} |
||||
editor.setModel(model) |
}) |
||||
|
if (oldModel) { |
||||
|
oldModel.dispose() |
||||
|
} |
||||
|
editor.setModel(model) |
||||
} |
} |
||||
} |
} |
||||
function resizeLayout() { |
function resizeLayout() { |
||||
if (editor) { |
if (editor) { |
||||
editor.layout() |
editor.layout() |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
onMounted(() => { |
onMounted(() => { |
||||
if (editorRef.value && !editor) { |
if (editorRef.value && !editor) { |
||||
editor = monaco.editor.create(editorRef.value, { |
editor = monaco.editor.create(editorRef.value, { |
||||
theme: "vs-light", |
theme: "vs-light", |
||||
fontFamily: props.fontFamily ?? "Cascadia Mono, Consolas, 'Courier New', monospace", |
fontFamily: props.fontFamily ?? "Cascadia Mono, Consolas, 'Courier New', monospace", |
||||
readOnly: props.readonly, |
readOnly: props.readonly, |
||||
minimap: { |
minimap: { |
||||
autohide: true, |
autohide: true, |
||||
}, |
}, |
||||
}) as monaco.editor.IStandaloneCodeEditor |
}) as monaco.editor.IStandaloneCodeEditor |
||||
editor.onDidChangeCursorPosition(e => { |
editor.onDidChangeCursorPosition(e => { |
||||
emit("cursor:position", [e.position.lineNumber, e.position.column]) |
emit("cursor:position", [e.position.lineNumber, e.position.column]) |
||||
}) |
}) |
||||
editorRef.value.addEventListener("resize", resizeLayout) |
editorRef.value.addEventListener("resize", resizeLayout) |
||||
} |
} |
||||
watch( |
watch( |
||||
() => props.placeholder, |
() => props.placeholder, |
||||
() => { |
() => { |
||||
if (editor) { |
if (editor) { |
||||
if (placeholderWidget) { |
if (placeholderWidget) { |
||||
placeholderWidget.dispose() |
placeholderWidget.dispose() |
||||
placeholderWidget = null |
placeholderWidget = null |
||||
} |
} |
||||
if (props.placeholder) { |
if (props.placeholder) { |
||||
placeholderWidget = new PlaceholderContentWidget(props.placeholder, editor) |
placeholderWidget = new PlaceholderContentWidget(props.placeholder, editor) |
||||
} |
} |
||||
} |
} |
||||
}, |
}, |
||||
{ |
{ |
||||
immediate: true, |
immediate: true, |
||||
}, |
}, |
||||
) |
) |
||||
// 如果不需要从动态外部更改代码的话应该就不需要这个 |
// 如果不需要从动态外部更改代码的话应该就不需要这个 |
||||
watch( |
watch( |
||||
() => props.modelValue, |
() => props.modelValue, |
||||
async str => { |
async str => { |
||||
if (editor && !isInnerChange) { |
if (editor && !isInnerChange) { |
||||
editor.setValue(str) |
editor.setValue(str) |
||||
} else { |
} else { |
||||
isInnerChange = false |
isInnerChange = false |
||||
} |
} |
||||
}, |
}, |
||||
{ immediate: true }, |
{ immediate: true }, |
||||
) |
) |
||||
watch( |
watch( |
||||
() => props.name, |
() => props.name, |
||||
async name => { |
async name => { |
||||
if (editor) { |
if (editor) { |
||||
updateModel(name, props.modelValue) |
updateModel(name, props.modelValue) |
||||
} |
} |
||||
}, |
}, |
||||
{ immediate: true }, |
{ immediate: true }, |
||||
) |
) |
||||
watch( |
watch( |
||||
() => props.readonly, |
() => props.readonly, |
||||
() => { |
() => { |
||||
if (editor) { |
if (editor) { |
||||
editor.updateOptions({ |
editor.updateOptions({ |
||||
readOnly: props.readonly, |
readOnly: props.readonly, |
||||
}) |
}) |
||||
} |
} |
||||
}, |
}, |
||||
) |
) |
||||
watch( |
watch( |
||||
() => props.fontFamily, |
() => props.fontFamily, |
||||
() => { |
() => { |
||||
if (editor) { |
if (editor) { |
||||
editor.updateOptions({ |
editor.updateOptions({ |
||||
fontFamily: props.fontFamily, |
fontFamily: props.fontFamily, |
||||
}) |
}) |
||||
} |
} |
||||
}, |
}, |
||||
) |
) |
||||
}) |
}) |
||||
if (import.meta.hot) { |
if (import.meta.hot) { |
||||
import.meta.hot.accept((newModule) => { |
import.meta.hot.accept(newModule => { |
||||
console.log(newModule); |
console.log(newModule) |
||||
|
|
||||
}) |
}) |
||||
} |
} |
||||
onBeforeUnmount(() => { |
onBeforeUnmount(() => { |
||||
if (editorRef.value) { |
if (editorRef.value) { |
||||
editorRef.value.removeEventListener("resize", resizeLayout) |
editorRef.value.removeEventListener("resize", resizeLayout) |
||||
} |
} |
||||
if (editor) { |
if (editor) { |
||||
const oldModel = editor.getModel() |
const oldModel = editor.getModel() |
||||
if (oldModel) { |
if (oldModel) { |
||||
oldModel.dispose() |
oldModel.dispose() |
||||
} |
} |
||||
editor.dispose() |
editor.dispose() |
||||
editor = null |
editor = null |
||||
} |
} |
||||
}) |
}) |
||||
const style = computed(() => { |
const style = computed(() => { |
||||
if (props.logo && props.logoType === "bg") { |
if (props.logo && props.logoType === "bg") { |
||||
return { |
return { |
||||
backgroundImage: `url(${props.logo})`, |
backgroundImage: `url(${props.logo})`, |
||||
backgroundSize: "cover", |
backgroundSize: "cover", |
||||
backgroundRepeat: "no-repeat", |
backgroundRepeat: "no-repeat", |
||||
backgroundPosition: "center center", |
backgroundPosition: "center center", |
||||
} |
} |
||||
} |
} |
||||
return {} |
return {} |
||||
}) |
}) |
||||
|
|
||||
const getLogo = computed(() => { |
const getLogo = computed(() => { |
||||
if (props.logo) return props.logo |
if (props.logo) return props.logo |
||||
return DefaultLogo |
return DefaultLogo |
||||
}) |
}) |
||||
|
|
||||
function useResizeObserver(callback: ResizeObserverCallback) { |
function useResizeObserver(callback: ResizeObserverCallback) { |
||||
const isSupported = window && "ResizeObserver" in window |
const isSupported = window && "ResizeObserver" in window |
||||
let observer: ResizeObserver | undefined |
let observer: ResizeObserver | undefined |
||||
const cleanup = () => { |
const cleanup = () => { |
||||
if (observer) { |
if (observer) { |
||||
observer.disconnect() |
observer.disconnect() |
||||
observer = undefined |
observer = undefined |
||||
} |
} |
||||
} |
} |
||||
const stopWatch = watch( |
const stopWatch = watch( |
||||
() => editorRef.value, |
() => editorRef.value, |
||||
el => { |
el => { |
||||
cleanup() |
cleanup() |
||||
if (isSupported && window && el) { |
if (isSupported && window && el) { |
||||
observer = new ResizeObserver(callback) |
observer = new ResizeObserver(callback) |
||||
observer!.observe(el, {}) |
observer!.observe(el, {}) |
||||
} |
} |
||||
}, |
}, |
||||
{ immediate: true }, |
{ immediate: true }, |
||||
) |
) |
||||
const stop = () => { |
const stop = () => { |
||||
cleanup() |
cleanup() |
||||
stopWatch() |
stopWatch() |
||||
} |
} |
||||
function tryOnScopeDispose(fn: () => void) { |
function tryOnScopeDispose(fn: () => void) { |
||||
if (getCurrentScope()) { |
if (getCurrentScope()) { |
||||
onScopeDispose(fn) |
onScopeDispose(fn) |
||||
return true |
return true |
||||
} |
} |
||||
return false |
return false |
||||
} |
} |
||||
tryOnScopeDispose(() => { |
tryOnScopeDispose(() => { |
||||
stop() |
stop() |
||||
}) |
}) |
||||
} |
} |
||||
useResizeObserver(() => { |
useResizeObserver(() => { |
||||
if (editor) { |
if (editor) { |
||||
editor.layout() |
editor.layout() |
||||
} |
} |
||||
}) |
}) |
||||
</script> |
</script> |
||||
|
|
||||
<template> |
<template> |
||||
<div class="monaco-wrapper"> |
<div class="monaco-wrapper"> |
||||
<div class="monaco-editor" ref="editorRef"></div> |
<div class="monaco-editor" ref="editorRef"></div> |
||||
<div class="monaco-bg" :style="style"> |
<div class="monaco-bg" :style="style"> |
||||
<img v-if="logoType === 'logo' && getLogo" class="monaco-logo" :src="getLogo" alt="" /> |
<img v-if="logoType === 'logo' && getLogo" class="monaco-logo" :src="getLogo" alt="" /> |
||||
</div> |
|
||||
</div> |
</div> |
||||
|
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
.monaco-wrapper { |
.monaco-wrapper { |
||||
height: 100%; |
height: 100%; |
||||
position: relative; |
position: relative; |
||||
|
|
||||
.monaco-editor { |
.monaco-editor { |
||||
height: 100%; |
height: 100%; |
||||
} |
} |
||||
|
|
||||
.monaco-bg { |
.monaco-bg { |
||||
position: absolute; |
position: absolute; |
||||
top: 0; |
top: 0; |
||||
left: 0; |
left: 0; |
||||
right: 0; |
right: 0; |
||||
bottom: 0; |
bottom: 0; |
||||
pointer-events: none; |
pointer-events: none; |
||||
opacity: 0.1; |
opacity: 0.1; |
||||
overflow: hidden; |
overflow: hidden; |
||||
|
|
||||
.monaco-logo { |
.monaco-logo { |
||||
@apply absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2; |
@apply absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2; |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
</style> |
</style> |
||||
|
@ -1,32 +1,32 @@ |
|||||
export function judgeFile(filename: string) { |
export function judgeFile(filename: string) { |
||||
if (!filename) return |
if (!filename) return |
||||
let ext = [ |
let ext = [ |
||||
{ language: "vue", ext: ".vue", index: -1 }, |
{ language: "vue", ext: ".vue", index: -1 }, |
||||
{ language: "javascript", ext: ".js", index: -1 }, |
{ language: "javascript", ext: ".js", index: -1 }, |
||||
{ language: "css", ext: ".css", index: -1 }, |
{ language: "css", ext: ".css", index: -1 }, |
||||
{ language: "html", ext: ".html", index: -1 }, |
{ language: "html", ext: ".html", index: -1 }, |
||||
{ language: "tsx", ext: ".tsx", index: -1 }, |
{ language: "tsx", ext: ".tsx", index: -1 }, |
||||
{ language: "typescript", ext: ".ts", index: -1 }, |
{ language: "typescript", ext: ".ts", index: -1 }, |
||||
{ language: "markdown", ext: ".md", index: -1 }, |
{ language: "markdown", ext: ".md", index: -1 }, |
||||
{ language: "json", ext: ".json", index: -1 }, |
{ language: "json", ext: ".json", index: -1 }, |
||||
{ language: "web", ext: ".web", index: -1 }, |
{ language: "web", ext: ".web", index: -1 }, |
||||
{ language: "dot", pre: ".", index: -1 }, |
{ language: "dot", pre: ".", index: -1 }, |
||||
] |
] |
||||
let cur |
let cur |
||||
for (let i = 0; i < ext.length; i++) { |
for (let i = 0; i < ext.length; i++) { |
||||
const e = ext[i] |
const e = ext[i] |
||||
if (e.ext && filename.endsWith(e.ext)) { |
if (e.ext && filename.endsWith(e.ext)) { |
||||
let index = filename.lastIndexOf(e.ext) |
let index = filename.lastIndexOf(e.ext) |
||||
e.index = index |
e.index = index |
||||
cur = e |
cur = e |
||||
break |
break |
||||
} |
|
||||
if (e.pre && filename.startsWith(e.pre)) { |
|
||||
let index = filename.indexOf(e.pre) |
|
||||
e.index = index |
|
||||
cur = e |
|
||||
break |
|
||||
} |
|
||||
} |
} |
||||
return cur |
if (e.pre && filename.startsWith(e.pre)) { |
||||
|
let index = filename.indexOf(e.pre) |
||||
|
e.index = index |
||||
|
cur = e |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
return cur |
||||
} |
} |
||||
|
@ -1,102 +1,102 @@ |
|||||
<template> |
<template> |
||||
<div |
<div |
||||
relative |
relative |
||||
h="30px" |
h="30px" |
||||
leading="29px" |
leading="29px" |
||||
pr="137px" |
pr="137px" |
||||
:style="{ paddingRight: isFullScreen ? '0' : '' }" |
:style="{ paddingRight: isFullScreen ? '0' : '' }" |
||||
select-none |
select-none |
||||
border-b="1px solid #E5E5E5" |
border-b="1px solid #E5E5E5" |
||||
bg="#F8F8F8" |
bg="#F8F8F8" |
||||
> |
> |
||||
<div absolute top-0 right-0 bottom-0 left-0 style="-webkit-app-region: drag"></div> |
<div absolute top-0 right-0 bottom-0 left-0 style="-webkit-app-region: drag"></div> |
||||
<div h-full px-2 flex items-center gap-1 justify-between> |
<div h-full px-2 flex items-center gap-1 justify-between> |
||||
<div flex items-center gap-1> |
<div flex items-center gap-1> |
||||
<img w="16px" h="16px" :src="icon" /> |
<img w="16px" h="16px" :src="icon" /> |
||||
<div relative h-full inline-flex items-center text-sm>{{ config.app_title }}</div> |
<div relative h-full inline-flex items-center text-sm>{{ config.app_title }}</div> |
||||
<div relative class="list"> |
<div relative class="list"> |
||||
<div class="item" @click="onClickMenu">{{ t("caidan") }}</div> |
<div class="item" @click="onClickMenu">{{ t("caidan") }}</div> |
||||
</div> |
|
||||
</div> |
|
||||
<div float-right h-full flex items-center relative style="-webkit-app-region: no-drag"> |
|
||||
<div |
|
||||
v-if="!isHome" |
|
||||
text-sm |
|
||||
px-2 |
|
||||
hover:rounded-md |
|
||||
hover:bg-gray-2 |
|
||||
hover:cursor-pointer |
|
||||
text="hover:hover" |
|
||||
title="返回上一页" |
|
||||
@click="back" |
|
||||
> |
|
||||
🏠 |
|
||||
</div> |
|
||||
<div text-sm px-2 hover:rounded-md hover:bg-gray-2 hover:cursor-pointer text="hover:hover" @click="onClickAbout">关于</div> |
|
||||
</div> |
|
||||
</div> |
</div> |
||||
|
</div> |
||||
|
<div float-right h-full flex items-center relative style="-webkit-app-region: no-drag"> |
||||
|
<div |
||||
|
v-if="!isHome" |
||||
|
text-sm |
||||
|
px-2 |
||||
|
hover:rounded-md |
||||
|
hover:bg-gray-2 |
||||
|
hover:cursor-pointer |
||||
|
text="hover:hover" |
||||
|
title="返回上一页" |
||||
|
@click="back" |
||||
|
> |
||||
|
🏠 |
||||
|
</div> |
||||
|
<div text-sm px-2 hover:rounded-md hover:bg-gray-2 hover:cursor-pointer text="hover:hover" @click="onClickAbout">关于</div> |
||||
|
</div> |
||||
</div> |
</div> |
||||
|
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<script setup lang="ts"> |
<script setup lang="ts"> |
||||
import icon from "@res/icon.png" |
import icon from "@res/icon.png" |
||||
import config from "config" |
import config from "config" |
||||
import { PopupMenu } from "@/bridge/PopupMenu" |
import { PopupMenu } from "@/bridge/PopupMenu" |
||||
import { usePlatForm } from "common/usePlatform" |
import { usePlatForm } from "common/usePlatform" |
||||
|
|
||||
const { PlatForm } = usePlatForm() |
const { PlatForm } = usePlatForm() |
||||
|
|
||||
const router = useRouter() |
const router = useRouter() |
||||
const route = useRoute() |
const route = useRoute() |
||||
const isFullScreen = ref(false) |
const isFullScreen = ref(false) |
||||
|
|
||||
onBeforeMount(async () => { |
onBeforeMount(async () => { |
||||
isFullScreen.value = await PlatForm.isFullScreen() |
isFullScreen.value = await PlatForm.isFullScreen() |
||||
}) |
}) |
||||
|
|
||||
const isHome = computed(() => { |
const isHome = computed(() => { |
||||
if (route.fullPath === "/") { |
if (route.fullPath === "/") { |
||||
return true |
return true |
||||
} |
} |
||||
return false |
return false |
||||
}) |
}) |
||||
|
|
||||
function back() { |
function back() { |
||||
router.push("/") |
router.push("/") |
||||
} |
} |
||||
const { t } = useI18n() |
const { t } = useI18n() |
||||
const onClickMenu = e => { |
const onClickMenu = e => { |
||||
const menu = new PopupMenu([ |
const menu = new PopupMenu([ |
||||
{ |
{ |
||||
label: isFullScreen.value ? t("qu-xiao-quan-ping") : t("quan-ping"), |
label: isFullScreen.value ? t("qu-xiao-quan-ping") : t("quan-ping"), |
||||
async click() { |
async click() { |
||||
await PlatForm.toggleFullScreen() |
await PlatForm.toggleFullScreen() |
||||
isFullScreen.value = !isFullScreen.value |
isFullScreen.value = !isFullScreen.value |
||||
}, |
|
||||
}, |
}, |
||||
{ |
}, |
||||
label: t("qie-huan-kai-fa-zhe-gong-ju"), |
{ |
||||
async click() { |
label: t("qie-huan-kai-fa-zhe-gong-ju"), |
||||
PlatForm.toggleDevTools() |
async click() { |
||||
}, |
PlatForm.toggleDevTools() |
||||
}, |
}, |
||||
|
}, |
||||
]) |
]) |
||||
const obj = e.target.getBoundingClientRect() |
const obj = e.target.getBoundingClientRect() |
||||
menu.show({ x: ~~obj.x, y: ~~(obj.y + obj.height) }) |
menu.show({ x: ~~obj.x, y: ~~(obj.y + obj.height) }) |
||||
} |
} |
||||
|
|
||||
const onClickAbout = () => { |
const onClickAbout = () => { |
||||
PlatForm.showAbout() |
PlatForm.showAbout() |
||||
} |
} |
||||
</script> |
</script> |
||||
|
|
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
.list { |
.list { |
||||
@apply: flex gap="5px"; |
@apply: flex gap="5px"; |
||||
-webkit-app-region: no-drag; |
-webkit-app-region: no-drag; |
||||
|
|
||||
.item { |
.item { |
||||
@apply: text-sm px-2 hover:rounded-md hover:bg-gray-2 hover:cursor-pointer text="hover:hover"; |
@apply: text-sm px-2 hover:rounded-md hover:bg-gray-2 hover:cursor-pointer text="hover:hover"; |
||||
} |
} |
||||
} |
} |
||||
</style> |
</style> |
||||
|
@ -1,13 +1,13 @@ |
|||||
<script setup lang="ts"> |
<script setup lang="ts"> |
||||
import { reactive } from "vue" |
import { reactive } from "vue" |
||||
|
|
||||
const versions = reactive({ ...window.electron.process.versions }) |
const versions = reactive({ ...window.electron.process.versions }) |
||||
</script> |
</script> |
||||
|
|
||||
<template> |
<template> |
||||
<ul class="versions"> |
<ul class="versions"> |
||||
<li class="electron-version">Electron v{{ versions.electron }}</li> |
<li class="electron-version">Electron v{{ versions.electron }}</li> |
||||
<li class="chrome-version">Chromium v{{ versions.chrome }}</li> |
<li class="chrome-version">Chromium v{{ versions.chrome }}</li> |
||||
<li class="node-version">Node v{{ versions.node }}</li> |
<li class="node-version">Node v{{ versions.node }}</li> |
||||
</ul> |
</ul> |
||||
</template> |
</template> |
||||
|
@ -1,3 +1,3 @@ |
|||||
export function useTest() { |
export function useTest() { |
||||
console.log("test") |
console.log("test") |
||||
} |
} |
||||
|
@ -1,15 +1,15 @@ |
|||||
<script lang="ts" setup> |
<script lang="ts" setup> |
||||
import Simplebar from "simplebar-vue" |
import Simplebar from "simplebar-vue" |
||||
</script> |
</script> |
||||
|
|
||||
<template> |
<template> |
||||
<Simplebar h-full> |
<Simplebar h-full> |
||||
<RouterView></RouterView> |
<RouterView></RouterView> |
||||
</Simplebar> |
</Simplebar> |
||||
</template> |
</template> |
||||
|
|
||||
<style scoped> |
<style scoped> |
||||
:deep(.simplebar-content) { |
:deep(.simplebar-content) { |
||||
height: 100%; |
height: 100%; |
||||
} |
} |
||||
</style> |
</style> |
||||
|
@ -1,3 +1,3 @@ |
|||||
<template> |
<template> |
||||
<div @click="$router.back()">Not Found</div> |
<div @click="$router.back()">Not Found</div> |
||||
</template> |
</template> |
||||
|
@ -1,14 +1,14 @@ |
|||||
<script setup lang="ts"> |
<script setup lang="ts"> |
||||
defineOptions({ |
defineOptions({ |
||||
title: "观山", |
title: "观山", |
||||
bg: "ty", |
bg: "ty", |
||||
}) |
}) |
||||
</script> |
</script> |
||||
|
|
||||
<template> |
<template> |
||||
<div> |
<div> |
||||
<input type="text" /> |
<input type="text" /> |
||||
<input type="text" /> |
<input type="text" /> |
||||
<input type="text" /> |
<input type="text" /> |
||||
</div> |
</div> |
||||
</template> |
</template> |
||||
|
@ -1,37 +1,37 @@ |
|||||
<script setup lang="ts"> |
<script setup lang="ts"> |
||||
definePage({ |
definePage({ |
||||
name: "about", |
name: "about", |
||||
meta: { |
meta: { |
||||
title: "听雨", |
title: "听雨", |
||||
bg: "gs", |
bg: "gs", |
||||
}, |
}, |
||||
}) |
}) |
||||
|
|
||||
// const activeTab = ref(0) |
// const activeTab = ref(0) |
||||
// const TopMenu = computed<any[]>(() => { |
// const TopMenu = computed<any[]>(() => { |
||||
// return [ |
// return [ |
||||
// { key: 0, title: "sada", url: "/setting" }, |
// { key: 0, title: "sada", url: "/setting" }, |
||||
// { key: 1, title: "sdas", url: "/setting/editor" }, |
// { key: 1, title: "sdas", url: "/setting/editor" }, |
||||
// { key: 2, title: "asdas", url: "/setting/update" }, |
// { key: 2, title: "asdas", url: "/setting/update" }, |
||||
// ] |
// ] |
||||
// }) |
// }) |
||||
// const route = useRoute() |
// const route = useRoute() |
||||
// watch( |
// watch( |
||||
// () => route, |
// () => route, |
||||
// route => { |
// route => { |
||||
// for (let i = 0; i < TopMenu.value.length; i++) { |
// for (let i = 0; i < TopMenu.value.length; i++) { |
||||
// const element = TopMenu.value[i] |
// const element = TopMenu.value[i] |
||||
// if (route.path.startsWith(element.url)) { |
// if (route.path.startsWith(element.url)) { |
||||
// activeTab.value = element.key |
// activeTab.value = element.key |
||||
// } |
// } |
||||
// } |
// } |
||||
// }, |
// }, |
||||
// { immediate: true }, |
// { immediate: true }, |
||||
// ) |
// ) |
||||
</script> |
</script> |
||||
<template> |
<template> |
||||
<div> |
<div> |
||||
about |
about |
||||
<!-- <HTab v-model="activeTab" :list="TopMenu"></HTab> --> |
<!-- <HTab v-model="activeTab" :list="TopMenu"></HTab> --> |
||||
</div> |
</div> |
||||
</template> |
</template> |
||||
|
@ -1,143 +1,143 @@ |
|||||
<script setup lang="ts"> |
<script setup lang="ts"> |
||||
import Simplebar from "simplebar-vue" |
import Simplebar from "simplebar-vue" |
||||
import { getAssetsFile } from "@/utils" |
import { getAssetsFile } from "@/utils" |
||||
|
|
||||
const allModules: Record<string, any> = import.meta.glob("./_ui/**/*.vue", { eager: true }) |
const allModules: Record<string, any> = import.meta.glob("./_ui/**/*.vue", { eager: true }) |
||||
let allApp: any[] = [] |
let allApp: any[] = [] |
||||
Object.keys(allModules).forEach(key => { |
Object.keys(allModules).forEach(key => { |
||||
// let [, p] = key.match("./_ui/(.*?).vue")! |
// let [, p] = key.match("./_ui/(.*?).vue")! |
||||
// p = p.replace(/\.vue$/, "") |
// p = p.replace(/\.vue$/, "") |
||||
const m = allModules[key]?.default || allModules[key] |
const m = allModules[key]?.default || allModules[key] |
||||
allApp.push({ |
allApp.push({ |
||||
label: m.title, |
label: m.title, |
||||
bg: m.bg, |
bg: m.bg, |
||||
_sort: m.index ?? 0, |
_sort: m.index ?? 0, |
||||
comp: m, |
comp: m, |
||||
}) |
}) |
||||
}) |
}) |
||||
allApp = allApp.sort((a, b) => (a.index - b.index <= 0 ? 1 : -1)) |
allApp = allApp.sort((a, b) => (a.index - b.index <= 0 ? 1 : -1)) |
||||
|
|
||||
const active = ref(0) |
const active = ref(0) |
||||
// const allApp = [ |
// const allApp = [ |
||||
// { label: "浏览器", comp: defineAsyncComponent(() => import("./_ui/Browser.vue")) }, |
// { label: "浏览器", comp: defineAsyncComponent(() => import("./_ui/Browser.vue")) }, |
||||
// { label: "观山", bg: "gs", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "观山", bg: "gs", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "听雨", bg: "ty", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "听雨", bg: "ty", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "赏月", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "赏月", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "抚琴", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "抚琴", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "望云", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "望云", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "踏雪", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "踏雪", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "卧松", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "卧松", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "泛舟", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "泛舟", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "弈星", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "弈星", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "垂钓", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "垂钓", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "采菊", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "采菊", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "倚栏", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "倚栏", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "望霞", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "望霞", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "枕风", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "枕风", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "沐泉", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "沐泉", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "汲露", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "汲露", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "步竹", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "步竹", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "剪烛", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "剪烛", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "拾阶", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "拾阶", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "撷兰", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "撷兰", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "访柳", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "访柳", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "谒梅", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "谒梅", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "拨荷", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "拨荷", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// { label: "望鹤", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
// { label: "望鹤", comp: defineAsyncComponent(() => import("./_ui/App.vue")) }, |
||||
// ] |
// ] |
||||
|
|
||||
const activeBg = computed(() => { |
const activeBg = computed(() => { |
||||
if (active.value === undefined) return "" |
if (active.value === undefined) return "" |
||||
const value = allApp[active.value]?.bg |
const value = allApp[active.value]?.bg |
||||
return value ? getAssetsFile(`@/assets/images/home/${value}.png`) : "" |
return value ? getAssetsFile(`@/assets/images/home/${value}.png`) : "" |
||||
}) |
}) |
||||
|
|
||||
function onClick(index: number) { |
function onClick(index: number) { |
||||
active.value = index |
active.value = index |
||||
} |
} |
||||
</script> |
</script> |
||||
|
|
||||
<template> |
<template> |
||||
<div h-full flex> |
<div h-full flex> |
||||
<div w="100px" h-full relative max-w="200px" min-w="80px"> |
<div w="100px" h-full relative max-w="200px" min-w="80px"> |
||||
<Simplebar h-full> |
<Simplebar h-full> |
||||
<div |
<div |
||||
v-for="(app, index) in allApp" |
v-for="(app, index) in allApp" |
||||
:key="index" |
:key="index" |
||||
p="8px 10px" |
p="8px 10px" |
||||
text="12px" |
text="12px" |
||||
border |
border |
||||
border-b |
border-b |
||||
h="30px" |
h="30px" |
||||
cursor="pointer" |
cursor="pointer" |
||||
hover:bg-gray-50 |
hover:bg-gray-50 |
||||
class="item" |
class="item" |
||||
transition-all |
transition-all |
||||
:class="{ active: active === index }" |
:class="{ active: active === index }" |
||||
@click="onClick(index)" |
@click="onClick(index)" |
||||
> |
> |
||||
<div class="text" transition-all position="absolute" left="10px">{{ app.label }}</div> |
<div class="text" transition-all position="absolute" left="10px">{{ app.label }}</div> |
||||
</div> |
|
||||
</Simplebar> |
|
||||
<!-- <AdjustLine></AdjustLine> --> |
|
||||
</div> |
|
||||
<div class="content" relative b-l="1px solid #E5E5E5" flex-1 w-0 overflow-auto flex flex-col> |
|
||||
<div v-if="activeBg" class="bg" :style="{ backgroundImage: activeBg ? `url(${activeBg})` : '' }"></div> |
|
||||
<div @click="$router.push('/about')">关于</div> |
|
||||
<component :is="allApp[active].comp" v-if="allApp[active]"></component> |
|
||||
</div> |
</div> |
||||
|
</Simplebar> |
||||
|
<!-- <AdjustLine></AdjustLine> --> |
||||
|
</div> |
||||
|
<div class="content" relative b-l="1px solid #E5E5E5" flex-1 w-0 overflow-auto flex flex-col> |
||||
|
<div v-if="activeBg" class="bg" :style="{ backgroundImage: activeBg ? `url(${activeBg})` : '' }"></div> |
||||
|
<div @click="$router.push('/about')">关于</div> |
||||
|
<component :is="allApp[active].comp" v-if="allApp[active]"></component> |
||||
</div> |
</div> |
||||
|
</div> |
||||
</template> |
</template> |
||||
|
|
||||
<style lang="scss" scoped> |
<style lang="scss" scoped> |
||||
.content { |
.content { |
||||
.bg { |
.bg { |
||||
position: absolute; |
position: absolute; |
||||
left: 0; |
left: 0; |
||||
right: 0; |
right: 0; |
||||
bottom: 0; |
bottom: 0; |
||||
top: 0; |
top: 0; |
||||
background-size: cover; |
background-size: cover; |
||||
background-position: center; |
background-position: center; |
||||
background-repeat: no-repeat; |
background-repeat: no-repeat; |
||||
z-index: -1; |
z-index: -1; |
||||
opacity: 0.1; |
opacity: 0.1; |
||||
// blur(4px) |
// blur(4px) |
||||
filter: brightness(1); |
filter: brightness(1); |
||||
} |
} |
||||
} |
} |
||||
.item { |
.item { |
||||
position: relative; |
position: relative; |
||||
&::before { |
&::before { |
||||
content: ""; |
content: ""; |
||||
position: absolute; |
position: absolute; |
||||
left: 0; |
left: 0; |
||||
top: 0; |
top: 0; |
||||
height: 100%; |
height: 100%; |
||||
width: 6px; |
width: 6px; |
||||
background-color: #f3f4f6; |
background-color: #f3f4f6; |
||||
transition: all linear 300ms; |
transition: all linear 300ms; |
||||
} |
} |
||||
&:hover { |
&:hover { |
||||
&::before { |
&::before { |
||||
width: 30px; |
width: 30px; |
||||
} |
} |
||||
.text { |
.text { |
||||
left: 20px; |
left: 20px; |
||||
} |
} |
||||
} |
} |
||||
&.active { |
&.active { |
||||
@apply: text-black; |
@apply: text-black; |
||||
&::before { |
&::before { |
||||
width: 100%; |
width: 100%; |
||||
} |
} |
||||
.text { |
.text { |
||||
left: 50%; |
left: 50%; |
||||
transform: translateX(-50%); |
transform: translateX(-50%); |
||||
} |
} |
||||
} |
} |
||||
.text { |
.text { |
||||
transition-duration: 300ms; |
transition-duration: 300ms; |
||||
} |
} |
||||
} |
} |
||||
</style> |
</style> |
||||
|
@ -1,31 +1,31 @@ |
|||||
<script setup lang="ts"> |
<script setup lang="ts"> |
||||
definePage({ |
definePage({ |
||||
meta: { |
meta: { |
||||
home: true, |
home: true, |
||||
}, |
}, |
||||
}) |
}) |
||||
|
|
||||
const state = reactive({ |
const state = reactive({ |
||||
content: "", |
content: "", |
||||
name: "aaa.ts", |
name: "aaa.ts", |
||||
}) |
}) |
||||
</script> |
</script> |
||||
|
|
||||
<template> |
<template> |
||||
<div class="locale-changer"> |
<div class="locale-changer"> |
||||
<select v-model="$i18n.locale"> |
<select v-model="$i18n.locale"> |
||||
<option v-for="locale in $i18n.availableLocales" :key="`locale-${locale}`" :value="locale">{{ locale }}</option> |
<option v-for="locale in $i18n.availableLocales" :key="`locale-${locale}`" :value="locale">{{ locale }}</option> |
||||
</select> |
</select> |
||||
</div> |
</div> |
||||
<div @click="$router.push('/about')"> |
<div @click="$router.push('/about')"> |
||||
<span>跳转 about</span> |
<span>跳转 about</span> |
||||
</div> |
</div> |
||||
<div @click="$router.push('/browser')"> |
<div @click="$router.push('/browser')"> |
||||
<span>跳转 browser</span> |
<span>跳转 browser</span> |
||||
</div> |
</div> |
||||
<input v-model="state.content" /> |
<input v-model="state.content" /> |
||||
<CodeEditor v-model="state.content" style="height: 400px" :name="state.name" placeholder="输入代码"></CodeEditor> |
<CodeEditor v-model="state.content" style="height: 400px" :name="state.name" placeholder="输入代码"></CodeEditor> |
||||
<div v-for="i in 1000" :key="i"> |
<div v-for="i in 1000" :key="i"> |
||||
<span>{{ i }}</span> |
<span>{{ i }}</span> |
||||
</div> |
</div> |
||||
</template> |
</template> |
||||
|
@ -1,5 +1,5 @@ |
|||||
import type { AttributifyAttributes } from "@unocss/preset-attributify" |
import type { AttributifyAttributes } from "@unocss/preset-attributify" |
||||
|
|
||||
declare module "@vue/runtime-dom" { |
declare module "@vue/runtime-dom" { |
||||
interface HTMLAttributes extends AttributifyAttributes {} |
interface HTMLAttributes extends AttributifyAttributes {} |
||||
} |
} |
||||
|
@ -1,21 +1,20 @@ |
|||||
|
|
||||
type Api = { |
type Api = { |
||||
call: (command: string, ...args: any[]) => Promise<any> |
call: (command: string, ...args: any[]) => Promise<any> |
||||
callLong: (command: string, ...args: any[]) => Promise<any> |
callLong: (command: string, ...args: any[]) => Promise<any> |
||||
callSync: (command: string, ...args: any[]) => any |
callSync: (command: string, ...args: any[]) => any |
||||
send: (command: string, ...argu: any[]) => any |
send: (command: string, ...argu: any[]) => any |
||||
sendSync: (command: string, ...argu: any[]) => any |
sendSync: (command: string, ...argu: any[]) => any |
||||
on: <T extends string>(command: T, cb: (event: IpcRendererEvent, ...args: any[]) => void) => () => void |
on: <T extends string>(command: T, cb: (event: IpcRendererEvent, ...args: any[]) => void) => () => void |
||||
once: (command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) => () => void |
once: (command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) => () => void |
||||
off: (command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) => void |
off: (command: string, cb: (event: IpcRendererEvent, ...args: any[]) => void) => void |
||||
offAll: (command: string) => void |
offAll: (command: string) => void |
||||
popupMenu: (options: IPopupMenuOption) => void |
popupMenu: (options: IPopupMenuOption) => void |
||||
} |
} |
||||
|
|
||||
declare const electron: typeof import("@electron-toolkit/preload").electronAPI |
declare const electron: typeof import("@electron-toolkit/preload").electronAPI |
||||
declare const api: Api |
declare const api: Api |
||||
|
|
||||
interface Window { |
interface Window { |
||||
electron: typeof import("@electron-toolkit/preload").electronAPI |
electron: typeof import("@electron-toolkit/preload").electronAPI |
||||
api: Api |
api: Api |
||||
} |
} |
||||
|
@ -1,12 +1,12 @@ |
|||||
import type { PopupOptions } from "electron" |
import type { PopupOptions } from "electron" |
||||
|
|
||||
export interface IMenuItemOption extends Electron.MenuItemConstructorOptions { |
export interface IMenuItemOption extends Electron.MenuItemConstructorOptions { |
||||
// 参见:https://www.electronjs.org/docs/api/menu-item
|
// 参见:https://www.electronjs.org/docs/api/menu-item
|
||||
_click_evt?: string |
_click_evt?: string |
||||
} |
} |
||||
|
|
||||
export interface IPopupMenuOption { |
export interface IPopupMenuOption { |
||||
menu_id: string |
menu_id: string |
||||
items: IMenuItemOption[] |
items: IMenuItemOption[] |
||||
popupOptions?: PopupOptions |
popupOptions?: PopupOptions |
||||
} |
} |
||||
|
@ -1,4 +1,4 @@ |
|||||
import { defineConfig } from "unplugin-vue-macros" |
import { defineConfig } from "unplugin-vue-macros" |
||||
export default defineConfig({ |
export default defineConfig({ |
||||
// 选项
|
// 选项
|
||||
}) |
}) |
||||
|
@ -1,15 +1,11 @@ |
|||||
|
|
||||
插件化: |
插件化: |
||||
|
|
||||
https://rubickcenter.github.io/docs/core/index.html#%E5%9F%BA%E4%BA%8E-browserview-%E5%AE%9E%E7%8E%B0%E6%8F%92%E4%BB%B6%E5%8C%96%E8%83%BD%E5%8A%9B |
https://rubickcenter.github.io/docs/core/index.html#%E5%9F%BA%E4%BA%8E-browserview-%E5%AE%9E%E7%8E%B0%E6%8F%92%E4%BB%B6%E5%8C%96%E8%83%BD%E5%8A%9B |
||||
|
|
||||
|
|
||||
electron+vue虚拟桌面开发遇坑之透明窗口鼠标穿透 |
electron+vue虚拟桌面开发遇坑之透明窗口鼠标穿透 |
||||
|
|
||||
|
|
||||
https://blog.csdn.net/weixin_42421494/article/details/102800491 |
https://blog.csdn.net/weixin_42421494/article/details/102800491 |
||||
|
|
||||
|
|
||||
截图 |
截图 |
||||
|
|
||||
https://zhuanlan.zhihu.com/p/46043613?from_voters_page=true |
https://zhuanlan.zhihu.com/p/46043613?from_voters_page=true |
||||
|
Loading…
Reference in new issue