mirror of
https://github.com/Molunerfinn/PicGo.git
synced 2024-04-21 10:31:33 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2def54ec49 | ||
|
|
1d5142da79 | ||
|
|
8e3c51d0b9 | ||
|
|
0cc3f70cd7 | ||
|
|
a71ba33156 | ||
|
|
960e01f17f | ||
|
|
86265ee5c5 | ||
|
|
a6377696d9 | ||
|
|
927d913f3b | ||
|
|
250a014fc1 | ||
|
|
5e5a5f133a | ||
|
|
78d0baef7d | ||
|
|
3490752e2f | ||
|
|
2b94fe49a2 | ||
|
|
d0fdbf1ea2 | ||
|
|
eddd39309e | ||
|
|
60ccd10e1a | ||
|
|
51cea7cdf0 | ||
|
|
f7dc41a927 | ||
|
|
ae9d8eee13 | ||
|
|
2d63f2ff42 | ||
|
|
778c910436 | ||
|
|
405548426a | ||
|
|
8ff7d15f8d | ||
|
|
5cf96d38b8 | ||
|
|
8ec128121f | ||
|
|
be6cce9501 | ||
|
|
72f2669cce | ||
|
|
cdf7e14548 | ||
|
|
0fe28ce675 | ||
|
|
9d979a2b04 | ||
|
|
9c12fb6ede | ||
|
|
8a8b97a937 | ||
|
|
1af9af74ce | ||
|
|
ca01d2c521 | ||
|
|
094b83b30b | ||
|
|
ea5b937c93 | ||
|
|
c149211def | ||
|
|
13c63ba66d | ||
|
|
dc1ba57a97 | ||
|
|
392af6c617 | ||
|
|
5909d35566 | ||
|
|
804072999e | ||
|
|
17755b9809 | ||
|
|
c3b2d7b941 | ||
|
|
4a420dd5c5 | ||
|
|
51281f345b | ||
|
|
c8b4bebe1d | ||
|
|
aea0c3661d | ||
|
|
cb32d43be4 | ||
|
|
4a0d398b4f | ||
|
|
fb5a10d792 | ||
|
|
65dfb4ac05 | ||
|
|
f309d18aed | ||
|
|
6fca0e73c0 | ||
|
|
0daafa3964 | ||
|
|
a2f1eec8d7 | ||
|
|
fc56d07ab2 | ||
|
|
cf504a0173 | ||
|
|
e0bb8480a2 | ||
|
|
d4c04ee1e6 | ||
|
|
4ab782e8dc | ||
|
|
a9ba87dc8e | ||
|
|
396faa2c60 | ||
|
|
930e4e663b | ||
|
|
ce9c2aed71 | ||
|
|
bba250b0d9 | ||
|
|
b74fe8676b | ||
|
|
8ad28f7fe5 | ||
|
|
1d91730eb9 | ||
|
|
5a25aa0b52 | ||
|
|
671c011feb | ||
|
|
62b616bfc7 | ||
|
|
2a801e6c19 | ||
|
|
313002cf9e | ||
|
|
bcb1859eaa | ||
|
|
8bc0aef482 | ||
|
|
353f553988 | ||
|
|
f48021d928 | ||
|
|
c9ecef60e3 | ||
|
|
0f4901bdbf | ||
|
|
f494e1486c | ||
|
|
1faf29732c | ||
|
|
5207326017 | ||
|
|
4d5b3fbdf6 | ||
|
|
abcd7b848d | ||
|
|
8785218b0d | ||
|
|
61a2a047c7 | ||
|
|
6aed2d2a97 | ||
|
|
46189d2f34 | ||
|
|
4d55ac941f | ||
|
|
62b0c2113c | ||
|
|
ed187f4438 | ||
|
|
372f71bcd9 | ||
|
|
415bdd31f3 | ||
|
|
5d9e0a311d | ||
|
|
1781a8902c | ||
|
|
1f95ef0ee2 | ||
|
|
a63e1717c7 |
+14
-12
@@ -12,7 +12,7 @@ const Multispinner = require('multispinner')
|
||||
|
||||
const mainConfig = require('./webpack.main.config')
|
||||
const rendererConfig = require('./webpack.renderer.config')
|
||||
const webConfig = require('./webpack.web.config')
|
||||
// const webConfig = require('./webpack.web.config')
|
||||
|
||||
const doneLog = chalk.bgGreen.white(' DONE ') + ' '
|
||||
const errorLog = chalk.bgRed.white(' ERROR ') + ' '
|
||||
@@ -72,6 +72,7 @@ function build () {
|
||||
|
||||
function pack (config) {
|
||||
return new Promise((resolve, reject) => {
|
||||
config.mode = 'production'
|
||||
webpack(config, (err, stats) => {
|
||||
if (err) reject(err.stack || err)
|
||||
else if (stats.hasErrors()) {
|
||||
@@ -97,19 +98,20 @@ function pack (config) {
|
||||
})
|
||||
}
|
||||
|
||||
function web () {
|
||||
del.sync(['dist/web/*', '!.gitkeep'])
|
||||
webpack(webConfig, (err, stats) => {
|
||||
if (err || stats.hasErrors()) console.log(err)
|
||||
// function web () {
|
||||
// del.sync(['dist/web/*', '!.gitkeep'])
|
||||
// webConfig.mode = 'production'
|
||||
// webpack(webConfig, (err, stats) => {
|
||||
// if (err || stats.hasErrors()) console.log(err)
|
||||
|
||||
console.log(stats.toString({
|
||||
chunks: false,
|
||||
colors: true
|
||||
}))
|
||||
// console.log(stats.toString({
|
||||
// chunks: false,
|
||||
// colors: true
|
||||
// }))
|
||||
|
||||
process.exit()
|
||||
})
|
||||
}
|
||||
// process.exit()
|
||||
// })
|
||||
// }
|
||||
|
||||
function greeting () {
|
||||
const cols = process.stdout.columns
|
||||
|
||||
+22
-10
@@ -41,21 +41,21 @@ function logStats (proc, data) {
|
||||
function startRenderer () {
|
||||
return new Promise((resolve, reject) => {
|
||||
rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer)
|
||||
|
||||
rendererConfig.mode = 'development'
|
||||
const compiler = webpack(rendererConfig)
|
||||
hotMiddleware = webpackHotMiddleware(compiler, {
|
||||
log: false,
|
||||
heartbeat: 2500
|
||||
hotMiddleware = webpackHotMiddleware(compiler, {
|
||||
log: false,
|
||||
heartbeat: 2500
|
||||
})
|
||||
|
||||
compiler.plugin('compilation', compilation => {
|
||||
compilation.plugin('html-webpack-plugin-after-emit', (data, cb) => {
|
||||
compiler.hooks.compilation.tap('compilation', compilation => {
|
||||
compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => {
|
||||
hotMiddleware.publish({ action: 'reload' })
|
||||
cb()
|
||||
})
|
||||
})
|
||||
|
||||
compiler.plugin('done', stats => {
|
||||
compiler.hooks.done.tap('done', stats => {
|
||||
logStats('Renderer', stats)
|
||||
})
|
||||
|
||||
@@ -80,10 +80,10 @@ function startRenderer () {
|
||||
function startMain () {
|
||||
return new Promise((resolve, reject) => {
|
||||
mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main)
|
||||
|
||||
mainConfig.mode = 'development'
|
||||
const compiler = webpack(mainConfig)
|
||||
|
||||
compiler.plugin('watch-run', (compilation, done) => {
|
||||
compiler.hooks.watchRun.tapAsync('watch-run', (compilation, done) => {
|
||||
logStats('Main', chalk.white.bold('compiling...'))
|
||||
hotMiddleware.publish({ action: 'compiling' })
|
||||
done()
|
||||
@@ -114,8 +114,20 @@ function startMain () {
|
||||
}
|
||||
|
||||
function startElectron () {
|
||||
electronProcess = spawn(electron, ['--inspect=5858', path.join(__dirname, '../dist/electron/main.js')])
|
||||
var args = [
|
||||
'--inspect=5858',
|
||||
path.join(__dirname, '../dist/electron/main.js')
|
||||
]
|
||||
|
||||
// detect yarn or npm and process commandline args accordingly
|
||||
if (process.env.npm_execpath.endsWith('yarn.js')) {
|
||||
args = args.concat(process.argv.slice(3))
|
||||
} else if (process.env.npm_execpath.endsWith('npm-cli.js')) {
|
||||
args = args.concat(process.argv.slice(2))
|
||||
}
|
||||
|
||||
electronProcess = spawn(electron, args)
|
||||
|
||||
electronProcess.stdout.on('data', data => {
|
||||
electronLog(data, 'blue')
|
||||
})
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
const path = require('path')
|
||||
const { VueLoaderPlugin } = require('vue-loader')
|
||||
const webpack = require('webpack')
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
context: path.resolve(__dirname, '../docs'),
|
||||
entry: './main.js',
|
||||
output: {
|
||||
@@ -28,12 +30,20 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.styl(us)?$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
'css-loader',
|
||||
'stylus-loader'
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.pug$/, loader: "pug-plain-loader"
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: 'css-loader'
|
||||
})
|
||||
use: ['vue-style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
@@ -85,28 +95,24 @@ module.exports = {
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new VueLoaderPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
template: path.resolve(__dirname, '../docs/template.html')
|
||||
}),
|
||||
new ExtractTextPlugin("styles.css")
|
||||
new MiniCssExtractPlugin({filename: 'styles.css'}),
|
||||
]
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
// module.exports.devtool = '#source-map'
|
||||
// http://vue-loader.vuejs.org/en/workflow/production.html
|
||||
module.exports.mode = 'production'
|
||||
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
|
||||
})
|
||||
|
||||
@@ -6,14 +6,14 @@ const path = require('path')
|
||||
const { dependencies } = require('../package.json')
|
||||
const webpack = require('webpack')
|
||||
|
||||
const babelMinifyWebpackPlugin = require('babel-minify-webpack-plugin')
|
||||
const BabiliWebpackPlugin = require('babili-webpack-plugin')
|
||||
|
||||
let mainConfig = {
|
||||
entry: {
|
||||
main: path.join(__dirname, '../src/main/index.js')
|
||||
},
|
||||
externals: [
|
||||
// ...Object.keys(dependencies || {})
|
||||
...Object.keys(dependencies || {})
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
@@ -73,7 +73,7 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
*/
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
mainConfig.plugins.push(
|
||||
new babelMinifyWebpackPlugin(),
|
||||
new BabiliWebpackPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': '"production"'
|
||||
})
|
||||
|
||||
@@ -8,8 +8,9 @@ const webpack = require('webpack')
|
||||
|
||||
const BabiliWebpackPlugin = require('babili-webpack-plugin')
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin')
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin')
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const { VueLoaderPlugin } = require('vue-loader')
|
||||
|
||||
/**
|
||||
* List of node_modules to include in webpack bundle
|
||||
@@ -41,12 +42,21 @@ let rendererConfig = {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
test: /\.styl(us)?$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
'css-loader',
|
||||
'stylus-loader'
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
use: ['vue-style-loader', 'css-loader', 'less-loader']
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ExtractTextPlugin.extract({
|
||||
fallback: 'style-loader',
|
||||
use: 'css-loader'
|
||||
})
|
||||
use: ['vue-style-loader', 'css-loader']
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
@@ -69,7 +79,9 @@ let rendererConfig = {
|
||||
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'
|
||||
scss: 'vue-style-loader!css-loader!sass-loader',
|
||||
less: 'vue-style-loader!css-loader!less-loader',
|
||||
stylus: 'vue-style-loader!css-loader!stylus-loader'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +121,8 @@ let rendererConfig = {
|
||||
__filename: process.env.NODE_ENV !== 'production'
|
||||
},
|
||||
plugins: [
|
||||
new ExtractTextPlugin('styles.css'),
|
||||
new VueLoaderPlugin(),
|
||||
new MiniCssExtractPlugin({filename: 'styles.css'}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: 'index.html',
|
||||
template: path.resolve(__dirname, '../src/index.ejs'),
|
||||
@@ -134,7 +147,9 @@ let rendererConfig = {
|
||||
alias: {
|
||||
'@': path.join(__dirname, '../src/renderer'),
|
||||
'vue$': 'vue/dist/vue.esm.js',
|
||||
'utils': path.join(__dirname, '../src/renderer/utils')
|
||||
'utils': path.join(__dirname, '../src/renderer/utils'),
|
||||
'~': path.join(__dirname, '../src'),
|
||||
'root': path.join(__dirname, '../')
|
||||
},
|
||||
extensions: ['.js', '.vue', '.json', '.css', '.node']
|
||||
},
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
test/unit/coverage/**
|
||||
test/unit/*.js
|
||||
test/e2e/*.js
|
||||
dist/
|
||||
dist/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<!--
|
||||
PicGo Issue 模板
|
||||
请依照该模板来提交,否则将会被关闭。
|
||||
**提问之前请注意你看过FAQ、配置手册以及那些被关闭的issues。否则同样的提问也会被关闭!**
|
||||
-->
|
||||
|
||||
## 问题类型
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@ language: c
|
||||
matrix:
|
||||
include:
|
||||
- os: osx
|
||||
# - os: linux
|
||||
- os: linux
|
||||
env: CC=clang CXX=clang++ npm_config_clang=1
|
||||
compiler: clang
|
||||
cache:
|
||||
@@ -25,11 +25,11 @@ 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
|
||||
- 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
|
||||
- nvm install 10
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash
|
||||
- source ~/.bashrc
|
||||
- npm install -g xvfb-maybe
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
## 常见问题
|
||||
|
||||
> 在使用PicGo期间你会遇到很多问题,不过很多问题其实之前就有人提问过,也被解决,所以你可以先看看[使用文档](https://github.com/Molunerfinn/PicGo/wiki/%E8%AF%A6%E7%BB%86%E7%AA%97%E5%8F%A3%E7%9A%84%E4%BD%BF%E7%94%A8#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BF%AB%E6%8D%B7%E9%94%AE),这份FAQ,以及那些被关闭的[issues](https://github.com/Molunerfinn/PicGo/issues?q=is%3Aissue+is%3Aclosed),应该能找到答案。
|
||||
|
||||
## 1.七牛图床上传图片成功后无法显示或图片无`http://`前缀
|
||||
|
||||
通常是你的七牛图床配置里的`设定访问网址`没有加上`http://`或者`https//`头。
|
||||
|
||||
参考:[issue#79](https://github.com/Molunerfinn/PicGo/issues/79)
|
||||
|
||||
## 2.能否支持图床远端同步删除
|
||||
|
||||
不能。有些图床(比如微博图床、SM.MS、Imgur等)不支持后台管理,为了架构统一不支持远端删除。
|
||||
|
||||
## 3.能否支持上传视频文件
|
||||
|
||||
目前不能。2.0版本出来后将会支持插件系统,届时如果有人开发了相应的插件理论可以支持任意文件上传。
|
||||
|
||||
## 4.微博图床上传之后无法显示预览图
|
||||
|
||||
通常是挂了全局代理导致的。
|
||||
|
||||
参考:[issue36](https://github.com/Molunerfinn/PicGo/issues/36)
|
||||
|
||||
## 5.能否支持某某某图床
|
||||
|
||||
截止v1.6,PicGo支持了如下图床:
|
||||
|
||||
- `微博图床` v1.0
|
||||
- `七牛图床` v1.0
|
||||
- `腾讯云COS v4\v5版本` v1.1 & v1.5.0
|
||||
- `又拍云` v1.2.0
|
||||
- `GitHub` v1.5.0
|
||||
- `SM.MS` v1.5.1
|
||||
- `阿里云OSS` v1.6.0
|
||||
- `Imgur` v1.6.0
|
||||
|
||||
所以本体内将不会再支持其他图床。待2.0版本出来支持插件系统后可以由其他开发者开发相应的图床插件用以支持其他图床。
|
||||
|
||||
## 6.一个图床设置多个信息
|
||||
|
||||
不能。因为目前的架构只支持一个图床一份信息。
|
||||
|
||||
## 7.v2.0版本有什么新功能
|
||||
|
||||
在不改变PicGo现有使用界面和使用习惯的基础上,将会重构整个底层`传输->转换->上传`的架构,使其能够支持插件系统,方便其他开发者基于PicGo的底层进行二次开发。
|
||||
|
||||
理论上有了插件系统之后,其他开发者能够支持如下功能:
|
||||
|
||||
1. 能够实现上传前图片压缩
|
||||
2. 能够通过图片url实现上传图片 -> 更深入地可以批量地实现图床的迁移
|
||||
3. 能够支持其他图床甚至是自己的图床
|
||||
|
||||
等等。
|
||||
|
||||
届时底层架构将会独立出来成为一个独立的npm包,能够直接在命令行里调用。
|
||||
|
||||
目前开发进度可以参考[PicGo-Core](https://github.com/Molunerfinn/PicGo-Core)。
|
||||
@@ -22,19 +22,41 @@
|
||||
|
||||
## 应用说明
|
||||
|
||||
PicGo目前支持了`微博图床`,`七牛图床`,`腾讯云COS v4\v5版本`,`又拍云`,`GitHub`。未来将支持更多图床。
|
||||
**PicGo在上传图片之后自动会将图片链接复制到你的剪贴板里,可选5种复制的链接格式。**
|
||||
|
||||
支持macOS、windows 64位(v1.3.0以上)系统,未来将支持linux。
|
||||
PicGo目前支持了
|
||||
|
||||
支持快捷键`command+shift+p`(macOS)或者`control+shift+p`(windows)用以支持快捷上传剪贴板里的图片(第一张)。PicGo v1.4.0版本及以上支持自定义快捷键啦,使用方法见[配置手册](https://github.com/Molunerfinn/PicGo/wiki/%E8%AF%A6%E7%BB%86%E7%AA%97%E5%8F%A3%E7%9A%84%E4%BD%BF%E7%94%A8#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BF%AB%E6%8D%B7%E9%94%AE)。
|
||||
- `微博图床` v1.0
|
||||
- `七牛图床` v1.0
|
||||
- `腾讯云COS v4\v5版本` v1.1 & v1.5.0
|
||||
- `又拍云` v1.2.0
|
||||
- `GitHub` v1.5.0
|
||||
- `SM.MS` v1.5.1
|
||||
- `阿里云OSS` v1.6.0
|
||||
- `Imgur` v1.6.0
|
||||
|
||||
点击此处下载[应用](https://github.com/Molunerfinn/PicGo/releases),macOS用户请下载最新版本的`dmg`文件,windows用户请下载最新版本的`exe`文件。**如果是国内用户下载github release文件速度很慢的话,推荐使用[Free Download Manager](http://www.freedownloadmanager.org/download.htm)来下载,速度会快。**
|
||||
**本体不再增加默认的图床支持。你可以自行开发第三方图床插件。**
|
||||
|
||||
如果第一次使用,请参考应用使用[手册](https://github.com/Molunerfinn/PicGo/wiki)。
|
||||
支持macOS、windows 64位(>= v1.3.1),linux(>= v1.6.0)。
|
||||
|
||||
支持快捷键`command+shift+p`(macOS)或者`control+shift+p`(windows\linux)用以支持快捷上传剪贴板里的图片(第一张)。
|
||||
PicGo支持自定义快捷键,使用方法见[配置手册](https://picgo.github.io/PicGo-Doc/zh/guide/config.html)。
|
||||
|
||||
开发进度可以查看[Projects](https://github.com/Molunerfinn/PicGo/projects),会同步更新开发进度。
|
||||
|
||||

|
||||
**如果第一次使用,请参考应用使用[快速上手](https://picgo.github.io/PicGo-Doc/zh/guide/getting-started.html)。遇到问题了还可以看看[FAQ](https://github.com/Molunerfinn/PicGo/blob/dev/FAQ.md)以及被关闭的[issues](https://github.com/Molunerfinn/PicGo/issues?q=is%3Aissue+is%3Aclosed)。**
|
||||
|
||||
## 下载安装
|
||||
|
||||
macOS用户请下载最新版本的`dmg`文件,windows用户请下载最新版本的`exe`文件,linux用户请下载`AppImage`文件。
|
||||
|
||||
点击此处下载[应用](https://github.com/Molunerfinn/PicGo/releases)。
|
||||
|
||||
**如果你是Arch类的Linux用户,可以直接通过`aurman -S picgo-appimage`来安装PicGo。感谢@houbaron的贡献!**
|
||||
|
||||
## 应用截图
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
@@ -73,9 +95,13 @@ npm run build
|
||||
|
||||
只需第一次构建的时候指定一下国内源即可。后续构建不需要特地指定。二进制文件下载在`~/.electron/`目录下。如果想要更新`electron`构建版本,可以删除`~/.electron/`目录,然后重新运行上一步,让`electron-builder`去下载最新的`electron`二进制文件。
|
||||
|
||||
## 其他相关
|
||||
|
||||
- [vs-picgo](https://github.com/Spades-S/vs-picgo):picgo的VSCode版。
|
||||
|
||||
## 赞助
|
||||
|
||||
如果你喜欢PicGo并且它对你确实有帮助,欢迎给我打赏一杯咖啡的钱哈~
|
||||
如果你喜欢PicGo并且它对你确实有帮助,欢迎给我打赏一杯咖啡哈~
|
||||
|
||||
支付宝:
|
||||
|
||||
|
||||
+33
-5
@@ -4,12 +4,16 @@
|
||||
.mask
|
||||
img.logo(src="~icons/256x256.png", alt="PicGo")
|
||||
h1.title PicGo
|
||||
small(v-if="version") {{ version }}
|
||||
h2.desc 图片上传+管理新体验
|
||||
button.download(@click="goLink('https://github.com/Molunerfinn/picgo/releases')") 免费下载Mac版
|
||||
button.download(@click="goLink('https://github.com/Molunerfinn/picgo/releases')") 免费下载Windows版
|
||||
button.download(@click="goLink('https://github.com/Molunerfinn/picgo/releases')") 免费下载
|
||||
button.download(@click="goLink('https://github.com/Molunerfinn/picgo')") 在github上查看
|
||||
h3.desc
|
||||
| 基于#[a(href="https://github.com/SimulatedGREG/electron-vue" target="_blank") electron-vue]开发
|
||||
h3.desc
|
||||
| 支持macOS,Windows,Linux
|
||||
h3.desc
|
||||
| 支持插件系统,让PicGo更强大
|
||||
#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")
|
||||
@@ -22,19 +26,26 @@
|
||||
.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]
|
||||
| ©2017 - {{ year }} #[a(href="https://github.com/Molunerfinn" target="_blank") Molunerfinn]
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: '',
|
||||
data () {
|
||||
return {
|
||||
version: '',
|
||||
year: new Date().getFullYear(),
|
||||
itemList: [
|
||||
{
|
||||
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fma907llb5j20m30ed46a',
|
||||
title: '精致设计',
|
||||
desc: 'macOS系统下,支持拖拽至menubar图标实现上传。menubar app 窗口显示最新上传的5张图片以及剪贴板里的图片。点击图片自动将上传的链接复制到剪贴板。(Windows平台不支持)'
|
||||
},
|
||||
{
|
||||
url: 'https://i.loli.net/2018/07/11/5b45768fb1276.png',
|
||||
title: 'Mini小窗',
|
||||
desc: 'Windows以及Linux系统下提供一个mini悬浮窗用于用户拖拽上传,节约你宝贵的桌面空间。'
|
||||
},
|
||||
{
|
||||
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fmd56zm2nej218g0p0teb',
|
||||
title: '便捷管理',
|
||||
@@ -43,19 +54,32 @@ export default {
|
||||
{
|
||||
url: 'https://ws1.sinaimg.cn/large/8700af19ly1fmd5ck9m0wj20lr0cxmzs',
|
||||
title: '可选图床',
|
||||
desc: '目前支持微博图床、七牛图床、腾讯云COS、又拍云。未来将支持更多。方便不同图床的上传需求。'
|
||||
desc: '默认支持微博图床、七牛图床、腾讯云COS、又拍云、GitHub、SM.MS、阿里云OSS、Imgur。方便不同图床的上传需求。2.0版本开始更可以自己开发插件实现其他图床的上传需求。'
|
||||
},
|
||||
{
|
||||
url: 'https://ws1.sinaimg.cn/large/8700af19gy1fmayjwttnbj218g0p0q4e',
|
||||
title: '多样链接',
|
||||
desc: '支持4种剪贴板链接格式,让你的文本编辑游刃有余。'
|
||||
desc: '支持5种默认剪贴板链接格式,包括一种自定义格式,让你的文本编辑游刃有余。'
|
||||
},
|
||||
{
|
||||
url: 'https://i.loli.net/2019/01/12/5c39a2f60a32a.png',
|
||||
title: '插件系统',
|
||||
desc: '2.0版本开始支持插件系统,让PicGo发挥无限潜能,成为一个极致的效率工具。'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.getVersion()
|
||||
},
|
||||
methods: {
|
||||
goLink (link) {
|
||||
window.open(link, '_blank')
|
||||
},
|
||||
async getVersion () {
|
||||
const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
|
||||
const res = await this.$http.get(release)
|
||||
this.version = res.data.name
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,6 +123,10 @@ h1
|
||||
font-size 36px
|
||||
font-weight 300
|
||||
margin 10px auto
|
||||
text-align center
|
||||
small
|
||||
margin-left 10px
|
||||
font-size 14px
|
||||
.desc
|
||||
font-weight 400
|
||||
margin 20px auto 10px
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import Vue from 'vue'
|
||||
import App from './APP.vue'
|
||||
import 'melody.css'
|
||||
import axios from 'axios'
|
||||
|
||||
Vue.prototype.$http = axios
|
||||
|
||||
new Vue({
|
||||
render: h => h(App)
|
||||
|
||||
+23
-21
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "picgo",
|
||||
"version": "1.5.1",
|
||||
"version": "2.0.0",
|
||||
"author": "Molunerfinn <marksz@teamsz.xyz>",
|
||||
"description": "Easy to upload your pic & copy to write",
|
||||
"license": "MIT",
|
||||
@@ -67,13 +67,14 @@
|
||||
"allowToChangeInstallationDirectory": true
|
||||
},
|
||||
"linux": {
|
||||
"icon": "build/icons"
|
||||
"icon": "build/icons/"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.16.1",
|
||||
"element-ui": "^2.0.5",
|
||||
"fecha": "^2.3.3",
|
||||
"dateformat": "^3.0.3",
|
||||
"element-ui": "^2.4.11",
|
||||
"fix-path": "^2.1.0",
|
||||
"fs-extra": "^4.0.2",
|
||||
"image-size": "^0.6.1",
|
||||
"keycode": "^2.1.9",
|
||||
@@ -81,12 +82,12 @@
|
||||
"lowdb": "^1.0.0",
|
||||
"md5": "^2.2.1",
|
||||
"melody.css": "^1.0.2",
|
||||
"picgo": "^1.1.15",
|
||||
"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-lazyload": "^1.2.6",
|
||||
"vue-router": "^2.5.3",
|
||||
"vuex": "^2.3.1"
|
||||
},
|
||||
@@ -94,7 +95,6 @@
|
||||
"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",
|
||||
@@ -109,22 +109,21 @@
|
||||
"css-loader": "^0.28.4",
|
||||
"del": "^3.0.0",
|
||||
"devtron": "^1.4.0",
|
||||
"electron": "^1.7.5",
|
||||
"electron-builder": "^19.19.1",
|
||||
"electron": "4.0.0",
|
||||
"electron-builder": "^20.38.4",
|
||||
"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-loader": "^2.1.1",
|
||||
"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",
|
||||
"file-loader": "^3.0.1",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"inject-loader": "^3.0.0",
|
||||
"karma": "^1.3.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
@@ -134,24 +133,27 @@
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-spec-reporter": "^0.0.31",
|
||||
"karma-webpack": "^2.0.1",
|
||||
"mini-css-extract-plugin": "0.4.0",
|
||||
"mocha": "^3.0.2",
|
||||
"multispinner": "^0.2.1",
|
||||
"node-loader": "^0.6.0",
|
||||
"pug": "^2.0.0-rc.4",
|
||||
"pug-loader": "^2.3.0",
|
||||
"pug-plain-loader": "^1.0.0",
|
||||
"require-dir": "^0.3.0",
|
||||
"spectron": "^3.7.1",
|
||||
"style-loader": "^0.18.2",
|
||||
"style-loader": "^0.23.1",
|
||||
"stylus": "^0.54.5",
|
||||
"stylus-loader": "^3.0.1",
|
||||
"url-loader": "^0.5.9",
|
||||
"url-loader": "^1.1.2",
|
||||
"vue-html-loader": "^1.2.4",
|
||||
"vue-loader": "^13.0.5",
|
||||
"vue-style-loader": "^3.0.1",
|
||||
"vue-loader": "^15.4.2",
|
||||
"vue-style-loader": "^4.1.2",
|
||||
"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"
|
||||
"webpack": "^4.15.1",
|
||||
"webpack-cli": "^3.0.8",
|
||||
"webpack-dev-server": "^3.1.4",
|
||||
"webpack-hot-middleware": "^2.22.2",
|
||||
"webpack-merge": "^4.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
+16
-2
@@ -5,6 +5,13 @@ import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import { remote, app } from 'electron'
|
||||
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
global.__static = path.join(__dirname, '/static').replace(/\\/g, '\\\\')
|
||||
}
|
||||
if (process.env.DEBUG_ENV === 'debug') {
|
||||
global.__static = path.join(__dirname, '../../static').replace(/\\/g, '\\\\')
|
||||
}
|
||||
|
||||
const APP = process.type === 'renderer' ? remote.app : app
|
||||
const STORE_PATH = APP.getPath('userData')
|
||||
|
||||
@@ -29,10 +36,17 @@ if (!db.has('picBed').value()) {
|
||||
}).write()
|
||||
}
|
||||
|
||||
if (!db.has('shortKey').value()) {
|
||||
db.set('shortKey', {
|
||||
if (!db.has('settings.shortKey').value()) {
|
||||
db.set('settings.shortKey', {
|
||||
upload: 'CommandOrControl+Shift+P'
|
||||
}).write()
|
||||
}
|
||||
|
||||
// init generate clipboard image files
|
||||
if (!fs.pathExistsSync(path.join(STORE_PATH, 'windows.ps1'))) {
|
||||
fs.copySync(path.join(__static, '/linux.sh'), path.join(STORE_PATH, '/linux.sh'))
|
||||
fs.copySync(path.join(__static, '/mac.applescript'), path.join(STORE_PATH, '/mac.applescript'))
|
||||
fs.copySync(path.join(__static, '/windows.ps1'), path.join(STORE_PATH, '/windows.ps1'))
|
||||
}
|
||||
|
||||
export default db
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import weiboUpload from '../main/utils/weiboUpload'
|
||||
import qiniuUpload from '../main/utils/qiniuUpload'
|
||||
import tcYunUpload from '../main/utils/tcYunUpload'
|
||||
import upYunUpload from '../main/utils/upYunUpload'
|
||||
import githubUpload from '../main/utils/githubUpload'
|
||||
import smmsUpload from '../main/utils/smmsUpload'
|
||||
|
||||
const picBedHandler = {
|
||||
weibo: weiboUpload,
|
||||
qiniu: qiniuUpload,
|
||||
tcyun: tcYunUpload,
|
||||
upyun: upYunUpload,
|
||||
github: githubUpload,
|
||||
smms: smmsUpload
|
||||
}
|
||||
|
||||
export default picBedHandler
|
||||
@@ -30,6 +30,16 @@ let picBed = [
|
||||
type: 'smms',
|
||||
name: 'SM.MS图床',
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
type: 'aliyun',
|
||||
name: '阿里云OSS',
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
type: 'imgur',
|
||||
name: 'Imgur图床',
|
||||
visible: true
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
+1
-15
@@ -7,9 +7,6 @@
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
// Set environment for development
|
||||
process.env.NODE_ENV = 'development'
|
||||
|
||||
// Install `electron-debug` with `devtron`
|
||||
require('electron-debug')({ showDevTools: false })
|
||||
|
||||
@@ -24,15 +21,4 @@ require('electron').app.on('ready', () => {
|
||||
})
|
||||
|
||||
// Require `main` process to boot app
|
||||
if (process.env.DEBUG_ENV === 'debug') {
|
||||
require('babel-core/register')({
|
||||
'presets': [
|
||||
['env', {
|
||||
'targets': {
|
||||
'node': true
|
||||
}
|
||||
}]
|
||||
]
|
||||
})
|
||||
}
|
||||
require('./index')
|
||||
require('./index')
|
||||
+196
-140
@@ -1,11 +1,14 @@
|
||||
'use strict'
|
||||
|
||||
import uploader from './utils/uploader.js'
|
||||
import Uploader from './utils/uploader.js'
|
||||
import { app, BrowserWindow, Tray, Menu, Notification, clipboard, ipcMain, globalShortcut, dialog } from 'electron'
|
||||
import db from '../datastore'
|
||||
import pasteTemplate from './utils/pasteTemplate'
|
||||
import updateChecker from './utils/updateChecker'
|
||||
import { getPicBeds } from './utils/getPicBeds'
|
||||
import pkg from '../../package.json'
|
||||
import picgoCoreIPC from './utils/picgoCoreIPC'
|
||||
import fixPath from 'fix-path'
|
||||
/**
|
||||
* Set `__static` path to static files in production
|
||||
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
|
||||
@@ -19,6 +22,7 @@ if (process.env.DEBUG_ENV === 'debug') {
|
||||
|
||||
let window
|
||||
let settingWindow
|
||||
let miniWindow
|
||||
let tray
|
||||
let menu
|
||||
let contextMenu
|
||||
@@ -28,18 +32,28 @@ const winURL = process.env.NODE_ENV === 'development'
|
||||
const settingWinURL = process.env.NODE_ENV === 'development'
|
||||
? `http://localhost:9080/#setting/upload`
|
||||
: `file://${__dirname}/index.html#setting/upload`
|
||||
const miniWinURL = process.env.NODE_ENV === 'development'
|
||||
? `http://localhost:9080/#mini-page`
|
||||
: `file://${__dirname}/index.html#mini-page`
|
||||
|
||||
const uploadFailed = () => {
|
||||
const notification = new Notification({
|
||||
title: '上传失败',
|
||||
body: '请检查你的图床配置!'
|
||||
// fix the $PATH in macOS
|
||||
fixPath()
|
||||
|
||||
function createContextMenu () {
|
||||
const picBeds = getPicBeds(app)
|
||||
const submenu = picBeds.map(item => {
|
||||
return {
|
||||
label: item.name,
|
||||
type: 'radio',
|
||||
checked: db.read().get('picBed.current').value() === item.type,
|
||||
click () {
|
||||
db.read().set('picBed.current', item.type).write()
|
||||
if (settingWindow) {
|
||||
settingWindow.webContents.send('syncPicBed')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
|
||||
function createTray () {
|
||||
const menubarPic = process.platform === 'darwin' ? `${__static}/menubar.png` : `${__static}/menubar-nodarwin.png`
|
||||
tray = new Tray(menubarPic)
|
||||
contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '关于',
|
||||
@@ -61,66 +75,23 @@ function createTray () {
|
||||
settingWindow.show()
|
||||
settingWindow.focus()
|
||||
}
|
||||
if (miniWindow) {
|
||||
miniWindow.hide()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
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()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '腾讯云COS',
|
||||
type: 'radio',
|
||||
checked: db.read().get('picBed.current').value() === 'tcyun',
|
||||
click () {
|
||||
db.read().set('picBed.current', 'tcyun')
|
||||
.write()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '又拍云图床',
|
||||
type: 'radio',
|
||||
checked: db.read().get('picBed.current').value() === 'upyun',
|
||||
click () {
|
||||
db.read().set('picBed.current', 'upyun')
|
||||
.write()
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'GitHub图床',
|
||||
type: 'radio',
|
||||
checked: db.read().get('picBed.current').value() === 'github',
|
||||
click () {
|
||||
db.read().set('picBed.current', 'github')
|
||||
.write()
|
||||
}
|
||||
}
|
||||
]
|
||||
submenu
|
||||
},
|
||||
{
|
||||
label: '打开更新助手',
|
||||
type: 'checkbox',
|
||||
checked: db.get('picBed.showUpdateTip').value(),
|
||||
checked: db.get('settings.showUpdateTip').value(),
|
||||
click () {
|
||||
const value = db.read().get('picBed.showUpdateTip').value()
|
||||
db.read().set('picBed.showUpdateTip', !value).write()
|
||||
const value = db.read().get('settings.showUpdateTip').value()
|
||||
db.read().set('settings.showUpdateTip', !value).write()
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -128,11 +99,19 @@ function createTray () {
|
||||
label: '退出'
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
function createTray () {
|
||||
const menubarPic = process.platform === 'darwin' ? `${__static}/menubar.png` : `${__static}/menubar-nodarwin.png`
|
||||
tray = new Tray(menubarPic)
|
||||
tray.on('right-click', () => {
|
||||
window.hide()
|
||||
if (window) {
|
||||
window.hide()
|
||||
}
|
||||
createContextMenu()
|
||||
tray.popUpContextMenu(contextMenu)
|
||||
})
|
||||
tray.on('click', () => {
|
||||
tray.on('click', (event, bounds) => {
|
||||
if (process.platform === 'darwin') {
|
||||
let img = clipboard.readImage()
|
||||
let obj = []
|
||||
@@ -145,12 +124,14 @@ function createTray () {
|
||||
imgUrl
|
||||
})
|
||||
}
|
||||
toggleWindow()
|
||||
toggleWindow(bounds)
|
||||
setTimeout(() => {
|
||||
window.webContents.send('clipboardFiles', obj)
|
||||
}, 0)
|
||||
} else {
|
||||
window.hide()
|
||||
if (window) {
|
||||
window.hide()
|
||||
}
|
||||
if (settingWindow === null) {
|
||||
createSettingWindow()
|
||||
settingWindow.show()
|
||||
@@ -158,6 +139,9 @@ function createTray () {
|
||||
settingWindow.show()
|
||||
settingWindow.focus()
|
||||
}
|
||||
if (miniWindow) {
|
||||
miniWindow.hide()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -170,11 +154,12 @@ function createTray () {
|
||||
})
|
||||
|
||||
tray.on('drop-files', async (event, files) => {
|
||||
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
const imgs = await uploader(files, 'imgFromPath', window.webContents)
|
||||
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
|
||||
const imgs = await new Uploader(files, window.webContents).upload()
|
||||
if (imgs !== false) {
|
||||
for (let i in imgs) {
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, imgs[i].imgUrl))
|
||||
const url = imgs[i].url || imgs[i].imgUrl
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, url))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: imgs[i].imgUrl,
|
||||
@@ -183,16 +168,18 @@ function createTray () {
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
db.read().get('uploaded').insert(imgs[i]).write()
|
||||
}
|
||||
window.webContents.send('dragFiles', imgs)
|
||||
} else {
|
||||
uploadFailed()
|
||||
}
|
||||
})
|
||||
// toggleWindow()
|
||||
}
|
||||
|
||||
const createWindow = () => {
|
||||
if (process.platform !== 'darwin' && process.platform !== 'win32') {
|
||||
return
|
||||
}
|
||||
window = new BrowserWindow({
|
||||
height: 350,
|
||||
width: 196, // 196
|
||||
@@ -216,9 +203,45 @@ const createWindow = () => {
|
||||
window.on('blur', () => {
|
||||
window.hide()
|
||||
})
|
||||
}
|
||||
|
||||
createSettingWindow()
|
||||
createMenu()
|
||||
const createMiniWidow = () => {
|
||||
if (miniWindow) {
|
||||
return false
|
||||
}
|
||||
let obj = {
|
||||
height: 64,
|
||||
width: 64,
|
||||
show: true,
|
||||
frame: false,
|
||||
fullscreenable: false,
|
||||
resizable: false,
|
||||
transparent: true,
|
||||
icon: `${__static}/logo.png`,
|
||||
webPreferences: {
|
||||
backgroundThrottling: false
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
obj.transparent = false
|
||||
}
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
obj.show = false
|
||||
}
|
||||
|
||||
if (db.read().get('miniWindowOntop').value()) {
|
||||
obj.alwaysOnTop = true
|
||||
}
|
||||
|
||||
miniWindow = new BrowserWindow(obj)
|
||||
|
||||
miniWindow.loadURL(miniWinURL)
|
||||
|
||||
miniWindow.on('closed', () => {
|
||||
miniWindow = null
|
||||
})
|
||||
}
|
||||
|
||||
const createSettingWindow = () => {
|
||||
@@ -235,13 +258,16 @@ const createSettingWindow = () => {
|
||||
transparent: true,
|
||||
titleBarStyle: 'hidden',
|
||||
webPreferences: {
|
||||
backgroundThrottling: false
|
||||
backgroundThrottling: false,
|
||||
webSecurity: false
|
||||
}
|
||||
}
|
||||
if (process.platform === 'win32') {
|
||||
options.show = true
|
||||
if (process.platform !== 'darwin') {
|
||||
options.show = false
|
||||
options.frame = false
|
||||
options.backgroundColor = '#3f3c37'
|
||||
options.transparent = false
|
||||
options.icon = `${__static}/logo.png`
|
||||
}
|
||||
settingWindow = new BrowserWindow(options)
|
||||
|
||||
@@ -249,7 +275,12 @@ const createSettingWindow = () => {
|
||||
|
||||
settingWindow.on('closed', () => {
|
||||
settingWindow = null
|
||||
if (process.platform === 'linux') {
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
createMenu()
|
||||
createMiniWidow()
|
||||
}
|
||||
|
||||
const createMenu = () => {
|
||||
@@ -278,60 +309,46 @@ const createMenu = () => {
|
||||
}
|
||||
}
|
||||
|
||||
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 = () => {
|
||||
const toggleWindow = (bounds) => {
|
||||
if (window.isVisible()) {
|
||||
window.hide()
|
||||
} else {
|
||||
showWindow()
|
||||
showWindow(bounds)
|
||||
}
|
||||
}
|
||||
|
||||
const showWindow = () => {
|
||||
const position = getWindowPosition()
|
||||
window.setPosition(position.x, position.y, false)
|
||||
const showWindow = (bounds) => {
|
||||
window.setPosition(bounds.x - 98 + 11, bounds.y, false)
|
||||
window.webContents.send('updateFiles')
|
||||
window.show()
|
||||
window.focus()
|
||||
}
|
||||
|
||||
const uploadClipboardFiles = async () => {
|
||||
let img = clipboard.readImage()
|
||||
let uploadImg = null
|
||||
if (!img.isEmpty()) {
|
||||
// 从剪贴板来的图片默认转为png
|
||||
const imgUrl = 'data:image/png;base64,' + Buffer.from(img.toPNG(), 'binary').toString('base64')
|
||||
uploadImg = {
|
||||
width: img.getSize().width,
|
||||
height: img.getSize().height,
|
||||
imgUrl
|
||||
}
|
||||
let win
|
||||
if (miniWindow.isVisible()) {
|
||||
win = miniWindow
|
||||
} else {
|
||||
win = settingWindow || window
|
||||
}
|
||||
const win = settingWindow || window
|
||||
img = await uploader(uploadImg, 'imgFromClipboard', win.webContents)
|
||||
let img = await new Uploader(undefined, win.webContents).upload()
|
||||
if (img !== false) {
|
||||
if (img.length > 0) {
|
||||
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, img[0].imgUrl))
|
||||
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
|
||||
const url = img[0].url || img[0].imgUrl
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, url))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: img[0].imgUrl,
|
||||
icon: img[0].imgUrl
|
||||
})
|
||||
notification.show()
|
||||
db.read().get('uploaded').insert(img[0]).write()
|
||||
window.webContents.send('clipboardFiles', [])
|
||||
window.webContents.send('uploadFiles', img)
|
||||
if (settingWindow) {
|
||||
settingWindow.webContents.send('updateGallery')
|
||||
}
|
||||
} else {
|
||||
const notification = new Notification({
|
||||
title: '上传不成功',
|
||||
@@ -339,16 +356,18 @@ const uploadClipboardFiles = async () => {
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
} else {
|
||||
uploadFailed()
|
||||
}
|
||||
}
|
||||
|
||||
picgoCoreIPC(app, ipcMain)
|
||||
|
||||
// from macOS tray
|
||||
ipcMain.on('uploadClipboardFiles', async (evt, file) => {
|
||||
const img = await uploader(file, 'imgFromClipboard', window.webContents)
|
||||
const img = await new Uploader(file, window.webContents).upload()
|
||||
if (img !== false) {
|
||||
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, img[0].imgUrl))
|
||||
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
|
||||
const url = img[0].url || img[0].imgUrl
|
||||
clipboard.writeText(pasteTemplate(pasteStyle, url))
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: img[0].imgUrl,
|
||||
@@ -356,10 +375,12 @@ ipcMain.on('uploadClipboardFiles', async (evt, file) => {
|
||||
icon: img[0].imgUrl
|
||||
})
|
||||
notification.show()
|
||||
db.read().get('uploaded').insert(img[0]).write()
|
||||
window.webContents.send('clipboardFiles', [])
|
||||
window.webContents.send('uploadFiles', img)
|
||||
} else {
|
||||
uploadFailed()
|
||||
window.webContents.send('uploadFiles')
|
||||
if (settingWindow) {
|
||||
settingWindow.webContents.send('updateGallery')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -368,12 +389,14 @@ ipcMain.on('uploadClipboardFilesFromUploadPage', () => {
|
||||
})
|
||||
|
||||
ipcMain.on('uploadChoosedFiles', async (evt, files) => {
|
||||
const imgs = await uploader(files, 'imgFromUploader', settingWindow.webContents)
|
||||
const input = files.map(item => item.path)
|
||||
const imgs = await new Uploader(input, evt.sender).upload()
|
||||
if (imgs !== false) {
|
||||
const pasteStyle = db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
|
||||
let pasteText = ''
|
||||
for (let i in imgs) {
|
||||
pasteText += pasteTemplate(pasteStyle, imgs[i].imgUrl) + '\r\n'
|
||||
const url = imgs[i].url || imgs[i].imgUrl
|
||||
pasteText += pasteTemplate(pasteStyle, url) + '\r\n'
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: imgs[i].imgUrl,
|
||||
@@ -382,18 +405,20 @@ ipcMain.on('uploadChoosedFiles', async (evt, files) => {
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
db.read().get('uploaded').insert(imgs[i]).write()
|
||||
}
|
||||
clipboard.writeText(pasteText)
|
||||
window.webContents.send('uploadFiles', imgs)
|
||||
} else {
|
||||
uploadFailed()
|
||||
if (settingWindow) {
|
||||
settingWindow.webContents.send('updateGallery')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('updateShortKey', (evt, oldKey) => {
|
||||
globalShortcut.unregisterAll()
|
||||
for (let key in oldKey) {
|
||||
globalShortcut.register(db.read().get('shortKey').value()[key], () => {
|
||||
globalShortcut.register(db.read().get('settings.shortKey').value()[key], () => {
|
||||
return shortKeyHash[key]()
|
||||
})
|
||||
}
|
||||
@@ -412,50 +437,78 @@ ipcMain.on('updateCustomLink', (evt, oldLink) => {
|
||||
notification.show()
|
||||
})
|
||||
|
||||
ipcMain.on('updteDefaultPicBed', (evt) => {
|
||||
const types = ['weibo', 'qiniu', 'tcyun', 'upyun']
|
||||
let submenuItem = contextMenu.items[2].submenu.items
|
||||
submenuItem.forEach((item, index) => {
|
||||
const result = db.read().get('picBed.current').value() === types[index]
|
||||
if (result) {
|
||||
item.click() // It's a bug which can not set checked status
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on('autoStart', (evt, val) => {
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: val
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on('openSettingWindow', (evt) => {
|
||||
if (!settingWindow) {
|
||||
createSettingWindow()
|
||||
} else {
|
||||
settingWindow.show()
|
||||
}
|
||||
miniWindow.hide()
|
||||
})
|
||||
|
||||
ipcMain.on('openMiniWindow', (evt) => {
|
||||
if (!miniWindow) {
|
||||
createMiniWidow()
|
||||
}
|
||||
miniWindow.show()
|
||||
miniWindow.focus()
|
||||
settingWindow.hide()
|
||||
})
|
||||
|
||||
// from mini window
|
||||
ipcMain.on('syncPicBed', (evt) => {
|
||||
if (settingWindow) {
|
||||
settingWindow.webContents.send('syncPicBed')
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('getPicBeds', (evt) => {
|
||||
const picBeds = getPicBeds(app)
|
||||
evt.sender.send('getPicBeds', picBeds)
|
||||
evt.returnValue = picBeds
|
||||
})
|
||||
|
||||
const shortKeyHash = {
|
||||
upload: uploadClipboardFiles
|
||||
}
|
||||
|
||||
const isSecondInstance = app.makeSingleInstance(() => {
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
|
||||
if (!gotTheLock) {
|
||||
app.quit()
|
||||
} else {
|
||||
if (settingWindow) {
|
||||
if (settingWindow.isMinimized()) {
|
||||
settingWindow.restore()
|
||||
}
|
||||
settingWindow.focus()
|
||||
}
|
||||
})
|
||||
|
||||
if (isSecondInstance) {
|
||||
app.quit()
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
app.setAppUserModelId(pkg.build.appId)
|
||||
}
|
||||
|
||||
if (process.env.XDG_CURRENT_DESKTOP && process.env.XDG_CURRENT_DESKTOP.includes('Unity')) {
|
||||
process.env.XDG_CURRENT_DESKTOP = 'Unity'
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
createWindow()
|
||||
createTray()
|
||||
createSettingWindow()
|
||||
if (process.platform === 'darwin' || process.platform === 'win32') {
|
||||
createTray()
|
||||
}
|
||||
db.read().set('needReload', false).write()
|
||||
updateChecker()
|
||||
|
||||
globalShortcut.register(db.read().get('shortKey.upload').value(), () => {
|
||||
globalShortcut.register(db.read().get('settings.shortKey.upload').value(), () => {
|
||||
uploadClipboardFiles()
|
||||
})
|
||||
})
|
||||
@@ -467,9 +520,12 @@ app.on('window-all-closed', () => {
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
if (window === null || settingWindow === null) {
|
||||
if (window === null) {
|
||||
createWindow()
|
||||
}
|
||||
if (settingWindow === null) {
|
||||
createSettingWindow()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('will-quit', () => {
|
||||
@@ -477,7 +533,7 @@ app.on('will-quit', () => {
|
||||
})
|
||||
|
||||
app.setLoginItemSettings({
|
||||
openAtLogin: db.read().get('picBed.autoStart').value() || false
|
||||
openAtLogin: db.read().get('settings.autoStart').value() || false
|
||||
})
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import path from 'path'
|
||||
import db from '../../datastore'
|
||||
// eslint-disable-next-line
|
||||
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
|
||||
|
||||
const getPicBeds = (app) => {
|
||||
const PicGo = requireFunc('picgo')
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
|
||||
const picgo = new PicGo(CONFIG_PATH)
|
||||
const picBedTypes = picgo.helper.uploader.getIdList()
|
||||
const picBedFromDB = db.read().get('picBed.list').value() || []
|
||||
const picBeds = picBedTypes.map(item => {
|
||||
const visible = picBedFromDB.find(i => i.type === item) // object or undefined
|
||||
return {
|
||||
type: item,
|
||||
name: picgo.helper.uploader.get(item).name || item,
|
||||
visible: visible ? visible.visible : true
|
||||
}
|
||||
})
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
return picBeds
|
||||
}
|
||||
|
||||
export {
|
||||
getPicBeds
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import request from 'request-promise'
|
||||
import * as img2Base64 from './img2base64'
|
||||
import db from '../../datastore/index'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
const postOptions = (fileName, options, data) => {
|
||||
const path = options.path || ''
|
||||
const {token, repo} = options
|
||||
return {
|
||||
method: 'PUT',
|
||||
url: `https://api.github.com/repos/${repo}/contents/${encodeURI(path)}${encodeURI(fileName)}`,
|
||||
headers: {
|
||||
Authorization: `token ${token}`,
|
||||
'User-Agent': 'PicGo'
|
||||
},
|
||||
body: data,
|
||||
json: true
|
||||
}
|
||||
}
|
||||
|
||||
const githubUpload = async function (img, type, webContents) {
|
||||
try {
|
||||
webContents.send('uploadProgress', 0)
|
||||
const imgList = await img2Base64[type](img)
|
||||
const length = imgList.length
|
||||
const githubOptions = db.read().get('picBed.github').value()
|
||||
webContents.send('uploadProgress', 30)
|
||||
for (let i in imgList) {
|
||||
const data = {
|
||||
message: 'Upload by PicGo',
|
||||
branch: githubOptions.branch,
|
||||
content: imgList[i].base64Image,
|
||||
path: githubOptions.path + encodeURI(imgList[i].fileName)
|
||||
}
|
||||
const postConfig = postOptions(imgList[i].fileName, githubOptions, data)
|
||||
const body = await request(postConfig)
|
||||
if (body) {
|
||||
delete imgList[i].base64Image
|
||||
if (githubOptions.customUrl) {
|
||||
imgList[i]['imgUrl'] = `${githubOptions.customUrl}/${githubOptions.path}${imgList[i].fileName}`
|
||||
} else {
|
||||
imgList[i]['imgUrl'] = body.content.download_url
|
||||
}
|
||||
imgList[i]['type'] = 'github'
|
||||
if (i - length === -1) {
|
||||
webContents.send('uploadProgress', 60)
|
||||
}
|
||||
} else {
|
||||
webContents.send('uploadProgress', -1)
|
||||
return new Error()
|
||||
}
|
||||
}
|
||||
webContents.send('uploadProgress', 100)
|
||||
return imgList
|
||||
} catch (err) {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: '服务端出错,请重试'
|
||||
})
|
||||
notification.show()
|
||||
throw new Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
export default githubUpload
|
||||
@@ -0,0 +1,87 @@
|
||||
import {
|
||||
dialog,
|
||||
BrowserWindow,
|
||||
clipboard,
|
||||
Notification
|
||||
} from 'electron'
|
||||
import db from '../../datastore'
|
||||
import Uploader from './uploader'
|
||||
import pasteTemplate from './pasteTemplate'
|
||||
const WEBCONTENTS = Symbol('WEBCONTENTS')
|
||||
const IPCMAIN = Symbol('IPCMAIN')
|
||||
const PICGO = Symbol('PICGO')
|
||||
|
||||
class GuiApi {
|
||||
constructor (ipcMain, webcontents, picgo) {
|
||||
this[WEBCONTENTS] = webcontents
|
||||
this[IPCMAIN] = ipcMain
|
||||
this[PICGO] = picgo
|
||||
}
|
||||
|
||||
/**
|
||||
* for plugin showInputBox
|
||||
* @param {object} options
|
||||
* return type is string or ''
|
||||
*/
|
||||
showInputBox (options) {
|
||||
if (options === undefined) {
|
||||
options = {
|
||||
title: '',
|
||||
placeholder: ''
|
||||
}
|
||||
}
|
||||
this[WEBCONTENTS].send('showInputBox', options)
|
||||
return new Promise((resolve, reject) => {
|
||||
this[IPCMAIN].once('showInputBox', (event, value) => {
|
||||
resolve(value)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* for plugin show file explorer
|
||||
* @param {object} options
|
||||
*/
|
||||
showFileExplorer (options) {
|
||||
if (options === undefined) {
|
||||
options = {}
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
dialog.showOpenDialog(BrowserWindow.fromWebContents(this[WEBCONTENTS]), options, filename => {
|
||||
resolve(filename)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* for plugin to upload file
|
||||
* @param {array} input
|
||||
*/
|
||||
async upload (input) {
|
||||
const imgs = await new Uploader(input, this[WEBCONTENTS], this[PICGO]).upload()
|
||||
if (imgs !== false) {
|
||||
const pasteStyle = db.read().get('settings.pasteStyle').value() || 'markdown'
|
||||
let pasteText = ''
|
||||
for (let i in imgs) {
|
||||
const url = imgs[i].url || imgs[i].imgUrl
|
||||
pasteText += pasteTemplate(pasteStyle, url) + '\r\n'
|
||||
const notification = new Notification({
|
||||
title: '上传成功',
|
||||
body: imgs[i].imgUrl,
|
||||
icon: imgs[i].imgUrl
|
||||
})
|
||||
setTimeout(() => {
|
||||
notification.show()
|
||||
}, i * 100)
|
||||
db.read().get('uploaded').insert(imgs[i]).write()
|
||||
}
|
||||
clipboard.writeText(pasteText)
|
||||
this[WEBCONTENTS].send('uploadFiles', imgs)
|
||||
this[WEBCONTENTS].send('updateGallery')
|
||||
return imgs
|
||||
}
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export default GuiApi
|
||||
@@ -1,145 +0,0 @@
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import sizeOf from 'image-size'
|
||||
import fecha from 'fecha'
|
||||
import { BrowserWindow, ipcMain } from 'electron'
|
||||
import db from '../../datastore/index.js'
|
||||
const renameURL = process.env.NODE_ENV === 'development' ? `http://localhost:9080/#rename-page` : `file://${__dirname}/index.html#rename-page`
|
||||
|
||||
const createRenameWindow = () => {
|
||||
let options = {
|
||||
height: 175,
|
||||
width: 300,
|
||||
show: true,
|
||||
fullscreenable: false,
|
||||
resizable: false,
|
||||
vibrancy: 'ultra-dark',
|
||||
webPreferences: {
|
||||
backgroundThrottling: false
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
options.show = true
|
||||
options.backgroundColor = '#3f3c37'
|
||||
options.autoHideMenuBar = true
|
||||
}
|
||||
|
||||
const window = new BrowserWindow(options)
|
||||
window.loadURL(renameURL)
|
||||
return window
|
||||
}
|
||||
|
||||
const imgFromPath = async (imgPath) => {
|
||||
let results = []
|
||||
const rename = db.read().get('picBed.rename').value()
|
||||
const autoRename = db.read().get('picBed.autoRename').value()
|
||||
await Promise.all(imgPath.map(async item => {
|
||||
let name
|
||||
let fileName
|
||||
if (autoRename) {
|
||||
fileName = fecha.format(new Date(), 'YYYYMMDDHHmmss') + path.extname(item)
|
||||
} else {
|
||||
fileName = path.basename(item)
|
||||
}
|
||||
if (rename) {
|
||||
const window = createRenameWindow()
|
||||
await waitForShow(window.webContents)
|
||||
window.webContents.send('rename', fileName, window.webContents.id)
|
||||
name = await waitForRename(window, window.webContents.id)
|
||||
}
|
||||
let buffer = await fs.readFile(item)
|
||||
let base64Image = Buffer.from(buffer, 'binary').toString('base64')
|
||||
let imgSize = sizeOf(item)
|
||||
results.push({
|
||||
base64Image,
|
||||
fileName: name || fileName,
|
||||
width: imgSize.width,
|
||||
height: imgSize.height,
|
||||
extname: path.extname(item)
|
||||
})
|
||||
}))
|
||||
return results
|
||||
}
|
||||
|
||||
const imgFromClipboard = async (file) => {
|
||||
let result = []
|
||||
let rename = db.read().get('picBed.rename').value()
|
||||
if (file !== null) {
|
||||
let name
|
||||
const today = fecha.format(new Date(), 'YYYYMMDDHHmmss') + '.png'
|
||||
if (rename) {
|
||||
const window = createRenameWindow()
|
||||
await waitForShow(window.webContents)
|
||||
window.webContents.send('rename', today, window.webContents.id)
|
||||
name = await waitForRename(window, window.webContents.id)
|
||||
}
|
||||
result.push({
|
||||
base64Image: file.imgUrl.replace(/^data\S+,/, ''),
|
||||
fileName: name || today,
|
||||
width: file.width,
|
||||
height: file.height,
|
||||
extname: '.png'
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const imgFromUploader = async (files) => {
|
||||
let results = []
|
||||
const rename = db.read().get('picBed.rename').value()
|
||||
const autoRename = db.read().get('picBed.autoRename').value()
|
||||
await Promise.all(files.map(async item => {
|
||||
let name
|
||||
let fileName
|
||||
if (autoRename) {
|
||||
fileName = fecha.format(new Date(), 'YYYYMMDDHHmmss') + path.extname(item.name)
|
||||
} else {
|
||||
fileName = path.basename(item.path)
|
||||
}
|
||||
if (rename) {
|
||||
const window = createRenameWindow()
|
||||
await waitForShow(window.webContents)
|
||||
window.webContents.send('rename', fileName, window.webContents.id)
|
||||
name = await waitForRename(window, window.webContents.id)
|
||||
}
|
||||
let buffer = await fs.readFile(item.path)
|
||||
let base64Image = Buffer.from(buffer, 'binary').toString('base64')
|
||||
let imgSize = sizeOf(item.path)
|
||||
results.push({
|
||||
base64Image,
|
||||
fileName: name || fileName,
|
||||
width: imgSize.width,
|
||||
height: imgSize.height,
|
||||
extname: path.extname(item.name)
|
||||
})
|
||||
}))
|
||||
return results
|
||||
}
|
||||
|
||||
const waitForShow = (webcontent) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
webcontent.on('dom-ready', () => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const waitForRename = (window, id) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
ipcMain.once(`rename${id}`, (evt, newName) => {
|
||||
resolve(newName)
|
||||
window.hide()
|
||||
})
|
||||
window.on('close', () => {
|
||||
resolve(null)
|
||||
ipcMain.removeAllListeners(`rename${id}`)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export {
|
||||
imgFromPath,
|
||||
imgFromClipboard,
|
||||
imgFromUploader
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
import path from 'path'
|
||||
import GuiApi from './guiApi'
|
||||
|
||||
// eslint-disable-next-line
|
||||
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
|
||||
const PicGo = requireFunc('picgo')
|
||||
const PluginHandler = requireFunc('picgo/dist/lib/PluginHandler').default
|
||||
|
||||
// get uploader or transformer config
|
||||
const getConfig = (name, type, ctx) => {
|
||||
let config = []
|
||||
if (name === '') {
|
||||
return config
|
||||
} else {
|
||||
const handler = ctx.helper[type].get(name)
|
||||
if (handler) {
|
||||
if (handler.config) {
|
||||
config = handler.config(ctx)
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfigWithFunction = config => {
|
||||
for (let i in config) {
|
||||
if (typeof config[i].default === 'function') {
|
||||
config[i].default = config[i].default()
|
||||
}
|
||||
if (typeof config[i].choices === 'function') {
|
||||
config[i].choices = config[i].choices()
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
const handleGetPluginList = (ipcMain, STORE_PATH, CONFIG_PATH) => {
|
||||
ipcMain.on('getPluginList', event => {
|
||||
const picgo = new PicGo(CONFIG_PATH)
|
||||
const pluginList = picgo.pluginLoader.getList()
|
||||
const list = []
|
||||
for (let i in pluginList) {
|
||||
const plugin = picgo.pluginLoader.getPlugin(pluginList[i])
|
||||
const pluginPath = path.join(STORE_PATH, `/node_modules/${pluginList[i]}`)
|
||||
const pluginPKG = requireFunc(path.join(pluginPath, 'package.json'))
|
||||
const uploaderName = plugin.uploader || ''
|
||||
const transformerName = plugin.transformer || ''
|
||||
let menu = []
|
||||
if (plugin.guiMenu) {
|
||||
menu = plugin.guiMenu(picgo)
|
||||
}
|
||||
const obj = {
|
||||
name: pluginList[i].replace(/picgo-plugin-/, ''),
|
||||
author: pluginPKG.author.name || pluginPKG.author,
|
||||
description: pluginPKG.description,
|
||||
logo: 'file://' + path.join(pluginPath, 'logo.png').split(path.sep).join('/'),
|
||||
version: pluginPKG.version,
|
||||
gui: pluginPKG.gui || false,
|
||||
config: {
|
||||
plugin: {
|
||||
name: pluginList[i].replace(/picgo-plugin-/, ''),
|
||||
config: plugin.config ? handleConfigWithFunction(plugin.config(picgo)) : []
|
||||
},
|
||||
uploader: {
|
||||
name: uploaderName,
|
||||
config: handleConfigWithFunction(getConfig(uploaderName, 'uploader', picgo))
|
||||
},
|
||||
transformer: {
|
||||
name: transformerName,
|
||||
config: handleConfigWithFunction(getConfig(uploaderName, 'transformer', picgo))
|
||||
}
|
||||
},
|
||||
enabled: picgo.getConfig(`plugins.${pluginList[i]}`),
|
||||
homepage: pluginPKG.homepage ? pluginPKG.homepage : '',
|
||||
guiMenu: menu,
|
||||
ing: false
|
||||
}
|
||||
list.push(obj)
|
||||
}
|
||||
event.sender.send('pluginList', list)
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginInstall = (ipcMain, CONFIG_PATH) => {
|
||||
ipcMain.on('installPlugin', (event, msg) => {
|
||||
const picgo = new PicGo(CONFIG_PATH)
|
||||
const pluginHandler = new PluginHandler(picgo)
|
||||
picgo.on('installSuccess', notice => {
|
||||
event.sender.send('installSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
||||
})
|
||||
pluginHandler.install([msg])
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginUninstall = (ipcMain, CONFIG_PATH) => {
|
||||
ipcMain.on('uninstallPlugin', (event, msg) => {
|
||||
const picgo = new PicGo(CONFIG_PATH)
|
||||
const pluginHandler = new PluginHandler(picgo)
|
||||
picgo.on('uninstallSuccess', notice => {
|
||||
event.sender.send('uninstallSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
||||
})
|
||||
pluginHandler.uninstall([msg])
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginUpdate = (ipcMain, CONFIG_PATH) => {
|
||||
ipcMain.on('updatePlugin', (event, msg) => {
|
||||
const picgo = new PicGo(CONFIG_PATH)
|
||||
const pluginHandler = new PluginHandler(picgo)
|
||||
picgo.on('updateSuccess', notice => {
|
||||
event.sender.send('updateSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
||||
})
|
||||
pluginHandler.update([msg])
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
const handleGetPicBedConfig = (ipcMain, CONFIG_PATH) => {
|
||||
ipcMain.on('getPicBedConfig', (event, type) => {
|
||||
const picgo = new PicGo(CONFIG_PATH)
|
||||
const name = picgo.helper.uploader.get(type).name || type
|
||||
if (picgo.helper.uploader.get(type).config) {
|
||||
const config = handleConfigWithFunction(picgo.helper.uploader.get(type).config(picgo))
|
||||
event.sender.send('getPicBedConfig', config, name)
|
||||
} else {
|
||||
event.sender.send('getPicBedConfig', [], name)
|
||||
}
|
||||
picgo.cmd.program.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
const handlePluginActions = (ipcMain, CONFIG_PATH) => {
|
||||
ipcMain.on('pluginActions', (event, name, label) => {
|
||||
const picgo = new PicGo(CONFIG_PATH)
|
||||
const plugin = picgo.pluginLoader.getPlugin(`picgo-plugin-${name}`)
|
||||
const guiApi = new GuiApi(ipcMain, event.sender, picgo)
|
||||
if (plugin.guiMenu && plugin.guiMenu.length > 0) {
|
||||
const menu = plugin.guiMenu(picgo)
|
||||
menu.forEach(item => {
|
||||
if (item.label === label) {
|
||||
item.handle(picgo, guiApi)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleRemoveFiles = (ipcMain, CONFIG_PATH) => {
|
||||
ipcMain.on('removeFiles', (event, files) => {
|
||||
const picgo = new PicGo(CONFIG_PATH)
|
||||
picgo.emit('remove', files)
|
||||
})
|
||||
}
|
||||
|
||||
export default (app, ipcMain) => {
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
|
||||
handleGetPluginList(ipcMain, STORE_PATH, CONFIG_PATH)
|
||||
handlePluginInstall(ipcMain, CONFIG_PATH)
|
||||
handlePluginUninstall(ipcMain, CONFIG_PATH)
|
||||
handlePluginUpdate(ipcMain, CONFIG_PATH)
|
||||
handleGetPicBedConfig(ipcMain, CONFIG_PATH)
|
||||
handlePluginActions(ipcMain, CONFIG_PATH)
|
||||
handleRemoveFiles(ipcMain, CONFIG_PATH)
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
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 path = db.read().get('picBed.qiniu.path').value() || ''
|
||||
const base64FileName = Buffer.from(path + fileName, 'utf-8').toString('base64').replace(/\+/g, '-').replace(/\//g, '_')
|
||||
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
|
||||
@@ -1,55 +0,0 @@
|
||||
import request from 'request-promise'
|
||||
import * as img2Base64 from './img2base64'
|
||||
import { Notification } from 'electron'
|
||||
|
||||
const postOptions = (fileName, imgBase64) => {
|
||||
return {
|
||||
method: 'POST',
|
||||
url: `https://sm.ms/api/upload`,
|
||||
headers: {
|
||||
contentType: 'multipart/form-data',
|
||||
'User-Agent': 'PicGo'
|
||||
},
|
||||
formData: {
|
||||
smfile: {
|
||||
value: Buffer.from(imgBase64, 'base64'),
|
||||
options: {
|
||||
filename: fileName
|
||||
}
|
||||
},
|
||||
ssl: 'true'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const smmsUpload = async function (img, type, webContents) {
|
||||
try {
|
||||
webContents.send('uploadProgress', 0)
|
||||
const imgList = await img2Base64[type](img)
|
||||
webContents.send('uploadProgress', 30)
|
||||
for (let i in imgList) {
|
||||
const postConfig = postOptions(imgList[i].fileName, imgList[i].base64Image)
|
||||
let body = await request(postConfig)
|
||||
body = JSON.parse(body)
|
||||
if (body.code === 'success') {
|
||||
delete imgList[i].base64Image
|
||||
imgList[i]['imgUrl'] = body.data.url
|
||||
} else {
|
||||
webContents.send('uploadProgress', -1)
|
||||
return new Error()
|
||||
}
|
||||
}
|
||||
webContents.send('uploadProgress', 100)
|
||||
return imgList
|
||||
} catch (err) {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: '服务端出错,请重试'
|
||||
})
|
||||
notification.show()
|
||||
throw new Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
export default smmsUpload
|
||||
@@ -1,155 +0,0 @@
|
||||
import request from 'request-promise'
|
||||
import * as img2Base64 from './img2base64'
|
||||
import db from '../../datastore/index'
|
||||
import { Notification, clipboard } from 'electron'
|
||||
import crypto from 'crypto'
|
||||
|
||||
// generate COS signature string
|
||||
const generateSignature = (fileName) => {
|
||||
const options = db.read().get('picBed.tcyun').value()
|
||||
const secretId = options.secretId
|
||||
const secretKey = options.secretKey
|
||||
const appId = options.appId
|
||||
const bucket = options.bucket
|
||||
let signature
|
||||
let signTime
|
||||
if (!options.version || options.version === 'v4') {
|
||||
const random = Math.floor(Math.random() * 10000000000)
|
||||
const current = parseInt(new Date().getTime() / 1000) - 1
|
||||
const expired = current + 3600
|
||||
|
||||
const multiSignature = `a=${appId}&b=${bucket}&k=${secretId}&e=${expired}&t=${current}&r=${random}&f=`
|
||||
|
||||
const signHexKey = crypto.createHmac('sha1', secretKey).update(multiSignature).digest()
|
||||
const tempString = Buffer.concat([signHexKey, Buffer.from(multiSignature)])
|
||||
signature = Buffer.from(tempString).toString('base64')
|
||||
} else {
|
||||
// https://cloud.tencent.com/document/product/436/7778#signature
|
||||
const today = Math.floor(new Date().getTime() / 1000)
|
||||
const tomorrow = today + 86400
|
||||
signTime = `${today};${tomorrow}`
|
||||
const signKey = crypto.createHmac('sha1', secretKey).update(signTime).digest('hex')
|
||||
const httpString = `put\n/${options.path}${fileName}\n\nhost=${options.bucket}.cos.${options.area}.myqcloud.com\n`
|
||||
const sha1edHttpString = crypto.createHash('sha1').update(httpString).digest('hex')
|
||||
const stringToSign = `sha1\n${signTime}\n${sha1edHttpString}\n`
|
||||
signature = crypto.createHmac('sha1', signKey).update(stringToSign).digest('hex')
|
||||
}
|
||||
return {
|
||||
signature,
|
||||
appId,
|
||||
bucket,
|
||||
signTime
|
||||
}
|
||||
}
|
||||
|
||||
const postOptions = (fileName, signature, imgBase64) => {
|
||||
const options = db.read().get('picBed.tcyun').value()
|
||||
const area = options.area
|
||||
const path = options.path
|
||||
if (!options.version || options.version === 'v4') {
|
||||
return {
|
||||
method: 'POST',
|
||||
url: `http://${area}.file.myqcloud.com/files/v2/${signature.appId}/${signature.bucket}/${encodeURI(path)}${fileName}`,
|
||||
headers: {
|
||||
Host: `${area}.file.myqcloud.com`,
|
||||
Authorization: signature.signature,
|
||||
contentType: 'multipart/form-data'
|
||||
},
|
||||
formData: {
|
||||
op: 'upload',
|
||||
filecontent: Buffer.from(imgBase64, 'base64')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
method: 'PUT',
|
||||
url: `http://${options.bucket}.cos.${options.area}.myqcloud.com/${encodeURI(path)}${encodeURI(fileName)}`,
|
||||
headers: {
|
||||
Host: `${options.bucket}.cos.${options.area}.myqcloud.com`,
|
||||
Authorization: `q-sign-algorithm=sha1&q-ak=${options.secretId}&q-sign-time=${signature.signTime}&q-key-time=${signature.signTime}&q-header-list=host&q-url-param-list=&q-signature=${signature.signature}`,
|
||||
contentType: 'multipart/form-data'
|
||||
},
|
||||
body: Buffer.from(imgBase64, 'base64'),
|
||||
resolveWithFullResponse: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tcYunUpload = async (img, type, webContents) => {
|
||||
try {
|
||||
webContents.send('uploadProgress', 0)
|
||||
const imgList = await img2Base64[type](img)
|
||||
webContents.send('uploadProgress', 30)
|
||||
const length = imgList.length
|
||||
const tcYunOptions = db.read().get('picBed.tcyun').value()
|
||||
const customUrl = tcYunOptions.customUrl
|
||||
const path = tcYunOptions.path
|
||||
const useV4 = !tcYunOptions.version || tcYunOptions.version === 'v4'
|
||||
for (let i in imgList) {
|
||||
const singature = generateSignature(imgList[i].fileName)
|
||||
const options = postOptions(imgList[i].fileName, singature, imgList[i].base64Image)
|
||||
const res = await request(options)
|
||||
.then(res => res)
|
||||
.catch(err => {
|
||||
console.log(err.response.body)
|
||||
return {
|
||||
statusCode: 400,
|
||||
body: {
|
||||
msg: '认证失败!'
|
||||
}
|
||||
}
|
||||
})
|
||||
let body
|
||||
if (useV4) {
|
||||
body = JSON.parse(res)
|
||||
} else {
|
||||
body = res
|
||||
}
|
||||
if (useV4 && body.message === 'SUCCESS') {
|
||||
delete imgList[i].base64Image
|
||||
if (customUrl) {
|
||||
imgList[i]['imgUrl'] = `${customUrl}/${path}${imgList[i].fileName}`
|
||||
} else {
|
||||
imgList[i]['imgUrl'] = body.data.source_url
|
||||
}
|
||||
imgList[i]['type'] = 'tcyun'
|
||||
if (i - length === -1) {
|
||||
webContents.send('uploadProgress', 60)
|
||||
}
|
||||
} else if (!useV4 && body && body.statusCode === 200) {
|
||||
delete imgList[i].base64Image
|
||||
if (customUrl) {
|
||||
imgList[i]['imgUrl'] = `${customUrl}/${path}${imgList[i].fileName}`
|
||||
} else {
|
||||
imgList[i]['imgUrl'] = `https://${tcYunOptions.bucket}.cos.${tcYunOptions.area}.myqcloud.com/${path}${imgList[i].fileName}`
|
||||
}
|
||||
imgList[i]['type'] = 'tcyun'
|
||||
if (i - length === -1) {
|
||||
webContents.send('uploadProgress', 60)
|
||||
}
|
||||
} else {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: res.body.msg
|
||||
})
|
||||
notification.show()
|
||||
return false
|
||||
}
|
||||
}
|
||||
webContents.send('uploadProgress', 100)
|
||||
return imgList
|
||||
} catch (err) {
|
||||
const body = JSON.parse(err.error)
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: `错误码:${body.code},请打开浏览器粘贴地址查看相关原因。`
|
||||
})
|
||||
notification.show()
|
||||
clipboard.writeText('https://cloud.tencent.com/document/product/436/8432')
|
||||
throw new Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
export default tcYunUpload
|
||||
@@ -1,78 +0,0 @@
|
||||
import request from 'request-promise'
|
||||
import * as img2Base64 from './img2base64'
|
||||
import db from '../../datastore/index'
|
||||
import { Notification, clipboard } from 'electron'
|
||||
import crypto from 'crypto'
|
||||
import MD5 from 'md5'
|
||||
|
||||
// generate COS signature string
|
||||
const generateSignature = (fileName) => {
|
||||
const options = db.read().get('picBed.upyun').value()
|
||||
const path = options.path || ''
|
||||
const operator = options.operator
|
||||
const password = options.password
|
||||
const md5Password = MD5(password)
|
||||
const date = new Date().toGMTString()
|
||||
const uri = `/${options.bucket}/${encodeURI(path)}${encodeURI(fileName)}`
|
||||
const value = `PUT&${uri}&${date}`
|
||||
const sign = crypto.createHmac('sha1', md5Password).update(value).digest('base64')
|
||||
return `UPYUN ${operator}:${sign}`
|
||||
}
|
||||
|
||||
const postOptions = (fileName, signature, imgBase64) => {
|
||||
const options = db.read().get('picBed.upyun').value()
|
||||
const bucket = options.bucket
|
||||
const path = options.path || ''
|
||||
return {
|
||||
method: 'PUT',
|
||||
url: `https://v0.api.upyun.com/${bucket}/${encodeURI(path)}${encodeURI(fileName)}`,
|
||||
headers: {
|
||||
Authorization: signature,
|
||||
Date: new Date().toGMTString()
|
||||
},
|
||||
body: Buffer.from(imgBase64, 'base64'),
|
||||
resolveWithFullResponse: true
|
||||
}
|
||||
}
|
||||
|
||||
const upYunUpload = async (img, type, webContents) => {
|
||||
try {
|
||||
webContents.send('uploadProgress', 0)
|
||||
const imgList = await img2Base64[type](img)
|
||||
webContents.send('uploadProgress', 30)
|
||||
const length = imgList.length
|
||||
const upyunOptions = db.read().get('picBed.upyun').value()
|
||||
const path = upyunOptions.path || ''
|
||||
for (let i in imgList) {
|
||||
const singature = generateSignature(imgList[i].fileName)
|
||||
const options = postOptions(imgList[i].fileName, singature, imgList[i].base64Image)
|
||||
const body = await request(options)
|
||||
if (body.statusCode === 200) {
|
||||
delete imgList[i].base64Image
|
||||
imgList[i]['imgUrl'] = `${upyunOptions.url}/${path}${imgList[i].fileName}${upyunOptions.options}`
|
||||
imgList[i]['type'] = 'upyun'
|
||||
if (i - length === -1) {
|
||||
webContents.send('uploadProgress', 60)
|
||||
}
|
||||
} else {
|
||||
webContents.send('uploadProgress', -1)
|
||||
return new Error()
|
||||
}
|
||||
}
|
||||
webContents.send('uploadProgress', 100)
|
||||
return imgList
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
const body = JSON.parse(err.error)
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: `错误码:${body.code},请打开浏览器粘贴地址查看相关原因。`
|
||||
})
|
||||
notification.show()
|
||||
clipboard.writeText('http://docs.upyun.com/api/errno/')
|
||||
// throw new Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
export default upYunUpload
|
||||
@@ -7,9 +7,9 @@ const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
|
||||
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
|
||||
|
||||
const checkVersion = async () => {
|
||||
let showTip = db.read().get('picBed.showUpdateTip').value()
|
||||
let showTip = db.read().get('settings.showUpdateTip').value()
|
||||
if (showTip === undefined) {
|
||||
db.read().set('picBed.showUpdateTip', true).write()
|
||||
db.read().set('settings.showUpdateTip', true).write()
|
||||
showTip = true
|
||||
}
|
||||
if (showTip) {
|
||||
@@ -22,14 +22,14 @@ const checkVersion = async () => {
|
||||
type: 'info',
|
||||
title: '发现新版本',
|
||||
buttons: ['Yes', 'No'],
|
||||
message: '发现新版本,更新了很多功能,是否去下载最新的版本?',
|
||||
message: `发现新版本${latest},更新了很多功能,是否去下载最新的版本?`,
|
||||
checkboxLabel: '以后不再提醒',
|
||||
checkboxChecked: false
|
||||
}, (res, checkboxChecked) => {
|
||||
if (res === 0) { // if selected yes
|
||||
shell.openExternal(downloadUrl)
|
||||
}
|
||||
db.read().set('picBed.showUpdateTip', !checkboxChecked).write()
|
||||
db.read().set('settings.showUpdateTip', !checkboxChecked).write()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
|
||||
+147
-21
@@ -1,30 +1,156 @@
|
||||
import db from '../../datastore/index'
|
||||
import { Notification } from 'electron'
|
||||
import picBeds from '../../datastore/pic-bed-handler'
|
||||
import {
|
||||
app,
|
||||
Notification,
|
||||
BrowserWindow,
|
||||
ipcMain
|
||||
} from 'electron'
|
||||
import path from 'path'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const checkUploader = (type) => {
|
||||
const currentUploader = db.read().get(`picBed.${type}`).value()
|
||||
if (currentUploader) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
// eslint-disable-next-line
|
||||
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
|
||||
const PicGo = requireFunc('picgo')
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
|
||||
const renameURL = process.env.NODE_ENV === 'development' ? `http://localhost:9080/#rename-page` : `file://${__dirname}/index.html#rename-page`
|
||||
|
||||
const createRenameWindow = (win) => {
|
||||
let options = {
|
||||
height: 175,
|
||||
width: 300,
|
||||
show: true,
|
||||
fullscreenable: false,
|
||||
resizable: false,
|
||||
vibrancy: 'ultra-dark',
|
||||
webPreferences: {
|
||||
backgroundThrottling: false
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform !== 'darwin') {
|
||||
options.show = true
|
||||
options.backgroundColor = '#3f3c37'
|
||||
options.autoHideMenuBar = true
|
||||
options.transparent = false
|
||||
}
|
||||
|
||||
const window = new BrowserWindow(options)
|
||||
window.loadURL(renameURL)
|
||||
// check if this window is visible
|
||||
if (win.isVisible()) {
|
||||
// bounds: { x: 821, y: 75, width: 800, height: 450 }
|
||||
const bounds = win.getBounds()
|
||||
const positionX = bounds.x + bounds.width / 2 - 150
|
||||
let positionY
|
||||
// if is the settingWindow
|
||||
if (bounds.height > 400) {
|
||||
positionY = bounds.y + bounds.height / 2 - 88
|
||||
} else { // if is the miniWindow
|
||||
positionY = bounds.y + bounds.height / 2
|
||||
}
|
||||
window.setPosition(positionX, positionY, false)
|
||||
}
|
||||
return window
|
||||
}
|
||||
|
||||
const uploader = (img, type, webContents) => {
|
||||
if (db.read().get('picBed.uploadNotification').value()) {
|
||||
const notification = new Notification({
|
||||
title: '上传进度',
|
||||
body: '正在上传'
|
||||
const waitForShow = (webcontent) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
webcontent.on('dom-ready', () => {
|
||||
resolve()
|
||||
})
|
||||
notification.show()
|
||||
})
|
||||
}
|
||||
|
||||
const waitForRename = (window, id) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
ipcMain.once(`rename${id}`, (evt, newName) => {
|
||||
resolve(newName)
|
||||
window.hide()
|
||||
})
|
||||
window.on('close', () => {
|
||||
resolve(null)
|
||||
ipcMain.removeAllListeners(`rename${id}`)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
class Uploader {
|
||||
constructor (img, webContents, picgo = undefined) {
|
||||
this.img = img
|
||||
this.webContents = webContents
|
||||
this.picgo = picgo
|
||||
}
|
||||
const uploadType = db.read().get('picBed.current').value()
|
||||
if (checkUploader(uploadType)) {
|
||||
return picBeds[uploadType](img, type, webContents)
|
||||
} else {
|
||||
return false
|
||||
|
||||
upload () {
|
||||
const win = BrowserWindow.fromWebContents(this.webContents)
|
||||
const picgo = this.picgo || new PicGo(CONFIG_PATH)
|
||||
picgo.config.debug = true
|
||||
// for picgo-core
|
||||
picgo.config.PICGO_ENV = 'GUI'
|
||||
let input = this.img
|
||||
|
||||
picgo.helper.beforeUploadPlugins.register('renameFn', {
|
||||
handle: async ctx => {
|
||||
const rename = picgo.getConfig('settings.rename')
|
||||
const autoRename = picgo.getConfig('settings.autoRename')
|
||||
await Promise.all(ctx.output.map(async (item, index) => {
|
||||
let name
|
||||
let fileName
|
||||
if (autoRename) {
|
||||
fileName = dayjs().add(index, 'second').format('YYYYMMDDHHmmss') + item.extname
|
||||
} else {
|
||||
fileName = item.fileName
|
||||
}
|
||||
if (rename) {
|
||||
const window = createRenameWindow(win)
|
||||
await waitForShow(window.webContents)
|
||||
window.webContents.send('rename', fileName, window.webContents.id)
|
||||
name = await waitForRename(window, window.webContents.id)
|
||||
}
|
||||
item.fileName = name || fileName
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
picgo.on('beforeTransform', ctx => {
|
||||
if (ctx.getConfig('settings.uploadNotification')) {
|
||||
const notification = new Notification({
|
||||
title: '上传进度',
|
||||
body: '正在上传'
|
||||
})
|
||||
notification.show()
|
||||
}
|
||||
})
|
||||
|
||||
picgo.upload(input)
|
||||
|
||||
picgo.on('notification', message => {
|
||||
const notification = new Notification(message)
|
||||
notification.show()
|
||||
})
|
||||
|
||||
picgo.on('uploadProgress', progress => {
|
||||
this.webContents.send('uploadProgress', progress)
|
||||
})
|
||||
|
||||
return new Promise((resolve) => {
|
||||
picgo.on('finished', ctx => {
|
||||
if (ctx.output.every(item => item.imgUrl)) {
|
||||
resolve(ctx.output)
|
||||
} else {
|
||||
resolve(false)
|
||||
}
|
||||
})
|
||||
picgo.on('failed', ctx => {
|
||||
const notification = new Notification({
|
||||
title: '上传失败',
|
||||
body: '请检查配置和上传的文件是否符合要求'
|
||||
})
|
||||
notification.show()
|
||||
resolve(false)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default uploader
|
||||
export default Uploader
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
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'
|
||||
|
||||
const 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 cookie = db.read().get('picBed.weibo.cookie').value()
|
||||
const chooseCookie = db.read().get('picBed.weibo.chooseCookie').value()
|
||||
const options = postOptions(formData)
|
||||
let res
|
||||
if (!chooseCookie) {
|
||||
res = await rp(options)
|
||||
}
|
||||
webContents.send('uploadProgress', 30)
|
||||
if (chooseCookie || res.body.retcode === 20000000) {
|
||||
if (res) {
|
||||
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 opt = {
|
||||
formData: {
|
||||
b64_data: imgList[i].base64Image
|
||||
}
|
||||
}
|
||||
if (chooseCookie) {
|
||||
opt.headers = {
|
||||
Cookie: cookie
|
||||
}
|
||||
}
|
||||
let result = await rp.post(UPLOAD_URL, opt)
|
||||
result = result.replace(/<.*?\/>/, '').replace(/<(\w+).*?>.*?<\/\1>/, '')
|
||||
delete imgList[i].base64Image
|
||||
const resTextJson = JSON.parse(result)
|
||||
if (resTextJson.data.pics.pic_1.pid === undefined) {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: '微博Cookie失效,请在网页版重新登录一遍'
|
||||
})
|
||||
notification.show()
|
||||
return new Error()
|
||||
} else {
|
||||
const extname = imgList[i].extname === '.gif' ? '.gif' : '.jpg'
|
||||
imgList[i]['imgUrl'] = `https://ws1.sinaimg.cn/${quality}/${resTextJson.data.pics.pic_1.pid}${extname}`
|
||||
imgList[i]['type'] = 'weibo'
|
||||
}
|
||||
delete imgList[i].extname
|
||||
}
|
||||
webContents.send('uploadProgress', 100)
|
||||
return imgList
|
||||
} else {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: res.body.msg
|
||||
})
|
||||
notification.show()
|
||||
return new Error()
|
||||
}
|
||||
} catch (err) {
|
||||
webContents.send('uploadProgress', -1)
|
||||
const notification = new Notification({
|
||||
title: '上传失败!',
|
||||
body: '服务端出错,请重试'
|
||||
})
|
||||
notification.show()
|
||||
throw new Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
export default weiboUpload
|
||||
@@ -18,18 +18,10 @@
|
||||
height 100%
|
||||
font-family "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif
|
||||
#app
|
||||
overflow-x hidden
|
||||
user-select none
|
||||
overflow hidden
|
||||
.el-button-group
|
||||
width 100%
|
||||
.el-button
|
||||
width 50%
|
||||
*::-webkit-scrollbar
|
||||
width 8px
|
||||
height 8px
|
||||
*::-webkit-scrollbar-thumb
|
||||
border-radius 4px
|
||||
background #6f6f6f
|
||||
*::-webkit-scrollbar-track
|
||||
background-color transparent
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<div id="config-form">
|
||||
<el-form
|
||||
label-position="right"
|
||||
label-width="120px"
|
||||
:model="ruleForm"
|
||||
ref="form"
|
||||
size="mini"
|
||||
>
|
||||
<el-form-item
|
||||
v-for="(item, index) in configList"
|
||||
:label="item.name"
|
||||
:required="item.required"
|
||||
:prop="item.name"
|
||||
:key="item.name + index"
|
||||
>
|
||||
<el-input
|
||||
v-if="item.type === 'input' || item.type === 'password'"
|
||||
:type="item.type === 'password' ? 'password' : 'input'"
|
||||
v-model="ruleForm[item.name]"
|
||||
:placeholder="item.message || item.name"
|
||||
></el-input>
|
||||
<el-select
|
||||
v-else-if="item.type === 'list'"
|
||||
v-model="ruleForm[item.name]"
|
||||
:placeholder="item.message || item.name"
|
||||
>
|
||||
<el-option
|
||||
v-for="(choice, idx) in item.choices"
|
||||
:label="choice.name || choice.value || choice"
|
||||
:key="choice.name || choice.value || choice"
|
||||
:value="choice.value || choice"
|
||||
></el-option>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-else-if="item.type === 'checkbox'"
|
||||
v-model="ruleForm[item.name]"
|
||||
:placeholder="item.message || item.name"
|
||||
multiple
|
||||
collapse-tags
|
||||
>
|
||||
<el-option
|
||||
v-for="(choice, idx) in item.choices"
|
||||
:label="choice.name || choice.value || choice"
|
||||
:key="choice.value || choice"
|
||||
:value="choice.value || choice"
|
||||
></el-option>
|
||||
</el-select>
|
||||
<el-switch
|
||||
v-else-if="item.type === 'confirm'"
|
||||
v-model="ruleForm[item.name]"
|
||||
active-text="yes"
|
||||
inactive-text="no"
|
||||
>
|
||||
</el-switch>
|
||||
</el-form-item>
|
||||
<slot></slot>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { cloneDeep, union } from 'lodash'
|
||||
export default {
|
||||
name: 'config-form',
|
||||
props: {
|
||||
config: Array,
|
||||
type: String,
|
||||
id: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
configList: [],
|
||||
ruleForm: {}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
},
|
||||
watch: {
|
||||
config: {
|
||||
deep: true,
|
||||
handler (val) {
|
||||
this.ruleForm = Object.assign({}, {})
|
||||
const config = this.$db.read().get(`picBed.${this.id}`).value()
|
||||
if (val.length > 0) {
|
||||
this.configList = cloneDeep(val).map(item => {
|
||||
let defaultValue = item.default !== undefined
|
||||
? item.default : item.type === 'checkbox'
|
||||
? [] : null
|
||||
if (item.type === 'checkbox') {
|
||||
const defaults = item.choices.filter(i => {
|
||||
return i.checked
|
||||
}).map(i => i.value)
|
||||
defaultValue = union(defaultValue, defaults)
|
||||
}
|
||||
if (config && config[item.name] !== undefined) {
|
||||
defaultValue = config[item.name]
|
||||
}
|
||||
this.$set(this.ruleForm, item.name, defaultValue)
|
||||
return item
|
||||
})
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async validate () {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
resolve(this.ruleForm)
|
||||
} else {
|
||||
resolve(false)
|
||||
return false
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
#config-form
|
||||
.el-form
|
||||
label
|
||||
line-height 22px
|
||||
padding-bottom 0
|
||||
.el-button-group
|
||||
width 100%
|
||||
.el-button
|
||||
width 50%
|
||||
.el-input__inner
|
||||
border-radius 19px
|
||||
.el-radio-group
|
||||
margin-left 25px
|
||||
.el-switch__label
|
||||
&.is-active
|
||||
color #409EFF
|
||||
</style>
|
||||
@@ -1,196 +0,0 @@
|
||||
<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-edit-outline" @click="openDialog(item)"></i>
|
||||
<i class="el-icon-delete" @click="remove(item.id)"></i>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog
|
||||
:visible.sync="dialogVisible"
|
||||
title="修改图片URL"
|
||||
width="500px"
|
||||
:modal-append-to-body="false"
|
||||
>
|
||||
<el-input v-model="imgInfo.imgUrl"></el-input>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="confirmModify">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</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
|
||||
},
|
||||
dialogVisible: false,
|
||||
imgInfo: {
|
||||
id: null,
|
||||
imgUrl: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
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
|
||||
})
|
||||
},
|
||||
openDialog (item) {
|
||||
this.imgInfo.id = item.id
|
||||
this.imgInfo.imgUrl = item.imgUrl
|
||||
this.dialogVisible = true
|
||||
},
|
||||
confirmModify () {
|
||||
this.$db.read().get('uploaded')
|
||||
.getById(this.imgInfo.id)
|
||||
.assign({imgUrl: this.imgInfo.imgUrl})
|
||||
.write()
|
||||
const obj = {
|
||||
title: '修改图片URL成功',
|
||||
body: this.imgInfo.imgUrl,
|
||||
icon: this.imgInfo.imgUrl
|
||||
}
|
||||
const myNotification = new window.Notification(obj.title, obj)
|
||||
myNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
this.dialogVisible = false
|
||||
this.getGallery()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 10px auto
|
||||
.sub-title
|
||||
font-size 14px
|
||||
#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-edit-outline
|
||||
&:hover
|
||||
color #69C282
|
||||
&.el-icon-delete
|
||||
&:hover
|
||||
color #F15140
|
||||
.blueimp-gallery
|
||||
.title
|
||||
top 30px
|
||||
</style>
|
||||
@@ -1,9 +1,12 @@
|
||||
<template>
|
||||
<div id="setting-page">
|
||||
<div class="fake-title-bar">
|
||||
PicGo - {{ version }}
|
||||
<div class="handle-bar" v-if="os === 'win32'">
|
||||
<div class="fake-title-bar" :class="{ 'darwin': os === 'darwin' }">
|
||||
<div class="fake-title-bar__title">
|
||||
PicGo - {{ version }}
|
||||
</div>
|
||||
<div class="handle-bar" v-if="os !== 'darwin'">
|
||||
<i class="el-icon-minus" @click="minimizeWindow"></i>
|
||||
<i class="el-icon-circle-plus-outline" @click="openMiniWindow"></i>
|
||||
<i class="el-icon-close" @click="closeWindow"></i>
|
||||
</div>
|
||||
</div>
|
||||
@@ -35,7 +38,7 @@
|
||||
>
|
||||
<el-menu-item
|
||||
v-if="item.visible"
|
||||
:index="item.type"
|
||||
:index="`picbeds-${item.type}`"
|
||||
:key="item.type"
|
||||
>
|
||||
<!-- <i :class="`el-icon-ui-${item.type}`"></i> -->
|
||||
@@ -47,13 +50,24 @@
|
||||
<i class="el-icon-setting"></i>
|
||||
<span slot="title">PicGo设置</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="plugin">
|
||||
<i class="el-icon-share"></i>
|
||||
<span slot="title">插件设置</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
<i class="el-icon-info setting-window" @click="openDialog"></i>
|
||||
</el-col>
|
||||
<el-col :span="19" :offset="5">
|
||||
<router-view></router-view>
|
||||
<el-col
|
||||
:span="19"
|
||||
:offset="5"
|
||||
style="height: 428px"
|
||||
class="main-wrapper"
|
||||
:class="{ 'darwin': os === 'darwin' }">
|
||||
<transition name="picgo-fade" mode="out-in">
|
||||
<router-view :key="$route.params ? $route.params.type : $route.path"></router-view>
|
||||
</transition>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-row>
|
||||
<el-dialog
|
||||
title="赞助PicGo"
|
||||
:visible.sync="visible"
|
||||
@@ -127,13 +141,15 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import pkg from '../../../package.json'
|
||||
import pkg from 'root/package.json'
|
||||
import keyDetect from 'utils/key-binding'
|
||||
import { remote } from 'electron'
|
||||
import db from '../../datastore'
|
||||
import db from '~/datastore'
|
||||
import mixin from '@/utils/mixin'
|
||||
const { Menu, dialog, BrowserWindow } = remote
|
||||
export default {
|
||||
name: 'setting-page',
|
||||
mixins: [mixin],
|
||||
data () {
|
||||
const customLinkRule = (rule, value, callback) => {
|
||||
if (!/\$url/.test(value)) {
|
||||
@@ -161,18 +177,37 @@ export default {
|
||||
shortKey: {
|
||||
upload: db.read().get('shortKey.upload').value()
|
||||
},
|
||||
picBed: this.$picBed
|
||||
picBed: []
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.os = process.platform
|
||||
this.buildMenu()
|
||||
this.$electron.ipcRenderer.send('getPicBeds')
|
||||
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
|
||||
},
|
||||
methods: {
|
||||
handleSelect (index) {
|
||||
this.$router.push({
|
||||
name: index
|
||||
})
|
||||
const type = index.match(/picbeds-/)
|
||||
if (type === null) {
|
||||
this.$router.push({
|
||||
name: index
|
||||
})
|
||||
} else {
|
||||
const picBed = index.replace(/picbeds-/, '')
|
||||
if (this.$builtInPicBed.includes(picBed)) {
|
||||
this.$router.push({
|
||||
name: picBed
|
||||
})
|
||||
} else {
|
||||
this.$router.push({
|
||||
name: 'others',
|
||||
params: {
|
||||
type: picBed
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
minimizeWindow () {
|
||||
const window = BrowserWindow.getFocusedWindow()
|
||||
@@ -205,7 +240,7 @@ export default {
|
||||
this.menu = Menu.buildFromTemplate(template)
|
||||
},
|
||||
openDialog () {
|
||||
this.menu.popup(remote.getCurrentWindow)
|
||||
this.menu.popup(remote.getCurrentWindow())
|
||||
},
|
||||
keyDetect (type, event) {
|
||||
this.shortKey[type] = keyDetect(event).join('+')
|
||||
@@ -234,16 +269,39 @@ export default {
|
||||
return false
|
||||
}
|
||||
})
|
||||
},
|
||||
openMiniWindow () {
|
||||
this.$electron.ipcRenderer.send('openMiniWindow')
|
||||
},
|
||||
getPicBeds (event, picBeds) {
|
||||
this.picBed = picBeds
|
||||
}
|
||||
},
|
||||
beforeRouteEnter: (to, from, next) => {
|
||||
next(vm => {
|
||||
vm.defaultActive = to.name
|
||||
})
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
$darwinBg = transparentify(#172426, #000, 0.7)
|
||||
.picgo-fade
|
||||
&-enter,
|
||||
&-leave,
|
||||
&-leave-active
|
||||
opacity 0
|
||||
&-enter-active,
|
||||
&-leave-active
|
||||
transition opacity 100ms linear
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 10px auto
|
||||
#setting-page
|
||||
.fake-title-bar
|
||||
-webkit-app-region drag
|
||||
@@ -255,11 +313,22 @@ export default {
|
||||
line-height h
|
||||
position fixed
|
||||
z-index 100
|
||||
&.darwin
|
||||
background transparent
|
||||
background-image linear-gradient(
|
||||
to right,
|
||||
transparent 0%,
|
||||
transparent 167px,
|
||||
$darwinBg 167px,
|
||||
$darwinBg 100%
|
||||
)
|
||||
.fake-title-bar__title
|
||||
padding-left 167px
|
||||
.handle-bar
|
||||
position absolute
|
||||
top 2px
|
||||
right 4px
|
||||
width 40px
|
||||
width 60px
|
||||
z-index 10000
|
||||
-webkit-app-region no-drag
|
||||
i
|
||||
@@ -271,6 +340,12 @@ export default {
|
||||
.el-icon-close
|
||||
&:hover
|
||||
color #F15140
|
||||
.el-icon-circle-plus-outline
|
||||
&:hover
|
||||
color #69C282
|
||||
.main-wrapper
|
||||
&.darwin
|
||||
background $darwinBg
|
||||
.side-bar-menu
|
||||
position fixed
|
||||
height calc(100vh - 22px)
|
||||
@@ -281,7 +356,7 @@ export default {
|
||||
position fixed
|
||||
bottom 4px
|
||||
left 4px
|
||||
cursor pointer
|
||||
cursor poiter
|
||||
color #878d99
|
||||
transition .2s all ease-in-out
|
||||
&:hover
|
||||
@@ -334,4 +409,12 @@ export default {
|
||||
.align-center
|
||||
input
|
||||
text-align center
|
||||
*::-webkit-scrollbar
|
||||
width 8px
|
||||
height 8px
|
||||
*::-webkit-scrollbar-thumb
|
||||
border-radius 4px
|
||||
background #6f6f6f
|
||||
*::-webkit-scrollbar-track
|
||||
background-color transparent
|
||||
</style>
|
||||
+13
-2
@@ -8,8 +8,10 @@ import store from './store'
|
||||
import db from '../datastore/index'
|
||||
import { webFrame } from 'electron'
|
||||
import './assets/fonts/iconfont.css'
|
||||
import picBed from '../datastore/pic-bed'
|
||||
import VueLazyLoad from 'vue-lazyload'
|
||||
|
||||
Vue.use(ElementUI)
|
||||
Vue.use(VueLazyLoad)
|
||||
|
||||
webFrame.setVisualZoomLevelLimits(1, 1)
|
||||
webFrame.setLayoutZoomLevelLimits(0, 0)
|
||||
@@ -17,7 +19,16 @@ webFrame.setLayoutZoomLevelLimits(0, 0)
|
||||
if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
|
||||
Vue.http = Vue.prototype.$http = axios
|
||||
Vue.prototype.$db = db
|
||||
Vue.prototype.$picBed = picBed
|
||||
Vue.prototype.$builtInPicBed = [
|
||||
'smms',
|
||||
'weibo',
|
||||
'imgur',
|
||||
'qiniu',
|
||||
'tcyun',
|
||||
'upyun',
|
||||
'aliyun',
|
||||
'github'
|
||||
]
|
||||
Vue.config.productionTip = false
|
||||
|
||||
/* eslint-disable no-new */
|
||||
|
||||
@@ -0,0 +1,455 @@
|
||||
<template>
|
||||
<div id="gallery-view">
|
||||
<div class="view-title">
|
||||
相册 - {{ filterList.length }} <i class="el-icon-caret-bottom" @click="toggleHandleBar" :class="{'active': handleBarActive}"></i>
|
||||
</div>
|
||||
<transition name="el-zoom-in-top">
|
||||
<el-row v-show="handleBarActive">
|
||||
<el-col :span="20" :offset="2">
|
||||
<el-row class="handle-bar" :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-select
|
||||
v-model="choosedPicBed"
|
||||
multiple
|
||||
collapse-tags
|
||||
size="mini"
|
||||
style="width: 100%"
|
||||
placeholder="请选择显示的图床">
|
||||
<el-option
|
||||
v-for="item in picBed"
|
||||
:key="item.type"
|
||||
:label="item.name"
|
||||
:value="item.type">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-select
|
||||
v-model="pasteStyle"
|
||||
size="mini"
|
||||
style="width: 100%"
|
||||
@change="handlePasteStyleChange"
|
||||
placeholder="请选择粘贴的格式">
|
||||
<el-option
|
||||
v-for="(value, key) in pasteStyleMap"
|
||||
:key="key"
|
||||
:label="key"
|
||||
:value="value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="handle-bar" :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-input
|
||||
placeholder="搜索"
|
||||
size="mini"
|
||||
v-model="searchText">
|
||||
<i slot="suffix" class="el-input__icon el-icon-close" v-if="searchText" @click="cleanSearch" style="cursor: pointer"></i>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="item-base copy round" :class="{ active: isMultiple(choosedList)}" @click="multiCopy">
|
||||
<i class="el-icon-document"></i> 批量复制
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="item-base delete round" :class="{ active: isMultiple(choosedList)}" @click="multiRemove">
|
||||
<i class="el-icon-delete"></i> 批量删除
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</transition>
|
||||
<el-row class="gallery-list" :class="{ small: handleBarActive }">
|
||||
<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" class="gallery-list__img">
|
||||
<div
|
||||
class="gallery-list__item"
|
||||
v-lazy:background-image="item.imgUrl"
|
||||
@click="zoomImage(index)"
|
||||
>
|
||||
</div>
|
||||
<div class="gallery-list__tool-panel">
|
||||
<i class="el-icon-document" @click="copy(item)"></i>
|
||||
<i class="el-icon-edit-outline" @click="openDialog(item)"></i>
|
||||
<i class="el-icon-delete" @click="remove(item.id)"></i>
|
||||
<el-checkbox v-model="choosedList[item.id]" class="pull-right" @change=" handleBarActive = true"></el-checkbox>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog
|
||||
:visible.sync="dialogVisible"
|
||||
title="修改图片URL"
|
||||
width="500px"
|
||||
:modal-append-to-body="false"
|
||||
>
|
||||
<el-input v-model="imgInfo.imgUrl"></el-input>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="confirmModify">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</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
|
||||
},
|
||||
dialogVisible: false,
|
||||
imgInfo: {
|
||||
id: null,
|
||||
imgUrl: ''
|
||||
},
|
||||
choosedList: {},
|
||||
choosedPicBed: [],
|
||||
searchText: '',
|
||||
handleBarActive: false,
|
||||
pasteStyle: '',
|
||||
pasteStyleMap: {
|
||||
Markdown: 'markdown',
|
||||
HTML: 'HTML',
|
||||
URL: 'URL',
|
||||
UBB: 'UBB',
|
||||
Custom: 'Custom'
|
||||
},
|
||||
picBed: []
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.getGallery()
|
||||
this.$electron.ipcRenderer.on('updateGallery', (event) => {
|
||||
this.$nextTick(() => {
|
||||
this.filterList = this.getGallery()
|
||||
})
|
||||
})
|
||||
this.$electron.ipcRenderer.send('getPicBeds')
|
||||
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
|
||||
this.getPasteStyle()
|
||||
this.getPicBeds()
|
||||
},
|
||||
computed: {
|
||||
filterList: {
|
||||
get () {
|
||||
return this.getGallery()
|
||||
},
|
||||
set (val) {
|
||||
return this.val
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPicBeds (event, picBeds) {
|
||||
this.picBed = picBeds
|
||||
},
|
||||
getGallery () {
|
||||
if (this.choosedPicBed.length > 0) {
|
||||
let arr = []
|
||||
this.choosedPicBed.forEach(item => {
|
||||
let obj = {
|
||||
type: item
|
||||
}
|
||||
if (this.searchText) {
|
||||
obj.fileName = this.searchText
|
||||
}
|
||||
arr = arr.concat(this.$db.read().get('uploaded').filter(obj => {
|
||||
return obj.fileName.indexOf(this.searchText) !== -1 && obj.type === item
|
||||
}).reverse().value())
|
||||
})
|
||||
this.images = arr
|
||||
} else {
|
||||
if (this.searchText) {
|
||||
let data = this.$db.read().get('uploaded')
|
||||
.filter(item => {
|
||||
return item.fileName.indexOf(this.searchText) !== -1
|
||||
}).reverse().value()
|
||||
this.images = data
|
||||
} else {
|
||||
this.images = this.$db.read().get('uploaded').slice().reverse().value()
|
||||
}
|
||||
}
|
||||
return this.images
|
||||
},
|
||||
zoomImage (index) {
|
||||
this.idx = index
|
||||
this.changeZIndexForGallery(true)
|
||||
},
|
||||
changeZIndexForGallery (isOpen) {
|
||||
if (isOpen) {
|
||||
document.querySelector('.main-content.el-row').style.zIndex = 101
|
||||
} else {
|
||||
document.querySelector('.main-content.el-row').style.zIndex = 10
|
||||
}
|
||||
},
|
||||
handleClose () {
|
||||
this.idx = null
|
||||
this.changeZIndexForGallery(false)
|
||||
},
|
||||
copy (item) {
|
||||
const url = item.url || item.imgUrl
|
||||
const style = this.$db.read().get('settings.pasteStyle').value() || 'markdown'
|
||||
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(() => {
|
||||
const file = this.$db.read().get('uploaded').getById(id).value()
|
||||
this.$db.read().get('uploaded').removeById(id).write()
|
||||
this.$electron.ipcRenderer.send('removeFiles', [file])
|
||||
const obj = {
|
||||
title: '操作结果',
|
||||
body: '删除成功'
|
||||
}
|
||||
const myNotification = new window.Notification(obj.title, obj)
|
||||
myNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
this.getGallery()
|
||||
}).catch(() => {
|
||||
return true
|
||||
})
|
||||
},
|
||||
openDialog (item) {
|
||||
this.imgInfo.id = item.id
|
||||
this.imgInfo.imgUrl = item.imgUrl
|
||||
this.dialogVisible = true
|
||||
},
|
||||
confirmModify () {
|
||||
this.$db.read().get('uploaded')
|
||||
.getById(this.imgInfo.id)
|
||||
.assign({imgUrl: this.imgInfo.imgUrl})
|
||||
.write()
|
||||
const obj = {
|
||||
title: '修改图片URL成功',
|
||||
body: this.imgInfo.imgUrl,
|
||||
icon: this.imgInfo.imgUrl
|
||||
}
|
||||
const myNotification = new window.Notification(obj.title, obj)
|
||||
myNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
this.dialogVisible = false
|
||||
this.getGallery()
|
||||
},
|
||||
choosePicBed (type) {
|
||||
let idx = this.choosedPicBed.indexOf(type)
|
||||
if (idx !== -1) {
|
||||
this.choosedPicBed.splice(idx, 1)
|
||||
} else {
|
||||
this.choosedPicBed.push(type)
|
||||
}
|
||||
},
|
||||
cleanSearch () {
|
||||
this.searchText = ''
|
||||
},
|
||||
isMultiple (obj) {
|
||||
return Object.values(obj).some(item => item)
|
||||
},
|
||||
multiRemove () {
|
||||
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
|
||||
if (Object.values(this.choosedList).some(item => item)) {
|
||||
this.$confirm('将删除刚才选中的图片,是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
let files = []
|
||||
Object.keys(this.choosedList).forEach(key => {
|
||||
if (this.choosedList[key]) {
|
||||
const file = this.$db.read().get('uploaded').getById(key).value()
|
||||
files.push(file)
|
||||
this.$db.read().get('uploaded').removeById(key).write()
|
||||
}
|
||||
})
|
||||
this.choosedList = {}
|
||||
this.getGallery()
|
||||
const obj = {
|
||||
title: '操作结果',
|
||||
body: '删除成功'
|
||||
}
|
||||
this.$electron.ipcRenderer.send('removeFiles', files)
|
||||
const myNotification = new window.Notification(obj.title, obj)
|
||||
myNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
}).catch(() => {
|
||||
return true
|
||||
})
|
||||
}
|
||||
},
|
||||
multiCopy () {
|
||||
if (Object.values(this.choosedList).some(item => item)) {
|
||||
let copyString = ''
|
||||
const style = this.$db.read().get('settings.pasteStyle').value() || 'markdown'
|
||||
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
|
||||
Object.keys(this.choosedList).forEach(key => {
|
||||
if (this.choosedList[key]) {
|
||||
const item = this.$db.read().get('uploaded').getById(key).value()
|
||||
const url = item.url || item.imgUrl
|
||||
copyString += pasteStyle(style, url) + '\n'
|
||||
this.choosedList[key] = false
|
||||
}
|
||||
})
|
||||
const obj = {
|
||||
title: '批量复制链接成功',
|
||||
body: copyString
|
||||
}
|
||||
const myNotification = new window.Notification(obj.title, obj)
|
||||
this.$electron.clipboard.writeText(copyString)
|
||||
myNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
}
|
||||
},
|
||||
toggleHandleBar () {
|
||||
this.handleBarActive = !this.handleBarActive
|
||||
},
|
||||
getPasteStyle () {
|
||||
this.pasteStyle = this.$db.read().get('settings.pasteStyle').value() || 'markdown'
|
||||
},
|
||||
handlePasteStyleChange (val) {
|
||||
this.$db.read().set('settings.pasteStyle', val)
|
||||
.write()
|
||||
this.pasteStyle = val
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$electron.ipcRenderer.removeAllListeners('updateGallery')
|
||||
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 10px auto
|
||||
.sub-title
|
||||
font-size 14px
|
||||
.el-icon-caret-bottom
|
||||
cursor: pointer
|
||||
transition all .2s ease-in-out
|
||||
&.active
|
||||
transform: rotate(180deg)
|
||||
.item-base
|
||||
background #2E2E2E
|
||||
text-align center
|
||||
padding 5px 0
|
||||
cursor pointer
|
||||
font-size 13px
|
||||
transition all .2s ease-in-out
|
||||
height: 28px
|
||||
box-sizing: border-box
|
||||
&.copy
|
||||
cursor not-allowed
|
||||
background #49B1F5
|
||||
&.active
|
||||
cursor pointer
|
||||
background #1B9EF3
|
||||
color #fff
|
||||
&.delete
|
||||
cursor not-allowed
|
||||
background #F47466
|
||||
&.active
|
||||
cursor pointer
|
||||
background #F15140
|
||||
color #fff
|
||||
#gallery-view
|
||||
position relative
|
||||
.round
|
||||
border-radius 14px
|
||||
.pull-right
|
||||
float right
|
||||
.gallery-list
|
||||
height 360px
|
||||
box-sizing border-box
|
||||
padding 8px 0
|
||||
overflow-y auto
|
||||
overflow-x hidden
|
||||
position absolute
|
||||
top: 38px
|
||||
transition all .2s ease-in-out .1s
|
||||
width 100%
|
||||
&.small
|
||||
height: 287px
|
||||
top: 113px
|
||||
&__img
|
||||
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-edit-outline
|
||||
&:hover
|
||||
color #69C282
|
||||
&.el-icon-delete
|
||||
&:hover
|
||||
color #F15140
|
||||
.handle-bar
|
||||
color #ddd
|
||||
margin-bottom 10px
|
||||
.el-input__inner
|
||||
border-radius 14px
|
||||
</style>
|
||||
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div id="mini-page"
|
||||
:style="{ backgroundImage: 'url(' + logo + ')' }"
|
||||
:class="{ linux: os === 'linux' }"
|
||||
>
|
||||
<!-- <i class="el-icon-upload2"></i> -->
|
||||
<div
|
||||
id="upload-area"
|
||||
:class="{ 'is-dragover': dragover, uploading: showProgress, linux: os === 'linux' }" @drop.prevent="onDrop" @dragover.prevent="dragover = true" @dragleave.prevent="dragover = false"
|
||||
:style="{ backgroundPosition: '0 ' + progress + '%'}"
|
||||
>
|
||||
<div id="upload-dragger" @dblclick="openUploadWindow">
|
||||
<input type="file" id="file-uploader" @change="onChange" multiple>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '@/utils/mixin'
|
||||
export default {
|
||||
name: 'mini-page',
|
||||
mixins: [mixin],
|
||||
data () {
|
||||
return {
|
||||
logo: 'static/logo.png',
|
||||
dragover: false,
|
||||
progress: 0,
|
||||
showProgress: false,
|
||||
showError: false,
|
||||
dragging: false,
|
||||
wX: '',
|
||||
wY: '',
|
||||
screenX: '',
|
||||
screenY: '',
|
||||
menu: null,
|
||||
os: '',
|
||||
picBed: []
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.os = process.platform
|
||||
this.$electron.ipcRenderer.on('uploadProgress', (event, progress) => {
|
||||
if (progress !== -1) {
|
||||
this.showProgress = true
|
||||
this.progress = progress
|
||||
} else {
|
||||
this.progress = 100
|
||||
this.showError = true
|
||||
}
|
||||
})
|
||||
this.getPicBeds()
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('mousedown', this.handleMouseDown, false)
|
||||
window.addEventListener('mousemove', this.handleMouseMove, false)
|
||||
window.addEventListener('mouseup', this.handleMouseUp, false)
|
||||
},
|
||||
watch: {
|
||||
progress (val) {
|
||||
if (val === 100) {
|
||||
setTimeout(() => {
|
||||
this.showProgress = false
|
||||
this.showError = false
|
||||
}, 1000)
|
||||
setTimeout(() => {
|
||||
this.progress = 0
|
||||
}, 1200)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getPicBeds () {
|
||||
this.picBed = this.$electron.ipcRenderer.sendSync('getPicBeds')
|
||||
this.buildMenu()
|
||||
},
|
||||
onDrop (e) {
|
||||
this.dragover = false
|
||||
this.ipcSendFiles(e.dataTransfer.files)
|
||||
},
|
||||
openUploadWindow () {
|
||||
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)
|
||||
},
|
||||
handleMouseDown (e) {
|
||||
this.dragging = true
|
||||
this.wX = e.pageX
|
||||
this.wY = e.pageY
|
||||
this.screenX = e.screenX
|
||||
this.screenY = e.screenY
|
||||
},
|
||||
handleMouseMove (e) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
if (this.dragging) {
|
||||
const xLoc = e.screenX - this.wX
|
||||
const yLoc = e.screenY - this.wY
|
||||
this.$electron.remote.BrowserWindow.getFocusedWindow().setBounds({
|
||||
x: xLoc,
|
||||
y: yLoc,
|
||||
width: 64,
|
||||
height: 64
|
||||
})
|
||||
}
|
||||
},
|
||||
handleMouseUp (e) {
|
||||
this.dragging = false
|
||||
if (this.screenX === e.screenX && this.screenY === e.screenY) {
|
||||
if (e.button === 0) { // left mouse
|
||||
this.openUploadWindow()
|
||||
} else {
|
||||
this.getPicBeds()
|
||||
this.openContextMenu()
|
||||
}
|
||||
}
|
||||
},
|
||||
openContextMenu () {
|
||||
this.menu.popup(this.$electron.remote.getCurrentWindow())
|
||||
},
|
||||
buildMenu () {
|
||||
const _this = this
|
||||
const submenu = this.picBed.map(item => {
|
||||
return {
|
||||
label: item.name,
|
||||
type: 'radio',
|
||||
checked: this.$db.read().get('picBed.current').value() === item.type,
|
||||
click () {
|
||||
_this.$db.read().set('picBed.current', item.type).write()
|
||||
_this.$electron.ipcRenderer.send('syncPicBed')
|
||||
}
|
||||
}
|
||||
})
|
||||
const template = [
|
||||
{
|
||||
label: '打开详细窗口',
|
||||
click () {
|
||||
_this.$electron.ipcRenderer.send('openSettingWindow')
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '选择默认图床',
|
||||
type: 'submenu',
|
||||
submenu
|
||||
},
|
||||
{
|
||||
label: '剪贴板图片上传',
|
||||
click () {
|
||||
_this.$electron.ipcRenderer.send('uploadClipboardFilesFromUploadPage')
|
||||
}
|
||||
},
|
||||
{
|
||||
role: 'quit',
|
||||
label: '退出'
|
||||
}
|
||||
]
|
||||
this.menu = this.$electron.remote.Menu.buildFromTemplate(template)
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$electron.ipcRenderer.removeAllListeners('uploadProgress')
|
||||
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
|
||||
window.removeEventListener('mousedown', this.handleMouseDown, false)
|
||||
window.removeEventListener('mousemove', this.handleMouseMove, false)
|
||||
window.removeEventListener('mouseup', this.handleMouseUp, false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
#mini-page
|
||||
background #409EFF
|
||||
color #FFF
|
||||
height 100vh
|
||||
width 100vw
|
||||
border-radius 50%
|
||||
text-align center
|
||||
line-height 100vh
|
||||
font-size 40px
|
||||
background-size 90vh 90vw
|
||||
background-position center center
|
||||
background-repeat no-repeat
|
||||
position relative
|
||||
border 4px solid #fff
|
||||
box-sizing border-box
|
||||
cursor pointer
|
||||
&.linux
|
||||
border-radius 0
|
||||
background-size 100vh 100vw
|
||||
#upload-area
|
||||
height 100%
|
||||
width 100%
|
||||
border-radius 50%
|
||||
transition all .2s ease-in-out
|
||||
&.linux
|
||||
border-radius 0
|
||||
&.uploading
|
||||
background: linear-gradient(to top, #409EFF 50%, #fff 51%)
|
||||
background-size 200%
|
||||
#upload-dragger
|
||||
height 100%
|
||||
&.is-dragover
|
||||
background rgba(0,0,0,0.3)
|
||||
#file-uploader
|
||||
display none
|
||||
</style>
|
||||
+119
-41
@@ -12,7 +12,12 @@
|
||||
size="small"
|
||||
>
|
||||
<el-form-item
|
||||
label="修改快捷键"
|
||||
label="打开配置文件"
|
||||
>
|
||||
<el-button type="primary" round size="mini" @click="openConfigFile">点击打开</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="修改上传快捷键"
|
||||
>
|
||||
<el-button type="primary" round size="mini" @click="keyBindingVisible = true">点击设置</el-button>
|
||||
</el-form-item>
|
||||
@@ -21,10 +26,15 @@
|
||||
>
|
||||
<el-button type="primary" round size="mini" @click="customLinkVisible = true">点击设置</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="设置代理"
|
||||
>
|
||||
<el-button type="primary" round size="mini" @click="proxyVisible = true">点击设置</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="检查更新"
|
||||
>
|
||||
<el-button type="primary" round size="mini" @click="checkUpdate">点击设置</el-button>
|
||||
<el-button type="primary" round size="mini" @click="checkUpdate">点击检查</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="打开更新助手"
|
||||
@@ -76,6 +86,17 @@
|
||||
@change="handleUploadNotification"
|
||||
></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="os !== 'darwin'"
|
||||
label="Mini窗口置顶"
|
||||
>
|
||||
<el-switch
|
||||
v-model="form.miniWindowOntop"
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@change="handleMiniWindowOntop"
|
||||
></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="选择显示的图床"
|
||||
>
|
||||
@@ -95,7 +116,7 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog
|
||||
title="修改快捷键"
|
||||
title="修改上传快捷键"
|
||||
:visible.sync="keyBindingVisible"
|
||||
:modal-append-to-body="false"
|
||||
>
|
||||
@@ -114,8 +135,8 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="cancelKeyBinding">取消</el-button>
|
||||
<el-button type="primary" @click="confirmKeyBinding">确定</el-button>
|
||||
<el-button @click="cancelKeyBinding" round>取消</el-button>
|
||||
<el-button type="primary" @click="confirmKeyBinding" round>确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
@@ -144,8 +165,35 @@
|
||||
如[]($url)
|
||||
</div>
|
||||
<span slot="footer">
|
||||
<el-button @click="cancelCustomLink">取消</el-button>
|
||||
<el-button type="primary" @click="confirmCustomLink">确定</el-button>
|
||||
<el-button @click="cancelCustomLink" round>取消</el-button>
|
||||
<el-button type="primary" @click="confirmCustomLink" round>确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
title="设置代理"
|
||||
:visible.sync="proxyVisible"
|
||||
:modal-append-to-body="false"
|
||||
>
|
||||
<el-form
|
||||
label-position="right"
|
||||
:model="customLink"
|
||||
ref="customLink"
|
||||
:rules="rules"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-form-item
|
||||
label="代理地址"
|
||||
>
|
||||
<el-input
|
||||
v-model="proxy"
|
||||
:autofocus="true"
|
||||
placeholder="例如:http://127.0.0.1:1080"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="cancelProxy" round>取消</el-button>
|
||||
<el-button type="primary" @click="confirmProxy" round>确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
@@ -163,16 +211,16 @@
|
||||
PicGo更新啦,请点击确定打开下载页面
|
||||
</div>
|
||||
<span slot="footer">
|
||||
<el-button @click="cancelCheckVersion">取消</el-button>
|
||||
<el-button type="primary" @click="confirmCheckVersion">确定</el-button>
|
||||
<el-button @click="cancelCheckVersion" round>取消</el-button>
|
||||
<el-button type="primary" @click="confirmCheckVersion" round>确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import db from '../../../datastore'
|
||||
import keyDetect from 'utils/key-binding'
|
||||
import pkg from '../../../../package.json'
|
||||
import pkg from 'root/package.json'
|
||||
import path from 'path'
|
||||
const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
|
||||
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
|
||||
export default {
|
||||
@@ -196,61 +244,77 @@ export default {
|
||||
}
|
||||
return {
|
||||
form: {
|
||||
updateHelper: this.$db.get('picBed.showUpdateTip').value(),
|
||||
updateHelper: this.$db.read().get('settings.showUpdateTip').value(),
|
||||
showPicBedList: [],
|
||||
autoStart: this.$db.get('picBed.autoStart').value() || false,
|
||||
rename: this.$db.get('picBed.rename').value() || false,
|
||||
autoRename: this.$db.get('picBed.autoRename').value() || false,
|
||||
uploadNotification: this.$db.get('picBed.uploadNotification').value() || false
|
||||
autoStart: this.$db.read().get('settings.autoStart').value() || false,
|
||||
rename: this.$db.read().get('settings.rename').value() || false,
|
||||
autoRename: this.$db.read().get('settings.autoRename').value() || false,
|
||||
uploadNotification: this.$db.read().get('settings.uploadNotification').value() || false,
|
||||
miniWindowOntop: this.$db.read().get('settings.miniWindowOntop').value() || false
|
||||
},
|
||||
picBed: this.$picBed,
|
||||
picBed: [],
|
||||
keyBindingVisible: false,
|
||||
customLinkVisible: false,
|
||||
checkUpdateVisible: false,
|
||||
proxyVisible: false,
|
||||
customLink: {
|
||||
value: db.read().get('customLink').value() || '$url'
|
||||
value: this.$db.read().get('settings.customLink').value() || '$url'
|
||||
},
|
||||
shortKey: {
|
||||
upload: db.read().get('shortKey.upload').value()
|
||||
upload: this.$db.read().get('settings.shortKey.upload').value()
|
||||
},
|
||||
proxy: this.$db.read().get('picBed.proxy').value() || undefined,
|
||||
rules: {
|
||||
value: [
|
||||
{ validator: customLinkRule, trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
version: pkg.version,
|
||||
latestVersion: ''
|
||||
latestVersion: '',
|
||||
os: ''
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.form.showPicBedList = this.picBed.map(item => {
|
||||
if (item.visible) {
|
||||
return item.name
|
||||
}
|
||||
})
|
||||
this.os = process.platform
|
||||
this.$electron.ipcRenderer.send('getPicBeds')
|
||||
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
|
||||
},
|
||||
methods: {
|
||||
getPicBeds (event, picBeds) {
|
||||
this.picBed = picBeds
|
||||
this.form.showPicBedList = this.picBed.map(item => {
|
||||
if (item.visible) {
|
||||
return item.name
|
||||
}
|
||||
})
|
||||
},
|
||||
openConfigFile () {
|
||||
const { app, shell } = this.$electron.remote
|
||||
const STORE_PATH = app.getPath('userData')
|
||||
const CONFIG_FILE = path.join(STORE_PATH, '/data.json')
|
||||
shell.openItem(CONFIG_FILE)
|
||||
},
|
||||
keyDetect (type, event) {
|
||||
this.shortKey[type] = keyDetect(event).join('+')
|
||||
},
|
||||
cancelKeyBinding () {
|
||||
this.keyBindingVisible = false
|
||||
this.shortKey = db.read().get('shortKey').value()
|
||||
this.shortKey = this.$db.read().get('settings.shortKey').value()
|
||||
},
|
||||
confirmKeyBinding () {
|
||||
const oldKey = db.read().get('shortKey').value()
|
||||
db.read().set('shortKey', this.shortKey).write()
|
||||
const oldKey = this.$db.read().get('settings.shortKey').value()
|
||||
this.$db.read().set('settings.shortKey', this.shortKey).write()
|
||||
this.keyBindingVisible = false
|
||||
this.$electron.ipcRenderer.send('updateShortKey', oldKey)
|
||||
},
|
||||
cancelCustomLink () {
|
||||
this.customLinkVisible = false
|
||||
this.customLink.value = db.read().get('customLink').value() || '$url'
|
||||
this.customLink.value = this.$db.read().get('settings.customLink').value() || '$url'
|
||||
},
|
||||
confirmCustomLink () {
|
||||
this.$refs.customLink.validate((valid) => {
|
||||
if (valid) {
|
||||
db.read().set('customLink', this.customLink.value).write()
|
||||
this.$db.read().set('settings.customLink', this.customLink.value).write()
|
||||
this.customLinkVisible = false
|
||||
this.$electron.ipcRenderer.send('updateCustomLink')
|
||||
} else {
|
||||
@@ -258,8 +322,22 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
cancelProxy () {
|
||||
this.proxyVisible = false
|
||||
this.proxy = this.$db.read().get('picBed.proxy').value() || undefined
|
||||
},
|
||||
confirmProxy () {
|
||||
this.proxyVisible = false
|
||||
this.$db.read().set('picBed.proxy', this.proxy).write()
|
||||
const successNotification = new window.Notification('设置代理', {
|
||||
body: '设置成功'
|
||||
})
|
||||
successNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
},
|
||||
updateHelperChange (val) {
|
||||
this.$db.read().set('picBed.showUpdateTip', val).write()
|
||||
this.$db.read().set('settings.showUpdateTip', val).write()
|
||||
},
|
||||
handleShowPicBedListChange (val) {
|
||||
const list = this.picBed.map(item => {
|
||||
@@ -271,16 +349,17 @@ export default {
|
||||
return item
|
||||
})
|
||||
this.$db.read().set('picBed.list', list).write()
|
||||
this.$electron.ipcRenderer.send('getPicBeds')
|
||||
},
|
||||
handleAutoStartChange (val) {
|
||||
this.$db.read().set('picBed.autoStart', val).write()
|
||||
this.$db.read().set('settings.autoStart', val).write()
|
||||
this.$electron.ipcRenderer.send('autoStart', val)
|
||||
},
|
||||
handleRename (val) {
|
||||
this.$db.read().set('picBed.rename', val).write()
|
||||
this.$db.read().set('settings.rename', val).write()
|
||||
},
|
||||
handleAutoRename (val) {
|
||||
this.$db.read().set('picBed.autoRename', val).write()
|
||||
this.$db.read().set('settings.autoRename', val).write()
|
||||
},
|
||||
compareVersion2Update (current, latest) {
|
||||
const currentVersion = current.split('.').map(item => parseInt(item))
|
||||
@@ -315,22 +394,21 @@ export default {
|
||||
this.checkUpdateVisible = false
|
||||
},
|
||||
handleUploadNotification (val) {
|
||||
this.$db.read().set('picBed.uploadNotification', val).write()
|
||||
this.$db.read().set('settings.uploadNotification', val).write()
|
||||
},
|
||||
handleMiniWindowOntop (val) {
|
||||
this.$db.read().set('settings.miniWindowOntop', val).write()
|
||||
this.$message('需要重启生效')
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$electron.ipcRenderer.removeAllListeners('autoStart')
|
||||
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
.el-message
|
||||
left 60%
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 20px auto
|
||||
#picgo-setting
|
||||
.sub-title
|
||||
font-size 14px
|
||||
@@ -0,0 +1,580 @@
|
||||
<template>
|
||||
<div id="plugin-view">
|
||||
<div class="view-title">
|
||||
插件设置
|
||||
</div>
|
||||
<el-row class="handle-bar" :class="{ 'cut-width': pluginList.length > 6 }">
|
||||
<el-input
|
||||
v-model="searchText"
|
||||
placeholder="搜索npm上的PicGo插件"
|
||||
size="small"
|
||||
>
|
||||
<i slot="suffix" class="el-input__icon el-icon-close" v-if="searchText" @click="cleanSearch" style="cursor: pointer"></i>
|
||||
</el-input>
|
||||
</el-row>
|
||||
<el-row :gutter="10" class="plugin-list" v-loading="loading">
|
||||
<el-col :span="12" v-for="item in pluginList" :key="item.name">
|
||||
<div class="plugin-item" :class="{ 'darwin': os === 'darwin' }">
|
||||
<div class="cli-only-badge" v-if="!item.gui" title="CLI only">CLI</div>
|
||||
<img class="plugin-item__logo" :src="item.logo"
|
||||
onerror="this.src='static/logo.png'"
|
||||
>
|
||||
<div
|
||||
class="plugin-item__content"
|
||||
:class="{ disabled: !item.enabled }"
|
||||
>
|
||||
<div class="plugin-item__name" @click="openHomepage(item.homepage)">
|
||||
{{ item.name }} <small>{{ ' ' + item.version }}</small>
|
||||
</div>
|
||||
<div class="plugin-item__desc" :title="item.description">
|
||||
{{ item.description }}
|
||||
</div>
|
||||
<div class="plugin-item__info-bar">
|
||||
<span class="plugin-item__author">
|
||||
{{ item.author }}
|
||||
</span>
|
||||
<span class="plugin-item__config" >
|
||||
<template v-if="searchText">
|
||||
<template v-if="!item.hasInstall">
|
||||
<span class="config-button install" v-if="!item.ing" @click="installPlugin(item)">
|
||||
安装
|
||||
</span>
|
||||
<span v-else-if="item.ing" class="config-button ing">
|
||||
安装中
|
||||
</span>
|
||||
</template>
|
||||
<span v-else class="config-button ing">
|
||||
已安装
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span v-if="item.ing" class="config-button ing">
|
||||
进行中
|
||||
</span>
|
||||
<template v-else>
|
||||
<i
|
||||
v-if="item.enabled"
|
||||
class="el-icon-setting"
|
||||
@click="buildContextMenu(item)"
|
||||
></i>
|
||||
<i
|
||||
v-else
|
||||
class="el-icon-remove-outline"
|
||||
@click="buildContextMenu(item)"
|
||||
></i>
|
||||
</template>
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-show="needReload" class="reload-mask" :class="{ 'cut-width': pluginList.length > 6 }">
|
||||
<el-button type="primary" @click="reloadApp" size="mini" round>重启以生效</el-button>
|
||||
</el-row>
|
||||
<el-dialog
|
||||
:visible.sync="dialogVisible"
|
||||
:modal-append-to-body="false"
|
||||
:title="`配置${configName}`"
|
||||
width="70%"
|
||||
>
|
||||
<config-form
|
||||
:config="config"
|
||||
:type="currentType"
|
||||
:id="configName"
|
||||
ref="configForm"
|
||||
>
|
||||
</config-form>
|
||||
<span slot="footer">
|
||||
<el-button @click="dialogVisible = false" round>取消</el-button>
|
||||
<el-button type="primary" @click="handleConfirmConfig" round>确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
:title="inputBoxOptions.title || '输入框'"
|
||||
:visible.sync="showInputBoxVisible"
|
||||
:modal-append-to-body="false"
|
||||
@close="handleInputBoxClose"
|
||||
>
|
||||
<el-input
|
||||
v-model="inputBoxValue"
|
||||
:placeholder="inputBoxOptions.placeholder"></el-input>
|
||||
<span slot="footer">
|
||||
<el-button @click="showInputBoxVisible = false" round>取消</el-button>
|
||||
<el-button type="primary" @click="showInputBoxVisible = false" round>确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import ConfigForm from '@/components/ConfigForm'
|
||||
import { debounce } from 'lodash'
|
||||
export default {
|
||||
name: 'plugin',
|
||||
components: {
|
||||
ConfigForm
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
searchText: '',
|
||||
pluginList: [],
|
||||
menu: null,
|
||||
config: [],
|
||||
currentType: '',
|
||||
configName: '',
|
||||
dialogVisible: false,
|
||||
pluginNameList: [],
|
||||
loading: true,
|
||||
needReload: false,
|
||||
id: '',
|
||||
os: '',
|
||||
// for showInputBox
|
||||
showInputBoxVisible: false,
|
||||
inputBoxValue: '',
|
||||
inputBoxOptions: {
|
||||
title: '',
|
||||
placeholder: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
npmSearchText () {
|
||||
return this.searchText.match('picgo-plugin-')
|
||||
? this.searchText
|
||||
: this.searchText !== ''
|
||||
? `picgo-plugin-${this.searchText}`
|
||||
: this.searchText
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
npmSearchText (val) {
|
||||
if (val) {
|
||||
this.loading = true
|
||||
this.pluginList = []
|
||||
this.getSearchResult(val)
|
||||
} else {
|
||||
this.getPluginList()
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.os = process.platform
|
||||
this.$electron.ipcRenderer.on('pluginList', (evt, list) => {
|
||||
this.pluginList = list
|
||||
this.pluginNameList = list.map(item => item.name)
|
||||
this.loading = false
|
||||
})
|
||||
this.$electron.ipcRenderer.on('installSuccess', (evt, plugin) => {
|
||||
this.loading = false
|
||||
this.pluginList.forEach(item => {
|
||||
if (item.name === plugin) {
|
||||
item.ing = false
|
||||
item.hasInstall = true
|
||||
}
|
||||
})
|
||||
})
|
||||
this.$electron.ipcRenderer.on('updateSuccess', (evt, plugin) => {
|
||||
this.loading = false
|
||||
this.pluginList.forEach(item => {
|
||||
if (item.name === plugin) {
|
||||
item.ing = false
|
||||
item.hasInstall = true
|
||||
}
|
||||
this.getPicBeds()
|
||||
})
|
||||
this.handleReload()
|
||||
this.getPluginList()
|
||||
})
|
||||
this.$electron.ipcRenderer.on('uninstallSuccess', (evt, plugin) => {
|
||||
this.loading = false
|
||||
this.pluginList = this.pluginList.filter(item => {
|
||||
if (item.name === plugin) { // restore Uploader & Transformer after uninstalling
|
||||
if (item.config.transformer.name) {
|
||||
this.handleRestoreState('transformer', item.config.transformer.name)
|
||||
}
|
||||
if (item.config.uploader.name) {
|
||||
this.handleRestoreState('uploader', item.config.uploader.name)
|
||||
}
|
||||
this.getPicBeds()
|
||||
}
|
||||
return item.name !== plugin
|
||||
})
|
||||
this.pluginNameList = this.pluginNameList.filter(item => item !== plugin)
|
||||
})
|
||||
this.getPluginList()
|
||||
this.getSearchResult = debounce(this.getSearchResult, 50)
|
||||
this.needReload = this.$db.read().get('needReload').value()
|
||||
this.$electron.ipcRenderer.on('showInputBox', (evt, options) => {
|
||||
this.inputBoxValue = ''
|
||||
this.inputBoxOptions.title = options.title || ''
|
||||
this.inputBoxOptions.placeholder = options.placeholder || ''
|
||||
this.showInputBoxVisible = true
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
buildContextMenu (plugin) {
|
||||
const _this = this
|
||||
let menu = [{
|
||||
label: '启用插件',
|
||||
enabled: !plugin.enabled,
|
||||
click () {
|
||||
_this.$db.read().set(`plugins.picgo-plugin-${plugin.name}`, true).write()
|
||||
plugin.enabled = true
|
||||
_this.getPicBeds()
|
||||
}
|
||||
}, {
|
||||
label: '禁用插件',
|
||||
enabled: plugin.enabled,
|
||||
click () {
|
||||
_this.$db.read().set(`plugins.picgo-plugin-${plugin.name}`, false).write()
|
||||
plugin.enabled = false
|
||||
_this.getPicBeds()
|
||||
if (plugin.config.transformer.name) {
|
||||
_this.handleRestoreState('transformer', plugin.config.transformer.name)
|
||||
}
|
||||
if (plugin.config.uploader.name) {
|
||||
_this.handleRestoreState('uploader', plugin.config.uploader.name)
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: '卸载插件',
|
||||
click () {
|
||||
_this.uninstallPlugin(plugin.name)
|
||||
}
|
||||
}, {
|
||||
label: '更新插件',
|
||||
click () {
|
||||
_this.updatePlugin(plugin.name)
|
||||
}
|
||||
}]
|
||||
for (let i in plugin.config) {
|
||||
if (plugin.config[i].config.length > 0) {
|
||||
const obj = {
|
||||
label: `配置${i} - ${plugin.config[i].name}`,
|
||||
click () {
|
||||
_this.currentType = i
|
||||
_this.configName = plugin.config[i].name
|
||||
_this.dialogVisible = true
|
||||
_this.config = plugin.config[i].config
|
||||
}
|
||||
}
|
||||
menu.push(obj)
|
||||
}
|
||||
}
|
||||
|
||||
// handle transformer
|
||||
if (plugin.config.transformer.name) {
|
||||
let currentTransformer = this.$db.read().get('picBed.transformer').value() || 'path'
|
||||
let pluginTransformer = plugin.config.transformer.name
|
||||
const obj = {
|
||||
label: `${currentTransformer === pluginTransformer ? '禁用' : '启用'}transformer - ${plugin.config.transformer.name}`,
|
||||
click () {
|
||||
_this.toggleTransformer(plugin.config.transformer.name)
|
||||
}
|
||||
}
|
||||
menu.push(obj)
|
||||
}
|
||||
|
||||
// plugin custom menus
|
||||
if (plugin.guiMenu) {
|
||||
menu.push({
|
||||
type: 'separator'
|
||||
})
|
||||
for (let i of plugin.guiMenu) {
|
||||
menu.push({
|
||||
label: i.label,
|
||||
click () {
|
||||
_this.$electron.ipcRenderer.send('pluginActions', plugin.name, i.label)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
this.menu = this.$electron.remote.Menu.buildFromTemplate(menu)
|
||||
this.menu.popup(this.$electron.remote.getCurrentWindow())
|
||||
},
|
||||
getPluginList () {
|
||||
this.$electron.ipcRenderer.send('getPluginList')
|
||||
},
|
||||
getPicBeds () {
|
||||
this.$electron.ipcRenderer.send('getPicBeds')
|
||||
},
|
||||
installPlugin (item) {
|
||||
if (!item.gui) {
|
||||
this.$confirm('该插件未对可视化界面进行优化, 是否继续安装?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
item.ing = true
|
||||
this.$electron.ipcRenderer.send('installPlugin', item.name)
|
||||
}).catch(() => {
|
||||
console.log('Install canceled')
|
||||
})
|
||||
} else {
|
||||
item.ing = true
|
||||
this.$electron.ipcRenderer.send('installPlugin', item.name)
|
||||
}
|
||||
},
|
||||
uninstallPlugin (val) {
|
||||
this.pluginList.forEach(item => {
|
||||
if (item.name === val) {
|
||||
item.ing = true
|
||||
}
|
||||
})
|
||||
this.$electron.ipcRenderer.send('uninstallPlugin', val)
|
||||
},
|
||||
updatePlugin (val) {
|
||||
this.pluginList.forEach(item => {
|
||||
if (item.name === val) {
|
||||
item.ing = true
|
||||
}
|
||||
})
|
||||
this.$electron.ipcRenderer.send('updatePlugin', val)
|
||||
},
|
||||
reloadApp () {
|
||||
this.$electron.remote.app.relaunch()
|
||||
this.$electron.remote.app.exit(0)
|
||||
},
|
||||
handleReload () {
|
||||
this.$db.read().set('needReload', true).write()
|
||||
this.needReload = true
|
||||
const successNotification = new window.Notification('更新成功', {
|
||||
body: '请点击此通知重启应用以生效'
|
||||
})
|
||||
successNotification.onclick = () => {
|
||||
this.reloadApp()
|
||||
}
|
||||
},
|
||||
cleanSearch () {
|
||||
this.searchText = ''
|
||||
},
|
||||
toggleTransformer (transformer) {
|
||||
let currentTransformer = this.$db.read().get('picBed.transformer').value() || 'path'
|
||||
if (currentTransformer === transformer) {
|
||||
this.$db.read().set('picBed.transformer', 'path').write()
|
||||
} else {
|
||||
this.$db.read().set('picBed.transformer', transformer).write()
|
||||
}
|
||||
},
|
||||
async handleConfirmConfig () {
|
||||
const result = await this.$refs.configForm.validate()
|
||||
if (result !== false) {
|
||||
switch (this.currentType) {
|
||||
case 'plugin':
|
||||
this.$db.read().set(`picgo-plugin-${this.configName}`, result).write()
|
||||
break
|
||||
case 'uploader':
|
||||
this.$db.read().set(`picBed.${this.configName}`, result).write()
|
||||
break
|
||||
case 'transformer':
|
||||
this.$db.read().set(`transformer.${this.configName}`, result).write()
|
||||
break
|
||||
}
|
||||
const successNotification = new window.Notification('设置结果', {
|
||||
body: '设置成功'
|
||||
})
|
||||
successNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
this.dialogVisible = false
|
||||
this.getPluginList()
|
||||
}
|
||||
},
|
||||
getSearchResult: function (val) {
|
||||
// this.$http.get(`https://api.npms.io/v2/search?q=${val}`)
|
||||
this.$http.get(`https://registry.npmjs.com/-/v1/search?text=${val}`)
|
||||
.then(res => {
|
||||
this.pluginList = res.data.objects.map(item => {
|
||||
return this.handleSearchResult(item)
|
||||
})
|
||||
this.loading = false
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
handleSearchResult (item) {
|
||||
const name = item.package.name.replace(/picgo-plugin-/, '')
|
||||
return {
|
||||
name: name,
|
||||
author: item.package.author.name,
|
||||
description: item.package.description,
|
||||
logo: `https://cdn.jsdelivr.net/npm/${item.package.name}/logo.png`,
|
||||
config: {},
|
||||
homepage: item.package.links ? item.package.links.homepage : '',
|
||||
hasInstall: this.pluginNameList.some(plugin => plugin === item.package.name.replace(/picgo-plugin-/, '')),
|
||||
version: item.package.version,
|
||||
gui: item.package.gui || false,
|
||||
ing: false // installing or uninstalling
|
||||
}
|
||||
},
|
||||
// restore Uploader & Transformer
|
||||
handleRestoreState (item, name) {
|
||||
if (item === 'uploader') {
|
||||
const current = this.$db.read().get('picBed.current').value()
|
||||
if (current === name) {
|
||||
this.$db.read().set('picBed.current', 'smms').write()
|
||||
}
|
||||
}
|
||||
if (item === 'transformer') {
|
||||
const current = this.$db.read().get('picBed.transformer').value()
|
||||
if (current === name) {
|
||||
this.$db.read().set('picBed.transformer', 'path').write()
|
||||
}
|
||||
}
|
||||
},
|
||||
openHomepage (url) {
|
||||
if (url) {
|
||||
this.$electron.remote.shell.openExternal(url)
|
||||
}
|
||||
},
|
||||
handleInputBoxClose () {
|
||||
this.$electron.ipcRenderer.send('showInputBox', this.inputBoxValue)
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$electron.ipcRenderer.removeAllListeners('pluginList')
|
||||
this.$electron.ipcRenderer.removeAllListeners('installSuccess')
|
||||
this.$electron.ipcRenderer.removeAllListeners('uninstallSuccess')
|
||||
this.$electron.ipcRenderer.removeAllListeners('updateSuccess')
|
||||
this.$electron.ipcRenderer.removeAllListeners('showInputBox')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
$darwinBg = #172426
|
||||
#plugin-view
|
||||
position relative
|
||||
padding 0 20px 0
|
||||
.el-loading-mask
|
||||
background-color rgba(0, 0, 0, 0.8)
|
||||
.plugin-list
|
||||
height: 339px;
|
||||
box-sizing: border-box;
|
||||
padding: 8px 15px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
position: absolute;
|
||||
top: 70px;
|
||||
left: 5px;
|
||||
transition: all 0.2s ease-in-out 0.1s;
|
||||
width: 100%
|
||||
.el-loading-mask
|
||||
left: 20px
|
||||
width: calc(100% - 40px)
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 10px auto
|
||||
.handle-bar
|
||||
margin-bottom 20px
|
||||
&.cut-width
|
||||
padding-right: 8px
|
||||
.el-input__inner
|
||||
border-radius 0
|
||||
.plugin-item
|
||||
box-sizing border-box
|
||||
height 80px
|
||||
background #444
|
||||
padding 8px
|
||||
user-select text
|
||||
transition all .2s ease-in-out
|
||||
margin-bottom 10px
|
||||
position relative
|
||||
.cli-only-badge
|
||||
position absolute
|
||||
right 0px
|
||||
top 0
|
||||
font-size 12px
|
||||
padding 3px 8px
|
||||
background #49B1F5
|
||||
color #eee
|
||||
&.darwin
|
||||
background transparentify($darwinBg, #000, 0.75)
|
||||
&:hover
|
||||
background transparentify($darwinBg, #000, 0.85)
|
||||
&:hover
|
||||
background #333
|
||||
&__logo
|
||||
width 64px
|
||||
height 64px
|
||||
float left
|
||||
&__content
|
||||
float left
|
||||
width calc(100% - 72px)
|
||||
height 64px
|
||||
color #ddd
|
||||
margin-left 8px
|
||||
display flex
|
||||
flex-direction column
|
||||
justify-content space-between
|
||||
&.disabled
|
||||
color #aaa
|
||||
&__name
|
||||
font-size 16px
|
||||
height 22px
|
||||
line-height 22px
|
||||
// font-weight 600
|
||||
font-weight 600
|
||||
cursor pointer
|
||||
transition all .2s ease-in-out
|
||||
&:hover
|
||||
color: #1B9EF3
|
||||
&__desc
|
||||
font-size 14px
|
||||
height 21px
|
||||
line-height 21px
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
white-space nowrap
|
||||
&__info-bar
|
||||
font-size 14px
|
||||
height 21px
|
||||
line-height 28px
|
||||
position relative
|
||||
&__author
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
white-space nowrap
|
||||
&__config
|
||||
float right
|
||||
font-size 16px
|
||||
cursor pointer
|
||||
transition all .2s ease-in-out
|
||||
&:hover
|
||||
color: #1B9EF3
|
||||
.config-button
|
||||
font-size 12px
|
||||
color #ddd
|
||||
background #222
|
||||
padding 1px 8px
|
||||
height 18px
|
||||
line-height 18px
|
||||
text-align center
|
||||
position absolute
|
||||
top 4px
|
||||
right 20px
|
||||
transition all .2s ease-in-out
|
||||
&.reload
|
||||
right 0px
|
||||
&.ing
|
||||
right 0px
|
||||
&.install
|
||||
right 0px
|
||||
&:hover
|
||||
background: #1B9EF3
|
||||
color #fff
|
||||
.reload-mask
|
||||
position absolute
|
||||
width calc(100% - 40px)
|
||||
bottom -320px
|
||||
text-align center
|
||||
background rgba(0,0,0,0.4)
|
||||
padding 10px 0
|
||||
&.cut-width
|
||||
width calc(100% - 48px)
|
||||
</style>
|
||||
@@ -22,8 +22,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '@/utils/mixin'
|
||||
export default {
|
||||
name: 'rename-page',
|
||||
mixins: [mixin],
|
||||
data () {
|
||||
return {
|
||||
fileName: '',
|
||||
@@ -23,9 +23,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import pasteTemplate from '../../main/utils/pasteTemplate'
|
||||
import mixin from '@/utils/mixin'
|
||||
import pasteTemplate from '~/main/utils/pasteTemplate'
|
||||
export default {
|
||||
name: 'tray-page',
|
||||
mixins: [mixin],
|
||||
data () {
|
||||
return {
|
||||
files: [],
|
||||
@@ -54,10 +56,7 @@
|
||||
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.$electron.ipcRenderer.on('uploadFiles', (event) => {
|
||||
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
|
||||
})
|
||||
this.$electron.ipcRenderer.on('updateFiles', (event) => {
|
||||
@@ -78,7 +77,7 @@
|
||||
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()
|
||||
const pasteStyle = this.$db.read().get('settings.pasteStyle').value() || 'markdown'
|
||||
this.$electron.clipboard.writeText(pasteTemplate(pasteStyle, item.imgUrl))
|
||||
myNotification.onclick = () => {
|
||||
return true
|
||||
@@ -3,11 +3,14 @@
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="20" :offset="2">
|
||||
<div class="view-title">
|
||||
图片上传 - {{ picBed }}
|
||||
图片上传 - {{ picBedName }}
|
||||
</div>
|
||||
<div
|
||||
id="upload-area"
|
||||
:class="{ 'is-dragover': dragover }" @drop.prevent="onDrop" @dragover.prevent="dragover = true" @dragleave.prevent="dragover = false"
|
||||
: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>
|
||||
@@ -22,7 +25,7 @@
|
||||
:show-text="false"
|
||||
class="upload-progress"
|
||||
:class="{ 'show': showProgress }"
|
||||
:status="showError ? 'exception' : ''"
|
||||
:status="showError ? 'exception' : 'text'"
|
||||
></el-progress>
|
||||
<div class="paste-style">
|
||||
<div class="el-col-16">
|
||||
@@ -53,10 +56,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '../mixin'
|
||||
export default {
|
||||
name: 'upload',
|
||||
mixins: [mixin],
|
||||
data () {
|
||||
return {
|
||||
dragover: false,
|
||||
@@ -64,7 +65,8 @@ export default {
|
||||
showProgress: false,
|
||||
showError: false,
|
||||
pasteStyle: '',
|
||||
picBed: ''
|
||||
picBed: [],
|
||||
picBedName: ''
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
@@ -79,6 +81,11 @@ export default {
|
||||
})
|
||||
this.getPasteStyle()
|
||||
this.getDefaultPicBed()
|
||||
this.$electron.ipcRenderer.on('syncPicBed', () => {
|
||||
this.getDefaultPicBed()
|
||||
})
|
||||
this.$electron.ipcRenderer.send('getPicBeds')
|
||||
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
|
||||
},
|
||||
watch: {
|
||||
progress (val) {
|
||||
@@ -95,6 +102,8 @@ export default {
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$electron.ipcRenderer.removeAllListeners('uploadProgress')
|
||||
this.$electron.ipcRenderer.removeAllListeners('syncPicBed')
|
||||
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
|
||||
},
|
||||
methods: {
|
||||
onDrop (e) {
|
||||
@@ -120,10 +129,10 @@ export default {
|
||||
this.$electron.ipcRenderer.send('uploadChoosedFiles', sendFiles)
|
||||
},
|
||||
getPasteStyle () {
|
||||
this.pasteStyle = this.$db.read().get('picBed.pasteStyle').value() || 'markdown'
|
||||
this.pasteStyle = this.$db.read().get('settings.pasteStyle').value() || 'markdown'
|
||||
},
|
||||
handlePasteStyleChange (val) {
|
||||
this.$db.read().set('picBed.pasteStyle', val)
|
||||
this.$db.read().set('settings.pasteStyle', val)
|
||||
.write()
|
||||
},
|
||||
uploadClipboardFiles () {
|
||||
@@ -131,11 +140,15 @@ export default {
|
||||
},
|
||||
getDefaultPicBed () {
|
||||
const current = this.$db.read().get('picBed.current').value()
|
||||
this.$picBed.forEach(item => {
|
||||
this.picBed.forEach(item => {
|
||||
if (item.type === current) {
|
||||
this.picBed = item.name
|
||||
this.picBedName = item.name
|
||||
}
|
||||
})
|
||||
},
|
||||
getPicBeds (event, picBeds) {
|
||||
this.picBed = picBeds
|
||||
this.getDefaultPicBed()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,8 +158,10 @@ export default {
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 20px auto
|
||||
margin 10px auto
|
||||
#upload-view
|
||||
.view-title
|
||||
margin 20px auto
|
||||
#upload-area
|
||||
height 220px
|
||||
border 2px dashed #dddddd
|
||||
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div id="aliyun-view">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="16" :offset="4">
|
||||
<div class="view-title">
|
||||
阿里云OSS设置
|
||||
</div>
|
||||
<el-form
|
||||
ref="aliyun"
|
||||
label-position="right"
|
||||
label-width="120px"
|
||||
:model="form"
|
||||
size="mini">
|
||||
<el-form-item
|
||||
label="设定KeyId"
|
||||
prop="accessKeyId"
|
||||
:rules="{
|
||||
required: true, message: 'AccessKeyId不能为空', trigger: 'blur'
|
||||
}">
|
||||
<el-input v-model="form.accessKeyId" placeholder="AccessKeyId" @keyup.native.enter="confirm"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="设定KeySecret"
|
||||
prop="accessKeySecret"
|
||||
:rules="{
|
||||
required: true, message: 'AccessKeySecret不能为空', trigger: 'blur'
|
||||
}">
|
||||
<el-input v-model="form.accessKeySecret" type="password" @keyup.native.enter="confirm" placeholder="AccessKeySecret"></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="area"
|
||||
:rules="{
|
||||
required: true, message: '区域代码不能为空', trigger: 'blur'
|
||||
}">
|
||||
<el-input v-model="form.area" @keyup.native.enter="confirm" placeholder="例如oss-cn-beijing"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="指定存储路径"
|
||||
>
|
||||
<el-input v-model="form.path" @keyup.native.enter="confirm" placeholder="例如img/"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="设定自定义域名"
|
||||
>
|
||||
<el-input v-model="form.customUrl" @keyup.native.enter="confirm" placeholder="例如https://xxxx.com"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button-group>
|
||||
<el-button type="primary" @click="confirm" round>确定</el-button>
|
||||
<el-button type="success" @click="setDefaultPicBed('aliyun')" round :disabled="defaultPicBed === 'aliyun'">设为默认图床</el-button>
|
||||
</el-button-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '@/utils/ConfirmButtonMixin'
|
||||
export default {
|
||||
mixins: [mixin],
|
||||
name: 'aliyun',
|
||||
data () {
|
||||
return {
|
||||
form: {
|
||||
accessKeyId: '',
|
||||
accessKeySecret: '',
|
||||
bucket: '',
|
||||
area: '',
|
||||
path: '',
|
||||
customUrl: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const config = this.$db.get('picBed.aliyun').value()
|
||||
if (config) {
|
||||
for (let i in config) {
|
||||
this.form[i] = config[i]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirm () {
|
||||
this.$refs.aliyun.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$db.set('picBed.aliyun', this.form).write()
|
||||
const successNotification = new window.Notification('设置结果', {
|
||||
body: '设置成功'
|
||||
})
|
||||
successNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
#aliyun-view
|
||||
.el-form
|
||||
label
|
||||
line-height 22px
|
||||
padding-bottom 0
|
||||
color #eee
|
||||
.el-input__inner
|
||||
border-radius 19px
|
||||
&-item
|
||||
margin-bottom 10.5px
|
||||
.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
|
||||
.el-switch__label
|
||||
color #eee
|
||||
&.is-active
|
||||
color #409EFF
|
||||
.el-icon-question
|
||||
font-size 20px
|
||||
float right
|
||||
margin-top 9px
|
||||
color #eee
|
||||
cursor pointer
|
||||
transition .2s color ease-in-out
|
||||
&:hover
|
||||
color #409EFF
|
||||
</style>
|
||||
+2
-7
@@ -57,9 +57,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '../mixin'
|
||||
import mixin from '@/utils/ConfirmButtonMixin'
|
||||
export default {
|
||||
name: 'upyun',
|
||||
name: 'github',
|
||||
mixins: [mixin],
|
||||
data () {
|
||||
return {
|
||||
@@ -100,11 +100,6 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 20px auto
|
||||
#github-view
|
||||
.el-form
|
||||
label
|
||||
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<div id="imgur-view">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="16" :offset="4">
|
||||
<div class="view-title">
|
||||
Imgur图床设置
|
||||
</div>
|
||||
<el-form
|
||||
ref="imgur"
|
||||
label-position="right"
|
||||
label-width="120px"
|
||||
:model="form"
|
||||
size="mini">
|
||||
<el-form-item
|
||||
label="设定ClientId"
|
||||
prop="clientId"
|
||||
:rules="{
|
||||
required: true, message: 'ClientId不能为空', trigger: 'blur'
|
||||
}">
|
||||
<el-input v-model="form.clientId" placeholder="ClientId" @keyup.native.enter="confirm"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="设定代理"
|
||||
prop="proxy"
|
||||
>
|
||||
<el-input v-model="form.proxy" placeholder="例如:http://127.0.0.1:1080" @keyup.native.enter="confirm"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button-group>
|
||||
<el-button type="primary" @click="confirm" round>确定</el-button>
|
||||
<el-button type="success" @click="setDefaultPicBed('imgur')" round :disabled="defaultPicBed === 'imgur'">设为默认图床</el-button>
|
||||
</el-button-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '@/utils/ConfirmButtonMixin'
|
||||
export default {
|
||||
name: 'imgur',
|
||||
mixins: [mixin],
|
||||
data () {
|
||||
return {
|
||||
form: {
|
||||
clientId: '',
|
||||
proxy: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const config = this.$db.get('picBed.imgur').value()
|
||||
if (config) {
|
||||
for (let i in config) {
|
||||
this.form[i] = config[i]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirm () {
|
||||
this.$refs.imgur.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$db.set('picBed.imgur', this.form).write()
|
||||
const successNotification = new window.Notification('设置结果', {
|
||||
body: '设置成功'
|
||||
})
|
||||
successNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
#imgur-view
|
||||
.el-form
|
||||
label
|
||||
line-height 22px
|
||||
padding-bottom 0
|
||||
color #eee
|
||||
.el-input__inner
|
||||
border-radius 19px
|
||||
&-item
|
||||
margin-bottom 10.5px
|
||||
.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
|
||||
.el-switch__label
|
||||
color #eee
|
||||
&.is-active
|
||||
color #409EFF
|
||||
.el-icon-question
|
||||
font-size 20px
|
||||
float right
|
||||
margin-top 9px
|
||||
color #eee
|
||||
cursor pointer
|
||||
transition .2s color ease-in-out
|
||||
&:hover
|
||||
color #409EFF
|
||||
</style>
|
||||
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div id="others-view">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="16" :offset="4">
|
||||
<div class="view-title">
|
||||
{{ picBedName }}设置
|
||||
</div>
|
||||
<config-form
|
||||
v-if="config.length > 0"
|
||||
:config="config"
|
||||
type="uploader"
|
||||
ref="configForm"
|
||||
:id="type"
|
||||
>
|
||||
<el-form-item>
|
||||
<el-button-group>
|
||||
<el-button type="primary" @click="handleConfirm" round>确定</el-button>
|
||||
<el-button type="success" @click="setDefaultPicBed(type)" round :disabled="defaultPicBed === type">设为默认图床</el-button>
|
||||
</el-button-group>
|
||||
</el-form-item>
|
||||
</config-form>
|
||||
<div v-else class="single">
|
||||
<div class="notice">暂无配置项</div>
|
||||
<el-button type="success" @click="setDefaultPicBed(type)" round :disabled="defaultPicBed === type" size="mini">设为默认图床</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import ConfigForm from '@/components/ConfigForm'
|
||||
import mixin from '@/utils/ConfirmButtonMixin'
|
||||
export default {
|
||||
name: 'OtherPicBed',
|
||||
mixins: [mixin],
|
||||
components: {
|
||||
ConfigForm
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
type: '',
|
||||
config: [],
|
||||
picBedName: ''
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.type = this.$route.params.type
|
||||
this.$electron.ipcRenderer.send('getPicBedConfig', this.$route.params.type)
|
||||
this.$electron.ipcRenderer.on('getPicBedConfig', this.getPicBeds)
|
||||
},
|
||||
methods: {
|
||||
async handleConfirm () {
|
||||
const result = await this.$refs.configForm.validate()
|
||||
if (result !== false) {
|
||||
this.$db.read().set(`picBed.${this.type}`, result).write()
|
||||
const successNotification = new window.Notification('设置结果', {
|
||||
body: '设置成功'
|
||||
})
|
||||
successNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
}
|
||||
},
|
||||
setDefaultPicBed (type) {
|
||||
this.$db.read().set('picBed.current', type).write()
|
||||
this.defaultPicBed = type
|
||||
const successNotification = new window.Notification('设置默认图床', {
|
||||
body: '设置成功'
|
||||
})
|
||||
successNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
},
|
||||
getPicBeds (event, config, name) {
|
||||
this.config = config
|
||||
this.picBedName = name
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.$electron.ipcRenderer.removeListener('getPicBedConfig', this.getPicBeds)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
#others-view
|
||||
.el-form
|
||||
label
|
||||
line-height 22px
|
||||
padding-bottom 0
|
||||
color #eee
|
||||
.el-button-group
|
||||
width 100%
|
||||
.el-button
|
||||
width 50%
|
||||
.el-input__inner
|
||||
border-radius 19px
|
||||
.el-radio-group
|
||||
margin-left 25px
|
||||
.el-switch__label
|
||||
color #eee
|
||||
&.is-active
|
||||
color #409EFF
|
||||
.notice
|
||||
color #eee
|
||||
text-align center
|
||||
margin-bottom 10px
|
||||
.single
|
||||
text-align center
|
||||
</style>
|
||||
+2
-7
@@ -75,10 +75,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '../mixin'
|
||||
import mixin from '@/utils/ConfirmButtonMixin'
|
||||
export default {
|
||||
name: 'qiniu',
|
||||
mixins: [mixin],
|
||||
name: 'qiniu',
|
||||
data () {
|
||||
return {
|
||||
form: {
|
||||
@@ -120,11 +120,6 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 20px auto
|
||||
#qiniu-view
|
||||
.el-form
|
||||
label
|
||||
@@ -16,10 +16,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '../mixin'
|
||||
import mixin from '@/utils/ConfirmButtonMixin'
|
||||
export default {
|
||||
name: 'upyun',
|
||||
mixins: [mixin],
|
||||
name: 'upyun',
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
@@ -38,11 +38,6 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 20px auto
|
||||
#smms-view
|
||||
.content
|
||||
text-align center
|
||||
+3
-8
@@ -86,10 +86,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '../mixin'
|
||||
import mixin from '@/utils/ConfirmButtonMixin'
|
||||
export default {
|
||||
name: 'tcyun',
|
||||
mixins: [mixin],
|
||||
name: 'tcyun',
|
||||
data () {
|
||||
return {
|
||||
form: {
|
||||
@@ -129,17 +129,12 @@ export default {
|
||||
})
|
||||
},
|
||||
openWiki () {
|
||||
this.$electron.remote.shell.openExternal('https://github.com/Molunerfinn/PicGo/wiki/%E8%AF%A6%E7%BB%86%E7%AA%97%E5%8F%A3%E7%9A%84%E4%BD%BF%E7%94%A8#%E5%BE%AE%E5%8D%9A%E5%9B%BE%E5%BA%8A')
|
||||
this.$electron.remote.shell.openExternal('https://github.com/Molunerfinn/PicGo/wiki/%E8%AF%A6%E7%BB%86%E7%AA%97%E5%8F%A3%E7%9A%84%E4%BD%BF%E7%94%A8#腾讯云cos')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 20px auto
|
||||
#tcyun-view
|
||||
.el-form
|
||||
label
|
||||
+1
-6
@@ -65,7 +65,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '../mixin'
|
||||
import mixin from '@/utils/ConfirmButtonMixin'
|
||||
export default {
|
||||
name: 'upyun',
|
||||
mixins: [mixin],
|
||||
@@ -108,11 +108,6 @@ export default {
|
||||
}
|
||||
</script>
|
||||
<style lang='stylus'>
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 20px auto
|
||||
#tcyun-view
|
||||
.el-form
|
||||
label
|
||||
+1
-6
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import mixin from '../mixin'
|
||||
import mixin from '@/utils/ConfirmButtonMixin'
|
||||
export default {
|
||||
name: 'weibo',
|
||||
mixins: [mixin],
|
||||
@@ -121,11 +121,6 @@ export default {
|
||||
<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
|
||||
@@ -8,62 +8,87 @@ export default new Router({
|
||||
{
|
||||
path: '/',
|
||||
name: 'tray-page',
|
||||
component: require('@/components/TrayPage').default
|
||||
component: require('@/pages/TrayPage').default
|
||||
},
|
||||
{
|
||||
path: '/rename-page',
|
||||
name: 'rename-page',
|
||||
component: require('@/components/RenamePage').default
|
||||
component: require('@/pages/RenamePage').default
|
||||
},
|
||||
{
|
||||
path: '/mini-page',
|
||||
name: 'mini-page',
|
||||
component: require('@/pages/MiniPage').default
|
||||
},
|
||||
{
|
||||
path: '/setting',
|
||||
name: 'setting-page',
|
||||
component: require('@/components/SettingPage').default,
|
||||
component: require('@/layouts/SettingPage').default,
|
||||
children: [
|
||||
{
|
||||
path: 'upload',
|
||||
component: require('@/components/SettingView/Upload').default,
|
||||
component: require('@/pages/Upload').default,
|
||||
name: 'upload'
|
||||
},
|
||||
{
|
||||
path: 'weibo',
|
||||
component: require('@/components/SettingView/Weibo').default,
|
||||
component: require('@/pages/picbeds/Weibo').default,
|
||||
name: 'weibo'
|
||||
},
|
||||
{
|
||||
path: 'qiniu',
|
||||
component: require('@/components/SettingView/Qiniu').default,
|
||||
component: require('@/pages/picbeds/Qiniu').default,
|
||||
name: 'qiniu'
|
||||
},
|
||||
{
|
||||
path: 'tcyun',
|
||||
component: require('@/components/SettingView/TcYun').default,
|
||||
component: require('@/pages/picbeds/TcYun').default,
|
||||
name: 'tcyun'
|
||||
},
|
||||
{
|
||||
path: 'upyun',
|
||||
component: require('@/components/SettingView/UpYun').default,
|
||||
component: require('@/pages/picbeds/UpYun').default,
|
||||
name: 'upyun'
|
||||
},
|
||||
{
|
||||
path: 'github',
|
||||
component: require('@/components/SettingView/GitHub').default,
|
||||
component: require('@/pages/picbeds/GitHub').default,
|
||||
name: 'github'
|
||||
},
|
||||
{
|
||||
path: 'smms',
|
||||
component: require('@/components/SettingView/SMMS').default,
|
||||
component: require('@/pages/picbeds/SMMS').default,
|
||||
name: 'smms'
|
||||
},
|
||||
{
|
||||
path: 'aliyun',
|
||||
component: require('@/pages/picbeds/AliYun').default,
|
||||
name: 'aliyun'
|
||||
},
|
||||
{
|
||||
path: 'imgur',
|
||||
component: require('@/pages/picbeds/Imgur').default,
|
||||
name: 'imgur'
|
||||
},
|
||||
{
|
||||
path: 'others/:type',
|
||||
component: require('@/pages/picbeds/Others').default,
|
||||
name: 'others'
|
||||
},
|
||||
{
|
||||
path: 'gallery',
|
||||
component: require('@/components/SettingView/Gallery').default,
|
||||
component: require('@/pages/Gallery').default,
|
||||
name: 'gallery'
|
||||
},
|
||||
{
|
||||
path: 'setting',
|
||||
component: require('@/components/SettingView/PicGoSetting').default,
|
||||
component: require('@/pages/PicGoSetting').default,
|
||||
name: 'setting'
|
||||
},
|
||||
{
|
||||
path: 'plugin',
|
||||
component: require('@/pages/Plugin').default,
|
||||
name: 'plugin'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
export default {
|
||||
name: '',
|
||||
data () {
|
||||
return {
|
||||
defaultPicBed: this.$db.read().get('picBed.current').value()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setDefaultPicBed (type) {
|
||||
this.$db.read().set('picBed.current', type).write()
|
||||
this.defaultPicBed = type
|
||||
const successNotification = new window.Notification('设置默认图床', {
|
||||
body: '设置成功'
|
||||
})
|
||||
successNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
class LS {
|
||||
get (name) {
|
||||
if (localStorage.getItem(name)) {
|
||||
return JSON.parse(localStorage.getItem(name))
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
set (name, value) {
|
||||
return localStorage.setItem(name, JSON.stringify(value))
|
||||
}
|
||||
}
|
||||
|
||||
export default new LS()
|
||||
@@ -1,13 +1,7 @@
|
||||
import db from '../../datastore'
|
||||
export default {
|
||||
mounted () {
|
||||
this.disableDragEvent()
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
defaultPicBed: db.read().get('picBed.current').value()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
disableDragEvent () {
|
||||
window.addEventListener('dragenter', this.disableDrag, false)
|
||||
@@ -21,17 +15,6 @@ export default {
|
||||
e.dataTransfer.effectAllowed = 'none'
|
||||
e.dataTransfer.dropEffect = 'none'
|
||||
}
|
||||
},
|
||||
setDefaultPicBed (type) {
|
||||
db.read().set('picBed.current', type).write()
|
||||
this.defaultPicBed = type
|
||||
this.$electron.ipcRenderer.send('updateDefaultPicBed')
|
||||
const successNotification = new window.Notification('设置默认图床', {
|
||||
body: '设置成功'
|
||||
})
|
||||
successNotification.onclick = () => {
|
||||
return true
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
@@ -0,0 +1,16 @@
|
||||
# from https://github.com/favers/vscode-qiniu-upload-image/blob/master/lib/linux.sh
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
# require xclip(see http://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script/677212#677212)
|
||||
command -v xclip >/dev/null 2>&1 || { echo >&1 "no xclip"; exit 1; }
|
||||
|
||||
# write image in clipboard to file (see http://unix.stackexchange.com/questions/145131/copy-image-from-clipboard-to-file)
|
||||
if
|
||||
xclip -selection clipboard -target image/png -o >/dev/null 2>&1
|
||||
then
|
||||
xclip -selection clipboard -target image/png -o >$1 2>/dev/null
|
||||
echo $1
|
||||
else
|
||||
echo "no image"
|
||||
fi
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,38 @@
|
||||
-- From https://github.com/mushanshitiancai/vscode-paste-image
|
||||
property fileTypes : {{«class PNGf», ".png"}}
|
||||
|
||||
on run argv
|
||||
if argv is {} then
|
||||
return ""
|
||||
end if
|
||||
|
||||
set imagePath to (item 1 of argv)
|
||||
set theType to getType()
|
||||
|
||||
if theType is not missing value then
|
||||
try
|
||||
set myFile to (open for access imagePath with write permission)
|
||||
set eof myFile to 0
|
||||
write (the clipboard as (first item of theType)) to myFile
|
||||
close access myFile
|
||||
return (POSIX path of imagePath)
|
||||
on error
|
||||
try
|
||||
close access myFile
|
||||
end try
|
||||
return ""
|
||||
end try
|
||||
else
|
||||
return "no image"
|
||||
end if
|
||||
end run
|
||||
|
||||
on getType()
|
||||
repeat with aType in fileTypes
|
||||
repeat with theInfo in (clipboard info)
|
||||
if (first item of theInfo) is equal to (first item of aType) then return aType
|
||||
end repeat
|
||||
end repeat
|
||||
return missing value
|
||||
end getType
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
param($imagePath)
|
||||
|
||||
# Adapted from https://github.com/octan3/img-clipboard-dump/blob/master/dump-clipboard-png.ps1
|
||||
|
||||
Add-Type -Assembly PresentationCore
|
||||
$img = [Windows.Clipboard]::GetImage()
|
||||
|
||||
if ($img -eq $null) {
|
||||
"no image"
|
||||
Exit 1
|
||||
}
|
||||
|
||||
if (-not $imagePath) {
|
||||
"no image"
|
||||
Exit 1
|
||||
}
|
||||
|
||||
$fcb = new-object Windows.Media.Imaging.FormatConvertedBitmap($img, [Windows.Media.PixelFormats]::Rgb24, $null, 0)
|
||||
$stream = [IO.File]::Open($imagePath, "OpenOrCreate")
|
||||
$encoder = New-Object Windows.Media.Imaging.PngBitmapEncoder
|
||||
$encoder.Frames.Add([Windows.Media.Imaging.BitmapFrame]::Create($fcb)) | out-null
|
||||
$encoder.Save($stream) | out-null
|
||||
$stream.Dispose() | out-null
|
||||
|
||||
$imagePath
|
||||
Reference in New Issue
Block a user