Compare commits
72 Commits
dev
...
395c547e85
| Author | SHA1 | Date | |
|---|---|---|---|
|
395c547e85
|
|||
|
43779227c2
|
|||
|
69e572ae23
|
|||
|
df98339838
|
|||
|
7d3ec39da3
|
|||
|
32686848d2
|
|||
|
1df704f65e
|
|||
|
c5637d5e7f
|
|||
|
a7577373ef
|
|||
|
3dc434a6ac
|
|||
|
898075cf6e
|
|||
|
58bb5eb262
|
|||
|
31a458416d
|
|||
|
2b1bd719ad
|
|||
|
606064b1fb
|
|||
|
19113a56af
|
|||
|
6dd1f51c89
|
|||
|
22e2e15db8
|
|||
|
4f55364f65
|
|||
|
d0ddb276c0
|
|||
|
8f22084d10
|
|||
|
30ec21a71e
|
|||
|
513f66418a
|
|||
|
580249a85c
|
|||
|
1ef2db5f47
|
|||
|
d677aeed07
|
|||
|
361a5c923d
|
|||
|
c87e697ea6
|
|||
|
058984ad85
|
|||
|
3ddd4c88e0
|
|||
|
c64777e759
|
|||
|
d918e527a0
|
|||
|
b51fdb11f2
|
|||
|
6e2012cd7a
|
|||
|
cab0c9d879
|
|||
|
a5ca0be53c
|
|||
|
e5bc568d23
|
|||
|
2176c0ce2a
|
|||
|
ac2e3c8e6e
|
|||
|
63c76440a6
|
|||
|
7f034db314
|
|||
|
d326c8b8e5
|
|||
|
ea68945df1
|
|||
|
59eef73895
|
|||
|
2f9c01981b
|
|||
|
5250b662f5
|
|||
|
77166831bf
|
|||
|
9152ec8dfb
|
|||
|
cfddc85302
|
|||
|
76470d741f
|
|||
|
62a70f92ae
|
|||
|
5a12020d69
|
|||
|
71faca682e
|
|||
|
cd1802b5d6
|
|||
|
0aad1ac966
|
|||
|
e209bea2f3
|
|||
|
c65c548ecc
|
|||
|
9b96c7dd62
|
|||
|
a2d0afb38c
|
|||
|
48c9ec148a
|
|||
|
07602bebc8
|
|||
|
5c2a77db30
|
|||
|
7d5265ecda
|
|||
|
058746702d
|
|||
|
84d2e6c3f8
|
|||
|
61d3bb21ad
|
|||
|
8730513340
|
|||
|
6f53a867c3
|
|||
|
c098368a79
|
|||
|
de3d1831d0
|
|||
|
715806b85d
|
|||
|
0b314e4ea9
|
@@ -1,9 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
VITE_API_URL=http://localhost:8080
|
VITE_PLATFORM=DESKTOP
|
||||||
|
VITE_DESKTOP_PROTOCOL=dev-oxygen-desktop
|
||||||
|
VITE_APP_PROTOCOL=dev-oxygen-app
|
||||||
|
VITE_UI_URL=${DEV_UI_URL}
|
||||||
|
VITE_API_URL=${DEV_API_URL}
|
||||||
VITE_API_TOKEN_URL=${VITE_API_URL}/token
|
VITE_API_TOKEN_URL=${VITE_API_URL}/token
|
||||||
VITE_TURNSTILE_SITE_KEY=${TURNSTILE_SITE_KEY}
|
VITE_TURNSTILE_SITE_KEY=${TURNSTILE_SITE_KEY}
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
VITE_PLATFORM=DESKTOP
|
||||||
|
VITE_DESKTOP_PROTOCOL=oxygen-desktop
|
||||||
|
VITE_APP_PROTOCOL=oxygen-app
|
||||||
|
VITE_UI_URL=${PRODUCT_UI_URL}
|
||||||
VITE_API_URL=${PRODUCT_API_URL}
|
VITE_API_URL=${PRODUCT_API_URL}
|
||||||
VITE_API_TOKEN_URL=${VITE_API_URL}/token
|
VITE_API_TOKEN_URL=${VITE_API_URL}/token
|
||||||
VITE_TURNSTILE_SITE_KEY=${TURNSTILE_SITE_KEY}
|
VITE_TURNSTILE_SITE_KEY=${TURNSTILE_SITE_KEY}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
VITE_PLATFORM=DESKTOP
|
||||||
|
VITE_DESKTOP_PROTOCOL=test-oxygen-desktop
|
||||||
|
VITE_APP_PROTOCOL=test-oxygen-app
|
||||||
|
VITE_UI_URL=${TEST_UI_URL}
|
||||||
VITE_API_URL=${TEST_API_URL}
|
VITE_API_URL=${TEST_API_URL}
|
||||||
VITE_API_TOKEN_URL=${VITE_API_URL}/token
|
VITE_API_TOKEN_URL=${VITE_API_URL}/token
|
||||||
VITE_TURNSTILE_SITE_KEY=${TURNSTILE_SITE_KEY}
|
VITE_TURNSTILE_SITE_KEY=${TURNSTILE_SITE_KEY}
|
||||||
@@ -34,6 +34,7 @@ module.exports = {
|
|||||||
'warn',
|
'warn',
|
||||||
{ allowConstantExport: true }
|
{ allowConstantExport: true }
|
||||||
],
|
],
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off'
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'react-hooks/exhaustive-deps': 'off',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
build/icon.icns
BIN
build/icon.ico
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 264 KiB |
BIN
build/icon.png
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 978 B After Width: | Height: | Size: 978 B |
@@ -149,7 +149,10 @@ const matchComponents: IMatcher[] = [
|
|||||||
pattern: /^Mentions/,
|
pattern: /^Mentions/,
|
||||||
styleDir: 'mentions'
|
styleDir: 'mentions'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
pattern: /^QRCode/,
|
||||||
|
styleDir: 'qr-code'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
pattern: /^Step/,
|
pattern: /^Step/,
|
||||||
styleDir: 'steps'
|
styleDir: 'steps'
|
||||||
@@ -337,6 +340,7 @@ const primitiveNames = [
|
|||||||
'Rate',
|
'Rate',
|
||||||
'Result',
|
'Result',
|
||||||
'Row',
|
'Row',
|
||||||
|
'QRCode',
|
||||||
'Select',
|
'Select',
|
||||||
'SelectOptGroup',
|
'SelectOptGroup',
|
||||||
'SelectOption',
|
'SelectOption',
|
||||||
@@ -417,7 +421,6 @@ export const AntDesignResolver = (options: AntDesignResolverOptions = {}): Compo
|
|||||||
sideEffects: getSideEffects(importName, options)
|
sideEffects: getSideEffects(importName, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
appId: com.electron.app
|
productName: OxygenDesktop
|
||||||
productName: oxygen-desktop
|
copyright: Copyright © 2017-2024 FatWeb. All rights reserved.
|
||||||
|
appId: top.fatweb.oxygen.desktop
|
||||||
|
artifactName: ${name}-${version}-${os}-${arch}.${ext}
|
||||||
directories:
|
directories:
|
||||||
buildResources: build
|
buildResources: build
|
||||||
files:
|
files:
|
||||||
@@ -8,35 +10,78 @@ files:
|
|||||||
- '!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}'
|
||||||
asarUnpack:
|
asarUnpack:
|
||||||
- resources/**
|
- resources/**
|
||||||
win:
|
win:
|
||||||
executableName: oxygen-desktop
|
target:
|
||||||
|
- target: nsis
|
||||||
|
arch:
|
||||||
|
- x64
|
||||||
|
- ia32
|
||||||
|
- arm64
|
||||||
|
- target: msi
|
||||||
|
arch:
|
||||||
|
- x64
|
||||||
|
- ia32
|
||||||
|
- arm64
|
||||||
|
- target: zip
|
||||||
|
arch:
|
||||||
|
- x64
|
||||||
|
- ia32
|
||||||
|
- arm64
|
||||||
nsis:
|
nsis:
|
||||||
artifactName: ${name}-${version}-setup.${ext}
|
oneClick: false
|
||||||
shortcutName: ${productName}
|
allowToChangeInstallationDirectory: true
|
||||||
uninstallDisplayName: ${productName}
|
artifactName: ${name}-${version}-${os}-${arch}-setup.${ext}
|
||||||
createDesktopShortcut: always
|
shortcutName: Oxygen Desktop
|
||||||
|
createDesktopShortcut: true
|
||||||
|
msi:
|
||||||
|
oneClick: false
|
||||||
|
shortcutName: Oxygen Desktop
|
||||||
mac:
|
mac:
|
||||||
|
category: public.app-category.productivity
|
||||||
|
target:
|
||||||
|
- default
|
||||||
entitlementsInherit: build/entitlements.mac.plist
|
entitlementsInherit: build/entitlements.mac.plist
|
||||||
|
darkModeSupport: true
|
||||||
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:
|
|
||||||
artifactName: ${name}-${version}.${ext}
|
|
||||||
linux:
|
linux:
|
||||||
target:
|
target:
|
||||||
- AppImage
|
- target: AppImage
|
||||||
- snap
|
arch:
|
||||||
- deb
|
- x64
|
||||||
maintainer: electronjs.org
|
- arm64
|
||||||
|
- armv7l
|
||||||
|
- target: deb
|
||||||
|
arch:
|
||||||
|
- x64
|
||||||
|
- arm64
|
||||||
|
- armv7l
|
||||||
|
- target: rpm
|
||||||
|
arch:
|
||||||
|
- x64
|
||||||
|
- arm64
|
||||||
|
- armv7l
|
||||||
|
- target: pacman
|
||||||
|
arch:
|
||||||
|
- x64
|
||||||
|
- arm64
|
||||||
|
- armv7l
|
||||||
|
- target: tar.gz
|
||||||
|
arch:
|
||||||
|
- x64
|
||||||
|
- arm64
|
||||||
|
- armv7l
|
||||||
category: Utility
|
category: Utility
|
||||||
appImage:
|
desktop:
|
||||||
artifactName: ${name}-${version}.${ext}
|
Name: Oxygen Desktop
|
||||||
|
Comment: Oxygen Toolbox multi-platform desktop version
|
||||||
npmRebuild: false
|
npmRebuild: false
|
||||||
publish:
|
publish:
|
||||||
provider: generic
|
provider: generic
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { resolve } from 'path'
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
|
||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
import AutoImport from 'unplugin-auto-import/vite'
|
import AutoImport from 'unplugin-auto-import/vite'
|
||||||
@@ -10,7 +10,7 @@ import { FileSystemIconLoader } from 'unplugin-icons/loaders'
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
main: {
|
main: {
|
||||||
plugins: [externalizeDepsPlugin()]
|
plugins: [externalizeDepsPlugin({ exclude: ['electron-store'] })]
|
||||||
},
|
},
|
||||||
preload: {
|
preload: {
|
||||||
plugins: [externalizeDepsPlugin()]
|
plugins: [externalizeDepsPlugin()]
|
||||||
@@ -82,7 +82,7 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': resolve('src/renderer/src')
|
'@': fileURLToPath(new URL('./src/renderer/src', import.meta.url))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5471
package-lock.json
generated
110
package.json
@@ -1,87 +1,101 @@
|
|||||||
{
|
{
|
||||||
"name": "oxygen-desktop",
|
"name": "oxygen-desktop",
|
||||||
"version": "1.0.0",
|
"private": true,
|
||||||
"description": "An Electron application with React and TypeScript",
|
"version": "1.0.0-SNAPSHOT",
|
||||||
|
"description": "Oxygen Toolbox multi-platform desktop version",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
"author": "example.com",
|
"author": {
|
||||||
"homepage": "https://electron-vite.org",
|
"name": "FatttSnake",
|
||||||
|
"email": "fatttsnake@fatweb.top",
|
||||||
|
"url": "https://fatweb.top"
|
||||||
|
},
|
||||||
|
"homepage": "https://tool.fatweb.top",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "electron-vite dev",
|
"dev": "electron-vite dev --sourcemap --remote-debugging-port=9000",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
||||||
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
|
"typecheck": "tsc",
|
||||||
"typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false",
|
"clean": "rimraf out dist .eslintrc-auto-import.json src/renderer/auto-imports.d.ts",
|
||||||
"typecheck": "npm run typecheck:node && npm run typecheck:web",
|
|
||||||
"start": "electron-vite preview",
|
"start": "electron-vite preview",
|
||||||
"build": "electron-vite build && npm run typecheck",
|
"build": "electron-vite build && npm run typecheck",
|
||||||
|
"build-test": "electron-vite build --mode testing && npm run typecheck",
|
||||||
"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-test:unpack": "npm run build-test && electron-builder --dir",
|
||||||
"build:win": "npm run build && electron-builder --win",
|
"build:win": "npm run build && electron-builder --win",
|
||||||
"build:mac": "electron-vite build && electron-builder --mac",
|
"build-test:win": "npm run build-test && electron-builder --win",
|
||||||
"build:linux": "electron-vite build && electron-builder --linux"
|
"build:mac": "npm run build && electron-builder --mac",
|
||||||
|
"build-test:mac": "npm run build-test && electron-builder --mac",
|
||||||
|
"build:linux": "npm run build && electron-builder --linux",
|
||||||
|
"build-test:linux": "npm run build-test && electron-builder --linux"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-toolkit/preload": "^3.0.0",
|
"@electron-toolkit/preload": "^3.0.1",
|
||||||
"@electron-toolkit/utils": "^3.0.0",
|
"@electron-toolkit/utils": "^3.0.0",
|
||||||
"electron-updater": "^6.1.7"
|
"electron-store": "^9.0.0",
|
||||||
|
"electron-updater": "^6.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ant-design/icons": "^5.3.1",
|
"@ant-design/icons": "^5.3.7",
|
||||||
|
"@dnd-kit/core": "^6.1.0",
|
||||||
|
"@dnd-kit/sortable": "^8.0.0",
|
||||||
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
|
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
|
||||||
"@electron-toolkit/eslint-config-ts": "^1.0.1",
|
"@electron-toolkit/eslint-config-ts": "^2.0.0",
|
||||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||||
"@marsidev/react-turnstile": "^0.5.3",
|
"@marsidev/react-turnstile": "^0.7.1",
|
||||||
"@monaco-editor/react": "^4.6.0",
|
"@monaco-editor/react": "^4.6.0",
|
||||||
"@svgr/core": "^8.1.0",
|
"@svgr/core": "^8.1.0",
|
||||||
"@svgr/plugin-jsx": "^8.1.0",
|
"@svgr/plugin-jsx": "^8.1.0",
|
||||||
"@types/jsdom": "^21.1.6",
|
"@types/jsdom": "^21.1.7",
|
||||||
"@types/lodash": "^4.17.0",
|
"@types/lodash": "^4.17.5",
|
||||||
"@types/node": "^18.19.9",
|
"@types/node": "^20.14.2",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.2.18",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
"@typescript-eslint/eslint-plugin": "^7.13.0",
|
||||||
"@typescript-eslint/parser": "^6.21.0",
|
"@typescript-eslint/parser": "^7.13.0",
|
||||||
"@typescript/ata": "^0.9.4",
|
"@typescript/ata": "^0.9.6",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.3.1",
|
||||||
"antd": "^5.15.2",
|
"antd": "^5.18.1",
|
||||||
"axios": "^1.6.7",
|
"axios": "^1.7.2",
|
||||||
"dayjs": "^1.11.10",
|
"custom-protocol-check": "^1.4.0",
|
||||||
|
"dayjs": "^1.11.11",
|
||||||
"echarts": "^5.5.0",
|
"echarts": "^5.5.0",
|
||||||
"electron": "^28.2.0",
|
"electron": "^31.0.1",
|
||||||
"electron-builder": "^24.9.1",
|
"electron-builder": "^24.13.3",
|
||||||
"electron-vite": "^2.0.0",
|
"electron-vite": "^2.2.0",
|
||||||
"esbuild-wasm": "^0.20.1",
|
"esbuild-wasm": "^0.21.5",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-config-love": "^52.0.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-config-standard-with-typescript": "^43.0.1",
|
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-promise": "^6.1.1",
|
"eslint-plugin-promise": "^6.2.0",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"eslint-plugin-react-refresh": "^0.4.5",
|
"eslint-plugin-react-refresh": "^0.4.7",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"fflate": "^0.8.2",
|
"fflate": "^0.8.2",
|
||||||
"jsdom": "^24.0.0",
|
"jsdom": "^24.1.0",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"match-sorter": "^6.3.4",
|
"match-sorter": "^6.3.4",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"monaco-editor": "^0.47.0",
|
"monaco-editor": "^0.49.0",
|
||||||
"monaco-jsx-syntax-highlight": "^1.2.0",
|
"monaco-jsx-syntax-highlight": "^1.2.0",
|
||||||
"prettier": "^3.2.4",
|
"prettier": "^3.3.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.3.1",
|
||||||
"react-draggable": "^4.4.6",
|
"react-draggable": "^4.4.6",
|
||||||
"react-router": "^6.22.3",
|
"react-router": "^6.23.1",
|
||||||
"react-router-dom": "^6.22.3",
|
"react-router-dom": "^6.23.1",
|
||||||
"sass": "^1.71.1",
|
"sass": "^1.77.5",
|
||||||
"size-sensor": "^1.0.2",
|
"size-sensor": "^1.0.2",
|
||||||
"stylelint-config-prettier": "^9.0.5",
|
"stylelint-config-prettier": "^9.0.5",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.4.5",
|
||||||
"unplugin-auto-import": "^0.17.5",
|
"unplugin-auto-import": "^0.17.6",
|
||||||
"unplugin-icons": "^0.18.5",
|
"unplugin-icons": "^0.19.0",
|
||||||
"vanilla-tilt": "^1.8.1",
|
"vanilla-tilt": "^1.8.1",
|
||||||
"vite": "^5.0.12"
|
"vite": "^5.2.13"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 260 KiB |
|
Before Width: | Height: | Size: 43 KiB |
21
src/main/dataProcess.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import Store, { Schema } from 'electron-store'
|
||||||
|
import { ipcMain } from 'electron'
|
||||||
|
|
||||||
|
const schema: Schema<StoreSchema> = {
|
||||||
|
installedTools: {
|
||||||
|
default: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = new Store<StoreSchema>({ schema })
|
||||||
|
|
||||||
|
ipcMain.handle('store:installTool', (_, value: Record<string, Record<Platform, ToolVo>>) => {
|
||||||
|
const installedTools = store.get('installedTools')
|
||||||
|
|
||||||
|
store.set('installedTools', { ...installedTools, ...value })
|
||||||
|
return store.get('installedTools')
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('store:getInstalledTool', () => {
|
||||||
|
return store.get('installedTools')
|
||||||
|
})
|
||||||
93
src/main/global.d.ts
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
type Platform = 'WEB' | 'DESKTOP' | 'ANDROID'
|
||||||
|
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_PLATFORM: Platform
|
||||||
|
readonly VITE_DESKTOP_PROTOCOL: string
|
||||||
|
readonly VITE_APP_PROTOCOL: string
|
||||||
|
readonly VITE_UI_URL: string
|
||||||
|
readonly VITE_API_URL: string
|
||||||
|
readonly VITE_API_TOKEN_URL: string
|
||||||
|
readonly VITE_TURNSTILE_SITE_KEY: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StoreSchema {
|
||||||
|
installedTools: Record<string, Record<Platform, ToolVo>>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToolVo {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
toolId: string
|
||||||
|
icon: string
|
||||||
|
platform: Platform
|
||||||
|
description: string
|
||||||
|
base: ToolBaseVo
|
||||||
|
author: UserWithInfoVo
|
||||||
|
ver: string
|
||||||
|
keywords: string[]
|
||||||
|
categories: ToolCategoryVo[]
|
||||||
|
source: ToolDataVo
|
||||||
|
dist: ToolDataVo
|
||||||
|
entryPoint: string
|
||||||
|
publish: string
|
||||||
|
review: 'NONE' | 'PROCESSING' | 'PASS' | 'REJECT'
|
||||||
|
createTime: string
|
||||||
|
updateTime: string
|
||||||
|
favorite: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToolBaseVo {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
source: ToolDataVo
|
||||||
|
dist: ToolDataVo
|
||||||
|
platform: Platform
|
||||||
|
compiled: boolean
|
||||||
|
createTime: string
|
||||||
|
updateTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToolDataVo {
|
||||||
|
id: string
|
||||||
|
data?: string
|
||||||
|
createTime?: string
|
||||||
|
updateTime?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserWithInfoVo {
|
||||||
|
id: string
|
||||||
|
username: string
|
||||||
|
twoFactor: boolean
|
||||||
|
verified: boolean
|
||||||
|
locking: boolean
|
||||||
|
expiration: string
|
||||||
|
credentialsExpiration: string
|
||||||
|
enable: boolean
|
||||||
|
currentLoginTime: string
|
||||||
|
currentLoginIp: string
|
||||||
|
lastLoginTime: string
|
||||||
|
lastLoginIp: string
|
||||||
|
createTime: string
|
||||||
|
updateTime: string
|
||||||
|
userInfo: UserInfoVo
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserInfoVo {
|
||||||
|
id: string
|
||||||
|
userId: string
|
||||||
|
nickname: string
|
||||||
|
avatar: string
|
||||||
|
email: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToolCategoryVo {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
enable: boolean
|
||||||
|
createTime: string
|
||||||
|
updateTime: string
|
||||||
|
}
|
||||||
@@ -1,11 +1,70 @@
|
|||||||
import { app, shell, BrowserWindow, ipcMain } from 'electron'
|
import { app, shell, BrowserWindow, ipcMain, protocol, net } from 'electron'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
||||||
import icon from '../../resources/logo.ico?asset'
|
import icon from '../../build/icon.ico?asset'
|
||||||
|
import path from 'node:path'
|
||||||
|
import url from 'node:url'
|
||||||
|
|
||||||
function createWindow(): void {
|
let mainWindow: BrowserWindow
|
||||||
|
|
||||||
|
// Application singleton execution
|
||||||
|
if (!app.requestSingleInstanceLock()) {
|
||||||
|
app.quit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register protocol client
|
||||||
|
const args: string[] = []
|
||||||
|
if (!app.isPackaged) {
|
||||||
|
args.push(path.resolve(process.argv[1]))
|
||||||
|
}
|
||||||
|
args.push('--')
|
||||||
|
app.setAsDefaultProtocolClient(import.meta.env.VITE_DESKTOP_PROTOCOL, process.execPath, args)
|
||||||
|
// app.removeAsDefaultProtocolClient(import.meta.env.VITE_DESKTOP_PROTOCOL, process.execPath, args)
|
||||||
|
|
||||||
|
const handleArgv = (argv: string[]) => {
|
||||||
|
const prefix = `${import.meta.env.VITE_DESKTOP_PROTOCOL}:`
|
||||||
|
const offset = app.isPackaged ? 1 : 2
|
||||||
|
const url = argv.find((arg, index) => index >= offset && arg.startsWith(prefix))
|
||||||
|
if (url) {
|
||||||
|
handleUrl(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUrl = (url: string) => {
|
||||||
|
const { hostname, pathname } = new URL(url)
|
||||||
|
if (hostname === 'openurl' && mainWindow) {
|
||||||
|
mainWindow.webContents.send('open-url', pathname)
|
||||||
|
mainWindow.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows
|
||||||
|
handleArgv(process.argv)
|
||||||
|
app.on('second-instance', (_, argv) => {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
handleArgv(argv)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// macOS
|
||||||
|
app.on('open-url', (_, argv) => {
|
||||||
|
handleUrl(argv)
|
||||||
|
})
|
||||||
|
|
||||||
|
protocol.registerSchemesAsPrivileged([
|
||||||
|
{
|
||||||
|
scheme: 'local',
|
||||||
|
privileges: {
|
||||||
|
standard: true,
|
||||||
|
secure: true,
|
||||||
|
supportFetchAPI: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const createWindow = () => {
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
const mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
width: 900,
|
width: 900,
|
||||||
height: 670,
|
height: 670,
|
||||||
show: false,
|
show: false,
|
||||||
@@ -32,7 +91,10 @@ function createWindow(): void {
|
|||||||
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
||||||
void mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
|
void mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
|
||||||
} else {
|
} else {
|
||||||
void mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
// void mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
||||||
|
void mainWindow.loadURL(
|
||||||
|
`local://oxygen.fatweb.top/${join(__dirname, '../renderer/index.html')}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +102,17 @@ function createWindow(): void {
|
|||||||
// initialization and is ready to create browser windows.
|
// initialization and is ready to create browser windows.
|
||||||
// Some APIs can only be used after this event occurs.
|
// Some APIs can only be used after this event occurs.
|
||||||
void app.whenReady().then(() => {
|
void app.whenReady().then(() => {
|
||||||
|
protocol.handle('local', (request) => {
|
||||||
|
const { host } = new URL(request.url)
|
||||||
|
if (host === 'oxygen.fatweb.top') {
|
||||||
|
const filePath = request.url.slice('local://oxygen.fatweb.top/'.length)
|
||||||
|
return net.fetch(url.pathToFileURL(filePath).toString())
|
||||||
|
} else {
|
||||||
|
const filePath = request.url.slice('local://'.length)
|
||||||
|
return net.fetch(url.pathToFileURL(filePath).toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Set app user model id for windows
|
// Set app user model id for windows
|
||||||
electronApp.setAppUserModelId('top.fatweb')
|
electronApp.setAppUserModelId('top.fatweb')
|
||||||
|
|
||||||
@@ -73,3 +146,4 @@ app.on('window-all-closed', () => {
|
|||||||
|
|
||||||
// In this file you can include the rest of your app's specific main process
|
// In this file you can include the rest of your app's specific main process
|
||||||
// code. You can also put them in separate files and require them here.
|
// code. You can also put them in separate files and require them here.
|
||||||
|
import './dataProcess'
|
||||||
|
|||||||
@@ -1,23 +1,27 @@
|
|||||||
import { contextBridge, Notification } from 'electron'
|
import { contextBridge, Notification, ipcRenderer } from 'electron'
|
||||||
import { electronAPI } from '@electron-toolkit/preload'
|
import { electronAPI } from '@electron-toolkit/preload'
|
||||||
|
|
||||||
// Custom APIs for renderer
|
// Custom APIs for renderer
|
||||||
const api = {}
|
const api = {
|
||||||
|
installTool: (newTools: Record<string, Record<Platform, ToolVo>>) =>
|
||||||
|
ipcRenderer.invoke('store:installTool', newTools),
|
||||||
|
getInstalledTool: () => ipcRenderer.invoke('store:getInstalledTool')
|
||||||
|
}
|
||||||
|
|
||||||
// Use `contextBridge` APIs to expose Electron APIs to
|
// Use `contextBridge` APIs to expose Electron APIs to
|
||||||
// renderer only if context isolation is enabled, otherwise
|
// renderer only if context isolation is enabled, otherwise
|
||||||
// just add to the DOM global.
|
// just add to the DOM global.
|
||||||
if (process.contextIsolated) {
|
if (process.contextIsolated) {
|
||||||
try {
|
try {
|
||||||
contextBridge.exposeInMainWorld('electron', electronAPI)
|
contextBridge.exposeInMainWorld('electronAPI', electronAPI)
|
||||||
contextBridge.exposeInMainWorld('api', api)
|
contextBridge.exposeInMainWorld('api', api)
|
||||||
contextBridge.exposeInMainWorld('notification', Notification)
|
contextBridge.exposeInMainWorld('Notification', Notification)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// @ts-expect-error (define in dts)
|
// @ts-expect-error (define in dts)
|
||||||
window.electron = electronAPI
|
window.electronAPI = electronAPI
|
||||||
// @ts-expect-error (define in dts)
|
// @ts-expect-error (define in dts)
|
||||||
window.api = api
|
window.api = api
|
||||||
// @ts-expect-error (define in dts)
|
// @ts-expect-error (define in dts)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { getRedirectUrl } from '@/util/route'
|
|||||||
import { getLoginStatus, getVerifyStatus_async } from '@/util/auth'
|
import { getLoginStatus, getVerifyStatus_async } from '@/util/auth'
|
||||||
|
|
||||||
const AuthRoute = () => {
|
const AuthRoute = () => {
|
||||||
|
const navigate = useNavigate()
|
||||||
const [searchParams] = useSearchParams()
|
const [searchParams] = useSearchParams()
|
||||||
const matches = useMatches()
|
const matches = useMatches()
|
||||||
const lastMatch = matches.reduce((_, second) => second)
|
const lastMatch = matches.reduce((_, second) => second)
|
||||||
@@ -12,6 +13,12 @@ const AuthRoute = () => {
|
|||||||
const isLogin = getLoginStatus()
|
const isLogin = getLoginStatus()
|
||||||
const isVerify = getVerifyStatus_async()
|
const isVerify = getVerifyStatus_async()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.electronAPI.ipcRenderer.on('open-url', (_, url: string) => {
|
||||||
|
navigate(url)
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
document.title = `${handle?.titlePrefix ?? ''}${
|
document.title = `${handle?.titlePrefix ?? ''}${
|
||||||
handle?.title ? handle?.title : PRODUCTION_NAME
|
handle?.title ? handle?.title : PRODUCTION_NAME
|
||||||
|
|||||||
@@ -14,8 +14,7 @@
|
|||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
|
|
||||||
.hide-scrollbar-content {
|
.hide-scrollbar-content {
|
||||||
display: inline-block;
|
min-width: 100%;
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +67,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.vertical-scrollbar {
|
.vertical-scrollbar {
|
||||||
padding: 12px 2px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -80,7 +78,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.horizontal-scrollbar {
|
.horizontal-scrollbar {
|
||||||
padding: 4px 12px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
font-size: constants.$SIZE_ICON_SM;
|
font-size: constants.$SIZE_ICON_SM;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
transform: rotateZ(180deg);
|
transform: rotateZ(180deg);
|
||||||
transition: all .3s;
|
transition: all .3s;
|
||||||
@@ -55,16 +56,18 @@
|
|||||||
.scroll {
|
.scroll {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
> li {
|
> li, > div > li {
|
||||||
|
padding: 2px 14px;
|
||||||
|
|
||||||
&.item {
|
&.item {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 4px 14px;
|
font-size: 1rem;
|
||||||
font-size: 1.4em;
|
|
||||||
|
|
||||||
>.menu-bt {
|
> .menu-bt {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@@ -78,6 +81,10 @@
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
font-size: constants.$SIZE_ICON_SM;
|
font-size: constants.$SIZE_ICON_SM;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@@ -86,15 +93,24 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
background-color: constants.$origin-color;
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
|
width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: constants.$origin-color;
|
color: constants.$origin-color;
|
||||||
background-color: constants.$main-color !important;
|
background-color: constants.$main-color;
|
||||||
|
|
||||||
|
img {
|
||||||
|
filter: drop-shadow(1000px 0 0 constants.$origin-color);
|
||||||
|
transform: translate(-1000px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +118,7 @@
|
|||||||
.submenu {
|
.submenu {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
padding-left: 20px;
|
padding-left: 10px;
|
||||||
z-index: 10000;
|
z-index: 10000;
|
||||||
animation: 0.1s ease forwards;
|
animation: 0.1s ease forwards;
|
||||||
@include mixins.unique-keyframes {
|
@include mixins.unique-keyframes {
|
||||||
@@ -123,24 +139,30 @@
|
|||||||
padding: 10px 10px;
|
padding: 10px 10px;
|
||||||
background-color: constants.$origin-color;
|
background-color: constants.$origin-color;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
box-shadow: 2px 2px 10px 0 rgba(0,0,0,0.1);
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
.text {
|
||||||
|
width: unset;
|
||||||
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: constants.$origin-color;
|
color: constants.$origin-color;
|
||||||
background-color: constants.$main-color !important;
|
background-color: constants.$main-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover a {
|
&:hover a:not(.active) {
|
||||||
background-color: constants.$background-color;
|
background-color: constants.$background-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,8 +170,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
>.menu-bt {
|
> .menu-bt {
|
||||||
a {
|
a:not(.active) {
|
||||||
background-color: constants.$background-color;
|
background-color: constants.$background-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,6 +193,22 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
.menu-bt {
|
||||||
|
border: {
|
||||||
|
width: 1px;
|
||||||
|
color: constants.$error-secondary-color;
|
||||||
|
style: dashed;
|
||||||
|
};
|
||||||
|
filter: drop-shadow(1000px 0 0 constants.$error-secondary-color);
|
||||||
|
transform: translate(-1000px);
|
||||||
|
|
||||||
|
> a {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +260,7 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
a{
|
a {
|
||||||
color: constants.$main-color;
|
color: constants.$main-color;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
@@ -251,13 +289,14 @@
|
|||||||
transition: all .3s;
|
transition: all .3s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-bt {
|
.menu-bt {
|
||||||
.text {
|
.text, .extend {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,6 +311,7 @@
|
|||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -282,7 +322,7 @@
|
|||||||
padding-left: 6px;
|
padding-left: 6px;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
box-shadow: 5px 5px 15px 0 rgba(0,0,0,0.1);
|
box-shadow: 5px 5px 15px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
@@ -291,6 +331,7 @@
|
|||||||
|
|
||||||
.icon-exit {
|
.icon-exit {
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: constants.$background-color;
|
background-color: constants.$background-color;
|
||||||
@@ -299,7 +340,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.hide {
|
&.hide {
|
||||||
display: none!important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
28
src/renderer/src/assets/css/components/common/url-card.scss
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
@use '@/assets/css/constants' as constants;
|
||||||
|
|
||||||
|
[data-component=component-url-card] {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.url-card {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin-top: 80px;
|
||||||
|
text-align: center;
|
||||||
|
gap: 42px;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
color: constants.$production-color;
|
||||||
|
font-size: constants.$SIZE_ICON_XL;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[data-component=component-drag-handle] {
|
||||||
|
background-color: transparent;
|
||||||
|
color: unset;
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
30
src/renderer/src/assets/css/components/dnd/drop-mask.scss
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
@use "@/assets/css/constants" as constants;
|
||||||
|
|
||||||
|
[data-component=component-drop-mask] {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: {
|
||||||
|
left: 10px;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
};
|
||||||
|
background-color: constants.$origin-color;
|
||||||
|
|
||||||
|
.drop-mask-border {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border: {
|
||||||
|
width: 2px;
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
style: dashed;
|
||||||
|
radius: 8px;
|
||||||
|
};
|
||||||
|
font-size: 1.8em;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
@use '@/assets/css/constants' as constants;
|
||||||
|
|
||||||
|
[data-component=component-setting-card] {
|
||||||
|
.settings-card {
|
||||||
|
padding: 20px;
|
||||||
|
gap: 20px;
|
||||||
|
color: constants.$main-color;
|
||||||
|
|
||||||
|
> .head {
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: constants.$SIZE_ICON_MD;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
:nth-child(n+3) {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
color: constants.$font-main-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bt-save {
|
||||||
|
color: constants.$main-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
@use '@/assets/css/constants' as constants;
|
||||||
|
|
||||||
|
[data-component=component-statistics-card] {
|
||||||
|
.statistics-card {
|
||||||
|
padding: 20px;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
> .head {
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
color: constants.$main-color;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: constants.$SIZE_ICON_MD;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
:nth-child(n+3) {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
color: constants.$font-main-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
font-size: 1.1em;
|
||||||
|
padding: 0 10px;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.key, .value-percent {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
color: constants.$font-main-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-chart {
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 0;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
max-height: 12px;
|
||||||
|
height: 12px;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
transform: translateY(1px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-percent {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.big-chart {
|
||||||
|
width: 0;
|
||||||
|
height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
@use '@/assets/css/constants' as constants;
|
||||||
|
|
||||||
|
[data-component=component-load-more-card] {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.load-more-card {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
display: flex;
|
||||||
|
font-size: constants.$SIZE_ICON_XXL;
|
||||||
|
color: constants.$production-color;
|
||||||
|
align-items: center;
|
||||||
|
transform: translateY(-20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
position: absolute;
|
||||||
|
top: 60%;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
110
src/renderer/src/assets/css/components/tools/local-card.scss
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
@use '@/assets/css/constants' as constants;
|
||||||
|
|
||||||
|
[data-component=component-local-card] {
|
||||||
|
height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.local-card {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
display: block;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.version {
|
||||||
|
width: 0;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation {
|
||||||
|
display: flex;
|
||||||
|
font-size: 1.6em;
|
||||||
|
gap: 4px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
> *:hover {
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
display: flex;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
color: constants.$production-color;
|
||||||
|
font-size: constants.$SIZE_ICON_XL;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: constants.$SIZE_ICON_XL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
padding-top: 20px;
|
||||||
|
|
||||||
|
.tool-name {
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-desc {
|
||||||
|
margin: {
|
||||||
|
top: 10px;
|
||||||
|
left: auto;
|
||||||
|
right: auto;
|
||||||
|
};
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-height: 40px;
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.author {
|
||||||
|
display: flex;
|
||||||
|
margin-top: auto;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: end;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
> * {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:hover {
|
||||||
|
.header {
|
||||||
|
.version {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
@use '@/assets/css/constants' as constants;
|
||||||
|
|
||||||
|
[data-component=component-repository-card] {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.repository-card {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
display: block;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.version-select {
|
||||||
|
width: 9em;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
>:not(.version-select) {
|
||||||
|
font-size: 1.6em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
display: flex;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
color: constants.$production-color;
|
||||||
|
font-size: constants.$SIZE_ICON_XL;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: constants.$SIZE_ICON_XL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-name {
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 1.6em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
111
src/renderer/src/assets/css/components/tools/store-card.scss
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
@use '@/assets/css/constants' as constants;
|
||||||
|
|
||||||
|
[data-component=component-store-card] {
|
||||||
|
height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.store-card {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
display: block;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.version {
|
||||||
|
width: 0;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation {
|
||||||
|
display: flex;
|
||||||
|
font-size: 1.6em;
|
||||||
|
gap: 4px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.2s;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
> *:hover {
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
display: flex;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
color: constants.$production-color;
|
||||||
|
font-size: constants.$SIZE_ICON_XL;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: constants.$SIZE_ICON_XL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
padding-top: 20px;
|
||||||
|
|
||||||
|
.tool-name {
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-desc {
|
||||||
|
margin: {
|
||||||
|
top: 10px;
|
||||||
|
left: auto;
|
||||||
|
right: auto;
|
||||||
|
};
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-height: 40px;
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.author {
|
||||||
|
display: flex;
|
||||||
|
margin-top: auto;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: end;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
> * {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.author-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:hover {
|
||||||
|
.header {
|
||||||
|
.version {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operation {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,40 +2,16 @@
|
|||||||
|
|
||||||
[data-component=system] {
|
[data-component=system] {
|
||||||
.root-content {
|
.root-content {
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
> .card-box {
|
> .card-box {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
height: 360px;
|
height: 320px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.common-card {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin-top: 100px;
|
|
||||||
text-align: center;
|
|
||||||
gap: 42px;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
color: constants.$production-color;
|
|
||||||
font-size: constants.$SIZE_ICON_XL;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
|
||||||
font-weight: bolder;
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[data-component=system-settings] {
|
[data-component=system-settings] {
|
||||||
.root-content {
|
.root-content {
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|
||||||
.root-col {
|
.root-col {
|
||||||
@@ -11,36 +11,6 @@
|
|||||||
> * {
|
> * {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-card {
|
|
||||||
padding: 20px;
|
|
||||||
gap: 20px;
|
|
||||||
color: constants.$main-color;
|
|
||||||
|
|
||||||
> .head {
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
font-size: constants.$SIZE_ICON_MD;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
display: flex;
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
:nth-child(n+3) {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
color: constants.$font-main-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bt-save {
|
|
||||||
color: constants.$main-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[data-component=system-statistics] {
|
[data-component=system-statistics] {
|
||||||
.root-content {
|
.root-content {
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -10,81 +10,6 @@
|
|||||||
> .card-box {
|
> .card-box {
|
||||||
width: 48%;
|
width: 48%;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|
||||||
.common-card {
|
|
||||||
padding: 20px;
|
|
||||||
gap: 20px;
|
|
||||||
|
|
||||||
> .head {
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
color: constants.$main-color;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
font-size: constants.$SIZE_ICON_MD;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
display: flex;
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
:nth-child(n+3) {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
color: constants.$font-main-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content {
|
|
||||||
font-size: 1.1em;
|
|
||||||
padding: 0 10px;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.key, .value-percent {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
color: constants.$font-main-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
color: constants.$font-secondary-color;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.value-chart {
|
|
||||||
justify-content: space-around;
|
|
||||||
width: 0;
|
|
||||||
|
|
||||||
> div {
|
|
||||||
max-height: 12px;
|
|
||||||
height: 12px;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
transform: translateY(1px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.value-percent {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.big-chart {
|
|
||||||
width: 0;
|
|
||||||
height: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> * {
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[data-component=system-tools-base] {
|
[data-component=system-tools-base] {
|
||||||
.root-content {
|
.root-content {
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -13,13 +13,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
>*:first-child {
|
>*:first-child {
|
||||||
|
width: 0;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
> *:nth-child(2) {
|
> *:nth-child(2) {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 20px;
|
top: 20px;
|
||||||
height: calc(100vh - 60px);
|
height: calc(100vh - 40px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-editor-btn {
|
.close-editor-btn {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[data-component=system-tools-code] {
|
[data-component=system-tools-code] {
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
|
|
||||||
.card-box {
|
.card-box {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[data-component=system-tools-execute] {
|
[data-component=system-tools-execute] {
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
|
|
||||||
.card-box {
|
.card-box {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
43
src/renderer/src/assets/css/pages/system/tools/template.scss
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
@use '@/assets/css/constants' as constants;
|
||||||
|
|
||||||
|
[data-component=system-tools-template] {
|
||||||
|
.root-content {
|
||||||
|
padding: 20px;
|
||||||
|
gap: 10px;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.has-edited::after {
|
||||||
|
content: '*';
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
>*:first-child {
|
||||||
|
width: 0;
|
||||||
|
height: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
> *:nth-child(2) {
|
||||||
|
position: sticky;
|
||||||
|
top: 20px;
|
||||||
|
height: calc(100vh - 40px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-editor-btn {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
background-color: constants.$font-secondary-color;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: white;
|
||||||
|
opacity: 0.6;
|
||||||
|
box-shadow: 2px 2px 10px 0 rgba(0,0,0,0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,14 @@
|
|||||||
[data-component=tools-framework] {
|
[data-component=tools-framework] {
|
||||||
.left-panel {
|
.left-panel {
|
||||||
background-color: constants.$origin-color;
|
background-color: constants.$origin-color;
|
||||||
|
|
||||||
|
.menu-droppable {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-panel {
|
.right-panel {
|
||||||
|
|||||||
@@ -43,6 +43,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.no-preview {
|
||||||
|
font-weight: bolder;
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
[data-component=tools-edit] {
|
[data-component=tools-edit] {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.card-box {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
.root-content {
|
.root-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -13,10 +19,11 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.draggable-content {
|
.draggable-content {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset-inline-end: 32px;
|
inset-inline-end: 48px;
|
||||||
inset-block-end: 48px;
|
inset-block-end: 48px;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
|
|||||||
@@ -3,59 +3,18 @@
|
|||||||
|
|
||||||
[data-component=tools] {
|
[data-component=tools] {
|
||||||
.root-content {
|
.root-content {
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
.own-content {
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
> .card-box {
|
> .card-box, > div {
|
||||||
width: 180px;
|
width: 180px;
|
||||||
height: 290px;
|
height: 290px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|
||||||
.common-card {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
text-align: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
display: block;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.version-select {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
left: 10px;
|
|
||||||
width: 8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upgrade-bt {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
font-size: 1.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
display: flex;
|
|
||||||
padding-top: 50px;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
color: constants.$production-color;
|
|
||||||
font-size: constants.$SIZE_ICON_XL;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: constants.$SIZE_ICON_XL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-name {
|
|
||||||
font-weight: bolder;
|
|
||||||
font-size: 1.6em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -112,4 +71,40 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.favorite-divider {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
:first-child, :last-child {
|
||||||
|
height: 0;
|
||||||
|
border: {
|
||||||
|
width: 1px;
|
||||||
|
color: constants.$divide-color;
|
||||||
|
style: dashed;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider-text {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
font-size: 1.2em;
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.star-content {
|
||||||
|
gap: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
> .card-box, > div {
|
||||||
|
width: 180px;
|
||||||
|
height: 290px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
49
src/renderer/src/assets/css/pages/tools/local.scss
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
@use '@/assets/css/constants' as constants;
|
||||||
|
|
||||||
|
[data-component=tools-local] {
|
||||||
|
.search {
|
||||||
|
display: flex;
|
||||||
|
position: sticky;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 20px;
|
||||||
|
top: 20px;
|
||||||
|
z-index: 10;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.hide {
|
||||||
|
transform: translateY(-60px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-content {
|
||||||
|
padding: 20px;
|
||||||
|
gap: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
width: 180px;
|
||||||
|
height: 290px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-tool {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.4em;
|
||||||
|
font-weight: bolder;
|
||||||
|
color: constants.$font-secondary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.android-qrcode {
|
||||||
|
align-items: center;
|
||||||
|
transform: translateX(-16px);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
[data-component=tools-source] {
|
[data-component=tools-source] {
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
|
|
||||||
.card-box {
|
.card-box {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -21,116 +21,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.root-content {
|
.root-content {
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
> .card-box {
|
> div {
|
||||||
width: 180px;
|
width: 180px;
|
||||||
height: 290px;
|
height: 290px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.common-card {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
text-align: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
display: block;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
display: flex;
|
|
||||||
padding-top: 40px;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
color: constants.$production-color;
|
|
||||||
font-size: constants.$SIZE_ICON_XL;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: constants.$SIZE_ICON_XL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.version {
|
|
||||||
position: absolute;
|
|
||||||
left: 10px;
|
|
||||||
top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
padding-top: 20px;
|
|
||||||
|
|
||||||
.tool-name {
|
|
||||||
font-weight: bolder;
|
|
||||||
font-size: 1.6em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-desc {
|
|
||||||
margin-top: 10px;
|
|
||||||
color: constants.$font-secondary-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.author {
|
|
||||||
display: flex;
|
|
||||||
margin-top: auto;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: end;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
> * {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.author-name {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.operation {
|
|
||||||
position: absolute;
|
|
||||||
top: 6px;
|
|
||||||
right: 12px;
|
|
||||||
font-size: 1.6em;
|
|
||||||
|
|
||||||
> *:hover {
|
|
||||||
color: constants.$font-secondary-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.load-more-card {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
text-align: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
display: flex;
|
|
||||||
font-size: constants.$SIZE_ICON_XXL;
|
|
||||||
color: constants.$production-color;
|
|
||||||
align-items: center;
|
|
||||||
transform: translateY(-20px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
|
||||||
position: absolute;
|
|
||||||
top: 60%;
|
|
||||||
font-size: 1.2em;
|
|
||||||
font-weight: bolder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-tool {
|
.no-tool {
|
||||||
@@ -141,4 +40,10 @@
|
|||||||
color: constants.$font-secondary-color;
|
color: constants.$font-secondary-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.android-qrcode {
|
||||||
|
align-items: center;
|
||||||
|
transform: translateX(-16px);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
[data-component=tools-store-user] .root-content {
|
[data-component=tools-store-user] .root-content {
|
||||||
padding: {
|
padding: {
|
||||||
top: 80px;
|
top: 80px;
|
||||||
left: 30px;
|
left: 20px;
|
||||||
right: 30px;
|
right: 20px;
|
||||||
bottom: 30px;
|
bottom: 20px;
|
||||||
};
|
};
|
||||||
|
|
||||||
.root-box {
|
.root-box {
|
||||||
@@ -68,111 +68,10 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
> .card-box {
|
> div {
|
||||||
width: 180px;
|
width: 180px;
|
||||||
height: 290px;
|
height: 290px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.common-card {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
text-align: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
> * {
|
|
||||||
display: block;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
display: flex;
|
|
||||||
padding-top: 40px;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
color: constants.$production-color;
|
|
||||||
font-size: constants.$SIZE_ICON_XL;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: constants.$SIZE_ICON_XL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.version {
|
|
||||||
position: absolute;
|
|
||||||
left: 10px;
|
|
||||||
top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
padding-top: 20px;
|
|
||||||
|
|
||||||
.tool-name {
|
|
||||||
font-weight: bolder;
|
|
||||||
font-size: 1.6em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-desc {
|
|
||||||
margin-top: 10px;
|
|
||||||
color: constants.$font-secondary-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.author {
|
|
||||||
display: flex;
|
|
||||||
margin-top: auto;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: end;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
> * {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.author-name {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.operation {
|
|
||||||
position: absolute;
|
|
||||||
top: 6px;
|
|
||||||
right: 12px;
|
|
||||||
font-size: 1.6em;
|
|
||||||
|
|
||||||
> *:hover {
|
|
||||||
color: constants.$font-secondary-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.load-more-card {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
text-align: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
display: flex;
|
|
||||||
font-size: constants.$SIZE_ICON_XXL;
|
|
||||||
color: constants.$production-color;
|
|
||||||
align-items: center;
|
|
||||||
transform: translateY(-20px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.text {
|
|
||||||
position: absolute;
|
|
||||||
top: 60%;
|
|
||||||
font-size: 1.2em;
|
|
||||||
font-weight: bolder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-tool {
|
.no-tool {
|
||||||
@@ -185,4 +84,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.android-qrcode {
|
||||||
|
align-items: center;
|
||||||
|
transform: translateX(-16px);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
[data-component=tools-view] {
|
[data-component=tools-view] {
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
|
|
||||||
.card-box {
|
.card-box {
|
||||||
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
[data-component=user] .root-content {
|
[data-component=user] .root-content {
|
||||||
padding: {
|
padding: {
|
||||||
top: 80px;
|
top: 80px;
|
||||||
left: 30px;
|
left: 20px;
|
||||||
right: 30px;
|
right: 20px;
|
||||||
bottom: 30px;
|
bottom: 20px;
|
||||||
};
|
};
|
||||||
|
|
||||||
.card-box {
|
.card-box {
|
||||||
@@ -70,6 +70,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.url {
|
.url {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/renderer/src/assets/svg/arrowDown.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M482.133333 738.133333L136.533333 392.533333c-17.066667-17.066667-17.066667-42.666667 0-59.733333 8.533333-8.533333 19.2-12.8 29.866667-12.8h689.066667c23.466667 0 42.666667 19.2 42.666666 42.666667 0 10.666667-4.266667 21.333333-12.8 29.866666L541.866667 738.133333c-17.066667 17.066667-42.666667 17.066667-59.733334 0z" /></svg>
|
||||||
|
After Width: | Height: | Size: 404 B |
1
src/renderer/src/assets/svg/browser.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M512 885.76a392.2944 392.2944 0 1 1 392.192-392.192A392.6016 392.6016 0 0 1 512 885.76z m0-702.5664A310.3744 310.3744 0 1 0 822.6816 493.568 310.6816 310.6816 0 0 0 512 183.1936z" /><path d="M978.432 263.8848c-22.9376-46.6944-127.2832-36.5568-193.7408-11.3664l29.0816 76.5952c4.3008-1.6384 8.6016-3.1744 12.8-4.5056l-8.704 5.4272c-76.0832 47.0016-187.0848 107.1104-304.64 165.0688S280.2688 604.16 196.7104 636.1088l-2.1504 0.8192-30.72-49.5616C91.136 632.2176 22.4256 674.7136 46.3872 723.1488c9.216 18.8416 26.3168 26.112 48.3328 26.112 34.816 0 81.92-18.0224 131.072-36.5568 87.1424-33.0752 202.0352-84.1728 323.584-144.0768S781.6192 448.7168 860.8768 399.36c72.8064-44.544 141.5168-87.04 117.5552-135.4752zM125.7472 675.0208l24.064-26.624 11.3664 26.624z m739.7376-350.5152a98.304 98.304 0 0 0-18.2272-32.4608 152.1664 152.1664 0 0 1 57.0368-10.24 115.5072 115.5072 0 0 1-38.8096 42.7008z" /></svg>
|
||||||
|
After Width: | Height: | Size: 975 B |
1
src/renderer/src/assets/svg/desktop.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M810.666667 128H213.333333a128 128 0 0 0-128 128v341.333333a128 128 0 0 0 128 128h256v85.333334H298.666667a42.666667 42.666667 0 0 0 0 85.333333h426.666666a42.666667 42.666667 0 0 0 0-85.333333h-170.666666v-85.333334h256a128 128 0 0 0 128-128V256a128 128 0 0 0-128-128z m42.666666 469.333333a42.666667 42.666667 0 0 1-42.666666 42.666667H213.333333a42.666667 42.666667 0 0 1-42.666666-42.666667V256a42.666667 42.666667 0 0 1 42.666666-42.666667h597.333334a42.666667 42.666667 0 0 1 42.666666 42.666667z" /></svg>
|
||||||
|
After Width: | Height: | Size: 586 B |
1
src/renderer/src/assets/svg/download.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M554.667 554.667V792.96l77.994-77.995 60.374 60.374L512 956.33 330.965 775.339l60.374-60.374 77.994 77.995V554.667h85.334zM512 85.333a298.71 298.71 0 0 1 296.704 264.278 234.667 234.667 0 0 1-40.661 460.117v-85.93a149.333 149.333 0 1 0-47.446-294.827 213.333 213.333 0 1 0-417.152 0 149.333 149.333 0 0 0-55.125 293.546l7.68 1.28v85.931a234.667 234.667 0 0 1-40.704-460.117A298.667 298.667 0 0 1 512 85.333z" /></svg>
|
||||||
|
After Width: | Height: | Size: 491 B |
1
src/renderer/src/assets/svg/handle.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M7 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 2zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 14zm6-8a2 2 0 1 0-.001-4.001A2 2 0 0 0 13 6zm0 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 14z" /></svg>
|
||||||
|
After Width: | Height: | Size: 325 B |
1
src/renderer/src/assets/svg/info.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M512 938.715429A426.642286 426.642286 0 1 1 512 85.357714a426.642286 426.642286 0 0 1 0 853.357715z m-42.642286-469.357715v256h85.284572v-256H469.284571z m0-170.642285V384h85.284572V298.715429H469.284571z" /></svg>
|
||||||
|
After Width: | Height: | Size: 288 B |
1
src/renderer/src/assets/svg/installed.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M544 531.2V704c0 19.2-12.8 32-32 32s-32-12.8-32-32V531.2L332.8 441.6c-12.8-6.4-19.2-25.6-6.4-44.8 6.4-12.8 25.6-19.2 44.8-6.4L512 473.6l140.8-83.2c12.8-6.4 32-6.4 44.8 12.8 6.4 12.8 6.4 32-12.8 44.8L544 531.2z m-12.8-428.8c-12.8-6.4-25.6-6.4-32 0L147.2 307.2c-12.8 6.4-19.2 19.2-19.2 25.6v352c0 12.8 6.4 19.2 19.2 25.6l352 211.2c12.8 6.4 25.6 6.4 32 0l352-211.2c12.8-6.4 19.2-12.8 19.2-25.6V332.8c0-12.8-6.4-19.2-19.2-25.6L531.2 102.4z m32-57.6L908.8 256c32 12.8 51.2 44.8 51.2 76.8v358.4c0 32-19.2 64-51.2 76.8l-345.6 211.2c-32 19.2-70.4 19.2-102.4 0L115.2 768c-32-12.8-51.2-44.8-51.2-76.8V332.8c0-32 19.2-64 51.2-76.8L460.8 44.8C492.8 25.6 531.2 25.6 563.2 44.8z" /></svg>
|
||||||
|
After Width: | Height: | Size: 748 B |
1
src/renderer/src/assets/svg/mobile.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M768 64a64 64 0 0 1 64 64v768a64 64 0 0 1-64 64H256a64 64 0 0 1-64-64V128a64 64 0 0 1 64-64h512z m-32 64H288a32 32 0 0 0-32 32v704a32 32 0 0 0 32 32h448a32 32 0 0 0 32-32V160a32 32 0 0 0-32-32z m-223.488 608a48 48 0 1 1 0 96 48 48 0 0 1 0-96z" /></svg>
|
||||||
|
After Width: | Height: | Size: 326 B |
1
src/renderer/src/assets/svg/receive.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M927.929032 640.065509a31.994541 31.994541 0 0 0-31.994541 31.994541v127.978163c0 52.918971-43.064652 95.983623-95.983623 95.983623h-575.901736c-52.918971 0-95.983623-43.064652-95.983623-95.983623v-127.978163a31.994541 31.994541 0 0 0-63.989082 0v127.978163C64.076427 888.279157 135.872177 960.010918 224.049132 960.010918h575.901736c88.240944 0 159.972705-71.731761 159.972705-159.972705v-127.978163a31.994541 31.994541 0 0 0-31.994541-31.994541z" /><path d="M412.113043 777.386079c18.684812 18.684812 43.256619 28.027218 67.892416 28.027217s49.143615-9.342406 67.892416-28.027217l274.705128-274.705129A31.930552 31.930552 0 0 0 799.950868 448.098263h-159.140846a629.524587 629.524587 0 0 1 44.408423-202.909378L765.652721 44.0712a32.122519 32.122519 0 0 0-7.998636-35.449951 32.122519 32.122519 0 0 0-35.961864-5.055137L566.582687 81.120879A443.124392 443.124392 0 0 0 321.184558 448.098263H160.06005a31.994541 31.994541 0 0 0-22.652135 54.646676l274.705128 274.64114zM352.027295 512.023356a31.994541 31.994541 0 0 0 31.994541-31.994541A379.711212 379.711212 0 0 1 595.185806 138.391107l79.858375-39.929187-49.207604 122.923026A692.7458 692.7458 0 0 0 575.989082 480.092804a31.994541 31.994541 0 0 0 31.994541 31.994541h114.732424l-220.058453 220.058453a31.994541 31.994541 0 0 1-45.240281 0L237.294872 512.087345 352.027295 512.023356z" /></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
1
src/renderer/src/assets/svg/rotateLeft.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M672 418H144c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32z m-44 402H188V494h440v326z" /><path d="M819.3 328.5c-78.8-100.7-196-153.6-314.6-154.2l-0.2-64c0-6.5-7.6-10.1-12.6-6.1l-128 101c-4 3.1-3.9 9.1 0 12.3L492 318.6c5.1 4 12.7 0.4 12.6-6.1v-63.9c12.9 0.1 25.9 0.9 38.8 2.5 42.1 5.2 82.1 18.2 119 38.7 38.1 21.2 71.2 49.7 98.4 84.3 27.1 34.7 46.7 73.7 58.1 115.8 11 40.7 14 82.7 8.9 124.8-0.7 5.4-1.4 10.8-2.4 16.1h74.9c14.8-103.6-11.3-213-81-302.3z" /></svg>
|
||||||
|
After Width: | Height: | Size: 585 B |
1
src/renderer/src/assets/svg/rotateRight.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M480.5 251.2c13-1.6 25.9-2.4 38.8-2.5v63.9c0 6.5 7.5 10.1 12.6 6.1L660 217.6c4-3.2 4-9.2 0-12.3l-128-101c-5.1-4-12.6-0.4-12.6 6.1l-0.2 64c-118.6 0.5-235.8 53.4-314.6 154.2-69.6 89.2-95.7 198.6-81.1 302.4h74.9c-0.9-5.3-1.7-10.7-2.4-16.1-5.1-42.1-2.1-84.1 8.9-124.8 11.4-42.2 31-81.1 58.1-115.8 27.2-34.7 60.3-63.2 98.4-84.3 37-20.6 76.9-33.6 119.1-38.8z" /><path d="M880 418H352c-17.7 0-32 14.3-32 32v414c0 17.7 14.3 32 32 32h528c17.7 0 32-14.3 32-32V450c0-17.7-14.3-32-32-32z m-44 402H396V494h440v326z" /></svg>
|
||||||
|
After Width: | Height: | Size: 585 B |
1
src/renderer/src/assets/svg/star.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1151 1024"><path d="M1056.069394 342.982943L763.905526 300.38821 633.321676 35.620955c-23.397106-47.194163-91.188723-47.794089-114.785804 0L387.952021 300.38821 95.788154 342.982943c-52.39352 7.59906-73.390924 72.191072-35.395623 109.186496l211.373859 205.974527-49.993817 290.964016c-8.998887 52.593496 46.394262 91.988624 92.788525 67.391666L575.928774 879.116638l261.367676 137.38301c46.394262 24.396983 101.787412-14.79817 92.788525-67.391666l-49.993818-290.964016 211.373859-205.974527c37.995301-36.995425 16.997898-101.587436-35.395622-109.186496zM777.103894 624.548121l47.394139 276.765772L575.928774 770.730042l-248.569259 130.583851 47.394139-276.765772-201.175121-195.975763 277.965624-40.395005 124.384617-251.968838 124.384617 251.968838 277.965623 40.395005-201.17512 195.975763z" /></svg>
|
||||||
|
After Width: | Height: | Size: 856 B |
1
src/renderer/src/assets/svg/starFilled.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1151 1024"><path d="M518.535872 35.620955L387.952021 300.38821 95.788154 342.982943c-52.39352 7.59906-73.390924 72.191072-35.395623 109.186496l211.373859 205.974527-49.993817 290.964016c-8.998887 52.593496 46.394262 91.988624 92.788525 67.391666L575.928774 879.116638l261.367676 137.38301c46.394262 24.396983 101.787412-14.79817 92.788525-67.391666l-49.993818-290.964016 211.373859-205.974527c37.995301-36.995425 16.997898-101.587436-35.395622-109.186496L763.905526 300.38821 633.321676 35.620955c-23.397106-47.194163-91.188723-47.794089-114.785804 0z" /></svg>
|
||||||
|
After Width: | Height: | Size: 615 B |
1
src/renderer/src/assets/svg/zoom.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M720 464H560V304a48 48 0 1 0-96 0v160H304a48 48 0 1 0 0 96h160v160a48 48 0 1 0 96 0V560h160a48 48 0 1 0 0-96zM512 0C229.232 0 0 229.232 0 512c0 282.768 229.232 512 512 512h464a48 48 0 0 0 48-48V512.032 512C1024 229.232 794.768 0 512 0z m416 512.016C928 741.744 741.776 927.968 512.064 928H512C282.256 928 96 741.76 96 512 96 282.256 282.256 96 512 96s416 186.256 416 416.016z" /></svg>
|
||||||
|
After Width: | Height: | Size: 459 B |
@@ -76,11 +76,11 @@ export const useEditor = () => {
|
|||||||
export const useTypesProgress = () => {
|
export const useTypesProgress = () => {
|
||||||
const [progress, setProgress] = useState(0)
|
const [progress, setProgress] = useState(0)
|
||||||
const [total, setTotal] = useState(0)
|
const [total, setTotal] = useState(0)
|
||||||
const [finished, setFinished] = useState(false)
|
const [isFinished, setIsFinished] = useState(false)
|
||||||
|
|
||||||
const onWatch = (typeHelper: TypeHelper) => {
|
const onWatch = (typeHelper: TypeHelper) => {
|
||||||
const handleStarted = () => {
|
const handleStarted = () => {
|
||||||
setFinished(false)
|
setIsFinished(false)
|
||||||
}
|
}
|
||||||
typeHelper.addListener('started', handleStarted)
|
typeHelper.addListener('started', handleStarted)
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ export const useTypesProgress = () => {
|
|||||||
typeHelper.addListener('progress', handleProgress)
|
typeHelper.addListener('progress', handleProgress)
|
||||||
|
|
||||||
const handleFinished = () => {
|
const handleFinished = () => {
|
||||||
setFinished(true)
|
setIsFinished(true)
|
||||||
}
|
}
|
||||||
typeHelper.addListener('progress', handleFinished)
|
typeHelper.addListener('progress', handleFinished)
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ export const useTypesProgress = () => {
|
|||||||
return {
|
return {
|
||||||
progress,
|
progress,
|
||||||
total,
|
total,
|
||||||
finished,
|
finished: isFinished,
|
||||||
onWatch
|
onWatch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { loader } from '@monaco-editor/react'
|
import { loader } from '@monaco-editor/react'
|
||||||
|
import * as monaco from 'monaco-editor'
|
||||||
|
|
||||||
loader.config({
|
loader.config({
|
||||||
paths: {
|
monaco
|
||||||
vs: 'https://unpkg.com/monaco-editor@0.45.0/min/vs'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ const Item = ({
|
|||||||
}: ItemProps) => {
|
}: ItemProps) => {
|
||||||
const inputRef = useRef<HTMLInputElement>(null)
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
const [fileName, setFileName] = useState(value)
|
const [fileName, setFileName] = useState(value)
|
||||||
const [creating, setCreating] = useState(prop.creating)
|
const [isCreating, setIsCreating] = useState(prop.creating)
|
||||||
|
|
||||||
const handleOnClick = () => {
|
const handleOnClick = () => {
|
||||||
if (hasEditing) {
|
if (hasEditing) {
|
||||||
@@ -52,35 +52,35 @@ const Item = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const finishNameFile = () => {
|
const finishNameFile = () => {
|
||||||
if (!creating || onValidate ? !onValidate?.(fileName, value) : false) {
|
if (!isCreating || onValidate ? !onValidate?.(fileName, value) : false) {
|
||||||
inputRef.current?.focus()
|
inputRef.current?.focus()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileName === value && active) {
|
if (fileName === value && active) {
|
||||||
setCreating(false)
|
setIsCreating(false)
|
||||||
setHasEditing?.(false)
|
setHasEditing?.(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
onOk?.(fileName)
|
onOk?.(fileName)
|
||||||
setCreating(false)
|
setIsCreating(false)
|
||||||
setHasEditing?.(false)
|
setHasEditing?.(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const cancelNameFile = () => {
|
const cancelNameFile = () => {
|
||||||
setFileName(value)
|
setFileName(value)
|
||||||
setCreating(false)
|
setIsCreating(false)
|
||||||
setHasEditing?.(false)
|
setHasEditing?.(false)
|
||||||
onCancel?.()
|
onCancel?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOnDoubleClick = () => {
|
const handleOnDoubleClick = () => {
|
||||||
if (readonly || creating || hasEditing) {
|
if (readonly || isCreating || hasEditing) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setCreating(true)
|
setIsCreating(true)
|
||||||
setHasEditing?.(true)
|
setHasEditing?.(true)
|
||||||
setFileName(value)
|
setFileName(value)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -112,7 +112,7 @@ const Item = ({
|
|||||||
className={`tab-item${active ? ' active' : ''}${className ? ` ${className}` : ''}`}
|
className={`tab-item${active ? ' active' : ''}${className ? ` ${className}` : ''}`}
|
||||||
onClick={handleOnClick}
|
onClick={handleOnClick}
|
||||||
>
|
>
|
||||||
{creating ? (
|
{isCreating ? (
|
||||||
<div className={'tab-item-input'}>
|
<div className={'tab-item-input'}>
|
||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const FileSelector = ({
|
|||||||
}: FileSelectorProps) => {
|
}: FileSelectorProps) => {
|
||||||
const hideScrollbarRef = useRef<HideScrollbarElement>(null)
|
const hideScrollbarRef = useRef<HideScrollbarElement>(null)
|
||||||
const [tabs, setTabs] = useState<string[]>([])
|
const [tabs, setTabs] = useState<string[]>([])
|
||||||
const [creating, setCreating] = useState(false)
|
const [isCreating, setIsCreating] = useState(false)
|
||||||
const [hasEditing, setHasEditing] = useState(false)
|
const [hasEditing, setHasEditing] = useState(false)
|
||||||
|
|
||||||
const getMaxSequenceTabName = (filesName: string[]) => {
|
const getMaxSequenceTabName = (filesName: string[]) => {
|
||||||
@@ -56,7 +56,7 @@ const FileSelector = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTabs([...tabs, getMaxSequenceTabName(tabs)])
|
setTabs([...tabs, getMaxSequenceTabName(tabs)])
|
||||||
setCreating(true)
|
setIsCreating(true)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
hideScrollbarRef.current?.scrollRight(1000)
|
hideScrollbarRef.current?.scrollRight(1000)
|
||||||
})
|
})
|
||||||
@@ -64,16 +64,16 @@ const FileSelector = ({
|
|||||||
|
|
||||||
const handleOnCancel = () => {
|
const handleOnCancel = () => {
|
||||||
onError?.('')
|
onError?.('')
|
||||||
if (!creating) {
|
if (!isCreating) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tabs.pop()
|
tabs.pop()
|
||||||
setTabs([...tabs])
|
setTabs([...tabs])
|
||||||
setCreating(false)
|
setIsCreating(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOnClickTab = (fileName: string) => {
|
const handleOnClickTab = (fileName: string) => {
|
||||||
if (creating) {
|
if (isCreating) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,9 +97,9 @@ const FileSelector = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleOnSaveTab = (value: string, item: string) => {
|
const handleOnSaveTab = (value: string, item: string) => {
|
||||||
if (creating) {
|
if (isCreating) {
|
||||||
onAddFile?.(value)
|
onAddFile?.(value)
|
||||||
setCreating(false)
|
setIsCreating(false)
|
||||||
} else {
|
} else {
|
||||||
onUpdateFileName?.(value, item)
|
onUpdateFileName?.(value, item)
|
||||||
}
|
}
|
||||||
@@ -166,14 +166,20 @@ const FileSelector = ({
|
|||||||
<>
|
<>
|
||||||
<div data-component={'playground-file-selector'} className={'tab'}>
|
<div data-component={'playground-file-selector'} className={'tab'}>
|
||||||
<div className={'multiple'}>
|
<div className={'multiple'}>
|
||||||
<HideScrollbar ref={hideScrollbarRef}>
|
<HideScrollbar
|
||||||
|
ref={hideScrollbarRef}
|
||||||
|
autoHideWaitingTime={800}
|
||||||
|
scrollbarWidth={1}
|
||||||
|
scrollbarAsidePadding={0}
|
||||||
|
scrollbarEdgePadding={0}
|
||||||
|
>
|
||||||
<FlexBox direction={'horizontal'} className={'tab-content'}>
|
<FlexBox direction={'horizontal'} className={'tab-content'}>
|
||||||
{tabs.map((item, index) => (
|
{tabs.map((item, index) => (
|
||||||
<Item
|
<Item
|
||||||
key={index + item}
|
key={index + item}
|
||||||
value={item}
|
value={item}
|
||||||
active={selectedFileName === item}
|
active={selectedFileName === item}
|
||||||
creating={creating}
|
creating={isCreating}
|
||||||
readonly={readonly || notRemovableFiles.includes(item)}
|
readonly={readonly || notRemovableFiles.includes(item)}
|
||||||
hasEditing={hasEditing}
|
hasEditing={hasEditing}
|
||||||
setHasEditing={setHasEditing}
|
setHasEditing={setHasEditing}
|
||||||
|
|||||||
@@ -1,19 +1,30 @@
|
|||||||
|
import { ChangeEvent } from 'react'
|
||||||
import '@/components/Playground/Output/Preview/render.scss'
|
import '@/components/Playground/Output/Preview/render.scss'
|
||||||
|
import { COLOR_FONT_MAIN } from '@/constants/common.constants'
|
||||||
import iframeRaw from '@/components/Playground/Output/Preview/iframe.html?raw'
|
import iframeRaw from '@/components/Playground/Output/Preview/iframe.html?raw'
|
||||||
|
import HideScrollbar from '@/components/common/HideScrollbar'
|
||||||
|
|
||||||
interface RenderProps {
|
interface RenderProps {
|
||||||
iframeKey: string
|
iframeKey: string
|
||||||
compiledCode: string
|
compiledCode: string
|
||||||
|
mobileMode?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMessage {
|
interface IMessage {
|
||||||
type: 'LOADED' | 'ERROR' | 'UPDATE' | 'DONE'
|
type: 'LOADED' | 'ERROR' | 'UPDATE' | 'DONE' | 'SCALE'
|
||||||
msg: string
|
msg: string
|
||||||
data: {
|
data: {
|
||||||
compiledCode: string
|
compiledCode?: string
|
||||||
|
zoom?: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IDevice {
|
||||||
|
name: string
|
||||||
|
width: number
|
||||||
|
height: number
|
||||||
|
}
|
||||||
|
|
||||||
const getIframeUrl = (iframeRaw: string) => {
|
const getIframeUrl = (iframeRaw: string) => {
|
||||||
const shimsUrl = '//unpkg.com/es-module-shims/dist/es-module-shims.js'
|
const shimsUrl = '//unpkg.com/es-module-shims/dist/es-module-shims.js'
|
||||||
// 判断浏览器是否支持esm ,不支持esm就引入es-module-shims
|
// 判断浏览器是否支持esm ,不支持esm就引入es-module-shims
|
||||||
@@ -29,12 +40,115 @@ const getIframeUrl = (iframeRaw: string) => {
|
|||||||
|
|
||||||
const iframeUrl = getIframeUrl(iframeRaw)
|
const iframeUrl = getIframeUrl(iframeRaw)
|
||||||
|
|
||||||
const Render = ({ iframeKey, compiledCode }: RenderProps) => {
|
const Render = ({ iframeKey, compiledCode, mobileMode = false }: RenderProps) => {
|
||||||
const iframeRef = useRef<HTMLIFrameElement>(null)
|
const iframeRef = useRef<HTMLIFrameElement>(null)
|
||||||
const [loaded, setLoaded] = useState(false)
|
const [isLoaded, setIsLoaded] = useState(false)
|
||||||
|
const [selectedDevice, setSelectedDevice] = useState('Pixel 7')
|
||||||
|
const [zoom, setZoom] = useState(1)
|
||||||
|
const [isRotate, setIsRotate] = useState(false)
|
||||||
|
|
||||||
|
const devices: IDevice[] = [
|
||||||
|
{
|
||||||
|
name: 'iPhone SE',
|
||||||
|
width: 375,
|
||||||
|
height: 667
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'iPhone XR',
|
||||||
|
width: 414,
|
||||||
|
height: 896
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'iPhone 12 Pro',
|
||||||
|
width: 390,
|
||||||
|
height: 844
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'iPhone 14 Pro Max',
|
||||||
|
width: 430,
|
||||||
|
height: 932
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Pixel 7',
|
||||||
|
width: 412,
|
||||||
|
height: 915
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Samsung Galaxy S8+',
|
||||||
|
width: 360,
|
||||||
|
height: 740
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Samsung Galaxy S20 Ultra',
|
||||||
|
width: 412,
|
||||||
|
height: 915
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'iPad Mini',
|
||||||
|
width: 768,
|
||||||
|
height: 1024
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'iPad Air',
|
||||||
|
width: 820,
|
||||||
|
height: 1180
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'iPad Pro',
|
||||||
|
width: 1024,
|
||||||
|
height: 1366
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Surface Pro 7',
|
||||||
|
width: 912,
|
||||||
|
height: 1368
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Surface Duo',
|
||||||
|
width: 540,
|
||||||
|
height: 720
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Galaxy Fold',
|
||||||
|
width: 280,
|
||||||
|
height: 653
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Asus Zenbook Fold',
|
||||||
|
width: 853,
|
||||||
|
height: 1280
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Samsung Galaxy A51/71',
|
||||||
|
width: 412,
|
||||||
|
height: 914
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Nest Hub',
|
||||||
|
width: 1024,
|
||||||
|
height: 600
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Nest Hub Max',
|
||||||
|
width: 1280,
|
||||||
|
height: 800
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const handleOnChangeDevice = (e: ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
setSelectedDevice(e.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnChangeZoom = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setZoom(Number(e.target.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnRotateDevice = () => {
|
||||||
|
setIsRotate(!isRotate)
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loaded) {
|
if (isLoaded) {
|
||||||
iframeRef.current?.contentWindow?.postMessage(
|
iframeRef.current?.contentWindow?.postMessage(
|
||||||
{
|
{
|
||||||
type: 'UPDATE',
|
type: 'UPDATE',
|
||||||
@@ -43,16 +157,97 @@ const Render = ({ iframeKey, compiledCode }: RenderProps) => {
|
|||||||
'*'
|
'*'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, [compiledCode, loaded])
|
}, [isLoaded, compiledCode])
|
||||||
|
|
||||||
return (
|
useEffect(() => {
|
||||||
|
if (isLoaded) {
|
||||||
|
iframeRef.current?.contentWindow?.postMessage(
|
||||||
|
{
|
||||||
|
type: 'SCALE',
|
||||||
|
data: { zoom: zoom }
|
||||||
|
} as IMessage,
|
||||||
|
'*'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [isLoaded, zoom])
|
||||||
|
|
||||||
|
return mobileMode ? (
|
||||||
|
<>
|
||||||
|
<HideScrollbar
|
||||||
|
className={'mobile-mode-background'}
|
||||||
|
isShowVerticalScrollbar
|
||||||
|
isShowHorizontalScrollbar
|
||||||
|
autoHideWaitingTime={1000}
|
||||||
|
>
|
||||||
|
<div className={'mobile-mode-content'} style={{ zoom }}>
|
||||||
|
<div className={`device${isRotate ? ' rotate' : ''}`}>
|
||||||
|
<div className={`device-header${isRotate ? ' rotate' : ''}`} />
|
||||||
|
<div
|
||||||
|
className={`device-content${isRotate ? ' rotate' : ''}`}
|
||||||
|
style={{
|
||||||
|
width: isRotate
|
||||||
|
? devices.find((value) => value.name === selectedDevice)
|
||||||
|
?.height ?? 915
|
||||||
|
: devices.find((value) => value.name === selectedDevice)
|
||||||
|
?.width ?? 412,
|
||||||
|
height: isRotate
|
||||||
|
? devices.find((value) => value.name === selectedDevice)
|
||||||
|
?.width ?? 412
|
||||||
|
: devices.find((value) => value.name === selectedDevice)
|
||||||
|
?.height ?? 915
|
||||||
|
}}
|
||||||
|
>
|
||||||
<iframe
|
<iframe
|
||||||
data-component={'playground-output-preview-render'}
|
data-component={'playground-output-preview-render'}
|
||||||
key={iframeKey}
|
key={iframeKey}
|
||||||
ref={iframeRef}
|
ref={iframeRef}
|
||||||
src={iframeUrl}
|
src={iframeUrl}
|
||||||
onLoad={() => setLoaded(true)}
|
onLoad={() => setIsLoaded(true)}
|
||||||
sandbox="allow-downloads allow-forms allow-modals allow-scripts"
|
sandbox="allow-downloads allow-forms allow-modals allow-scripts"
|
||||||
|
allow={'clipboard-read; clipboard-write'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={`device-footer${isRotate ? ' rotate' : ''}`} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</HideScrollbar>
|
||||||
|
|
||||||
|
<div className={'switch-device'}>
|
||||||
|
<IconOxygenMobile fill={COLOR_FONT_MAIN} />
|
||||||
|
<select value={selectedDevice} onChange={handleOnChangeDevice}>
|
||||||
|
{devices.map((value) => (
|
||||||
|
<option value={value.name}>{value.name}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<div className={'rotate-device'} title={'旋转屏幕'} onClick={handleOnRotateDevice}>
|
||||||
|
{isRotate ? (
|
||||||
|
<IconOxygenRotateRight fill={COLOR_FONT_MAIN} />
|
||||||
|
) : (
|
||||||
|
<IconOxygenRotateLeft fill={COLOR_FONT_MAIN} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={'switch-zoom'}>
|
||||||
|
<IconOxygenZoom fill={COLOR_FONT_MAIN} />
|
||||||
|
<input
|
||||||
|
type={'range'}
|
||||||
|
min={0.5}
|
||||||
|
max={2}
|
||||||
|
step={0.1}
|
||||||
|
value={zoom}
|
||||||
|
onChange={handleOnChangeZoom}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<iframe
|
||||||
|
data-component={'playground-output-preview-render'}
|
||||||
|
key={iframeKey}
|
||||||
|
ref={iframeRef}
|
||||||
|
src={iframeUrl}
|
||||||
|
onLoad={() => setIsLoaded(true)}
|
||||||
|
sandbox="allow-downloads allow-forms allow-modals allow-scripts"
|
||||||
|
allow={'clipboard-read; clipboard-write'}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
window.addEventListener("message", ({ data }) => {
|
window.addEventListener("message", ({ data }) => {
|
||||||
if (data?.type === "UPDATE") {
|
if (data?.type === "UPDATE") {
|
||||||
// Record old styles that need to be removed
|
// Record old styles that need to be removed
|
||||||
const appStyleElement = document.querySelectorAll("style[id^=\"style_\"]") || [];
|
const appStyleElement = document.querySelectorAll("style:not(style[id$=\"_oxygen_base_style.css\"])") || [];
|
||||||
|
|
||||||
// Remove old app
|
// Remove old app
|
||||||
const appSrcElement = document.querySelector("#appSrc");
|
const appSrcElement = document.querySelector("#appSrc");
|
||||||
@@ -36,6 +36,10 @@
|
|||||||
document.body.appendChild(script);
|
document.body.appendChild(script);
|
||||||
URL.revokeObjectURL(oldSrc);
|
URL.revokeObjectURL(oldSrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data?.type === "SCALE") {
|
||||||
|
document.getElementById("root").style.zoom = data.data.zoom
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<script type="module" id="appSrc"></script>
|
<script type="module" id="appSrc"></script>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ interface PreviewProps {
|
|||||||
entryPoint: string
|
entryPoint: string
|
||||||
preExpansionCode?: string
|
preExpansionCode?: string
|
||||||
postExpansionCode?: string
|
postExpansionCode?: string
|
||||||
|
mobileMode?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const Preview = ({
|
const Preview = ({
|
||||||
@@ -18,7 +19,8 @@ const Preview = ({
|
|||||||
importMap,
|
importMap,
|
||||||
entryPoint,
|
entryPoint,
|
||||||
preExpansionCode = '',
|
preExpansionCode = '',
|
||||||
postExpansionCode = ''
|
postExpansionCode = '',
|
||||||
|
mobileMode = false
|
||||||
}: PreviewProps) => {
|
}: PreviewProps) => {
|
||||||
const [errorMsg, setErrorMsg] = useState('')
|
const [errorMsg, setErrorMsg] = useState('')
|
||||||
const [compiledCode, setCompiledCode] = useState('')
|
const [compiledCode, setCompiledCode] = useState('')
|
||||||
@@ -41,7 +43,7 @@ const Preview = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-component={'playground-preview'}>
|
<div data-component={'playground-preview'}>
|
||||||
<Render iframeKey={iframeKey} compiledCode={compiledCode} />
|
<Render iframeKey={iframeKey} compiledCode={compiledCode} mobileMode={mobileMode} />
|
||||||
{errorMsg && <div className={'playground-error-message'}>{errorMsg}</div>}
|
{errorMsg && <div className={'playground-error-message'}>{errorMsg}</div>}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
[data-component=playground-preview] {
|
[data-component=playground-preview] {
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
height: 0;
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
.playground-error-message {
|
.playground-error-message {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@@ -1,6 +1,80 @@
|
|||||||
|
@use '@/assets/css/constants' as constants;
|
||||||
|
|
||||||
[data-component=playground-output-preview-render] {
|
[data-component=playground-output-preview-render] {
|
||||||
border: none;
|
border: none;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mobile-mode-background {
|
||||||
|
background-color: rgba(204, 204, 204, 0.66);
|
||||||
|
|
||||||
|
.mobile-mode-content {
|
||||||
|
padding: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #EEEFF2;
|
||||||
|
width: fit-content;
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: 40px;
|
||||||
|
|
||||||
|
&.rotate {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-header {
|
||||||
|
margin: 20px auto;
|
||||||
|
width: 60px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: #C8C9CC;
|
||||||
|
|
||||||
|
&.rotate {
|
||||||
|
margin: auto 20px;
|
||||||
|
width: 10px;
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-content {
|
||||||
|
margin: 0 10px;
|
||||||
|
background-color: white;
|
||||||
|
|
||||||
|
&.rotate {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-footer {
|
||||||
|
margin: 20px auto;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #C8C9CC;
|
||||||
|
|
||||||
|
&.rotate {
|
||||||
|
margin: auto 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-device, .switch-zoom {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-device {
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-zoom {
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ interface OutputProps {
|
|||||||
entryPoint: string
|
entryPoint: string
|
||||||
preExpansionCode?: string
|
preExpansionCode?: string
|
||||||
postExpansionCode?: string
|
postExpansionCode?: string
|
||||||
|
mobileMode?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const Output = ({
|
const Output = ({
|
||||||
@@ -19,7 +20,8 @@ const Output = ({
|
|||||||
importMap,
|
importMap,
|
||||||
entryPoint,
|
entryPoint,
|
||||||
preExpansionCode,
|
preExpansionCode,
|
||||||
postExpansionCode
|
postExpansionCode,
|
||||||
|
mobileMode = false
|
||||||
}: OutputProps) => {
|
}: OutputProps) => {
|
||||||
const [selectedTab, setSelectedTab] = useState('Preview')
|
const [selectedTab, setSelectedTab] = useState('Preview')
|
||||||
|
|
||||||
@@ -42,6 +44,7 @@ const Output = ({
|
|||||||
entryPoint={entryPoint}
|
entryPoint={entryPoint}
|
||||||
preExpansionCode={preExpansionCode}
|
preExpansionCode={preExpansionCode}
|
||||||
postExpansionCode={postExpansionCode}
|
postExpansionCode={postExpansionCode}
|
||||||
|
mobileMode={mobileMode}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{selectedTab === 'Transform' && <Transform file={files[selectedFileName]} />}
|
{selectedTab === 'Transform' && <Transform file={files[selectedFileName]} />}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import esbuild, { Loader, OnLoadArgs, Plugin, PluginBuild } from 'esbuild-wasm'
|
import esbuild, { Loader, OnLoadArgs, Plugin, PluginBuild } from 'esbuild-wasm'
|
||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import esbuildWasmUrl from 'esbuild-wasm/esbuild.wasm?url'
|
||||||
import { IFiles, IImportMap } from '@/components/Playground/shared'
|
import { IFiles, IImportMap } from '@/components/Playground/shared'
|
||||||
import { cssToJs, jsonToJs, addReactImport } from '@/components/Playground/files'
|
import { cssToJs, jsonToJs, addReactImport } from '@/components/Playground/files'
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ class Compiler {
|
|||||||
void esbuild
|
void esbuild
|
||||||
.initialize({
|
.initialize({
|
||||||
worker: true,
|
worker: true,
|
||||||
wasmURL: 'https://esm.sh/esbuild-wasm@0.20.1/esbuild.wasm'
|
wasmURL: esbuildWasmUrl
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.init = true
|
this.init = true
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ interface HideScrollbarProps
|
|||||||
minHeight?: string | number
|
minHeight?: string | number
|
||||||
scrollbarWidth?: string | number
|
scrollbarWidth?: string | number
|
||||||
autoHideWaitingTime?: number
|
autoHideWaitingTime?: number
|
||||||
|
scrollbarAsidePadding?: number
|
||||||
|
scrollbarEdgePadding?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HideScrollbarElement {
|
export interface HideScrollbarElement {
|
||||||
@@ -74,6 +76,8 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
|||||||
children,
|
children,
|
||||||
style,
|
style,
|
||||||
className,
|
className,
|
||||||
|
scrollbarAsidePadding = 12,
|
||||||
|
scrollbarEdgePadding = 4,
|
||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
@@ -179,16 +183,16 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
|||||||
const [verticalScrollbarWidth, setVerticalScrollbarWidth] = useState(0)
|
const [verticalScrollbarWidth, setVerticalScrollbarWidth] = useState(0)
|
||||||
const [verticalScrollbarLength, setVerticalScrollbarLength] = useState(100)
|
const [verticalScrollbarLength, setVerticalScrollbarLength] = useState(100)
|
||||||
const [verticalScrollbarPosition, setVerticalScrollbarPosition] = useState(0)
|
const [verticalScrollbarPosition, setVerticalScrollbarPosition] = useState(0)
|
||||||
const [verticalScrollbarOnClick, setVerticalScrollbarOnClick] = useState(false)
|
const [isVerticalScrollbarOnClick, setIsVerticalScrollbarOnClick] = useState(false)
|
||||||
const [verticalScrollbarOnTouch, setVerticalScrollbarOnTouch] = useState(false)
|
const [isVerticalScrollbarOnTouch, setIsVerticalScrollbarOnTouch] = useState(false)
|
||||||
const [verticalScrollbarAutoHide, setVerticalScrollbarAutoHide] = useState(false)
|
const [isVerticalScrollbarAutoHide, setIsVerticalScrollbarAutoHide] = useState(false)
|
||||||
|
|
||||||
const [horizontalScrollbarWidth, setHorizontalScrollbarWidth] = useState(0)
|
const [horizontalScrollbarWidth, setHorizontalScrollbarWidth] = useState(0)
|
||||||
const [horizontalScrollbarLength, setHorizontalScrollbarLength] = useState(100)
|
const [horizontalScrollbarLength, setHorizontalScrollbarLength] = useState(100)
|
||||||
const [horizontalScrollbarPosition, setHorizontalScrollbarPosition] = useState(0)
|
const [horizontalScrollbarPosition, setHorizontalScrollbarPosition] = useState(0)
|
||||||
const [horizontalScrollbarOnClick, setHorizontalScrollbarOnClick] = useState(false)
|
const [isHorizontalScrollbarOnClick, setIsHorizontalScrollbarOnClick] = useState(false)
|
||||||
const [horizontalScrollbarOnTouch, setHorizontalScrollbarOnTouch] = useState(false)
|
const [isHorizontalScrollbarOnTouch, setIsHorizontalScrollbarOnTouch] = useState(false)
|
||||||
const [horizontalScrollbarAutoHide, setHorizontalScrollbarAutoHide] = useState(false)
|
const [isHorizontalScrollbarAutoHide, setIsHorizontalScrollbarAutoHide] = useState(false)
|
||||||
|
|
||||||
const isPreventAnyScroll =
|
const isPreventAnyScroll =
|
||||||
isPreventScroll || isPreventVerticalScroll || isPreventHorizontalScroll
|
isPreventScroll || isPreventVerticalScroll || isPreventHorizontalScroll
|
||||||
@@ -197,10 +201,10 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
|||||||
if (autoHideWaitingTime === undefined) {
|
if (autoHideWaitingTime === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setVerticalScrollbarAutoHide(false)
|
setIsVerticalScrollbarAutoHide(false)
|
||||||
if (autoHideWaitingTime > 0) {
|
if (autoHideWaitingTime > 0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setVerticalScrollbarAutoHide(true)
|
setIsVerticalScrollbarAutoHide(true)
|
||||||
}, autoHideWaitingTime)
|
}, autoHideWaitingTime)
|
||||||
}
|
}
|
||||||
}, [autoHideWaitingTime, verticalScrollbarPosition])
|
}, [autoHideWaitingTime, verticalScrollbarPosition])
|
||||||
@@ -209,10 +213,10 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
|||||||
if (autoHideWaitingTime === undefined) {
|
if (autoHideWaitingTime === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setHorizontalScrollbarAutoHide(false)
|
setIsHorizontalScrollbarAutoHide(false)
|
||||||
if (autoHideWaitingTime > 0) {
|
if (autoHideWaitingTime > 0) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setHorizontalScrollbarAutoHide(true)
|
setIsHorizontalScrollbarAutoHide(true)
|
||||||
}, autoHideWaitingTime)
|
}, autoHideWaitingTime)
|
||||||
}
|
}
|
||||||
}, [autoHideWaitingTime, horizontalScrollbarPosition])
|
}, [autoHideWaitingTime, horizontalScrollbarPosition])
|
||||||
@@ -314,20 +318,20 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
|||||||
}
|
}
|
||||||
switch (scrollbarFlag) {
|
switch (scrollbarFlag) {
|
||||||
case 'vertical':
|
case 'vertical':
|
||||||
setVerticalScrollbarOnClick(true)
|
setIsVerticalScrollbarOnClick(true)
|
||||||
break
|
break
|
||||||
case 'horizontal':
|
case 'horizontal':
|
||||||
setHorizontalScrollbarOnClick(true)
|
setIsHorizontalScrollbarOnClick(true)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'up':
|
case 'up':
|
||||||
case 'leave':
|
case 'leave':
|
||||||
setVerticalScrollbarOnClick(false)
|
setIsVerticalScrollbarOnClick(false)
|
||||||
setHorizontalScrollbarOnClick(false)
|
setIsHorizontalScrollbarOnClick(false)
|
||||||
break
|
break
|
||||||
case 'move':
|
case 'move':
|
||||||
if (verticalScrollbarOnClick) {
|
if (isVerticalScrollbarOnClick) {
|
||||||
rootRef.current?.scrollTo({
|
rootRef.current?.scrollTo({
|
||||||
top:
|
top:
|
||||||
rootRef.current?.scrollTop +
|
rootRef.current?.scrollTop +
|
||||||
@@ -337,7 +341,7 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
|||||||
behavior: 'instant'
|
behavior: 'instant'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (horizontalScrollbarOnClick) {
|
if (isHorizontalScrollbarOnClick) {
|
||||||
rootRef.current?.scrollTo({
|
rootRef.current?.scrollTo({
|
||||||
left:
|
left:
|
||||||
rootRef.current?.scrollLeft +
|
rootRef.current?.scrollLeft +
|
||||||
@@ -368,23 +372,23 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
|||||||
}
|
}
|
||||||
switch (scrollbarFlag) {
|
switch (scrollbarFlag) {
|
||||||
case 'vertical':
|
case 'vertical':
|
||||||
setVerticalScrollbarOnTouch(true)
|
setIsVerticalScrollbarOnTouch(true)
|
||||||
break
|
break
|
||||||
case 'horizontal':
|
case 'horizontal':
|
||||||
setHorizontalScrollbarOnTouch(true)
|
setIsHorizontalScrollbarOnTouch(true)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'end':
|
case 'end':
|
||||||
case 'cancel':
|
case 'cancel':
|
||||||
setVerticalScrollbarOnTouch(false)
|
setIsVerticalScrollbarOnTouch(false)
|
||||||
setHorizontalScrollbarOnTouch(false)
|
setIsHorizontalScrollbarOnTouch(false)
|
||||||
break
|
break
|
||||||
case 'move':
|
case 'move':
|
||||||
if (event.touches.length !== 1) {
|
if (event.touches.length !== 1) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (verticalScrollbarOnTouch) {
|
if (isVerticalScrollbarOnTouch) {
|
||||||
rootRef.current?.scrollTo({
|
rootRef.current?.scrollTo({
|
||||||
top:
|
top:
|
||||||
rootRef.current?.scrollTop +
|
rootRef.current?.scrollTop +
|
||||||
@@ -395,7 +399,7 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
|||||||
behavior: 'instant'
|
behavior: 'instant'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (horizontalScrollbarOnTouch) {
|
if (isHorizontalScrollbarOnTouch) {
|
||||||
rootRef.current?.scrollTo({
|
rootRef.current?.scrollTo({
|
||||||
left:
|
left:
|
||||||
rootRef.current?.scrollLeft +
|
rootRef.current?.scrollLeft +
|
||||||
@@ -567,7 +571,7 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
|||||||
(!isHiddenVerticalScrollbarWhenFull || verticalScrollbarLength < 100) && (
|
(!isHiddenVerticalScrollbarWhenFull || verticalScrollbarLength < 100) && (
|
||||||
<div
|
<div
|
||||||
className={`scrollbar vertical-scrollbar${
|
className={`scrollbar vertical-scrollbar${
|
||||||
verticalScrollbarAutoHide ? ' hide' : ''
|
isVerticalScrollbarAutoHide ? ' hide' : ''
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
height: maskRef.current
|
height: maskRef.current
|
||||||
@@ -578,7 +582,8 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
|||||||
? maskRef.current?.clientLeft +
|
? maskRef.current?.clientLeft +
|
||||||
maskRef.current?.clientWidth -
|
maskRef.current?.clientWidth -
|
||||||
1
|
1
|
||||||
: undefined
|
: undefined,
|
||||||
|
padding: `${scrollbarAsidePadding}px ${scrollbarEdgePadding}px`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={'box'} style={{ width: scrollbarWidth }}>
|
<div className={'box'} style={{ width: scrollbarWidth }}>
|
||||||
@@ -609,7 +614,7 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
|||||||
horizontalScrollbarLength < 100) && (
|
horizontalScrollbarLength < 100) && (
|
||||||
<div
|
<div
|
||||||
className={`scrollbar horizontal-scrollbar${
|
className={`scrollbar horizontal-scrollbar${
|
||||||
horizontalScrollbarAutoHide ? ' hide' : ''
|
isHorizontalScrollbarAutoHide ? ' hide' : ''
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
width: maskRef.current
|
width: maskRef.current
|
||||||
@@ -620,7 +625,8 @@ const HideScrollbar = forwardRef<HideScrollbarElement, HideScrollbarProps>(
|
|||||||
? maskRef.current?.clientTop +
|
? maskRef.current?.clientTop +
|
||||||
maskRef.current?.clientHeight -
|
maskRef.current?.clientHeight -
|
||||||
1
|
1
|
||||||
: undefined
|
: undefined,
|
||||||
|
padding: `${scrollbarAsidePadding}px ${scrollbarEdgePadding}px`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={'box'} style={{ height: scrollbarWidth }}>
|
<div className={'box'} style={{ height: scrollbarWidth }}>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ interface LoadingMaskProps extends PropsWithChildren {
|
|||||||
hidden?: boolean
|
hidden?: boolean
|
||||||
maskContent?: ReactNode
|
maskContent?: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
const LoadingMask = (props: LoadingMaskProps) => {
|
const LoadingMask = (props: LoadingMaskProps) => {
|
||||||
const loadingIcon = (
|
const loadingIcon = (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import Icon from '@ant-design/icons'
|
|||||||
import { COLOR_ERROR } from '@/constants/common.constants'
|
import { COLOR_ERROR } from '@/constants/common.constants'
|
||||||
import { getRedirectUrl } from '@/util/route'
|
import { getRedirectUrl } from '@/util/route'
|
||||||
import { getAvatar, getLoginStatus, getNickname, removeToken } from '@/util/auth'
|
import { getAvatar, getLoginStatus, getNickname, removeToken } from '@/util/auth'
|
||||||
|
import { navigateToLogin, navigateToUser } from '@/util/navigation'
|
||||||
import { r_auth_logout } from '@/services/auth'
|
import { r_auth_logout } from '@/services/auth'
|
||||||
|
|
||||||
const Footer = () => {
|
const Footer = () => {
|
||||||
@@ -9,24 +10,24 @@ const Footer = () => {
|
|||||||
const lastMatch = matches.reduce((_, second) => second)
|
const lastMatch = matches.reduce((_, second) => second)
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [exiting, setExiting] = useState(false)
|
const [isExiting, setIsExiting] = useState(false)
|
||||||
const [nickname, setNickname] = useState('')
|
const [nickname, setNickname] = useState('')
|
||||||
const [avatar, setAvatar] = useState('')
|
const [avatar, setAvatar] = useState('')
|
||||||
|
|
||||||
const handleClickAvatar = () => {
|
const handleClickAvatar = () => {
|
||||||
if (getLoginStatus()) {
|
if (getLoginStatus()) {
|
||||||
navigate('/user')
|
navigateToUser(navigate)
|
||||||
} else {
|
} else {
|
||||||
navigate(getRedirectUrl('/login', `${lastMatch.pathname}${location.search}`))
|
navigateToLogin(navigate, undefined, `${lastMatch.pathname}${location.search}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
if (exiting) {
|
if (isExiting) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setExiting(true)
|
setIsExiting(true)
|
||||||
void r_auth_logout().finally(() => {
|
void r_auth_logout().finally(() => {
|
||||||
removeToken()
|
removeToken()
|
||||||
notification.info({
|
notification.info({
|
||||||
@@ -82,8 +83,8 @@ const Footer = () => {
|
|||||||
<div className={'content'}>
|
<div className={'content'}>
|
||||||
<span hidden={!getLoginStatus()} className={'icon-exit'} onClick={handleLogout}>
|
<span hidden={!getLoginStatus()} className={'icon-exit'} onClick={handleLogout}>
|
||||||
<Icon
|
<Icon
|
||||||
component={exiting ? IconOxygenLoading : IconOxygenExit}
|
component={isExiting ? IconOxygenLoading : IconOxygenExit}
|
||||||
spin={exiting}
|
spin={isExiting}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,16 +3,17 @@ import Icon from '@ant-design/icons'
|
|||||||
import Submenu from '@/components/common/Sidebar/Submenu'
|
import Submenu from '@/components/common/Sidebar/Submenu'
|
||||||
|
|
||||||
type ItemProps = {
|
type ItemProps = {
|
||||||
icon?: IconComponent
|
icon?: IconComponent | string
|
||||||
text?: string
|
text?: string
|
||||||
path: string
|
path: string
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
|
extend?: ReactNode
|
||||||
end?: boolean
|
end?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const Item = (props: ItemProps) => {
|
const Item = (props: ItemProps) => {
|
||||||
const [submenuTop, setSubmenuTop] = useState(0)
|
const [submenuTop, setSubmenuTop] = useState(Number.MAX_VALUE)
|
||||||
const [submenuLeft, setSubmenuLeft] = useState(0)
|
const [submenuLeft, setSubmenuLeft] = useState(Number.MAX_VALUE)
|
||||||
|
|
||||||
const showSubmenu = (e: MouseEvent) => {
|
const showSubmenu = (e: MouseEvent) => {
|
||||||
const parentElement = e.currentTarget.parentElement
|
const parentElement = e.currentTarget.parentElement
|
||||||
@@ -41,10 +42,21 @@ const Item = (props: ItemProps) => {
|
|||||||
isPending ? 'pending' : isActive ? 'active' : ''
|
isPending ? 'pending' : isActive ? 'active' : ''
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
{props.icon && (
|
||||||
<div className={'icon-box'}>
|
<div className={'icon-box'}>
|
||||||
{props.icon && <Icon className={'icon'} component={props.icon} />}
|
{typeof props.icon === 'string' ? (
|
||||||
|
<img
|
||||||
|
className={'icon'}
|
||||||
|
src={`data:image/svg+xml;base64,${props.icon}`}
|
||||||
|
alt={'icon'}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Icon className={'icon'} component={props.icon} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
<span className={'text'}>{props.text}</span>
|
<span className={'text'}>{props.text}</span>
|
||||||
|
<div className={'extend'}>{props.extend}</div>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
{props.children && (
|
{props.children && (
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const Separate = ({
|
|||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>) => {
|
}: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>) => {
|
||||||
return <div className={`separate ${className ? ` ${className}` : ''}`} {...props} />
|
return <div className={`separate${className ? ` ${className}` : ''}`} {...props} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Separate
|
export default Separate
|
||||||
|
|||||||
@@ -17,18 +17,18 @@ interface SidebarProps extends PropsWithChildren {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Sidebar = (props: SidebarProps) => {
|
const Sidebar = (props: SidebarProps) => {
|
||||||
const [hideSidebar, setHideSidebar] = useState(getLocalStorage('HIDE_SIDEBAR') === 'true')
|
const [isHideSidebar, setIsHideSidebar] = useState(getLocalStorage('HIDE_SIDEBAR') === 'true')
|
||||||
|
|
||||||
const switchSidebar = () => {
|
const switchSidebar = () => {
|
||||||
setLocalStorage('HIDE_SIDEBAR', !hideSidebar ? 'true' : 'false')
|
setLocalStorage('HIDE_SIDEBAR', !isHideSidebar ? 'true' : 'false')
|
||||||
setHideSidebar(!hideSidebar)
|
setIsHideSidebar(!isHideSidebar)
|
||||||
props.onSidebarSwitch?.(hideSidebar)
|
props.onSidebarSwitch?.(isHideSidebar)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className={`sidebar${hideSidebar ? ' hide' : ''}`}
|
className={`sidebar${isHideSidebar ? ' hide' : ''}`}
|
||||||
style={{ width: props.width ?? 'clamp(180px, 20vw, 240px)' }}
|
style={{ width: props.width ?? 'clamp(180px, 20vw, 240px)' }}
|
||||||
>
|
>
|
||||||
<div className={'title'}>
|
<div className={'title'}>
|
||||||
|
|||||||
57
src/renderer/src/components/common/UrlCard.tsx
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { DetailedHTMLProps, HTMLAttributes, ReactNode } from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||||
|
import '@/assets/css/components/common/url-card.scss'
|
||||||
|
import Card from '@/components/common/Card'
|
||||||
|
import FlexBox from '@/components/common/FlexBox'
|
||||||
|
|
||||||
|
interface UrlCardProps extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||||
|
icon: IconComponent
|
||||||
|
description?: ReactNode
|
||||||
|
options?: TiltOptions
|
||||||
|
url?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const UrlCard = ({
|
||||||
|
style,
|
||||||
|
icon,
|
||||||
|
description,
|
||||||
|
options = {
|
||||||
|
reverse: true,
|
||||||
|
max: 8,
|
||||||
|
glare: true,
|
||||||
|
scale: 1.03
|
||||||
|
},
|
||||||
|
url,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: UrlCardProps) => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const cardRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
cardRef.current && VanillaTilt.init(cardRef.current, options)
|
||||||
|
}, [options])
|
||||||
|
|
||||||
|
const handleCardOnClick = () => {
|
||||||
|
url && navigate(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
data-component={'component-url-card'}
|
||||||
|
style={{ overflow: 'visible', ...style }}
|
||||||
|
{...props}
|
||||||
|
ref={cardRef}
|
||||||
|
onClick={handleCardOnClick}
|
||||||
|
>
|
||||||
|
<FlexBox className={'url-card'}>
|
||||||
|
<Icon component={icon} className={'icon'} />
|
||||||
|
<div className={'text'}>{children}</div>
|
||||||
|
<div className={'description'}>{description}</div>
|
||||||
|
</FlexBox>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UrlCard
|
||||||
27
src/renderer/src/components/dnd/DragHandle.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { HandleContextInst } from '@/components/dnd/HandleContext'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import '@/assets/css/components/dnd/drag-handle.scss'
|
||||||
|
|
||||||
|
interface DragHandleProps {
|
||||||
|
padding?: string | number
|
||||||
|
}
|
||||||
|
|
||||||
|
const DragHandle = ({ padding }: DragHandleProps) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
|
const { attributes, listeners, ref } = useContext(HandleContextInst)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
data-component={'component-drag-handle'}
|
||||||
|
style={{ padding }}
|
||||||
|
ref={ref}
|
||||||
|
className={'drag-handle'}
|
||||||
|
{...attributes}
|
||||||
|
{...listeners}
|
||||||
|
>
|
||||||
|
<Icon component={IconOxygenHandle} />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DragHandle
|
||||||
47
src/renderer/src/components/dnd/Draggable.tsx
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { CSSProperties, PropsWithChildren } from 'react'
|
||||||
|
import { useDraggable } from '@dnd-kit/core'
|
||||||
|
import { HandleContext, HandleContextInst } from '@/components/dnd/HandleContext'
|
||||||
|
|
||||||
|
interface DraggableProps extends PropsWithChildren {
|
||||||
|
id: string
|
||||||
|
data: ToolMenuItem
|
||||||
|
}
|
||||||
|
|
||||||
|
const Draggable = ({ id, data, children }: DraggableProps) => {
|
||||||
|
const {
|
||||||
|
attributes,
|
||||||
|
isDragging,
|
||||||
|
listeners,
|
||||||
|
setNodeRef: draggableRef,
|
||||||
|
setActivatorNodeRef,
|
||||||
|
transform
|
||||||
|
} = useDraggable({
|
||||||
|
id,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
const context = useMemo<HandleContext>(
|
||||||
|
() => ({
|
||||||
|
attributes,
|
||||||
|
listeners,
|
||||||
|
ref: setActivatorNodeRef
|
||||||
|
}),
|
||||||
|
[attributes, listeners, setActivatorNodeRef]
|
||||||
|
)
|
||||||
|
const style: CSSProperties | undefined = transform
|
||||||
|
? {
|
||||||
|
opacity: isDragging ? 0 : undefined,
|
||||||
|
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
|
||||||
|
zIndex: 10000
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HandleContextInst.Provider value={context}>
|
||||||
|
<div ref={draggableRef} style={style}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</HandleContextInst.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Draggable
|
||||||
22
src/renderer/src/components/dnd/DraggableOverlay.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { PropsWithChildren } from 'react'
|
||||||
|
import { defaultDropAnimationSideEffects, DragOverlay, DropAnimation } from '@dnd-kit/core'
|
||||||
|
|
||||||
|
interface DraggableOverlayProps extends PropsWithChildren {
|
||||||
|
isDelete?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const dropAnimationConfig: DropAnimation = {
|
||||||
|
sideEffects: defaultDropAnimationSideEffects({
|
||||||
|
styles: {
|
||||||
|
active: {
|
||||||
|
opacity: '0.4'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const DraggableOverlay = ({ children }: DraggableOverlayProps) => {
|
||||||
|
return <DragOverlay dropAnimation={dropAnimationConfig}>{children}</DragOverlay>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DraggableOverlay
|
||||||
14
src/renderer/src/components/dnd/DropMask.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import '@/assets/css/components/dnd/drop-mask.scss'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
|
||||||
|
const DropMask = () => {
|
||||||
|
return (
|
||||||
|
<div data-component={'component-drop-mask'}>
|
||||||
|
<div className={'drop-mask-border'}>
|
||||||
|
<Icon component={IconOxygenReceive} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DropMask
|
||||||
16
src/renderer/src/components/dnd/Droppable.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { DetailedHTMLProps, HTMLAttributes } from 'react'
|
||||||
|
import { useDroppable } from '@dnd-kit/core'
|
||||||
|
|
||||||
|
interface DroppableProps extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const Droppable = ({ id, ...props }: DroppableProps) => {
|
||||||
|
const { setNodeRef: droppableRef } = useDroppable({
|
||||||
|
id
|
||||||
|
})
|
||||||
|
|
||||||
|
return <div {...props} ref={droppableRef} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Droppable
|
||||||
13
src/renderer/src/components/dnd/HandleContext.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { DraggableSyntheticListeners } from '@dnd-kit/core'
|
||||||
|
|
||||||
|
export interface HandleContext {
|
||||||
|
attributes: Record<string, any>
|
||||||
|
listeners: DraggableSyntheticListeners
|
||||||
|
ref(node: HTMLElement | null): void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HandleContextInst = createContext<HandleContext>({
|
||||||
|
attributes: {},
|
||||||
|
listeners: undefined,
|
||||||
|
ref() {}
|
||||||
|
})
|
||||||
54
src/renderer/src/components/dnd/Sortable.tsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { CSSProperties, PropsWithChildren } from 'react'
|
||||||
|
import { useSortable } from '@dnd-kit/sortable'
|
||||||
|
import { HandleContext, HandleContextInst } from '@/components/dnd/HandleContext'
|
||||||
|
|
||||||
|
interface SortableProps extends PropsWithChildren {
|
||||||
|
id: string
|
||||||
|
data: ToolMenuItem
|
||||||
|
isDelete?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const Sortable = ({ id, data, isDelete, children }: SortableProps) => {
|
||||||
|
const {
|
||||||
|
attributes,
|
||||||
|
isDragging,
|
||||||
|
listeners,
|
||||||
|
setNodeRef: draggableRef,
|
||||||
|
setActivatorNodeRef,
|
||||||
|
transform,
|
||||||
|
transition
|
||||||
|
} = useSortable({
|
||||||
|
id,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
const context = useMemo<HandleContext>(
|
||||||
|
() => ({
|
||||||
|
attributes,
|
||||||
|
listeners,
|
||||||
|
ref: setActivatorNodeRef
|
||||||
|
}),
|
||||||
|
[attributes, listeners, setActivatorNodeRef]
|
||||||
|
)
|
||||||
|
const style: CSSProperties | undefined = transform
|
||||||
|
? {
|
||||||
|
opacity: isDragging ? 0.4 : undefined,
|
||||||
|
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
|
||||||
|
zIndex: 10000,
|
||||||
|
transition
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HandleContextInst.Provider value={context}>
|
||||||
|
<div
|
||||||
|
ref={draggableRef}
|
||||||
|
style={style}
|
||||||
|
className={isDragging && isDelete ? 'delete' : undefined}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</HandleContextInst.Provider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Sortable
|
||||||
48
src/renderer/src/components/system/SettingCard.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { PropsWithChildren, ReactNode } from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import '@/assets/css/components/system/setting-card.scss'
|
||||||
|
import Card from '@/components/common/Card'
|
||||||
|
import FlexBox from '@/components/common/FlexBox'
|
||||||
|
import Permission from '@/components/common/Permission'
|
||||||
|
import LoadingMask from '@/components/common/LoadingMask'
|
||||||
|
|
||||||
|
interface SettingsCardProps extends PropsWithChildren {
|
||||||
|
icon: IconComponent
|
||||||
|
title: string
|
||||||
|
loading?: boolean
|
||||||
|
modifyOperationCode?: string[]
|
||||||
|
expand?: ReactNode
|
||||||
|
onReset?: () => void
|
||||||
|
onSave?: () => void
|
||||||
|
}
|
||||||
|
export const SettingsCard = (props: SettingsCardProps) => {
|
||||||
|
return (
|
||||||
|
<Card data-component={'component-setting-card'}>
|
||||||
|
<FlexBox className={'settings-card'}>
|
||||||
|
<FlexBox direction={'horizontal'} className={'head'}>
|
||||||
|
<Icon component={props.icon} className={'icon'} />
|
||||||
|
<div className={'title'}>{props.title}</div>
|
||||||
|
{!props.loading && (
|
||||||
|
<Permission operationCode={props.modifyOperationCode}>
|
||||||
|
{props.expand}
|
||||||
|
<AntdButton onClick={props.onReset} title={'重置'}>
|
||||||
|
<Icon component={IconOxygenBack} />
|
||||||
|
</AntdButton>
|
||||||
|
<AntdButton className={'bt-save'} onClick={props.onSave} title={'保存'}>
|
||||||
|
<Icon component={IconOxygenSave} />
|
||||||
|
</AntdButton>
|
||||||
|
</Permission>
|
||||||
|
)}
|
||||||
|
</FlexBox>
|
||||||
|
<LoadingMask
|
||||||
|
maskContent={<AntdSkeleton active paragraph={{ rows: 6 }} />}
|
||||||
|
hidden={!props.loading}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</LoadingMask>
|
||||||
|
</FlexBox>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SettingsCard
|
||||||
35
src/renderer/src/components/system/StatisticsCard.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { PropsWithChildren, ReactNode } from 'react'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import '@/assets/css/components/system/statistics-card.scss'
|
||||||
|
import Card from '@/components/common/Card'
|
||||||
|
import FlexBox from '@/components/common/FlexBox'
|
||||||
|
import LoadingMask from '@/components/common/LoadingMask'
|
||||||
|
|
||||||
|
interface StatisticsCardProps extends PropsWithChildren {
|
||||||
|
icon: IconComponent
|
||||||
|
title: ReactNode
|
||||||
|
loading?: boolean
|
||||||
|
expand?: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StatisticsCard = (props: StatisticsCardProps) => {
|
||||||
|
return (
|
||||||
|
<Card data-component={'component-statistics-card'} style={{ overflow: 'visible' }}>
|
||||||
|
<FlexBox className={'statistics-card'}>
|
||||||
|
<FlexBox direction={'horizontal'} className={'head'}>
|
||||||
|
<Icon component={props.icon} className={'icon'} />
|
||||||
|
<div className={'title'}>{props.title}</div>
|
||||||
|
{props.expand}
|
||||||
|
</FlexBox>
|
||||||
|
<LoadingMask
|
||||||
|
hidden={!props.loading}
|
||||||
|
maskContent={<AntdSkeleton active paragraph={{ rows: 6 }} />}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</LoadingMask>
|
||||||
|
</FlexBox>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StatisticsCard
|
||||||
42
src/renderer/src/components/tools/LoadMoreCard.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import VanillaTilt from 'vanilla-tilt'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import '@/assets/css/components/tools/load-more-card.scss'
|
||||||
|
import FlexBox from '@/components/common/FlexBox'
|
||||||
|
import Card from '@/components/common/Card'
|
||||||
|
|
||||||
|
interface LoadMoreCardProps {
|
||||||
|
onClick: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoadMoreCard = ({ onClick }: LoadMoreCardProps) => {
|
||||||
|
const cardRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
cardRef.current &&
|
||||||
|
VanillaTilt.init(cardRef.current, {
|
||||||
|
reverse: true,
|
||||||
|
max: 8,
|
||||||
|
glare: true,
|
||||||
|
['max-glare']: 0.3,
|
||||||
|
scale: 1.03
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
data-component={'component-load-more-card'}
|
||||||
|
style={{ overflow: 'visible' }}
|
||||||
|
ref={cardRef}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<FlexBox className={'load-more-card'}>
|
||||||
|
<div className={'icon'}>
|
||||||
|
<Icon component={IconOxygenMore} />{' '}
|
||||||
|
</div>
|
||||||
|
<div className={'text'}>加载更多</div>
|
||||||
|
</FlexBox>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoadMoreCard
|
||||||
198
src/renderer/src/components/tools/LocalCard.tsx
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
import { DetailedHTMLProps, HTMLAttributes, MouseEvent } from 'react'
|
||||||
|
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import '@/assets/css/components/tools/local-card.scss'
|
||||||
|
import { COLOR_BACKGROUND, COLOR_MAIN } from '@/constants/common.constants'
|
||||||
|
import { checkDesktop, omitText } from '@/util/common'
|
||||||
|
import { getAndroidUrl, navigateToStore, navigateToView } from '@/util/navigation'
|
||||||
|
import Card from '@/components/common/Card'
|
||||||
|
import FlexBox from '@/components/common/FlexBox'
|
||||||
|
import DragHandle from '@/components/dnd/DragHandle'
|
||||||
|
import Draggable from '@/components/dnd/Draggable'
|
||||||
|
|
||||||
|
interface StoreCardProps extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||||
|
icon: string
|
||||||
|
toolName: string
|
||||||
|
toolId: string
|
||||||
|
toolDesc: string
|
||||||
|
options?: TiltOptions
|
||||||
|
author: UserWithInfoVo
|
||||||
|
showAuthor?: boolean
|
||||||
|
ver: string
|
||||||
|
platform: Platform
|
||||||
|
supportPlatform: Platform[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const StoreCard = ({
|
||||||
|
style,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
ref,
|
||||||
|
icon,
|
||||||
|
toolName,
|
||||||
|
toolId,
|
||||||
|
toolDesc,
|
||||||
|
options = {
|
||||||
|
reverse: true,
|
||||||
|
max: 8,
|
||||||
|
glare: true,
|
||||||
|
['max-glare']: 0.3,
|
||||||
|
scale: 1.03
|
||||||
|
},
|
||||||
|
author,
|
||||||
|
showAuthor = true,
|
||||||
|
ver,
|
||||||
|
platform,
|
||||||
|
supportPlatform,
|
||||||
|
...props
|
||||||
|
}: StoreCardProps) => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const [modal, contextHolder] = AntdModal.useModal()
|
||||||
|
const cardRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
cardRef.current && VanillaTilt.init(cardRef.current, options)
|
||||||
|
}, [options])
|
||||||
|
|
||||||
|
const handleCardOnClick = () => {
|
||||||
|
if (!checkDesktop() && platform === 'DESKTOP') {
|
||||||
|
void message.warning('此应用需要桌面端环境,请在桌面端打开')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (platform === 'ANDROID') {
|
||||||
|
void modal.confirm({
|
||||||
|
centered: true,
|
||||||
|
icon: <Icon style={{ color: COLOR_MAIN }} component={IconOxygenInfo} />,
|
||||||
|
title: 'Android 端',
|
||||||
|
content: (
|
||||||
|
<FlexBox className={'android-qrcode'}>
|
||||||
|
<AntdQRCode value={getAndroidUrl(author.username, toolId)} size={300} />
|
||||||
|
<AntdTag className={'tag'}>请使用手机端扫描上方二维码</AntdTag>
|
||||||
|
</FlexBox>
|
||||||
|
),
|
||||||
|
okText: '确定',
|
||||||
|
cancelText: '模拟器',
|
||||||
|
onCancel() {
|
||||||
|
navigateToView(navigate, author.username, toolId, platform, undefined, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
navigateToView(navigate, author.username, toolId, platform, undefined, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnClickAuthor = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
navigateToStore(navigate, author.username)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnAndroidBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
void modal.confirm({
|
||||||
|
centered: true,
|
||||||
|
icon: <Icon style={{ color: COLOR_MAIN }} component={IconOxygenInfo} />,
|
||||||
|
title: 'Android 端',
|
||||||
|
content: (
|
||||||
|
<FlexBox className={'android-qrcode'}>
|
||||||
|
<AntdQRCode value={getAndroidUrl(author.username, toolId)} size={300} />
|
||||||
|
<AntdTag className={'tag'}>请使用手机端扫描上方二维码</AntdTag>
|
||||||
|
</FlexBox>
|
||||||
|
),
|
||||||
|
okText: '确定',
|
||||||
|
cancelText: '模拟器',
|
||||||
|
onCancel() {
|
||||||
|
navigateToView(navigate, author.username, toolId, 'ANDROID', undefined, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnWebBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
navigateToView(navigate, author.username, toolId, 'WEB', undefined, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Draggable
|
||||||
|
id={`${author.username}:${toolId}:local:${platform}`}
|
||||||
|
data={{
|
||||||
|
icon,
|
||||||
|
toolName,
|
||||||
|
toolId,
|
||||||
|
authorUsername: author.username,
|
||||||
|
ver: 'local',
|
||||||
|
platform: platform
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
data-component={'component-local-card'}
|
||||||
|
style={{ overflow: 'visible', ...style }}
|
||||||
|
ref={cardRef}
|
||||||
|
{...props}
|
||||||
|
onClick={handleCardOnClick}
|
||||||
|
>
|
||||||
|
<FlexBox className={'local-card'}>
|
||||||
|
<div className={'header'}>
|
||||||
|
<div className={'version'}>
|
||||||
|
<AntdTag>
|
||||||
|
{platform.slice(0, 1)}-{ver}
|
||||||
|
</AntdTag>
|
||||||
|
</div>
|
||||||
|
<div className={'operation'}>
|
||||||
|
{platform !== 'ANDROID' && supportPlatform.includes('ANDROID') && (
|
||||||
|
<AntdTooltip title={'Android 端'}>
|
||||||
|
<Icon
|
||||||
|
component={IconOxygenMobile}
|
||||||
|
onClick={handleOnAndroidBtnClick}
|
||||||
|
/>
|
||||||
|
</AntdTooltip>
|
||||||
|
)}
|
||||||
|
{platform === 'DESKTOP' && supportPlatform.includes('WEB') && (
|
||||||
|
<AntdTooltip title={'Web 端'}>
|
||||||
|
<Icon
|
||||||
|
component={IconOxygenBrowser}
|
||||||
|
onClick={handleOnWebBtnClick}
|
||||||
|
/>
|
||||||
|
</AntdTooltip>
|
||||||
|
)}
|
||||||
|
<DragHandle />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={'icon'}>
|
||||||
|
<img src={`data:image/svg+xml;base64,${icon}`} alt={'Icon'} />
|
||||||
|
</div>
|
||||||
|
<div className={'info'}>
|
||||||
|
<div className={'tool-name'}>{toolName}</div>
|
||||||
|
<div className={'tool-id'}>{`ID: ${toolId}`}</div>
|
||||||
|
{toolDesc && (
|
||||||
|
<div
|
||||||
|
className={'tool-desc'}
|
||||||
|
title={toolDesc}
|
||||||
|
>{`简介:${omitText(toolDesc, 18)}`}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{showAuthor && (
|
||||||
|
<div className={'author'} onClick={handleOnClickAuthor}>
|
||||||
|
<div className={'avatar'}>
|
||||||
|
<AntdAvatar
|
||||||
|
src={
|
||||||
|
<AntdImage
|
||||||
|
preview={false}
|
||||||
|
src={`data:image/png;base64,${author.userInfo.avatar}`}
|
||||||
|
alt={'Avatar'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
style={{ background: COLOR_BACKGROUND }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={'author-name'}>{author.userInfo.nickname}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</FlexBox>
|
||||||
|
</Card>
|
||||||
|
</Draggable>
|
||||||
|
{contextHolder}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StoreCard
|
||||||
115
src/renderer/src/components/tools/RepositoryCard.tsx
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import { DetailedHTMLProps, HTMLAttributes } from 'react'
|
||||||
|
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||||
|
import '@/assets/css/components/tools/repository-card.scss'
|
||||||
|
import Card from '@/components/common/Card'
|
||||||
|
import FlexBox from '@/components/common/FlexBox'
|
||||||
|
import Draggable from '@/components/dnd/Draggable'
|
||||||
|
import DragHandle from '@/components/dnd/DragHandle'
|
||||||
|
|
||||||
|
interface RepositoryCardProps
|
||||||
|
extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||||
|
icon: string
|
||||||
|
toolName: string
|
||||||
|
toolId: string
|
||||||
|
ver: string
|
||||||
|
platform: Platform
|
||||||
|
options?: TiltOptions
|
||||||
|
onOpen?: () => void
|
||||||
|
onEdit?: () => void
|
||||||
|
onSource?: () => void
|
||||||
|
onPublish?: () => void
|
||||||
|
onCancelReview?: () => void
|
||||||
|
onDelete?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const RepositoryCard = ({
|
||||||
|
style,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
ref,
|
||||||
|
icon,
|
||||||
|
toolName,
|
||||||
|
toolId,
|
||||||
|
ver,
|
||||||
|
platform,
|
||||||
|
options = {
|
||||||
|
reverse: true,
|
||||||
|
max: 8,
|
||||||
|
glare: true,
|
||||||
|
['max-glare']: 0.3,
|
||||||
|
scale: 1.03
|
||||||
|
},
|
||||||
|
onOpen,
|
||||||
|
onEdit,
|
||||||
|
onSource,
|
||||||
|
onPublish,
|
||||||
|
onCancelReview,
|
||||||
|
onDelete,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: RepositoryCardProps) => {
|
||||||
|
const cardRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
cardRef.current && VanillaTilt.init(cardRef.current, options)
|
||||||
|
}, [options])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Draggable
|
||||||
|
id={`!:${toolId}:${ver}:${platform}`}
|
||||||
|
data={{ icon, toolName, toolId, authorUsername: '!', ver, platform }}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
data-component={'component-repository-card'}
|
||||||
|
style={{ overflow: 'visible', ...style }}
|
||||||
|
ref={cardRef}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<FlexBox className={'repository-card'}>
|
||||||
|
<div className={'header'}>
|
||||||
|
{children}
|
||||||
|
<DragHandle />
|
||||||
|
</div>
|
||||||
|
<div className={'icon'}>
|
||||||
|
<img src={`data:image/svg+xml;base64,${icon}`} alt={'Icon'} />
|
||||||
|
</div>
|
||||||
|
<div className={'info'}>
|
||||||
|
<div className={'tool-name'}>{toolName}</div>
|
||||||
|
<div className={'tool-id'}>{`ID: ${toolId}`}</div>
|
||||||
|
</div>
|
||||||
|
<div className={'operation'}>
|
||||||
|
{onOpen && (
|
||||||
|
<AntdButton onClick={onOpen} size={'small'} type={'primary'}>
|
||||||
|
打开
|
||||||
|
</AntdButton>
|
||||||
|
)}
|
||||||
|
{onEdit && onPublish && (
|
||||||
|
<div className={'edit'}>
|
||||||
|
<AntdButton.Group size={'small'}>
|
||||||
|
<AntdButton onClick={onEdit}>编辑</AntdButton>
|
||||||
|
<AntdButton onClick={onPublish}>发布</AntdButton>
|
||||||
|
</AntdButton.Group>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{onSource && (
|
||||||
|
<AntdButton size={'small'} onClick={onSource}>
|
||||||
|
源码
|
||||||
|
</AntdButton>
|
||||||
|
)}
|
||||||
|
{onCancelReview && (
|
||||||
|
<AntdButton size={'small'} onClick={onCancelReview}>
|
||||||
|
取消审核
|
||||||
|
</AntdButton>
|
||||||
|
)}
|
||||||
|
{onDelete && (
|
||||||
|
<AntdButton size={'small'} danger onClick={onDelete}>
|
||||||
|
删除
|
||||||
|
</AntdButton>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</FlexBox>
|
||||||
|
</Card>
|
||||||
|
</Draggable>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RepositoryCard
|
||||||
407
src/renderer/src/components/tools/StoreCard.tsx
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
import { DetailedHTMLProps, HTMLAttributes, MouseEvent } from 'react'
|
||||||
|
import VanillaTilt, { TiltOptions } from 'vanilla-tilt'
|
||||||
|
import protocolCheck from 'custom-protocol-check'
|
||||||
|
import Icon from '@ant-design/icons'
|
||||||
|
import '@/assets/css/components/tools/store-card.scss'
|
||||||
|
import {
|
||||||
|
COLOR_BACKGROUND,
|
||||||
|
COLOR_MAIN,
|
||||||
|
COLOR_PRODUCTION,
|
||||||
|
DATABASE_SELECT_SUCCESS
|
||||||
|
} from '@/constants/common.constants'
|
||||||
|
import { checkDesktop, omitText } from '@/util/common'
|
||||||
|
import { getLoginStatus, getUserId } from '@/util/auth'
|
||||||
|
import {
|
||||||
|
getAndroidUrl,
|
||||||
|
navigateToLogin,
|
||||||
|
navigateToSource,
|
||||||
|
navigateToStore,
|
||||||
|
navigateToView
|
||||||
|
} from '@/util/navigation'
|
||||||
|
import {
|
||||||
|
l_tool_get,
|
||||||
|
l_tool_install,
|
||||||
|
r_tool_add_favorite,
|
||||||
|
r_tool_detail,
|
||||||
|
r_tool_remove_favorite
|
||||||
|
} from '@/services/tool'
|
||||||
|
import Card from '@/components/common/Card'
|
||||||
|
import FlexBox from '@/components/common/FlexBox'
|
||||||
|
import DragHandle from '@/components/dnd/DragHandle'
|
||||||
|
import Draggable from '@/components/dnd/Draggable'
|
||||||
|
|
||||||
|
interface StoreCardProps extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
||||||
|
icon: string
|
||||||
|
toolName: string
|
||||||
|
toolId: string
|
||||||
|
toolDesc: string
|
||||||
|
options?: TiltOptions
|
||||||
|
author: UserWithInfoVo
|
||||||
|
showAuthor?: boolean
|
||||||
|
ver: string
|
||||||
|
platform: Platform
|
||||||
|
supportPlatform: Platform[]
|
||||||
|
vers: Record<Platform, string>
|
||||||
|
favorite: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const StoreCard = ({
|
||||||
|
style,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
ref,
|
||||||
|
icon,
|
||||||
|
toolName,
|
||||||
|
toolId,
|
||||||
|
toolDesc,
|
||||||
|
options = {
|
||||||
|
reverse: true,
|
||||||
|
max: 8,
|
||||||
|
glare: true,
|
||||||
|
['max-glare']: 0.3,
|
||||||
|
scale: 1.03
|
||||||
|
},
|
||||||
|
author,
|
||||||
|
showAuthor = true,
|
||||||
|
ver,
|
||||||
|
platform,
|
||||||
|
supportPlatform,
|
||||||
|
vers,
|
||||||
|
favorite,
|
||||||
|
...props
|
||||||
|
}: StoreCardProps) => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const [modal, contextHolder] = AntdModal.useModal()
|
||||||
|
const cardRef = useRef<HTMLDivElement>(null)
|
||||||
|
const [favorite_, setFavorite_] = useState<boolean>(favorite)
|
||||||
|
const [userId, setUserId] = useState('')
|
||||||
|
const [isInstalling, setIsInstalling] = useState(false)
|
||||||
|
const [isInstalled, setIsInstalled] = useState(true)
|
||||||
|
const [isAvailableUpdate, setIsAvailableUpdate] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
cardRef.current && VanillaTilt.init(cardRef.current, options)
|
||||||
|
if (getLoginStatus()) {
|
||||||
|
void getUserId().then((value) => setUserId(value))
|
||||||
|
}
|
||||||
|
}, [options])
|
||||||
|
|
||||||
|
const handleCardOnClick = () => {
|
||||||
|
if (!checkDesktop() && platform === 'DESKTOP') {
|
||||||
|
void message.warning('此应用需要桌面端环境,请在桌面端打开')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (platform === 'ANDROID') {
|
||||||
|
void modal.confirm({
|
||||||
|
centered: true,
|
||||||
|
icon: <Icon style={{ color: COLOR_MAIN }} component={IconOxygenInfo} />,
|
||||||
|
title: 'Android 端',
|
||||||
|
content: (
|
||||||
|
<FlexBox className={'android-qrcode'}>
|
||||||
|
<AntdQRCode value={getAndroidUrl(author.username, toolId)} size={300} />
|
||||||
|
<AntdTag className={'tag'}>请使用手机端扫描上方二维码</AntdTag>
|
||||||
|
</FlexBox>
|
||||||
|
),
|
||||||
|
okText: '确定',
|
||||||
|
cancelText: '模拟器',
|
||||||
|
onCancel() {
|
||||||
|
navigateToView(navigate, author.username, toolId, platform)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
navigateToView(navigate, author.username, toolId, platform)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnClickAuthor = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
navigateToStore(navigate, author.username)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnSourceBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
navigateToSource(navigate, author.username, toolId, platform)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnStarBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
if (!getLoginStatus()) {
|
||||||
|
navigateToLogin(navigate, undefined, `${location.pathname}${location.search}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (favorite_) {
|
||||||
|
void r_tool_remove_favorite({
|
||||||
|
authorId: author.id,
|
||||||
|
toolId: toolId,
|
||||||
|
platform: platform
|
||||||
|
}).then((res) => {
|
||||||
|
const response = res.data
|
||||||
|
if (response.success) {
|
||||||
|
setFavorite_(false)
|
||||||
|
} else {
|
||||||
|
void message.error('取消收藏失败,请稍后重试')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
void r_tool_add_favorite({
|
||||||
|
authorId: author.id,
|
||||||
|
toolId: toolId,
|
||||||
|
platform: platform
|
||||||
|
}).then((res) => {
|
||||||
|
const response = res.data
|
||||||
|
if (response.success) {
|
||||||
|
setFavorite_(true)
|
||||||
|
} else {
|
||||||
|
void message.error('收藏失败,请稍后重试')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnInstallBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
if (isInstalling) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setIsInstalling(true)
|
||||||
|
|
||||||
|
void message.loading({
|
||||||
|
content: isAvailableUpdate ? '更新中' : '安装中',
|
||||||
|
key: 'INSTALLING',
|
||||||
|
duration: 0
|
||||||
|
})
|
||||||
|
const newTools = {} as Record<Platform, ToolVo>
|
||||||
|
const flags: boolean[] = []
|
||||||
|
supportPlatform.forEach((platform) => {
|
||||||
|
void r_tool_detail(author.username, toolId, 'latest', platform)
|
||||||
|
.then((res) => {
|
||||||
|
const response = res.data
|
||||||
|
switch (response.code) {
|
||||||
|
case DATABASE_SELECT_SUCCESS:
|
||||||
|
newTools[platform] = response.data!
|
||||||
|
flags.push(true)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
flags.push(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
flags.push(false)
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
message.destroy('INSTALLING')
|
||||||
|
setIsInstalling(false)
|
||||||
|
if (flags.length !== supportPlatform.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (flags.every((item) => item)) {
|
||||||
|
void l_tool_install({ [`${author.username}:${toolId}`]: newTools }).then(
|
||||||
|
() => {
|
||||||
|
void message.success(isAvailableUpdate ? '更新成功' : '安装成功')
|
||||||
|
setIsInstalled(true)
|
||||||
|
setIsAvailableUpdate(false)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
void message.error(
|
||||||
|
isAvailableUpdate ? '更新失败,请稍后重试' : '安装失败,请稍后重试'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnAndroidBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
void modal.confirm({
|
||||||
|
centered: true,
|
||||||
|
icon: <Icon style={{ color: COLOR_MAIN }} component={IconOxygenInfo} />,
|
||||||
|
title: 'Android 端',
|
||||||
|
content: (
|
||||||
|
<FlexBox className={'android-qrcode'}>
|
||||||
|
<AntdQRCode value={getAndroidUrl(author.username, toolId)} size={300} />
|
||||||
|
<AntdTag className={'tag'}>请使用手机端扫描上方二维码</AntdTag>
|
||||||
|
</FlexBox>
|
||||||
|
),
|
||||||
|
okText: '确定',
|
||||||
|
cancelText: '模拟器',
|
||||||
|
onCancel() {
|
||||||
|
navigateToView(navigate, author.username, toolId, 'ANDROID')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnDesktopBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
if (!checkDesktop()) {
|
||||||
|
void message.loading({ content: '启动桌面端中……', key: 'LOADING', duration: 0 })
|
||||||
|
protocolCheck(
|
||||||
|
`${import.meta.env.VITE_DESKTOP_PROTOCOL}://openurl/view/${author.username}/${toolId}`,
|
||||||
|
() => {
|
||||||
|
void message.warning('打开失败,此应用需要桌面端环境,请安装桌面端后重试')
|
||||||
|
void message.destroy('LOADING')
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
void message.destroy('LOADING')
|
||||||
|
},
|
||||||
|
2000,
|
||||||
|
() => {
|
||||||
|
void message.warning('打开失败,此应用需要桌面端环境,请安装桌面端后重试')
|
||||||
|
void message.destroy('LOADING')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
navigateToView(navigate, author.username, toolId, 'DESKTOP')
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOnWebBtnClick = (e: MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
navigateToView(navigate, author.username, toolId, 'WEB')
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
void l_tool_get().then((value) => {
|
||||||
|
const tools = value[`${author.username}:${toolId}`]
|
||||||
|
if (!tools) {
|
||||||
|
setIsInstalled(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setIsInstalled(true)
|
||||||
|
|
||||||
|
if (
|
||||||
|
Object.keys(tools).length !== supportPlatform.length ||
|
||||||
|
!supportPlatform.every((platform) => Object.keys(tools).includes(platform))
|
||||||
|
) {
|
||||||
|
setIsAvailableUpdate(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supportPlatform.some((platform) => vers[platform] !== tools[platform].ver)) {
|
||||||
|
setIsAvailableUpdate(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Draggable
|
||||||
|
id={`${author.username}:${toolId}:${ver}:${platform}`}
|
||||||
|
data={{
|
||||||
|
icon,
|
||||||
|
toolName,
|
||||||
|
toolId,
|
||||||
|
authorUsername: author.username,
|
||||||
|
ver: '',
|
||||||
|
platform: platform
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
data-component={'component-store-card'}
|
||||||
|
style={{ overflow: 'visible', ...style }}
|
||||||
|
ref={cardRef}
|
||||||
|
{...props}
|
||||||
|
onClick={handleCardOnClick}
|
||||||
|
>
|
||||||
|
<FlexBox className={'store-card'}>
|
||||||
|
<div className={'header'}>
|
||||||
|
<div className={'version'}>
|
||||||
|
<AntdTag>
|
||||||
|
{platform.slice(0, 1)}-{ver}
|
||||||
|
</AntdTag>
|
||||||
|
</div>
|
||||||
|
<div className={'operation'}>
|
||||||
|
{(!isInstalled || isAvailableUpdate) && (
|
||||||
|
<AntdTooltip title={isAvailableUpdate ? '更新' : '安装'}>
|
||||||
|
<Icon
|
||||||
|
component={IconOxygenDownload}
|
||||||
|
onClick={handleOnInstallBtnClick}
|
||||||
|
disabled={isInstalling}
|
||||||
|
/>
|
||||||
|
</AntdTooltip>
|
||||||
|
)}
|
||||||
|
{platform !== 'ANDROID' && supportPlatform.includes('ANDROID') && (
|
||||||
|
<AntdTooltip title={'Android 端'}>
|
||||||
|
<Icon
|
||||||
|
component={IconOxygenMobile}
|
||||||
|
onClick={handleOnAndroidBtnClick}
|
||||||
|
/>
|
||||||
|
</AntdTooltip>
|
||||||
|
)}
|
||||||
|
{platform === 'DESKTOP' && supportPlatform.includes('WEB') && (
|
||||||
|
<AntdTooltip title={'Web 端'}>
|
||||||
|
<Icon
|
||||||
|
component={IconOxygenBrowser}
|
||||||
|
onClick={handleOnWebBtnClick}
|
||||||
|
/>
|
||||||
|
</AntdTooltip>
|
||||||
|
)}
|
||||||
|
{platform === 'WEB' && supportPlatform.includes('DESKTOP') && (
|
||||||
|
<AntdTooltip title={'桌面端'}>
|
||||||
|
<Icon
|
||||||
|
component={IconOxygenDesktop}
|
||||||
|
onClick={handleOnDesktopBtnClick}
|
||||||
|
/>
|
||||||
|
</AntdTooltip>
|
||||||
|
)}
|
||||||
|
<AntdTooltip title={'源码'}>
|
||||||
|
<Icon
|
||||||
|
component={IconOxygenCode}
|
||||||
|
onClick={handleOnSourceBtnClick}
|
||||||
|
/>
|
||||||
|
</AntdTooltip>
|
||||||
|
{author.id !== userId && (
|
||||||
|
<AntdTooltip title={favorite_ ? '取消收藏' : '收藏'}>
|
||||||
|
<Icon
|
||||||
|
component={
|
||||||
|
favorite_ ? IconOxygenStarFilled : IconOxygenStar
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
color: favorite_ ? COLOR_PRODUCTION : undefined
|
||||||
|
}}
|
||||||
|
onClick={handleOnStarBtnClick}
|
||||||
|
/>
|
||||||
|
</AntdTooltip>
|
||||||
|
)}
|
||||||
|
<DragHandle />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={'icon'}>
|
||||||
|
<img src={`data:image/svg+xml;base64,${icon}`} alt={'Icon'} />
|
||||||
|
</div>
|
||||||
|
<div className={'info'}>
|
||||||
|
<div className={'tool-name'}>{toolName}</div>
|
||||||
|
<div className={'tool-id'}>{`ID: ${toolId}`}</div>
|
||||||
|
{toolDesc && (
|
||||||
|
<div
|
||||||
|
className={'tool-desc'}
|
||||||
|
title={toolDesc}
|
||||||
|
>{`简介:${omitText(toolDesc, 18)}`}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{showAuthor && (
|
||||||
|
<div className={'author'} onClick={handleOnClickAuthor}>
|
||||||
|
<div className={'avatar'}>
|
||||||
|
<AntdAvatar
|
||||||
|
src={
|
||||||
|
<AntdImage
|
||||||
|
preview={false}
|
||||||
|
src={`data:image/png;base64,${author.userInfo.avatar}`}
|
||||||
|
alt={'Avatar'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
style={{ background: COLOR_BACKGROUND }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={'author-name'}>{author.userInfo.nickname}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</FlexBox>
|
||||||
|
</Card>
|
||||||
|
</Draggable>
|
||||||
|
{contextHolder}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StoreCard
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
export const PRODUCTION_NAME = 'Oxygen Toolbox'
|
export const PRODUCTION_NAME = 'Oxygen Toolbox'
|
||||||
export const STORAGE_TOKEN_KEY = 'JWT_TOKEN'
|
export const STORAGE_TOKEN_KEY = 'JWT_TOKEN'
|
||||||
export const STORAGE_USER_INFO_KEY = 'USER_INFO'
|
export const STORAGE_USER_INFO_KEY = 'USER_INFO'
|
||||||
|
export const STORAGE_TOOL_MENU_ITEM_KEY = 'TOOL_MENU_ITEM'
|
||||||
export const COLOR_ORIGIN = 'white'
|
export const COLOR_ORIGIN = 'white'
|
||||||
export const COLOR_PRODUCTION = '#4E47BB'
|
export const COLOR_PRODUCTION = '#4E47BB'
|
||||||
export const COLOR_MAIN = COLOR_PRODUCTION
|
export const COLOR_MAIN = COLOR_PRODUCTION
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export const URL_SYS_STATISTICS_ACTIVE = `${URL_SYS_STATISTICS}/active`
|
|||||||
export const URL_SYS_TOOL = '/system/tool'
|
export const URL_SYS_TOOL = '/system/tool'
|
||||||
export const URL_SYS_TOOL_CATEGORY = `${URL_SYS_TOOL}/category`
|
export const URL_SYS_TOOL_CATEGORY = `${URL_SYS_TOOL}/category`
|
||||||
export const URL_SYS_TOOL_BASE = `${URL_SYS_TOOL}/base`
|
export const URL_SYS_TOOL_BASE = `${URL_SYS_TOOL}/base`
|
||||||
|
export const URL_SYS_TOOL_BASE_LIST = `${URL_SYS_TOOL_BASE}/list`
|
||||||
export const URL_SYS_TOOL_TEMPLATE = `${URL_SYS_TOOL}/template`
|
export const URL_SYS_TOOL_TEMPLATE = `${URL_SYS_TOOL}/template`
|
||||||
|
|
||||||
export const URL_TOOL = '/tool'
|
export const URL_TOOL = '/tool'
|
||||||
@@ -37,6 +38,7 @@ export const URL_TOOL_STORE = `${URL_TOOL}/store`
|
|||||||
export const URL_TOOL_TEMPLATE = `${URL_TOOL}/template`
|
export const URL_TOOL_TEMPLATE = `${URL_TOOL}/template`
|
||||||
export const URL_TOOL_CATEGORY = `${URL_TOOL}/category`
|
export const URL_TOOL_CATEGORY = `${URL_TOOL}/category`
|
||||||
export const URL_TOOL_DETAIL = `${URL_TOOL}/detail`
|
export const URL_TOOL_DETAIL = `${URL_TOOL}/detail`
|
||||||
|
export const URL_TOOL_FAVORITE = `${URL_TOOL_STORE}/favorite`
|
||||||
|
|
||||||
export const URL_API_V1 = '/api/v1'
|
export const URL_API_V1 = '/api/v1'
|
||||||
export const URL_API_V1_AVATAR_RANDOM_BASE64 = `${URL_API_V1}/avatar/base64`
|
export const URL_API_V1_AVATAR_RANDOM_BASE64 = `${URL_API_V1}/avatar/base64`
|
||||||
|
|||||||
16
src/renderer/src/electron.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { ElectronAPI } from '@electron-toolkit/preload'
|
||||||
|
import { Notification } from 'electron'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
type _ElectronAPI = ElectronAPI
|
||||||
|
|
||||||
|
class _Notification extends Notification {}
|
||||||
|
|
||||||
|
interface API {
|
||||||
|
installTool: (
|
||||||
|
newTools: Record<string, Record<Platform, ToolVo>>
|
||||||
|
) => Promise<Record<string, Record<Platform, ToolVo>>>
|
||||||
|
|
||||||
|
getInstalledTool: () => Promise<Record<string, Record<Platform, ToolVo>>>
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/renderer/src/global.d.ts
vendored
@@ -1,7 +1,14 @@
|
|||||||
/// <reference types="vite/client" />
|
/// <reference types="vite/client" />
|
||||||
|
/// <reference types="./electron" />
|
||||||
/// <reference types="./ant-design" />
|
/// <reference types="./ant-design" />
|
||||||
|
|
||||||
|
type Platform = 'WEB' | 'DESKTOP' | 'ANDROID'
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
|
readonly VITE_PLATFORM: Platform
|
||||||
|
readonly VITE_DESKTOP_PROTOCOL: string
|
||||||
|
readonly VITE_APP_PROTOCOL: string
|
||||||
|
readonly VITE_UI_URL: string
|
||||||
readonly VITE_API_URL: string
|
readonly VITE_API_URL: string
|
||||||
readonly VITE_API_TOKEN_URL: string
|
readonly VITE_API_TOKEN_URL: string
|
||||||
readonly VITE_TURNSTILE_SITE_KEY: string
|
readonly VITE_TURNSTILE_SITE_KEY: string
|
||||||
@@ -11,6 +18,12 @@ interface ImportMeta {
|
|||||||
readonly env: ImportMetaEnv
|
readonly env: ImportMetaEnv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Window {
|
||||||
|
electronAPI: _ElectronAPI
|
||||||
|
Notification: typeof _Notification
|
||||||
|
api: API
|
||||||
|
}
|
||||||
|
|
||||||
interface RouteJsonObject {
|
interface RouteJsonObject {
|
||||||
path: string
|
path: string
|
||||||
absolutePath: string
|
absolutePath: string
|
||||||
@@ -545,6 +558,7 @@ interface ToolBaseVo {
|
|||||||
name: string
|
name: string
|
||||||
source: ToolDataVo
|
source: ToolDataVo
|
||||||
dist: ToolDataVo
|
dist: ToolDataVo
|
||||||
|
platform: Platform
|
||||||
compiled: boolean
|
compiled: boolean
|
||||||
createTime: string
|
createTime: string
|
||||||
updateTime: string
|
updateTime: string
|
||||||
@@ -555,6 +569,7 @@ interface ToolBaseAddEditParam {
|
|||||||
name?: string
|
name?: string
|
||||||
source?: string
|
source?: string
|
||||||
dist?: string
|
dist?: string
|
||||||
|
platform?: Platform
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ToolTemplateVo {
|
interface ToolTemplateVo {
|
||||||
@@ -562,6 +577,7 @@ interface ToolTemplateVo {
|
|||||||
name: string
|
name: string
|
||||||
baseId: string
|
baseId: string
|
||||||
source: ToolDataVo
|
source: ToolDataVo
|
||||||
|
platform: Platform
|
||||||
entryPoint: string
|
entryPoint: string
|
||||||
enable: boolean
|
enable: boolean
|
||||||
createTime: string
|
createTime: string
|
||||||
@@ -574,6 +590,7 @@ interface ToolTemplateAddEditParam {
|
|||||||
name?: string
|
name?: string
|
||||||
baseId?: string
|
baseId?: string
|
||||||
source?: string
|
source?: string
|
||||||
|
platform?: Platform
|
||||||
entryPoint?: string
|
entryPoint?: string
|
||||||
enable?: boolean
|
enable?: boolean
|
||||||
}
|
}
|
||||||
@@ -583,6 +600,7 @@ interface ToolVo {
|
|||||||
name: string
|
name: string
|
||||||
toolId: string
|
toolId: string
|
||||||
icon: string
|
icon: string
|
||||||
|
platform: Platform
|
||||||
description: string
|
description: string
|
||||||
base: ToolBaseVo
|
base: ToolBaseVo
|
||||||
author: UserWithInfoVo
|
author: UserWithInfoVo
|
||||||
@@ -596,12 +614,14 @@ interface ToolVo {
|
|||||||
review: 'NONE' | 'PROCESSING' | 'PASS' | 'REJECT'
|
review: 'NONE' | 'PROCESSING' | 'PASS' | 'REJECT'
|
||||||
createTime: string
|
createTime: string
|
||||||
updateTime: string
|
updateTime: string
|
||||||
|
favorite: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ToolCreateParam {
|
interface ToolCreateParam {
|
||||||
name: string
|
name: string
|
||||||
toolId: string
|
toolId: string
|
||||||
icon: string
|
icon: string
|
||||||
|
platform: Platform
|
||||||
description: string
|
description: string
|
||||||
ver: string
|
ver: string
|
||||||
templateId: string
|
templateId: string
|
||||||
@@ -612,6 +632,7 @@ interface ToolCreateParam {
|
|||||||
interface ToolUpgradeParam {
|
interface ToolUpgradeParam {
|
||||||
toolId: string
|
toolId: string
|
||||||
ver: string
|
ver: string
|
||||||
|
platform: Platform
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ToolUpdateParam {
|
interface ToolUpdateParam {
|
||||||
@@ -637,3 +658,18 @@ interface ToolManagementPassParam {
|
|||||||
interface ToolStoreGetParam extends PageParam {
|
interface ToolStoreGetParam extends PageParam {
|
||||||
searchValue?: string
|
searchValue?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ToolFavoriteAddRemoveParam {
|
||||||
|
authorId: string
|
||||||
|
toolId: string
|
||||||
|
platform: Platform
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToolMenuItem {
|
||||||
|
icon: string
|
||||||
|
toolName: string
|
||||||
|
toolId: string
|
||||||
|
authorUsername: string
|
||||||
|
ver: string
|
||||||
|
platform: Platform
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ createRoot(document.getElementById('root')!).render(
|
|||||||
<StrictMode>
|
<StrictMode>
|
||||||
<AntdConfigProvider
|
<AntdConfigProvider
|
||||||
theme={{
|
theme={{
|
||||||
token: { colorPrimary: COLOR_MAIN, colorLinkHover: COLOR_MAIN },
|
token: {
|
||||||
|
colorPrimary: COLOR_MAIN,
|
||||||
|
colorLinkHover: COLOR_MAIN
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
Tree: {
|
Tree: {
|
||||||
colorBgContainer: 'transparent'
|
colorBgContainer: 'transparent'
|
||||||
|
|||||||