This commit is contained in:
muwoo 2021-06-02 17:17:09 +08:00
commit ce490acb6a
52 changed files with 16764 additions and 0 deletions

30
.babelrc Normal file
View File

@ -0,0 +1,30 @@
{
"comments": false,
"env": {
"main": {
"presets": [
["env", {
"targets": { "node": 7 }
}],
"stage-0"
]
},
"renderer": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
},
"web": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
}
},
"plugins": ["transform-runtime"]
}

132
.electron-vue/build.js Normal file
View File

@ -0,0 +1,132 @@
'use strict'
process.env.NODE_ENV = 'production'
const { say } = require('cfonts')
const chalk = require('chalk')
const del = require('del')
const { spawn } = require('child_process')
const webpack = require('webpack')
const Multispinner = require('multispinner')
const mainConfig = require('./webpack.main.config')
const rendererConfig = require('./webpack.renderer.config')
const webConfig = require('./webpack.web.config')
const doneLog = chalk.bgGreen.white(' DONE ') + ' '
const errorLog = chalk.bgRed.white(' ERROR ') + ' '
const okayLog = chalk.bgBlue.white(' OKAY ') + ' '
const isCI = process.env.CI || false
if (process.env.BUILD_TARGET === 'clean') clean()
else if (process.env.BUILD_TARGET === 'web') web()
else build()
function clean () {
del.sync(['build/*', '!build/icons', '!build/icons/icon.*'])
console.log(`\n${doneLog}\n`)
process.exit()
}
function build () {
greeting()
del.sync(['dist/electron/*', '!.gitkeep'])
const tasks = ['main', 'renderer']
const m = new Multispinner(tasks, {
preText: 'building',
postText: 'process'
})
let results = ''
m.on('success', () => {
process.stdout.write('\x1B[2J\x1B[0f')
console.log(`\n\n${results}`)
console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`)
process.exit()
})
pack(mainConfig).then(result => {
results += result + '\n\n'
m.success('main')
}).catch(err => {
m.error('main')
console.log(`\n ${errorLog}failed to build main process`)
console.error(`\n${err}\n`)
process.exit(1)
})
pack(rendererConfig).then(result => {
results += result + '\n\n'
m.success('renderer')
}).catch(err => {
m.error('renderer')
console.log(`\n ${errorLog}failed to build renderer process`)
console.error(`\n${err}\n`)
process.exit(1)
})
}
function pack (config) {
return new Promise((resolve, reject) => {
config.mode = 'production'
webpack(config, (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 web () {
del.sync(['dist/web/*', '!.gitkeep'])
webConfig.mode = 'production'
webpack(webConfig, (err, stats) => {
if (err || stats.hasErrors()) console.log(err)
console.log(stats.toString({
chunks: false,
colors: true
}))
process.exit()
})
}
function greeting () {
const cols = process.stdout.columns
let text = ''
if (cols > 85) text = 'lets-build'
else if (cols > 60) text = 'lets-|build'
else text = false
if (text && !isCI) {
say(text, {
colors: ['yellow'],
font: 'simple3d',
space: false
})
} else console.log(chalk.yellow.bold('\n lets-build'))
console.log()
}

View File

@ -0,0 +1,40 @@
const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(event => {
/**
* Reload browser when HTMLWebpackPlugin emits a new index.html
*
* Currently disabled until jantimon/html-webpack-plugin#680 is resolved.
* https://github.com/SimulatedGREG/electron-vue/issues/437
* https://github.com/jantimon/html-webpack-plugin/issues/680
*/
// if (event.action === 'reload') {
// window.location.reload()
// }
/**
* Notify `mainWindow` when `main` process is compiling,
* giving notice for an expected reload of the `electron` process
*/
if (event.action === 'compiling') {
document.body.innerHTML += `
<style>
#dev-client {
background: #4fc08d;
border-radius: 4px;
bottom: 20px;
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
color: #fff;
font-family: 'Source Sans Pro', sans-serif;
left: 20px;
padding: 8px 12px;
position: absolute;
}
</style>
<div id="dev-client">
Compiling Main Process...
</div>
`
}
})

191
.electron-vue/dev-runner.js Normal file
View File

@ -0,0 +1,191 @@
'use strict'
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 startRenderer () {
return new Promise((resolve, reject) => {
rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer)
rendererConfig.mode = 'development'
const compiler = webpack(rendererConfig)
hotMiddleware = webpackHotMiddleware(compiler, {
log: false,
heartbeat: 2500
})
compiler.hooks.compilation.tap('compilation', compilation => {
compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
compiler.hooks.done.tap('done', stats => {
logStats('Renderer', stats)
})
const server = new WebpackDevServer(
compiler,
{
contentBase: path.join(__dirname, '../'),
quiet: true,
hot: true,
before (app, ctx) {
// app.use(hotMiddleware)
ctx.middleware.waitUntilValid(() => {
resolve()
})
}
}
)
server.listen(9080)
})
}
function startMain () {
return new Promise((resolve, reject) => {
mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].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)
if (electronProcess && electronProcess.kill) {
manualRestart = true
process.kill(electronProcess.pid)
electronProcess = null
startElectron()
setTimeout(() => {
manualRestart = false
}, 5000)
}
resolve()
})
})
}
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 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()
Promise.all([startRenderer(), startMain()])
.then(() => {
startElectron()
})
.catch(err => {
console.error(err)
})
}
init()

View File

@ -0,0 +1,72 @@
'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.js')
},
externals: [
...Object.keys(dependencies || {})
],
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.node$/,
use: 'node-loader'
}
]
},
node: {
__dirname: process.env.NODE_ENV !== 'production',
__filename: process.env.NODE_ENV !== 'production'
},
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist/electron')
},
plugins: [
new webpack.NoEmitOnErrorsPlugin()
],
resolve: {
extensions: ['.js', '.json', '.node']
},
target: 'electron-main'
}
/**
* Adjust mainConfig for development settings
*/
if (process.env.NODE_ENV !== 'production') {
mainConfig.plugins.push(
new webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
})
)
}
/**
* Adjust mainConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
mainConfig.plugins.push(
new MinifyPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
})
)
}
module.exports = mainConfig

View File

@ -0,0 +1,191 @@
'use strict'
process.env.BABEL_ENV = 'renderer'
const path = require('path')
const { dependencies } = require('../package.json')
const webpack = require('webpack')
const MinifyPlugin = require("babel-minify-webpack-plugin")
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
/**
* List of node_modules to include in webpack bundle
*
* Required for specific packages like Vue UI libraries
* that provide pure *.vue files that need compiling
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals
*/
let whiteListedModules = ['vue']
let rendererConfig = {
devtool: '#cheap-module-eval-source-map',
entry: {
renderer: path.join(__dirname, '../src/renderer/main.js')
},
externals: [
...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d))
],
module: {
rules: [
{
test: /\.scss$/,
use: ['vue-style-loader', 'css-loader', 'sass-loader']
},
{
test: /\.sass$/,
use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax']
},
{
test: /\.less$/,
use: ['vue-style-loader', 'css-loader', 'less-loader']
},
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader']
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.node$/,
use: 'node-loader'
},
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
options: {
extractCSS: process.env.NODE_ENV === 'production',
loaders: {
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
scss: 'vue-style-loader!css-loader!sass-loader',
less: 'vue-style-loader!css-loader!less-loader'
}
}
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name]--[folder].[ext]'
}
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name]--[folder].[ext]'
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name]--[folder].[ext]'
}
}
}
]
},
node: {
__dirname: process.env.NODE_ENV !== 'production',
__filename: process.env.NODE_ENV !== 'production'
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({filename: 'styles.css'}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../src/index.ejs'),
templateParameters(compilation, assets, options) {
return {
compilation: compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options: options,
},
process,
};
},
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true
},
nodeModules: process.env.NODE_ENV !== 'production'
? path.resolve(__dirname, '../node_modules')
: false
}),
new webpack.NoEmitOnErrorsPlugin()
],
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist/electron')
},
resolve: {
alias: {
'@': path.join(__dirname, '../src/renderer'),
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['.js', '.vue', '.json', '.css', '.node']
},
target: 'electron-renderer'
}
/**
* Adjust rendererConfig for development settings
*/
if (process.env.NODE_ENV !== 'production') {
rendererConfig.plugins.push(
new webpack.HotModuleReplacementPlugin(),
new webpack.DefinePlugin({
'__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
})
)
}
/**
* Adjust rendererConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
rendererConfig.devtool = ''
rendererConfig.plugins.push(
new MinifyPlugin(),
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/electron/static'),
ignore: ['.*']
}
]),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
)
}
module.exports = rendererConfig

View File

@ -0,0 +1,152 @@
'use strict'
process.env.BABEL_ENV = 'web'
const path = require('path')
const webpack = require('webpack')
const MinifyPlugin = require("babel-minify-webpack-plugin")
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
let webConfig = {
devtool: '#cheap-module-eval-source-map',
entry: {
web: path.join(__dirname, '../src/renderer/main.js')
},
module: {
rules: [
{
test: /\.scss$/,
use: ['vue-style-loader', 'css-loader', 'sass-loader']
},
{
test: /\.sass$/,
use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax']
},
{
test: /\.less$/,
use: ['vue-style-loader', 'css-loader', 'less-loader']
},
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader']
},
{
test: /\.html$/,
use: 'vue-html-loader'
},
{
test: /\.js$/,
use: 'babel-loader',
include: [ path.resolve(__dirname, '../src/renderer') ],
exclude: /node_modules/
},
{
test: /\.vue$/,
use: {
loader: 'vue-loader',
options: {
extractCSS: true,
loaders: {
sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
scss: 'vue-style-loader!css-loader!sass-loader',
less: 'vue-style-loader!css-loader!less-loader'
}
}
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name].[ext]'
}
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name].[ext]'
}
}
}
]
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({filename: 'styles.css'}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.resolve(__dirname, '../src/index.ejs'),
templateParameters(compilation, assets, options) {
return {
compilation: compilation,
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options: options,
},
process,
};
},
minify: {
collapseWhitespace: true,
removeAttributeQuotes: true,
removeComments: true
},
nodeModules: false
}),
new webpack.DefinePlugin({
'process.env.IS_WEB': 'true'
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
],
output: {
filename: '[name].js',
path: path.join(__dirname, '../dist/web')
},
resolve: {
alias: {
'@': path.join(__dirname, '../src/renderer'),
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['.js', '.vue', '.json', '.css']
},
target: 'web'
}
/**
* Adjust webConfig for production settings
*/
if (process.env.NODE_ENV === 'production') {
webConfig.devtool = ''
webConfig.plugins.push(
new MinifyPlugin(),
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/web/static'),
ignore: ['.*']
}
]),
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
)
}
module.exports = webConfig

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
.DS_Store
dist/electron/*
dist/web/*
build/
!build/icons
node_modules/
npm-debug.log
npm-debug.log.*
thumbs.db
!.gitkeep
.idea
dist/

36
.travis.yml Normal file
View File

@ -0,0 +1,36 @@
osx_image: xcode8.3
sudo: required
dist: trusty
language: c
matrix:
include:
- os: osx
- os: linux
env: CC=clang CXX=clang++ npm_config_clang=1
compiler: clang
cache:
directories:
- node_modules
- "$HOME/.electron"
- "$HOME/.cache"
addons:
apt:
packages:
- libgnome-keyring-dev
- icnsutils
before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install git-lfs; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi
install:
- nvm install 10
- curl -o- -L https://yarnpkg.com/install.sh | bash
- source ~/.bashrc
- npm install -g xvfb-maybe
- yarn
before_script:
- git lfs pull
script:
- yarn run build
branches:
only:
- master

22
README.md Normal file
View File

@ -0,0 +1,22 @@
# rubick2
> An electron-vue project
#### Build Setup
``` bash
# install dependencies
npm install
# serve with hot reload at localhost:9080
npm run dev
# build electron application for production
npm run build
```
---
This project was generated with [electron-vue](https://github.com/SimulatedGREG/electron-vue) using [vue-cli](https://github.com/vuejs/vue-cli). Documentation about the original structure can be found [here](https://simulatedgreg.gitbooks.io/electron-vue/content/index.html).

9
api/index.js Normal file
View File

@ -0,0 +1,9 @@
const notice = require('./operators/notice');
const network = require('./operators/network');
const common = require('./operators/common');
window.rubick = {
notice,
network,
common,
}

7
api/operators/common.js Normal file
View File

@ -0,0 +1,7 @@
const { shell } = require('electron');
module.exports = {
openInBrowser(url) {
shell.openExternal(url);
}
}

71
api/operators/network.js Normal file
View File

@ -0,0 +1,71 @@
const {ipcRenderer} = require("electron");
const AnyProxy = require('anyproxy');
const options = {
port: 8001,
webInterface: {
enable: true,
webPort: 8002
},
forceProxyHttps: false,
wsIntercept: false, // 不开启websocket代理
silent: true
};
class Network {
constructor() {
this.mockList = [];
this.proxyServer = null;
}
initNetwork(op, {
beforeSendRequest,
beforeSendResponse,
success,
}) {
if (op === 'start') {
if (!this.proxyServer || !this.proxyServer.recorder) {
const _this = this;
options.rule = {
*beforeSendRequest(requestDetail) {
if (beforeSendRequest) {
return beforeSendRequest(requestDetail);
}
return requestDetail
},
*beforeSendResponse (requestDetail, responseDetail) {
if (beforeSendResponse) {
return beforeSendResponse(requestDetail, responseDetail);
}
return responseDetail;
}
};
this.proxyServer = new AnyProxy.ProxyServer(options);
this.proxyServer.once('ready', () => {
console.log('启动完成');
success && success(this.proxyServer);
});
}
this.proxyServer.start();
} else {
AnyProxy.utils.systemProxyMgr.disableGlobalProxy('http');
AnyProxy.utils.systemProxyMgr.disableGlobalProxy('https');
this.proxyServer.close();
success && success();
}
}
getIPAddress() {
const interfaces = require('os').networkInterfaces();
for (const devName in interfaces) {
const iface = interfaces[devName];
for (let i = 0; i < iface.length; i++) {
const alias = iface[i];
if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
return alias.address;
}
}
}
}
}
module.exports = new Network();

11
api/operators/notice.js Normal file
View File

@ -0,0 +1,11 @@
module.exports = {
system({title, body}, cb) {
const myNotification = new Notification(title, {
body
});
myNotification.onclick = () => {
cb();
}
}
}

29
appveyor.yml Normal file
View File

@ -0,0 +1,29 @@
version: 0.1.{build}
branches:
only:
- master
image: Visual Studio 2017
platform:
- x64
cache:
- node_modules
- '%APPDATA%\npm-cache'
- '%USERPROFILE%\.electron'
- '%USERPROFILE%\AppData\Local\Yarn\cache'
init:
- git config --global core.autocrlf input
install:
- ps: Install-Product node 8 x64
- git reset --hard HEAD
- yarn
- node --version
build_script:
- yarn build
test: off

14453
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

111
package.json Normal file
View File

@ -0,0 +1,111 @@
{
"name": "rubick2",
"version": "0.0.1",
"author": "muwoo <2424880409@qq.com>",
"description": "An electron-vue project",
"license": null,
"main": "./dist/electron/main.js",
"scripts": {
"build": "node .electron-vue/build.js && electron-builder",
"build:dir": "node .electron-vue/build.js && electron-builder --dir",
"build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js",
"build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js",
"dev": "node .electron-vue/dev-runner.js",
"pack": "npm run pack:main && npm run pack:renderer",
"pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
"pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
"postinstall": ""
},
"build": {
"asar": false,
"productName": "rubick2",
"appId": "com.example.yourapp2",
"directories": {
"output": "build"
},
"files": [
"dist/electron/**/*"
],
"electronDownload": {
"mirror": "https://npm.taobao.org/mirrors/electron/"
},
"dmg": {
"contents": [
{
"x": 410,
"y": 150,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 150,
"type": "file"
}
]
},
"mac": {
"icon": "build/icons/icon.icns",
"target":"pkg"
},
"win": {
"icon": "build/icons/icon.ico"
},
"linux": {
"icon": "build/icons"
}
},
"dependencies": {
"ant-design-vue": "^1.7.5",
"anyproxy": "^4.1.3",
"axios": "^0.18.0",
"download-git-repo": "^3.0.2",
"electron-store": "^8.0.0",
"vue": "^2.5.16",
"vue-electron": "^1.0.6",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",
"vuex-electron": "^1.0.0"
},
"devDependencies": {
"ajv": "^6.5.0",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.4",
"babel-minify-webpack-plugin": "^0.3.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.26.0",
"cfonts": "^2.1.2",
"chalk": "^2.4.1",
"copy-webpack-plugin": "^4.5.1",
"cross-env": "^5.1.6",
"css-loader": "^0.28.11",
"del": "^3.0.0",
"devtron": "^1.4.0",
"electron": "^11.0.2",
"electron-builder": "^20.19.2",
"electron-debug": "^1.5.0",
"electron-devtools-installer": "^2.2.4",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.2.0",
"listr": "^0.14.3",
"mini-css-extract-plugin": "0.4.0",
"multispinner": "^0.2.1",
"node-loader": "^0.6.0",
"node-sass": "^4.9.2",
"react": "^17.0.2",
"sass-loader": "^7.0.3",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"vue-html-loader": "^1.2.4",
"vue-loader": "^15.2.4",
"vue-style-loader": "^4.1.0",
"vue-template-compiler": "^2.5.16",
"webpack": "^4.15.1",
"webpack-cli": "^3.0.8",
"webpack-dev-server": "^3.1.4",
"webpack-hot-middleware": "^2.22.2",
"webpack-merge": "^4.1.3"
}
}

View File

@ -0,0 +1,11 @@
import { ipcMain } from 'electron';
const init = () => {
ipcMain.on('msg-trigger', async (event, arg) => {
});
}
export default () => {
init();
}

7
src/common/utils.js Normal file
View File

@ -0,0 +1,7 @@
export const getlocalDataFile = () => {
let localDataFile = process.env.HOME;
if (!localDataFile) {
localDataFile = process.env.LOCALAPPDATA;
}
return localDataFile;
};

24
src/index.ejs Normal file
View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>rubick2</title>
<% if (htmlWebpackPlugin.options.nodeModules) { %>
<!-- Add `node_modules/` to global paths so `require` works properly in development -->
<script>
require('module').globalPaths.push('<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, '\\\\') %>')
</script>
<% } %>
</head>
<body>
<div id="app"></div>
<!-- Set `__static` path to static files in production -->
<% if (!process.browser) { %>
<script>
if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
</script>
<% } %>
<!-- webpack builds are automatically injected -->
</body>
</html>

17
src/main/common.js Normal file
View File

@ -0,0 +1,17 @@
import {BrowserWindow, globalShortcut, ipcMain} from 'electron';
export default function init(mainWindow) {
ipcMain.on('changeWindowSize', (event, arg) => {
mainWindow.setSize(arg.width || 788, arg.height);
});
mainWindow.on('blur', () => {
// mainWindow.hide();
});
globalShortcut.register('Alt+R', () => {
mainWindow.show();
});
}

24
src/main/index.dev.js Normal file
View File

@ -0,0 +1,24 @@
/**
* This file is used specifically and only for development. It installs
* `electron-debug` & `vue-devtools`. There shouldn't be any need to
* modify this file, but it can be used to extend your development
* environment.
*/
/* eslint-disable */
// Install `electron-debug` with `devtron`
require('electron-debug')({ showDevTools: true })
// Install `vue-devtools`
require('electron').app.on('ready', () => {
let installExtension = require('electron-devtools-installer')
installExtension.default(installExtension.VUEJS_DEVTOOLS)
.then(() => {})
.catch(err => {
console.log('Unable to install `vue-devtools`: \n', err)
})
})
// Require `main` process to boot app
require('./index')

81
src/main/index.js Normal file
View File

@ -0,0 +1,81 @@
import { app, BrowserWindow } from 'electron'
import '../renderer/store'
import eventTracker from '../common/event-tracker';
import init from './common';
import createTray from './tray';
/**
* Set `__static` path to static files in production
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
*/
if (process.env.NODE_ENV !== 'development') {
global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
}
let mainWindow
const winURL = process.env.NODE_ENV === 'development'
? `http://localhost:9080`
: `file://${__dirname}/index.html`
function createWindow () {
/**
* Initial window options
*/
mainWindow = new BrowserWindow({
height: 60,
useContentSize: true,
width: 788,
frame: false,
title: '拉比克',
webPreferences: {
enableRemoteModule: true,
backgroundThrottling: false,
webviewTag: true,
nodeIntegration: true // 在网页中集成Node
}
})
mainWindow.loadURL(winURL)
mainWindow.on('closed', () => {
mainWindow = null
});
init(mainWindow);
eventTracker();
}
app.on('ready', () => {
createWindow()
createTray(mainWindow);
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (mainWindow === null) {
createWindow()
}
})
/**
* Auto Updater
*
* Uncomment the following code below and install `electron-updater` to
* support auto updating. Code Signing with a valid certificate is required.
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-builder.html#auto-updating
*/
/*
import { autoUpdater } from 'electron-updater'
autoUpdater.on('update-downloaded', () => {
autoUpdater.quitAndInstall()
})
app.on('ready', () => {
if (process.env.NODE_ENV === 'production') autoUpdater.checkForUpdates()
})
*/

59
src/main/tray.js Normal file
View File

@ -0,0 +1,59 @@
import { dialog, Menu, Tray, app, shell, ipcMain } from 'electron';
import path from 'path';
import pkg from '../../package.json';
function createTray(window) {
return new Promise((resolve, reject) => {
const appIcon = new Tray(path.join(__static, './rocket-t.png'));
const contextMenu = Menu.buildFromTemplate([
{
id: 3,
label: '显示窗口',
accelerator: "Alt+R",
click() {
window.show();
}
},
{
id: 4,
label: '文档',
click() {
shell.openExternal('https://muwoo.github.io/rubick-doc/');
}
},
{
id: 5,
label: '显示窗口',
click() {
window.show();
}
},
{
id: 6,
label: '关于',
click() {
dialog.showMessageBox({
title: '拉比克',
message: '一站式前端开发工具箱',
detail: `Version: ${pkg.version}\nAuthor: muwoo`
});
}
},
{
id: 7,
role: 'quit',
label: '退出'
}
]);
appIcon.on('click', () => {
appIcon.popUpContextMenu(contextMenu);
});
appIcon.setContextMenu(contextMenu);
resolve(appIcon);
});
}
export default createTray;

84
src/renderer/App.vue Normal file
View File

@ -0,0 +1,84 @@
<template>
<a-layout id="components-layout-demo-custom-trigger">
<div class="rubick-select">
<div class="tag-container" v-if="selected">
<a-tag
:key="selected.key"
@close="closeTag"
class="select-tag"
color="green"
closable
>
{{ selected.name }}
</a-tag>
</div>
<a-input
placeholder="Hi, Rubick"
class="main-input"
@change="onSearch"
:value="searchValue"
>
<a-icon class="icon-tool" type="tool" slot="suffix" @click="() => {
showMainUI();
changePath({key: 'market'})
}"/>
}
</a-input>
<div class="options" v-show="options.length && !showMain">
<a-list item-layout="horizontal" :data-source="options">
<a-list-item @click="() => item.click($router)" class="op-item" slot="renderItem" slot-scope="item, index">
<a-list-item-meta
:description="item.desc"
>
<span slot="title" >{{ item.name }}</span>
<a-avatar
slot="avatar"
src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
/>
</a-list-item-meta>
</a-list-item>
</a-list>
</div>
</div>
<router-view></router-view>
</a-layout>
</template>
<script>
import {mapActions, mapMutations, mapState} from "vuex";
import {ipcRenderer} from "electron";
import {getWindowHeight} from "./assets/common/utils";
export default {
methods: {
...mapActions('main', ['onSearch', 'showMainUI']),
...mapMutations('main', ['commonUpdate']),
changePath({key}) {
this.$router.push({path: `/home/${key}`});
this.commonUpdate({
current: [key]
})
},
closeTag(v) {
this.commonUpdate({
selected: null,
showMain: false,
});
ipcRenderer.send('changeWindowSize', {
height: getWindowHeight([]),
});
this.$router.push({
path: '/home',
});
}
},
computed: {
...mapState('main', ['showMain', 'current', 'options', 'selected', 'searchValue'])
}
};
</script>
<style lang="scss">
* {
margin: 0;
padding: 0;
}
</style>

