commit
2555b99a5d
81 changed files with 13851 additions and 0 deletions
@ -0,0 +1,14 @@ |
|||||
|
{ |
||||
|
"comments": false, |
||||
|
"env": { |
||||
|
"main": { |
||||
|
"presets": [ |
||||
|
["@babel/preset-env", { |
||||
|
"targets": { "node": 7 } |
||||
|
}], |
||||
|
"@babel/preset-typescript" |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
"plugins": ["@babel/plugin-transform-runtime"] |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
# http://editorconfig.org |
||||
|
root = true # 应在文件顶部指定的特殊属性,true表示停止搜索当前文件 |
||||
|
|
||||
|
[*] |
||||
|
charset = utf-8 # 统一字符格式 |
||||
|
indent_style = space # 缩进方式为空格 |
||||
|
indent_size = 2 # 缩进为两个空格 |
||||
|
end_of_line = lf # 换行符统一用lf |
||||
|
insert_final_newline = true # 保证文件结尾留一个空行 |
||||
|
trim_trailing_whitespace = true # 移除新一行前的空白字符 |
||||
|
|
||||
|
[*.md] |
||||
|
insert_final_newline = false |
||||
|
trim_trailing_whitespace = false |
@ -0,0 +1,2 @@ |
|||||
|
# vite 服务器端口 |
||||
|
PORT=3344 |
@ -0,0 +1,5 @@ |
|||||
|
node_modules |
||||
|
out |
||||
|
.idea |
||||
|
.vscode |
||||
|
dist/electron |
@ -0,0 +1,11 @@ |
|||||
|
{ |
||||
|
"tabWidth": 2, |
||||
|
"useTabs": false, |
||||
|
"semi": false, |
||||
|
"singleQuote": false, |
||||
|
"TrailingCooma": "all", |
||||
|
"bracketSpacing": true, |
||||
|
"jsxBracketSameLine": false, |
||||
|
"arrowParens": "avoid", |
||||
|
"printWidth": 140 |
||||
|
} |
@ -0,0 +1,15 @@ |
|||||
|
https://www.jianshu.com/p/4699b825d285 |
||||
|
|
||||
|
|
||||
|
// // 指定了就出不来了,官网的解释是:No need to specify which files to include in the app |
||||
|
// "files": [ |
||||
|
// "dist/electron/**/*" |
||||
|
// ], |
||||
|
|
||||
|
|
||||
|
https://www.wyr.me/post/680 |
||||
|
|
||||
|
<!-- 切换 --> |
||||
|
https://github.com/webpack/webpack/issues/11767 |
||||
|
|
||||
|
https://vite-rollup-plugins.patak.dev/ |
@ -0,0 +1,5 @@ |
|||||
|
{ |
||||
|
"name": "my-electron-app", |
||||
|
"version": "1.0.0", |
||||
|
"lockfileVersion": 1 |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
{ |
||||
|
"name": "my-electron-app", |
||||
|
"version": "1.0.0", |
||||
|
"description": "description", |
||||
|
"main": "./electron/entry.js", |
||||
|
"scripts": {}, |
||||
|
"keywords": [], |
||||
|
"author": "TopOne", |
||||
|
"license": "ISC", |
||||
|
"devDependencies": {}, |
||||
|
"dependencies": {} |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
const { app, BrowserWindow } = require('electron') |
||||
|
const path = require('path') |
||||
|
|
||||
|
function createWindow () { |
||||
|
const win = new BrowserWindow({ |
||||
|
width: 800, |
||||
|
height: 600, |
||||
|
webPreferences: { |
||||
|
preload: path.join(__dirname, 'preload.js') |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
win.loadFile('index.html') |
||||
|
} |
||||
|
|
||||
|
app.whenReady().then(() => { |
||||
|
createWindow() |
||||
|
app.on('activate', function () { |
||||
|
if (BrowserWindow.getAllWindows().length === 0) createWindow() |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
app.on('window-all-closed', function () { |
||||
|
if (process.platform !== 'darwin') app.quit() |
||||
|
}) |
||||
|
|
File diff suppressed because it is too large
@ -0,0 +1,140 @@ |
|||||
|
{ |
||||
|
"name": "my-electron-app", |
||||
|
"version": "1.0.0", |
||||
|
"description": "description", |
||||
|
"main": "dist/electron/entry.js", |
||||
|
"scripts": { |
||||
|
"dev": "node script/run.js", |
||||
|
"build": "node script/run.js --build", |
||||
|
"build:in": "npm run build:vite && node script/build.js && npm run package", |
||||
|
"buildaa": "npm run build:vite && ts-node -r tsconfig-paths/register script/build --env=production && npm run package", |
||||
|
"webpack": "node script/webpack/runMain.js", |
||||
|
"deva": "ts-node -r tsconfig-paths/register script/dev-runner --env=development --watch", |
||||
|
"devaa": "npm run dev:all", |
||||
|
"dev:all": "concurrently -n=vue,ele -c=green,blue \"npm run dev:vue\" \"npm run dev:ele\"", |
||||
|
"dev:vue": "node script/before", |
||||
|
"dev:ele": "node -r ts-node/register script/build-main --env=development --watch", |
||||
|
"dev:test": "electron-forge start --inspect-electron", |
||||
|
"package": "electron-builder build --x64 --win", |
||||
|
"packageaaa": "electron-forge package", |
||||
|
"make": "electron-forge make", |
||||
|
"build:vite": "vite build", |
||||
|
"serve": "vite preview" |
||||
|
}, |
||||
|
"keywords": [], |
||||
|
"author": "TopOne", |
||||
|
"license": "ISC", |
||||
|
"devDependencies": { |
||||
|
"@babel/core": "^7.14.8", |
||||
|
"@babel/plugin-transform-runtime": "^7.14.5", |
||||
|
"@babel/preset-env": "^7.14.8", |
||||
|
"@babel/preset-stage-0": "^7.8.3", |
||||
|
"@babel/preset-typescript": "^7.14.5", |
||||
|
"@rollup/plugin-alias": "^3.1.4", |
||||
|
"@rollup/plugin-commonjs": "^19.0.1", |
||||
|
"@rollup/plugin-json": "^4.1.0", |
||||
|
"@rollup/plugin-node-resolve": "^13.0.4", |
||||
|
"@rollup/plugin-replace": "^3.0.0", |
||||
|
"@rollup/plugin-typescript": "^8.2.3", |
||||
|
"@types/minimist": "^1.2.2", |
||||
|
"@types/node": "^15.14.3", |
||||
|
"@types/react": "^17.0.0", |
||||
|
"@types/react-dom": "^17.0.0", |
||||
|
"@types/react-router-dom": "^5.1.7", |
||||
|
"@vitejs/plugin-react-refresh": "^1.3.1", |
||||
|
"axios": "^0.21.1", |
||||
|
"babel-loader": "^8.2.2", |
||||
|
"babel-minify-webpack-plugin": "^0.3.1", |
||||
|
"cfonts": "^2.9.3", |
||||
|
"chalk": "^4.1.1", |
||||
|
"classnames": "^2.3.1", |
||||
|
"concurrently": "^6.2.0", |
||||
|
"cross-env": "^7.0.3", |
||||
|
"dotenv": "^10.0.0", |
||||
|
"electron": "^13.1.7", |
||||
|
"electron-builder": "^22.11.7", |
||||
|
"electron-debug": "^3.2.0", |
||||
|
"electron-devtools-installer": "^3.2.0", |
||||
|
"electron-squirrel-startup": "^1.0.0", |
||||
|
"execa": "^5.1.1", |
||||
|
"i18next": "^20.3.4", |
||||
|
"i18next-browser-languagedetector": "^6.1.2", |
||||
|
"node-loader": "^2.0.0", |
||||
|
"react": "^17.0.0", |
||||
|
"react-dom": "^17.0.0", |
||||
|
"react-i18next": "^11.11.3", |
||||
|
"react-redux": "^7.2.4", |
||||
|
"react-router-dom": "^5.2.0", |
||||
|
"redux": "^4.1.0", |
||||
|
"redux-devtools": "^3.7.0", |
||||
|
"redux-saga": "^1.1.3", |
||||
|
"sass": "^1.35.1", |
||||
|
"styled-jsx": "^3.4.4", |
||||
|
"ts-loader": "^9.2.4", |
||||
|
"ts-node": "^10.1.0", |
||||
|
"tsconfig-paths": "^3.10.1", |
||||
|
"typescript": "^4.3.2", |
||||
|
"vite": "^2.4.3", |
||||
|
"vite-plugin-html": "^2.0.7", |
||||
|
"vite-plugin-windicss": "^1.2.0", |
||||
|
"vitejs-plugin-electron": "^0.1.3", |
||||
|
"webpack": "^5.47.0", |
||||
|
"webpack-cli": "^4.7.2", |
||||
|
"webpack-dev-server": "^3.11.2", |
||||
|
"windicss": "^3.1.3" |
||||
|
}, |
||||
|
"dependencies": {}, |
||||
|
"build": { |
||||
|
"productName": "my-project", |
||||
|
"appId": "com.example.yourapp", |
||||
|
"copyright": "xxxx", |
||||
|
"directories": { |
||||
|
"output": "out", |
||||
|
"app": "./dist" |
||||
|
}, |
||||
|
"nsis": { |
||||
|
"oneClick": false, |
||||
|
"allowElevation": true, |
||||
|
"createDesktopShortcut": true, |
||||
|
"createStartMenuShortcut": true, |
||||
|
"allowToChangeInstallationDirectory": true, |
||||
|
"perMachine": true |
||||
|
}, |
||||
|
"dmg": { |
||||
|
"contents": [ |
||||
|
{ |
||||
|
"x": 410, |
||||
|
"y": 150, |
||||
|
"type": "link", |
||||
|
"path": "/Applications" |
||||
|
}, |
||||
|
{ |
||||
|
"x": 130, |
||||
|
"y": 150, |
||||
|
"type": "file" |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
"extraResources": { |
||||
|
"from": "src/preload", |
||||
|
"to": "src/preload" |
||||
|
}, |
||||
|
"mac": { |
||||
|
"icon": "resource/electron/static/icon.png" |
||||
|
}, |
||||
|
"win": { |
||||
|
"icon": "resource/electron/static/icon.png", |
||||
|
"target": [ |
||||
|
{ |
||||
|
"target": "nsis", |
||||
|
"arch": [ |
||||
|
"ia32" |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
"linux": { |
||||
|
"icon": "resource/electron/static/icon.png" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Document</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
aaaaaaaaaaaaaaaa |
||||
|
</body> |
||||
|
</html> |
After Width: | Height: | Size: 18 KiB |
@ -0,0 +1,5 @@ |
|||||
|
// @ts-nocheck
|
||||
|
|
||||
|
const {buildMain} = require("./webpack/runMain") |
||||
|
|
||||
|
buildMain() |
@ -0,0 +1,36 @@ |
|||||
|
// @ts-nocheck
|
||||
|
import { spawn } from "child_process" |
||||
|
import { join } from "path" |
||||
|
import { rollup, OutputOptions } from "rollup" |
||||
|
import * as chalk from "chalk" |
||||
|
import options from "../rollup.config" |
||||
|
|
||||
|
export function tscCheck() { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
let tscProcess = spawn("node", [join(__dirname, "../../node_modules/typescript/bin/tsc")], { |
||||
|
stdio: "pipe", |
||||
|
// env: Object.assign(process.env, { NODE_ENV: argv.env }),
|
||||
|
}) |
||||
|
if (tscProcess) { |
||||
|
tscProcess.stdout.on("end", data => { |
||||
|
console.log(`[tsc check end]`) |
||||
|
resolve(tscProcess) |
||||
|
}) |
||||
|
tscProcess.stderr.on("data", data => { |
||||
|
console.error(`[tsc check err]: ${data}`) |
||||
|
reject() |
||||
|
}) |
||||
|
} else { |
||||
|
reject() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
export async function buildMain() { |
||||
|
try { |
||||
|
const opts = options(process.env.NODE_ENV) |
||||
|
const build = await rollup(opts) |
||||
|
build.write(opts.output as OutputOptions) |
||||
|
} catch (error) { |
||||
|
console.log(`\n${"[build-main.ts]"} ${chalk.red("构建报错")}\n`, error, "\n") |
||||
|
} |
||||
|
} |
@ -0,0 +1,47 @@ |
|||||
|
// @ts-nocheck
|
||||
|
import { exec, spawn, ChildProcess } from "child_process" |
||||
|
import { join } from "path" |
||||
|
import * as electron from "electron" |
||||
|
import { main } from "../../package.json" |
||||
|
|
||||
|
export function devElectron() { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
let electronProcess = spawn(electron as unknown as string, ['--inspect=5858',join(__dirname, `../../${main}`)], { |
||||
|
stdio: "pipe", |
||||
|
// env: Object.assign(process.env, { NODE_ENV: argv.env }),
|
||||
|
}) |
||||
|
if (electronProcess) { |
||||
|
electronProcess.stdout.on("data", data => { |
||||
|
console.log(`${data}`) |
||||
|
}) |
||||
|
resolve(electronProcess) |
||||
|
} else { |
||||
|
reject() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
export function buildElectron() { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
let tscProcess = spawn("node", [join(__dirname, "../../node_modules/@electron-forge/cli/dist/electron-forge.js"), "package"], { |
||||
|
stdio: "pipe", |
||||
|
// env: Object.assign(process.env, { NODE_ENV: argv.env }),
|
||||
|
}) |
||||
|
if (tscProcess) { |
||||
|
tscProcess.stdout.on("data", data => { |
||||
|
console.log(`[electron build]: ${data}`) |
||||
|
}) |
||||
|
tscProcess.stdout.on("end", data => { |
||||
|
console.log(`[electron build end]`) |
||||
|
resolve(tscProcess) |
||||
|
}) |
||||
|
tscProcess.stderr.on("data", data => { |
||||
|
console.error(`[electron build err]: ${data}`) |
||||
|
reject() |
||||
|
}) |
||||
|
} else { |
||||
|
reject() |
||||
|
} |
||||
|
}) |
||||
|
} |
@ -0,0 +1,54 @@ |
|||||
|
// @ts-nocheck
|
||||
|
import { exec, spawn, ChildProcess } from "child_process" |
||||
|
import { join } from "path" |
||||
|
|
||||
|
export function devVite() { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
let viteProcess = spawn("node", [join(__dirname, "../../node_modules/vite/bin/vite.js")], { |
||||
|
stdio: "pipe", |
||||
|
// env: Object.assign(process.env, { NODE_ENV: argv.env }),
|
||||
|
}) |
||||
|
if (viteProcess) { |
||||
|
let isReady = false |
||||
|
viteProcess.stdout.on("data", data => { |
||||
|
console.log(`${data}`) |
||||
|
if (!isReady && data.indexOf("ready") != -1) { |
||||
|
resolve(viteProcess) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
viteProcess.stderr.on("data", data => { |
||||
|
console.error(`[vite err]: ${data}`) |
||||
|
reject() |
||||
|
}) |
||||
|
|
||||
|
viteProcess.on("close", code => { |
||||
|
console.log(`[vite close]: exited with code ${code}`) |
||||
|
reject() |
||||
|
}) |
||||
|
} else { |
||||
|
reject() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
export function buildVite() { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
let viteProcess = spawn("node", [join(__dirname, "../../node_modules/vite/bin/vite.js"), "build"], { |
||||
|
stdio: "pipe", |
||||
|
// env: Object.assign(process.env, { NODE_ENV: argv.env }),
|
||||
|
}) |
||||
|
if (viteProcess) { |
||||
|
viteProcess.stdout.on("end", data => { |
||||
|
console.log(`\n[vite build end]`) |
||||
|
resolve(viteProcess) |
||||
|
}) |
||||
|
viteProcess.stderr.on("data", data => { |
||||
|
console.error(`\n[vite build err]: ${data}`) |
||||
|
reject() |
||||
|
}) |
||||
|
} else { |
||||
|
reject() |
||||
|
} |
||||
|
}) |
||||
|
} |
@ -0,0 +1,82 @@ |
|||||
|
// @ts-nocheck
|
||||
|
import * as chalk from "chalk" |
||||
|
import { devVite } from "./code/runVite" |
||||
|
import { devElectron } from "./code/runElectron" |
||||
|
|
||||
|
import { ChildProcess } from "child_process" |
||||
|
import { watch } from "rollup" |
||||
|
import options from "./rollup.config" |
||||
|
const { startMain } = require("./webpack/runMain") |
||||
|
|
||||
|
;(async () => { |
||||
|
let vitePorcess = await devVite() |
||||
|
console.log("[vite]", chalk.green(`vite ready.`)) |
||||
|
let child: ChildProcess | null |
||||
|
let manualRestart = false |
||||
|
|
||||
|
startMain(async () => { |
||||
|
if (child && child.kill) { |
||||
|
manualRestart = true |
||||
|
child.kill() |
||||
|
child = null |
||||
|
setTimeout(() => { |
||||
|
manualRestart = false |
||||
|
}, 5000) |
||||
|
} |
||||
|
try { |
||||
|
child = await devElectron() |
||||
|
console.log("[electron]", chalk.green(`electron ready.`)) |
||||
|
child.on("close", () => { |
||||
|
if (!manualRestart) { |
||||
|
// https://juejin.cn/post/6844904071682326535
|
||||
|
vitePorcess.kill() |
||||
|
child.kill() |
||||
|
process.exit() |
||||
|
} |
||||
|
}) |
||||
|
} catch (ev) { |
||||
|
console.log(ev.error) |
||||
|
vitePorcess.kill() |
||||
|
child.kill() |
||||
|
process.exit() |
||||
|
} |
||||
|
}) |
||||
|
})() |
||||
|
|
||||
|
// const watcher = watch(opts)
|
||||
|
// let child: ChildProcess | null
|
||||
|
// let manualRestart = false
|
||||
|
// watcher.on("change", filename => {
|
||||
|
// const log = chalk.green(`change -- ${filename}`)
|
||||
|
// console.log(TAG, log)
|
||||
|
// })
|
||||
|
// watcher.on("event",async ev => {
|
||||
|
// if (ev.code === "END") {
|
||||
|
// if (child && child.kill) {
|
||||
|
// manualRestart = true
|
||||
|
// child.kill()
|
||||
|
// child = null
|
||||
|
// setTimeout(() => {
|
||||
|
// manualRestart = false
|
||||
|
// }, 5000)
|
||||
|
// }
|
||||
|
// try {
|
||||
|
// child = await runElectron()
|
||||
|
// console.log('[electron]', chalk.green(`electron ready.`))
|
||||
|
// child.on("close", () => {
|
||||
|
// if (!manualRestart) {
|
||||
|
// vitePorcess.kill()
|
||||
|
// child.kill()
|
||||
|
// process.exit()
|
||||
|
// }
|
||||
|
// })
|
||||
|
// } catch (ev) {
|
||||
|
// console.log(ev.error)
|
||||
|
// vitePorcess.kill()
|
||||
|
// child.kill()
|
||||
|
// process.exit()
|
||||
|
// }
|
||||
|
// } else if (ev.code === "ERROR") {
|
||||
|
// console.log(ev.error)
|
||||
|
// }
|
||||
|
// })
|
@ -0,0 +1,58 @@ |
|||||
|
const { say } = require("cfonts") |
||||
|
const chalk = require("chalk") |
||||
|
|
||||
|
export function logStats(proc, data) { |
||||
|
let log = "" |
||||
|
|
||||
|
log += chalk.yellow.bold(`┏ ${proc} Process ${new Array(19 - proc.length + 1).join("-")}`) |
||||
|
log += "\n\n" |
||||
|
|
||||
|
if (typeof data === "object") { |
||||
|
data |
||||
|
.toString({ |
||||
|
colors: true, |
||||
|
chunks: false, |
||||
|
}) |
||||
|
.split(/\r?\n/) |
||||
|
.forEach(line => { |
||||
|
log += " " + line + "\n" |
||||
|
}) |
||||
|
} else { |
||||
|
log += ` ${data}\n` |
||||
|
} |
||||
|
|
||||
|
log += "\n" + chalk.yellow.bold(`┗ ${new Array(28 + 1).join("-")}`) + "\n" |
||||
|
|
||||
|
console.log(log) |
||||
|
} |
||||
|
|
||||
|
export function electronLog(data, color) { |
||||
|
let log = "" |
||||
|
data = data.toString().split(/\r?\n/) |
||||
|
data.forEach(line => { |
||||
|
log += ` ${line}\n` |
||||
|
}) |
||||
|
if (/[0-9A-z]+/.test(log)) { |
||||
|
console.log( |
||||
|
chalk[color].bold("┏ Electron -------------------") + "\n\n" + log + chalk[color].bold("┗ ----------------------------") + "\n" |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export function greeting() { |
||||
|
const cols = process.stdout.columns |
||||
|
let text |
||||
|
|
||||
|
if (cols > 104) text = "electron-vue" |
||||
|
else if (cols > 76) text = "electron-|vue" |
||||
|
else text = false |
||||
|
|
||||
|
if (text) { |
||||
|
say(text, { |
||||
|
colors: ["yellow"], |
||||
|
font: "simple3d", |
||||
|
space: false, |
||||
|
}) |
||||
|
} else console.log(chalk.yellow.bold("\n electron-vue")) |
||||
|
console.log(chalk.blue(" getting ready...") + "\n") |
||||
|
} |
@ -0,0 +1,41 @@ |
|||||
|
import { join } from "path" |
||||
|
import { RollupOptions } from "rollup" |
||||
|
import { builtins } from "./utils" |
||||
|
|
||||
|
const commonjs = require("@rollup/plugin-commonjs") |
||||
|
const json = require("@rollup/plugin-json") |
||||
|
const typescript = require("@rollup/plugin-typescript") |
||||
|
const alias = require("@rollup/plugin-alias") |
||||
|
const {nodeResolve} = require("@rollup/plugin-node-resolve") |
||||
|
|
||||
|
export default (env = "production") => { |
||||
|
console.log(env); |
||||
|
|
||||
|
const options: RollupOptions = { |
||||
|
input: join(__dirname, "../src/main/index.ts"), |
||||
|
output: { |
||||
|
file: join(__dirname, "../dist/electron/entry.js"), |
||||
|
format: "cjs", |
||||
|
name: "ElectronMainBundle", |
||||
|
sourcemap: true, |
||||
|
}, |
||||
|
plugins: [ |
||||
|
nodeResolve(), |
||||
|
commonjs(), |
||||
|
json(), |
||||
|
typescript(), |
||||
|
alias({ |
||||
|
entries: [ |
||||
|
{ find: "@", replacement: join(__dirname, "../src/render") }, |
||||
|
{ find: "@render", replacement: join(__dirname, "../src/render") }, |
||||
|
{ find: "@main", replacement: join(__dirname, "../src/main") }, |
||||
|
{ find: "@src", replacement: join(__dirname, "../src") }, |
||||
|
{ find: "@root", replacement: join(__dirname, "..") }, |
||||
|
], |
||||
|
}), |
||||
|
], |
||||
|
external: [...builtins(), "electron"], |
||||
|
} |
||||
|
|
||||
|
return options |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
const crossEnv = require("cross-env") |
||||
|
const minimist = require("minimist") |
||||
|
const {join} = require("path") |
||||
|
|
||||
|
const argv = minimist(process.argv.slice(2)) |
||||
|
|
||||
|
if (argv.build) { |
||||
|
crossEnv([ |
||||
|
"NODE_ENV=production", |
||||
|
"npm run build:in" |
||||
|
]) |
||||
|
} else { |
||||
|
const process = crossEnv([ |
||||
|
`STATIC=${join(__dirname, '../resource/electron/static')}`, |
||||
|
"PORT=3344", |
||||
|
"NODE_ENV=development", |
||||
|
"ts-node -r tsconfig-paths/register script/dev-runner --watch" |
||||
|
]) |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
import { builtinModules } from "module" |
||||
|
import { get } from "http" |
||||
|
import { green } from "chalk" |
||||
|
import { exec, spawn, ChildProcess } from "child_process" |
||||
|
|
||||
|
/** 轮询监听 vite 启动 */ |
||||
|
export function waitOn(arg0: { port: string | number; interval?: number }) { |
||||
|
return new Promise(resolve => { |
||||
|
const { port, interval = 149 } = arg0 |
||||
|
const url = `http://localhost:${port}` |
||||
|
let counter = 0 |
||||
|
const timer: NodeJS.Timer = setInterval(() => { |
||||
|
get(url, res => { |
||||
|
clearInterval(timer) |
||||
|
console.log("[waitOn]", green(`"${url}" are already responsive.`), `(${res.statusCode}: ${res.statusMessage})`) |
||||
|
resolve(res.statusCode) |
||||
|
}).on("error", err => { |
||||
|
console.log("[waitOn]", `counter: ${counter++}`) |
||||
|
}) |
||||
|
}, interval) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
/** node.js builtins module */ |
||||
|
export const builtins = () => builtinModules.filter(x => !/^_|^(internal|v8|node-inspect)\/|\//.test(x)) |
@ -0,0 +1,146 @@ |
|||||
|
const chalk = require('chalk') |
||||
|
const electron = require('electron') |
||||
|
const path = require('path') |
||||
|
const { say } = require('cfonts') |
||||
|
const { spawn } = require('child_process') |
||||
|
const webpack = require('webpack') |
||||
|
// const WebpackDevServer = require('webpack-dev-server')
|
||||
|
// const webpackHotMiddleware = require('webpack-hot-middleware')
|
||||
|
|
||||
|
const mainConfig = require('./webpack.main.config') |
||||
|
// const rendererConfig = require('./webpack.renderer.config')
|
||||
|
|
||||
|
let electronProcess = null |
||||
|
let manualRestart = false |
||||
|
// let hotMiddleware
|
||||
|
|
||||
|
function logStats (proc, data) { |
||||
|
let log = '' |
||||
|
|
||||
|
log += chalk.yellow.bold(`┏ ${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`) |
||||
|
log += '\n\n' |
||||
|
|
||||
|
if (typeof data === 'object') { |
||||
|
data.toString({ |
||||
|
colors: true, |
||||
|
chunks: false |
||||
|
}).split(/\r?\n/).forEach(line => { |
||||
|
log += ' ' + line + '\n' |
||||
|
}) |
||||
|
} else { |
||||
|
log += ` ${data}\n` |
||||
|
} |
||||
|
|
||||
|
log += '\n' + chalk.yellow.bold(`┗ ${new Array(28 + 1).join('-')}`) + '\n' |
||||
|
|
||||
|
console.log(log) |
||||
|
} |
||||
|
|
||||
|
function buildMain () { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
mainConfig.mode = 'production' |
||||
|
webpack(mainConfig, (err, stats) => { |
||||
|
if (err) reject(err.stack || err) |
||||
|
else if (stats.hasErrors()) { |
||||
|
let err = '' |
||||
|
stats.toString({ |
||||
|
chunks: false, |
||||
|
colors: true |
||||
|
}) |
||||
|
.split(/\r?\n/) |
||||
|
.forEach(line => { |
||||
|
err += ` ${line}\n` |
||||
|
}) |
||||
|
|
||||
|
reject(err) |
||||
|
} |
||||
|
else { |
||||
|
resolve(stats.toString({ |
||||
|
chunks: false, |
||||
|
colors: true |
||||
|
})) |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
function startMain (callback) { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
mainConfig.entry.main = [path.join(__dirname, '../../src/main/index.dev.ts')]//.concat(mainConfig.entry.main)
|
||||
|
mainConfig.mode = 'development' |
||||
|
const compiler = webpack(mainConfig) |
||||
|
|
||||
|
compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => { |
||||
|
logStats('Main', chalk.white.bold('compiling...')) |
||||
|
// hotMiddleware.publish({ action: 'compiling' })
|
||||
|
done() |
||||
|
}) |
||||
|
|
||||
|
compiler.watch({}, (err, stats) => { |
||||
|
if (err) { |
||||
|
console.log(err) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
logStats('Main', stats) |
||||
|
|
||||
|
callback && callback() |
||||
|
|
||||
|
resolve() |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
module.exports = { |
||||
|
startMain, |
||||
|
buildMain, |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function electronLog (data, color) { |
||||
|
let log = '' |
||||
|
data = data.toString().split(/\r?\n/) |
||||
|
data.forEach(line => { |
||||
|
log += ` ${line}\n` |
||||
|
}) |
||||
|
if (/[0-9A-z]+/.test(log)) { |
||||
|
console.log( |
||||
|
chalk[color].bold('┏ Electron -------------------') + |
||||
|
'\n\n' + |
||||
|
log + |
||||
|
chalk[color].bold('┗ ----------------------------') + |
||||
|
'\n' |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function greeting () { |
||||
|
const cols = process.stdout.columns |
||||
|
let text = '' |
||||
|
|
||||
|
if (cols > 104) text = 'electron-vue' |
||||
|
else if (cols > 76) text = 'electron-|vue' |
||||
|
else text = false |
||||
|
|
||||
|
if (text) { |
||||
|
say(text, { |
||||
|
colors: ['yellow'], |
||||
|
font: 'simple3d', |
||||
|
space: false |
||||
|
}) |
||||
|
} else console.log(chalk.yellow.bold('\n electron-vue')) |
||||
|
console.log(chalk.blue(' getting ready...') + '\n') |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// function init () {
|
||||
|
// greeting()
|
||||
|
// startMain()
|
||||
|
// Promise.all([startRenderer(), startMain()])
|
||||
|
// .then(() => {
|
||||
|
// startElectron()
|
||||
|
// })
|
||||
|
// .catch(err => {
|
||||
|
// console.error(err)
|
||||
|
// })
|
||||
|
// }
|
||||
|
|
||||
|
// init()
|
@ -0,0 +1,65 @@ |
|||||
|
'use strict' |
||||
|
|
||||
|
process.env.BABEL_ENV = 'main' |
||||
|
|
||||
|
const path = require('path') |
||||
|
const { dependencies } = require('../../package.json') |
||||
|
const webpack = require('webpack') |
||||
|
|
||||
|
const MinifyPlugin = require("babel-minify-webpack-plugin") |
||||
|
|
||||
|
let mainConfig = { |
||||
|
entry: { |
||||
|
main: path.join(__dirname, '../../src/main/index.ts') |
||||
|
}, |
||||
|
externals: [ |
||||
|
...Object.keys(dependencies || {}) |
||||
|
], |
||||
|
module: { |
||||
|
rules: [ |
||||
|
{ |
||||
|
test: /\.(js|ts)$/, |
||||
|
use: ['babel-loader','ts-loader'], |
||||
|
exclude: /node_modules/ |
||||
|
}, |
||||
|
{ |
||||
|
test: /\.node$/, |
||||
|
use: 'node-loader' |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
// https://www.webpackjs.com/configuration/node/
|
||||
|
node: { |
||||
|
// __dirname: process.env.NODE_ENV !== 'production', // 不转化为字符串
|
||||
|
// __filename: process.env.NODE_ENV !== 'production' // 不转化为字符串
|
||||
|
}, |
||||
|
output: { |
||||
|
filename: 'entry.js', // [name]
|
||||
|
libraryTarget: 'commonjs2', |
||||
|
path: path.join(__dirname, '../../dist/electron') |
||||
|
}, |
||||
|
plugins: [ |
||||
|
new webpack.NoEmitOnErrorsPlugin() |
||||
|
], |
||||
|
resolve: { |
||||
|
alias: { |
||||
|
"@": path.join(__dirname, "../../src/render"), |
||||
|
"@render": path.join(__dirname, "../../src/render"), |
||||
|
"@main": path.join(__dirname, "../../src/main"), |
||||
|
"@src": path.join(__dirname, "../../src"), |
||||
|
"@root": path.join(__dirname, "../../"), |
||||
|
}, |
||||
|
extensions: ['.js', '.json', '.node', '.ts'] |
||||
|
}, |
||||
|
target: 'electron-main' |
||||
|
} |
||||
|
|
||||
|
if (process.env.NODE_ENV !== 'production') { |
||||
|
mainConfig.plugins.push( |
||||
|
new webpack.DefinePlugin({ |
||||
|
'__static': `"${path.join(__dirname, '../../resource/electron/static').replace(/\\/g, '\\\\')}"` |
||||
|
}) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
module.exports = mainConfig |
@ -0,0 +1,7 @@ |
|||||
|
/** |
||||
|
* !!! ensure process.cwd() correct |
||||
|
*/ |
||||
|
process.chdir(__dirname.slice(0, __dirname.lastIndexOf('dist'))) |
||||
|
|
||||
|
|
||||
|
export {}; |
@ -0,0 +1,63 @@ |
|||||
|
import Shared from "./share" |
||||
|
import { Menu, Tray, ipcMain, BrowserWindow, App } from "electron" |
||||
|
const path = require("path") |
||||
|
|
||||
|
// 隐藏主窗口,并创建托盘,绑定关闭事件
|
||||
|
export default function setTray(app: App, mainWindow: BrowserWindow) { |
||||
|
if (Shared.data.miniWindow) { |
||||
|
mainWindow.hide() |
||||
|
return |
||||
|
} |
||||
|
// 用一个 Tray 来表示一个图标,这个图标处于正在运行的系统的通知区
|
||||
|
// 通常被添加到一个 context menu 上.
|
||||
|
// 系统托盘右键菜单
|
||||
|
const trayMenuTemplate = [ |
||||
|
{ |
||||
|
// 系统托盘图标目录
|
||||
|
label: "打开主窗口", |
||||
|
click: () => { |
||||
|
mainWindow.show() |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
// 系统托盘图标目录
|
||||
|
label: "打开悬浮窗", |
||||
|
click: () => { |
||||
|
ipcMain.emit("showSuspensionWindow") |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
// 系统托盘图标目录
|
||||
|
label: "退出", |
||||
|
click: () => { |
||||
|
Shared.data.forceClose = true |
||||
|
app.quit() |
||||
|
}, |
||||
|
}, |
||||
|
] |
||||
|
// 设置系统托盘图标
|
||||
|
const iconPath = path.join(__static, "/icon.png") |
||||
|
|
||||
|
Shared.data.miniWindow = new Tray(iconPath) |
||||
|
|
||||
|
// 图标的上下文菜单
|
||||
|
const contextMenu = Menu.buildFromTemplate(trayMenuTemplate) |
||||
|
|
||||
|
// 展示主窗口,隐藏主窗口 mainWindow.hide()
|
||||
|
mainWindow.hide() |
||||
|
|
||||
|
// 设置托盘悬浮提示
|
||||
|
Shared.data.miniWindow.setToolTip("never forget") |
||||
|
|
||||
|
// 设置托盘菜单
|
||||
|
Shared.data.miniWindow.setContextMenu(contextMenu) |
||||
|
|
||||
|
// 单击托盘小图标显示应用
|
||||
|
Shared.data.miniWindow.on("double-click", () => { |
||||
|
// 显示主程序
|
||||
|
mainWindow.show() |
||||
|
// 关闭托盘显示
|
||||
|
// Shared.data.miniWindow.destroy();
|
||||
|
}) |
||||
|
return Shared.data.miniWindow |
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
import { ipcMain, dialog } from "electron" |
||||
|
|
||||
|
/** |
||||
|
* 格式:@类型:扩展:函数 |
||||
|
*/ |
||||
|
|
||||
|
// 保存数据
|
||||
|
ipcMain.on("@func:buildin:close", data => { |
||||
|
// dialog.showMessageBox(
|
||||
|
// {
|
||||
|
// type: "info",
|
||||
|
// title: "Information",
|
||||
|
// defaultId: 0,
|
||||
|
// cancelId: 0,
|
||||
|
// message: "确定要关闭吗?" + data,
|
||||
|
// buttons: ["没事", "最小化到托盘", "直接退出"],
|
||||
|
// },
|
||||
|
// index => {}
|
||||
|
// )
|
||||
|
}) |
@ -0,0 +1,119 @@ |
|||||
|
import Shared from "./share" |
||||
|
import { getFileUrl } from './util' |
||||
|
import { BrowserWindow, ipcMain, screen, Menu, shell, webContents, app } from "electron" |
||||
|
// webContents
|
||||
|
console.log(webContents.getAllWebContents()) |
||||
|
|
||||
|
const window: any = null //BrowserWindow.fromWebContents(webContents.getFocusedWebContents())
|
||||
|
|
||||
|
ipcMain.on("@float:setPosition", (event, x, y) => { |
||||
|
Shared.data.floatWindow?.setPosition(x, y) |
||||
|
}) |
||||
|
|
||||
|
ipcMain.on("showSuspensionWindow", () => { |
||||
|
if (Shared.data.floatWindow) { |
||||
|
if (Shared.data.floatWindow.isVisible()) { |
||||
|
// createSuspensionWindow()
|
||||
|
} else { |
||||
|
Shared.data.floatWindow.showInactive() |
||||
|
} |
||||
|
} else { |
||||
|
createSuspensionWindow() |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
ipcMain.on("createSuspensionMenu", e => { |
||||
|
const rightM = Menu.buildFromTemplate([ |
||||
|
{ label: "开始全部任务", enabled: false }, |
||||
|
{ label: "暂停全部任务", enabled: false }, |
||||
|
{ |
||||
|
label: "本次传输完自动关机", |
||||
|
click: () => { |
||||
|
ipcMain.emit("@func:buildin:saveData", 32232) |
||||
|
}, |
||||
|
}, |
||||
|
{ type: "separator" }, |
||||
|
{ |
||||
|
label: "隐藏悬浮窗", |
||||
|
click: () => { |
||||
|
if (window) { |
||||
|
window.webContents.send("hideSuspension", false) |
||||
|
} |
||||
|
Shared.data.floatWindow.hide() |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
label: "打开主窗口", |
||||
|
click: () => { |
||||
|
// && !Shared.data.mainWindow.isVisible()
|
||||
|
if (Shared.data.mainWindow) { |
||||
|
Shared.data.mainWindow.show() |
||||
|
} |
||||
|
// window.webContents.send('hideSuspension', false)
|
||||
|
}, |
||||
|
}, |
||||
|
{ type: "separator" }, |
||||
|
{ |
||||
|
label: "加入qq群", |
||||
|
click: () => { |
||||
|
shell.openExternal( |
||||
|
"tencent://groupwpa/?subcmd=all¶m=7B2267726F757055696E223A3831343237303636392C2274696D655374616D70223A313533393531303138387D0A" |
||||
|
) |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
label: "GitHub地址", |
||||
|
click: () => { |
||||
|
shell.openExternal("https://github.com/lihaotian0607/auth") |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
label: "退出软件", |
||||
|
click: () => { |
||||
|
Shared.data.forceClose = true |
||||
|
app.quit() |
||||
|
}, |
||||
|
}, |
||||
|
]) |
||||
|
rightM.popup({}) |
||||
|
}) |
||||
|
|
||||
|
function createSuspensionWindow() { |
||||
|
Shared.data.floatWindow = new BrowserWindow({ |
||||
|
width: 102, // 悬浮窗口的宽度 比实际DIV的宽度要多2px 因为有1px的边框
|
||||
|
height: 27, // 悬浮窗口的高度 比实际DIV的高度要多2px 因为有1px的边框
|
||||
|
type: "toolbar", // 创建的窗口类型为工具栏窗口
|
||||
|
frame: false, // 要创建无边框窗口
|
||||
|
resizable: false, // 禁止窗口大小缩放
|
||||
|
show: false, // 先不让窗口显示
|
||||
|
webPreferences: { |
||||
|
devTools: false, // 关闭调试工具
|
||||
|
nodeIntegration: true, |
||||
|
contextIsolation: false, |
||||
|
}, |
||||
|
transparent: true, // 设置透明
|
||||
|
alwaysOnTop: true, // 窗口是否总是显示在其他窗口之前
|
||||
|
}) |
||||
|
const size = screen.getPrimaryDisplay().workAreaSize // 获取显示器的宽高
|
||||
|
const winSize = Shared.data.floatWindow.getSize() // 获取窗口宽高
|
||||
|
// 设置窗口的位置 注意x轴要桌面的宽度 - 窗口的宽度
|
||||
|
Shared.data.floatWindow.setPosition(size.width - winSize[0], 100) |
||||
|
Shared.data.floatWindow.setPosition(size.width / 2, size.height / 2) |
||||
|
Shared.data.floatWindow.loadURL(getFileUrl("float")) |
||||
|
|
||||
|
Shared.data.floatWindow.once("ready-to-show", () => { |
||||
|
Shared.data.floatWindow.show() |
||||
|
}) |
||||
|
Shared.data.floatWindow.on("double-click", () => { |
||||
|
alert(123) |
||||
|
}) |
||||
|
Shared.data.floatWindow.on("close", () => { |
||||
|
Shared.data.floatWindow = null |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
ipcMain.on("hideSuspensionWindow", () => { |
||||
|
if (Shared.data.floatWindow) { |
||||
|
Shared.data.floatWindow.hide() |
||||
|
} |
||||
|
}) |
@ -0,0 +1,18 @@ |
|||||
|
|
||||
|
// Install `electron-debug` with `devtron`
|
||||
|
import electronDebug from "electron-debug" |
||||
|
|
||||
|
electronDebug({ showDevTools: true }) |
||||
|
|
||||
|
// Install `vue-devtools`
|
||||
|
require('electron').app.on('ready', () => { |
||||
|
let installExtension = require('electron-devtools-installer') |
||||
|
installExtension.default(installExtension.VUEJS_DEVTOOLS) |
||||
|
.then(() => {}) |
||||
|
.catch((err:Error) => { |
||||
|
console.log('Unable to install `vue-devtools`: \n', err) |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
// Require `main` process to boot app
|
||||
|
import './index' |
@ -0,0 +1,133 @@ |
|||||
|
/** |
||||
|
* electron 主文件 |
||||
|
*/ |
||||
|
"use strict" |
||||
|
import Shared from "./share" |
||||
|
import setTray from "./disk" |
||||
|
import { getFileUrl } from "./util" |
||||
|
// import '../renderer/store'
|
||||
|
import "./facilities" |
||||
|
import { app, BrowserWindow, dialog } from "electron" |
||||
|
|
||||
|
Shared.data = { |
||||
|
mainWindow: null, // 主窗口
|
||||
|
floatWindow: null, // 浮动窗口
|
||||
|
miniWindow: null, |
||||
|
forceClose: false, |
||||
|
lastChoice: -1, // 做过的选择
|
||||
|
} |
||||
|
console.log("asdasadsads") |
||||
|
/** |
||||
|
* Set `__static` path to static files in production |
||||
|
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
|
||||
|
*/ |
||||
|
let isDev = process.env.NODE_ENV == "development" ? true : false |
||||
|
|
||||
|
if (!isDev) { |
||||
|
// @ts-ignore
|
||||
|
global.__static = require("path").join(__dirname, "/static").replace(/\\/g, "\\\\") |
||||
|
} |
||||
|
|
||||
|
function createWindow() { |
||||
|
/** |
||||
|
* Initial window options |
||||
|
*/ |
||||
|
Shared.data.mainWindow = new BrowserWindow({ |
||||
|
height: 400, |
||||
|
useContentSize: true, |
||||
|
width: 600, |
||||
|
resizable: true, |
||||
|
minWidth: 450, |
||||
|
minHeight: 400, |
||||
|
icon: __static + "/icon.png", |
||||
|
// frame: false,
|
||||
|
// transparent: true,
|
||||
|
alwaysOnTop: false, |
||||
|
webPreferences: { |
||||
|
nodeIntegration: true, |
||||
|
contextIsolation: false, |
||||
|
}, |
||||
|
}) |
||||
|
|
||||
|
Shared.data.mainWindow.loadURL(getFileUrl("")) |
||||
|
Shared.data.mainWindow.on("close", (event: any) => { |
||||
|
if (Shared.data.forceClose) { |
||||
|
Shared.data.mainWindow = null |
||||
|
app.quit() |
||||
|
} else if (Shared.data.mainWindow) { |
||||
|
if (Shared.data.lastChoice === 1) { |
||||
|
if (Shared.data.miniWindow) { |
||||
|
Shared.data.mainWindow.hide() // 调用 最小化实例方法
|
||||
|
} else { |
||||
|
setTray(app, Shared.data.mainWindow) |
||||
|
} |
||||
|
event.preventDefault() |
||||
|
} else { |
||||
|
const choice = dialog.showMessageBoxSync(Shared.data.mainWindow, { |
||||
|
type: "info", |
||||
|
title: "Information", |
||||
|
defaultId: 0, |
||||
|
cancelId: 0, |
||||
|
message: "确定要关闭吗?", |
||||
|
buttons: ["没事", "最小化到托盘", "直接退出"], |
||||
|
}) |
||||
|
if (choice === 1) { |
||||
|
Shared.data.lastChoice = 1 |
||||
|
if (Shared.data.miniWindow) { |
||||
|
Shared.data.mainWindow.hide() // 调用 最小化实例方法
|
||||
|
} else { |
||||
|
setTray(app, Shared.data.mainWindow) |
||||
|
} |
||||
|
event.preventDefault() |
||||
|
} else if (choice === 2) { |
||||
|
Shared.data.mainWindow = null |
||||
|
// app.quit()
|
||||
|
// 不要用quit();试了会弹两次
|
||||
|
Shared.data.forceClose = true |
||||
|
app.quit() // exit()直接关闭客户端,不会执行quit();
|
||||
|
} else { |
||||
|
event.preventDefault() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const gotTheLock = app.requestSingleInstanceLock() |
||||
|
if (!gotTheLock) { |
||||
|
app.exit() |
||||
|
} else { |
||||
|
require("./menu") |
||||
|
require("./float") |
||||
|
|
||||
|
app.on("second-instance", (event, commandLine, workingDirectory) => { |
||||
|
// 当运行第二个实例时,将会聚焦到mainWindow这个窗口
|
||||
|
if (Shared.data.mainWindow) { |
||||
|
if (Shared.data.mainWindow.isMinimized()) Shared.data.mainWindow.restore() |
||||
|
Shared.data.mainWindow.focus() |
||||
|
Shared.data.mainWindow.show() |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
app.on("ready", createWindow) |
||||
|
|
||||
|
app.on("before-quit", event => { |
||||
|
if (Shared.data.forceClose) { |
||||
|
app.exit() |
||||
|
} else { |
||||
|
event.preventDefault() |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
app.on("window-all-closed", () => { |
||||
|
if (process.platform !== "darwin") { |
||||
|
app.exit() |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
app.on("activate", () => { |
||||
|
if (Shared.data.mainWindow === null) { |
||||
|
createWindow() |
||||
|
} |
||||
|
}) |
||||
|
} |
@ -0,0 +1,66 @@ |
|||||
|
import electron from "electron" |
||||
|
|
||||
|
const BrowserWindow = electron.BrowserWindow |
||||
|
const Menu = electron.Menu |
||||
|
const app = electron.app |
||||
|
const dialog = electron.dialog |
||||
|
const ipcMain = electron.ipcMain |
||||
|
|
||||
|
let newwin:electron.BrowserWindow|null = null |
||||
|
|
||||
|
let template = [ |
||||
|
{ |
||||
|
label: "关于", |
||||
|
click: function (item:any, focusedWindow:any) { |
||||
|
// https://www.electronjs.org/docs/api/browser-window#winsetmenubarvisibilityvisible-windows-linux
|
||||
|
if (focusedWindow && !newwin) { |
||||
|
newwin = new BrowserWindow({ |
||||
|
width: 600, |
||||
|
height: 200, |
||||
|
// modal: true,
|
||||
|
show: false, |
||||
|
resizable: true, |
||||
|
parent: focusedWindow, // win是主窗口
|
||||
|
//
|
||||
|
webPreferences: { |
||||
|
// 下面两个必须这么用,看a.md的文档
|
||||
|
nodeIntegration: true, |
||||
|
contextIsolation: false, |
||||
|
// 预加载动画
|
||||
|
// preload: join(__dirname, "../../src/preload/index.js"),
|
||||
|
}, |
||||
|
}) |
||||
|
// 隐藏菜单
|
||||
|
newwin.setMenuBarVisibility(false) |
||||
|
newwin.loadURL(process.env.NODE_ENV === "development" ? `http://localhost:${process.env.PORT}/#/about` : `file://${__dirname}/index.html#/about`); |
||||
|
newwin.on("ready-to-show", () => { |
||||
|
newwin?.show() |
||||
|
}) |
||||
|
newwin.on("closed", () => { |
||||
|
newwin = null |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
] |
||||
|
|
||||
|
// if (process.platform === 'darwin') {
|
||||
|
//
|
||||
|
// }
|
||||
|
//
|
||||
|
// if (process.platform === 'win32') {
|
||||
|
//
|
||||
|
// }
|
||||
|
app.on("ready", function () { |
||||
|
const menu = Menu.buildFromTemplate(template) |
||||
|
Menu.setApplicationMenu(menu) |
||||
|
}) |
||||
|
app.on("browser-window-created", function () { |
||||
|
// let reopenMenuItem = findReopenMenuItem()
|
||||
|
// if (reopenMenuItem) reopenMenuItem.enabled = false
|
||||
|
}) |
||||
|
app.on("window-all-closed", function () { |
||||
|
app.exit() |
||||
|
// let reopenMenuItem = findReopenMenuItem()
|
||||
|
// if (reopenMenuItem) reopenMenuItem.enabled = true
|
||||
|
}) |
@ -0,0 +1,189 @@ |
|||||
|
// const path = require('path')
|
||||
|
import { BrowserWindow } from "electron" |
||||
|
import Shared from "./share" |
||||
|
import { getFileUrl } from "./util" |
||||
|
const electron = require("electron") |
||||
|
const setTray = require("./disk").default |
||||
|
// const BrowserWindow = electron.BrowserWindow
|
||||
|
const Menu = electron.Menu |
||||
|
const app = electron.app |
||||
|
const dialog = electron.dialog |
||||
|
const ipcMain = electron.ipcMain |
||||
|
|
||||
|
let newwin: BrowserWindow | null |
||||
|
|
||||
|
let template = [ |
||||
|
{ |
||||
|
label: "选择保存目录", |
||||
|
click: function (item: any, focusedWindow: BrowserWindow) { |
||||
|
dialog |
||||
|
.showOpenDialog(focusedWindow, { |
||||
|
properties: ["openDirectory"], |
||||
|
}) |
||||
|
.then(result => { |
||||
|
ipcMain.emit("@menu:selectDir", result) |
||||
|
}) |
||||
|
.catch(err => { |
||||
|
throw err |
||||
|
}) |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
label: "置顶", |
||||
|
key: "alwaysTop", |
||||
|
click: function (item: any, focusedWindow: BrowserWindow) { |
||||
|
if (Shared.data.mainWindow.isAlwaysOnTop()) { |
||||
|
Shared.data.mainWindow.setAlwaysOnTop(false) |
||||
|
} else { |
||||
|
Shared.data.mainWindow.setAlwaysOnTop(true) |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
label: "重载", |
||||
|
accelerator: "CmdOrCtrl+R", |
||||
|
click: function (item: any, focusedWindow: BrowserWindow) { |
||||
|
if (focusedWindow) { |
||||
|
// 重载之后, 刷新并关闭所有的次要窗体
|
||||
|
if (focusedWindow.id === 1) { |
||||
|
BrowserWindow.getAllWindows().forEach(function (win) { |
||||
|
if (win.id > 1) { |
||||
|
win.close() |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
focusedWindow.reload() |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
label: "功能", |
||||
|
submenu: [ |
||||
|
{ |
||||
|
label: "悬浮窗", |
||||
|
click: function (item: any, focusedWindow: BrowserWindow) { |
||||
|
ipcMain.emit("showSuspensionWindow") |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
label: "最小化到托盘", |
||||
|
click: function (item: any, focusedWindow: BrowserWindow) { |
||||
|
Shared.data.lastChoice = 1 |
||||
|
if (Shared.data.miniWindow) { |
||||
|
Shared.data.mainWindow.hide() // 调用 最小化实例方法
|
||||
|
} else { |
||||
|
setTray(app, Shared.data.mainWindow) |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
label: "切换全屏", |
||||
|
accelerator: (function () { |
||||
|
if (process.platform === "darwin") { |
||||
|
return "Ctrl+Command+F" |
||||
|
} else { |
||||
|
return "F11" |
||||
|
} |
||||
|
})(), |
||||
|
click: function (item: any, focusedWindow: BrowserWindow) { |
||||
|
if (focusedWindow) { |
||||
|
focusedWindow.setFullScreen(!focusedWindow.isFullScreen()) |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
label: "开发者", |
||||
|
submenu: [ |
||||
|
{ |
||||
|
label: "切换开发者工具", |
||||
|
accelerator: (function () { |
||||
|
if (process.platform === "darwin") { |
||||
|
return "Alt+Command+I" |
||||
|
} else { |
||||
|
return "Ctrl+Shift+I" |
||||
|
} |
||||
|
})(), |
||||
|
click: function (item: any, focusedWindow: BrowserWindow) { |
||||
|
if (focusedWindow) { |
||||
|
// @ts-ignore
|
||||
|
focusedWindow.toggleDevTools() |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
// {
|
||||
|
// label: '重新启动',
|
||||
|
// click: function(item, focusedWindow) {
|
||||
|
// app.exit()
|
||||
|
// app.relaunch()
|
||||
|
// // app.relaunch({ args: process.argv.slice(1).concat(['--relaunch']) })
|
||||
|
// // app.quit()
|
||||
|
// }
|
||||
|
// },
|
||||
|
{ |
||||
|
label: "关于", |
||||
|
click: function (item: any, focusedWindow: BrowserWindow) { |
||||
|
// https://www.electronjs.org/docs/api/browser-window#winsetmenubarvisibilityvisible-windows-linux
|
||||
|
if (focusedWindow && !newwin) { |
||||
|
newwin = new BrowserWindow({ |
||||
|
width: 600, |
||||
|
height: 200, |
||||
|
minimizable: false, |
||||
|
darkTheme: true, |
||||
|
modal: true, |
||||
|
show: false, |
||||
|
resizable: false, |
||||
|
webPreferences: { |
||||
|
nodeIntegration: true, |
||||
|
contextIsolation: false, |
||||
|
}, |
||||
|
// parent: focusedWindow // win是主窗口
|
||||
|
}) |
||||
|
// 隐藏菜单
|
||||
|
newwin.setMenuBarVisibility(false) |
||||
|
// vue是单页面,需要改成多页面才行
|
||||
|
newwin.loadURL(getFileUrl("about")) |
||||
|
newwin.on("ready-to-show", () => { |
||||
|
newwin?.show() |
||||
|
}) |
||||
|
newwin.on("close", () => { |
||||
|
newwin = null |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
}, |
||||
|
] |
||||
|
// function findTopItem() {
|
||||
|
// const menu = Menu.getApplicationMenu()
|
||||
|
// if (!menu) return
|
||||
|
// let reopenMenuItem
|
||||
|
// menu.items.forEach(function(item) {
|
||||
|
// if (item.key === 'alwaysTop') {
|
||||
|
// reopenMenuItem = item
|
||||
|
// }
|
||||
|
// // if (item.submenu) {
|
||||
|
// // item.submenu.items.forEach(function(item) {
|
||||
|
// // if (item.key === 'alwaysTop') {
|
||||
|
// // reopenMenuItem = item
|
||||
|
// // }
|
||||
|
// // })
|
||||
|
// // }
|
||||
|
// })
|
||||
|
// console.log(reopenMenuItem)
|
||||
|
// return reopenMenuItem
|
||||
|
// }
|
||||
|
app.on("ready", function () { |
||||
|
const menu = Menu.buildFromTemplate(<any>template) |
||||
|
Menu.setApplicationMenu(menu) |
||||
|
}) |
||||
|
app.on("browser-window-created", function () { |
||||
|
// let reopenMenuItem = findReopenMenuItem()
|
||||
|
// if (reopenMenuItem) reopenMenuItem.enabled = false
|
||||
|
}) |
||||
|
app.on("window-all-closed", function () { |
||||
|
// let reopenMenuItem = findReopenMenuItem()
|
||||
|
// if (reopenMenuItem) reopenMenuItem.enabled = true
|
||||
|
}) |
@ -0,0 +1,12 @@ |
|||||
|
interface IPayload{ |
||||
|
data: { |
||||
|
[propName:string]: any; |
||||
|
}; |
||||
|
[propName:string]: any; |
||||
|
} |
||||
|
|
||||
|
const payload:IPayload = { |
||||
|
data: {} |
||||
|
} |
||||
|
|
||||
|
export default payload |
@ -0,0 +1,12 @@ |
|||||
|
|
||||
|
export function getFileUrl(route: string, isSPA: boolean = true) { |
||||
|
if (isSPA) { |
||||
|
const winURL = |
||||
|
process.env.NODE_ENV === "development" |
||||
|
? `http://localhost:${process.env.PORT}/#/${route}` |
||||
|
: `file://${__dirname}/index.html#/${route}` |
||||
|
return winURL |
||||
|
} else { |
||||
|
return "" |
||||
|
} |
||||
|
} |
@ -0,0 +1,125 @@ |
|||||
|
// @ts-nocheck
|
||||
|
|
||||
|
/** docoment 加载完成 */ |
||||
|
function domReady(...args) { |
||||
|
const condition = args.length ? [...args] : ['complete', 'interactive'] |
||||
|
return new Promise(resolve => { |
||||
|
if (condition.includes(document.readyState)) { |
||||
|
resolve(true) |
||||
|
} else { |
||||
|
document.addEventListener('readystatechange', () => { |
||||
|
if (condition.includes(document.readyState)) { |
||||
|
resolve(true) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
/** 插入 loading */ |
||||
|
function loadingBootstrap() { |
||||
|
const loadingStyle = document.createElement('style'); |
||||
|
const loadingBox = document.createElement('div'); |
||||
|
|
||||
|
loadingStyle.id = 'preload-loading-style'; |
||||
|
loadingBox.id = 'preload-loading-box'; |
||||
|
|
||||
|
loadingStyle.textContent += ` |
||||
|
/* https://projects.lukehaas.me/css-loaders/ */ |
||||
|
.loading-box { height: 100vh; width: 100vw; position: fixed; left: 0; top: 0; display: flex; align-items: center; background-color: #242424; z-index: 9; } |
||||
|
.load1 .loader, |
||||
|
.load1 .loader:before, |
||||
|
.load1 .loader:after { |
||||
|
background: #ffffff; |
||||
|
-webkit-animation: load1 1s infinite ease-in-out; |
||||
|
animation: load1 1s infinite ease-in-out; |
||||
|
width: 1em; |
||||
|
height: 4em; |
||||
|
} |
||||
|
.load1 .loader { |
||||
|
color: #ffffff; |
||||
|
text-indent: -9999em; |
||||
|
margin: 88px auto; |
||||
|
position: relative; |
||||
|
font-size: 11px; |
||||
|
-webkit-transform: translateZ(0); |
||||
|
-ms-transform: translateZ(0); |
||||
|
transform: translateZ(0); |
||||
|
-webkit-animation-delay: -0.16s; |
||||
|
animation-delay: -0.16s; |
||||
|
} |
||||
|
.load1 .loader:before, |
||||
|
.load1 .loader:after { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
content: ''; |
||||
|
} |
||||
|
.load1 .loader:before { |
||||
|
left: -1.5em; |
||||
|
-webkit-animation-delay: -0.32s; |
||||
|
animation-delay: -0.32s; |
||||
|
} |
||||
|
.load1 .loader:after { |
||||
|
left: 1.5em; |
||||
|
} |
||||
|
@-webkit-keyframes load1 { |
||||
|
0%, |
||||
|
80%, |
||||
|
100% { |
||||
|
box-shadow: 0 0; |
||||
|
height: 4em; |
||||
|
} |
||||
|
40% { |
||||
|
box-shadow: 0 -2em; |
||||
|
height: 5em; |
||||
|
} |
||||
|
} |
||||
|
@keyframes load1 { |
||||
|
0%, |
||||
|
80%, |
||||
|
100% { |
||||
|
box-shadow: 0 0; |
||||
|
height: 4em; |
||||
|
} |
||||
|
40% { |
||||
|
box-shadow: 0 -2em; |
||||
|
height: 5em; |
||||
|
} |
||||
|
}`;
|
||||
|
|
||||
|
loadingBox.classList.add('loading-box', 'load1'); |
||||
|
loadingBox.innerHTML += '<div class="loader"></div>'; |
||||
|
|
||||
|
const appendLoading = () => { |
||||
|
document.head.appendChild(loadingStyle); |
||||
|
document.body.appendChild(loadingBox); |
||||
|
}; |
||||
|
|
||||
|
const removeLoading = () => { |
||||
|
const _loadingStyle = document.getElementById('preload-loading-style'); |
||||
|
const _loadingBox = document.getElementById('preload-loading-box'); |
||||
|
|
||||
|
// Ensure the remove child exists.
|
||||
|
_loadingStyle && document.head.removeChild(_loadingStyle); |
||||
|
_loadingBox && document.body.removeChild(_loadingBox); |
||||
|
}; |
||||
|
|
||||
|
return { loadingStyle, loadingBox, removeLoading, appendLoading } |
||||
|
} |
||||
|
|
||||
|
; (async function () { |
||||
|
await domReady(); |
||||
|
|
||||
|
let _isCallRemoveLoading = false; |
||||
|
const { removeLoading, appendLoading } = loadingBootstrap(); |
||||
|
|
||||
|
window.removeLoading = () => { |
||||
|
_isCallRemoveLoading = true; |
||||
|
removeLoading(); |
||||
|
}; |
||||
|
|
||||
|
// 5 秒超时自动关闭
|
||||
|
setTimeout(() => !_isCallRemoveLoading && removeLoading(), 4999); |
||||
|
|
||||
|
appendLoading(); |
||||
|
})(); |
@ -0,0 +1,91 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
|
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>electron-vue3-vite</title> |
||||
|
<style> |
||||
|
/* https://projects.lukehaas.me/css-loaders/ */ |
||||
|
body { margin: 0; background-color: #242424; } |
||||
|
.loading-box { height: 100vh; display: flex; align-items: center; } |
||||
|
|
||||
|
.load1 .loader, |
||||
|
.load1 .loader:before, |
||||
|
.load1 .loader:after { |
||||
|
background: #ffffff; |
||||
|
-webkit-animation: load1 1s infinite ease-in-out; |
||||
|
animation: load1 1s infinite ease-in-out; |
||||
|
width: 1em; |
||||
|
height: 4em; |
||||
|
} |
||||
|
|
||||
|
.load1 .loader { |
||||
|
color: #ffffff; |
||||
|
text-indent: -9999em; |
||||
|
margin: 88px auto; |
||||
|
position: relative; |
||||
|
font-size: 11px; |
||||
|
-webkit-transform: translateZ(0); |
||||
|
-ms-transform: translateZ(0); |
||||
|
transform: translateZ(0); |
||||
|
-webkit-animation-delay: -0.16s; |
||||
|
animation-delay: -0.16s; |
||||
|
} |
||||
|
|
||||
|
.load1 .loader:before, |
||||
|
.load1 .loader:after { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
content: ''; |
||||
|
} |
||||
|
|
||||
|
.load1 .loader:before { |
||||
|
left: -1.5em; |
||||
|
-webkit-animation-delay: -0.32s; |
||||
|
animation-delay: -0.32s; |
||||
|
} |
||||
|
|
||||
|
.load1 .loader:after { |
||||
|
left: 1.5em; |
||||
|
} |
||||
|
|
||||
|
@-webkit-keyframes load1 { |
||||
|
|
||||
|
0%, |
||||
|
80%, |
||||
|
100% { |
||||
|
box-shadow: 0 0; |
||||
|
height: 4em; |
||||
|
} |
||||
|
|
||||
|
40% { |
||||
|
box-shadow: 0 -2em; |
||||
|
height: 5em; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@keyframes load1 { |
||||
|
|
||||
|
0%, |
||||
|
80%, |
||||
|
100% { |
||||
|
box-shadow: 0 0; |
||||
|
height: 4em; |
||||
|
} |
||||
|
|
||||
|
40% { |
||||
|
box-shadow: 0 -2em; |
||||
|
height: 5em; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
|
</head> |
||||
|
|
||||
|
<body> |
||||
|
<div class="loading-box load1"> |
||||
|
<div class="loader"></div> |
||||
|
</div> |
||||
|
</body> |
||||
|
|
||||
|
</html> |
@ -0,0 +1,58 @@ |
|||||
|
import { HashRouter as Router, Switch, Redirect, Route, Link } from "react-router-dom" |
||||
|
import React, { Fragment } from "react" |
||||
|
|
||||
|
import { pageList } from "@/plugins/pageHoc" |
||||
|
|
||||
|
import Layout from "@/views/Layout" |
||||
|
|
||||
|
import Auth from "@/ui/Auth" |
||||
|
import routes, { Loading } from "./route" |
||||
|
|
||||
|
function LoadWrapper(props: any) { |
||||
|
if (props.isLazy) { |
||||
|
const LoadingComp=props.loading?props.loading: ()=><Loading color="#ff0000"></Loading> //NormalLoading;
|
||||
|
return <React.Suspense fallback={<LoadingComp></LoadingComp>}>{props.children}</React.Suspense> |
||||
|
} |
||||
|
return <Fragment>{props.children}</Fragment> |
||||
|
} |
||||
|
|
||||
|
function RouteMap(props: any) { |
||||
|
const routes: any[] = props.routes |
||||
|
|
||||
|
return ( |
||||
|
<Switch> |
||||
|
{routes.map((route, index) => { |
||||
|
const { exact = false } = route |
||||
|
if (route.redirect) { |
||||
|
return ( |
||||
|
<Route key={index} path={route.path} exact={exact}> |
||||
|
<Redirect to={route.redirect}></Redirect> |
||||
|
</Route> |
||||
|
) |
||||
|
} |
||||
|
if (route.component) { |
||||
|
return ( |
||||
|
<Auth key={index} needAuth={!!route.meta?.auth} path={route.path} exact={exact}> |
||||
|
<LoadWrapper loading={route.loading} isLazy={typeof route.component.$$typeof === "symbol"}> |
||||
|
<route.component meta={route.meta}>{route.children && <RouteMap routes={route.children}></RouteMap>}</route.component> |
||||
|
</LoadWrapper> |
||||
|
</Auth> |
||||
|
) |
||||
|
} |
||||
|
})} |
||||
|
</Switch> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
export default function () { |
||||
|
return ( |
||||
|
<Router> |
||||
|
<RouteMap routes={routes}></RouteMap> |
||||
|
{/* <Layout |
||||
|
render={() => { |
||||
|
return <RouteMap routes={routes}></RouteMap> |
||||
|
}} |
||||
|
></Layout> */} |
||||
|
</Router> |
||||
|
) |
||||
|
} |
After Width: | Height: | Size: 4.0 MiB |
After Width: | Height: | Size: 1.2 MiB |
@ -0,0 +1,10 @@ |
|||||
|
{ |
||||
|
"login":{ |
||||
|
"title": "Bronze Age 2022", |
||||
|
"username_placeholder": "Username", |
||||
|
"password_placeholder": "Password", |
||||
|
"btnText": "Login" |
||||
|
}, |
||||
|
"home":"Bronze Age 2021", |
||||
|
"welcome":"Welcome To Home" |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
{ |
||||
|
"login":{ |
||||
|
"title": "青铜时代2022", |
||||
|
"username_placeholder": "用户名", |
||||
|
"password_placeholder": "密码", |
||||
|
"btnText": "登录" |
||||
|
}, |
||||
|
"home":"青铜时代2021", |
||||
|
"welcome":"欢迎来首页" |
||||
|
} |
@ -0,0 +1,112 @@ |
|||||
|
/* cyrillic-ext */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 200; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3i94_wmhduz8A.woff2) format('woff2'); |
||||
|
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; |
||||
|
} |
||||
|
/* cyrillic */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 200; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3i94_wkxduz8A.woff2) format('woff2'); |
||||
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; |
||||
|
} |
||||
|
/* greek-ext */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 200; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3i94_wmxduz8A.woff2) format('woff2'); |
||||
|
unicode-range: U+1F00-1FFF; |
||||
|
} |
||||
|
/* greek */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 200; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3i94_wlBduz8A.woff2) format('woff2'); |
||||
|
unicode-range: U+0370-03FF; |
||||
|
} |
||||
|
/* vietnamese */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 200; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3i94_wmBduz8A.woff2) format('woff2'); |
||||
|
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; |
||||
|
} |
||||
|
/* latin-ext */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 200; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3i94_wmRduz8A.woff2) format('woff2'); |
||||
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; |
||||
|
} |
||||
|
/* latin */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 200; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3i94_wlxdu.woff2) format('woff2'); |
||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; |
||||
|
} |
||||
|
/* cyrillic-ext */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 300; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmhduz8A.woff2) format('woff2'); |
||||
|
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; |
||||
|
} |
||||
|
/* cyrillic */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 300; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwkxduz8A.woff2) format('woff2'); |
||||
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; |
||||
|
} |
||||
|
/* greek-ext */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 300; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmxduz8A.woff2) format('woff2'); |
||||
|
unicode-range: U+1F00-1FFF; |
||||
|
} |
||||
|
/* greek */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 300; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlBduz8A.woff2) format('woff2'); |
||||
|
unicode-range: U+0370-03FF; |
||||
|
} |
||||
|
/* vietnamese */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 300; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmBduz8A.woff2) format('woff2'); |
||||
|
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; |
||||
|
} |
||||
|
/* latin-ext */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 300; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmRduz8A.woff2) format('woff2'); |
||||
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; |
||||
|
} |
||||
|
/* latin */ |
||||
|
@font-face { |
||||
|
font-family: 'Source Sans Pro'; |
||||
|
font-style: normal; |
||||
|
font-weight: 300; |
||||
|
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu.woff2) format('woff2'); |
||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; |
||||
|
} |
@ -0,0 +1,127 @@ |
|||||
|
/* http://meyerweb.com/eric/tools/css/reset/ |
||||
|
v2.0 | 20110126 |
||||
|
License: none (public domain) |
||||
|
*/ |
||||
|
|
||||
|
html, |
||||
|
body, |
||||
|
div, |
||||
|
span, |
||||
|
applet, |
||||
|
object, |
||||
|
iframe, |
||||
|
h1, |
||||
|
h2, |
||||
|
h3, |
||||
|
h4, |
||||
|
h5, |
||||
|
h6, |
||||
|
p, |
||||
|
blockquote, |
||||
|
pre, |
||||
|
a, |
||||
|
abbr, |
||||
|
acronym, |
||||
|
address, |
||||
|
big, |
||||
|
cite, |
||||
|
code, |
||||
|
del, |
||||
|
dfn, |
||||
|
em, |
||||
|
img, |
||||
|
ins, |
||||
|
kbd, |
||||
|
q, |
||||
|
s, |
||||
|
samp, |
||||
|
small, |
||||
|
strike, |
||||
|
strong, |
||||
|
sub, |
||||
|
sup, |
||||
|
tt, |
||||
|
var, |
||||
|
b, |
||||
|
u, |
||||
|
i, |
||||
|
center, |
||||
|
dl, |
||||
|
dt, |
||||
|
dd, |
||||
|
ol, |
||||
|
ul, |
||||
|
li, |
||||
|
fieldset, |
||||
|
form, |
||||
|
label, |
||||
|
legend, |
||||
|
table, |
||||
|
caption, |
||||
|
tbody, |
||||
|
tfoot, |
||||
|
thead, |
||||
|
tr, |
||||
|
th, |
||||
|
td, |
||||
|
article, |
||||
|
aside, |
||||
|
canvas, |
||||
|
details, |
||||
|
embed, |
||||
|
figure, |
||||
|
figcaption, |
||||
|
footer, |
||||
|
header, |
||||
|
hgroup, |
||||
|
menu, |
||||
|
nav, |
||||
|
output, |
||||
|
ruby, |
||||
|
section, |
||||
|
summary, |
||||
|
time, |
||||
|
mark, |
||||
|
audio, |
||||
|
video { |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
border: 0; |
||||
|
font-size: 100%; |
||||
|
font: inherit; |
||||
|
vertical-align: baseline; |
||||
|
} |
||||
|
/* HTML5 display-role reset for older browsers */ |
||||
|
article, |
||||
|
aside, |
||||
|
details, |
||||
|
figcaption, |
||||
|
figure, |
||||
|
footer, |
||||
|
header, |
||||
|
hgroup, |
||||
|
menu, |
||||
|
nav, |
||||
|
section { |
||||
|
display: block; |
||||
|
} |
||||
|
|
||||
|
ol, |
||||
|
ul { |
||||
|
list-style: none; |
||||
|
} |
||||
|
blockquote, |
||||
|
q { |
||||
|
quotes: none; |
||||
|
} |
||||
|
blockquote:before, |
||||
|
blockquote:after, |
||||
|
q:before, |
||||
|
q:after { |
||||
|
content: ""; |
||||
|
content: none; |
||||
|
} |
||||
|
table { |
||||
|
border-collapse: collapse; |
||||
|
border-spacing: 0; |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
@import "./reset.scss"; |
||||
|
@import "./_font.scss"; |
||||
|
|
||||
|
body, |
||||
|
button, |
||||
|
input, |
||||
|
select, |
||||
|
textarea { |
||||
|
font: 12px/1.5 tahoma, arial, "Hiragino Sans GB", "\5b8b\4f53", sans-serif; |
||||
|
} |
||||
|
|
||||
|
body{ |
||||
|
// background-image: url("@/assets/images/0.jpg"); |
||||
|
// background-attachment: fixed; |
||||
|
// background-position: center top 36px; |
||||
|
// background-repeat: no-repeat; |
||||
|
// background-size: cover; |
||||
|
} |
||||
|
a { |
||||
|
// color: initial; |
||||
|
display: inline-block; |
||||
|
text-decoration: none; |
||||
|
color: inherit; |
||||
|
} |
||||
|
|
||||
|
.clearfix { |
||||
|
@include clearfix; |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
$color: green; |
||||
|
|
||||
|
|
||||
|
@mixin clearfix { |
||||
|
&:after { |
||||
|
clear: both; |
||||
|
content: "."; |
||||
|
display: block; |
||||
|
height: 0; |
||||
|
line-height: 0; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
*height: 1%; |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
import React from "react"; |
||||
|
import cs from "classnames" |
||||
|
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom"; |
||||
|
|
||||
|
export default function () { |
||||
|
return ( |
||||
|
<div className={cs("header clearfix")}> |
||||
|
这是一个标题 |
||||
|
</div> |
||||
|
); |
||||
|
} |
@ -0,0 +1,120 @@ |
|||||
|
|
||||
|
.bg-bubbles { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
|
||||
|
z-index: 1; |
||||
|
|
||||
|
li { |
||||
|
position: absolute; |
||||
|
list-style: none; |
||||
|
display: block; |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
background-color: rgba(255, 255, 255, 0.15); |
||||
|
bottom: -160px; |
||||
|
|
||||
|
-webkit-animation: square 25s infinite; |
||||
|
animation: square 25s infinite; |
||||
|
|
||||
|
-webkit-transition-timing-function: linear; |
||||
|
transition-timing-function: linear; |
||||
|
|
||||
|
&:nth-child(1) { |
||||
|
left: 10%; |
||||
|
} |
||||
|
|
||||
|
&:nth-child(2) { |
||||
|
left: 20%; |
||||
|
|
||||
|
width: 80px; |
||||
|
height: 80px; |
||||
|
|
||||
|
animation-delay: 2s; |
||||
|
animation-duration: 17s; |
||||
|
} |
||||
|
|
||||
|
&:nth-child(3) { |
||||
|
left: 25%; |
||||
|
animation-delay: 4s; |
||||
|
} |
||||
|
|
||||
|
&:nth-child(4) { |
||||
|
left: 40%; |
||||
|
width: 60px; |
||||
|
height: 60px; |
||||
|
|
||||
|
animation-duration: 22s; |
||||
|
|
||||
|
background-color: rgba(255, 255, 255, 0.25); |
||||
|
} |
||||
|
|
||||
|
&:nth-child(5) { |
||||
|
left: 70%; |
||||
|
} |
||||
|
|
||||
|
&:nth-child(6) { |
||||
|
left: 80%; |
||||
|
width: 120px; |
||||
|
height: 120px; |
||||
|
|
||||
|
animation-delay: 3s; |
||||
|
background-color: rgba(255, 255, 255, 0.2); |
||||
|
} |
||||
|
|
||||
|
&:nth-child(7) { |
||||
|
left: 32%; |
||||
|
width: 160px; |
||||
|
height: 160px; |
||||
|
|
||||
|
animation-delay: 7s; |
||||
|
} |
||||
|
|
||||
|
&:nth-child(8) { |
||||
|
left: 55%; |
||||
|
width: 20px; |
||||
|
height: 20px; |
||||
|
|
||||
|
animation-delay: 15s; |
||||
|
animation-duration: 40s; |
||||
|
} |
||||
|
|
||||
|
&:nth-child(9) { |
||||
|
left: 25%; |
||||
|
width: 10px; |
||||
|
height: 10px; |
||||
|
|
||||
|
animation-delay: 2s; |
||||
|
animation-duration: 40s; |
||||
|
background-color: rgba(255, 255, 255, 0.3); |
||||
|
} |
||||
|
|
||||
|
&:nth-child(10) { |
||||
|
left: 90%; |
||||
|
width: 160px; |
||||
|
height: 160px; |
||||
|
|
||||
|
animation-delay: 11s; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@-webkit-keyframes square { |
||||
|
0% { |
||||
|
transform: translateY(0); |
||||
|
} |
||||
|
100% { |
||||
|
transform: translateY(-700px) rotate(600deg); |
||||
|
} |
||||
|
} |
||||
|
@keyframes square { |
||||
|
0% { |
||||
|
transform: translateY(0); |
||||
|
} |
||||
|
100% { |
||||
|
transform: translateY(-700px) rotate(600deg); |
||||
|
} |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
import React from "react" |
||||
|
import style from "./Bubbles.module.scss" |
||||
|
export default function () { |
||||
|
return ( |
||||
|
<ul className={style["bg-bubbles"]}> |
||||
|
<li></li> |
||||
|
<li></li> |
||||
|
<li></li> |
||||
|
<li></li> |
||||
|
<li></li> |
||||
|
<li></li> |
||||
|
<li></li> |
||||
|
<li></li> |
||||
|
<li></li> |
||||
|
<li></li> |
||||
|
</ul> |
||||
|
) |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
.ring { |
||||
|
position: relative; |
||||
|
width: 45px; |
||||
|
height: 45px; |
||||
|
margin: 0 auto; |
||||
|
border: 4px solid #4b9cdb; |
||||
|
border-radius: 100%; |
||||
|
} |
||||
|
|
||||
|
.ball { |
||||
|
position: absolute; |
||||
|
top: -11px; |
||||
|
left: 0; |
||||
|
width: 16px; |
||||
|
height: 16px; |
||||
|
border-radius: 100%; |
||||
|
background: #4282b3; |
||||
|
} |
||||
|
|
||||
|
.loading .holder { |
||||
|
position: absolute; |
||||
|
width: 12px; |
||||
|
height: 45px; |
||||
|
left: 17px; |
||||
|
top: 0px; |
||||
|
animation: loadingE 1.3s linear infinite; |
||||
|
} |
||||
|
|
||||
|
@keyframes loadingE { |
||||
|
0% { |
||||
|
transform: rotate(0deg); |
||||
|
} |
||||
|
100% { |
||||
|
transform: rotate(360deg); |
||||
|
} |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
import React from "react" |
||||
|
import style from "./Rainbow.module.scss" |
||||
|
|
||||
|
console.log(style) |
||||
|
|
||||
|
export default () => ( |
||||
|
<div className={style.loading}> |
||||
|
<div className={style.ring}> |
||||
|
<div className={style.holder}> |
||||
|
<div className={style.ball}></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
@ -0,0 +1,16 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8" /> |
||||
|
<link rel="icon" href="/favicon.ico" /> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
||||
|
<!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self' https: 'unsafe-inline'; style-src * 'unsafe-inline'; font-src * data:;"> --> |
||||
|
<!-- <meta http-equiv="X-Content-Security-Policy" content="default-src 'self' https: 'unsafe-inline'; style-src * 'unsafe-inline'; font-src * data:;"> --> |
||||
|
<!-- --> |
||||
|
<title><%- title %></title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div id="root"></div> |
||||
|
<script type="module" src="/main.tsx"></script> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,24 @@ |
|||||
|
import "@/assets/style/common.scss"; |
||||
|
import store from "@/store"; |
||||
|
import React from "react"; |
||||
|
import ReactDOM from "react-dom"; |
||||
|
import "@/plugins/i18n" |
||||
|
import {Provider} from 'react-redux' |
||||
|
|
||||
|
import electron from "@/plugins/electron" |
||||
|
|
||||
|
import Router from "./AppRouter"; |
||||
|
|
||||
|
// 静态资源地址变量请用"__static",在html中使用请用__static
|
||||
|
console.log(electron); |
||||
|
console.log(__staticVar); |
||||
|
|
||||
|
|
||||
|
ReactDOM.render( |
||||
|
<React.StrictMode> |
||||
|
<Provider store={store}> |
||||
|
<Router></Router> |
||||
|
</Provider> |
||||
|
</React.StrictMode>, |
||||
|
document.getElementById("root") |
||||
|
); |
@ -0,0 +1,3 @@ |
|||||
|
const electron = require("electron") // 只能用require
|
||||
|
|
||||
|
export default electron |
@ -0,0 +1,28 @@ |
|||||
|
import LanguageDetector from "i18next-browser-languagedetector" |
||||
|
import i18n from "i18next" |
||||
|
import enUsTrans from "@/assets/locales/en-us.json" |
||||
|
import zhCnTrans from "@/assets/locales/zh-cn.json" |
||||
|
import { initReactI18next } from "react-i18next" |
||||
|
|
||||
|
i18n |
||||
|
.use(LanguageDetector) //嗅探当前浏览器语言
|
||||
|
.use(initReactI18next) //init i18next
|
||||
|
.init({ |
||||
|
//引入资源文件
|
||||
|
resources: { |
||||
|
en: { |
||||
|
translation: enUsTrans, |
||||
|
}, |
||||
|
zh: { |
||||
|
translation: zhCnTrans, |
||||
|
}, |
||||
|
}, |
||||
|
//选择默认语言,选择内容为上述配置中的key,即en/zh
|
||||
|
fallbackLng: "zh", |
||||
|
debug: false, |
||||
|
interpolation: { |
||||
|
escapeValue: false, // not needed for react as it escapes by default
|
||||
|
}, |
||||
|
}) |
||||
|
|
||||
|
export default i18n |
@ -0,0 +1,27 @@ |
|||||
|
import React, { Component } from "react" |
||||
|
|
||||
|
export const pageList: any[] = [] |
||||
|
|
||||
|
export default function (options: any) { |
||||
|
return (WrappedComponent: any) => { |
||||
|
const { |
||||
|
path, // 页面路径
|
||||
|
} = options |
||||
|
|
||||
|
class HOC extends React.Component { |
||||
|
constructor(props: any) { |
||||
|
super(props) |
||||
|
} |
||||
|
componentDidMount() {} |
||||
|
|
||||
|
componentWillUnmount() {} |
||||
|
|
||||
|
render() { |
||||
|
return <WrappedComponent {...options} {...this.props} /> |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
pageList.push([{ ...options, Component: HOC }]) |
||||
|
return HOC |
||||
|
} |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
export interface IOption { |
||||
|
page: string; // 页面路径
|
||||
|
title: string; // 页面标题
|
||||
|
} |
@ -0,0 +1,78 @@ |
|||||
|
import Page404 from "@/views/Auth/Page404" |
||||
|
import Login from "@/views/Login" |
||||
|
import React, { lazy } from "react" |
||||
|
|
||||
|
export const Loading = (props: any) => <div style={{ color: props.color }}>Lodeing.22..</div> |
||||
|
|
||||
|
let delay = |
||||
|
(Comp: any, duration = 1000) => |
||||
|
() => |
||||
|
new Promise<any>(resolve => |
||||
|
Comp() |
||||
|
.then((res: any) => { |
||||
|
setTimeout(() => { |
||||
|
resolve(res) |
||||
|
}, duration) |
||||
|
}) |
||||
|
.catch(() => { |
||||
|
resolve({ |
||||
|
default: () => <div>Error</div>, |
||||
|
}) |
||||
|
}) |
||||
|
) |
||||
|
|
||||
|
export default [ |
||||
|
{ |
||||
|
path: "/about", |
||||
|
component: lazy(() => import("@/views/About")), |
||||
|
exact: false, |
||||
|
meta: { |
||||
|
auth: false, |
||||
|
}, |
||||
|
loading: () => <Loading color="red"></Loading>, |
||||
|
children: [ |
||||
|
{ |
||||
|
path: "/about/test", |
||||
|
exact: true, |
||||
|
meta: { |
||||
|
auth: false, |
||||
|
}, |
||||
|
loading: () => <Loading color="green"></Loading>, |
||||
|
component: lazy(delay(() => import("@/views/Login/Test"))), |
||||
|
}, |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
path: "/float", |
||||
|
component: lazy(() => import("@/views/Float")), |
||||
|
exact: true, |
||||
|
meta: { |
||||
|
auth: false, |
||||
|
}, |
||||
|
children: [], |
||||
|
}, |
||||
|
{ |
||||
|
path: "/home", |
||||
|
component: lazy(() => import("@/views/Home")), |
||||
|
exact: true, |
||||
|
loading: () => <Loading color="blue"></Loading>, |
||||
|
meta: { |
||||
|
auth: false, |
||||
|
}, |
||||
|
children: [], |
||||
|
}, |
||||
|
{ |
||||
|
path: "/", |
||||
|
exact: true, |
||||
|
redirect: "/home", |
||||
|
}, |
||||
|
{ |
||||
|
path: "/login", |
||||
|
component: Login //lazy(() => import("@/views/Login")),
|
||||
|
}, |
||||
|
{ |
||||
|
path: "*", |
||||
|
exact: false, |
||||
|
component: Page404, |
||||
|
}, |
||||
|
] |
@ -0,0 +1 @@ |
|||||
|
export * from "./todo" |
@ -0,0 +1,17 @@ |
|||||
|
import {ADD, REMOVE, ADD_ASYNC} from "@/store/constant/todo" |
||||
|
|
||||
|
let nextTodoId = 0 |
||||
|
|
||||
|
type Action = (param: any) => IAction |
||||
|
|
||||
|
export const addTodo: Action = text => ({ |
||||
|
type: ADD_ASYNC, |
||||
|
id: nextTodoId++, |
||||
|
text |
||||
|
}) |
||||
|
|
||||
|
export const removeTodo: Action = id => ({ |
||||
|
type: REMOVE, |
||||
|
id: id |
||||
|
}) |
||||
|
|
@ -0,0 +1,4 @@ |
|||||
|
export const ADD = "ADD_TODO" |
||||
|
export const ADD_ASYNC = "ADD_ASYNC" |
||||
|
|
||||
|
export const REMOVE = "REMOVE_TODO" |
@ -0,0 +1,13 @@ |
|||||
|
import {applyMiddleware, combineReducers, createStore} from 'redux' |
||||
|
import reducer from "./reducer" |
||||
|
import createSagaMiddleware from 'redux-saga' |
||||
|
import mySaga from './saga' |
||||
|
|
||||
|
const sagaMiddleware = createSagaMiddleware() |
||||
|
|
||||
|
const reduces = combineReducers(reducer) |
||||
|
const store = createStore(reduces, applyMiddleware(sagaMiddleware)) |
||||
|
|
||||
|
sagaMiddleware.run(mySaga) |
||||
|
|
||||
|
export default store |
@ -0,0 +1,5 @@ |
|||||
|
import todo from "./todo" |
||||
|
|
||||
|
export default { |
||||
|
todo |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
import {ADD, REMOVE} from "@/store/constant/todo" |
||||
|
import {combineReducers} from "redux"; |
||||
|
|
||||
|
let initData: ITodo[] = [] |
||||
|
|
||||
|
const todos = (state = initData, action: IAction) => { |
||||
|
switch (action.type) { |
||||
|
case ADD: |
||||
|
return [ |
||||
|
...state, |
||||
|
{ |
||||
|
id: action.id, |
||||
|
text: action.text |
||||
|
} |
||||
|
] |
||||
|
case REMOVE: |
||||
|
const list = state.filter(v => v.id != action.id) |
||||
|
return [ |
||||
|
...list |
||||
|
] |
||||
|
default: |
||||
|
return state |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default todos; |
@ -0,0 +1,52 @@ |
|||||
|
import { call, put, takeEvery, takeLatest } from "redux-saga/effects" |
||||
|
import { ADD, REMOVE, ADD_ASYNC } from "@/store/constant/todo" |
||||
|
import axios, { Method } from "axios" |
||||
|
|
||||
|
const instance = axios.create({ |
||||
|
timeout: 3000, |
||||
|
}) |
||||
|
const http = (method: Method = "GET", url: string) => { |
||||
|
return instance({ |
||||
|
method, |
||||
|
url, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
let Api: any = { |
||||
|
fetchUser: async (id: any) => { |
||||
|
return await http("GET", "https://gank.io/api/v2/banners") |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
// worker Saga : 将在 USER_FETCH_REQUESTED action 被 dispatch 时调用
|
||||
|
function* fetchUser(action: any) { |
||||
|
try { |
||||
|
// @ts-ignore
|
||||
|
const user = yield call(Api.fetchUser, action?.id) |
||||
|
console.log(user) |
||||
|
yield put({ type: ADD, id: action.id, text: user.data.data[0].image }) |
||||
|
} catch (e) { |
||||
|
yield put({ type: "USER_FETCH_FAILED", message: e.message }) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
在每个 `USER_FETCH_REQUESTED` action 被 dispatch 时调用 fetchUser |
||||
|
允许并发(译注:即同时处理多个相同的 action) |
||||
|
*/ |
||||
|
function* mySaga() { |
||||
|
yield takeEvery(ADD_ASYNC, fetchUser) |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
也可以使用 takeLatest |
||||
|
|
||||
|
不允许并发,dispatch 一个 `USER_FETCH_REQUESTED` action 时, |
||||
|
如果在这之前已经有一个 `USER_FETCH_REQUESTED` action 在处理中, |
||||
|
那么处理中的 action 会被取消,只会执行当前的 |
||||
|
*/ |
||||
|
// function* mySaga() {
|
||||
|
// yield takeLatest("USER_FETCH_REQUESTED", fetchUser)
|
||||
|
// }
|
||||
|
|
||||
|
export default mySaga |
@ -0,0 +1,4 @@ |
|||||
|
interface ITodo { |
||||
|
id: number; |
||||
|
text: string; |
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
import React, { Component, ReactElement } from "react" |
||||
|
// 高阶组件,就是对原来的组件进行封装
|
||||
|
import { Route, Redirect, NavLink } from "react-router-dom" |
||||
|
|
||||
|
interface IProps { |
||||
|
needAuth?: boolean |
||||
|
path: string |
||||
|
exact?: boolean |
||||
|
} |
||||
|
|
||||
|
class Auth extends Component<IProps> { |
||||
|
constructor(props: IProps) { |
||||
|
super(props) |
||||
|
} |
||||
|
|
||||
|
render() { |
||||
|
const { path, needAuth = false, exact = false } = this.props // 结构语法的概
|
||||
|
if (!needAuth) { |
||||
|
return ( |
||||
|
// 如果已经登陆,就直接显示原来的组件
|
||||
|
<Route exact={exact} path={path}> |
||||
|
{this.props.children} |
||||
|
</Route> |
||||
|
) |
||||
|
} else { |
||||
|
return ( |
||||
|
<div> |
||||
|
<NavLink to="/login">你还有没有登陆好吧!</NavLink> |
||||
|
|
||||
|
{/* <Redirect to={{ pathname: '/login', state: { from: { path }} }} > </Redirect> */} |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export default Auth |
@ -0,0 +1,27 @@ |
|||||
|
import React from "react" |
||||
|
import { useLocation, Route, Switch, useHistory } from "react-router-dom" |
||||
|
|
||||
|
function Test() { |
||||
|
return <div>test</div> |
||||
|
} |
||||
|
|
||||
|
export default function About(props: any) { |
||||
|
let history = useHistory() |
||||
|
// let location = useLocation()
|
||||
|
function back(){ |
||||
|
console.log(1231); |
||||
|
|
||||
|
history.goBack() |
||||
|
} |
||||
|
return ( |
||||
|
<div className=""> |
||||
|
<div onClick={() =>back()}>阿萨 阿松大asdasd</div> |
||||
|
<div>22{props.children}{location.href}</div> |
||||
|
<p>sadsa</p> |
||||
|
<p>sadsa</p> |
||||
|
<Route path="/about/aa" exact={true}> |
||||
|
<Test></Test> |
||||
|
</Route> |
||||
|
</div> |
||||
|
) |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
import React from "react" |
||||
|
import { NavLink } from "react-router-dom" |
||||
|
|
||||
|
export default function Page404() { |
||||
|
return ( |
||||
|
<div> |
||||
|
<NavLink to="/">阿松大asddadsa</NavLink> |
||||
|
</div> |
||||
|
) |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
.view-float { |
||||
|
width: 100px; |
||||
|
height: 25px; |
||||
|
line-height: 25px; |
||||
|
background-color: #fff; |
||||
|
position: fixed; |
||||
|
overflow: auto; |
||||
|
user-select: none; |
||||
|
border: 1px solid red; |
||||
|
} |
@ -0,0 +1,43 @@ |
|||||
|
import React, { MouseEventHandler, useEffect } from "react" |
||||
|
import electron from "@/plugins/electron" |
||||
|
import style from "./index.module.scss" |
||||
|
import { useLocation, Route, Switch, useHistory } from "react-router-dom" |
||||
|
|
||||
|
export default function Float(props: any) { |
||||
|
useEffect(() => { |
||||
|
// let win = electron.remote.getCurrentWindow()
|
||||
|
let biasX = 0 |
||||
|
let biasY = 0 |
||||
|
document.addEventListener("mousedown", function (e) { |
||||
|
switch (e.button) { |
||||
|
case 0: |
||||
|
biasX = e.x |
||||
|
biasY = e.y |
||||
|
document.addEventListener("mousemove", moveEvent) |
||||
|
break |
||||
|
case 2: |
||||
|
electron.ipcRenderer.send("createSuspensionMenu") |
||||
|
break |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
document.addEventListener("mouseup", function () { |
||||
|
biasX = 0 |
||||
|
biasY = 0 |
||||
|
document.removeEventListener("mousemove", moveEvent) |
||||
|
}) |
||||
|
|
||||
|
function moveEvent(e: any) { |
||||
|
electron.ipcRenderer.send('@float:setPosition', e.screenX - biasX, e.screenY - biasY) |
||||
|
// win.setPosition(e.screenX - biasX, e.screenY - biasY)
|
||||
|
} |
||||
|
}, []) |
||||
|
function dblclick(e: MouseEventHandler<HTMLDivElement>) { |
||||
|
alert("asd") |
||||
|
} |
||||
|
return ( |
||||
|
<div className={style["view-float"]} onDoubleClick={(e: any) => dblclick(e)}> |
||||
|
悬浮窗 |
||||
|
</div> |
||||
|
) |
||||
|
} |
@ -0,0 +1,49 @@ |
|||||
|
import { addTodo, removeTodo } from "@/store/action/todo" |
||||
|
import React, { FormEvent, useRef, useContext } from "react" |
||||
|
import { connect } from "react-redux" |
||||
|
import { NavLink } from "react-router-dom" |
||||
|
|
||||
|
export interface HomeProps { |
||||
|
add(text: string): void |
||||
|
todo: ITodo[] |
||||
|
remove(id: number): void |
||||
|
} |
||||
|
|
||||
|
function Home(props: HomeProps) { |
||||
|
const { todo, add, remove } = props |
||||
|
const inputRef = useRef<HTMLInputElement>(null) |
||||
|
|
||||
|
function addOne(e: FormEvent) { |
||||
|
e.preventDefault() |
||||
|
let text = inputRef.current!.value |
||||
|
if (text) { |
||||
|
inputRef.current!.value = "" |
||||
|
add(text) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<div> |
||||
|
<NavLink to="/home">首页</NavLink> <br /> |
||||
|
<NavLink to="/about">关于</NavLink><br /> |
||||
|
<NavLink to="/login">登录</NavLink><br /> |
||||
|
<NavLink to="/float">Float</NavLink><br /> |
||||
|
<div> |
||||
|
<img src='__static/icon.png' style={{width:"50px",height: "50px"}} alt=""/> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
const mapStateToProps = (state: any) => { |
||||
|
return { |
||||
|
todo: state.todo, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const mapDispatchToProps = (dispatch: any) => ({ |
||||
|
add: (text: string) => dispatch(addTodo(text)), |
||||
|
remove: (id: string | number) => dispatch(removeTodo(id)), |
||||
|
}) |
||||
|
|
||||
|
export default connect(mapStateToProps, mapDispatchToProps)(Home) |
@ -0,0 +1,16 @@ |
|||||
|
import React, { ReactElement } from "react" |
||||
|
import { useLocation } from "react-router-dom" |
||||
|
|
||||
|
// interface IProps {
|
||||
|
// render(): ReactElement
|
||||
|
// }
|
||||
|
|
||||
|
export default function (props: any) { |
||||
|
return ( |
||||
|
<div> |
||||
|
<div className="content"> |
||||
|
<props.render></props.render> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
import React from "react"; |
||||
|
|
||||
|
export default function(){ |
||||
|
return ( |
||||
|
<div> |
||||
|
saadsadsaddddddddddddddddd |
||||
|
</div> |
||||
|
) |
||||
|
} |
@ -0,0 +1,107 @@ |
|||||
|
@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,300); |
||||
|
|
||||
|
$color: #53e3a6; |
||||
|
.login.page { |
||||
|
font-family: "Source Sans Pro", sans-serif; |
||||
|
color: white; |
||||
|
font-weight: 300; |
||||
|
.wrapper { |
||||
|
background: #50a3a2; |
||||
|
background: -webkit-linear-gradient(top left, #50a3a2 0%, #53e3a6 100%); |
||||
|
background: -moz-linear-gradient(top left, #50a3a2 0%, #53e3a6 100%); |
||||
|
background: -o-linear-gradient(top left, #50a3a2 0%, #53e3a6 100%); |
||||
|
background: linear-gradient(to bottom right, #50a3a2 0%, #53e3a6 100%); |
||||
|
|
||||
|
position: absolute; |
||||
|
top: 50%; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 400px; |
||||
|
margin-top: -200px; |
||||
|
overflow: hidden; |
||||
|
|
||||
|
&.form-success { |
||||
|
form { |
||||
|
opacity: 0; |
||||
|
pointer-events: none; |
||||
|
} |
||||
|
.container { |
||||
|
h1 { |
||||
|
transform: translateY(85px); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.container { |
||||
|
max-width: 600px; |
||||
|
margin: 0 auto; |
||||
|
padding: 80px 0; |
||||
|
height: 400px; |
||||
|
text-align: center; |
||||
|
|
||||
|
h1 { |
||||
|
font-size: 40px; |
||||
|
transition-duration: 1s; |
||||
|
transition-timing-function: ease-in-put; |
||||
|
font-weight: 200; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
form { |
||||
|
padding: 20px 0; |
||||
|
position: relative; |
||||
|
z-index: 2; |
||||
|
transition: opacity 0.5s linear; |
||||
|
|
||||
|
input { |
||||
|
display: block; |
||||
|
appearance: none; |
||||
|
outline: 0; |
||||
|
border: 1px solid rgba(255, 255, 255, 0.4); |
||||
|
background-color: rgba(255, 255, 255, 0.2); |
||||
|
width: 250px; |
||||
|
|
||||
|
border-radius: 3px; |
||||
|
padding: 10px 15px; |
||||
|
margin: 0 auto 10px auto; |
||||
|
display: block; |
||||
|
text-align: center; |
||||
|
font-size: 18px; |
||||
|
|
||||
|
color: white; |
||||
|
|
||||
|
transition-duration: 0.25s; |
||||
|
font-weight: 300; |
||||
|
|
||||
|
&:hover { |
||||
|
background-color: rgba(255, 255, 255, 0.4); |
||||
|
} |
||||
|
|
||||
|
&:focus { |
||||
|
background-color: white; |
||||
|
width: 300px; |
||||
|
|
||||
|
color: $color; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
button { |
||||
|
appearance: none; |
||||
|
outline: 0; |
||||
|
background-color: white; |
||||
|
border: 0; |
||||
|
padding: 10px 15px; |
||||
|
color: $color; |
||||
|
border-radius: 3px; |
||||
|
width: 250px; |
||||
|
cursor: pointer; |
||||
|
font-size: 18px; |
||||
|
transition-duration: 0.25s; |
||||
|
|
||||
|
&:hover { |
||||
|
background-color: rgb(245, 247, 249); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,53 @@ |
|||||
|
import React, { useRef } from "react" |
||||
|
import cn from "classnames" |
||||
|
import style from "./index.module.scss" |
||||
|
import { useLocation, Route, Switch } from "react-router-dom" |
||||
|
import { useState } from "react" |
||||
|
import Bubbles from "@/components/Loading/Bubbles" |
||||
|
import { Trans, Translation, useTranslation } from "react-i18next" |
||||
|
|
||||
|
export default function Login(props: any) { |
||||
|
const [isSuccess, setIsSuccess] = useState(false) |
||||
|
const [usename, setUsername] = useState("23") |
||||
|
const [password, setPassword] = useState("") |
||||
|
const { t ,i18n} = useTranslation() |
||||
|
|
||||
|
function clickLogin(event: any) { |
||||
|
event.preventDefault() |
||||
|
|
||||
|
setIsSuccess(true) |
||||
|
setTimeout(() => { |
||||
|
setUsername('') |
||||
|
setPassword('') |
||||
|
setIsSuccess(false) |
||||
|
}, 2000) |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<div className={cn(style.login, style.page)}> |
||||
|
|
||||
|
<div style={{background:"red"}}> |
||||
|
<button onClick={()=>i18n.changeLanguage(i18n.language=='en'?'zh':'en')}>{i18n.language=='en'?'zh':'en'}</button> |
||||
|
<h1>{t('home')}阿松大</h1> |
||||
|
<h1>{usename}</h1> |
||||
|
<h1>{password}</h1> |
||||
|
<h2><Trans>home</Trans></h2> |
||||
|
<Translation>{t => <h3>{t('home')}</h3>}</Translation> |
||||
|
</div> |
||||
|
|
||||
|
<div className={cn(style.wrapper, isSuccess ? style["form-success"] : "")}> |
||||
|
<div className={cn(style.container)}> |
||||
|
<h1><Trans>login.title</Trans></h1> |
||||
|
<form className={cn(style.form)}> |
||||
|
<input type="text" value={usename} onChange={(e)=>setUsername(e.target.value)} placeholder={t('login.username_placeholder')} /> |
||||
|
<input type="password" value={password} onChange={(e)=>setPassword(e.target.value)} placeholder={t('login.password_placeholder')} autoComplete="" /> |
||||
|
<button type="submit" id={cn(style["login-login"])} onClick={clickLogin}> |
||||
|
<Trans>login.btnText</Trans> |
||||
|
</button> |
||||
|
</form> |
||||
|
</div> |
||||
|
<Bubbles></Bubbles> |
||||
|
</div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
@ -0,0 +1 @@ |
|||||
|
/// <reference types="vite/client" />
|
@ -0,0 +1,18 @@ |
|||||
|
const { say } = require("cfonts") |
||||
|
const chalk = require("chalk") |
||||
|
|
||||
|
|
||||
|
function electronLog(data, color) { |
||||
|
let log = "" |
||||
|
data = data.toString().split(/\r?\n/) |
||||
|
data.forEach(line => { |
||||
|
log += ` ${line}\n` |
||||
|
}) |
||||
|
if (/[0-9A-z]+/.test(log)) { |
||||
|
console.log( |
||||
|
chalk[color].bold("┏ Electron -------------------") + "\n\n" + log + chalk[color].bold("┗ ----------------------------") + "\n" |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
electronLog("dfssssssssssssssssssssssssssssssssssssssssssssssss","red") |
@ -0,0 +1,35 @@ |
|||||
|
|
||||
|
{ |
||||
|
"compilerOptions": { |
||||
|
"target": "ESNext", |
||||
|
"lib": ["DOM", "DOM.Iterable", "ESNext"], |
||||
|
"allowJs": false, |
||||
|
"skipLibCheck": false, |
||||
|
"esModuleInterop": false, |
||||
|
"allowSyntheticDefaultImports": true, |
||||
|
"strict": true, |
||||
|
"forceConsistentCasingInFileNames": true, |
||||
|
"module": "ESNext", |
||||
|
"moduleResolution": "node", |
||||
|
"resolveJsonModule": true, |
||||
|
"isolatedModules": true, |
||||
|
"noEmit": false, |
||||
|
"jsx": "react", |
||||
|
"rootDir": ".", |
||||
|
"baseUrl": ".", |
||||
|
"paths": { |
||||
|
"@/*": ["src/render/*"], |
||||
|
"@render/*": ["src/render/*"], |
||||
|
"@main/*": ["src/main/*"], |
||||
|
"@src/*": ["src/*"], |
||||
|
"@root/*": ["./*"] |
||||
|
}, |
||||
|
}, |
||||
|
"include": ["src", "types"], |
||||
|
"exclude": ["node_modules"], |
||||
|
"ts-node": { |
||||
|
"compilerOptions": { |
||||
|
"module": "CommonJS" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
|
||||
|
interface IAny{ |
||||
|
[props: string]: any; |
||||
|
} |
||||
|
|
||||
|
interface IAction extends IAny{ |
||||
|
type: string |
||||
|
} |
||||
|
|
||||
|
|
||||
|
declare const __static: string; |
||||
|
declare const __staticVar: string; |
@ -0,0 +1,65 @@ |
|||||
|
import { defineConfig } from "vite" |
||||
|
import reactRefresh from "@vitejs/plugin-react-refresh" |
||||
|
// import WindiCSS from "vite-plugin-windicss"
|
||||
|
const { resolve, join } = require("path") |
||||
|
import electron from "vitejs-plugin-electron" |
||||
|
import { minifyHtml, injectHtml } from "vite-plugin-html" |
||||
|
import replace from '@rollup/plugin-replace'; |
||||
|
|
||||
|
|
||||
|
let isDev = process.env.NODE_ENV === "development" |
||||
|
|
||||
|
let plugins = [] |
||||
|
let staticPath = isDev? '/static': 'static' |
||||
|
plugins.push( |
||||
|
replace({ |
||||
|
preventAssignment: true, |
||||
|
"__static": staticPath, |
||||
|
"__staticVar": `"${staticPath}"` |
||||
|
}) |
||||
|
) |
||||
|
|
||||
|
// https://vitejs.dev/config/
|
||||
|
export default defineConfig({ |
||||
|
root: resolve(__dirname, "src/render"), |
||||
|
base: "./", |
||||
|
publicDir: resolve(__dirname, "resource/electron"), |
||||
|
css: { |
||||
|
preprocessorOptions: { |
||||
|
scss: { |
||||
|
additionalData: `@import "@/assets/style/global.scss";`, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
server: { |
||||
|
port: +process.env.PORT, |
||||
|
}, |
||||
|
build: { |
||||
|
outDir: resolve(__dirname, "dist/electron"), |
||||
|
emptyOutDir: true, |
||||
|
minify: false, |
||||
|
commonjsOptions: {}, |
||||
|
assetsDir: "", // 相对路径 加载问题
|
||||
|
sourcemap: true, |
||||
|
}, |
||||
|
resolve: { |
||||
|
alias: { |
||||
|
"@": join(__dirname, "src/render"), |
||||
|
"@render": join(__dirname, "src/render"), |
||||
|
"@main": join(__dirname, "src/main"), |
||||
|
"@src": join(__dirname, "src"), |
||||
|
"@root": __dirname, |
||||
|
}, |
||||
|
}, |
||||
|
//
|
||||
|
plugins: [ |
||||
|
...plugins, |
||||
|
reactRefresh(), |
||||
|
minifyHtml(), |
||||
|
injectHtml({ |
||||
|
injectData: { |
||||
|
title: "代码片段管理工具" |
||||
|
}, |
||||
|
}), |
||||
|
], |
||||
|
}) |
@ -0,0 +1,34 @@ |
|||||
|
### 渲染层 |
||||
|
|
||||
|
* 必须使用hash模式,方便 |
||||
|
|
||||
|
全局替换字符 |
||||
|
|
||||
|
* `__static`: 静态资源字符串,注意没有用引号包裹,不能直接赋值 |
||||
|
* `__staticVar`: 静态资源字符串变量,可以赋值 |
||||
|
|
||||
|
|
||||
|
* 静态资源目录为根目录:`resource/electron`, 里面的东西会原封不动的复制到`dist/electron`中 |
||||
|
|
||||
|
预加载资源 |
||||
|
* 预加载的脚本会自动从`src/preload`直接复制到`dist/src/preload`; |
||||
|
|
||||
|
### mian层 |
||||
|
|
||||
|
全局变量 |
||||
|
|
||||
|
* `__static`: `resource/electron/static`的资源目录字符串变量,生产环境则是在`entry.js`根据此文件定位的`./static`中 |
||||
|
|
||||
|
|
||||
|
## 开发环境变量 |
||||
|
* `process.env.PORT` 开发环境时启动服务的端口,可以修改 |
||||
|
* `process.env.NODE_ENV` 区分是开发环境 |
||||
|
|
||||
|
## 生产环境 |
||||
|
* `process.env.PORT` 不需要 |
||||
|
* `process.env.NODE_ENV` 区分是生产环境 |
||||
|
|
||||
|
|
||||
|
### TODO |
||||
|
|
||||
|
- [ ] 完善终端的信息输出 |
Loading…
Reference in new issue