mirror of
https://github.com/rubickCenter/rubick
synced 2025-07-16 12:21:25 +08:00
init
This commit is contained in:
commit
ce490acb6a
30
.babelrc
Normal file
30
.babelrc
Normal 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
132
.electron-vue/build.js
Normal 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()
|
||||
}
|
40
.electron-vue/dev-client.js
Normal file
40
.electron-vue/dev-client.js
Normal 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
191
.electron-vue/dev-runner.js
Normal 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()
|
72
.electron-vue/webpack.main.config.js
Normal file
72
.electron-vue/webpack.main.config.js
Normal 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
|
191
.electron-vue/webpack.renderer.config.js
Normal file
191
.electron-vue/webpack.renderer.config.js
Normal 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
|
152
.electron-vue/webpack.web.config.js
Normal file
152
.electron-vue/webpack.web.config.js
Normal 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
12
.gitignore
vendored
Normal 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
36
.travis.yml
Normal 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
22
README.md
Normal 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
9
api/index.js
Normal 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
7
api/operators/common.js
Normal file
@ -0,0 +1,7 @@
|
||||
const { shell } = require('electron');
|
||||
|
||||
module.exports = {
|
||||
openInBrowser(url) {
|
||||
shell.openExternal(url);
|
||||
}
|
||||
}
|
71
api/operators/network.js
Normal file
71
api/operators/network.js
Normal 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
11
api/operators/notice.js
Normal 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
29
appveyor.yml
Normal 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
14453
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
111
package.json
Normal file
111
package.json
Normal 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"
|
||||
}
|
||||
}
|
11
src/common/event-tracker/index.js
Normal file
11
src/common/event-tracker/index.js
Normal 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
7
src/common/utils.js
Normal 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
24
src/index.ejs
Normal 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
17
src/main/common.js
Normal 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
24
src/main/index.dev.js
Normal 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
81
src/main/index.js
Normal 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
59
src/main/tray.js
Normal 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
84
src/renderer/App.vue
Normal 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>
|
0
src/renderer/assets/.gitkeep
Normal file
0
src/renderer/assets/.gitkeep
Normal file
4
src/renderer/assets/api/config.js
Normal file
4
src/renderer/assets/api/config.js
Normal file
@ -0,0 +1,4 @@
|
||||
export default {
|
||||
development: 'http://localhost:7001',
|
||||
// development: 'http://kaer-server.qa.91jkys.com',
|
||||
};
|
5
src/renderer/assets/api/index.js
Normal file
5
src/renderer/assets/api/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
import plugin from './list/plugin';
|
||||
|
||||
export default {
|
||||
plugin
|
||||
}
|
16
src/renderer/assets/api/list/plugin.js
Normal file
16
src/renderer/assets/api/list/plugin.js
Normal 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;
|
||||
}
|
||||
}
|
10
src/renderer/assets/api/request.js
Normal file
10
src/renderer/assets/api/request.js
Normal 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;
|
9
src/renderer/assets/common/constans.js
Normal file
9
src/renderer/assets/common/constans.js
Normal 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,
|
||||
}
|
94
src/renderer/assets/common/utils.js
Normal file
94
src/renderer/assets/common/utils.js
Normal 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,
|
||||
}
|
BIN
src/renderer/assets/logo.png
Normal file
BIN
src/renderer/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
23
src/renderer/main.js
Normal file
23
src/renderer/main.js
Normal 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')
|
62
src/renderer/pages/index/index.vue
Normal file
62
src/renderer/pages/index/index.vue
Normal 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>
|
37
src/renderer/pages/plugins/index.vue
Normal file
37
src/renderer/pages/plugins/index.vue
Normal 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>
|
115
src/renderer/pages/search/index.vue
Normal file
115
src/renderer/pages/search/index.vue
Normal 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>
|
125
src/renderer/pages/search/subpages/dev.vue
Normal file
125
src/renderer/pages/search/subpages/dev.vue
Normal 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>
|
113
src/renderer/pages/search/subpages/market.vue
Normal file
113
src/renderer/pages/search/subpages/market.vue
Normal 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>
|
35
src/renderer/router/index.js
Normal file
35
src/renderer/router/index.js
Normal 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'
|
||||
}
|
||||
]
|
||||
})
|
10
src/renderer/store/index.js
Normal file
10
src/renderer/store/index.js
Normal 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'
|
||||
})
|
25
src/renderer/store/modules/dev.js
Normal file
25
src/renderer/store/modules/dev.js
Normal 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
|
||||
}
|
14
src/renderer/store/modules/index.js
Normal file
14
src/renderer/store/modules/index.js
Normal 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
|
162
src/renderer/store/modules/main.js
Normal file
162
src/renderer/store/modules/main.js
Normal 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
0
static/.gitkeep
Normal file
2
static/plugins/rubick-plugin-demo/.gitignore
vendored
Normal file
2
static/plugins/rubick-plugin-demo/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
.idea
|
6
static/plugins/rubick-plugin-demo/index.html
Normal file
6
static/plugins/rubick-plugin-demo/index.html
Normal file
@ -0,0 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
hello world123123
|
||||
</body>
|
||||
</html>
|
BIN
static/plugins/rubick-plugin-demo/logo.png
Normal file
BIN
static/plugins/rubick-plugin-demo/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
15
static/plugins/rubick-plugin-demo/plugin.json
Normal file
15
static/plugins/rubick-plugin-demo/plugin.json
Normal 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"
|
||||
}
|
6
static/plugins/rubick-plugin-demo/preload.js
Normal file
6
static/plugins/rubick-plugin-demo/preload.js
Normal file
@ -0,0 +1,6 @@
|
||||
const { readFileSync } = require('fs')
|
||||
|
||||
window.readConfig = function () {
|
||||
const data = '123123'
|
||||
return data
|
||||
}
|
BIN
static/rocket-t.png
Normal file
BIN
static/rocket-t.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 494 B |
BIN
static/rocket.png
Normal file
BIN
static/rocket.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 857 B |
Loading…
x
Reference in New Issue
Block a user