commit
7f88caf415
58 changed files with 9161 additions and 0 deletions
@ -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 |
@ -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,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,95 @@ |
|||||
|
{ |
||||
|
"name": "my-electron-app", |
||||
|
"version": "1.0.0", |
||||
|
"description": "description", |
||||
|
"main": "dist/electron/entry.js", |
||||
|
"scripts": { |
||||
|
"deva": "electron-forge start --inspect-electron --app-path dist/electron/entry.js", |
||||
|
"dev": "npm run dev:all", |
||||
|
"dev:all": "concurrently -n=vue,ele -c=green,blue \"npm run dev:vue\" \"npm run dev:ele\"", |
||||
|
"dev:vue": "vite", |
||||
|
"dev:ele": "node -r ts-node/register script/build-main --env=development --watch", |
||||
|
"dev:test": "electron-forge start --inspect-electron", |
||||
|
"package": "electron-forge package", |
||||
|
"make": "electron-forge make", |
||||
|
"build": "tsc && vite build", |
||||
|
"serve": "vite preview" |
||||
|
}, |
||||
|
"keywords": [], |
||||
|
"author": "TopOne", |
||||
|
"license": "ISC", |
||||
|
"devDependencies": { |
||||
|
"@electron-forge/cli": "^6.0.0-beta.58", |
||||
|
"@electron-forge/maker-deb": "^6.0.0-beta.58", |
||||
|
"@electron-forge/maker-rpm": "^6.0.0-beta.58", |
||||
|
"@electron-forge/maker-squirrel": "^6.0.0-beta.58", |
||||
|
"@electron-forge/maker-zip": "^6.0.0-beta.58", |
||||
|
"@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-typescript": "^8.2.3", |
||||
|
"@types/minimist": "^1.2.2", |
||||
|
"cfonts": "^2.9.3", |
||||
|
"chalk": "^4.1.1", |
||||
|
"concurrently": "^6.2.0", |
||||
|
"dotenv": "^10.0.0", |
||||
|
"electron": "^13.1.7", |
||||
|
"execa": "^5.1.1", |
||||
|
"ts-node": "^10.1.0", |
||||
|
"vitejs-plugin-electron": "^0.1.3" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"electron-squirrel-startup": "^1.0.0", |
||||
|
"axios": "^0.21.1", |
||||
|
"classnames": "^2.3.1", |
||||
|
"i18next": "^20.3.4", |
||||
|
"i18next-browser-languagedetector": "^6.1.2", |
||||
|
"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-saga": "^1.1.3", |
||||
|
"styled-jsx": "^3.4.4", |
||||
|
"@types/node": "^15.12.5", |
||||
|
"@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", |
||||
|
"redux-devtools": "^3.7.0", |
||||
|
"sass": "^1.35.1", |
||||
|
"typescript": "^4.3.2", |
||||
|
"vite": "^2.3.8", |
||||
|
"vite-plugin-windicss": "^1.2.0", |
||||
|
"windicss": "^3.1.3" |
||||
|
}, |
||||
|
"config": { |
||||
|
"forge": { |
||||
|
"packagerConfig": {}, |
||||
|
"makers": [ |
||||
|
{ |
||||
|
"name": "@electron-forge/maker-squirrel", |
||||
|
"config": { |
||||
|
"name": "my_electron_app" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"name": "@electron-forge/maker-zip", |
||||
|
"platforms": [ |
||||
|
"darwin" |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
"name": "@electron-forge/maker-deb", |
||||
|
"config": {} |
||||
|
}, |
||||
|
{ |
||||
|
"name": "@electron-forge/maker-rpm", |
||||
|
"config": {} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,70 @@ |
|||||
|
/** |
||||
|
* electron 打包 |
||||
|
*/ |
||||
|
|
||||
|
import { join } from "path" |
||||
|
import { exec, spawn, ChildProcess } from "child_process" |
||||
|
import { watch, rollup, OutputOptions } from "rollup" |
||||
|
import electron from "electron" |
||||
|
import { waitOn } from "./utils" |
||||
|
import options from "./rollup.config" |
||||
|
import { main } from "../package.json" |
||||
|
const ora = require("ora") |
||||
|
const dotenv = require("dotenv") |
||||
|
const chalk = require("chalk") |
||||
|
const minimist = require("minimist") |
||||
|
|
||||
|
dotenv.config({ path: join(__dirname, "../.env") }) |
||||
|
const argv = minimist(process.argv.slice(2)) |
||||
|
const opts = options(argv.env) |
||||
|
const TAG = "[build-main.ts]" |
||||
|
const spinner = ora(`${TAG} Electron build...`) |
||||
|
|
||||
|
|
||||
|
|
||||
|
if (argv.watch) { |
||||
|
waitOn({ port: process.env.PORT as string }).then(msg => { |
||||
|
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", ev => { |
||||
|
if (ev.code === "END") { |
||||
|
if (child && child.kill) { |
||||
|
console.log(child.pid) |
||||
|
manualRestart = true |
||||
|
// <number>child.pid
|
||||
|
// child.kill()
|
||||
|
process.kill( <number>child.pid) |
||||
|
child = null |
||||
|
setTimeout(() => { |
||||
|
manualRestart = false |
||||
|
}, 5000) |
||||
|
} |
||||
|
child = exec(`electron-forge start --inspect-electron --app-path ${join(__dirname, `../${main}`)}`, { |
||||
|
env: Object.assign(process.env, { NODE_ENV: argv.env }), |
||||
|
}) |
||||
|
child.on("close", () => { |
||||
|
if (!manualRestart) process.exit() |
||||
|
}) |
||||
|
} else if (ev.code === "ERROR") { |
||||
|
console.log(ev.error) |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
} else { |
||||
|
spinner.start() |
||||
|
rollup(opts) |
||||
|
.then(build => { |
||||
|
spinner.stop() |
||||
|
console.log(TAG, chalk.green("Electron build successed.")) |
||||
|
build.write(opts.output as OutputOptions) |
||||
|
}) |
||||
|
.catch(error => { |
||||
|
spinner.stop() |
||||
|
console.log(`\n${TAG} ${chalk.red("构建报错")}\n`, error, "\n") |
||||
|
}) |
||||
|
} |
@ -0,0 +1,158 @@ |
|||||
|
// @ts-nocheck
|
||||
|
const electron = require("electron") |
||||
|
const execa = require("execa") |
||||
|
const vite = require("vite") |
||||
|
const path = require("path") |
||||
|
const fs = require("fs") |
||||
|
const os = require("os") |
||||
|
const esbuild = require("esbuild") |
||||
|
const { say } = require("cfonts") |
||||
|
const { spawn } = require("child_process") |
||||
|
const chalk = require("chalk") |
||||
|
|
||||
|
function getEnvScript () { |
||||
|
let script = `process.env={...process.env};` |
||||
|
script += `process.env.RES_DIR = require("path").join(require("path").dirname(process.execPath),"resources/resource/release")` |
||||
|
return script |
||||
|
} |
||||
|
|
||||
|
async function buildRender() { |
||||
|
const subprocess = execa("vite",['-c','vite.config.ts']).stdout.pipe(process.stdout);; |
||||
|
setTimeout(() => { |
||||
|
subprocess.killed(); |
||||
|
}, 1000); |
||||
|
|
||||
|
try { |
||||
|
await subprocess; |
||||
|
} catch (error) { |
||||
|
console.log(subprocess.killed); // true
|
||||
|
console.log(error.isCanceled); // true
|
||||
|
} |
||||
|
return |
||||
|
let options = { |
||||
|
root: path.join(__dirname, "../src/render"), |
||||
|
enableEsbuild: true, |
||||
|
minify: false, |
||||
|
resolve:{ |
||||
|
alias: [ |
||||
|
{ find: "@", replacement: path.resolve(__dirname, "src/render") }, |
||||
|
{ find: "@render", replacement: path.resolve(__dirname, "src/render") }, |
||||
|
{ find: "@main", replacement: path.resolve(__dirname, "src/main") }, |
||||
|
{ find: "@src", replacement: path.resolve(__dirname, "src/src") }, |
||||
|
{ find: "@root", replacement: __dirname }, |
||||
|
], |
||||
|
}, |
||||
|
optimizeDeps: { |
||||
|
exclude: ["process"], |
||||
|
}, |
||||
|
} |
||||
|
await vite.build(options) |
||||
|
// let htmlPath = path.join(__dirname, "../dist/electron", "index.html")
|
||||
|
// let html = fs.readFileSync(htmlPath, { encoding: "utf8" })
|
||||
|
// html = html.replace("<head>", `<head><script>${getEnvScript()};</script>`)
|
||||
|
// fs.writeFileSync(htmlPath, html)
|
||||
|
} |
||||
|
|
||||
|
function buildMain() { |
||||
|
let outfile = path.join(process.cwd(), "dist/electron/entry.js") |
||||
|
let entryFilePath = path.join(process.cwd(), "src/main/index.js") |
||||
|
esbuild.buildSync({ |
||||
|
entryPoints: [entryFilePath], |
||||
|
outfile, |
||||
|
minify: false, |
||||
|
bundle: true, |
||||
|
platform: "node", |
||||
|
sourcemap: false, |
||||
|
external: ["electron"], |
||||
|
}) |
||||
|
let js = `${getEnvScript()}${os.EOL}${fs.readFileSync(outfile)}` |
||||
|
fs.writeFileSync(outfile, js) |
||||
|
} |
||||
|
|
||||
|
function startElectron() { |
||||
|
var args = ["--inspect=5858", path.join(__dirname, "../dist/electron/main.js")] |
||||
|
|
||||
|
// detect yarn or npm and process commandline args accordingly
|
||||
|
if (process.env.npm_execpath.endsWith("yarn.js")) { |
||||
|
args = args.concat(process.argv.slice(3)) |
||||
|
} else if (process.env.npm_execpath.endsWith("npm-cli.js")) { |
||||
|
args = args.concat(process.argv.slice(2)) |
||||
|
} |
||||
|
|
||||
|
// electronProcess = spawn(electron, args)
|
||||
|
|
||||
|
// electronProcess.stdout.on("data", data => {
|
||||
|
// electronLog(data, "blue")
|
||||
|
// })
|
||||
|
// electronProcess.stderr.on("data", data => {
|
||||
|
// electronLog(data, "red")
|
||||
|
// })
|
||||
|
|
||||
|
// electronProcess.on("close", () => {
|
||||
|
// if (!manualRestart) process.exit()
|
||||
|
// })
|
||||
|
} |
||||
|
|
||||
|
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 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 = '' |
||||
|
|
||||
|
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() |
||||
|
// buildMain()
|
||||
|
buildRender() |
||||
|
} |
||||
|
|
||||
|
init() |
@ -0,0 +1,39 @@ |
|||||
|
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") => { |
||||
|
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,24 @@ |
|||||
|
import { builtinModules } from "module" |
||||
|
import { get } from "http" |
||||
|
import { green } from "chalk" |
||||
|
|
||||
|
/** 轮询监听 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,4 @@ |
|||||
|
/** |
||||
|
* !!! ensure process.cwd() correct |
||||
|
*/ |
||||
|
process.chdir(__dirname.slice(0, __dirname.lastIndexOf('dist'))) |
@ -0,0 +1,36 @@ |
|||||
|
/** |
||||
|
* electron 主文件 |
||||
|
*/ |
||||
|
console.log('2222'); |
||||
|
import '@src/common/patch' |
||||
|
const {join} = require("path") |
||||
|
const {app,BrowserWindow} = require("electron") |
||||
|
const dotenv = require("dotenv") |
||||
|
|
||||
|
dotenv.config({ path: join(__dirname, '../../../.env') }) |
||||
|
|
||||
|
let win |
||||
|
|
||||
|
console.log('1232asd啊2aasdaaaa'); |
||||
|
|
||||
|
function createWin() { |
||||
|
console.log('萨达'); |
||||
|
// 创建浏览器窗口
|
||||
|
win = new BrowserWindow({ |
||||
|
width: 1024, |
||||
|
height: 768, |
||||
|
webPreferences: { |
||||
|
nodeIntegration: true, |
||||
|
contextIsolation: false, |
||||
|
preload: join(__dirname, '../../src/preload/index.js'), |
||||
|
}, |
||||
|
}) |
||||
|
|
||||
|
const URL = app.isPackaged |
||||
|
? `file://${join(__dirname, '../render/index.html')}` // vite 构建后的静态文件地址
|
||||
|
: `http://localhost:${process.env.PORT}/login` // vite 启动的服务器地址
|
||||
|
|
||||
|
win?.loadURL(URL) |
||||
|
} |
||||
|
|
||||
|
app.whenReady().then(createWin) |
@ -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,60 @@ |
|||||
|
import { BrowserRouter 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 () { |
||||
|
console.log(pageList) |
||||
|
|
||||
|
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,13 @@ |
|||||
|
<!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" /> |
||||
|
<title>Vite App</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div id="root"></div> |
||||
|
<script type="module" src="/main.tsx"></script> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,19 @@ |
|||||
|
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 "virtual:windi-devtools"; |
||||
|
import "virtual:windi.css"; |
||||
|
|
||||
|
import Router from "./AppRouter"; |
||||
|
|
||||
|
ReactDOM.render( |
||||
|
<React.StrictMode> |
||||
|
<Provider store={store}> |
||||
|
<Router></Router> |
||||
|
</Provider> |
||||
|
</React.StrictMode>, |
||||
|
document.getElementById("root") |
||||
|
); |
@ -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,69 @@ |
|||||
|
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: "/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,21 @@ |
|||||
|
import React from "react" |
||||
|
import { useLocation, Route, Switch } from "react-router-dom" |
||||
|
|
||||
|
function Test() { |
||||
|
return <div>test</div> |
||||
|
} |
||||
|
|
||||
|
export default function About(props: any) { |
||||
|
let location = useLocation() |
||||
|
console.log(location) |
||||
|
console.log(props) |
||||
|
return ( |
||||
|
<div className="container mx-auto"> |
||||
|
<div>阿萨 阿松大asdasd</div> |
||||
|
<div>22{props.children}</div> |
||||
|
<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,86 @@ |
|||||
|
import React, { useState } from "react" |
||||
|
|
||||
|
export default () => { |
||||
|
const title = "TESAaT" |
||||
|
const [leftMenuList, setLeftMenuList] = useState<any[]>([ |
||||
|
{ title: "首页", path: "/" }, |
||||
|
{ title: "角色", children: [{ title: "月儿", path: "/about" }] }, |
||||
|
]) |
||||
|
|
||||
|
const [rightMenuList, setRightMenuList] = useState<any[]>([ |
||||
|
{ |
||||
|
title: "登录/注册", |
||||
|
click: true, |
||||
|
}, |
||||
|
]) |
||||
|
|
||||
|
function onLeftClick(e: any, menu: any, allMenu: any) { |
||||
|
if (menu.click || !menu.path) { |
||||
|
e.preventDefault() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function onRightClick(e: any, menu: any, allMenu: any) { |
||||
|
if (menu.click || !menu.path) { |
||||
|
e.preventDefault() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ( |
||||
|
<div className=""> |
||||
|
<div className="shadow bg-white h-12 leading-12 fixed top-0 left-0 right-0"> |
||||
|
<div className="container h-full clearfix mx-auto"> |
||||
|
<div className="h-full float-left cursor-pointer text-size-25px flex items-center">{title}</div> |
||||
|
<ul className="h-full float-left ml-10"> |
||||
|
{leftMenuList.map((menu, index) => { |
||||
|
return ( |
||||
|
<li className="h-full float-left group relative" key={index}> |
||||
|
<a |
||||
|
href={menu.path ? menu.path : "#"} |
||||
|
onClick={e => onLeftClick(e, menu, leftMenuList)} |
||||
|
className="h-full px-5 hover:(bg-cool-gray-200 text-black) text-gray-400 text-size-14px flex items-center" |
||||
|
> |
||||
|
{menu.title} |
||||
|
</a> |
||||
|
{menu.children && menu.children.length && ( |
||||
|
<ul className="absolute overflow-hidden transition-all duration-150 max-h-0 group-hover:max-h-500px left-0 top-full shadow"> |
||||
|
{menu.children.map((subMenu: any, jndex: number) => { |
||||
|
return ( |
||||
|
<li key={jndex} className="float-left relative"> |
||||
|
<a |
||||
|
href={subMenu.path ? subMenu.path : "#"} |
||||
|
onClick={e => onLeftClick(e, subMenu, leftMenuList)} |
||||
|
className="h-12 px-5 hover:(bg-cool-gray-200 text-black) text-gray-400 text-size-14px flex items-center" |
||||
|
> |
||||
|
{subMenu.title} |
||||
|
</a> |
||||
|
</li> |
||||
|
) |
||||
|
})} |
||||
|
</ul> |
||||
|
)} |
||||
|
</li> |
||||
|
) |
||||
|
})} |
||||
|
</ul> |
||||
|
<ul className="float-right h-full"> |
||||
|
{rightMenuList.map((menu, index) => { |
||||
|
return ( |
||||
|
<li key={index} className="h-full float-left relative cursor-pointer"> |
||||
|
<a |
||||
|
href={menu.path ? menu.path : "#"} |
||||
|
className="h-full px-5 text-size-14px flex items-center" |
||||
|
onClick={e => onRightClick(e, menu, rightMenuList)} |
||||
|
> |
||||
|
{menu.title} |
||||
|
</a> |
||||
|
</li> |
||||
|
) |
||||
|
})} |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div className="h-12"></div> |
||||
|
</div> |
||||
|
) |
||||
|
} |
@ -0,0 +1,71 @@ |
|||||
|
import { addTodo, removeTodo } from "@/store/action/todo" |
||||
|
import React, { FormEvent, useRef, useContext } from "react" |
||||
|
import { connect } from "react-redux" |
||||
|
import Header from "./Header" |
||||
|
|
||||
|
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> |
||||
|
<Header></Header> |
||||
|
<div> |
||||
|
<div className="bg-white min-h-100vh"> |
||||
|
<div className="container clearfix mx-auto h-500px"> |
||||
|
<form onSubmit={e => addOne(e)}> |
||||
|
<input ref={inputRef} type="text" /> |
||||
|
<button type="submit">添加222aaaasaadd</button> |
||||
|
</form> |
||||
|
{todo.map((v: ITodo) => { |
||||
|
return ( |
||||
|
<p onClick={() => remove(v.id)} key={v.id}> |
||||
|
{v.text} |
||||
|
{v.id} |
||||
|
</p> |
||||
|
) |
||||
|
})} |
||||
|
</div> |
||||
|
</div> |
||||
|
<div className="min-h-100vh"></div> |
||||
|
<div className="bg-white min-h-100vh"> |
||||
|
<div className="container clearfix mx-auto"> |
||||
|
{[...Array(100)] |
||||
|
.map((v, i) => i) |
||||
|
.map(v => { |
||||
|
return <p key={v}>v</p> |
||||
|
})} |
||||
|
</div> |
||||
|
</div> |
||||
|
</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,134 @@ |
|||||
|
@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; |
||||
|
|
||||
|
::-webkit-input-placeholder { |
||||
|
/* WebKit browsers */ |
||||
|
font-family: "Source Sans Pro", sans-serif; |
||||
|
color: white; |
||||
|
font-weight: 300; |
||||
|
} |
||||
|
:-moz-placeholder { |
||||
|
/* Mozilla Firefox 4 to 18 */ |
||||
|
font-family: "Source Sans Pro", sans-serif; |
||||
|
color: white; |
||||
|
opacity: 1; |
||||
|
font-weight: 300; |
||||
|
} |
||||
|
::-moz-placeholder { |
||||
|
/* Mozilla Firefox 19+ */ |
||||
|
font-family: "Source Sans Pro", sans-serif; |
||||
|
color: white; |
||||
|
opacity: 1; |
||||
|
font-weight: 300; |
||||
|
} |
||||
|
:-ms-input-placeholder { |
||||
|
/* Internet Explorer 10+ */ |
||||
|
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,20 @@ |
|||||
|
|
||||
|
// // http://localhost:3344
|
||||
|
// get("http://baidu.com",res=>{
|
||||
|
// console.log(res.statusCode);
|
||||
|
// })
|
||||
|
// import { spawn, ChildProcess } from 'child_process'
|
||||
|
// spawn(
|
||||
|
// electronForge,
|
||||
|
// [
|
||||
|
// "start",
|
||||
|
// "--inspect-electron",
|
||||
|
// "--app-path",
|
||||
|
// join(__dirname, "dist/electron/entry.js"),//join(__dirname, `../${main}`),
|
||||
|
// ] ,
|
||||
|
// {
|
||||
|
// stdio: 'inherit',
|
||||
|
// env: Object.assign(process.env, { NODE_ENV: "development" }),
|
||||
|
// })
|
||||
|
|
||||
|
process.kill(19656) |
@ -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": true, |
||||
|
"jsx": "react", |
||||
|
"rootDir": ".", |
||||
|
"baseUrl": ".", |
||||
|
"paths": { |
||||
|
"@/*": ["src/render/*"], |
||||
|
"@render/*": ["src/render/*"], |
||||
|
"@main/*": ["src/main/*"], |
||||
|
"@src/*": ["src/*"], |
||||
|
"@root/*": ["./*"] |
||||
|
}, |
||||
|
}, |
||||
|
"include": ["src/render", "types"], |
||||
|
"exclude": ["node_modules"], |
||||
|
"ts-node": { |
||||
|
"compilerOptions": { |
||||
|
"module": "CommonJS" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
|
||||
|
interface IAny{ |
||||
|
[props: string]: any; |
||||
|
} |
||||
|
|
||||
|
interface IAction extends IAny{ |
||||
|
type: string |
||||
|
} |
@ -0,0 +1,43 @@ |
|||||
|
|
||||
|
|
||||
|
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" |
||||
|
|
||||
|
|
||||
|
require('dotenv').config({ path: join(__dirname, '.env') }) |
||||
|
|
||||
|
// https://vitejs.dev/config/
|
||||
|
export default defineConfig({ |
||||
|
root: resolve(__dirname, "src/render"), |
||||
|
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: [WindiCSS(), reactRefresh(), electron()], |
||||
|
}) |
@ -0,0 +1,15 @@ |
|||||
|
import { defineConfig } from "vite-plugin-windicss"; |
||||
|
function range(size, startAt = 1) { |
||||
|
return Array.from(Array(size).keys()).map((i) => i + startAt); |
||||
|
} |
||||
|
export default defineConfig({ |
||||
|
preflight: false, |
||||
|
safelist: [ |
||||
|
range(30).map((i) => `p-${i}`), // p-1 to p-3
|
||||
|
range(10).map((i) => `mt-${i}`), // mt-1 to mt-10
|
||||
|
], |
||||
|
extract: { |
||||
|
include: ['src/render/**/*.{vue,html,jsx,tsx}'], |
||||
|
exclude: ['node_modules', '.git'], |
||||
|
}, |
||||
|
}); |
Loading…
Reference in new issue