Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fecd35cb06 | ||
|
|
4a9623e49f | ||
|
|
e1ed998153 | ||
|
|
3a97d019d1 | ||
|
|
4b725930d2 | ||
|
|
7204e05601 | ||
|
|
7c4b4f28e1 | ||
|
|
f6752c6f8a | ||
|
|
ad986689e5 | ||
|
|
cfa360bb09 | ||
|
|
d87501212f | ||
|
|
493d79d33d | ||
|
|
0964c1423b | ||
|
|
ec12ba4e0e | ||
|
|
d0fdb79821 | ||
|
|
4dd6586cb4 | ||
|
|
3814125986 | ||
|
|
d1b9e0f592 |
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"comments": false,
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": [
|
||||
["env", {
|
||||
"targets": { "node": 7 }
|
||||
}],
|
||||
"stage-0"
|
||||
],
|
||||
"plugins": ["istanbul"]
|
||||
},
|
||||
"main": {
|
||||
"presets": [
|
||||
["env", {
|
||||
"targets": { "node": 7 }
|
||||
}],
|
||||
"stage-0"
|
||||
]
|
||||
},
|
||||
"renderer": {
|
||||
"presets": [
|
||||
["env", {
|
||||
"modules": false
|
||||
}],
|
||||
"stage-0"
|
||||
]
|
||||
},
|
||||
"production": {
|
||||
"presets": [
|
||||
["env", {
|
||||
"modules": false
|
||||
}],
|
||||
"stage-0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"plugins": ["transform-runtime"]
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
'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) => {
|
||||
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'])
|
||||
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()
|
||||
}
|
||||
@@ -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>
|
||||
`
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,178 @@
|
||||
'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)
|
||||
|
||||
const compiler = webpack(rendererConfig)
|
||||
hotMiddleware = webpackHotMiddleware(compiler, {
|
||||
log: false,
|
||||
heartbeat: 2500
|
||||
})
|
||||
|
||||
compiler.plugin('compilation', compilation => {
|
||||
compilation.plugin('html-webpack-plugin-after-emit', (data, cb) => {
|
||||
hotMiddleware.publish({ action: 'reload' })
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
compiler.plugin('done', stats => {
|
||||
logStats('Renderer', stats)
|
||||
})
|
||||
|
||||
const server = new WebpackDevServer(
|
||||
compiler,
|
||||
{
|
||||
contentBase: path.join(__dirname, '../'),
|
||||
quiet: 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)
|
||||
|
||||
const compiler = webpack(mainConfig)
|
||||
|
||||
compiler.plugin('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 () {
|
||||
electronProcess = spawn(electron, ['--inspect=5858', path.join(__dirname, '../dist/electron/main.js')])
|
||||
|
||||
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()
|
||||
@@ -0,0 +1,114 @@
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
|
||||
module.exports = {
|
||||
context: path.resolve(__dirname, '../docs'),
|
||||
entry: './main.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../docs/dist'),
|
||||
filename: "index.js"
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'icons': path.resolve(__dirname, '../build/icons')
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|vue)$/,
|
||||
enforce: 'pre',
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'eslint-loader',
|
||||
options: {
|
||||
formatter: require('eslint-friendly-formatter')
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: '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',
|
||||
}
|
||||
},
|
||||
{
|
||||
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]'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.resolve(__dirname, '../docs/template.html')
|
||||
}),
|
||||
new ExtractTextPlugin("styles.css")
|
||||
]
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
// module.exports.devtool = '#source-map'
|
||||
// http://vue-loader.vuejs.org/en/workflow/production.html
|
||||
module.exports.plugins = (module.exports.plugins || []).concat([
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: '"production"'
|
||||
}
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
sourceMap: true,
|
||||
compress: {
|
||||
warnings: false
|
||||
}
|
||||
}),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: true
|
||||
})
|
||||
])
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
'use strict'
|
||||
|
||||
process.env.BABEL_ENV = 'main'
|
||||
|
||||
const path = require('path')
|
||||
const { dependencies } = require('../package.json')
|
||||
const webpack = require('webpack')
|
||||
|
||||
const babelMinifyWebpackPlugin = require('babel-minify-webpack-plugin')
|
||||
|
||||
let mainConfig = {
|
||||
entry: {
|
||||
main: path.join(__dirname, '../src/main/index.js')
|
||||
},
|
||||
externals: [
|
||||
// ...Object.keys(dependencies || {})
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js)$/,
|
||||
enforce: 'pre',
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'eslint-loader',
|
||||
options: {
|
||||
formatter: require('eslint-friendly-formatter')
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
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 babelMinifyWebpackPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': '"production"'
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = mainConfig
|
||||
@@ -0,0 +1,178 @@
|
||||
'use strict'
|
||||
|
||||
process.env.BABEL_ENV = 'renderer'
|
||||
|
||||
const path = require('path')
|
||||
const { dependencies } = require('../package.json')
|
||||
const webpack = require('webpack')
|
||||
|
||||
const BabiliWebpackPlugin = require('babili-webpack-plugin')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
||||
/**
|
||||
* 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: /\.(js|vue)$/,
|
||||
enforce: 'pre',
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'eslint-loader',
|
||||
options: {
|
||||
formatter: require('eslint-friendly-formatter')
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: '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'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
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 ExtractTextPlugin('styles.css'),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: path.resolve(__dirname, '../src/index.ejs'),
|
||||
minify: {
|
||||
collapseWhitespace: true,
|
||||
removeAttributeQuotes: true,
|
||||
removeComments: true
|
||||
},
|
||||
nodeModules: process.env.NODE_ENV !== 'production'
|
||||
? path.resolve(__dirname, '../node_modules')
|
||||
: false
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
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.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 BabiliWebpackPlugin(),
|
||||
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
|
||||
@@ -0,0 +1,139 @@
|
||||
'use strict'
|
||||
|
||||
process.env.BABEL_ENV = 'web'
|
||||
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
|
||||
const BabiliWebpackPlugin = require('babili-webpack-plugin')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
|
||||
let webConfig = {
|
||||
devtool: '#cheap-module-eval-source-map',
|
||||
entry: {
|
||||
web: path.join(__dirname, '../src/renderer/main.js')
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|vue)$/,
|
||||
enforce: 'pre',
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'eslint-loader',
|
||||
options: {
|
||||
formatter: require('eslint-friendly-formatter')
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: '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'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
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 ExtractTextPlugin('styles.css'),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: path.resolve(__dirname, '../src/index.ejs'),
|
||||
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 BabiliWebpackPlugin(),
|
||||
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
|
||||
@@ -0,0 +1,3 @@
|
||||
test/unit/coverage/**
|
||||
test/unit/*.js
|
||||
test/e2e/*.js
|
||||
@@ -0,0 +1,26 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: 'babel-eslint',
|
||||
parserOptions: {
|
||||
sourceType: 'module'
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true
|
||||
},
|
||||
extends: 'standard',
|
||||
globals: {
|
||||
__static: true
|
||||
},
|
||||
plugins: [
|
||||
'html'
|
||||
],
|
||||
'rules': {
|
||||
// allow paren-less arrow functions
|
||||
'arrow-parens': 0,
|
||||
// allow async-await
|
||||
'generator-star-spacing': 0,
|
||||
// allow debugger during development
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
.DS_Store
|
||||
dist/electron/*
|
||||
dist/web/*
|
||||
build/*
|
||||
!build/icons
|
||||
coverage
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
npm-debug.log.*
|
||||
thumbs.db
|
||||
!.gitkeep
|
||||
yarn-error.log
|
||||
docs/dist/
|
||||
@@ -0,0 +1,52 @@
|
||||
# Commented sections below can be used to run tests on the CI server
|
||||
# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
|
||||
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
|
||||
#- xvfb
|
||||
before_install:
|
||||
- mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([
|
||||
"$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz
|
||||
| tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull
|
||||
# - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi
|
||||
install:
|
||||
#- export DISPLAY=':99.0'
|
||||
#- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
|
||||
- nvm install 8.9
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash
|
||||
- source ~/.bashrc
|
||||
- npm install -g xvfb-maybe
|
||||
- yarn
|
||||
script:
|
||||
#- xvfb-maybe node_modules/.bin/karma start test/unit/karma.conf.js
|
||||
#- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e
|
||||
- npm run release
|
||||
- yarn run build:docs
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
after_script:
|
||||
- cd docs/dist
|
||||
- git init
|
||||
- git config user.name "Molunerfinn"
|
||||
- git config user.email "marksz@teamsz.xyz"
|
||||
- git add .
|
||||
- git commit -m "Travis build docs"
|
||||
- git push --force --quiet "https://${GH_TOKEN}@github.com/Molunerfinn/PicGo.git" master:gh-pages
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach",
|
||||
"type": "node",
|
||||
"request": "attach",
|
||||
"port": 5858,
|
||||
"sourceMaps": false,
|
||||
"outFiles": [],
|
||||
"localRoot": "${workspaceRoot}",
|
||||
"remoteRoot": null
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"eslint.enable": true,
|
||||
"eslint.autoFixOnSave": true
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
MIT License
|
||||
Copyright (c) <year> <copyright holders>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,41 @@
|
||||
# PicGo
|
||||
|
||||
> 图片上传+管理新体验
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/12621342/33876119-85a5148e-df5f-11e7-8843-46224e595d52.png" alt="">
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/feross/standard">
|
||||
<img src="https://img.shields.io/badge/code%20style-standard-green.svg?style=flat-square" alt="">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 精致设计
|
||||
|
||||

|
||||
|
||||
macOS系统下,支持拖拽至menubar图标实现上传。menubar app 窗口显示最新上传的5张图片以及剪贴板里的图片。点击图片自动将上传的链接复制到剪贴板。
|
||||
|
||||
## 便捷管理
|
||||
|
||||

|
||||
|
||||
查看你的上传记录,重复使用更方便。支持点击图片大图查看。支持删除图片(仅本地记录),让界面更加干净。
|
||||
|
||||
## 可选图床
|
||||
|
||||

|
||||
|
||||
目前支持微博图床和七牛图床。未来将支持更多。方便不同图床的上传需求。
|
||||
|
||||
## 多样链接
|
||||
|
||||

|
||||
|
||||
支持4种剪贴板链接格式,让你的文本编辑游刃有余。
|
||||
|
||||
## License
|
||||
|
||||
[MIT](http://opensource.org/licenses/MIT)
|
||||
|
||||
Copyright (c) 2017 Molunerfinn
|
||||
@@ -0,0 +1,33 @@
|
||||
# Commented sections below can be used to run tests on the CI server
|
||||
# https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
|
||||
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
|
||||
- choco install yarn --ignore-dependencies
|
||||
- git reset --hard HEAD
|
||||
- yarn
|
||||
- node --version
|
||||
|
||||
build_script:
|
||||
#- yarn test
|
||||
- yarn build
|
||||
|
||||
test: off
|
||||
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,185 @@
|
||||
<template lang='pug'>
|
||||
#app(v-cloak)
|
||||
#header
|
||||
.mask
|
||||
img.logo(src="~icons/256x256.png", alt="PicGo")
|
||||
h1.title PicGo
|
||||
h2.desc 图片上传+管理新体验
|
||||
button.download(@click="goLink('https://github.com/Molunerfinn/picgo/releases')") 免费下载Mac版
|
||||
button.download(@click="goLink('https://github.com/Molunerfinn/picgo')") 在github上查看
|
||||
h3.desc
|
||||
| 基于#[a(href="https://github.com/SimulatedGREG/electron-vue" target="_blank") electron-vue]开发
|
||||
#container.container-fluid
|
||||
.row.ex-width
|
||||
img.gallery.col-xs-10.col-xs-offset-1.col-md-offset-2.col-md-8(src="https://ws1.sinaimg.cn/large/8700af19gy1fmayjwttnbj218g0p0q4e")
|
||||
.row.ex-width.display-list
|
||||
.display-list__item(v-for="(item, index) in itemList" :key="index" :class="{ 'o-item': index % 2 !== 0 }")
|
||||
.col-xs-10.col-xs-offset-1.col-md-7.col-md-offset-0
|
||||
img(:src="item.url")
|
||||
.col-xs-10.col-xs-offset-1.col-md-5.col-md-offset-0.display-list__content
|
||||
.display-list__title {{ item.title }}
|
||||
.display-list__desc {{ item.desc }}
|
||||
.row.ex-width.info
|
||||
.col-xs-10.col-xs-offset-1
|
||||
| ©2017 #[a(href="https://github.com/Molunerfinn" target="_blank") Molunerfinn]
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: '',
|
||||
data () {
|
||||
return {
|
||||
itemList: [
|
||||
{
|
||||
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fma907llb5j20m30ed46a',
|
||||
title: '精致设计',
|
||||
desc: 'macOS系统下,支持拖拽至menubar图标实现上传。menubar app 窗口显示最新上传的5张图片以及剪贴板里的图片。点击图片自动将上传的链接复制到剪贴板。'
|
||||
},
|
||||
{
|
||||
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fmd56zm2nej218g0p0teb',
|
||||
title: '便捷管理',
|
||||
desc: '查看你的上传记录,重复使用更方便。支持点击图片大图查看。支持删除图片(仅本地记录),让界面更加干净。'
|
||||
},
|
||||
{
|
||||
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fmd5ck9m0wj20lr0cxmzs',
|
||||
title: '可选图床',
|
||||
desc: '目前支持微博图床和七牛图床。未来将支持更多。方便不同图床的上传需求。'
|
||||
},
|
||||
{
|
||||
url: 'https://ws1.sinaimg.cn/large/8700af19gy1fmayjwttnbj218g0p0q4e',
|
||||
title: '多样链接',
|
||||
desc: '支持4种剪贴板链接格式,让你的文本编辑游刃有余。'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goLink (link) {
|
||||
window.open(link, '_blank')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
[v-cloak]
|
||||
display none
|
||||
*
|
||||
box-sizing border-box
|
||||
body,
|
||||
html,
|
||||
h1
|
||||
margin 0
|
||||
padding 0
|
||||
font-family "Source Sans Pro","Helvetica Neue","PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif
|
||||
#app
|
||||
position relative
|
||||
.mask
|
||||
position absolute
|
||||
width 100vw
|
||||
height 100vh
|
||||
top 0
|
||||
left 0
|
||||
background rgba(0,0,0, 0.7)
|
||||
z-index -1
|
||||
#header
|
||||
height 100vh
|
||||
width 100vw
|
||||
background-image url("https://ws1.sinaimg.cn/large/8700af19ly1fm9ru6fqvjj22p81stdta")
|
||||
background-attachment fixed
|
||||
background-size cover
|
||||
background-position center
|
||||
text-align center
|
||||
padding 15vh
|
||||
position relative
|
||||
z-index 2
|
||||
.logo
|
||||
width 120px
|
||||
.title
|
||||
color #4BA2E2
|
||||
font-size 36px
|
||||
font-weight 300
|
||||
margin 10px auto
|
||||
.desc
|
||||
font-weight 400
|
||||
margin 20px auto 10px
|
||||
color #ddd
|
||||
a
|
||||
text-decoration none
|
||||
color #4BA2E2
|
||||
.download
|
||||
display inline-block
|
||||
line-height 1
|
||||
white-space nowrap
|
||||
cursor pointer
|
||||
background transparent
|
||||
border 1px solid #d8dce5
|
||||
color #ddd
|
||||
-webkit-appearance none
|
||||
text-align center
|
||||
box-sizing border-box
|
||||
outline none
|
||||
margin 20px 12px
|
||||
transition .1s
|
||||
font-weight 500
|
||||
user-select none
|
||||
padding 12px 20px
|
||||
font-size 14px
|
||||
border-radius 20px
|
||||
padding 12px 23px
|
||||
transition .2s all ease-in-out
|
||||
&:hover
|
||||
background #ddd
|
||||
color rgba(0,0,0, 0.7)
|
||||
#container
|
||||
position relative
|
||||
text-align center
|
||||
margin-top -10vh
|
||||
z-index 3
|
||||
.gallery
|
||||
margin-bottom 60px
|
||||
cursor pointer
|
||||
transition all .2s ease-in-out
|
||||
&:hover
|
||||
transform scale(1.05)
|
||||
.display-list
|
||||
&__item
|
||||
padding 48px
|
||||
text-align left
|
||||
background #2E2E2E
|
||||
overflow hidden
|
||||
&.o-item
|
||||
background #fff
|
||||
.display-list__desc
|
||||
color #2E2E2E
|
||||
img
|
||||
width 100%
|
||||
cursor pointer
|
||||
transition all .2s ease-in-out
|
||||
&:hover
|
||||
transform scale(1.05)
|
||||
&__content
|
||||
padding-top 120px
|
||||
&__title
|
||||
color #4BA2E2
|
||||
font-size 50px
|
||||
&__desc
|
||||
color #fff
|
||||
margin-top 20px
|
||||
.info
|
||||
padding 48px 0
|
||||
background #2E2E2E
|
||||
color #fff
|
||||
a
|
||||
text-decoration none
|
||||
color #fff
|
||||
@media (max-width: 768px)
|
||||
#container
|
||||
.display-list
|
||||
&__item
|
||||
padding 24px 12px
|
||||
&__content
|
||||
padding-top 30px
|
||||
&__title
|
||||
font-size 25px
|
||||
&__desc
|
||||
margin-top 12px
|
||||
</style>
|
||||
@@ -0,0 +1,7 @@
|
||||
import Vue from 'vue'
|
||||
import App from './APP.vue'
|
||||
import 'melody.css'
|
||||
|
||||
new Vue({
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>PicGo</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 16 KiB |
@@ -1 +0,0 @@
|
||||
<!DOCTYPE html> <html lang=en> <head> <meta charset=UTF-8> <meta name=viewport content="width=device-width,initial-scale=1"> <meta http-equiv=X-UA-Compatible content="ie=edge"> <title>PicGo</title> </head> <body> <div id=app></div> <script type="text/javascript" src="index.js"></script></body> </html>
|
||||
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"name": "picgo",
|
||||
"version": "1.0.2",
|
||||
"author": "Molunerfinn <marksz@teamsz.xyz>",
|
||||
"description": "Easy to upload your pic & copy to write",
|
||||
"license": "MIT",
|
||||
"main": "./dist/electron/main.js",
|
||||
"scripts": {
|
||||
"build": "node .electron-vue/build.js && electron-builder",
|
||||
"release": "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",
|
||||
"e2e": "npm run pack && mocha test/e2e",
|
||||
"lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src test",
|
||||
"lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test",
|
||||
"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",
|
||||
"test": "npm run unit && npm run e2e",
|
||||
"unit": "karma start test/unit/karma.conf.js",
|
||||
"postinstall": "npm run lint:fix",
|
||||
"build:docs": "cross-env NODE_ENV=production webpack -p --config .electron-vue/webpack.docs.config.js",
|
||||
"docs": "webpack-dev-server --content-base docs/dist --config .electron-vue/webpack.docs.config.js --hot --inline",
|
||||
"patch": "npm version patch && git push origin master && git push origin --tags",
|
||||
"minor": "npm version minor && git push origin master && git push origin --tags",
|
||||
"major": "npm version major && git push origin master && git push origin --tags"
|
||||
},
|
||||
"build": {
|
||||
"productName": "PicGo",
|
||||
"appId": "org.simulatedgreg.electron-vue",
|
||||
"directories": {
|
||||
"output": "build"
|
||||
},
|
||||
"files": [
|
||||
"dist/electron/**/*"
|
||||
],
|
||||
"dmg": {
|
||||
"contents": [
|
||||
{
|
||||
"x": 410,
|
||||
"y": 150,
|
||||
"type": "link",
|
||||
"path": "/Applications"
|
||||
},
|
||||
{
|
||||
"x": 130,
|
||||
"y": 150,
|
||||
"type": "file"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mac": {
|
||||
"icon": "build/icons/icon.icns",
|
||||
"extendInfo": {
|
||||
"LSUIElement": 1
|
||||
}
|
||||
},
|
||||
"win": {
|
||||
"icon": "build/icons/icon.ico"
|
||||
},
|
||||
"linux": {
|
||||
"icon": "build/icons"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.16.1",
|
||||
"element-ui": "^2.0.5",
|
||||
"fs-extra": "^4.0.2",
|
||||
"image-size": "^0.6.1",
|
||||
"lodash-id": "^0.14.0",
|
||||
"lowdb": "^1.0.0",
|
||||
"melody.css": "^1.0.2",
|
||||
"qiniu": "^7.1.1",
|
||||
"request": "^2.83.0",
|
||||
"request-promise": "^4.2.2",
|
||||
"vue": "^2.3.3",
|
||||
"vue-electron": "^1.0.6",
|
||||
"vue-gallery": "^1.2.4",
|
||||
"vue-router": "^2.5.3",
|
||||
"vuex": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.25.0",
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-loader": "^7.1.1",
|
||||
"babel-minify-webpack-plugin": "^0.2.0",
|
||||
"babel-plugin-istanbul": "^4.1.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"babel-register": "^6.24.1",
|
||||
"babili-webpack-plugin": "^0.1.2",
|
||||
"cfonts": "^1.1.3",
|
||||
"chai": "^4.0.0",
|
||||
"chalk": "^2.1.0",
|
||||
"copy-webpack-plugin": "^4.0.1",
|
||||
"cross-env": "^5.0.5",
|
||||
"css-loader": "^0.28.4",
|
||||
"del": "^3.0.0",
|
||||
"devtron": "^1.4.0",
|
||||
"electron": "^1.7.5",
|
||||
"electron-builder": "^19.19.1",
|
||||
"electron-debug": "^1.4.0",
|
||||
"electron-devtools-installer": "^2.2.0",
|
||||
"eslint": "^4.4.1",
|
||||
"eslint-config-standard": "^10.2.1",
|
||||
"eslint-friendly-formatter": "^3.0.0",
|
||||
"eslint-loader": "^1.9.0",
|
||||
"eslint-plugin-html": "^3.1.1",
|
||||
"eslint-plugin-import": "^2.7.0",
|
||||
"eslint-plugin-node": "^5.1.1",
|
||||
"eslint-plugin-promise": "^3.5.0",
|
||||
"eslint-plugin-standard": "^3.0.1",
|
||||
"extract-text-webpack-plugin": "^3.0.0",
|
||||
"file-loader": "^0.11.2",
|
||||
"html-webpack-plugin": "^2.30.1",
|
||||
"inject-loader": "^3.0.0",
|
||||
"karma": "^1.3.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-coverage": "^1.1.1",
|
||||
"karma-electron": "^5.1.1",
|
||||
"karma-mocha": "^1.2.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-spec-reporter": "^0.0.31",
|
||||
"karma-webpack": "^2.0.1",
|
||||
"mocha": "^3.0.2",
|
||||
"multispinner": "^0.2.1",
|
||||
"node-loader": "^0.6.0",
|
||||
"pug": "^2.0.0-rc.4",
|
||||
"pug-loader": "^2.3.0",
|
||||
"require-dir": "^0.3.0",
|
||||
"spectron": "^3.7.1",
|
||||
"style-loader": "^0.18.2",
|
||||
"stylus": "^0.54.5",
|
||||
"stylus-loader": "^3.0.1",
|
||||
"url-loader": "^0.5.9",
|
||||
"vue-html-loader": "^1.2.4",
|
||||
"vue-loader": "^13.0.5",
|
||||
"vue-style-loader": "^3.0.1",
|
||||
"vue-template-compiler": "^2.4.2",
|
||||
"webpack": "^3.5.2",
|
||||
"webpack-dev-server": "^2.7.1",
|
||||
"webpack-hot-middleware": "^2.18.2",
|
||||
"webpack-merge": "^4.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import Datastore from 'lowdb'
|
||||
import LodashId from 'lodash-id'
|
||||
import FileSync from 'lowdb/adapters/FileSync'
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import { remote, app } from 'electron'
|
||||
|
||||
const APP = process.type === 'renderer' ? remote.app : app
|
||||
const STORE_PATH = APP.getPath('userData')
|
||||
|
||||
if (process.type !== 'renderer') {
|
||||
if (!fs.pathExistsSync(STORE_PATH)) {
|
||||
fs.mkdirpSync(STORE_PATH)
|
||||
}
|
||||
}
|
||||
|
||||
const adapter = new FileSync(path.join(STORE_PATH, '/data.json'))
|
||||
|
||||
const db = Datastore(adapter)
|
||||
db._.mixin(LodashId)
|
||||
|
||||
if (!db.has('uploaded').value()) {
|
||||
db.set('uploaded', []).write()
|
||||
}
|
||||
|
||||
if (!db.has('picBed').value()) {
|
||||
db.set('picBed', {
|
||||
current: 'weibo'
|
||||
}).write()
|
||||
}
|
||||
|
||||
export default db
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="referrer" content="never">
|
||||
<title>PicGo</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 -->
|
||||
<script>
|
||||
if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
|
||||
</script>
|
||||
|
||||
<!-- webpack builds are automatically injected -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 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 */
|
||||
|
||||
// Set environment for development
|
||||
process.env.NODE_ENV = 'development'
|
||||
|
||||
// Install `electron-debug` with `devtron`
|
||||
require('electron-debug')({ showDevTools: false })
|
||||
|
||||
// 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')
|
||||
@@ -0,0 +1,268 @@
|
||||
'use strict'
|
||||
|
||||
import uploader from './utils/uploader.js'
|
||||
import { app, BrowserWindow, Tray, Menu, Notification, clipboard, ipcMain } from 'electron'
|
||||
import db from '../datastore'
|
||||
import pasteTemplate from './utils/pasteTemplate'
|
||||
/**
|
||||
* 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 window
|
||||
let settingWindow
|
||||
let tray
|
||||
const winURL = process.env.NODE_ENV === 'development'
|
||||
? `http://localhost:9080`
|
||||
: `file://${__dirname}/index.html`
|
||||
const settingWinURL = process.env.NODE_ENV === 'development'
|
||||
? `http://localhost:9080/#setting/upload`
|
||||
: `file://${__dirname}/index.html#setting/upload`
|
||||
|
||||
function createTray () {
|
||||
tray = new Tray(`${__static}/menubar.png`)
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '打开详细窗口',
|
||||
click () {
|
||||
if (settingWindow === null) {
|
||||
createSettingWindow()
|
||||
settingWindow.show()
|
||||
} else {
|
||||
settingWindow.show()
|
||||
settingWindow.focus()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '选择默认图床',
|
||||
type: 'submenu',
|
||||
submenu: [
|
||||
{
|
||||
label: '微博图床',
|
||||
type: 'radio',
|
||||
checked: db.read().get('picBed.current').value() === 'weibo',
|
||||
click () {
|
||||
db.read().set('picBed.current', 'weibo')
|
||||
.write()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '七牛图床',
|
||||
type: 'radio',
|
||||
checked: db.read().get('picBed.current').value() === 'qiniu',
|
||||
click () {
|
||||
db.read().set('picBed.current', 'qiniu')
|
||||
.write()
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
role: 'quit',
|
||||
label: '退出'
|
||||
}
|
||||
])
|
||||
tray.on('right-click', () => {
|
||||
window.hide()
|
||||
tray.popUpContextMenu(contextMenu)
|
||||
})
|
||||
tray.on('click', () => {
|
||||
let img = clipboard.readImage()
|
||||
let obj = []
|
||||
if (!img.isEmpty()) {
|
||||
// 从剪贴板来的图片默认转为png
|
||||
const imgUrl = 'data:image/png;base64,' + Buffer.from(img.toPNG(), 'binary').toString('base64')
|
||||
obj.push({
|
||||
width: img.getSize().width,
|
||||
height: img.getSize().height,
|
||||
imgUrl
|
||||
})
|
||||
}
|
||||
toggleWindow()
|
||||
setTimeout(() => {
|
||||
window.webContents.send('clipboardFiles', obj)
|
||||
}, 0)
|
||||
})
|
||||
|
||||
tray.on('drag-enter', () => {
|
||||
tray.setImage(`${__static}/upload.png`)
|
||||
})
|
||||
|
||||
tray.on('drag-end', () => {
|
||||
tray.setImage(`${__static}/menubar.png`)
|
||||
})
|
||||
|
||||
tray.on('drop-files', async (event, files) => {
|
||||
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
const imgs = await uploader(files, 'imgFromPath', window.webContents)
|
||||
for (let i in imgs) {
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, imgs[i].imgUrl))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: imgs[i].imgUrl,
|
||||
icon: files[i]
|
||||
})
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
}
|
||||
window.webContents.send('dragFiles', imgs)
|
||||
})
|
||||
// toggleWindow()
|
||||
}
|
||||
|
||||
const createWindow = () => {
|
||||
window = new BrowserWindow({
|
||||
height: 350,
|
||||
width: 196, // 196
|
||||
show: false,
|
||||
frame: false,
|
||||
fullscreenable: false,
|
||||
resizable: false,
|
||||
transparent: true,
|
||||
vibrancy: 'ultra-dark',
|
||||
webPreferences: {
|
||||
backgroundThrottling: false
|
||||
}
|
||||
})
|
||||
|
||||
window.loadURL(winURL)
|
||||
|
||||
window.on('closed', () => {
|
||||
window = null
|
||||
})
|
||||
|
||||
window.on('blur', () => {
|
||||
window.hide()
|
||||
})
|
||||
|
||||
createSettingWindow()
|
||||
}
|
||||
|
||||
const createSettingWindow = () => {
|
||||
settingWindow = new BrowserWindow({
|
||||
height: 450,
|
||||
width: 800,
|
||||
show: false,
|
||||
frame: true,
|
||||
center: true,
|
||||
fullscreenable: false,
|
||||
resizable: false,
|
||||
title: 'Pic',
|
||||
vibrancy: 'ultra-dark',
|
||||
transparent: true,
|
||||
titleBarStyle: 'hidden',
|
||||
webPreferences: {
|
||||
backgroundThrottling: false
|
||||
}
|
||||
})
|
||||
|
||||
settingWindow.loadURL(settingWinURL)
|
||||
|
||||
settingWindow.on('closed', () => {
|
||||
settingWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
const getWindowPosition = () => {
|
||||
const windowBounds = window.getBounds()
|
||||
const trayBounds = tray.getBounds()
|
||||
const x = Math.round(trayBounds.x + (trayBounds.width / 2) - (windowBounds.width / 2))
|
||||
const y = Math.round(trayBounds.y + trayBounds.height - 10)
|
||||
|
||||
return {
|
||||
x,
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
const toggleWindow = () => {
|
||||
if (window.isVisible()) {
|
||||
window.hide()
|
||||
} else {
|
||||
showWindow()
|
||||
}
|
||||
}
|
||||
|
||||
const showWindow = () => {
|
||||
const position = getWindowPosition()
|
||||
window.setPosition(position.x, position.y, false)
|
||||
window.webContents.send('updateFiles')
|
||||
window.show()
|
||||
window.focus()
|
||||
}
|
||||
|
||||
ipcMain.on('uploadClipboardFiles', async (evt, file) => {
|
||||
const img = await uploader(file, 'imgFromClipboard', window.webContents)
|
||||
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, img[0].imgUrl))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: img[0].imgUrl,
|
||||
icon: file[0]
|
||||
})
|
||||
notification.show()
|
||||
window.webContents.send('clipboardFiles', [])
|
||||
window.webContents.send('uploadFiles', img)
|
||||
})
|
||||
|
||||
ipcMain.on('uploadChoosedFiles', async (evt, files) => {
|
||||
const imgs = await uploader(files, 'imgFromUploader', settingWindow.webContents)
|
||||
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
let pasteText = ''
|
||||
for (let i in imgs) {
|
||||
pasteText += pasteTemplate(pasteStyle, imgs[i].imgUrl) + '\r\n'
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: imgs[i].imgUrl,
|
||||
icon: files[i].path
|
||||
})
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
}
|
||||
clipboard.writeText(pasteText)
|
||||
window.webContents.send('uploadFiles', imgs)
|
||||
})
|
||||
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
createTray()
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (window === null || settingWindow === null) {
|
||||
createWindow()
|
||||
createTray()
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 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()
|
||||
// }
|
||||
// })
|
||||
@@ -0,0 +1,55 @@
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import sizeOf from 'image-size'
|
||||
|
||||
const imgFromPath = async (imgPath) => {
|
||||
let results = []
|
||||
await Promise.all(imgPath.map(async item => {
|
||||
let buffer = await fs.readFile(item)
|
||||
let base64Image = Buffer.from(buffer, 'binary').toString('base64')
|
||||
let fileName = path.basename(item)
|
||||
let imgSize = sizeOf(item)
|
||||
results.push({
|
||||
base64Image,
|
||||
fileName,
|
||||
width: imgSize.width,
|
||||
height: imgSize.height
|
||||
})
|
||||
}))
|
||||
return results
|
||||
}
|
||||
|
||||
const imgFromClipboard = (file) => {
|
||||
let result = []
|
||||
const today = new Date().toLocaleString().replace(/[ ]+/g, '-')
|
||||
result.push({
|
||||
base64Image: file.imgUrl.replace(/^data\S+,/, ''),
|
||||
fileName: `${today}.png`,
|
||||
width: file.width,
|
||||
height: file.height
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
const imgFromUploader = async (files) => {
|
||||
let results = []
|
||||
await Promise.all(files.map(async item => {
|
||||
let buffer = await fs.readFile(item.path)
|
||||
let base64Image = Buffer.from(buffer, 'binary').toString('base64')
|
||||
let fileName = item.name
|
||||
let imgSize = sizeOf(item.path)
|
||||
results.push({
|
||||
base64Image,
|
||||
fileName,
|
||||
width: imgSize.width,
|
||||
height: imgSize.height
|
||||
})
|
||||
}))
|
||||
return results
|
||||
}
|
||||
|
||||
export {
|
||||
imgFromPath,
|
||||
imgFromClipboard,
|
||||
imgFromUploader
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export default (style, url) => {
|
||||
const tpl = {
|
||||
'markdown': ``,
|
||||
'HTML': `<img src="${url}"/>`,
|
||||
'URL': url,
|
||||
'UBB': `[IMG]${url}[/IMG]`
|
||||
}
|
||||
return tpl[style]
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import request from 'request-promise'
|
||||
import * as img2Base64 from './img2base64'
|
||||
import db from '../../datastore/index'
|
||||
import * as qiniu from 'qiniu'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
function postOptions (fileName, token, imgBase64) {
|
||||
const area = selectArea(db.read().get('picBed.qiniu.area').value() || 'z0')
|
||||
const base64FileName = Buffer.from(fileName).toString('base64')
|
||||
return {
|
||||
method: 'POST',
|
||||
url: `http://upload${area}.qiniu.com/putb64/-1/key/${base64FileName}`,
|
||||
headers: {
|
||||
Authorization: `UpToken ${token}`,
|
||||
contentType: 'application/octet-stream'
|
||||
},
|
||||
body: imgBase64
|
||||
}
|
||||
}
|
||||
|
||||
function selectArea (area) {
|
||||
return area === 'z0' ? '' : '-' + area
|
||||
}
|
||||
|
||||
function getToken () {
|
||||
const accessKey = db.read().get('picBed.qiniu.accessKey').value()
|
||||
const secretKey = db.read().get('picBed.qiniu.secretKey').value()
|
||||
const mac = new qiniu.auth.digest.Mac(accessKey, secretKey)
|
||||
const options = {
|
||||
scope: db.read().get('picBed.qiniu.bucket').value()
|
||||
}
|
||||
const putPolicy = new qiniu.rs.PutPolicy(options)
|
||||
return putPolicy.uploadToken(mac)
|
||||
}
|
||||
|
||||
const qiniuUpload = async function (img, type, webContents) {
|
||||
try {
|
||||
webContents.send('uploadProgress', 0)
|
||||
const imgList = await img2Base64[type](img)
|
||||
webContents.send('uploadProgress', 30)
|
||||
const length = imgList.length
|
||||
for (let i in imgList) {
|
||||
const options = postOptions(imgList[i].fileName, getToken(), imgList[i].base64Image)
|
||||
const res = await request(options)
|
||||
const body = JSON.parse(res)
|
||||
if (body.key) {
|
||||
delete imgList[i].base64Image
|
||||
const baseUrl = db.get('picBed.qiniu.url').value()
|
||||
const options = db.get('picBed.qiniu.options').value()
|
||||
imgList[i]['imgUrl'] = `${baseUrl}/${body.key}${options}`
|
||||
imgList[i]['type'] = 'qiniu'
|
||||
if (i - length === -1) {
|
||||
webContents.send('uploadProgress', 60)
|
||||
}
|
||||
} else {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: res.body.msg
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
}
|
||||
webContents.send('uploadProgress', 100)
|
||||
return imgList
|
||||
} catch (err) {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const error = JSON.parse(err.response.body)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: error.error
|
||||
})
|
||||
notification.show()
|
||||
throw new Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
export default qiniuUpload
|
||||
@@ -0,0 +1,14 @@
|
||||
import weiboUpload from './weiboUpload'
|
||||
import qiniuUpload from './qiniuUpload'
|
||||
import db from '../../datastore/index'
|
||||
const uploader = (img, type, webContents) => {
|
||||
const uploadType = db.read().get('picBed.current').value()
|
||||
switch (uploadType) {
|
||||
case 'weibo':
|
||||
return weiboUpload(img, type, webContents)
|
||||
case 'qiniu':
|
||||
return qiniuUpload(img, type, webContents)
|
||||
}
|
||||
}
|
||||
|
||||
export default uploader
|
||||
@@ -0,0 +1,73 @@
|
||||
import request from 'request-promise'
|
||||
import * as img2Base64 from './img2base64'
|
||||
import db from '../../datastore/index'
|
||||
import { Notification } from 'electron'
|
||||
const j = request.jar()
|
||||
const rp = request.defaults({jar: j})
|
||||
const UPLOAD_URL = 'http://picupload.service.weibo.com/interface/pic_upload.php?ori=1&mime=image%2Fjpeg&data=base64&url=0&markpos=1&logo=&nick=0&marks=1&app=miniblog'
|
||||
|
||||
function postOptions (formData) {
|
||||
return {
|
||||
method: 'POST',
|
||||
url: 'https://passport.weibo.cn/sso/login',
|
||||
headers: {
|
||||
Referer: 'https://passport.weibo.cn/signin/login',
|
||||
contentType: 'application/x-www-form-urlencoded'
|
||||
},
|
||||
formData,
|
||||
json: true,
|
||||
resolveWithFullResponse: true
|
||||
}
|
||||
}
|
||||
|
||||
const weiboUpload = async function (img, type, webContents) {
|
||||
try {
|
||||
webContents.send('uploadProgress', 0)
|
||||
const formData = {
|
||||
username: db.read().get('picBed.weibo.username').value(),
|
||||
password: db.read().get('picBed.weibo.password').value()
|
||||
}
|
||||
const quality = db.read().get('picBed.weibo.quality').value()
|
||||
const options = postOptions(formData)
|
||||
const res = await rp(options)
|
||||
webContents.send('uploadProgress', 30)
|
||||
if (res.body.retcode === 20000000) {
|
||||
for (let i in res.body.data.crossdomainlist) {
|
||||
await rp.get(res.body.data.crossdomainlist[i])
|
||||
}
|
||||
webContents.send('uploadProgress', 60)
|
||||
const imgList = await img2Base64[type](img)
|
||||
for (let i in imgList) {
|
||||
let result = await rp.post(UPLOAD_URL, {
|
||||
formData: {
|
||||
b64_data: imgList[i].base64Image
|
||||
}
|
||||
})
|
||||
result = result.replace(/<.*?\/>/, '').replace(/<(\w+).*?>.*?<\/\1>/, '')
|
||||
delete imgList[i].base64Image
|
||||
const resTextJson = JSON.parse(result)
|
||||
imgList[i]['imgUrl'] = `https://ws1.sinaimg.cn/${quality}/${resTextJson.data.pics.pic_1.pid}`
|
||||
imgList[i]['type'] = 'weibo'
|
||||
}
|
||||
webContents.send('uploadProgress', 100)
|
||||
return imgList
|
||||
} else {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: res.body.msg
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
} catch (err) {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: '服务端出错,请重试'
|
||||
})
|
||||
notification.show()
|
||||
throw new Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
export default weiboUpload
|
||||
@@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'picgo'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
body,
|
||||
html
|
||||
padding 0
|
||||
margin 0
|
||||
height 100%
|
||||
font-family "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif
|
||||
#app
|
||||
overflow-x hidden
|
||||
user-select none
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
@font-face {font-family: "iconfont";
|
||||
src: url('iconfont.eot?t=1512627299425'); /* IE9*/
|
||||
src: url('iconfont.eot?t=1512627299425#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAccAAsAAAAACgAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFZW7kgJY21hcAAAAYAAAABtAAABqjJq0p5nbHlmAAAB8AAAAyIAAAOsU27YCmhlYWQAAAUUAAAALwAAADYPvT7DaGhlYQAABUQAAAAcAAAAJAfeA4ZobXR4AAAFYAAAABMAAAAUE+kAAGxvY2EAAAV0AAAADAAAAAwB+gL0bWF4cAAABYAAAAAfAAAAIAEVAHluYW1lAAAFoAAAAUUAAAJtPlT+fXBvc3QAAAboAAAAMwAAAETD3CS1eJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2Bk/s04gYGVgYOpk+kMAwNDP4RmfM1gxMjBwMDEwMrMgBUEpLmmMDgwVDyTYW7438AQw9zA0AAUZgTJAQAmaQyLeJzFkdENgCAMRK+Chhg3gBEcwE+H8csJmLhr4FH64wQceXC9lEAAwAogkJNEQF4Iuh6mYnnAbnnExTpxLFyripbW3GVzXWIdyVzkXvbKhmmSeUf/ddh8e8WXRnV4RXV6rnnQf0XLAMsHNt4SBQAAAHicTVLNixxFFK9X1VU9M91dPd3TUz0f2/O5272ym57dnd7uxI09ugkJmsAoS+IOOWzChkAgehA2e0nIgIgfRIyXRT2tokhELznoJScRvMx/4MH4cQ0hggERR6tXFlI8fnT96r2uer/3QxShf38h90kFldA8WkYn0csIAVuADscetIMoxAtQbtOy63ASdIO22u2E5DlwO8wRK3Hku0xlJnBoQL+9EgchDmA1SvEarAgPoFqvbdhzMza5A4VK0Hhr+hL+DMrN7oyZHpm+uDhwVlql3K5u21Xbvp1jlOYwVkwOr7kiT/MFNv2cmrXy/eYzuAl6NaidHRmtun3pneh1b87NA4zHUKq3+JcDq2bJuFETJbuqFo1cpWZ0Zx3Y/V2rlHTP/w3JxWSvP5InZIQECtEZ9CraRtdktw7r+EHSDSFQV0OI4oFsxl1WO/5qlEiSdTMy6Wdk2WFq7DZA5QchT5PYTmKhMjUFyVNJyrwUZCGRp/JDbjkEh3KRP7ngAIDfvZLL63o+d+MLsqdxrAx6CtW4JE5vYXO34UchIVc3n/1w+PXfDNx/Th/dxEAWL4aD9vTjW3cV5e6tcYb7J0YYj06sZ3hOeN6i5+G2Zpqacu0DIq/hZY5B+ebNhqkxurxOMJiCY3z5jKKZp5Q8PxWv7R+d7wH5alx8G2qW2lsv6PWZHN8e4uubox2Md0ab16cfwUaabsAB7jePNGUgpEg998kDchHpqIJmUYrQnOy2n0LkSwMxEMiNUeKjgCH3UAmpWcKkUmtQdoR0SZys9iXEPfA7KvwAReO4YVnGcd2aXqBgTiZgUjp9PJlMHyf04b17D+kBPuEEF7ULB4kw0oqY8Nt6S39D7sCqWhJlAX36B4eVEv+whMb3smv2uCasTw0DQWYQ8gh/i5akI2KmCqZ2AjlBN8xm2ciGKyMRcZJCEPn/GyBLEP3ZmPxUqij457+GL/B5j453AAvj5nvM0p3hxvktw3AgJ1pJgW8vOHUu/YUmeKcHCv+em0vPs614TBXz7Hfrr9xpHGtg7C0d+8RQC3bnfcuwDPvmUNN/la/7DxamqC0AAHicY2BkYGAA4oe6X1vi+W2+MnCzMIDANb+/yQj6/wIWBuYEIJeDgQkkCgBOHQtQAHicY2BkYGBu+N/AEMPCAAJAkpEBFbACAEcLAm54nGNhYGBgfsnAwMKAwAAOmwD9AAAAAAAAdgEeAYQB1nicY2BkYGBgZchlYGMAASYg5gJCBob/YD4DABQAAY8AeJxlj01OwzAQhV/6B6QSqqhgh+QFYgEo/RGrblhUavdddN+mTpsqiSPHrdQDcB6OwAk4AtyAO/BIJ5s2lsffvHljTwDc4Acejt8t95E9XDI7cg0XuBeuU38QbpBfhJto41W4Rf1N2MczpsJtdGF5g9e4YvaEd2EPHXwI13CNT+E69S/hBvlbuIk7/Aq30PHqwj7mXle4jUcv9sdWL5xeqeVBxaHJIpM5v4KZXu+Sha3S6pxrW8QmU4OgX0lTnWlb3VPs10PnIhVZk6oJqzpJjMqt2erQBRvn8lGvF4kehCblWGP+tsYCjnEFhSUOjDFCGGSIyujoO1Vm9K+xQ8Jee1Y9zed0WxTU/3OFAQL0z1xTurLSeTpPgT1fG1J1dCtuy56UNJFezUkSskJe1rZUQuoBNmVXjhF6XNGJPyhnSP8ACVpuyAAAAHicY2BigAAuBuyAlZGJkZmRhZGVkY2BsYK1PDUzKZ+ttCAnPzGFtTAzL7OUgQEAX20HdQA=') format('woff'),
|
||||
url('iconfont.ttf?t=1512627299425') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
|
||||
url('iconfont.svg?t=1512627299425#iconfont') format('svg'); /* iOS 4.1- */
|
||||
}
|
||||
|
||||
[class*=" el-icon-ui"], [class^=el-icon-ui] {
|
||||
font-family:"iconfont" !important;
|
||||
font-size:16px;
|
||||
font-style:normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.el-icon-ui-weibo:before { content: "\e61c"; }
|
||||
|
||||
.el-icon-ui-upload:before { content: "\e61b"; }
|
||||
|
||||
.el-icon-ui-qiniu:before { content: "\e601"; }
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<!--
|
||||
2013-9-30: Created.
|
||||
-->
|
||||
<svg>
|
||||
<metadata>
|
||||
Created by iconfont
|
||||
</metadata>
|
||||
<defs>
|
||||
|
||||
<font id="iconfont" horiz-adv-x="1024" >
|
||||
<font-face
|
||||
font-family="iconfont"
|
||||
font-weight="500"
|
||||
font-stretch="normal"
|
||||
units-per-em="1024"
|
||||
ascent="896"
|
||||
descent="-128"
|
||||
/>
|
||||
<missing-glyph />
|
||||
|
||||
<glyph glyph-name="x" unicode="x" horiz-adv-x="1001"
|
||||
d="M281 543q-27 -1 -53 -1h-83q-18 0 -36.5 -6t-32.5 -18.5t-23 -32t-9 -45.5v-76h912v41q0 16 -0.5 30t-0.5 18q0 13 -5 29t-17 29.5t-31.5 22.5t-49.5 9h-133v-97h-438v97zM955 310v-52q0 -23 0.5 -52t0.5 -58t-10.5 -47.5t-26 -30t-33 -16t-31.5 -4.5q-14 -1 -29.5 -0.5
|
||||
t-29.5 0.5h-32l-45 128h-439l-44 -128h-29h-34q-20 0 -45 1q-25 0 -41 9.5t-25.5 23t-13.5 29.5t-4 30v167h911zM163 247q-12 0 -21 -8.5t-9 -21.5t9 -21.5t21 -8.5q13 0 22 8.5t9 21.5t-9 21.5t-22 8.5zM316 123q-8 -26 -14 -48q-5 -19 -10.5 -37t-7.5 -25t-3 -15t1 -14.5
|
||||
t9.5 -10.5t21.5 -4h37h67h81h80h64h36q23 0 34 12t2 38q-5 13 -9.5 30.5t-9.5 34.5q-5 19 -11 39h-368zM336 498v228q0 11 2.5 23t10 21.5t20.5 15.5t34 6h188q31 0 51.5 -14.5t20.5 -52.5v-227h-327z" />
|
||||
|
||||
|
||||
|
||||
<glyph glyph-name="weibo" unicode="" d="M1013.46529415 545.47540804c0-14.8526376-11.46535652-26.88232091-25.58940469-26.8823209-14.12272255 0-25.56953354 12.03100762-25.56953352 26.8823209 0 1.34457994 0.18810806 2.64941756 0.37224319 3.94895792l-0.37224319 0c0 139.92901597-108.14394722 253.3466299-241.5222759 253.3466299-2.53151878 0-4.99680252-0.32587799-7.5296456-0.39211435l0 0.7842274c-12.33966437 1.93142522-21.81663342 13.00334437-21.81663343 26.49020656 0 13.48421356 9.47564343 24.53891019 21.81663343 26.4703354l0 0.02251979c0.06358643 0 0.14571841 0 0.20532929 0 1.16441905 0.17486105 2.34605934 0.36959327 3.56744197 0.36959325 0.39608859 0 0.79085155-0.08610622 1.18164029-0.10862599 0.79217586 0.04371526 1.57640327 0.10862599 2.38712599 0.10862599 161.74962231 0 292.86932347-137.4968504 292.86932348-307.08875061l-0.39608859 0C1013.25863924 548.1235012899999 1013.46529415 546.8199879900001 1013.46529415 545.47540804zM863.34794096 518.59176283c-14.12139824 0-25.56953354 12.03100762-25.56953354 26.8823209 0 3.56081781 0.68487418 6.96797006 1.88770995 10.0730871-2.98854387 64.2960783-52.1948614 115.73982917-113.60042345 118.60649873-2.81765575-1.13130087-5.84726626-1.80292934-9.0384895-1.80292935-14.11874832 0-25.58940468 12.02968331-25.5894047 26.86112543 0 13.50540903 9.47564343 24.55878134 21.81663342 26.49020656l0 0c0.06358643 0 0.12319732 0.0238454 0.18678375 0.0238454 1.18429021 0.17221243 2.3646062 0.36694464 3.58731185 0.36694464 0.39608859 0 0.79085155-0.10862599 1.18164028-0.12849716 0.79217586 0.06358643 1.57640327 0.12849716 2.38712598 0.12849716 92.9614561 0 168.34005063-79.10500064 168.34005064-176.6711449l-0.39078874 0c0.18810806-1.30086468 0.39078875-2.60437798 0.39078874-3.94895792C888.93602132 530.6227704400001 877.47066481 518.59176283 863.34794096 518.59176283zM754.87281718 449.10547541c20.4879504 2.58450683 43.02787472 1.45453025 68.32981684-5.21405452 70.0267688-18.47969046 98.02449494-65.7929995 94.95381909-137.84392388-3.10909257-73.41934842-80.46017767-204.8583047-196.12979627-275.14471646-77.16430135-51.85441078-177.86470788-87.75137581-282.42001895-87.7513758-221.16017403 0-429.07193204 134.69774021-429.07193204 300.83612299 0 2.19239377 0.20797922 4.40730732 0.30070958 6.60367404 0 0.0834563 0.01059709 0.17221243 0.01059709 0.23712316 0.15499118 3.17135339 0.35237202 6.34138245 0.70474535 9.51406017C25.08266092 424.84872796 281.96361331 686.07605117 358.85634901 693.93819893c71.77008594 7.33756332 148.28925417-16.54694094 142.21546312-106.1635069-0.4583494-6.73084688-1.94997206-13.94123735-3.85755316-21.34636133 57.46322827 34.46230593 125.08565114 58.5640622 185.94940747 46.83906271C766.77400333 597.1774775700001 787.13875511 513.92348823 754.87281718 449.10547541zM420.4857885 420.8110152c-165.81383039 0-300.19098985-93.13366851-300.19098984-208.04555373 0-114.89068974 134.37715946-208.02700817 300.19098984-208.02700818 165.77276377 0 300.16979438 93.13631843 300.16979438 208.02700818C720.6569072 327.67734540000004 586.25855228 420.8110152 420.4857885 420.8110152zM387.57869037 58.58919872000001c-86.91018613 0-157.33436782 65.55852496-157.33436783 146.3988943 0 80.84566786 70.4241817 146.39889428 157.33436783 146.39889429 86.90753621 0 157.35291338-65.5519021 157.35291337-146.39889429C544.93160374 124.1490493 474.48622658 58.58919872000001 387.57869037 58.58919872000001zM369.22484715 220.25404044000004c-28.36864372 0-51.34307333-24.08056079-51.34307335-53.76331621 0-29.68407973 22.97442832-53.74741927 51.34307335-53.74741926 28.36731941 0 51.36559312 24.06333954 51.36559313 53.74741926C420.59044028 196.17480396999997 397.59216656 220.25404044000004 369.22484715 220.25404044000004z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="upload" unicode="" d="M452.48 114.24000000000001c0-16.26 12.96-29.76 29.76-29.76h60c16.26 0 29.76 13.44 29.76 29.76S559.04 144 542.24 144h-60a29.64 29.64 0 0 1-29.76-29.76zM32 384c0-264.96 215.04-480 480-480s480 215.04 480 480-215.04 480-480 480-480-215.04-480-480z m60 0c0 231.84 188.16 420 420 420 231.78 0 420-188.16 420-420s-188.22-420-420-420c-231.84 0-420 188.16-420 420z m251.04 64.32A49.38 49.38 0 0 1 332 409.44c1.92-14.4 14.4-25.44 29.28-25.44H452v-150.24c0-16.26 12.96-29.76 29.76-29.76h60a29.64 29.64 0 0 1 29.76 29.76V384h89.28a29.46 29.46 0 0 1 29.22 25.44 49.08 49.08 0 0 1-10.98 38.88l-139.68 160.8c-15.84 17.76-40.8 17.76-56.16 0l-140.16-160.8z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="qiniu" unicode="" d="M986.84985761 629.03723499c30.51745834 67.31792153-8.37734134 41.88670665-8.37734131 41.88670665s-135.83260677-222.29873611-470.32788036-222.29873616c-52.95676399 0-100.82728686 5.68462529-143.91075724 15.25872806-18.84901915 62.53086903-44.28023406 110.70058282-82.8758428 117.88116044 0 0 1.79514556-38.89479969 4.78705255-94.8434706-172.33387907 64.32601459-244.1396622 187.29341849-244.13966223 187.29341611s-29.32069466 36.80046318-16.75468267-18.84901683c9.57410501-42.18589754 81.97827003-208.83515365 268.97249528-286.92394161 8.07815039-105.31514847 19.44739867-218.11006546 34.40693808-255.80810371 30.51745834-77.78959936 68.21549434-84.97017698 182.80555688-84.97017701 114.29087165 0 176.22335882 9.57410501 193.2772324 75.69526289 16.75468267 66.12115786 34.70612902 216.31492221 32.61179484 223.49550212-2.09433649 7.47976855 0.89757279 25.73040585-30.51745832-1.19676371-41.2883248-35.30451088-85.26936791-178.31769531-97.23699809-192.08047098-12.56601198-14.66034845-103.52000518-35.90289273-155.5791987-16.15630083-28.12393095 20.04578053-41.88670665 119.3771151-60.73572349 221.40116567 35.6037018-6.88138668 74.49849914-11.07005735 116.3852058-11.0700597 348.85643113 0 446.69180874 234.26636854 477.20926938 301.2850992z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
|
||||
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 60 KiB |
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div id="setting-page">
|
||||
<div class="fake-title-bar">
|
||||
PicGo
|
||||
</div>
|
||||
<el-row style="padding-top: 22px;">
|
||||
<el-col :span="5">
|
||||
<el-menu
|
||||
class="picgo-sidebar"
|
||||
:default-active="defaultActive"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<el-menu-item index="upload">
|
||||
<i class="el-icon-upload"></i>
|
||||
<span slot="title">上传区</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="gallery">
|
||||
<i class="el-icon-picture"></i>
|
||||
<span slot="title">相册</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="weibo">
|
||||
<i class="el-icon-ui-weibo"></i>
|
||||
<span slot="title">微博设置</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="qiniu">
|
||||
<i class="el-icon-ui-qiniu"></i>
|
||||
<span slot="title">七牛设置</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</el-col>
|
||||
<el-col :span="19" :offset="5">
|
||||
<router-view></router-view>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'setting-page',
|
||||
data () {
|
||||
return {
|
||||
defaultActive: 'upload'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelect (index) {
|
||||
this.$router.push({
|
||||
name: index
|
||||
})
|
||||
}
|
||||
},
|
||||
beforeRouteEnter: (to, from, next) => {
|
||||
next(vm => {
|
||||
vm.defaultActive = to.name
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
#setting-page
|
||||
.fake-title-bar
|
||||
-webkit-app-region drag
|
||||
height h = 22px
|
||||
width 100%
|
||||
text-align center
|
||||
color #eee
|
||||
font-size 12px
|
||||
line-height h
|
||||
position fixed
|
||||
.picgo-sidebar
|
||||
height calc(100vh - 22px)
|
||||
.el-menu
|
||||
border-right none
|
||||
background transparent
|
||||
position fixed
|
||||
&-item
|
||||
color #eee
|
||||
position relative
|
||||
&:focus,
|
||||
&:hover
|
||||
color #fff
|
||||
background transparent
|
||||
&.is-active
|
||||
color active-color = #409EFF
|
||||
&:before
|
||||
content ''
|
||||
position absolute
|
||||
width 3px
|
||||
height 20px
|
||||
right 0
|
||||
top 18px
|
||||
background active-color
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div id="choose-pic-bed">
|
||||
<span>选择 {{ label }} 作为你默认图床:</span>
|
||||
<el-switch
|
||||
v-model="value"
|
||||
@change="choosePicBed"
|
||||
>
|
||||
</el-switch>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'choose-pic-bed',
|
||||
props: {
|
||||
type: String,
|
||||
label: String
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
value: false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (this.type === this.$db.get('picBed.current').value()) {
|
||||
this.value = true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
choosePicBed (val) {
|
||||
this.$db.set('picBed.current', this.type)
|
||||
this.$emit('update:choosed', this.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
</style>
|
||||
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<div id="gallery-view">
|
||||
<div class="view-title">
|
||||
相册
|
||||
</div>
|
||||
<el-row class="gallery-list">
|
||||
<el-col :span="20" :offset="2">
|
||||
<el-row :gutter="16">
|
||||
<gallerys
|
||||
:images="images"
|
||||
:index="idx"
|
||||
@close="handleClose"
|
||||
:options="options"
|
||||
></gallerys>
|
||||
<el-col :span="6" v-for="(item, index) in images" :key="item.id">
|
||||
<div
|
||||
class="gallery-list__item"
|
||||
:style="{ 'background-image': `url(${item.imgUrl})` }"
|
||||
@click="zoomImage(index)"
|
||||
>
|
||||
</div>
|
||||
<div class="gallery-list__tool-panel">
|
||||
<i class="el-icon-document" @click="copy(item.imgUrl)"></i>
|
||||
<i class="el-icon-delete" @click="remove(item.id)"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import gallerys from 'vue-gallery'
|
||||
import pasteStyle from '../../../main/utils/pasteTemplate'
|
||||
export default {
|
||||
name: 'gallery',
|
||||
components: {
|
||||
gallerys
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
images: [],
|
||||
idx: null,
|
||||
options: {
|
||||
titleProperty: 'fileName',
|
||||
urlProperty: 'imgUrl',
|
||||
closeOnSlideClick: true
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.getGallery()
|
||||
},
|
||||
methods: {
|
||||
getGallery () {
|
||||
this.images = this.$db.read().get('uploaded').slice().reverse().value()
|
||||
},
|
||||
zoomImage (index) {
|
||||
this.idx = index
|
||||
},
|
||||
handleClose () {
|
||||
this.idx = null
|
||||
},
|
||||
copy (url) {
|
||||
const style = this.$db.read().get('picBed.pasteStyle').value()
|
||||
const copyLink = pasteStyle(style, url)
|
||||
const obj = {
|
||||
title: '复制链接成功',
|
||||
body: copyLink,
|
||||
icon: url
|
||||
}
|
||||
const myNotification = new window.Notification(obj.title, obj)
|
||||
this.$electron.clipboard.writeText(copyLink)
|
||||
myNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
},
|
||||
remove (id) {
|
||||
this.$confirm('此操作将把该图片移出相册, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$db.read().get('uploaded').removeById(id).write()
|
||||
const obj = {
|
||||
title: '操作结果',
|
||||
body: '删除成功'
|
||||
}
|
||||
const myNotification = new window.Notification(obj.title, obj)
|
||||
myNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
this.getGallery()
|
||||
}).catch(() => {
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 20px auto
|
||||
#gallery-view
|
||||
.gallery-list
|
||||
height 360px
|
||||
box-sizing border-box
|
||||
padding 8px 0
|
||||
overflow-y auto
|
||||
overflow-x hidden
|
||||
.el-col
|
||||
height 150px
|
||||
position relative
|
||||
margin-bottom 16px
|
||||
&__item
|
||||
width 100%
|
||||
height 120px
|
||||
background-size cover
|
||||
background-position 50% 50%
|
||||
background-repeat no-repeat
|
||||
transition all .2s ease-in-out
|
||||
cursor pointer
|
||||
margin-bottom 8px
|
||||
&-fake
|
||||
position absolute
|
||||
top 0
|
||||
left 0
|
||||
opacity 0
|
||||
width 100%
|
||||
z-index -1
|
||||
&:hover
|
||||
background-color #49B1F5
|
||||
transform scale(1.1)
|
||||
&__tool-panel
|
||||
color #ddd
|
||||
i
|
||||
cursor pointer
|
||||
transition all .2s ease-in-out
|
||||
&.el-icon-document
|
||||
&:hover
|
||||
color #49B1F5
|
||||
&.el-icon-delete
|
||||
&:hover
|
||||
color #F15140
|
||||
.blueimp-gallery
|
||||
.title
|
||||
top 30px
|
||||
</style>
|
||||
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<div id="qiniu-view">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="16" :offset="4">
|
||||
<div class="view-title">
|
||||
七牛图床设置
|
||||
</div>
|
||||
<el-form
|
||||
ref="qiniu"
|
||||
label-position="right"
|
||||
label-width="120px"
|
||||
:model="form"
|
||||
size="mini">
|
||||
<el-form-item
|
||||
label="设定AccessKey"
|
||||
prop="accessKey"
|
||||
:rules="{
|
||||
required: true, message: 'AccessKey不能为空', trigger: 'blur'
|
||||
}">
|
||||
<el-input v-model="form.accessKey" placeholder="AccessKey" @keyup.native.enter="confirm('weiboForm')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="设定SecretKey"
|
||||
prop="secretKey"
|
||||
:rules="{
|
||||
required: true, message: 'SecretKey不能为空', trigger: 'blur'
|
||||
}">
|
||||
<el-input v-model="form.secretKey" type="password" @keyup.native.enter="confirm" placeholder="SecretKey"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="设定存储空间名"
|
||||
prop="bucket"
|
||||
:rules="{
|
||||
required: true, message: 'Bucket不能为空', trigger: 'blur'
|
||||
}">
|
||||
<el-input v-model="form.bucket" @keyup.native.enter="confirm" placeholder="Bucket"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="设定上传网址"
|
||||
prop="url"
|
||||
:rules="{
|
||||
required: true, message: '网址不能为空', trigger: 'blur'
|
||||
}">
|
||||
<el-input v-model="form.url" @keyup.native.enter="confirm" placeholder="例如:http://xxx.yyy.glb.clouddn.com"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="确认存储区域"
|
||||
>
|
||||
<el-radio-group v-model="form.area" size="mini">
|
||||
<el-radio-button label="z0">华东</el-radio-button>
|
||||
<el-radio-button label="z1">华北</el-radio-button>
|
||||
<el-radio-button label="z2">华南</el-radio-button>
|
||||
<el-radio-button label="na0">北美</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="设定网址后缀"
|
||||
>
|
||||
<el-input v-model="form.options" @keyup.native.enter="confirm" placeholder="例如?imageslim"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="confirm">确定</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '../mixin'
|
||||
export default {
|
||||
name: 'qiniu',
|
||||
mixins: [mixin],
|
||||
data () {
|
||||
return {
|
||||
form: {
|
||||
accessKey: '',
|
||||
secretKey: '',
|
||||
bucket: '',
|
||||
url: '',
|
||||
area: 'z0',
|
||||
options: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const config = this.$db.get('picBed.qiniu').value()
|
||||
if (config) {
|
||||
for (let i in config) {
|
||||
this.form[i] = config[i]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirm () {
|
||||
this.$refs.qiniu.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$db.set('picBed.qiniu', this.form).write()
|
||||
const successNotification = new window.Notification('设置结果', {
|
||||
body: '设置成功'
|
||||
})
|
||||
successNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 20px auto
|
||||
#qiniu-view
|
||||
.el-form
|
||||
label
|
||||
line-height 22px
|
||||
padding-bottom 0
|
||||
color #eee
|
||||
.el-button
|
||||
width 100%
|
||||
border-radius 19px
|
||||
.el-input__inner
|
||||
border-radius 19px
|
||||
.el-radio-group
|
||||
width 100%
|
||||
label
|
||||
width 25%
|
||||
.el-radio-button__inner
|
||||
width 100%
|
||||
.el-radio-button:first-child
|
||||
.el-radio-button__inner
|
||||
border-left none
|
||||
border-radius 14px 0 0 14px
|
||||
.el-radio-button:last-child
|
||||
.el-radio-button__inner
|
||||
border-left none
|
||||
border-radius 0 14px 14px 0
|
||||
</style>
|
||||
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div id="upload-view">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="16" :offset="4">
|
||||
<div class="view-title">
|
||||
图片上传
|
||||
</div>
|
||||
<div
|
||||
id="upload-area"
|
||||
:class="{ 'is-dragover': dragover }" @drop.prevent="onDrop" @dragover.prevent="dragover = true" @dragleave.prevent="dragover = false"
|
||||
>
|
||||
<div id="upload-dragger" @click="openUplodWindow">
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="upload-dragger__text">
|
||||
将文件拖到此处,或 <span>点击上传</span>
|
||||
</div>
|
||||
<input type="file" id="file-uploader" @change="onChange" multiple>
|
||||
</div>
|
||||
</div>
|
||||
<el-progress
|
||||
:percentage="progress"
|
||||
:show-text="false"
|
||||
class="upload-progress"
|
||||
:class="{ 'show': showProgress }"
|
||||
:status="showError ? 'exception' : ''"
|
||||
></el-progress>
|
||||
<div class="paste-style">
|
||||
<div class="paste-style__text">
|
||||
链接格式
|
||||
</div>
|
||||
<el-radio-group v-model="pasteStyle" size="mini"
|
||||
@change="handlePasteStyleChange"
|
||||
>
|
||||
<el-radio-button label="markdown">
|
||||
Markdown
|
||||
</el-radio-button>
|
||||
<el-radio-button label="HTML"> </el-radio-button>
|
||||
<el-radio-button label="URL"> </el-radio-button>
|
||||
<el-radio-button label="UBB"> </el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '../mixin'
|
||||
export default {
|
||||
name: 'upload',
|
||||
mixins: [mixin],
|
||||
data () {
|
||||
return {
|
||||
dragover: false,
|
||||
progress: 0,
|
||||
showProgress: false,
|
||||
showError: false,
|
||||
pasteStyle: ''
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.$electron.ipcRenderer.on('uploadProgress', (event, progress) => {
|
||||
if (progress !== -1) {
|
||||
this.showProgress = true
|
||||
this.progress = progress
|
||||
} else {
|
||||
this.progress = 100
|
||||
this.showError = true
|
||||
}
|
||||
})
|
||||
this.getPasteStyle()
|
||||
},
|
||||
watch: {
|
||||
progress (val) {
|
||||
if (val === 100) {
|
||||
setTimeout(() => {
|
||||
this.showProgress = false
|
||||
this.showError = false
|
||||
}, 1000)
|
||||
setTimeout(() => {
|
||||
this.progress = 0
|
||||
}, 1200)
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$electron.ipcRenderer.removeAllListeners('uploadProgress')
|
||||
},
|
||||
methods: {
|
||||
onDrop (e) {
|
||||
this.dragover = false
|
||||
this.ipcSendFiles(e.dataTransfer.files)
|
||||
},
|
||||
openUplodWindow () {
|
||||
document.getElementById('file-uploader').click()
|
||||
},
|
||||
onChange (e) {
|
||||
this.ipcSendFiles(e.target.files)
|
||||
document.getElementById('file-uploader').value = ''
|
||||
},
|
||||
ipcSendFiles (files) {
|
||||
let sendFiles = []
|
||||
Array.from(files).forEach((item, index) => {
|
||||
let obj = {
|
||||
name: item.name,
|
||||
path: item.path
|
||||
}
|
||||
sendFiles.push(obj)
|
||||
})
|
||||
this.$electron.ipcRenderer.send('uploadChoosedFiles', sendFiles)
|
||||
},
|
||||
getPasteStyle () {
|
||||
this.pasteStyle = this.$db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
},
|
||||
handlePasteStyleChange (val) {
|
||||
this.$db.read().set('picBed.pasteStyle', val)
|
||||
.write()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 20px auto
|
||||
#upload-view
|
||||
#upload-area
|
||||
height 220px
|
||||
border 2px dashed #dddddd
|
||||
border-radius 8px
|
||||
text-align center
|
||||
color #dddddd
|
||||
cursor pointer
|
||||
transition all .2s ease-in-out
|
||||
#upload-dragger
|
||||
height 100%
|
||||
&.is-dragover,
|
||||
&:hover
|
||||
border 2px dashed #A4D8FA
|
||||
background-color rgba(164, 216, 250, 0.3)
|
||||
color #fff
|
||||
i
|
||||
font-size 66px
|
||||
margin 50px 0 16px
|
||||
line-height 66px
|
||||
span
|
||||
color #409EFF
|
||||
#file-uploader
|
||||
display none
|
||||
.upload-progress
|
||||
margin-top 20px
|
||||
opacity 0
|
||||
transition all .2s ease-in-out
|
||||
&.show
|
||||
opacity 1
|
||||
.el-progress-bar__inner
|
||||
transition all .2s ease-in-out
|
||||
.paste-style
|
||||
text-align center
|
||||
margin-top 16px
|
||||
&__text
|
||||
font-size 12px
|
||||
color #eeeeee
|
||||
margin-bottom 4px
|
||||
.el-radio-button:first-child
|
||||
.el-radio-button__inner
|
||||
border-left none
|
||||
.el-radio-button:first-child
|
||||
.el-radio-button__inner
|
||||
border-left none
|
||||
border-radius 14px 0 0 14px
|
||||
.el-radio-button:last-child
|
||||
.el-radio-button__inner
|
||||
border-left none
|
||||
border-radius 0 14px 14px 0
|
||||
</style>
|
||||
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div id="weibo-view">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12" :offset="6">
|
||||
<div class="view-title">
|
||||
微博图床设置
|
||||
</div>
|
||||
<el-form
|
||||
ref="weiboForm"
|
||||
label-position="top"
|
||||
label-width="80px"
|
||||
:model="form">
|
||||
<el-form-item
|
||||
label="设定用户名"
|
||||
prop="username"
|
||||
:rules="{
|
||||
required: true, message: '用户名不能为空', trigger: 'blur'
|
||||
}">
|
||||
<el-input v-model="form.username" placeholder="用户名" @keyup.native.enter="confirm('weiboForm')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="设定密码"
|
||||
prop="password"
|
||||
:rules="{
|
||||
required: true, message: '密码不能为空', trigger: 'blur'
|
||||
}">
|
||||
<el-input v-model="form.password" type="password" @keyup.native.enter="confirm('weiboForm')" placeholder="密码"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="* 图片质量">
|
||||
<el-radio-group v-model="quality">
|
||||
<el-radio label="thumbnail">缩略图</el-radio>
|
||||
<el-radio label="mw690">中等尺寸</el-radio>
|
||||
<el-radio label="large">原图</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="confirm('weiboForm')" round>确定</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '../mixin'
|
||||
export default {
|
||||
name: 'weibo',
|
||||
mixins: [mixin],
|
||||
data () {
|
||||
return {
|
||||
form: {
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
quality: 'large'
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const config = this.$db.read().get('picBed.weibo').value()
|
||||
if (config) {
|
||||
this.form.username = config.username
|
||||
this.form.password = config.password
|
||||
this.quality = config.quality || 'large'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirm (formName) {
|
||||
this.$refs[formName].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$db.read().set('picBed.weibo', {
|
||||
username: this.form.username,
|
||||
password: this.form.password,
|
||||
quality: this.quality
|
||||
}).write()
|
||||
const successNotification = new window.Notification('设置结果', {
|
||||
body: '设置成功'
|
||||
})
|
||||
successNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
.el-message
|
||||
left 60%
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 20px auto
|
||||
#weibo-view
|
||||
.el-form
|
||||
label
|
||||
line-height 22px
|
||||
padding-bottom 0
|
||||
color #eee
|
||||
.el-button
|
||||
width 100%
|
||||
.el-input__inner
|
||||
border-radius 19px
|
||||
.el-radio-group
|
||||
margin-left 25px
|
||||
</style>
|
||||
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div id="tray-page">
|
||||
<!-- <div class="header-arrow"></div> -->
|
||||
<div class="content">
|
||||
<div class="wait-upload-img" v-if="clipboardFiles.length > 0">
|
||||
<div class="list-title">等待上传</div>
|
||||
<div v-for="(item, index) in clipboardFiles" :key="index" class="img-list" :style="{height: calcHeight(item.width, item.height) + 'px'}">
|
||||
<div class="upload-img__container" @click="uploadClipboardFiles">
|
||||
<img :src="item.imgUrl" class="upload-img">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="uploaded-img">
|
||||
<div class="list-title">已上传</div>
|
||||
<div v-for="(item, index) in files" :key="index" class="img-list" :style="{height: calcHeight(item.width, item.height) + 'px'}">
|
||||
<div class="upload-img__container" @click="copyTheLink(item)">
|
||||
<img :src="item.imgUrl" class="upload-img">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import pasteTemplate from '../../main/utils/pasteTemplate'
|
||||
export default {
|
||||
name: 'tray-page',
|
||||
data () {
|
||||
return {
|
||||
files: [],
|
||||
notification: {
|
||||
title: '复制链接成功',
|
||||
body: '',
|
||||
icon: ''
|
||||
},
|
||||
clipboardFiles: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
reverseList () {
|
||||
return this.files.slice().reverse()
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.disableDragFile()
|
||||
this.getData()
|
||||
this.$electron.ipcRenderer.on('dragFiles', (event, files) => {
|
||||
files.forEach(item => {
|
||||
this.$db.read().get('uploaded').insert(item).write()
|
||||
})
|
||||
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
|
||||
})
|
||||
this.$electron.ipcRenderer.on('clipboardFiles', (event, files) => {
|
||||
this.clipboardFiles = files
|
||||
})
|
||||
this.$electron.ipcRenderer.on('uploadFiles', (event, files) => {
|
||||
files.forEach(item => {
|
||||
this.$db.read().get('uploaded').insert(item).write()
|
||||
})
|
||||
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
|
||||
})
|
||||
this.$electron.ipcRenderer.on('updateFiles', (event) => {
|
||||
this.getData()
|
||||
})
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$electron.ipcRenderer.removeAllListeners('dragFiles')
|
||||
this.$electron.ipcRenderer.removeAllListeners('clipboardFiles')
|
||||
this.$electron.ipcRenderer.removeAllListeners('uploadClipboardFiles')
|
||||
this.$electron.ipcRenderer.removeAllListeners('updateFiles')
|
||||
},
|
||||
methods: {
|
||||
getData () {
|
||||
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
|
||||
},
|
||||
copyTheLink (item) {
|
||||
this.notification.body = item.imgUrl
|
||||
this.notification.icon = item.imgUrl
|
||||
const myNotification = new window.Notification(this.notification.title, this.notification)
|
||||
const pasteStyle = this.$db.read().get('picBed.pasteStyle').value()
|
||||
this.$electron.clipboard.writeText(pasteTemplate(pasteStyle, item.imgUrl))
|
||||
myNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
},
|
||||
calcHeight (width, height) {
|
||||
return height * 160 / width
|
||||
},
|
||||
disableDragFile () {
|
||||
window.addEventListener('dragover', (e) => {
|
||||
e = e || event
|
||||
e.preventDefault()
|
||||
}, false)
|
||||
window.addEventListener('drop', (e) => {
|
||||
e = e || event
|
||||
e.preventDefault()
|
||||
}, false)
|
||||
},
|
||||
uploadClipboardFiles () {
|
||||
this.$electron.ipcRenderer.send('uploadClipboardFiles', this.clipboardFiles[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
#tray-page
|
||||
.list-title
|
||||
text-align center
|
||||
color #858585
|
||||
font-size 12px
|
||||
padding 6px 0
|
||||
position relative
|
||||
&:before
|
||||
content ''
|
||||
position absolute
|
||||
height 1px
|
||||
width calc(100% - 36px)
|
||||
bottom 0
|
||||
left 18px
|
||||
background #858585
|
||||
.header-arrow
|
||||
position absolute
|
||||
top 12px
|
||||
left 50%
|
||||
margin-left -10px
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 10px solid transparent
|
||||
border-right: 10px solid transparent
|
||||
border-bottom: 10px solid rgba(255,255,255, 1)
|
||||
.content
|
||||
position absolute
|
||||
top 0px
|
||||
width 100%
|
||||
.img-list
|
||||
padding 16px 8px
|
||||
display flex
|
||||
justify-content space-between
|
||||
align-items center
|
||||
height 45px
|
||||
cursor pointer
|
||||
transition all .2s ease-in-out
|
||||
&:hover
|
||||
background #49B1F5
|
||||
.upload-img__index
|
||||
color #fff
|
||||
.upload-img
|
||||
height 100%
|
||||
width 100%
|
||||
margin 0 auto
|
||||
&__container
|
||||
width 100%
|
||||
padding 8px 10px
|
||||
height 100%
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
export default {
|
||||
mounted () {
|
||||
this.disableDragEvent()
|
||||
},
|
||||
methods: {
|
||||
disableDragEvent () {
|
||||
window.addEventListener('dragenter', this.disableDrag, false)
|
||||
window.addEventListener('dragover', this.disableDrag)
|
||||
window.addEventListener('drop', this.disableDrag)
|
||||
},
|
||||
disableDrag (e) {
|
||||
const dropzone = document.getElementById('upload-area')
|
||||
if (dropzone === null || !dropzone.contains(e.target)) {
|
||||
e.preventDefault()
|
||||
e.dataTransfer.effectAllowed = 'none'
|
||||
e.dataTransfer.dropEffect = 'none'
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('dragenter', this.disableDrag, false)
|
||||
window.removeEventListener('dragover', this.disableDrag)
|
||||
window.removeEventListener('drop', this.disableDrag)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import Vue from 'vue'
|
||||
import axios from 'axios'
|
||||
import ElementUI from 'element-ui'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
import App from './App'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import db from '../datastore/index'
|
||||
import './assets/fonts/iconfont.css'
|
||||
Vue.use(ElementUI)
|
||||
|
||||
if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
|
||||
Vue.http = Vue.prototype.$http = axios
|
||||
Vue.prototype.$db = db
|
||||
Vue.config.productionTip = false
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
components: { App },
|
||||
router,
|
||||
store,
|
||||
template: '<App/>'
|
||||
}).$mount('#app')
|
||||
@@ -0,0 +1,45 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'tray-page',
|
||||
component: require('@/components/TrayPage').default
|
||||
},
|
||||
{
|
||||
path: '/setting',
|
||||
name: 'setting-page',
|
||||
component: require('@/components/SettingPage').default,
|
||||
children: [
|
||||
{
|
||||
path: 'upload',
|
||||
component: require('@/components/SettingView/Upload').default,
|
||||
name: 'upload'
|
||||
},
|
||||
{
|
||||
path: 'weibo',
|
||||
component: require('@/components/SettingView/Weibo').default,
|
||||
name: 'weibo'
|
||||
},
|
||||
{
|
||||
path: 'qiniu',
|
||||
component: require('@/components/SettingView/Qiniu').default,
|
||||
name: 'qiniu'
|
||||
},
|
||||
{
|
||||
path: 'gallery',
|
||||
component: require('@/components/SettingView/Gallery').default,
|
||||
name: 'gallery'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
redirect: '/'
|
||||
}
|
||||
]
|
||||
})
|
||||
@@ -0,0 +1,11 @@
|
||||
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'
|
||||
})
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 422 B |
|
After Width: | Height: | Size: 337 B |
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"env": {
|
||||
"mocha": true
|
||||
},
|
||||
"globals": {
|
||||
"assert": true,
|
||||
"expect": true,
|
||||
"should": true,
|
||||
"__static": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
'use strict'
|
||||
|
||||
// Set BABEL_ENV to use proper env config
|
||||
process.env.BABEL_ENV = 'test'
|
||||
|
||||
// Enable use of ES6+ on required files
|
||||
require('babel-register')({
|
||||
ignore: /node_modules/
|
||||
})
|
||||
|
||||
// Attach Chai APIs to global scope
|
||||
const { expect, should, assert } = require('chai')
|
||||
global.expect = expect
|
||||
global.should = should
|
||||
global.assert = assert
|
||||
|
||||
// Require all JS files in `./specs` for Mocha to consume
|
||||
require('require-dir')('./specs')
|
||||
@@ -0,0 +1,13 @@
|
||||
import utils from '../utils'
|
||||
|
||||
describe('Launch', function () {
|
||||
beforeEach(utils.beforeEach)
|
||||
afterEach(utils.afterEach)
|
||||
|
||||
it('shows the proper application title', function () {
|
||||
return this.app.client.getTitle()
|
||||
.then(title => {
|
||||
expect(title).to.equal('picgo')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,23 @@
|
||||
import electron from 'electron'
|
||||
import { Application } from 'spectron'
|
||||
|
||||
export default {
|
||||
afterEach () {
|
||||
this.timeout(10000)
|
||||
|
||||
if (this.app && this.app.isRunning()) {
|
||||
return this.app.stop()
|
||||
}
|
||||
},
|
||||
beforeEach () {
|
||||
this.timeout(10000)
|
||||
this.app = new Application({
|
||||
path: electron,
|
||||
args: ['dist/electron/main.js'],
|
||||
startTimeout: 10000,
|
||||
waitTimeout: 10000
|
||||
})
|
||||
|
||||
return this.app.start()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import Vue from 'vue'
|
||||
Vue.config.devtools = false
|
||||
Vue.config.productionTip = false
|
||||
|
||||
// require all test files (files that ends with .spec.js)
|
||||
const testsContext = require.context('./specs', true, /\.spec$/)
|
||||
testsContext.keys().forEach(testsContext)
|
||||
|
||||
// require all src files except main.js for coverage.
|
||||
// you can also change this to match only the subset of files that
|
||||
// you want coverage for.
|
||||
const srcContext = require.context('../../src/renderer', true, /^\.\/(?!main(\.js)?$)/)
|
||||
srcContext.keys().forEach(srcContext)
|
||||
@@ -0,0 +1,62 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const merge = require('webpack-merge')
|
||||
const webpack = require('webpack')
|
||||
|
||||
const baseConfig = require('../../.electron-vue/webpack.renderer.config')
|
||||
const projectRoot = path.resolve(__dirname, '../../src/renderer')
|
||||
|
||||
// Set BABEL_ENV to use proper preset config
|
||||
process.env.BABEL_ENV = 'test'
|
||||
|
||||
let webpackConfig = merge(baseConfig, {
|
||||
devtool: '#inline-source-map',
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': '"testing"'
|
||||
})
|
||||
]
|
||||
})
|
||||
|
||||
// don't treat dependencies as externals
|
||||
delete webpackConfig.entry
|
||||
delete webpackConfig.externals
|
||||
delete webpackConfig.output.libraryTarget
|
||||
|
||||
// apply vue option to apply isparta-loader on js
|
||||
webpackConfig.module.rules
|
||||
.find(rule => rule.use.loader === 'vue-loader').use.options.loaders.js = 'babel-loader'
|
||||
|
||||
module.exports = config => {
|
||||
config.set({
|
||||
browsers: ['visibleElectron'],
|
||||
client: {
|
||||
useIframe: false
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: './coverage',
|
||||
reporters: [
|
||||
{ type: 'lcov', subdir: '.' },
|
||||
{ type: 'text-summary' }
|
||||
]
|
||||
},
|
||||
customLaunchers: {
|
||||
'visibleElectron': {
|
||||
base: 'Electron',
|
||||
flags: ['--show']
|
||||
}
|
||||
},
|
||||
frameworks: ['mocha', 'chai'],
|
||||
files: ['./index.js'],
|
||||
preprocessors: {
|
||||
'./index.js': ['webpack', 'sourcemap']
|
||||
},
|
||||
reporters: ['spec', 'coverage'],
|
||||
singleRun: true,
|
||||
webpack: webpackConfig,
|
||||
webpackMiddleware: {
|
||||
noInfo: true
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import Vue from 'vue'
|
||||
import LandingPage from '@/components/LandingPage'
|
||||
|
||||
describe('LandingPage.vue', () => {
|
||||
it('should render correct contents', () => {
|
||||
const vm = new Vue({
|
||||
el: document.createElement('div'),
|
||||
render: h => h(LandingPage)
|
||||
}).$mount()
|
||||
|
||||
expect(vm.$el.querySelector('.title').textContent).to.contain('Welcome to your new project!')
|
||||
})
|
||||
})
|
||||