View File

View File

@ -0,0 +1,4 @@
export default {
development: 'http://localhost:7001',
// development: 'http://kaer-server.qa.91jkys.com',
};

View File

@ -0,0 +1,5 @@
import plugin from './list/plugin';
export default {
plugin
}

View File

@ -0,0 +1,16 @@
import instance from '../request';
export default {
async add(params) {
const result = await instance.post('/plugin/create', params);
return result.data;
},
async update(params) {
const result = await instance.post('/plugin/update', params);
return result.data;
},
async query(params) {
const result = await instance.get('/plugin/query', {params});
return result.data;
}
}

View File

@ -0,0 +1,10 @@
import axios from 'axios';
import config from "./config";
const instance = axios.create({
baseURL: config[process.env.NODE_ENV],
timeout: 10000,
withCredentials: true
});
export default instance;

View File

@ -0,0 +1,9 @@
const WINDOW_MAX_HEIGHT = 766;
const WINDOW_MIN_HEIGHT = 60;
const PRE_ITEM_HEIGHT = 60;
export {
WINDOW_MAX_HEIGHT,
WINDOW_MIN_HEIGHT,
PRE_ITEM_HEIGHT,
}

View File

@ -0,0 +1,94 @@
import {WINDOW_MAX_HEIGHT, WINDOW_MIN_HEIGHT, PRE_ITEM_HEIGHT} from './constans';
import download from 'download-git-repo';
import path from 'path';
import fs from 'fs';
import process from 'child_process';
import Store from 'electron-store';
const store = new Store();
function getWindowHeight(searchList) {
if (!searchList) return WINDOW_MAX_HEIGHT;
if (!searchList.length) return WINDOW_MIN_HEIGHT;
return searchList.length * PRE_ITEM_HEIGHT + WINDOW_MIN_HEIGHT + 5 > WINDOW_MAX_HEIGHT ? WINDOW_MAX_HEIGHT : searchList.length * PRE_ITEM_HEIGHT + WINDOW_MIN_HEIGHT + 5;
}
function searchKeyValues(lists, value){
return lists.filter(item => item.indexOf(value) >= 0)
}
function existOrNot(path) {
return new Promise((resolve, reject) => {
fs.stat(path, async (err, stat) => {
if (err) {
resolve(false);
} else {
resolve(true);
}
});
});
}
function mkdirFolder(name) {
return new Promise((resolve, reject) => {
process.exec(`mkdir ${name}`, async function (error, stdout, stderr) {
if (error) {
reject(false);
} else {
resolve(true);
}
})
});
}
function downloadFunc(downloadRepoUrl, name) {
const plugin_path = path.join(__static, './plugins');
return new Promise(async (resolve, reject) => {
try {
if (!(await existOrNot(plugin_path))) {
await mkdirFolder(plugin_path);
}
// 基础模版所在目录,如果是初始化,则是模板名称,否则是项目名称
const temp_dest = `${plugin_path}/${name}`;
// 下载模板
if (await existOrNot(temp_dest)) {
await process.execSync(`rm -rf ${temp_dest}`);
}
download(`github:clouDr-f2e/${name}`, temp_dest, function (err) {
console.log(err ? 'Error' : 'Success')
if (err) {
console.log(err);
reject('请求模板下载失败');
} else {
resolve('请求模板下载成功');
}
})
} catch (e) {
console.log(e);
}
});
}
const sysFile = {
savePlugins(plugins) {
store.set('user-plugins', plugins);
},
getUserPlugins() {
try {
console.log(store.get('user-plugins').devPlugins)
return store.get('user-plugins').devPlugins;
} catch (e) {
return []
}
}
}
export {
getWindowHeight,
searchKeyValues,
downloadFunc,
sysFile,
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

23
src/renderer/main.js Normal file
View File

@ -0,0 +1,23 @@
import Vue from 'vue'
import axios from 'axios'
import App from './App'
import router from './router'
import store from './store'
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
Vue.http = Vue.prototype.$http = axios
Vue.config.productionTip = false
Vue.use(Antd);
/* eslint-disable no-new */
new Vue({
components: { App },
router,
store,
template: '<App/>'
}).$mount('#app')

View File

@ -0,0 +1,62 @@
<template>
<div>
<a-list item-layout="horizontal" :data-source="data">
<a-list-item slot="renderItem" slot-scope="item, index">
<a-list-item-meta>
<div class="desc" slot="description">
Ant Design, a design language for background applications, is refined by Ant UED Team
</div>
<div slot="title">
{{ item.title }}
<a-tag color="green">标签</a-tag>
</div>
<a-avatar
slot="avatar"
src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
/>
</a-list-item-meta>
<div class="fava-btn">
<a-icon type="star" /> 添加
</div>
</a-list-item>
</a-list>
</div>
</template>
<script>
const data = [
{
title: 'Ant Design Title 1',
},
{
title: 'Ant Design Title 2',
},
{
title: 'Ant Design Title 3',
},
{
title: 'Ant Design Title 4',
},
];
export default {
data() {
return {
data,
};
},
}
</script>
<style lang="scss">
.fava-btn {
cursor: pointer;
&:hover {
color: #f50;
}
}
.desc {
overflow: hidden;
text-overflow:ellipsis;
white-space: nowrap;
width: 400px;
}
</style>

View File

@ -0,0 +1,37 @@
<template>
<div>
<webview style="width: 100%;height: 100vh" id="webview" :src="path"></webview>
</div>
</template>
<script>
import path from 'path';
import fs from 'fs';
import {getlocalDataFile} from '../../../common/utils';
import { remote } from 'electron';
// import process from 'child_process';
export default {
name: "index.vue",
data() {
return {
path: `File://${this.$route.query.sourceFile}`,
preload: path.join(process.cwd(), './api/index.js')
}
},
mounted() {
console.log(1111, this.$route.query)
// const webview = document.querySelector('webview')
// webview.addEventListener('dom-ready', () => {
// webview.openDevTools()
// })
},
beforeRouteUpdate() {
this.path = `File://${this.$route.query.sourceFile}`
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,115 @@
<template>
<div>
<div v-show="showMain">
<a-menu :selectedKeys="current" mode="horizontal" @select="changePath">
<a-menu-item key="market">
<a-icon type="appstore"/>
插件中心
</a-menu-item>
<a-menu-item key="favo">
<a-icon type="heart"/>
已安装
</a-menu-item>
<a-menu-item key="dev">
<a-icon type="code"/>
开发者
</a-menu-item>
<a-menu-item key="set">
<a-icon type="setting"/>
设置
</a-menu-item>
</a-menu>
</div>
<router-view v-show="showMain"/>
</div>
</template>
<script>
import {mapActions, mapState, mapMutations} from 'vuex';
export default {
name: "search",
methods: {
...mapActions('main', ['onSearch', 'showMainUI']),
...mapMutations('main', ['commonUpdate']),
changePath({key}) {
this.$router.push({path: `/home/${key}`});
this.commonUpdate({
current: [key]
})
},
},
computed: {
...mapState('main', ['showMain', 'current', 'options', 'selected', 'searchValue'])
}
}
</script>
<style lang="scss">
.rubick-select {
display: flex;
padding-left: 10px;
background: #fff;
position: relative;
.tag-container {
display: flex;
align-items: center;
background: #fff;
.select-tag {
height: 36px;
font-size: 20px;
display: flex;
align-items: center;
}
}
.ant-input:focus {
border: none;
box-shadow: none;
}
.options {
position: absolute;
top: 62px;
left: 0;
width: 100%;
z-index: 99;
.op-item {
padding: 0 10px;
height: 60px;
line-height: 50px;
max-height: 500px;
overflow: auto;
background: #fafafa;
}
}
}
.main-input {
-webkit-app-region: drag;
height: 60px !important;
flex: 1;
.ant-select-selection, .ant-input, .ant-select-selection__rendered {
height: 60px !important;
font-size: 22px;
border: none !important;
}
.icon-tool {
font-size: 24px;
background: #314659;
color: #fff;
height: 40px;
width: 40px;
border-radius: 100%;
line-height: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
}
</style>

View File

@ -0,0 +1,125 @@
<template>
<div class="dev-container">
<div class="dev-detail" v-if="devPlugins.length">
<a-menu v-model="currentSelect" style="width: 256px; height: 100%" mode="vertical">
<a-menu-item @click="currentSelect = [index]" v-for="(plugin, index) in devPlugins" :key="index">
<div>{{ plugin.pluginName }}</div>
<div>{{ plugin.description }}</div>
</a-menu-item>
</a-menu>
<div class="plugin-detail">
<div class="plugin-top">
<div class="left">
<div class="title">
{{pluginDetail.pluginName}}
<a-tag>{{pluginDetail.version}}</a-tag>
</div>
<div class="desc">
开发者{{`${pluginDetail.author || '未知'}`}}
</div>
<div class="desc">
{{pluginDetail.description}}
</div>
</div>
<div class="right">
<a-switch />
</div>
</div>
<a-tabs default-active-key="1">
<a-tab-pane key="1" tab="管理">
<div class="desc-item">
<div class="desc-title">
<p>重新加载</p>
<a-button type="link">重载</a-button>
</div>
<div class="desc-info">
如果你修改了plugin.json文件需要重新加载以应用最近版本
</div>
</div>
<div class="desc-item">
<div class="desc-title">
<p>发布</p>
<a-button type="link">发布</a-button>
</div>
<div class="desc-info">
发布后用户可以通过插件中心下载且享受最新的更新
</div>
</div>
<div class="desc-item">
<div class="desc-title">
<p>删除</p>
<a-button type="link">删除</a-button>
</div>
<div class="desc-info">
删除这个插件不可以恢复
</div>
</div>
</a-tab-pane>
<a-tab-pane key="2" tab="详情介绍">
Content of Tab Pane 2
</a-tab-pane>
</a-tabs>
</div>
</div>
</div>
</template>
<script>
import {mapState} from 'vuex';
export default {
data() {
return {
currentSelect: [0]
}
},
computed: {
...mapState('main', ['devPlugins']),
pluginDetail() {
console.log(this.$store)
return this.devPlugins[this.currentSelect]
}
}
}
</script>
<style lang="scss">
.dev-container {
height: calc(100vh - 110px);
.dev-detail {
display: flex;
align-items: flex-start;
height: 100%;
}
.plugin-detail {
padding: 20px;
box-sizing: border-box;
flex: 1;
.plugin-top {
display: flex;
align-items: flex-start;
justify-content: space-between;
.title {
font-size: 20px;
display: flex;
align-items: center;
}
.desc {
font-size: 13px;
color: #999;
}
}
.desc-item {
border-bottom: 1px solid #ddd;
padding: 10px 0;
.desc-title {
display: flex;
align-items: center;
justify-content: space-between;
}
.desc-info {
color: #999;
}
}
}
}
</style>

View File

@ -0,0 +1,113 @@
<template>
<div class="market">
<a-carousel arrows>
<div
slot="prevArrow"
slot-scope="props"
class="custom-slick-arrow"
style="left: 10px;zIndex: 1"
>
<a-icon type="left-circle" />
</div>
<div slot="nextArrow" slot-scope="props" class="custom-slick-arrow" style="right: 10px">
<a-icon type="right-circle" />
</div>
<div><h3>1</h3></div>
<div><h3>2</h3></div>
<div><h3>3</h3></div>
<div><h3>4</h3></div>
</a-carousel>
<a-divider></a-divider>
<h2>插件</h2>
<a-list item-layout="horizontal" style="width: 100%" :grid="{ gutter: 16, column: 2 }" :data-source="pluginList">
<a-list-item slot="renderItem" slot-scope="item, index">
<a-button :loading="loading[index]" type="link" slot="actions" @click="download(index, item)">
<a-icon v-show="!loading[index]" style="font-size: 20px;" type="cloud-download" />
</a-button>
<a-list-item-meta
:description="item.description"
>
<a slot="title" href="https://www.antdv.com/">{{ item.title }}</a>
<a-avatar
slot="avatar"
src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
/>
</a-list-item-meta>
</a-list-item>
</a-list>
</div>
</template>
<script>
import api from '../../../assets/api';
import {mapActions} from 'vuex';
export default {
data() {
return {
pluginList: [],
loading: {},
}
},
async created() {
const result = await api.plugin.query();
this.pluginList = result.result;
},
methods: {
async download(index, item) {
if (this.loading[index]) return;
this.$set(this.loading, index, true);
console.log(this.loading);
await this.downloadPlugin(item);
this.$set(this.loading, index, false);
console.log(this.loading);
},
...mapActions('main', ['downloadPlugin'])
}
}
</script>
<style lang="scss">
.market {
height: calc(100vh - 110px);
background: #fff;
padding: 20px;
box-sizing: border-box;
.ant-carousel .slick-slide {
text-align: center;
height: 200px;
line-height: 160px;
background: #364d79;
overflow: hidden;
}
.ant-carousel .custom-slick-arrow {
width: 25px;
height: 25px;
font-size: 25px;
color: #fff;
background-color: rgba(31, 45, 61, 0.11);
opacity: 0.3;
}
.ant-carousel .custom-slick-arrow:before {
display: none;
}
.ant-carousel .custom-slick-arrow:hover {
opacity: 0.5;
}
.ant-carousel .slick-slide h3 {
color: #fff;
}
.ant-list-item {
display: flex !important;
align-items: center;
justify-content: space-between;
}
}
</style>

View File

@ -0,0 +1,35 @@
import Vue from 'vue'
import Router from 'vue-router'
import Market from '../pages/search/subpages/market';
import Dev from '../pages/search/subpages/dev';
Vue.use(Router)
export default new Router({
routes: [
{
path: '/home',
name: 'search',
component: require('@/pages/search/index.vue').default,
children: [
{
path: 'market',
component: Market
},
{
path: 'dev',
component: Dev
},
]
},
{
path: '/plugin',
name: 'plugin',
component: require('@/pages/plugins/index.vue').default
},
{
path: '*',
redirect: '/home'
}
]
})

View File

@ -0,0 +1,10 @@
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
Vue.use(Vuex)
export default new Vuex.Store({
modules,
strict: process.env.NODE_ENV !== 'production'
})

View File

@ -0,0 +1,25 @@
const state = {
main: 0
}
const mutations = {
DECREMENT_MAIN_COUNTER (state) {
state.main--
},
INCREMENT_MAIN_COUNTER (state) {
state.main++
}
}
const actions = {
someAsyncTask ({ commit }) {
// do something async
commit('INCREMENT_MAIN_COUNTER')
}
}
export default {
state,
mutations,
actions
}

View File

@ -0,0 +1,14 @@
/**
* The file enables `@/store/index.js` to import all vuex modules
* in a one-shot manner. There should not be any reason to edit this file.
*/
const files = require.context('.', false, /\.js$/)
const modules = {}
files.keys().forEach(key => {
if (key === './index.js') return
modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
})
export default modules

View File

@ -0,0 +1,162 @@
import {clipboard, ipcRenderer} from "electron";
import {getWindowHeight, searchKeyValues, downloadFunc, sysFile} from '../../assets/common/utils';
import fs from "fs";
import path from 'path';
const state = {
selected: null,
options: [],
showMain: false,
current: ['market'],
searchValue: '',
devPlugins: sysFile.getUserPlugins() || [],
}
const mutations = {
commonUpdate (state, payload) {
Object.keys(payload).forEach((key) => {
state[key] = payload[key];
if (key === 'devPlugins') {
sysFile.savePlugins(payload)
}
});
},
}
const actions = {
showMainUI ({ commit, state }, paylpad) {
commit('commonUpdate', {
showMain: true,
selected: {
key: 'market',
name: '插件中心'
}
});
ipcRenderer.send('changeWindowSize', {
height: getWindowHeight(),
});
},
onSearch ({ commit }, paylpad) {
const value = paylpad.target.value;
const fileUrl = clipboard.read('public.file-url').replace('file://', '');
commit('commonUpdate', {searchValue: value})
// 复制文件
if (fileUrl && value === 'plugin.json') {
const config = JSON.parse(fs.readFileSync(fileUrl, 'utf-8'));
const pluginConfig = {
...JSON.parse(fs.readFileSync(fileUrl, 'utf-8')),
sourceFile: path.join(fileUrl, `../${config.main}`)
};
commit('commonUpdate', {
selected: {
key: 'plugin',
name: 'plugin.json'
},
searchValue: '',
devPlugins: [pluginConfig, ...state.devPlugins],
options: [
{
name: '新建rubick开发插件',
value: 'new-plugin',
icon: 'plus-circle',
desc: '新建rubick开发插件',
click: (router) => {
commit('commonUpdate', {
showMain: true,
selected: {
key: 'plugin',
name: '新建rubick开发插件'
},
current: ['dev'],
});
ipcRenderer.send('changeWindowSize', {
height: getWindowHeight(),
});
router.push('/home/dev')
}
},
{
name: '复制路径',
desc: '复制路径',
value: 'copy-path',
icon: 'plus-circle',
click: () => {
clipboard.writeText(fileUrl)
}
}
]
});
// 调整窗口大小
ipcRenderer.send('changeWindowSize', {
height: getWindowHeight(state.options),
});
return
}
let options = [];
// check 是否是插件
state.devPlugins.forEach((plugin) => {
const feature = plugin.features;
feature.forEach(fe => {
const cmds = searchKeyValues(fe.cmds, value);
options = [
...options,
...cmds.map((cmd) => ({
name: cmd,
value: 'plugin',
icon: 'plus-circle',
desc: fe.explain,
click: (router) => {
commit('commonUpdate', {
selected: {
key: cmd,
name: cmd
},
searchValue: '',
showMain: true,
});
ipcRenderer.send('changeWindowSize', {
height: getWindowHeight(),
});
router.push({
path: '/plugin',
query: plugin,
})
}
}))
]
})
});
commit('commonUpdate', {
options
});
ipcRenderer.send('changeWindowSize', {
height: getWindowHeight(state.options),
});
},
async downloadPlugin({commit}, payload) {
await downloadFunc(payload.gitUrl, payload.name);
const fileUrl = path.join(__static, `plugins/${payload.name}`);
// 复制文件
const config = JSON.parse(fs.readFileSync(`${fileUrl}/plugin.json`, 'utf-8'));
const pluginConfig = {
...config,
sourceFile: `${fileUrl}/${config.main}`
};
commit('commonUpdate', {
devPlugins: [pluginConfig, ...state.devPlugins],
});
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

0
static/.gitkeep Normal file
View File

View File

@ -0,0 +1,2 @@
node_modules
.idea

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
hello world123123
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View File

@ -0,0 +1,15 @@
{
"pluginName": "测试插件",
"description": "我的第一个 rubick 插件",
"main": "index.html",
"version": "0.0.1",
"logo": "logo.png",
"features": [
{
"code": "hello",
"explain": "这是一个测试的插件",
"cmds":["hello", "你好"]
}
],
"preload": "preload.js"
}

View File

@ -0,0 +1,6 @@
const { readFileSync } = require('fs')
window.readConfig = function () {
const data = '123123'
return data
}

BIN
static/rocket-t.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 B

BIN
static/rocket.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 B