mirror of
https://github.com/Molunerfinn/PicGo.git
synced 2024-04-21 10:31:33 +00:00
Compare commits
36
Commits
v2.2.2
...
v2.3.0-beta.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e855384e0 | ||
|
|
21d3942d2b | ||
|
|
4f0809e6b3 | ||
|
|
f40a1bb981 | ||
|
|
5f87018af8 | ||
|
|
7fabc47bd2 | ||
|
|
db71139f5f | ||
|
|
1c5880ab7c | ||
|
|
b93dd05fd3 | ||
|
|
59d3eba86d | ||
|
|
a693544291 | ||
|
|
2c57a27284 | ||
|
|
b6e3adb7af | ||
|
|
2adff1e788 | ||
|
|
18ad54251e | ||
|
|
72387baf55 | ||
|
|
b357dfb243 | ||
|
|
ad6acd8c4f | ||
|
|
2aeca509a8 | ||
|
|
b7cbd50b17 | ||
|
|
b5ceaeda1e | ||
|
|
7e866185e9 | ||
|
|
96cdfea497 | ||
|
|
3df2253ae9 | ||
|
|
cc182b08f7 | ||
|
|
67e526f163 | ||
|
|
cd70a1a5cc | ||
|
|
f3575575ac | ||
|
|
3f3ea69cd6 | ||
|
|
32334e9bc1 | ||
|
|
96544f5475 | ||
|
|
a28c67848d | ||
|
|
23ba2338c6 | ||
|
|
024d9cf41e | ||
|
|
fd7673e4d1 | ||
|
|
fb014ddc9e |
@@ -0,0 +1 @@
|
|||||||
|
custom: ["https://paypal.me/Molunerfinn"]
|
||||||
@@ -1,30 +1,34 @@
|
|||||||
<!--
|
<!--
|
||||||
PicGo Issue 模板
|
PicGo Issue 模板
|
||||||
请依照该模板来提交,否则将会被关闭。
|
请依照该模板来提交,否则将会被关闭。
|
||||||
**提问之前请注意你看过FAQ、配置手册以及那些被关闭的issues。否则同样的提问也会被关闭!**
|
**提问之前请注意你看过 FAQ、配置手册以及那些被关闭的 issues。否则同样的提问也会被关闭!**
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
**声明:我已经仔细看过 [文档](https://picgo.github.io/PicGo-Doc/)、[FAQ](https://github.com/Molunerfinn/PicGo/blob/dev/FAQ.md)、和搜索过已经关闭的 [issues](https://github.com/Molunerfinn/PicGo/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed) 后依然没有找到答案,所以才发了一个新的 issue。**
|
||||||
|
|
||||||
## 问题类型
|
## 问题类型
|
||||||
|
|
||||||
<!-- 你要提交的是Bug Report还是Feature Request?-->
|
<!-- 你要提交的是 Bug Report 还是 Feature Request?-->
|
||||||
|
|
||||||
## PicGo的相关信息
|
## PicGo 的相关信息
|
||||||
|
|
||||||
<!-- 版本、所在平台(Mac或者Windows) -->
|
<!-- PicGo 的版本、所在平台(Mac 或者 Windows 或者 Linux) -->
|
||||||
|
|
||||||
## 问题重现
|
## 问题重现
|
||||||
|
|
||||||
<!-- 如果是Bug Report请填写本项 -->
|
<!-- 如果是 Bug Report 请填写本项 -->
|
||||||
<!-- 请附上相关截图 -->
|
<!-- 请附上相关截图 -->
|
||||||
|
<!-- 请附上 PicGo 的相关报错日志(用文本的形式)否则无法判断原因-->
|
||||||
|
<!-- 报错日志可以在 PicGo 设置 -> 设置日志文件 -> 点击打开 后找到 -->
|
||||||
|
|
||||||
## 功能请求
|
## 功能请求
|
||||||
|
|
||||||
<!-- 如果是Feature Request请填写本项 -->
|
<!-- 如果是 Feature Request 请填写本项 -->
|
||||||
<!-- 详细描述你所预想的功能或者是现有功能的改进。 -->
|
<!-- 详细描述你所预想的功能或者是现有功能的改进。 -->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
最后,喜欢PicGo的话不妨给它点个star~
|
最后,喜欢 PicGo 的话不妨给它点个 star~
|
||||||
如果可以的话,请我喝杯咖啡?首页有赞助二维码,谢谢你的支持!
|
如果可以的话,请我喝杯咖啡?首页有赞助二维码,谢谢你的支持!
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -16,3 +16,4 @@ docs/dist/
|
|||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
dist_electron/
|
dist_electron/
|
||||||
|
test.js
|
||||||
|
|||||||
+3
-3
@@ -22,9 +22,7 @@ addons:
|
|||||||
- icnsutils
|
- icnsutils
|
||||||
#- xvfb
|
#- xvfb
|
||||||
before_install:
|
before_install:
|
||||||
- mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install git-lfs; fi
|
||||||
"$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:
|
install:
|
||||||
#- export DISPLAY=':99.0'
|
#- export DISPLAY=':99.0'
|
||||||
@@ -39,6 +37,8 @@ script:
|
|||||||
#- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e
|
#- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e
|
||||||
- npm run release
|
- npm run release
|
||||||
# - yarn run build:docs
|
# - yarn run build:docs
|
||||||
|
before_script:
|
||||||
|
- git lfs pull
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
|||||||
@@ -1,3 +1,86 @@
|
|||||||
|
# :tada: 2.3.0-beta.3 (2020-07-12)
|
||||||
|
|
||||||
|
|
||||||
|
### :bug: Bug Fixes
|
||||||
|
|
||||||
|
* choose default picBed failure ([21d3942](https://github.com/Molunerfinn/PicGo/commit/21d3942)), closes [#537](https://github.com/Molunerfinn/PicGo/issues/537)
|
||||||
|
* shortkey disabled failure ([4f0809e](https://github.com/Molunerfinn/PicGo/commit/4f0809e)), closes [#534](https://github.com/Molunerfinn/PicGo/issues/534)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# :tada: 2.3.0-beta.2 (2020-07-12)
|
||||||
|
|
||||||
|
|
||||||
|
### :sparkles: Features
|
||||||
|
|
||||||
|
* add qrcode for picbeds' config ([7fabc47](https://github.com/Molunerfinn/PicGo/commit/7fabc47))
|
||||||
|
|
||||||
|
|
||||||
|
### :bug: Bug Fixes
|
||||||
|
|
||||||
|
* encoding the result of picgo-server ([db71139](https://github.com/Molunerfinn/PicGo/commit/db71139))
|
||||||
|
* initialize db bugs ([5f87018](https://github.com/Molunerfinn/PicGo/commit/5f87018))
|
||||||
|
|
||||||
|
|
||||||
|
### :pencil: Documentation
|
||||||
|
|
||||||
|
* update readme ([1c5880a](https://github.com/Molunerfinn/PicGo/commit/1c5880a))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# :tada: 2.3.0-beta.1 (2020-06-28)
|
||||||
|
|
||||||
|
|
||||||
|
### :bug: Bug Fixes
|
||||||
|
|
||||||
|
* auto-copy option && copy style ([b6e3adb](https://github.com/Molunerfinn/PicGo/commit/b6e3adb))
|
||||||
|
* beta version update bug ([18ad542](https://github.com/Molunerfinn/PicGo/commit/18ad542))
|
||||||
|
* paste url encoding bug ([59d3eba](https://github.com/Molunerfinn/PicGo/commit/59d3eba)), closes [#454](https://github.com/Molunerfinn/PicGo/issues/454)
|
||||||
|
|
||||||
|
|
||||||
|
### :pencil: Documentation
|
||||||
|
|
||||||
|
* update FAQ && ISSUE_TEMPLATE ([2c57a27](https://github.com/Molunerfinn/PicGo/commit/2c57a27))
|
||||||
|
* update readme ([2adff1e](https://github.com/Molunerfinn/PicGo/commit/2adff1e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# :tada: 2.3.0-beta.0 (2020-04-30)
|
||||||
|
|
||||||
|
|
||||||
|
### :sparkles: Features
|
||||||
|
|
||||||
|
* add autoCopy option for users to use or not ([67e526f](https://github.com/Molunerfinn/PicGo/commit/67e526f))
|
||||||
|
* add beta-version update support ([ad6acd8](https://github.com/Molunerfinn/PicGo/commit/ad6acd8))
|
||||||
|
* add smms-v2 support ([3f3ea69](https://github.com/Molunerfinn/PicGo/commit/3f3ea69))
|
||||||
|
* add uploading image from URL support ([a28c678](https://github.com/Molunerfinn/PicGo/commit/a28c678))
|
||||||
|
* finish all-select && shift multi-select ([2aeca50](https://github.com/Molunerfinn/PicGo/commit/2aeca50)), closes [#342](https://github.com/Molunerfinn/PicGo/issues/342)
|
||||||
|
|
||||||
|
|
||||||
|
### :bug: Bug Fixes
|
||||||
|
|
||||||
|
* confused port number auto increasing when opening a new PicGo app ([cd70a1a](https://github.com/Molunerfinn/PicGo/commit/cd70a1a))
|
||||||
|
* correct inputbox value && remove listener ([32334e9](https://github.com/Molunerfinn/PicGo/commit/32334e9))
|
||||||
|
* give a hint when node.js is not installed ([7e86618](https://github.com/Molunerfinn/PicGo/commit/7e86618))
|
||||||
|
* right-click menu upload fails with PicGo open ([96cdfea](https://github.com/Molunerfinn/PicGo/commit/96cdfea)), closes [#415](https://github.com/Molunerfinn/PicGo/issues/415)
|
||||||
|
* some win10 upload clipboard image crash ([cc182b0](https://github.com/Molunerfinn/PicGo/commit/cc182b0))
|
||||||
|
* travis-ci bug ([b357dfb](https://github.com/Molunerfinn/PicGo/commit/b357dfb))
|
||||||
|
* url uploader bug ([96544f5](https://github.com/Molunerfinn/PicGo/commit/96544f5))
|
||||||
|
|
||||||
|
|
||||||
|
### :pencil: Documentation
|
||||||
|
|
||||||
|
* update choco install picgo ([f357557](https://github.com/Molunerfinn/PicGo/commit/f357557))
|
||||||
|
* update docs ([fd7673e](https://github.com/Molunerfinn/PicGo/commit/fd7673e))
|
||||||
|
* update readme ([fb014dd](https://github.com/Molunerfinn/PicGo/commit/fb014dd))
|
||||||
|
|
||||||
|
|
||||||
|
### :package: Chore
|
||||||
|
|
||||||
|
* update funding url ([024d9cf](https://github.com/Molunerfinn/PicGo/commit/024d9cf))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## :tada: 2.2.2 (2020-01-20)
|
## :tada: 2.2.2 (2020-01-20)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,58 +1,59 @@
|
|||||||
## 常见问题
|
## 常见问题
|
||||||
|
|
||||||
> 在使用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),应该能找到答案。
|
> 在使用 PicGo 期间你会遇到很多问题,不过很多问题其实之前就有人提问过,也被解决,所以你可以先看看 [使用文档](https://picgo.github.io/PicGo-Doc/zh/guide/getting-started.html#%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B),这份 FAQ,以及那些被关闭的 [issues](https://github.com/Molunerfinn/PicGo/issues?q=is%3Aissue+is%3Aclosed),应该能找到答案。
|
||||||
|
|
||||||
## 1.七牛图床上传图片成功后无法显示或图片无`http://`前缀
|
## 1. 七牛图床上传图片成功后,相册里无法显示或图片无`http://`前缀
|
||||||
|
|
||||||
通常是你的七牛图床配置里的`设定访问网址`没有加上`http://`或者`https//`头。
|
通常是你的七牛图床配置里的`设定访问网址`没有加上`http://`或者`https//`头。
|
||||||
|
|
||||||
参考:[issue#79](https://github.com/Molunerfinn/PicGo/issues/79)
|
参考:[issue#79](https://github.com/Molunerfinn/PicGo/issues/79)
|
||||||
|
|
||||||
## 2.能否支持图床远端同步删除
|
## 2. 能否支持图床远端同步删除
|
||||||
|
|
||||||
不能。有些图床(比如微博图床、SM.MS、Imgur等)不支持后台管理,为了架构统一不支持远端删除。
|
不能。有些图床(比如微博图床、SM.MS、Imgur 等)不支持后台管理,为了架构统一不支持远端删除。
|
||||||
|
|
||||||
## 3.能否支持上传视频文件
|
## 3. 能否支持上传视频文件
|
||||||
|
|
||||||
目前不能。2.0版本出来后将会支持插件系统,届时如果有人开发了相应的插件理论可以支持任意文件上传。
|
目前不能。如果有人开发了相应的插件理论可以支持任意文件上传。
|
||||||
|
|
||||||
## 4.微博图床上传之后无法显示预览图
|
## 4. 微博图床上传之后无法显示预览图
|
||||||
|
|
||||||
通常是挂了全局代理导致的。
|
通常是挂了全局代理导致的。
|
||||||
|
|
||||||
参考:[issue36](https://github.com/Molunerfinn/PicGo/issues/36)
|
参考:[issue36](https://github.com/Molunerfinn/PicGo/issues/36)
|
||||||
|
|
||||||
## 5.能否支持某某某图床
|
## 5. 能否支持某某某图床
|
||||||
|
|
||||||
截止v1.6,PicGo支持了如下图床:
|
截止 v1.6,PicGo 支持了如下图床:
|
||||||
|
|
||||||
- `微博图床` v1.0
|
- `微博图床` v1.0
|
||||||
- `七牛图床` v1.0
|
- `七牛图床` v1.0
|
||||||
- `腾讯云COS v4\v5版本` v1.1 & v1.5.0
|
- `腾讯云 COS v4\v5 版本` v1.1 & v1.5.0
|
||||||
- `又拍云` v1.2.0
|
- `又拍云` v1.2.0
|
||||||
- `GitHub` v1.5.0
|
- `GitHub` v1.5.0
|
||||||
- `SM.MS` v1.5.1
|
- `SM.MS` v1.5.1
|
||||||
- `阿里云OSS` v1.6.0
|
- `阿里云 OSS` v1.6.0
|
||||||
- `Imgur` v1.6.0
|
- `Imgur` v1.6.0
|
||||||
|
|
||||||
所以本体内将不会再支持其他图床。待2.0版本出来支持插件系统后可以由其他开发者开发相应的图床插件用以支持其他图床。
|
所以本体内将不会再支持其他图床。需要其他图床支持可以参考目前已有的三方 [插件](https://github.com/PicGo/Awesome-PicGo),如果还是没有你所需要的图床欢迎开发一个插件供大家使用。
|
||||||
|
|
||||||
## 6.一个图床设置多个信息
|
## 6. 一个图床设置多个信息
|
||||||
|
|
||||||
不能。因为目前的架构只支持一个图床一份信息。
|
不能。因为目前的架构只支持一个图床一份信息。
|
||||||
|
|
||||||
## 7.v2.0版本有什么新功能
|
## 7. GitHub 图床有时能上传,有时上传失败
|
||||||
|
|
||||||
在不改变PicGo现有使用界面和使用习惯的基础上,将会重构整个底层`传输->转换->上传`的架构,使其能够支持插件系统,方便其他开发者基于PicGo的底层进行二次开发。
|
1. GitHub 图床不支持上传同名文件,如果有同名文件上传,会报错。建议开启 `时间戳重命名` 避免同名文件。
|
||||||
|
2. GitHub 服务器和国内 GFW 的问题会导致有时上传成功,有时上传失败,无解。想要稳定请使用付费云存储,如阿里云、腾讯云等,价格也不会贵。
|
||||||
|
|
||||||
理论上有了插件系统之后,其他开发者能够支持如下功能:
|
## 8. Mac 上无法打开 PicGo 的主窗口界面
|
||||||
|
|
||||||
1. 能够实现上传前图片压缩
|
PicGo 在 Mac 上是一个顶部栏应用,在 dock 栏是不会有图标的。要打开主窗口,请右键或者双指点按顶部栏 PicGo 图标,选择「打开详细窗口」即可打开主窗口。
|
||||||
2. 能够通过图片url实现上传图片 -> 更深入地可以批量地实现图床的迁移
|
|
||||||
3. 能够支持其他图床甚至是自己的图床
|
|
||||||
|
|
||||||
等等。
|
## 9. 上传失败,或者是服务器出错
|
||||||
|
|
||||||
届时底层架构将会独立出来成为一个独立的npm包,能够直接在命令行里调用。
|
1. PicGo 自带的图床都经过测试,上传出错一般都不是 PicGo 自身的原因。如果你用的是 GitHub 图床请参考上面的第 7 点。
|
||||||
|
2. 检查 PicGo 的日志(报错日志可以在 PicGo 设置 -> 设置日志文件 -> 点击打开 后找到),看看 `[PicGo Error]` 的报错信息里有什么关键信息
|
||||||
目前开发进度可以参考[PicGo-Core](https://github.com/Molunerfinn/PicGo-Core)。
|
1. 先自行搜索 error 里的报错信息,往往你能百度或者谷歌出问题原因,不必开 issue。
|
||||||
|
2. 如果有带有 `401` 、`403` 等 `40X` 状态码字样的,不用怀疑,就是你配置写错了,仔细检查配置,看看是否多了空格之类的。
|
||||||
|
3. 如果带有 `HttpError`、`RequestError` 、 `socket hang up` 等字样的说明这是网络问题,我无法帮你解决网络问题,请检查你自己的网络,是否有代理,DNS 设置是否正常等。
|
||||||
|
|||||||
@@ -17,48 +17,68 @@
|
|||||||
<a href="https://github.com/PicGo/bump-version">
|
<a href="https://github.com/PicGo/bump-version">
|
||||||
<img src="https://img.shields.io/badge/picgo-convention-blue.svg?style=flat-square" alt="">
|
<img src="https://img.shields.io/badge/picgo-convention-blue.svg?style=flat-square" alt="">
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://gitter.im/picgo-all/PicGo?utm_source=share-link&utm_medium=link&utm_campaign=share-link">
|
||||||
|
<img src="https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-66ae93.svg?style=flat-square" alt="">
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## 应用说明
|
## 应用概述
|
||||||
|
|
||||||
**PicGo 在上传图片之后自动会将图片链接复制到你的剪贴板里,可选 5 种复制的链接格式。**
|
**PicGo: 一个用于快速上传图片并获取图片 URL 链接的工具**
|
||||||
|
|
||||||
PicGo 目前支持了
|
PicGo 本体支持如下图床:
|
||||||
|
|
||||||
- ~~`微博图床` v1.0~~ **微博图床从 2019 年 4 月开始进行防盗链,不建议继续使用**
|
|
||||||
- `七牛图床` v1.0
|
- `七牛图床` v1.0
|
||||||
- `腾讯云 COS v4\v5版本` v1.1 & v1.5.0
|
- `腾讯云 COS v4\v5 版本` v1.1 & v1.5.0
|
||||||
- `又拍云` v1.2.0
|
- `又拍云` v1.2.0
|
||||||
- `GitHub` v1.5.0
|
- `GitHub` v1.5.0
|
||||||
- `SM.MS` v1.5.1
|
- `SM.MS V2` v2.3.0-beta.0
|
||||||
- `阿里云 OSS` v1.6.0
|
- `阿里云 OSS` v1.6.0
|
||||||
- `Imgur` v1.6.0
|
- `Imgur` v1.6.0
|
||||||
|
|
||||||
**本体不再增加默认的图床支持。你可以自行开发第三方图床插件。详见 [PicGo-Core](https://picgo.github.io/PicGo-Core-Doc/)**。
|
**本体不再增加默认的图床支持。你可以自行开发第三方图床插件。详见 [PicGo-Core](https://picgo.github.io/PicGo-Core-Doc/)**。
|
||||||
|
|
||||||
第三方插件以及使用了 PicGo 底层的应用可以在 [Awesome-PicGo](https://github.com/PicGo/Awesome-PicGo) 找到。欢迎贡献!
|
## 特色功能
|
||||||
|
|
||||||
PicGo 支持 macOS、Windows 64位(>= v1.3.1),Linux(>= v1.6.0)。
|
- 支持拖拽图片上传
|
||||||
|
- 支持快捷键上传剪贴板里第一张图片
|
||||||
|
- Windows 和 macOS 支持右键图片文件通过菜单上传 (v2.1.0+)
|
||||||
|
- 上传图片后自动复制链接到剪贴板
|
||||||
|
- 支持自定义复制到剪贴板的链接格式
|
||||||
|
- 支持修改快捷键,默认快速上传快捷键:`command+shift+p`(macOS)| `control+shift+p`(Windows\Linux)
|
||||||
|
- 支持插件系统,已有插件支持 Gitee、青云等第三方图床
|
||||||
|
- 更多第三方插件以及使用了 PicGo 底层的应用可以在 [Awesome-PicGo](https://github.com/PicGo/Awesome-PicGo) 找到。欢迎贡献!
|
||||||
|
- 支持通过发送 HTTP 请求调用 PicGo 上传(v2.2.0+)
|
||||||
|
- 更多功能等你自己去发现,同时也会不断开发新功能
|
||||||
|
- 开发进度可以查看 [Projects](https://github.com/Molunerfinn/PicGo/projects),会同步更新开发进度
|
||||||
|
- 欢迎加入 [官方 Gitter 频道](https://gitter.im/picgo-all/PicGo?utm_source=share-link&utm_medium=link&utm_campaign=share-link) 与我交流
|
||||||
|
|
||||||
支持快捷键`command+shift+p`(macOS)或者`control+shift+p`(Windows\Linux)用以支持快捷上传剪贴板里的图片(第一张)。
|
**如果第一次使用,请参考应用 [使用文档](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)。**
|
||||||
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)。**
|
|
||||||
|
|
||||||
## 下载安装
|
## 下载安装
|
||||||
|
|
||||||
点击此处下载[应用](https://github.com/Molunerfinn/PicGo/releases)。
|
点击此处下载 [应用](https://github.com/Molunerfinn/PicGo/releases)。
|
||||||
|
|
||||||
macOS 用户请下载最新版本的 `dmg` 文件,Windows 用户请下载最新版本的 `exe` 文件,Linux用户请下载 `AppImage` 文件。
|
### Windows
|
||||||
|
|
||||||
|
Windows 用户请下载最新版本的 `exe` 文件。
|
||||||
|
|
||||||
|
**还可以使用 [Scoop](https://scoop.sh/) 来安装 PicGo: `scoop bucket add helbing https://github.com/helbing/scoop-bucket` & `scoop install picgo`。 感谢 @helbing 的贡献!**
|
||||||
|
|
||||||
|
**还可以使用 [Chocolatey](https://chocolatey.org/) 来安装 PicGo: `choco install picgo`。 感谢 @iYato 的贡献!**
|
||||||
|
|
||||||
|
### macOS
|
||||||
|
|
||||||
|
macOS 用户请下载最新版本的 `dmg` 文件。
|
||||||
|
|
||||||
|
**还可以使用 `brew cask` 来安装 PicGo: `brew cask install picgo`。感谢 @womeimingzi11 的贡献!**
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
Linux 用户请下载 `AppImage` 文件。
|
||||||
|
|
||||||
**如果你是 Arch 类的 Linux 用户,可以直接通过 `aurman -S picgo-appimage` 来安装 PicGo。感谢 @houbaron 的贡献!**
|
**如果你是 Arch 类的 Linux 用户,可以直接通过 `aurman -S picgo-appimage` 来安装 PicGo。感谢 @houbaron 的贡献!**
|
||||||
|
|
||||||
**如果你是 macOS 用户,可以使用 `brew cask` 来安装 PicGo: `brew cask install picgo`。感谢 @womeimingzi11 的贡献!**
|
|
||||||
|
|
||||||
**如果你是 Windows 用户,还可以使用 [Scoop](https://scoop.sh/) 来安装 PicGo: `scoop bucket add helbing https://github.com/helbing/scoop-bucket` & `scoop install picgo`。 感谢 @helbing 的贡献!**
|
|
||||||
|
|
||||||
## 应用截图
|
## 应用截图
|
||||||
|
|
||||||

|

|
||||||
@@ -73,9 +93,9 @@ macOS 用户请下载最新版本的 `dmg` 文件,Windows 用户请下载最
|
|||||||
|
|
||||||
> 如果想学习 Electron-vue 的开发,可以查看我写的系列教程——[Electron-vue 开发实战](https://molunerfinn.com/tags/Electron-vue/)
|
> 如果想学习 Electron-vue 的开发,可以查看我写的系列教程——[Electron-vue 开发实战](https://molunerfinn.com/tags/Electron-vue/)
|
||||||
|
|
||||||
1. 你需要有 Node、Git环境,了解 npm 的相关知识。
|
1. 你需要有 Node、Git 环境,了解 npm 的相关知识。
|
||||||
2. `git clone https://github.com/Molunerfinn/PicGo.git` 并进入项目
|
2. `git clone https://github.com/Molunerfinn/PicGo.git` 并进入项目。
|
||||||
3. `npm install` 下载依赖
|
3. `yarn` 下载依赖。注意如果你没有 `yarn`,请去 [官网](https://classic.yarnpkg.com/en/docs/install) 下载安装后再使用。 **用 `npm install` 将导致未知错误!**
|
||||||
4. Mac 需要有 Xcode 环境,Windows 需要有 VS 环境。
|
4. Mac 需要有 Xcode 环境,Windows 需要有 VS 环境。
|
||||||
|
|
||||||
### 开发模式
|
### 开发模式
|
||||||
@@ -84,19 +104,21 @@ macOS 用户请下载最新版本的 `dmg` 文件,Windows 用户请下载最
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
ctrl+c # 退出开发模式
|
ctrl+c # 退出开发模式
|
||||||
npm run dev # 重新进入开发模式
|
npm run electron:serve # 重新进入开发模式
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**注:Windows 开发模式运行之后会在底部任务栏的右下角应用区出现 PicGo 的应用图标。**
|
||||||
|
|
||||||
### 生产模式
|
### 生产模式
|
||||||
|
|
||||||
如果你需要自行构建,可以 `npm run electron:build` 开始进行构建。构建成功后,会在 `dist_electron` 目录里出现构建成功的相应安装文件。
|
如果你需要自行构建,可以 `npm run electron:build` 开始进行构建。构建成功后,会在 `dist_electron` 目录里出现构建成功的相应安装文件。
|
||||||
|
|
||||||
**注意**:如果你的网络环境不太好,可能会出现 `electron-builder` 下载 `electron` 二进制文件失败的情况。这个时候需要在 `npm run build` 之前指定一下 `electron` 的源为国内源:
|
**注意**:如果你的网络环境不太好,可能会出现 `electron-builder` 下载 `electron` 二进制文件失败的情况。这个时候需要在 `npm run electron:build` 之前指定一下 `electron` 的源为国内源:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/"
|
export ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/"
|
||||||
# 在 Windows 上,则可以使用 set ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/ (无需引号)
|
# 在 Windows 上,则可以使用 set ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/ (无需引号)
|
||||||
npm run build
|
npm run electron:build
|
||||||
```
|
```
|
||||||
|
|
||||||
只需第一次构建的时候指定一下国内源即可。后续构建不需要特地指定。二进制文件下载在 `~/.electron/` 目录下。如果想要更新 `electron` 构建版本,可以删除 `~/.electron/` 目录,然后重新运行上一步,让 `electron-builder `去下载最新的 `electron` 二进制文件。
|
只需第一次构建的时候指定一下国内源即可。后续构建不需要特地指定。二进制文件下载在 `~/.electron/` 目录下。如果想要更新 `electron` 构建版本,可以删除 `~/.electron/` 目录,然后重新运行上一步,让 `electron-builder `去下载最新的 `electron` 二进制文件。
|
||||||
|
|||||||
+5
-4
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "picgo",
|
"name": "picgo",
|
||||||
"version": "2.2.2",
|
"version": "2.3.0-beta.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "vue-cli-service serve",
|
"dev": "vue-cli-service electron:serve",
|
||||||
"build": "vue-cli-service build",
|
"build": "vue-cli-service electron:build",
|
||||||
"lint": "vue-cli-service lint",
|
"lint": "vue-cli-service lint",
|
||||||
"electron:build": "vue-cli-service electron:build",
|
"electron:build": "vue-cli-service electron:build",
|
||||||
"electron:serve": "vue-cli-service electron:serve",
|
"electron:serve": "vue-cli-service electron:serve",
|
||||||
@@ -42,7 +42,8 @@
|
|||||||
"keycode": "^2.2.0",
|
"keycode": "^2.2.0",
|
||||||
"lodash-id": "^0.14.0",
|
"lodash-id": "^0.14.0",
|
||||||
"lowdb": "^1.0.0",
|
"lowdb": "^1.0.0",
|
||||||
"picgo": "^1.4.4",
|
"picgo": "^1.4.11",
|
||||||
|
"qrcode.vue": "^1.7.0",
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.6.10",
|
||||||
"vue-gallery": "^2.0.1",
|
"vue-gallery": "^2.0.1",
|
||||||
"vue-lazyload": "^1.2.6",
|
"vue-lazyload": "^1.2.6",
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ function main {
|
|||||||
$stream.Dispose() | out-null
|
$stream.Dispose() | out-null
|
||||||
|
|
||||||
$imagePath
|
$imagePath
|
||||||
|
Exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
+2
-154
@@ -1,158 +1,6 @@
|
|||||||
import {
|
import { bootstrap } from '~/main/lifeCycle'
|
||||||
app,
|
|
||||||
globalShortcut,
|
|
||||||
protocol
|
|
||||||
} from 'electron'
|
|
||||||
import {
|
|
||||||
createProtocol,
|
|
||||||
installVueDevtools
|
|
||||||
} from 'vue-cli-plugin-electron-builder/lib'
|
|
||||||
import db from '#/datastore'
|
|
||||||
import { IWindowList } from '~/main/apis/window/constants'
|
|
||||||
import windowManager from '~/main/apis/window/windowManager'
|
|
||||||
import {
|
|
||||||
uploadChoosedFiles,
|
|
||||||
uploadClipboardFiles
|
|
||||||
} from '~/main/apis/uploader/api'
|
|
||||||
import beforeOpen from '~/main/utils/beforeOpen'
|
|
||||||
import updateChecker from '~/main/utils/updateChecker'
|
|
||||||
import ipcList from '~/main/apis/eventCenter/ipcList'
|
|
||||||
import busEventList from '~/main/apis/eventCenter/busEventList'
|
|
||||||
import fixPath from 'fix-path'
|
|
||||||
import { getUploadFiles } from '~/main/utils/handleArgv'
|
|
||||||
import bus from '~/main/utils/eventBus'
|
|
||||||
import {
|
|
||||||
updateShortKeyFromVersion212
|
|
||||||
} from '~/main/migrate'
|
|
||||||
import shortKeyHandler from '~/main/apis/shortKey/shortKeyHandler'
|
|
||||||
import server from '~/main/server/index'
|
|
||||||
import {
|
|
||||||
createTray
|
|
||||||
} from '~/main/apis/app'
|
|
||||||
|
|
||||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
bootstrap.launchApp()
|
||||||
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
|
|
||||||
|
|
||||||
beforeOpen()
|
|
||||||
|
|
||||||
// fix the $PATH in macOS
|
|
||||||
fixPath()
|
|
||||||
|
|
||||||
ipcList.listen()
|
|
||||||
busEventList.listen()
|
|
||||||
|
|
||||||
const gotTheLock = app.requestSingleInstanceLock()
|
|
||||||
|
|
||||||
if (!gotTheLock) {
|
|
||||||
app.quit()
|
|
||||||
} else {
|
|
||||||
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
|
||||||
let files = getUploadFiles(commandLine, workingDirectory)
|
|
||||||
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
|
|
||||||
if (files === null) {
|
|
||||||
uploadClipboardFiles()
|
|
||||||
} else {
|
|
||||||
const win = windowManager.getAvailableWindow()
|
|
||||||
uploadChoosedFiles(win.webContents, files)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
|
||||||
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
|
||||||
if (settingWindow.isMinimized()) {
|
|
||||||
settingWindow.restore()
|
|
||||||
}
|
|
||||||
settingWindow.focus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
app.setAppUserModelId('com.molunerfinn.picgo')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.XDG_CURRENT_DESKTOP && process.env.XDG_CURRENT_DESKTOP.includes('Unity')) {
|
|
||||||
process.env.XDG_CURRENT_DESKTOP = 'Unity'
|
|
||||||
}
|
|
||||||
|
|
||||||
app.on('ready', async () => {
|
|
||||||
createProtocol('picgo')
|
|
||||||
if (isDevelopment && !process.env.IS_TEST) {
|
|
||||||
// Install Vue Devtools
|
|
||||||
try {
|
|
||||||
await installVueDevtools()
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Vue Devtools failed to install:', e.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
windowManager.create(IWindowList.TRAY_WINDOW)
|
|
||||||
windowManager.create(IWindowList.SETTING_WINDOW)
|
|
||||||
if (process.platform === 'darwin' || process.platform === 'win32') {
|
|
||||||
createTray()
|
|
||||||
}
|
|
||||||
db.set('needReload', false)
|
|
||||||
updateChecker()
|
|
||||||
// 不需要阻塞
|
|
||||||
process.nextTick(() => {
|
|
||||||
updateShortKeyFromVersion212(db, db.get('settings.shortKey'))
|
|
||||||
shortKeyHandler.init()
|
|
||||||
})
|
|
||||||
server.startup()
|
|
||||||
if (process.env.NODE_ENV !== 'development') {
|
|
||||||
let files = getUploadFiles()
|
|
||||||
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
|
|
||||||
if (files === null) {
|
|
||||||
uploadClipboardFiles()
|
|
||||||
} else {
|
|
||||||
const win = windowManager.getAvailableWindow()
|
|
||||||
uploadChoosedFiles(win.webContents, files)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.on('window-all-closed', () => {
|
|
||||||
if (process.platform !== 'darwin') {
|
|
||||||
app.quit()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.on('activate', () => {
|
|
||||||
createProtocol('picgo')
|
|
||||||
if (!windowManager.has(IWindowList.TRAY_WINDOW)) {
|
|
||||||
windowManager.create(IWindowList.TRAY_WINDOW)
|
|
||||||
}
|
|
||||||
if (!windowManager.has(IWindowList.SETTING_WINDOW)) {
|
|
||||||
windowManager.create(IWindowList.SETTING_WINDOW)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.on('will-quit', () => {
|
|
||||||
globalShortcut.unregisterAll()
|
|
||||||
bus.removeAllListeners()
|
|
||||||
server.shutdown()
|
|
||||||
})
|
|
||||||
|
|
||||||
app.setLoginItemSettings({
|
|
||||||
openAtLogin: db.get('settings.autoStart') || false
|
|
||||||
})
|
|
||||||
|
|
||||||
// Exit cleanly on request from parent process in development mode.
|
|
||||||
if (isDevelopment) {
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
process.on('message', data => {
|
|
||||||
if (data === 'graceful-exit') {
|
|
||||||
app.quit()
|
|
||||||
server.shutdown()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
process.on('SIGTERM', () => {
|
|
||||||
app.quit()
|
|
||||||
server.shutdown()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Auto Updater
|
* Auto Updater
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'element-ui/lib/theme-chalk/index.css'
|
|||||||
import VueLazyLoad from 'vue-lazyload'
|
import VueLazyLoad from 'vue-lazyload'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import mainMixin from './renderer/utils/mainMixin'
|
import mainMixin from './renderer/utils/mainMixin'
|
||||||
|
import bus from '@/utils/bus'
|
||||||
|
|
||||||
webFrame.setVisualZoomLevelLimits(1, 1)
|
webFrame.setVisualZoomLevelLimits(1, 1)
|
||||||
webFrame.setLayoutZoomLevelLimits(0, 0)
|
webFrame.setLayoutZoomLevelLimits(0, 0)
|
||||||
@@ -25,6 +26,7 @@ Vue.prototype.$builtInPicBed = [
|
|||||||
]
|
]
|
||||||
Vue.prototype.$db = db
|
Vue.prototype.$db = db
|
||||||
Vue.prototype.$http = axios
|
Vue.prototype.$http = axios
|
||||||
|
Vue.prototype.$bus = bus
|
||||||
|
|
||||||
Vue.use(ElementUI)
|
Vue.use(ElementUI)
|
||||||
Vue.use(VueLazyLoad)
|
Vue.use(VueLazyLoad)
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# apis folder
|
||||||
|
|
||||||
|
## core
|
||||||
|
|
||||||
|
The lowest level APIs that are not dependent on each other. The upper APIs depend on them.
|
||||||
|
|
||||||
|
## app
|
||||||
|
|
||||||
|
Provide key API interfaces for PicGo application, including uploader, window management, shortcut key system, etc
|
||||||
|
|
||||||
|
## gui
|
||||||
|
|
||||||
|
GuiApi for PicGo plugins.
|
||||||
+20
-15
@@ -1,21 +1,18 @@
|
|||||||
import bus from '../../utils/eventBus'
|
import bus from '@core/bus'
|
||||||
import PicGoCore from '~/universal/types/picgo'
|
|
||||||
import path from 'path'
|
|
||||||
import {
|
import {
|
||||||
app,
|
globalShortcut
|
||||||
globalShortcut,
|
|
||||||
BrowserWindow
|
|
||||||
} from 'electron'
|
} from 'electron'
|
||||||
import logger from '../../utils/logger'
|
import logger from '@core/picgo/logger'
|
||||||
import GuiApi from '../gui'
|
import GuiApi from '../../gui'
|
||||||
import db from '#/datastore'
|
import db from '#/datastore'
|
||||||
|
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
|
||||||
import shortKeyService from './shortKeyService'
|
import shortKeyService from './shortKeyService'
|
||||||
import picgo from '../picgo'
|
import picgo from '@core/picgo'
|
||||||
|
|
||||||
class ShortKeyHandler {
|
class ShortKeyHandler {
|
||||||
private isInModifiedMode: boolean = false
|
private isInModifiedMode: boolean = false
|
||||||
constructor () {
|
constructor () {
|
||||||
bus.on('toggleShortKeyModifiedMode', flag => {
|
bus.on(TOGGLE_SHORTKEY_MODIFIED_MODE, flag => {
|
||||||
this.isInModifiedMode = flag
|
this.isInModifiedMode = flag
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -27,11 +24,14 @@ class ShortKeyHandler {
|
|||||||
const commands = db.get('settings.shortKey') as IShortKeyConfigs
|
const commands = db.get('settings.shortKey') as IShortKeyConfigs
|
||||||
Object.keys(commands)
|
Object.keys(commands)
|
||||||
.filter(item => item.includes('picgo:'))
|
.filter(item => item.includes('picgo:'))
|
||||||
.map(command => {
|
.forEach(command => {
|
||||||
const config = commands[command]
|
const config = commands[command]
|
||||||
globalShortcut.register(config.key, () => {
|
// if disabled, don't register #534
|
||||||
this.handler(command)
|
if (config.enable) {
|
||||||
})
|
globalShortcut.register(config.key, () => {
|
||||||
|
this.handler(command)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
private initPluginsShortKey () {
|
private initPluginsShortKey () {
|
||||||
@@ -49,7 +49,10 @@ class ShortKeyHandler {
|
|||||||
const command = `${item}:${cmd.name}`
|
const command = `${item}:${cmd.name}`
|
||||||
if (db.has(`settings.shortKey[${command}]`)) {
|
if (db.has(`settings.shortKey[${command}]`)) {
|
||||||
const commandConfig = db.get(`settings.shortKey.${command}`) as IShortKeyConfig
|
const commandConfig = db.get(`settings.shortKey.${command}`) as IShortKeyConfig
|
||||||
this.registerShortKey(commandConfig, command, cmd.handle, false)
|
// if disabled, don't register #534
|
||||||
|
if (commandConfig.enable) {
|
||||||
|
this.registerShortKey(commandConfig, command, cmd.handle, false)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.registerShortKey(cmd, command, cmd.handle, true)
|
this.registerShortKey(cmd, command, cmd.handle, true)
|
||||||
}
|
}
|
||||||
@@ -68,6 +71,8 @@ class ShortKeyHandler {
|
|||||||
} else {
|
} else {
|
||||||
logger.warn(`${command} do not provide a key to bind`)
|
logger.warn(`${command} do not provide a key to bind`)
|
||||||
}
|
}
|
||||||
|
// if the configfile already had this command
|
||||||
|
// then writeFlag -> false
|
||||||
if (writeFlag) {
|
if (writeFlag) {
|
||||||
picgo.saveConfig({
|
picgo.saveConfig({
|
||||||
[`settings.shortKey.${command}`]: {
|
[`settings.shortKey.${command}`]: {
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import logger from '../../utils/logger'
|
import logger from '@core/picgo/logger'
|
||||||
class ShortKeyService {
|
class ShortKeyService {
|
||||||
private commandList: Map<string, IShortKeyHandler> = new Map()
|
private commandList: Map<string, IShortKeyHandler> = new Map()
|
||||||
registerCommand (command: string, handler: IShortKeyHandler) {
|
registerCommand (command: string, handler: IShortKeyHandler) {
|
||||||
@@ -7,14 +7,15 @@ import {
|
|||||||
systemPreferences,
|
systemPreferences,
|
||||||
Notification
|
Notification
|
||||||
} from 'electron'
|
} from 'electron'
|
||||||
import uploader from '~/main/apis/uploader'
|
import uploader from 'apis/app/uploader'
|
||||||
import getPicBeds from '~/main/utils/getPicBeds'
|
import getPicBeds from '~/main/utils/getPicBeds'
|
||||||
import db from '#/datastore'
|
import db from '#/datastore'
|
||||||
import windowManager from '~/main/apis/window/windowManager'
|
import windowManager from 'apis/app/window/windowManager'
|
||||||
import { IWindowList } from '~/main/apis/window/constants'
|
import { IWindowList } from 'apis/app/window/constants'
|
||||||
import picgo from '~/main/apis/picgo'
|
import picgo from '@core/picgo'
|
||||||
import pasteTemplate from '#/utils/pasteTemplate'
|
import pasteTemplate from '#/utils/pasteTemplate'
|
||||||
import pkg from 'root/package.json'
|
import pkg from 'root/package.json'
|
||||||
|
import { handleCopyUrl } from '~/main/utils/common'
|
||||||
let contextMenu: Menu | null
|
let contextMenu: Menu | null
|
||||||
let menu: Menu | null
|
let menu: Menu | null
|
||||||
let tray: Tray | null
|
let tray: Tray | null
|
||||||
@@ -27,7 +28,8 @@ export function createContextMenu () {
|
|||||||
checked: db.get('picBed.current') === item.type,
|
checked: db.get('picBed.current') === item.type,
|
||||||
click () {
|
click () {
|
||||||
picgo.saveConfig({
|
picgo.saveConfig({
|
||||||
'picBed.current': item.type
|
'picBed.current': item.type,
|
||||||
|
'picBed.uploader': item.type
|
||||||
})
|
})
|
||||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
|
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('syncPicBed')
|
||||||
@@ -148,8 +150,9 @@ export function createTray () {
|
|||||||
.setWebContents(trayWindow.webContents)
|
.setWebContents(trayWindow.webContents)
|
||||||
.upload(files)
|
.upload(files)
|
||||||
if (imgs !== false) {
|
if (imgs !== false) {
|
||||||
|
const pasteText: string[] = []
|
||||||
for (let i = 0; i < imgs.length; i++) {
|
for (let i = 0; i < imgs.length; i++) {
|
||||||
clipboard.writeText(pasteTemplate(pasteStyle, imgs[i]))
|
pasteText.push(pasteTemplate(pasteStyle, imgs[i]))
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title: '上传成功',
|
title: '上传成功',
|
||||||
body: imgs[i].imgUrl!,
|
body: imgs[i].imgUrl!,
|
||||||
@@ -160,6 +163,7 @@ export function createTray () {
|
|||||||
}, i * 100)
|
}, i * 100)
|
||||||
db.insert('uploaded', imgs[i])
|
db.insert('uploaded', imgs[i])
|
||||||
}
|
}
|
||||||
|
handleCopyUrl(pasteText.join('\n'))
|
||||||
trayWindow.webContents.send('dragFiles', imgs)
|
trayWindow.webContents.send('dragFiles', imgs)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
clipboard,
|
|
||||||
Notification,
|
Notification,
|
||||||
WebContents
|
WebContents
|
||||||
} from 'electron'
|
} from 'electron'
|
||||||
import windowManager from '~/main/apis/window/windowManager'
|
import windowManager from 'apis/app/window/windowManager'
|
||||||
import { IWindowList } from '~/main/apis/window/constants'
|
import { IWindowList } from 'apis/app/window/constants'
|
||||||
import uploader from './'
|
import uploader from '.'
|
||||||
import pasteTemplate from '#/utils/pasteTemplate'
|
import pasteTemplate from '#/utils/pasteTemplate'
|
||||||
import db from '#/datastore'
|
import db from '#/datastore'
|
||||||
|
import { handleCopyUrl } from '~/main/utils/common'
|
||||||
|
import { handleUrlEncode } from '#/utils/common'
|
||||||
export const uploadClipboardFiles = async (): Promise<string> => {
|
export const uploadClipboardFiles = async (): Promise<string> => {
|
||||||
const win = windowManager.getAvailableWindow()
|
const win = windowManager.getAvailableWindow()
|
||||||
let img = await uploader.setWebContents(win!.webContents).upload()
|
let img = await uploader.setWebContents(win!.webContents).upload()
|
||||||
@@ -15,7 +16,7 @@ export const uploadClipboardFiles = async (): Promise<string> => {
|
|||||||
if (img.length > 0) {
|
if (img.length > 0) {
|
||||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)!
|
||||||
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
||||||
clipboard.writeText(pasteTemplate(pasteStyle, img[0]))
|
handleCopyUrl(pasteTemplate(pasteStyle, img[0]))
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title: '上传成功',
|
title: '上传成功',
|
||||||
body: img[0].imgUrl!,
|
body: img[0].imgUrl!,
|
||||||
@@ -28,7 +29,7 @@ export const uploadClipboardFiles = async (): Promise<string> => {
|
|||||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
|
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
|
||||||
}
|
}
|
||||||
return img[0].imgUrl as string
|
return handleUrlEncode(img[0].imgUrl as string)
|
||||||
} else {
|
} else {
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title: '上传不成功',
|
title: '上传不成功',
|
||||||
@@ -48,9 +49,9 @@ export const uploadChoosedFiles = async (webContents: WebContents, files: IFileW
|
|||||||
const result = []
|
const result = []
|
||||||
if (imgs !== false) {
|
if (imgs !== false) {
|
||||||
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
||||||
let pasteText = ''
|
const pasteText: string[] = []
|
||||||
for (let i = 0; i < imgs.length; i++) {
|
for (let i = 0; i < imgs.length; i++) {
|
||||||
pasteText += pasteTemplate(pasteStyle, imgs[i]) + '\r\n'
|
pasteText.push(pasteTemplate(pasteStyle, imgs[i]))
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title: '上传成功',
|
title: '上传成功',
|
||||||
body: imgs[i].imgUrl!,
|
body: imgs[i].imgUrl!,
|
||||||
@@ -60,9 +61,9 @@ export const uploadChoosedFiles = async (webContents: WebContents, files: IFileW
|
|||||||
notification.show()
|
notification.show()
|
||||||
}, i * 100)
|
}, i * 100)
|
||||||
db.insert('uploaded', imgs[i])
|
db.insert('uploaded', imgs[i])
|
||||||
result.push(imgs[i].imgUrl!)
|
result.push(handleUrlEncode(imgs[i].imgUrl!))
|
||||||
}
|
}
|
||||||
clipboard.writeText(pasteText)
|
handleCopyUrl(pasteText.join('\n'))
|
||||||
windowManager.get(IWindowList.TRAY_WINDOW)!.webContents.send('uploadFiles', imgs)
|
windowManager.get(IWindowList.TRAY_WINDOW)!.webContents.send('uploadFiles', imgs)
|
||||||
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||||
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
|
windowManager.get(IWindowList.SETTING_WINDOW)!.webContents.send('updateGallery')
|
||||||
@@ -6,10 +6,10 @@ import {
|
|||||||
WebContents
|
WebContents
|
||||||
} from 'electron'
|
} from 'electron'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import picgo from '~/main/apis/picgo'
|
import picgo from '@core/picgo'
|
||||||
import db from '#/datastore'
|
import db from '#/datastore'
|
||||||
import windowManager from '~/main/apis/window/windowManager'
|
import windowManager from 'apis/app/window/windowManager'
|
||||||
import { IWindowList } from '~/main/apis/window/constants'
|
import { IWindowList } from 'apis/app/window/constants'
|
||||||
|
|
||||||
const waitForShow = (webcontent: WebContents) => {
|
const waitForShow = (webcontent: WebContents) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -62,22 +62,24 @@ class Uploader {
|
|||||||
handle: async ctx => {
|
handle: async ctx => {
|
||||||
const rename = db.get('settings.rename')
|
const rename = db.get('settings.rename')
|
||||||
const autoRename = db.get('settings.autoRename')
|
const autoRename = db.get('settings.autoRename')
|
||||||
await Promise.all(ctx.output.map(async (item, index) => {
|
if (autoRename || rename) {
|
||||||
let name: undefined | string | null
|
await Promise.all(ctx.output.map(async (item, index) => {
|
||||||
let fileName: string | undefined
|
let name: undefined | string | null
|
||||||
if (autoRename) {
|
let fileName: string | undefined
|
||||||
fileName = dayjs().add(index, 'second').format('YYYYMMDDHHmmss') + item.extname
|
if (autoRename) {
|
||||||
} else {
|
fileName = dayjs().add(index, 'second').format('YYYYMMDDHHmmss') + item.extname
|
||||||
fileName = item.fileName
|
} else {
|
||||||
}
|
fileName = item.fileName
|
||||||
if (rename) {
|
}
|
||||||
const window = windowManager.create(IWindowList.RENAME_WINDOW)!
|
if (rename) {
|
||||||
await waitForShow(window.webContents)
|
const window = windowManager.create(IWindowList.RENAME_WINDOW)!
|
||||||
window.webContents.send('rename', fileName, window.webContents.id)
|
await waitForShow(window.webContents)
|
||||||
name = await waitForRename(window, window.webContents.id)
|
window.webContents.send('rename', fileName, window.webContents.id)
|
||||||
}
|
name = await waitForRename(window, window.webContents.id)
|
||||||
item.fileName = name || fileName
|
}
|
||||||
}))
|
item.fileName = name || fileName
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -12,8 +12,8 @@ export const TRAY_WINDOW_URL = isDevelopment
|
|||||||
: `picgo://./index.html`
|
: `picgo://./index.html`
|
||||||
|
|
||||||
export const SETTING_WINDOW_URL = isDevelopment
|
export const SETTING_WINDOW_URL = isDevelopment
|
||||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#setting/upload`
|
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#main-page/upload`
|
||||||
: `picgo://./index.html#setting/upload`
|
: `picgo://./index.html#main-page/upload`
|
||||||
|
|
||||||
export const MINI_WINDOW_URL = isDevelopment
|
export const MINI_WINDOW_URL = isDevelopment
|
||||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#mini-page`
|
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#mini-page`
|
||||||
@@ -22,6 +22,3 @@ export const MINI_WINDOW_URL = isDevelopment
|
|||||||
export const RENAME_WINDOW_URL = process.env.NODE_ENV === 'development'
|
export const RENAME_WINDOW_URL = process.env.NODE_ENV === 'development'
|
||||||
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#rename-page`
|
? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#rename-page`
|
||||||
: `picgo://./index.html#rename-page`
|
: `picgo://./index.html#rename-page`
|
||||||
|
|
||||||
export const DELETE_WINDOW_EVENT = 'DELETE_WINDOW_EVENT'
|
|
||||||
export const CREATE_WINDOW_EVENT = 'CREATE_WINDOW_EVENT'
|
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
import {
|
import {
|
||||||
IWindowList,
|
IWindowList,
|
||||||
DELETE_WINDOW_EVENT,
|
|
||||||
CREATE_WINDOW_EVENT,
|
|
||||||
SETTING_WINDOW_URL,
|
SETTING_WINDOW_URL,
|
||||||
TRAY_WINDOW_URL,
|
TRAY_WINDOW_URL,
|
||||||
MINI_WINDOW_URL,
|
MINI_WINDOW_URL,
|
||||||
RENAME_WINDOW_URL
|
RENAME_WINDOW_URL
|
||||||
} from './constants'
|
} from './constants'
|
||||||
import { IWindowListItem } from '#/types/electron'
|
import { IWindowListItem } from '#/types/electron'
|
||||||
import bus from '~/main/utils/eventBus'
|
import bus from '@core/bus'
|
||||||
|
import { CREATE_APP_MENU } from '@core/bus/constants'
|
||||||
import db from '#/datastore'
|
import db from '#/datastore'
|
||||||
import { getWindowId } from '~/main/apis/bus'
|
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
|
||||||
import { BrowserWindow, app } from 'electron'
|
import { app } from 'electron'
|
||||||
import { CREATE_APP_MENU } from '~/main/apis/bus/constants'
|
|
||||||
|
|
||||||
const windowList = new Map<IWindowList, IWindowListItem>()
|
const windowList = new Map<IWindowList, IWindowListItem>()
|
||||||
|
|
||||||
@@ -37,14 +35,9 @@ windowList.set(IWindowList.TRAY_WINDOW, {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
callback (window) {
|
callback (window) {
|
||||||
const id = window!.id
|
window.loadURL(TRAY_WINDOW_URL)
|
||||||
window!.loadURL(TRAY_WINDOW_URL)
|
window.on('blur', () => {
|
||||||
window!.on('closed', () => {
|
window.hide()
|
||||||
bus.emit(DELETE_WINDOW_EVENT, id)
|
|
||||||
})
|
|
||||||
|
|
||||||
window!.on('blur', () => {
|
|
||||||
window!.hide()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -81,12 +74,10 @@ windowList.set(IWindowList.SETTING_WINDOW, {
|
|||||||
}
|
}
|
||||||
return options
|
return options
|
||||||
},
|
},
|
||||||
callback (window) {
|
callback (window, windowManager) {
|
||||||
const id = window!.id
|
window.loadURL(SETTING_WINDOW_URL)
|
||||||
window!.loadURL(SETTING_WINDOW_URL)
|
window.on('closed', () => {
|
||||||
window!.on('closed', () => {
|
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, false)
|
||||||
bus.emit('toggleShortKeyModifiedMode', false)
|
|
||||||
bus.emit(DELETE_WINDOW_EVENT, id)
|
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
app.quit()
|
app.quit()
|
||||||
@@ -94,7 +85,7 @@ windowList.set(IWindowList.SETTING_WINDOW, {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
bus.emit(CREATE_APP_MENU)
|
bus.emit(CREATE_APP_MENU)
|
||||||
bus.emit(CREATE_WINDOW_EVENT, IWindowList.MINI_WINDOW)
|
windowManager.create(IWindowList.MINI_WINDOW)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -125,11 +116,7 @@ windowList.set(IWindowList.MINI_WINDOW, {
|
|||||||
return obj
|
return obj
|
||||||
},
|
},
|
||||||
callback (window) {
|
callback (window) {
|
||||||
const id = window!.id
|
window.loadURL(MINI_WINDOW_URL)
|
||||||
window!.loadURL(MINI_WINDOW_URL)
|
|
||||||
window!.on('closed', () => {
|
|
||||||
bus.emit(DELETE_WINDOW_EVENT, id)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -158,10 +145,9 @@ windowList.set(IWindowList.RENAME_WINDOW, {
|
|||||||
}
|
}
|
||||||
return options
|
return options
|
||||||
},
|
},
|
||||||
async callback (window) {
|
async callback (window, windowManager) {
|
||||||
window!.loadURL(RENAME_WINDOW_URL)
|
window.loadURL(RENAME_WINDOW_URL)
|
||||||
const currentWindowId = await getWindowId()
|
const currentWindow = windowManager.getAvailableWindow()
|
||||||
const currentWindow = BrowserWindow.fromId(currentWindowId)
|
|
||||||
if (currentWindow && currentWindow.isVisible()) {
|
if (currentWindow && currentWindow.isVisible()) {
|
||||||
// bounds: { x: 821, y: 75, width: 800, height: 450 }
|
// bounds: { x: 821, y: 75, width: 800, height: 450 }
|
||||||
const bounds = currentWindow.getBounds()
|
const bounds = currentWindow.getBounds()
|
||||||
@@ -173,7 +159,7 @@ windowList.set(IWindowList.RENAME_WINDOW, {
|
|||||||
} else { // if is the miniWindow
|
} else { // if is the miniWindow
|
||||||
positionY = bounds.y + bounds.height / 2
|
positionY = bounds.y + bounds.height / 2
|
||||||
}
|
}
|
||||||
window!.setPosition(positionX, positionY, false)
|
window.setPosition(positionX, positionY, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -4,26 +4,20 @@ import {
|
|||||||
import { IWindowManager, IWindowListItem } from '#/types/electron'
|
import { IWindowManager, IWindowListItem } from '#/types/electron'
|
||||||
import windowList from './windowList'
|
import windowList from './windowList'
|
||||||
import {
|
import {
|
||||||
IWindowList,
|
IWindowList
|
||||||
DELETE_WINDOW_EVENT,
|
|
||||||
CREATE_WINDOW_EVENT
|
|
||||||
} from './constants'
|
} from './constants'
|
||||||
import bus from '~/main/utils/eventBus'
|
|
||||||
|
|
||||||
class WindowManager implements IWindowManager {
|
class WindowManager implements IWindowManager {
|
||||||
private windowMap: Map<IWindowList | string, BrowserWindow> = new Map()
|
private windowMap: Map<IWindowList | string, BrowserWindow> = new Map()
|
||||||
private windowIdMap: Map<number, IWindowList | string> = new Map()
|
private windowIdMap: Map<number, IWindowList | string> = new Map()
|
||||||
constructor () {
|
create (name: IWindowList) {
|
||||||
bus.on(DELETE_WINDOW_EVENT, this.deleteById)
|
|
||||||
bus.on(CREATE_WINDOW_EVENT, this.create)
|
|
||||||
}
|
|
||||||
create = (name: IWindowList) => {
|
|
||||||
const windowConfig: IWindowListItem = windowList.get(name)!
|
const windowConfig: IWindowListItem = windowList.get(name)!
|
||||||
if (windowConfig.isValid) {
|
if (windowConfig.isValid) {
|
||||||
if (!windowConfig.multiple) {
|
if (!windowConfig.multiple) {
|
||||||
if (this.windowMap.has(name)) return this.windowMap.get(name)!
|
if (this.has(name)) return this.windowMap.get(name)!
|
||||||
}
|
}
|
||||||
const window = new BrowserWindow(windowConfig.options())
|
const window = new BrowserWindow(windowConfig.options())
|
||||||
|
const id = window.id
|
||||||
if (windowConfig.multiple) {
|
if (windowConfig.multiple) {
|
||||||
this.windowMap.set(`${name}_${window.id}`, window)
|
this.windowMap.set(`${name}_${window.id}`, window)
|
||||||
this.windowIdMap.set(window.id, `${name}_${window.id}`)
|
this.windowIdMap.set(window.id, `${name}_${window.id}`)
|
||||||
@@ -31,14 +25,17 @@ class WindowManager implements IWindowManager {
|
|||||||
this.windowMap.set(name, window)
|
this.windowMap.set(name, window)
|
||||||
this.windowIdMap.set(window.id, name)
|
this.windowIdMap.set(window.id, name)
|
||||||
}
|
}
|
||||||
windowConfig.callback(window)
|
windowConfig.callback(window, this)
|
||||||
|
window.on('close', () => {
|
||||||
|
this.deleteById(id)
|
||||||
|
})
|
||||||
return window
|
return window
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get (name: IWindowList) {
|
get (name: IWindowList) {
|
||||||
if (this.windowMap.has(name)) {
|
if (this.has(name)) {
|
||||||
return this.windowMap.get(name)!
|
return this.windowMap.get(name)!
|
||||||
} else {
|
} else {
|
||||||
const window = this.create(name)
|
const window = this.create(name)
|
||||||
@@ -48,13 +45,14 @@ class WindowManager implements IWindowManager {
|
|||||||
has (name: IWindowList) {
|
has (name: IWindowList) {
|
||||||
return this.windowMap.has(name)
|
return this.windowMap.has(name)
|
||||||
}
|
}
|
||||||
delete = (name: IWindowList) => {
|
// useless
|
||||||
const window = this.windowMap.get(name)
|
// delete (name: IWindowList) {
|
||||||
if (window) {
|
// const window = this.windowMap.get(name)
|
||||||
this.windowIdMap.delete(window.id)
|
// if (window) {
|
||||||
this.windowMap.delete(name)
|
// this.windowIdMap.delete(window.id)
|
||||||
}
|
// this.windowMap.delete(name)
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
deleteById = (id: number) => {
|
deleteById = (id: number) => {
|
||||||
const name = this.windowIdMap.get(id)
|
const name = this.windowIdMap.get(id)
|
||||||
if (name) {
|
if (name) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import bus from '../../utils/eventBus'
|
import bus from '.'
|
||||||
import {
|
import {
|
||||||
UPLOAD_WITH_FILES,
|
UPLOAD_WITH_FILES,
|
||||||
UPLOAD_WITH_FILES_RESPONSE,
|
UPLOAD_WITH_FILES_RESPONSE,
|
||||||
@@ -16,4 +16,4 @@ picgo.saveConfig({
|
|||||||
PICGO_ENV: 'GUI'
|
PICGO_ENV: 'GUI'
|
||||||
})
|
})
|
||||||
|
|
||||||
export default picgo as PicGoCore
|
export default picgo! as PicGoCore
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import picgo from '.'
|
||||||
|
|
||||||
|
const logger = picgo.log
|
||||||
|
|
||||||
|
export default logger
|
||||||
@@ -1,18 +1,22 @@
|
|||||||
import {
|
import {
|
||||||
dialog,
|
dialog,
|
||||||
BrowserWindow,
|
BrowserWindow,
|
||||||
clipboard,
|
|
||||||
Notification,
|
Notification,
|
||||||
ipcMain
|
ipcMain
|
||||||
} from 'electron'
|
} from 'electron'
|
||||||
import db from '#/datastore'
|
import db from '#/datastore'
|
||||||
import uploader from '../uploader'
|
import uploader from 'apis/app/uploader'
|
||||||
import pasteTemplate from '#/utils/pasteTemplate'
|
import pasteTemplate from '#/utils/pasteTemplate'
|
||||||
|
import { handleCopyUrl } from '~/main/utils/common'
|
||||||
import {
|
import {
|
||||||
getWindowId,
|
getWindowId,
|
||||||
getSettingWindowId
|
getSettingWindowId
|
||||||
} from '~/main/apis/bus'
|
} from '@core/bus/apis'
|
||||||
|
import {
|
||||||
|
SHOW_INPUT_BOX
|
||||||
|
} from '~/universal/events/constants'
|
||||||
|
|
||||||
|
// Cross-process support may be required in the future
|
||||||
class GuiApi implements IGuiApi {
|
class GuiApi implements IGuiApi {
|
||||||
private windowId: number = -1
|
private windowId: number = -1
|
||||||
private settingWindowId: number = -1
|
private settingWindowId: number = -1
|
||||||
@@ -40,9 +44,9 @@ class GuiApi implements IGuiApi {
|
|||||||
}) {
|
}) {
|
||||||
await this.showSettingWindow()
|
await this.showSettingWindow()
|
||||||
this.getWebcontentsByWindowId(this.settingWindowId)
|
this.getWebcontentsByWindowId(this.settingWindowId)
|
||||||
.send('showInputBox', options)
|
.send(SHOW_INPUT_BOX, options)
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
ipcMain.once('showInputBox', (event: Event, value: string) => {
|
ipcMain.once(SHOW_INPUT_BOX, (event: Event, value: string) => {
|
||||||
resolve(value)
|
resolve(value)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -63,9 +67,9 @@ class GuiApi implements IGuiApi {
|
|||||||
const imgs = await uploader.setWebContents(webContents).upload(input)
|
const imgs = await uploader.setWebContents(webContents).upload(input)
|
||||||
if (imgs !== false) {
|
if (imgs !== false) {
|
||||||
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
||||||
let pasteText = ''
|
const pasteText: string[] = []
|
||||||
for (let i = 0; i < imgs.length; i++) {
|
for (let i = 0; i < imgs.length; i++) {
|
||||||
pasteText += pasteTemplate(pasteStyle, imgs[i]) + '\r\n'
|
pasteText.push(pasteTemplate(pasteStyle, imgs[i]))
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title: '上传成功',
|
title: '上传成功',
|
||||||
body: imgs[i].imgUrl as string,
|
body: imgs[i].imgUrl as string,
|
||||||
@@ -76,7 +80,7 @@ class GuiApi implements IGuiApi {
|
|||||||
}, i * 100)
|
}, i * 100)
|
||||||
db.insert('uploaded', imgs[i])
|
db.insert('uploaded', imgs[i])
|
||||||
}
|
}
|
||||||
clipboard.writeText(pasteText)
|
handleCopyUrl(pasteText.join('\n'))
|
||||||
webContents.send('uploadFiles', imgs)
|
webContents.send('uploadFiles', imgs)
|
||||||
webContents.send('updateGallery')
|
webContents.send('updateGallery')
|
||||||
return imgs
|
return imgs
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import bus from '~/main/utils/eventBus'
|
import bus from '@core/bus'
|
||||||
import {
|
import {
|
||||||
uploadClipboardFiles,
|
uploadClipboardFiles,
|
||||||
uploadChoosedFiles
|
uploadChoosedFiles
|
||||||
} from '~/main/apis/uploader/api'
|
} from 'apis/app/uploader/apis'
|
||||||
import {
|
import {
|
||||||
createMenu
|
createMenu
|
||||||
} from '~/main/apis/app'
|
} from 'apis/app/system'
|
||||||
import { IWindowList } from '~/main/apis/window/constants'
|
import { IWindowList } from 'apis/app/window/constants'
|
||||||
import windowManager from '~/main/apis/window/windowManager'
|
import windowManager from 'apis/app/window/windowManager'
|
||||||
import {
|
import {
|
||||||
UPLOAD_WITH_FILES,
|
UPLOAD_WITH_FILES,
|
||||||
UPLOAD_WITH_FILES_RESPONSE,
|
UPLOAD_WITH_FILES_RESPONSE,
|
||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
GET_SETTING_WINDOW_ID,
|
GET_SETTING_WINDOW_ID,
|
||||||
GET_SETTING_WINDOW_ID_RESPONSE,
|
GET_SETTING_WINDOW_ID_RESPONSE,
|
||||||
CREATE_APP_MENU
|
CREATE_APP_MENU
|
||||||
} from '~/main/apis/bus/constants'
|
} from '@core/bus/constants'
|
||||||
function initEventCenter () {
|
function initEventCenter () {
|
||||||
const eventList: any = {
|
const eventList: any = {
|
||||||
'picgo:upload': uploadClipboardFiles,
|
'picgo:upload': uploadClipboardFiles,
|
||||||
@@ -1,24 +1,25 @@
|
|||||||
import {
|
import {
|
||||||
app,
|
app,
|
||||||
ipcMain,
|
ipcMain,
|
||||||
clipboard,
|
|
||||||
Notification,
|
Notification,
|
||||||
IpcMainEvent
|
IpcMainEvent
|
||||||
} from 'electron'
|
} from 'electron'
|
||||||
import windowManager from '~/main/apis/window/windowManager'
|
import windowManager from 'apis/app/window/windowManager'
|
||||||
import { IWindowList } from '~/main/apis/window/constants'
|
import { IWindowList } from 'apis/app/window/constants'
|
||||||
import uploader from '~/main/apis/uploader'
|
import uploader from 'apis/app/uploader'
|
||||||
import pasteTemplate from '#/utils/pasteTemplate'
|
import pasteTemplate from '#/utils/pasteTemplate'
|
||||||
import db from '#/datastore'
|
import db from '#/datastore'
|
||||||
import server from '~/main/server'
|
import server from '~/main/server'
|
||||||
import getPicBeds from '~/main/utils/getPicBeds'
|
import getPicBeds from '~/main/utils/getPicBeds'
|
||||||
import shortKeyHandler from '~/main/apis/shortKey/shortKeyHandler'
|
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||||
import bus from '~/main/utils/eventBus'
|
import bus from '@core/bus'
|
||||||
|
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
|
||||||
import {
|
import {
|
||||||
uploadClipboardFiles,
|
uploadClipboardFiles,
|
||||||
uploadChoosedFiles
|
uploadChoosedFiles
|
||||||
} from '~/main/apis/uploader/api'
|
} from '~/main/apis/app/uploader/apis'
|
||||||
import picgoCoreIPC from './picgoCoreIPC'
|
import picgoCoreIPC from './picgoCoreIPC'
|
||||||
|
import { handleCopyUrl } from '~/main/utils/common'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
listen () {
|
listen () {
|
||||||
@@ -29,7 +30,7 @@ export default {
|
|||||||
const img = await uploader.setWebContents(trayWindow.webContents).upload()
|
const img = await uploader.setWebContents(trayWindow.webContents).upload()
|
||||||
if (img !== false) {
|
if (img !== false) {
|
||||||
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
|
||||||
clipboard.writeText(pasteTemplate(pasteStyle, img[0]))
|
handleCopyUrl(pasteTemplate(pasteStyle, img[0]))
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title: '上传成功',
|
title: '上传成功',
|
||||||
body: img[0].imgUrl!,
|
body: img[0].imgUrl!,
|
||||||
@@ -131,8 +132,8 @@ export default {
|
|||||||
evt.returnValue = picBeds
|
evt.returnValue = picBeds
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('toggleShortKeyModifiedMode', (evt: IpcMainEvent, val: boolean) => {
|
ipcMain.on(TOGGLE_SHORTKEY_MODIFIED_MODE, (evt: IpcMainEvent, val: boolean) => {
|
||||||
bus.emit('toggleShortKeyModifiedMode', val)
|
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, val)
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('updateServer', () => {
|
ipcMain.on('updateServer', () => {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import GuiApi from '../gui'
|
import GuiApi from 'apis/gui'
|
||||||
import {
|
import {
|
||||||
dialog,
|
dialog,
|
||||||
shell,
|
shell,
|
||||||
@@ -9,8 +9,8 @@ import {
|
|||||||
} from 'electron'
|
} from 'electron'
|
||||||
import PicGoCore from '~/universal/types/picgo'
|
import PicGoCore from '~/universal/types/picgo'
|
||||||
import { IPicGoHelperType } from '#/types/enum'
|
import { IPicGoHelperType } from '#/types/enum'
|
||||||
import shortKeyHandler from '../shortKey/shortKeyHandler'
|
import shortKeyHandler from '../apis/app/shortKey/shortKeyHandler'
|
||||||
import picgo from '~/main/apis/picgo'
|
import picgo from '@core/picgo'
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
|
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
|
||||||
@@ -111,14 +111,16 @@ const handleGetPluginList = () => {
|
|||||||
|
|
||||||
const handlePluginInstall = () => {
|
const handlePluginInstall = () => {
|
||||||
ipcMain.on('installPlugin', async (event: IpcMainEvent, msg: string) => {
|
ipcMain.on('installPlugin', async (event: IpcMainEvent, msg: string) => {
|
||||||
|
const dispose = handleNPMError()
|
||||||
picgo.once('installSuccess', (notice: PicGoNotice) => {
|
picgo.once('installSuccess', (notice: PicGoNotice) => {
|
||||||
event.sender.send('installSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
event.sender.send('installSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
||||||
shortKeyHandler.registerPluginShortKey(notice.body[0])
|
shortKeyHandler.registerPluginShortKey(notice.body[0])
|
||||||
picgo.removeAllListeners('installFailed')
|
picgo.removeAllListeners('installFailed')
|
||||||
|
dispose()
|
||||||
})
|
})
|
||||||
picgo.once('installFailed', () => {
|
picgo.once('installFailed', () => {
|
||||||
handleNPMError()
|
|
||||||
picgo.removeAllListeners('installSuccess')
|
picgo.removeAllListeners('installSuccess')
|
||||||
|
dispose()
|
||||||
})
|
})
|
||||||
await picgo.pluginHandler.install([msg])
|
await picgo.pluginHandler.install([msg])
|
||||||
picgo.cmd.program.removeAllListeners()
|
picgo.cmd.program.removeAllListeners()
|
||||||
@@ -127,14 +129,16 @@ const handlePluginInstall = () => {
|
|||||||
|
|
||||||
const handlePluginUninstall = () => {
|
const handlePluginUninstall = () => {
|
||||||
ipcMain.on('uninstallPlugin', async (event: IpcMainEvent, msg: string) => {
|
ipcMain.on('uninstallPlugin', async (event: IpcMainEvent, msg: string) => {
|
||||||
|
const dispose = handleNPMError()
|
||||||
picgo.once('uninstallSuccess', (notice: PicGoNotice) => {
|
picgo.once('uninstallSuccess', (notice: PicGoNotice) => {
|
||||||
event.sender.send('uninstallSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
event.sender.send('uninstallSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
||||||
shortKeyHandler.unregisterPluginShortKey(notice.body[0])
|
shortKeyHandler.unregisterPluginShortKey(notice.body[0])
|
||||||
picgo.removeAllListeners('uninstallFailed')
|
picgo.removeAllListeners('uninstallFailed')
|
||||||
|
dispose()
|
||||||
})
|
})
|
||||||
picgo.once('uninstallFailed', () => {
|
picgo.once('uninstallFailed', () => {
|
||||||
handleNPMError()
|
|
||||||
picgo.removeAllListeners('uninstallSuccess')
|
picgo.removeAllListeners('uninstallSuccess')
|
||||||
|
dispose()
|
||||||
})
|
})
|
||||||
await picgo.pluginHandler.uninstall([msg])
|
await picgo.pluginHandler.uninstall([msg])
|
||||||
picgo.cmd.program.removeAllListeners()
|
picgo.cmd.program.removeAllListeners()
|
||||||
@@ -143,36 +147,44 @@ const handlePluginUninstall = () => {
|
|||||||
|
|
||||||
const handlePluginUpdate = () => {
|
const handlePluginUpdate = () => {
|
||||||
ipcMain.on('updatePlugin', async (event: IpcMainEvent, msg: string) => {
|
ipcMain.on('updatePlugin', async (event: IpcMainEvent, msg: string) => {
|
||||||
|
const dispose = handleNPMError()
|
||||||
picgo.once('updateSuccess', (notice: { body: string[], title: string }) => {
|
picgo.once('updateSuccess', (notice: { body: string[], title: string }) => {
|
||||||
event.sender.send('updateSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
event.sender.send('updateSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
|
||||||
picgo.removeAllListeners('updateFailed')
|
picgo.removeAllListeners('updateFailed')
|
||||||
|
dispose()
|
||||||
})
|
})
|
||||||
picgo.once('updateFailed', () => {
|
picgo.once('updateFailed', () => {
|
||||||
handleNPMError()
|
|
||||||
picgo.removeAllListeners('updateSuccess')
|
picgo.removeAllListeners('updateSuccess')
|
||||||
|
dispose()
|
||||||
})
|
})
|
||||||
await picgo.pluginHandler.update([msg])
|
await picgo.pluginHandler.update([msg])
|
||||||
picgo.cmd.program.removeAllListeners()
|
picgo.cmd.program.removeAllListeners()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleNPMError = () => {
|
const handleNPMError = (): IDispose => {
|
||||||
dialog.showMessageBox({
|
const handler = (msg: string) => {
|
||||||
title: '发生错误',
|
if (msg === 'NPM is not installed') {
|
||||||
message: '请安装Node.js并重启PicGo再继续操作',
|
dialog.showMessageBox({
|
||||||
buttons: ['Yes']
|
title: '发生错误',
|
||||||
}).then((res) => {
|
message: '请安装Node.js并重启PicGo再继续操作',
|
||||||
if (res.response === 0) {
|
buttons: ['Yes']
|
||||||
shell.openExternal('https://nodejs.org/')
|
}).then((res) => {
|
||||||
|
if (res.response === 0) {
|
||||||
|
shell.openExternal('https://nodejs.org/')
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
picgo.once('failed', handler)
|
||||||
|
return () => picgo.off('failed', handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleGetPicBedConfig = () => {
|
const handleGetPicBedConfig = () => {
|
||||||
ipcMain.on('getPicBedConfig', (event: IpcMainEvent, type: string) => {
|
ipcMain.on('getPicBedConfig', (event: IpcMainEvent, type: string) => {
|
||||||
const name = picgo.helper.uploader.get(type).name || type
|
const name = picgo.helper.uploader.get(type)?.name || type
|
||||||
if (picgo.helper.uploader.get(type).config) {
|
if (picgo.helper.uploader.get(type)?.config) {
|
||||||
const config = handleConfigWithFunction(picgo.helper.uploader.get(type).config(picgo))
|
const config = handleConfigWithFunction(picgo.helper.uploader.get(type)!.config(picgo))
|
||||||
event.sender.send('getPicBedConfig', config, name)
|
event.sender.send('getPicBedConfig', config, name)
|
||||||
} else {
|
} else {
|
||||||
event.sender.send('getPicBedConfig', [], name)
|
event.sender.send('getPicBedConfig', [], name)
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
import {
|
||||||
|
app,
|
||||||
|
globalShortcut,
|
||||||
|
protocol
|
||||||
|
} from 'electron'
|
||||||
|
import {
|
||||||
|
createProtocol,
|
||||||
|
installVueDevtools
|
||||||
|
} from 'vue-cli-plugin-electron-builder/lib'
|
||||||
|
import beforeOpen from '~/main/utils/beforeOpen'
|
||||||
|
import fixPath from 'fix-path'
|
||||||
|
import ipcList from '~/main/events/ipcList'
|
||||||
|
import busEventList from '~/main/events/busEventList'
|
||||||
|
import { IWindowList } from 'apis/app/window/constants'
|
||||||
|
import windowManager from 'apis/app/window/windowManager'
|
||||||
|
import {
|
||||||
|
updateShortKeyFromVersion212
|
||||||
|
} from '~/main/migrate'
|
||||||
|
import {
|
||||||
|
uploadChoosedFiles,
|
||||||
|
uploadClipboardFiles
|
||||||
|
} from 'apis/app/uploader/apis'
|
||||||
|
import {
|
||||||
|
createTray
|
||||||
|
} from 'apis/app/system'
|
||||||
|
import server from '~/main/server/index'
|
||||||
|
import updateChecker from '~/main/utils/updateChecker'
|
||||||
|
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
|
||||||
|
import { getUploadFiles } from '~/main/utils/handleArgv'
|
||||||
|
import db from '#/datastore'
|
||||||
|
import bus from '@core/bus'
|
||||||
|
|
||||||
|
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||||
|
class LifeCycle {
|
||||||
|
private beforeReady () {
|
||||||
|
protocol.registerSchemesAsPrivileged([{ scheme: 'picgo', privileges: { secure: true, standard: true } }])
|
||||||
|
// fix the $PATH in macOS
|
||||||
|
fixPath()
|
||||||
|
beforeOpen()
|
||||||
|
ipcList.listen()
|
||||||
|
busEventList.listen()
|
||||||
|
updateShortKeyFromVersion212(db, db.get('settings.shortKey'))
|
||||||
|
}
|
||||||
|
private onReady () {
|
||||||
|
app.on('ready', async () => {
|
||||||
|
createProtocol('picgo')
|
||||||
|
if (isDevelopment && !process.env.IS_TEST) {
|
||||||
|
// Install Vue Devtools
|
||||||
|
try {
|
||||||
|
await installVueDevtools()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Vue Devtools failed to install:', e.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
windowManager.create(IWindowList.TRAY_WINDOW)
|
||||||
|
windowManager.create(IWindowList.SETTING_WINDOW)
|
||||||
|
if (process.platform === 'darwin' || process.platform === 'win32') {
|
||||||
|
createTray()
|
||||||
|
}
|
||||||
|
db.set('needReload', false)
|
||||||
|
updateChecker()
|
||||||
|
// 不需要阻塞
|
||||||
|
process.nextTick(() => {
|
||||||
|
shortKeyHandler.init()
|
||||||
|
})
|
||||||
|
server.startup()
|
||||||
|
if (process.env.NODE_ENV !== 'development') {
|
||||||
|
let files = getUploadFiles()
|
||||||
|
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
|
||||||
|
if (files === null) {
|
||||||
|
uploadClipboardFiles()
|
||||||
|
} else {
|
||||||
|
const win = windowManager.getAvailableWindow()
|
||||||
|
uploadChoosedFiles(win.webContents, files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
private onRunning () {
|
||||||
|
app.on('second-instance', (event, commandLine, workingDirectory) => {
|
||||||
|
let files = getUploadFiles(commandLine, workingDirectory)
|
||||||
|
if (files === null || files.length > 0) { // 如果有文件列表作为参数,说明是命令行启动
|
||||||
|
if (files === null) {
|
||||||
|
uploadClipboardFiles()
|
||||||
|
} else {
|
||||||
|
const win = windowManager.getAvailableWindow()
|
||||||
|
uploadChoosedFiles(win.webContents, files)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||||
|
const settingWindow = windowManager.get(IWindowList.SETTING_WINDOW)!
|
||||||
|
if (settingWindow.isMinimized()) {
|
||||||
|
settingWindow.restore()
|
||||||
|
}
|
||||||
|
settingWindow.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
app.on('activate', () => {
|
||||||
|
createProtocol('picgo')
|
||||||
|
if (!windowManager.has(IWindowList.TRAY_WINDOW)) {
|
||||||
|
windowManager.create(IWindowList.TRAY_WINDOW)
|
||||||
|
}
|
||||||
|
if (!windowManager.has(IWindowList.SETTING_WINDOW)) {
|
||||||
|
windowManager.create(IWindowList.SETTING_WINDOW)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
app.setLoginItemSettings({
|
||||||
|
openAtLogin: db.get('settings.autoStart') || false
|
||||||
|
})
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
app.setAppUserModelId('com.molunerfinn.picgo')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.XDG_CURRENT_DESKTOP && process.env.XDG_CURRENT_DESKTOP.includes('Unity')) {
|
||||||
|
process.env.XDG_CURRENT_DESKTOP = 'Unity'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private onQuit () {
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
app.quit()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.on('will-quit', () => {
|
||||||
|
globalShortcut.unregisterAll()
|
||||||
|
bus.removeAllListeners()
|
||||||
|
server.shutdown()
|
||||||
|
})
|
||||||
|
// Exit cleanly on request from parent process in development mode.
|
||||||
|
if (isDevelopment) {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
process.on('message', data => {
|
||||||
|
if (data === 'graceful-exit') {
|
||||||
|
app.quit()
|
||||||
|
server.shutdown()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
app.quit()
|
||||||
|
server.shutdown()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launchApp () {
|
||||||
|
const gotTheLock = app.requestSingleInstanceLock()
|
||||||
|
if (!gotTheLock) {
|
||||||
|
app.quit()
|
||||||
|
} else {
|
||||||
|
this.beforeReady()
|
||||||
|
this.onReady()
|
||||||
|
this.onRunning()
|
||||||
|
this.onQuit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bootstrap = new LifeCycle()
|
||||||
|
|
||||||
|
export {
|
||||||
|
bootstrap
|
||||||
|
}
|
||||||
+26
-14
@@ -1,16 +1,18 @@
|
|||||||
import http from 'http'
|
import http from 'http'
|
||||||
import routers from './routerManager'
|
import routers from './routerManager'
|
||||||
import {
|
import {
|
||||||
handleResponse
|
handleResponse,
|
||||||
|
ensureHTTPLink
|
||||||
} from './utils'
|
} from './utils'
|
||||||
import picgo from '~/main/apis/picgo'
|
import picgo from '@core/picgo'
|
||||||
import logger from '~/main/utils/logger'
|
import logger from '@core/picgo/logger'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
private httpServer: http.Server
|
private httpServer: http.Server
|
||||||
private config: IServerConfig
|
private config: IServerConfig
|
||||||
constructor () {
|
constructor () {
|
||||||
let config = picgo.getConfig('settings.server')
|
let config = picgo.getConfig<IServerConfig>('settings.server')
|
||||||
const result = this.checkIfConfigIsValid(config)
|
const result = this.checkIfConfigIsValid(config)
|
||||||
if (result) {
|
if (result) {
|
||||||
this.config = config
|
this.config = config
|
||||||
@@ -75,16 +77,24 @@ class Server {
|
|||||||
response.end()
|
response.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private listen = (port: number) => {
|
// port as string is a bug
|
||||||
|
private listen = (port: number | string) => {
|
||||||
logger.info(`[PicGo Server] is listening at ${port}`)
|
logger.info(`[PicGo Server] is listening at ${port}`)
|
||||||
this.httpServer.listen(port, this.config.host).on('error', (err: ErrnoException) => {
|
if (typeof port === 'string') {
|
||||||
|
port = parseInt(port, 10)
|
||||||
|
}
|
||||||
|
this.httpServer.listen(port, this.config.host).on('error', async (err: ErrnoException) => {
|
||||||
if (err.errno === 'EADDRINUSE') {
|
if (err.errno === 'EADDRINUSE') {
|
||||||
logger.warn(`[PicGo Server] ${port} is busy, trying with port ${port + 1}`)
|
try {
|
||||||
this.config.port += 1
|
// make sure the system has a PicGo Server instance
|
||||||
picgo.saveConfig({
|
await axios.post(ensureHTTPLink(`${this.config.host}:${port}/heartbeat`))
|
||||||
'settings.server': this.config
|
this.shutdown(true)
|
||||||
})
|
} catch (e) {
|
||||||
this.listen(this.config.port)
|
logger.warn(`[PicGo Server] ${port} is busy, trying with port ${(port as number) + 1}`)
|
||||||
|
// fix a bug: not write an increase number to config file
|
||||||
|
// to solve the auto number problem
|
||||||
|
this.listen((port as number) + 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -93,9 +103,11 @@ class Server {
|
|||||||
this.listen(this.config.port)
|
this.listen(this.config.port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shutdown () {
|
shutdown (hasStarted?: boolean) {
|
||||||
this.httpServer.close()
|
this.httpServer.close()
|
||||||
logger.info('[PicGo Server] shutdown')
|
if (!hasStarted) {
|
||||||
|
logger.info('[PicGo Server] shutdown')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
restart () {
|
restart () {
|
||||||
this.config = picgo.getConfig('settings.server')
|
this.config = picgo.getConfig('settings.server')
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import router from './router'
|
|||||||
import {
|
import {
|
||||||
uploadWithClipboardFiles,
|
uploadWithClipboardFiles,
|
||||||
uploadWithFiles
|
uploadWithFiles
|
||||||
} from '~/main/apis/bus/index'
|
} from '@core/bus/apis'
|
||||||
import {
|
import {
|
||||||
handleResponse
|
handleResponse
|
||||||
} from './utils'
|
} from './utils'
|
||||||
import logger from '../utils/logger'
|
import logger from '@core/picgo/logger'
|
||||||
|
|
||||||
router.get('/upload', async ({
|
router.post('/upload', async ({
|
||||||
response,
|
response,
|
||||||
list = []
|
list = []
|
||||||
} : {
|
} : {
|
||||||
@@ -66,4 +66,18 @@ router.get('/upload', async ({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
router.post('/heartbeat', async ({
|
||||||
|
response
|
||||||
|
} : {
|
||||||
|
response: IHttpResponse,
|
||||||
|
}) => {
|
||||||
|
handleResponse({
|
||||||
|
response,
|
||||||
|
body: {
|
||||||
|
success: true,
|
||||||
|
result: 'alive'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|||||||
@@ -17,3 +17,9 @@ export const handleResponse = ({
|
|||||||
response.write(JSON.stringify(body))
|
response.write(JSON.stringify(body))
|
||||||
response.end()
|
response.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const ensureHTTPLink = (url: string): string => {
|
||||||
|
return url.startsWith('http')
|
||||||
|
? url
|
||||||
|
: `http://${url}`
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import db from '#/datastore'
|
||||||
|
import { clipboard } from 'electron'
|
||||||
|
|
||||||
|
export const handleCopyUrl = (str: string): void => {
|
||||||
|
if (db.get('settings.autoCopy') !== false) {
|
||||||
|
clipboard.writeText(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import picgo from '../apis/picgo'
|
import picgo from '@core/picgo'
|
||||||
|
|
||||||
const getPicBeds = () => {
|
const getPicBeds = () => {
|
||||||
const picBedTypes = picgo.helper.uploader.getIdList()
|
const picBedTypes = picgo.helper.uploader.getIdList()
|
||||||
const picBedFromDB = picgo.getConfig('picBed.list') || []
|
const picBedFromDB = picgo.getConfig<IPicBedType[]>('picBed.list') || []
|
||||||
const picBeds = picBedTypes.map((item: string) => {
|
const picBeds = picBedTypes.map((item: string) => {
|
||||||
const visible = picBedFromDB.find((i: IPicBedType) => i.type === item) // object or undefined
|
const visible = picBedFromDB.find((i: IPicBedType) => i.type === item) // object or undefined
|
||||||
return {
|
return {
|
||||||
type: item,
|
type: item,
|
||||||
name: picgo.helper.uploader.get(item).name || item,
|
name: picgo.helper.uploader.get(item)!.name || item,
|
||||||
visible: visible ? visible.visible : true
|
visible: visible ? visible.visible : true
|
||||||
}
|
}
|
||||||
}) as IPicBedType[]
|
}) as IPicBedType[]
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import chalk from 'chalk'
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
import fs from 'fs-extra'
|
|
||||||
import path from 'path'
|
|
||||||
import util from 'util'
|
|
||||||
import db from '#/datastore'
|
|
||||||
import { app } from 'electron'
|
|
||||||
import { IChalkType } from '#/types/enum'
|
|
||||||
const baseDir = app.getPath('userData')
|
|
||||||
|
|
||||||
class Logger {
|
|
||||||
private level = {
|
|
||||||
success: IChalkType.success,
|
|
||||||
info: IChalkType.info,
|
|
||||||
warn: IChalkType.warn,
|
|
||||||
error: IChalkType.error
|
|
||||||
}
|
|
||||||
protected handleLog (type: ILogType, msg: ILoggerType): ILoggerType {
|
|
||||||
// if configPath is invalid then this.ctx.config === undefined
|
|
||||||
// if not then check config.silent
|
|
||||||
const log = chalk[this.level[type]](`[PicGo ${type.toUpperCase()}]:`)
|
|
||||||
console.log(log, msg)
|
|
||||||
process.nextTick(() => {
|
|
||||||
this.handleWriteLog(type, msg)
|
|
||||||
})
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
protected handleWriteLog (type: string, msg: ILoggerType): void {
|
|
||||||
try {
|
|
||||||
const logLevel = db.get('settings.logLevel')
|
|
||||||
const logPath = db.get('settings.logPath') || path.join(baseDir, './picgo.log')
|
|
||||||
if (this.checkLogLevel(type, logLevel)) {
|
|
||||||
const picgoLog = fs.createWriteStream(logPath, { flags: 'a', encoding: 'utf8' })
|
|
||||||
let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicGo ${type.toUpperCase()}] ${msg}`
|
|
||||||
const logger = new console.Console(picgoLog)
|
|
||||||
if (typeof msg === 'object' && type === 'error') {
|
|
||||||
log += `\n------Error Stack Begin------\n${util.format(msg.stack)}\n-------Error Stack End-------`
|
|
||||||
}
|
|
||||||
logger.log(log)
|
|
||||||
picgoLog.destroy()
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected checkLogLevel (type: string, level: undefined | string | string[]): boolean {
|
|
||||||
if (level === undefined || level === 'all') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (Array.isArray(level)) {
|
|
||||||
return level.some((item: string) => (item === type || item === 'all'))
|
|
||||||
} else {
|
|
||||||
return type === level
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
success (msg: ILoggerType): ILoggerType {
|
|
||||||
return this.handleLog('success', msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
info (msg: ILoggerType): ILoggerType {
|
|
||||||
return this.handleLog('info', msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
error (msg: ILoggerType): ILoggerType {
|
|
||||||
return this.handleLog('error', msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
warn (msg: ILoggerType): ILoggerType {
|
|
||||||
return this.handleLog('warn', msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default new Logger()
|
|
||||||
@@ -2,6 +2,7 @@ import { dialog, shell } from 'electron'
|
|||||||
import db from '#/datastore'
|
import db from '#/datastore'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import pkg from 'root/package.json'
|
import pkg from 'root/package.json'
|
||||||
|
import { lt } from 'semver'
|
||||||
const version = pkg.version
|
const version = pkg.version
|
||||||
const releaseUrl = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
|
const releaseUrl = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
|
||||||
const releaseUrlBackup = 'https://cdn.jsdelivr.net/gh/Molunerfinn/PicGo@latest/package.json'
|
const releaseUrlBackup = 'https://cdn.jsdelivr.net/gh/Molunerfinn/PicGo@latest/package.json'
|
||||||
@@ -51,18 +52,17 @@ const checkVersion = async () => {
|
|||||||
|
|
||||||
// if true -> update else return false
|
// if true -> update else return false
|
||||||
const compareVersion2Update = (current: string, latest: string) => {
|
const compareVersion2Update = (current: string, latest: string) => {
|
||||||
const currentVersion = current.split('.').map(item => parseInt(item))
|
try {
|
||||||
const latestVersion = latest.split('.').map(item => parseInt(item))
|
if (latest.includes('beta')) {
|
||||||
|
const isCheckBetaUpdate = db.get('settings.checkBetaUpdate') !== false
|
||||||
for (let i = 0; i < 3; i++) {
|
if (!isCheckBetaUpdate) {
|
||||||
if (currentVersion[i] < latestVersion[i]) {
|
return false
|
||||||
return true
|
}
|
||||||
}
|
|
||||||
if (currentVersion[i] > latestVersion[i]) {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
return lt(current, latest)
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default checkVersion
|
export default checkVersion
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
choosePicBed (val) {
|
choosePicBed (val) {
|
||||||
this.letPicGoSaveData({
|
this.letPicGoSaveData({
|
||||||
'picBed.current': this.type
|
'picBed.current': this.type,
|
||||||
|
'picBed.uploader': this.type
|
||||||
})
|
})
|
||||||
this.$emit('update:choosed', this.type)
|
this.$emit('update:choosed', this.type)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="inputBoxOptions.title || '输入框'"
|
||||||
|
:visible.sync="showInputBoxVisible"
|
||||||
|
:modal-append-to-body="false"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="inputBoxValue"
|
||||||
|
:placeholder="inputBoxOptions.placeholder"></el-input>
|
||||||
|
<span slot="footer">
|
||||||
|
<el-button @click="handleInputBoxCancel" round>取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleInputBoxConfirm" round>确定</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue } from 'vue-property-decorator'
|
||||||
|
import { remote, ipcRenderer, IpcRendererEvent } from 'electron'
|
||||||
|
import {
|
||||||
|
SHOW_INPUT_BOX,
|
||||||
|
SHOW_INPUT_BOX_RESPONSE
|
||||||
|
} from '~/universal/events/constants'
|
||||||
|
@Component({
|
||||||
|
name: 'input-box-dialog'
|
||||||
|
})
|
||||||
|
export default class extends Vue {
|
||||||
|
inputBoxValue = ''
|
||||||
|
showInputBoxVisible = false
|
||||||
|
inputBoxOptions = {
|
||||||
|
title: '',
|
||||||
|
placeholder: ''
|
||||||
|
}
|
||||||
|
created () {
|
||||||
|
ipcRenderer.on(SHOW_INPUT_BOX, this.ipcEventHandler)
|
||||||
|
this.$bus.$on(SHOW_INPUT_BOX, this.initInputBoxValue)
|
||||||
|
}
|
||||||
|
ipcEventHandler (evt: IpcRendererEvent, options: IShowInputBoxOption) {
|
||||||
|
this.initInputBoxValue(options)
|
||||||
|
}
|
||||||
|
initInputBoxValue (options: IShowInputBoxOption) {
|
||||||
|
this.inputBoxValue = options.value || ''
|
||||||
|
this.inputBoxOptions.title = options.title || ''
|
||||||
|
this.inputBoxOptions.placeholder = options.placeholder || ''
|
||||||
|
this.showInputBoxVisible = true
|
||||||
|
}
|
||||||
|
handleInputBoxCancel () {
|
||||||
|
// TODO: RPCServer
|
||||||
|
this.showInputBoxVisible = false
|
||||||
|
ipcRenderer.send(SHOW_INPUT_BOX, '')
|
||||||
|
this.$bus.$emit(SHOW_INPUT_BOX_RESPONSE, '')
|
||||||
|
}
|
||||||
|
handleInputBoxConfirm () {
|
||||||
|
this.showInputBoxVisible = false
|
||||||
|
ipcRenderer.send(SHOW_INPUT_BOX, this.inputBoxValue)
|
||||||
|
this.$bus.$emit(SHOW_INPUT_BOX_RESPONSE, this.inputBoxValue)
|
||||||
|
}
|
||||||
|
beforeDestroy () {
|
||||||
|
ipcRenderer.removeListener(SHOW_INPUT_BOX, this.ipcEventHandler)
|
||||||
|
this.$bus.$off(SHOW_INPUT_BOX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang='stylus'>
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="setting-page">
|
<div id="main-page">
|
||||||
<div class="fake-title-bar" :class="{ 'darwin': os === 'darwin' }">
|
<div class="fake-title-bar" :class="{ 'darwin': os === 'darwin' }">
|
||||||
<div class="fake-title-bar__title">
|
<div class="fake-title-bar__title">
|
||||||
PicGo - {{ version }}
|
PicGo - {{ version }}
|
||||||
@@ -121,28 +121,71 @@
|
|||||||
</span>
|
</span>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
:title="inputBoxOptions.title || '输入框'"
|
class="qrcode-dialog"
|
||||||
:visible.sync="showInputBoxVisible"
|
top="3vh"
|
||||||
|
width="60%"
|
||||||
|
title="图床配置二维码"
|
||||||
|
:visible.sync="qrcodeVisible"
|
||||||
:modal-append-to-body="false"
|
:modal-append-to-body="false"
|
||||||
@close="handleInputBoxClose"
|
lock-scroll
|
||||||
>
|
>
|
||||||
<el-input
|
<el-form
|
||||||
v-model="inputBoxValue"
|
label-position="left"
|
||||||
:placeholder="inputBoxOptions.placeholder"></el-input>
|
label-width="70px"
|
||||||
<span slot="footer">
|
size="mini"
|
||||||
<el-button @click="showInputBoxVisible = false" round>取消</el-button>
|
>
|
||||||
<el-button type="primary" @click="showInputBoxVisible = false" round>确定</el-button>
|
<el-form-item
|
||||||
</span>
|
label="选择图床"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="choosedPicBedForQRCode"
|
||||||
|
multiple
|
||||||
|
collapse-tags
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in picBed"
|
||||||
|
:key="item.type"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.type"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
<el-button
|
||||||
|
v-show="choosedPicBedForQRCode.length > 0"
|
||||||
|
type="primary"
|
||||||
|
round
|
||||||
|
class="copy-picbed-config"
|
||||||
|
@click="handleCopyPicBedConfig"
|
||||||
|
>
|
||||||
|
复制图床配置
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div class="qrcode-container">
|
||||||
|
<qrcode-vue
|
||||||
|
v-show="choosedPicBedForQRCode.length > 0"
|
||||||
|
:size="280"
|
||||||
|
:value="picBedConfigString"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<input-box-dialog />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from 'vue-property-decorator'
|
import { Component, Vue, Watch } from 'vue-property-decorator'
|
||||||
|
import QrcodeVue from 'qrcode.vue'
|
||||||
|
import pick from 'lodash/pick'
|
||||||
import pkg from 'root/package.json'
|
import pkg from 'root/package.json'
|
||||||
import keyDetect from '@/utils/key-binding'
|
import keyDetect from '@/utils/key-binding'
|
||||||
import { remote, ipcRenderer, IpcRendererEvent } from 'electron'
|
import {
|
||||||
|
remote,
|
||||||
|
ipcRenderer,
|
||||||
|
IpcRendererEvent,
|
||||||
|
clipboard
|
||||||
|
} from 'electron'
|
||||||
import db from '#/datastore'
|
import db from '#/datastore'
|
||||||
import mixin from '@/utils/mixin'
|
import mixin from '@/utils/mixin'
|
||||||
|
import InputBoxDialog from '@/components/InputBoxDialog.vue'
|
||||||
const { Menu, dialog, BrowserWindow } = remote
|
const { Menu, dialog, BrowserWindow } = remote
|
||||||
const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) => void) => {
|
const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) => void) => {
|
||||||
if (!/\$url/.test(value)) {
|
if (!/\$url/.test(value)) {
|
||||||
@@ -152,8 +195,12 @@ const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) =>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Component({
|
@Component({
|
||||||
name: 'setting-page',
|
name: 'main-page',
|
||||||
mixins: [mixin]
|
mixins: [mixin],
|
||||||
|
components: {
|
||||||
|
InputBoxDialog,
|
||||||
|
QrcodeVue
|
||||||
|
}
|
||||||
})
|
})
|
||||||
export default class extends Vue {
|
export default class extends Vue {
|
||||||
version = process.env.NODE_ENV === 'production' ? pkg.version : 'Dev'
|
version = process.env.NODE_ENV === 'production' ? pkg.version : 'Dev'
|
||||||
@@ -175,25 +222,27 @@ export default class extends Vue {
|
|||||||
upload: db.get('shortKey.upload')
|
upload: db.get('shortKey.upload')
|
||||||
}
|
}
|
||||||
picBed: IPicBedType[] = []
|
picBed: IPicBedType[] = []
|
||||||
// for showInputBox
|
qrcodeVisible = false
|
||||||
showInputBoxVisible = false
|
picBedConfigString = ''
|
||||||
inputBoxValue = ''
|
choosedPicBedForQRCode: string[] = []
|
||||||
inputBoxOptions = {
|
|
||||||
title: '',
|
|
||||||
placeholder: ''
|
|
||||||
}
|
|
||||||
created () {
|
created () {
|
||||||
this.os = process.platform
|
this.os = process.platform
|
||||||
this.buildMenu()
|
this.buildMenu()
|
||||||
ipcRenderer.send('getPicBeds')
|
ipcRenderer.send('getPicBeds')
|
||||||
ipcRenderer.on('getPicBeds', this.getPicBeds)
|
ipcRenderer.on('getPicBeds', this.getPicBeds)
|
||||||
ipcRenderer.on('showInputBox', (evt: IpcRendererEvent, options: IShowInputBoxOption) => {
|
|
||||||
this.inputBoxValue = ''
|
|
||||||
this.inputBoxOptions.title = options.title || ''
|
|
||||||
this.inputBoxOptions.placeholder = options.placeholder || ''
|
|
||||||
this.showInputBoxVisible = true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Watch('choosedPicBedForQRCode')
|
||||||
|
choosedPicBedForQRCodeChange (val: string[], oldVal: string[]) {
|
||||||
|
if (val.length > 0) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const picBedConfig = db.get('picBed')
|
||||||
|
const config = pick(picBedConfig, ...this.choosedPicBedForQRCode)
|
||||||
|
this.picBedConfigString = JSON.stringify(config)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleSelect (index: string) {
|
handleSelect (index: string) {
|
||||||
const type = index.match(/picbeds-/)
|
const type = index.match(/picbeds-/)
|
||||||
if (type === null) {
|
if (type === null) {
|
||||||
@@ -242,6 +291,12 @@ export default class extends Vue {
|
|||||||
click () {
|
click () {
|
||||||
_this.visible = true
|
_this.visible = true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '生成图床配置二维码',
|
||||||
|
click () {
|
||||||
|
_this.qrcodeVisible = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
this.menu = Menu.buildFromTemplate(template)
|
this.menu = Menu.buildFromTemplate(template)
|
||||||
@@ -276,12 +331,13 @@ export default class extends Vue {
|
|||||||
openMiniWindow () {
|
openMiniWindow () {
|
||||||
ipcRenderer.send('openMiniWindow')
|
ipcRenderer.send('openMiniWindow')
|
||||||
}
|
}
|
||||||
|
handleCopyPicBedConfig () {
|
||||||
|
clipboard.writeText(this.picBedConfigString)
|
||||||
|
this.$message.success('图床配置复制成功')
|
||||||
|
}
|
||||||
getPicBeds (event: IpcRendererEvent, picBeds: IPicBedType[]) {
|
getPicBeds (event: IpcRendererEvent, picBeds: IPicBedType[]) {
|
||||||
this.picBed = picBeds
|
this.picBed = picBeds
|
||||||
}
|
}
|
||||||
handleInputBoxClose () {
|
|
||||||
ipcRenderer.send('showInputBox', this.inputBoxValue)
|
|
||||||
}
|
|
||||||
beforeRouteEnter (to: any, from: any, next: any) {
|
beforeRouteEnter (to: any, from: any, next: any) {
|
||||||
next((vm: this) => {
|
next((vm: this) => {
|
||||||
vm.defaultActive = to.name
|
vm.defaultActive = to.name
|
||||||
@@ -289,7 +345,6 @@ export default class extends Vue {
|
|||||||
}
|
}
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
|
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
|
||||||
ipcRenderer.removeAllListeners('showInputBox')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -308,7 +363,17 @@ $darwinBg = transparentify(#172426, #000, 0.7)
|
|||||||
font-size 20px
|
font-size 20px
|
||||||
text-align center
|
text-align center
|
||||||
margin 10px auto
|
margin 10px auto
|
||||||
#setting-page
|
#main-page
|
||||||
|
.qrcode-dialog
|
||||||
|
.qrcode-container
|
||||||
|
display flex
|
||||||
|
justify-content center
|
||||||
|
.el-dialog__body
|
||||||
|
padding-top 10px
|
||||||
|
.copy-picbed-config
|
||||||
|
margin-left 10px
|
||||||
|
.el-input__inner
|
||||||
|
border-radius 14px
|
||||||
.fake-title-bar
|
.fake-title-bar
|
||||||
-webkit-app-region drag
|
-webkit-app-region drag
|
||||||
height h = 22px
|
height h = 22px
|
||||||
@@ -48,14 +48,19 @@
|
|||||||
<i slot="suffix" class="el-input__icon el-icon-close" v-if="searchText" @click="cleanSearch" style="cursor: pointer"></i>
|
<i slot="suffix" class="el-input__icon el-icon-close" v-if="searchText" @click="cleanSearch" style="cursor: pointer"></i>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="4">
|
||||||
<div class="item-base copy round" :class="{ active: isMultiple(choosedList)}" @click="multiCopy">
|
<div class="item-base copy round" :class="{ active: isMultiple(choosedList)}" @click="multiCopy">
|
||||||
<i class="el-icon-document"></i> 批量复制
|
复制
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="4">
|
||||||
<div class="item-base delete round" :class="{ active: isMultiple(choosedList)}" @click="multiRemove">
|
<div class="item-base delete round" :class="{ active: isMultiple(choosedList)}" @click="multiRemove">
|
||||||
<i class="el-icon-delete"></i> 批量删除
|
删除
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="4">
|
||||||
|
<div class="item-base all-pick round" :class="{ active: filterList.length > 0}" @click="toggleSelectAll">
|
||||||
|
{{ isAllSelected ? '取消' : '全选' }}
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -82,7 +87,7 @@
|
|||||||
<i class="el-icon-document" @click="copy(item)"></i>
|
<i class="el-icon-document" @click="copy(item)"></i>
|
||||||
<i class="el-icon-edit-outline" @click="openDialog(item)"></i>
|
<i class="el-icon-edit-outline" @click="openDialog(item)"></i>
|
||||||
<i class="el-icon-delete" @click="remove(item.id)"></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>
|
<el-checkbox v-model="choosedList[item.id]" class="pull-right" @change="(val) => handleChooseImage(val, index)"></el-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -106,7 +111,7 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import gallerys from 'vue-gallery'
|
import gallerys from 'vue-gallery'
|
||||||
import pasteStyle from '#/utils/pasteTemplate'
|
import pasteStyle from '#/utils/pasteTemplate'
|
||||||
import { Component, Vue } from 'vue-property-decorator'
|
import { Component, Vue, Watch } from 'vue-property-decorator'
|
||||||
import {
|
import {
|
||||||
ipcRenderer,
|
ipcRenderer,
|
||||||
clipboard,
|
clipboard,
|
||||||
@@ -128,11 +133,13 @@ export default class extends Vue {
|
|||||||
}
|
}
|
||||||
dialogVisible = false
|
dialogVisible = false
|
||||||
imgInfo = {
|
imgInfo = {
|
||||||
id: null,
|
id: '',
|
||||||
imgUrl: ''
|
imgUrl: ''
|
||||||
}
|
}
|
||||||
choosedList: IObjT<boolean> = {}
|
choosedList: IObjT<boolean> = {}
|
||||||
choosedPicBed: string[] = []
|
choosedPicBed: string[] = []
|
||||||
|
lastChoosed: number = -1
|
||||||
|
isShiftKeyPress: boolean = false
|
||||||
searchText = ''
|
searchText = ''
|
||||||
handleBarActive = false
|
handleBarActive = false
|
||||||
pasteStyle = ''
|
pasteStyle = ''
|
||||||
@@ -144,12 +151,11 @@ export default class extends Vue {
|
|||||||
Custom: 'Custom'
|
Custom: 'Custom'
|
||||||
}
|
}
|
||||||
picBed: IPicBedType[] = []
|
picBed: IPicBedType[] = []
|
||||||
beforeRouteEnter (to: any, from: any, next: any) {
|
@Watch('$route')
|
||||||
next((vm: any) => {
|
handleRouteUpdate (to: any, from: any) {
|
||||||
vm.getGallery()
|
if (from.name === 'gallery') {
|
||||||
vm.getPasteStyle()
|
this.clearChoosedList()
|
||||||
vm.getPicBeds()
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
created () {
|
created () {
|
||||||
ipcRenderer.on('updateGallery', (event: IpcRendererEvent) => {
|
ipcRenderer.on('updateGallery', (event: IpcRendererEvent) => {
|
||||||
@@ -160,12 +166,31 @@ export default class extends Vue {
|
|||||||
ipcRenderer.send('getPicBeds')
|
ipcRenderer.send('getPicBeds')
|
||||||
ipcRenderer.on('getPicBeds', this.getPicBeds)
|
ipcRenderer.on('getPicBeds', this.getPicBeds)
|
||||||
}
|
}
|
||||||
|
mounted () {
|
||||||
|
document.addEventListener('keydown', this.handleDetectShiftKey)
|
||||||
|
document.addEventListener('keyup', this.handleDetectShiftKey)
|
||||||
|
}
|
||||||
|
handleDetectShiftKey (event: KeyboardEvent) {
|
||||||
|
if (event.keyCode === 16) {
|
||||||
|
this.isShiftKeyPress = !this.isShiftKeyPress
|
||||||
|
}
|
||||||
|
}
|
||||||
get filterList () {
|
get filterList () {
|
||||||
return this.getGallery()
|
return this.getGallery()
|
||||||
}
|
}
|
||||||
set filterList (val) {
|
set filterList (val) {
|
||||||
this.images = val
|
this.images = val
|
||||||
}
|
}
|
||||||
|
get isAllSelected () {
|
||||||
|
const values = Object.values(this.choosedList)
|
||||||
|
if (values.length === 0) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return this.filterList.every(item => {
|
||||||
|
return this.choosedList[item.id!]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
getPicBeds (event: IpcRendererEvent, picBeds: IPicBedType[]) {
|
getPicBeds (event: IpcRendererEvent, picBeds: IPicBedType[]) {
|
||||||
this.picBed = picBeds
|
this.picBed = picBeds
|
||||||
}
|
}
|
||||||
@@ -200,6 +225,31 @@ export default class extends Vue {
|
|||||||
}
|
}
|
||||||
return this.images
|
return this.images
|
||||||
}
|
}
|
||||||
|
@Watch('filterList')
|
||||||
|
handleFilterListChange () {
|
||||||
|
this.clearChoosedList()
|
||||||
|
}
|
||||||
|
handleChooseImage (val: boolean, index: number) {
|
||||||
|
if (val === true) {
|
||||||
|
this.handleBarActive = true
|
||||||
|
if (this.lastChoosed !== -1 && this.isShiftKeyPress) {
|
||||||
|
let min = Math.min(this.lastChoosed, index)
|
||||||
|
let max = Math.max(this.lastChoosed, index)
|
||||||
|
for (let i = min + 1; i < max; i++) {
|
||||||
|
const id = this.filterList[i].id!
|
||||||
|
this.$set(this.choosedList, id, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.lastChoosed = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearChoosedList () {
|
||||||
|
this.isShiftKeyPress = false
|
||||||
|
Object.keys(this.choosedList).forEach(key => {
|
||||||
|
this.choosedList[key] = false
|
||||||
|
})
|
||||||
|
this.lastChoosed = -1
|
||||||
|
}
|
||||||
zoomImage (index: number) {
|
zoomImage (index: number) {
|
||||||
this.idx = index
|
this.idx = index
|
||||||
this.changeZIndexForGallery(true)
|
this.changeZIndexForGallery(true)
|
||||||
@@ -255,7 +305,7 @@ export default class extends Vue {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
openDialog (item: ImgInfo) {
|
openDialog (item: ImgInfo) {
|
||||||
this.imgInfo.id = item.id
|
this.imgInfo.id = item.id!
|
||||||
this.imgInfo.imgUrl = item.imgUrl as string
|
this.imgInfo.imgUrl = item.imgUrl as string
|
||||||
this.dialogVisible = true
|
this.dialogVisible = true
|
||||||
}
|
}
|
||||||
@@ -291,10 +341,17 @@ export default class extends Vue {
|
|||||||
isMultiple (obj: IObj) {
|
isMultiple (obj: IObj) {
|
||||||
return Object.values(obj).some(item => item)
|
return Object.values(obj).some(item => item)
|
||||||
}
|
}
|
||||||
|
toggleSelectAll () {
|
||||||
|
const result = !this.isAllSelected
|
||||||
|
this.filterList.forEach(item => {
|
||||||
|
this.$set(this.choosedList, item.id!, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
multiRemove () {
|
multiRemove () {
|
||||||
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
|
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
|
||||||
if (Object.values(this.choosedList).some(item => item)) {
|
const multiRemoveNumber = Object.values(this.choosedList).filter(item => item).length
|
||||||
this.$confirm('将删除刚才选中的图片,是否继续?', '提示', {
|
if (multiRemoveNumber) {
|
||||||
|
this.$confirm(`将在相册中移除刚才选中的 ${multiRemoveNumber} 张图片,是否继续?`, '提示', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
@@ -307,7 +364,8 @@ export default class extends Vue {
|
|||||||
this.$db.removeById('uploaded', key)
|
this.$db.removeById('uploaded', key)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.choosedList = {}
|
this.clearChoosedList()
|
||||||
|
this.choosedList = {} // 只有删除才能将这个置空
|
||||||
this.getGallery()
|
this.getGallery()
|
||||||
const obj = {
|
const obj = {
|
||||||
title: '操作结果',
|
title: '操作结果',
|
||||||
@@ -325,22 +383,22 @@ export default class extends Vue {
|
|||||||
}
|
}
|
||||||
multiCopy () {
|
multiCopy () {
|
||||||
if (Object.values(this.choosedList).some(item => item)) {
|
if (Object.values(this.choosedList).some(item => item)) {
|
||||||
let copyString = ''
|
const copyString: string[] = []
|
||||||
const style = this.$db.get('settings.pasteStyle') || 'markdown'
|
const style = this.$db.get('settings.pasteStyle') || 'markdown'
|
||||||
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
|
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
|
||||||
Object.keys(this.choosedList).forEach(key => {
|
Object.keys(this.choosedList).forEach(key => {
|
||||||
if (this.choosedList[key]) {
|
if (this.choosedList[key]) {
|
||||||
const item = this.$db.getById('uploaded', key)
|
const item = this.$db.getById('uploaded', key)
|
||||||
copyString += pasteStyle(style, item) + '\n'
|
copyString.push(pasteStyle(style, item))
|
||||||
this.choosedList[key] = false
|
this.choosedList[key] = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const obj = {
|
const obj = {
|
||||||
title: '批量复制链接成功',
|
title: '批量复制链接成功',
|
||||||
body: copyString
|
body: copyString.join('\n')
|
||||||
}
|
}
|
||||||
const myNotification = new Notification(obj.title, obj)
|
const myNotification = new Notification(obj.title, obj)
|
||||||
clipboard.writeText(copyString)
|
clipboard.writeText(copyString.join('\n'))
|
||||||
myNotification.onclick = () => {
|
myNotification.onclick = () => {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -400,6 +458,13 @@ export default class extends Vue {
|
|||||||
cursor pointer
|
cursor pointer
|
||||||
background #F15140
|
background #F15140
|
||||||
color #fff
|
color #fff
|
||||||
|
&.all-pick
|
||||||
|
cursor not-allowed
|
||||||
|
background #69C282
|
||||||
|
&.active
|
||||||
|
cursor pointer
|
||||||
|
background #44B363
|
||||||
|
color #fff
|
||||||
#gallery-view
|
#gallery-view
|
||||||
position relative
|
position relative
|
||||||
.round
|
.round
|
||||||
|
|||||||
@@ -145,7 +145,8 @@ export default class extends Vue {
|
|||||||
checked: this.$db.get('picBed.current') === item.type,
|
checked: this.$db.get('picBed.current') === item.type,
|
||||||
click () {
|
click () {
|
||||||
_this.letPicGoSaveData({
|
_this.letPicGoSaveData({
|
||||||
'picBed.current': item.type
|
'picBed.current': item.type,
|
||||||
|
'picBed.uploader': item.type
|
||||||
})
|
})
|
||||||
ipcRenderer.send('syncPicBed')
|
ipcRenderer.send('syncPicBed')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
PicGo设置 - <i class="el-icon-document" @click="goConfigPage"></i>
|
PicGo设置 - <i class="el-icon-document" @click="goConfigPage"></i>
|
||||||
</div>
|
</div>
|
||||||
<el-row class="setting-list">
|
<el-row class="setting-list">
|
||||||
<el-col :span="15" :offset="4">
|
<el-col :span="16" :offset="4">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-form
|
<el-form
|
||||||
label-width="120px"
|
label-width="160px"
|
||||||
label-position="right"
|
label-position="right"
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
@@ -56,6 +56,17 @@
|
|||||||
@change="updateHelperChange"
|
@change="updateHelperChange"
|
||||||
></el-switch>
|
></el-switch>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-show="form.updateHelper"
|
||||||
|
label="接受Beta版本更新"
|
||||||
|
>
|
||||||
|
<el-switch
|
||||||
|
v-model="form.checkBetaUpdate"
|
||||||
|
active-text="开"
|
||||||
|
inactive-text="关"
|
||||||
|
@change="checkBetaUpdateChange"
|
||||||
|
></el-switch>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
label="开机自启"
|
label="开机自启"
|
||||||
>
|
>
|
||||||
@@ -107,6 +118,16 @@
|
|||||||
@change="handleMiniWindowOntop"
|
@change="handleMiniWindowOntop"
|
||||||
></el-switch>
|
></el-switch>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="上传后自动复制URL"
|
||||||
|
>
|
||||||
|
<el-switch
|
||||||
|
v-model="form.autoCopyUrl"
|
||||||
|
active-text="开"
|
||||||
|
inactive-text="关"
|
||||||
|
@change="handleAutoCopyUrl"
|
||||||
|
></el-switch>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
label="选择显示的图床"
|
label="选择显示的图床"
|
||||||
>
|
>
|
||||||
@@ -336,7 +357,9 @@ export default class extends Vue {
|
|||||||
autoRename: db.get('settings.autoRename') || false,
|
autoRename: db.get('settings.autoRename') || false,
|
||||||
uploadNotification: db.get('settings.uploadNotification') || false,
|
uploadNotification: db.get('settings.uploadNotification') || false,
|
||||||
miniWindowOntop: db.get('settings.miniWindowOntop') || false,
|
miniWindowOntop: db.get('settings.miniWindowOntop') || false,
|
||||||
logLevel
|
logLevel,
|
||||||
|
autoCopyUrl: db.get('settings.autoCopy') === undefined ? true : db.get('settings.autoCopy'),
|
||||||
|
checkBetaUpdate: db.get('settings.checkBetaUpdate') === undefined ? true : db.get('settings.checkBetaUpdate')
|
||||||
}
|
}
|
||||||
picBed: IPicBedType[] = []
|
picBed: IPicBedType[] = []
|
||||||
logFileVisible = false
|
logFileVisible = false
|
||||||
@@ -441,6 +464,9 @@ export default class extends Vue {
|
|||||||
updateHelperChange (val: boolean) {
|
updateHelperChange (val: boolean) {
|
||||||
db.set('settings.showUpdateTip', val)
|
db.set('settings.showUpdateTip', val)
|
||||||
}
|
}
|
||||||
|
checkBetaUpdateChange (val: boolean) {
|
||||||
|
db.set('settings.checkBetaUpdate', val)
|
||||||
|
}
|
||||||
handleShowPicBedListChange (val: string[]) {
|
handleShowPicBedListChange (val: string[]) {
|
||||||
const list = this.picBed.map(item => {
|
const list = this.picBed.map(item => {
|
||||||
if (!val.includes(item.name)) {
|
if (!val.includes(item.name)) {
|
||||||
@@ -513,6 +539,15 @@ export default class extends Vue {
|
|||||||
db.set('settings.miniWindowOntop', val)
|
db.set('settings.miniWindowOntop', val)
|
||||||
this.$message.info('需要重启生效')
|
this.$message.info('需要重启生效')
|
||||||
}
|
}
|
||||||
|
handleAutoCopyUrl (val: boolean) {
|
||||||
|
db.set('settings.autoCopy', val)
|
||||||
|
const successNotification = new Notification('设置自动复制链接', {
|
||||||
|
body: '设置成功'
|
||||||
|
})
|
||||||
|
successNotification.onclick = () => {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
confirmLogLevelSetting () {
|
confirmLogLevelSetting () {
|
||||||
if (this.form.logLevel.length === 0) {
|
if (this.form.logLevel.length === 0) {
|
||||||
return this.$message.error('请选择日志记录等级')
|
return this.$message.error('请选择日志记录等级')
|
||||||
@@ -541,6 +576,7 @@ export default class extends Vue {
|
|||||||
this.form.logLevel = logLevel
|
this.form.logLevel = logLevel
|
||||||
}
|
}
|
||||||
confirmServerSetting () {
|
confirmServerSetting () {
|
||||||
|
this.server.port = parseInt(this.server.port, 10)
|
||||||
this.letPicGoSaveData({
|
this.letPicGoSaveData({
|
||||||
'settings.server': this.server
|
'settings.server': this.server
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -433,7 +433,8 @@ export default class extends Vue {
|
|||||||
const current = this.$db.get('picBed.current')
|
const current = this.$db.get('picBed.current')
|
||||||
if (current === name) {
|
if (current === name) {
|
||||||
this.letPicGoSaveData({
|
this.letPicGoSaveData({
|
||||||
'picBed.current': 'smms'
|
'picBed.current': 'smms',
|
||||||
|
'picBed.uploader': 'smms'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,7 @@
|
|||||||
import { Component, Vue, Watch } from 'vue-property-decorator'
|
import { Component, Vue, Watch } from 'vue-property-decorator'
|
||||||
import keyDetect from '@/utils/key-binding'
|
import keyDetect from '@/utils/key-binding'
|
||||||
import { ipcRenderer, IpcRendererEvent } from 'electron'
|
import { ipcRenderer, IpcRendererEvent } from 'electron'
|
||||||
|
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '#/events/constants'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
name: 'shortkey-page'
|
name: 'shortkey-page'
|
||||||
@@ -119,7 +120,7 @@ export default class extends Vue {
|
|||||||
}
|
}
|
||||||
@Watch('keyBindingVisible')
|
@Watch('keyBindingVisible')
|
||||||
onKeyBindingVisibleChange (val: boolean) {
|
onKeyBindingVisibleChange (val: boolean) {
|
||||||
ipcRenderer.send('toggleShortKeyModifiedMode', val)
|
ipcRenderer.send(TOGGLE_SHORTKEY_MODIFIED_MODE, val)
|
||||||
}
|
}
|
||||||
calcOrigin (item: string) {
|
calcOrigin (item: string) {
|
||||||
const [origin] = item.split(':')
|
const [origin] = item.split(':')
|
||||||
@@ -162,7 +163,7 @@ export default class extends Vue {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
ipcRenderer.send('toggleShortKeyModifiedMode', false)
|
ipcRenderer.send(TOGGLE_SHORTKEY_MODIFIED_MODE, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -48,7 +48,8 @@
|
|||||||
<div class="paste-style__text">
|
<div class="paste-style__text">
|
||||||
快捷上传
|
快捷上传
|
||||||
</div>
|
</div>
|
||||||
<el-button type="primary" round size="mini" @click="uploadClipboardFiles" class="paste-upload">剪贴板图片上传</el-button>
|
<el-button type="primary" round size="mini" @click="uploadClipboardFiles" class="quick-upload" style="width: 50%">剪贴板图片</el-button>
|
||||||
|
<el-button type="primary" round size="mini" @click="uploadURLFiles" class="quick-upload" style="width: 46%; margin-left: 6px">URL</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -62,6 +63,13 @@ import {
|
|||||||
IpcRendererEvent,
|
IpcRendererEvent,
|
||||||
remote
|
remote
|
||||||
} from 'electron'
|
} from 'electron'
|
||||||
|
import {
|
||||||
|
SHOW_INPUT_BOX,
|
||||||
|
SHOW_INPUT_BOX_RESPONSE
|
||||||
|
} from '~/universal/events/constants'
|
||||||
|
import {
|
||||||
|
isUrl
|
||||||
|
} from '~/universal/utils/common'
|
||||||
const { Menu } = remote
|
const { Menu } = remote
|
||||||
@Component({
|
@Component({
|
||||||
name: 'upload'
|
name: 'upload'
|
||||||
@@ -92,6 +100,7 @@ export default class extends Vue {
|
|||||||
})
|
})
|
||||||
ipcRenderer.send('getPicBeds')
|
ipcRenderer.send('getPicBeds')
|
||||||
ipcRenderer.on('getPicBeds', this.getPicBeds)
|
ipcRenderer.on('getPicBeds', this.getPicBeds)
|
||||||
|
this.$bus.$on(SHOW_INPUT_BOX_RESPONSE, this.handleInputBoxValue)
|
||||||
}
|
}
|
||||||
@Watch('progress')
|
@Watch('progress')
|
||||||
onProgressChange (val: number) {
|
onProgressChange (val: number) {
|
||||||
@@ -106,13 +115,41 @@ export default class extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
|
this.$bus.$off(SHOW_INPUT_BOX_RESPONSE)
|
||||||
ipcRenderer.removeAllListeners('uploadProgress')
|
ipcRenderer.removeAllListeners('uploadProgress')
|
||||||
ipcRenderer.removeAllListeners('syncPicBed')
|
ipcRenderer.removeAllListeners('syncPicBed')
|
||||||
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
|
ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
|
||||||
}
|
}
|
||||||
onDrop (e: DragEvent) {
|
onDrop (e: DragEvent) {
|
||||||
this.dragover = false
|
this.dragover = false
|
||||||
this.ipcSendFiles(e.dataTransfer!.files)
|
const items = e.dataTransfer!.items
|
||||||
|
if (items.length === 2 && items[0].type === 'text/uri-list') {
|
||||||
|
this.handleURLDrag(items, e.dataTransfer!)
|
||||||
|
} else if (items[0].type === 'text/plain') {
|
||||||
|
const str = e.dataTransfer!.getData(items[0].type)
|
||||||
|
if (isUrl(str)) {
|
||||||
|
ipcRenderer.send('uploadChoosedFiles', [{ path: str }])
|
||||||
|
} else {
|
||||||
|
this.$message.error('请拖入合法的图片文件或者图片URL地址')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.ipcSendFiles(e.dataTransfer!.files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleURLDrag (items: DataTransferItemList, dataTransfer: DataTransfer) {
|
||||||
|
// text/html
|
||||||
|
// Use this data to get a more precise URL
|
||||||
|
const urlString = dataTransfer.getData(items[1].type)
|
||||||
|
const urlMatch = urlString.match(/<img.*src="(.*?)"/)
|
||||||
|
if (urlMatch) {
|
||||||
|
ipcRenderer.send('uploadChoosedFiles', [
|
||||||
|
{
|
||||||
|
path: urlMatch[1]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
this.$message.error('请拖入合法的图片文件或者图片URL地址')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
openUplodWindow () {
|
openUplodWindow () {
|
||||||
document.getElementById('file-uploader')!.click()
|
document.getElementById('file-uploader')!.click()
|
||||||
@@ -141,6 +178,24 @@ export default class extends Vue {
|
|||||||
uploadClipboardFiles () {
|
uploadClipboardFiles () {
|
||||||
ipcRenderer.send('uploadClipboardFilesFromUploadPage')
|
ipcRenderer.send('uploadClipboardFilesFromUploadPage')
|
||||||
}
|
}
|
||||||
|
async uploadURLFiles () {
|
||||||
|
const str = await navigator.clipboard.readText()
|
||||||
|
this.$bus.$emit(SHOW_INPUT_BOX, {
|
||||||
|
value: isUrl(str) ? str : '',
|
||||||
|
title: '请输入URL',
|
||||||
|
placeholder: 'http://或者https://开头'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
handleInputBoxValue (val: string) {
|
||||||
|
if (val === '') return false
|
||||||
|
if (isUrl(val)) {
|
||||||
|
ipcRenderer.send('uploadChoosedFiles', [{
|
||||||
|
path: val
|
||||||
|
}])
|
||||||
|
} else {
|
||||||
|
this.$message.error('请输入合法的URL')
|
||||||
|
}
|
||||||
|
}
|
||||||
getDefaultPicBed () {
|
getDefaultPicBed () {
|
||||||
const current: string = this.$db.get('picBed.current')
|
const current: string = this.$db.get('picBed.current')
|
||||||
this.picBed.forEach(item => {
|
this.picBed.forEach(item => {
|
||||||
@@ -167,7 +222,8 @@ export default class extends Vue {
|
|||||||
checked: this.$db.get('picBed.current') === item.type,
|
checked: this.$db.get('picBed.current') === item.type,
|
||||||
click () {
|
click () {
|
||||||
_this.letPicGoSaveData({
|
_this.letPicGoSaveData({
|
||||||
'picBed.current': item.type
|
'picBed.current': item.type,
|
||||||
|
'picBed.uploader': item.type
|
||||||
})
|
})
|
||||||
ipcRenderer.send('syncPicBed')
|
ipcRenderer.send('syncPicBed')
|
||||||
}
|
}
|
||||||
@@ -239,8 +295,6 @@ export default class extends Vue {
|
|||||||
.el-radio-button__inner
|
.el-radio-button__inner
|
||||||
border-left none
|
border-left none
|
||||||
border-radius 0 14px 14px 0
|
border-radius 0 14px 14px 0
|
||||||
.paste-upload
|
|
||||||
width 100%
|
|
||||||
.el-icon-caret-bottom
|
.el-icon-caret-bottom
|
||||||
cursor pointer
|
cursor pointer
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ export default class extends Vue {
|
|||||||
}
|
}
|
||||||
setDefaultPicBed (type: string) {
|
setDefaultPicBed (type: string) {
|
||||||
this.letPicGoSaveData({
|
this.letPicGoSaveData({
|
||||||
'picBed.current': type
|
'picBed.current': type,
|
||||||
|
'picBed.uploader': type
|
||||||
})
|
})
|
||||||
// @ts-ignore 来自mixin的数据
|
// @ts-ignore 来自mixin的数据
|
||||||
this.defaultPicBed = type
|
this.defaultPicBed = type
|
||||||
|
|||||||
@@ -5,12 +5,27 @@
|
|||||||
<div class="view-title">
|
<div class="view-title">
|
||||||
SM.MS设置
|
SM.MS设置
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<el-form
|
||||||
感谢SM.MS提供的优质服务
|
ref="smms"
|
||||||
</div>
|
label-position="right"
|
||||||
<div style="text-align: center; margin-top: 20px;">
|
label-width="120px"
|
||||||
<el-button type="success" @click="confirm" round :disabled="defaultPicBed === 'smms'" size="mini">设为默认图床</el-button>
|
:model="form"
|
||||||
</div>
|
size="mini">
|
||||||
|
<el-form-item
|
||||||
|
label="设定Token"
|
||||||
|
prop="token"
|
||||||
|
:rules="{
|
||||||
|
required: true, message: 'Token不能为空', trigger: 'blur'
|
||||||
|
}">
|
||||||
|
<el-input v-model="form.token" type="password" placeholder="token" @keyup.native.enter="confirm('smmsForm')"></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('smms')" round :disabled="defaultPicBed === 'smms'">设为默认图床</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
@@ -23,25 +38,58 @@ import mixin from '@/utils/ConfirmButtonMixin'
|
|||||||
mixins: [mixin]
|
mixins: [mixin]
|
||||||
})
|
})
|
||||||
export default class extends Vue {
|
export default class extends Vue {
|
||||||
confirm () {
|
form: ISMMSConfig = {
|
||||||
this.letPicGoSaveData({
|
token: ''
|
||||||
'picBed.smms': true
|
}
|
||||||
})
|
created () {
|
||||||
// @ts-ignore 来自mixin
|
const config = this.$db.get('picBed.smms.token') as (string | boolean)
|
||||||
this.setDefaultPicBed('smms')
|
if (typeof config !== 'boolean') {
|
||||||
const successNotification = new window.Notification('设置结果', {
|
this.form.token = config
|
||||||
body: '设置成功'
|
|
||||||
})
|
|
||||||
successNotification.onclick = () => {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
confirm () {
|
||||||
|
// @ts-ignore
|
||||||
|
this.$refs.smms.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
this.letPicGoSaveData({
|
||||||
|
'picBed.smms': this.form
|
||||||
|
})
|
||||||
|
const successNotification = new window.Notification('设置结果', {
|
||||||
|
body: '设置成功'
|
||||||
|
})
|
||||||
|
successNotification.onclick = () => {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang='stylus'>
|
<style lang='stylus'>
|
||||||
#smms-view
|
#smms-view
|
||||||
.content
|
.el-form
|
||||||
text-align center
|
label
|
||||||
font-size 14px
|
line-height 22px
|
||||||
color #eee
|
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
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ export default new Router({
|
|||||||
component: () => import(/* webpackChunkName: "MiniPage" */ '@/pages/MiniPage.vue')
|
component: () => import(/* webpackChunkName: "MiniPage" */ '@/pages/MiniPage.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/setting',
|
path: '/main-page',
|
||||||
name: 'setting-page',
|
name: 'main-page',
|
||||||
component: () => import(/* webpackChunkName: "SettingPage" */ '@/layouts/SettingPage.vue'),
|
component: () => import(/* webpackChunkName: "SettingPage" */ '@/layouts/Main.vue'),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'upload',
|
path: 'upload',
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ export default class extends Vue {
|
|||||||
defaultPicBed = this.$db.get('picBed.current')
|
defaultPicBed = this.$db.get('picBed.current')
|
||||||
setDefaultPicBed (type: string) {
|
setDefaultPicBed (type: string) {
|
||||||
this.letPicGoSaveData({
|
this.letPicGoSaveData({
|
||||||
'picBed.current': type
|
'picBed.current': type,
|
||||||
|
'picBed.uploader': type
|
||||||
})
|
})
|
||||||
this.defaultPicBed = type
|
this.defaultPicBed = type
|
||||||
const successNotification = new Notification('设置默认图床', {
|
const successNotification = new Notification('设置默认图床', {
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
export default new Vue()
|
||||||
@@ -29,7 +29,11 @@ class DB {
|
|||||||
|
|
||||||
if (!this.db.has('picBed').value()) {
|
if (!this.db.has('picBed').value()) {
|
||||||
this.db.set('picBed', {
|
this.db.set('picBed', {
|
||||||
current: 'weibo'
|
current: 'smms', // deprecated
|
||||||
|
uploader: 'smms',
|
||||||
|
smms: {
|
||||||
|
token: ''
|
||||||
|
}
|
||||||
}).write()
|
}).write()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const SHOW_INPUT_BOX = 'SHOW_INPUT_BOX'
|
||||||
|
export const SHOW_INPUT_BOX_RESPONSE = 'SHOW_INPUT_BOX_RESPONSE'
|
||||||
|
export const TOGGLE_SHORTKEY_MODIFIED_MODE = 'TOGGLE_SHORTKEY_MODIFIED_MODE'
|
||||||
|
|||||||
Vendored
+5
-3
@@ -3,18 +3,20 @@ import {
|
|||||||
} from 'electron'
|
} from 'electron'
|
||||||
import {
|
import {
|
||||||
IWindowList
|
IWindowList
|
||||||
} from '~/main/apis/window/constants'
|
} from 'apis/app/window/constants'
|
||||||
|
|
||||||
declare interface IWindowListItem {
|
declare interface IWindowListItem {
|
||||||
isValid: boolean
|
isValid: boolean
|
||||||
multiple: boolean
|
multiple: boolean
|
||||||
options: () => IBrowserWindowOptions,
|
options: () => IBrowserWindowOptions,
|
||||||
callback: (window: BrowserWindow | null) => void
|
callback: (window: BrowserWindow, windowManager: IWindowManager) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
declare interface IWindowManager {
|
declare interface IWindowManager {
|
||||||
create: (name: IWindowList) => BrowserWindow | null
|
create: (name: IWindowList) => BrowserWindow | null
|
||||||
get: (name: IWindowList) => BrowserWindow | null
|
get: (name: IWindowList) => BrowserWindow | null
|
||||||
delete: (name: IWindowList) => void
|
has: (name: IWindowList) => boolean
|
||||||
|
// delete: (name: IWindowList) => void
|
||||||
|
deleteById: (id: number) => void
|
||||||
getAvailableWindow: () => BrowserWindow
|
getAvailableWindow: () => BrowserWindow
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
@@ -8,6 +8,7 @@ declare module 'vue/types/vue' {
|
|||||||
$db: typeof db
|
$db: typeof db
|
||||||
$http: typeof axios
|
$http: typeof axios
|
||||||
$builtInPicBed: string[]
|
$builtInPicBed: string[]
|
||||||
|
$bus: Vue
|
||||||
letPicGoSaveData(data: IObj): void
|
letPicGoSaveData(data: IObj): void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+10
-2
@@ -30,8 +30,8 @@ interface IServerCTX {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IServerConfig {
|
interface IServerConfig {
|
||||||
port: number
|
port: number | string
|
||||||
host: string,
|
host: string
|
||||||
enable: boolean
|
enable: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +44,7 @@ interface ImgInfo {
|
|||||||
height?: number
|
height?: number
|
||||||
extname?: string
|
extname?: string
|
||||||
imgUrl?: string
|
imgUrl?: string
|
||||||
|
id?: string
|
||||||
[propName: string]: any
|
[propName: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +171,8 @@ interface INPMSearchResultObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IDispose = () => void
|
||||||
|
|
||||||
// GuiApi
|
// GuiApi
|
||||||
interface IGuiApi {
|
interface IGuiApi {
|
||||||
showInputBox: (options: IShowInputBoxOption) => Promise<string>
|
showInputBox: (options: IShowInputBoxOption) => Promise<string>
|
||||||
@@ -179,6 +182,7 @@ interface IGuiApi {
|
|||||||
showMessageBox: (options?: IShowMessageBoxOption) => Promise<IShowMessageBoxResult>
|
showMessageBox: (options?: IShowMessageBoxOption) => Promise<IShowMessageBoxResult>
|
||||||
}
|
}
|
||||||
interface IShowInputBoxOption {
|
interface IShowInputBoxOption {
|
||||||
|
value?: string
|
||||||
title: string
|
title: string
|
||||||
placeholder: string
|
placeholder: string
|
||||||
}
|
}
|
||||||
@@ -251,6 +255,10 @@ interface IQiniuConfig {
|
|||||||
path: string
|
path: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ISMMSConfig {
|
||||||
|
token: string
|
||||||
|
}
|
||||||
|
|
||||||
interface ITcYunConfig {
|
interface ITcYunConfig {
|
||||||
secretId: string,
|
secretId: string,
|
||||||
secretKey: string,
|
secretKey: string,
|
||||||
|
|||||||
Vendored
+2
@@ -7,6 +7,8 @@ interface ISettingForm {
|
|||||||
uploadNotification: boolean
|
uploadNotification: boolean
|
||||||
miniWindowOntop: boolean
|
miniWindowOntop: boolean
|
||||||
logLevel: string[]
|
logLevel: string[]
|
||||||
|
autoCopyUrl: boolean
|
||||||
|
checkBetaUpdate: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IShortKeyMap {
|
interface IShortKeyMap {
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export const isUrl = (url: string): boolean => (url.startsWith('http://') || url.startsWith('https://'))
|
||||||
|
export const isUrlEncode = (url: string): boolean => {
|
||||||
|
url = url || ''
|
||||||
|
try {
|
||||||
|
return url !== decodeURI(url)
|
||||||
|
} catch (e) {
|
||||||
|
// if some error caught, try to let it go
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleUrlEncode = (url: string): string => {
|
||||||
|
if (!isUrlEncode(url)) {
|
||||||
|
url = encodeURI(url)
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import db from '#/datastore'
|
import db from '#/datastore'
|
||||||
import { IPasteStyle } from '#/types/enum'
|
import { IPasteStyle } from '#/types/enum'
|
||||||
|
import { handleUrlEncode } from './common'
|
||||||
|
|
||||||
const formatCustomLink = (customLink: string, item: ImgInfo) => {
|
const formatCustomLink = (customLink: string, item: ImgInfo) => {
|
||||||
let fileName = item.fileName!.replace(new RegExp(`\\${item.extname}$`), '')
|
let fileName = item.fileName!.replace(new RegExp(`\\${item.extname}$`), '')
|
||||||
let url = item.url || item.imgUrl
|
const url = handleUrlEncode(item.url || item.imgUrl)
|
||||||
const formatObj = {
|
const formatObj = {
|
||||||
url,
|
url,
|
||||||
fileName
|
fileName
|
||||||
@@ -19,7 +20,7 @@ const formatCustomLink = (customLink: string, item: ImgInfo) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default (style: IPasteStyle, item: ImgInfo) => {
|
export default (style: IPasteStyle, item: ImgInfo) => {
|
||||||
let url = item.url || item.imgUrl
|
const url = handleUrlEncode(item.url || item.imgUrl)
|
||||||
const customLink = db.get('settings.customLink') || '$url'
|
const customLink = db.get('settings.customLink') || '$url'
|
||||||
const tpl = {
|
const tpl = {
|
||||||
'markdown': ``,
|
'markdown': ``,
|
||||||
|
|||||||
@@ -30,6 +30,12 @@
|
|||||||
],
|
],
|
||||||
"#/*": [
|
"#/*": [
|
||||||
"src/universal/*"
|
"src/universal/*"
|
||||||
|
],
|
||||||
|
"apis/*": [
|
||||||
|
"src/main/apis/*"
|
||||||
|
],
|
||||||
|
"@core/*": [
|
||||||
|
"src/main/apis/core/*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lib": [
|
"lib": [
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ module.exports = {
|
|||||||
.set('~', resolve('src'))
|
.set('~', resolve('src'))
|
||||||
.set('root', resolve('./'))
|
.set('root', resolve('./'))
|
||||||
.set('#', resolve('src/universal'))
|
.set('#', resolve('src/universal'))
|
||||||
|
.set('apis', resolve('src/main/apis'))
|
||||||
|
.set('@core', resolve('src/main/apis/core'))
|
||||||
},
|
},
|
||||||
builderOptions: {
|
builderOptions: {
|
||||||
productName: 'PicGo',
|
productName: 'PicGo',
|
||||||
|
|||||||
@@ -3659,7 +3659,7 @@ de-indent@^1.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
||||||
integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=
|
integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=
|
||||||
|
|
||||||
debug@2, debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
|
debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||||
@@ -3797,11 +3797,6 @@ deepmerge@^1.2.0, deepmerge@^1.5.2:
|
|||||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
|
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
|
||||||
integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==
|
integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==
|
||||||
|
|
||||||
deepmerge@^4.0.0:
|
|
||||||
version "4.2.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
|
|
||||||
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
|
|
||||||
|
|
||||||
deepmerge@~2.0.1:
|
deepmerge@~2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.0.1.tgz#25c1c24f110fb914f80001b925264dd77f3f4312"
|
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.0.1.tgz#25c1c24f110fb914f80001b925264dd77f3f4312"
|
||||||
@@ -5999,6 +5994,13 @@ ignore@^5.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf"
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf"
|
||||||
integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==
|
integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==
|
||||||
|
|
||||||
|
image-size@^0.8.3:
|
||||||
|
version "0.8.3"
|
||||||
|
resolved "https://registry.npmjs.org/image-size/-/image-size-0.8.3.tgz#f0b568857e034f29baffd37013587f2c0cad8b46"
|
||||||
|
integrity sha512-SMtq1AJ+aqHB45c3FsB4ERK0UCiA2d3H1uq8s+8T0Pf8A3W4teyBQyaFaktH6xvZqh+npwlKU7i4fJo0r7TYTg==
|
||||||
|
dependencies:
|
||||||
|
queue "6.0.1"
|
||||||
|
|
||||||
immediate@~3.0.5:
|
immediate@~3.0.5:
|
||||||
version "3.0.6"
|
version "3.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||||
@@ -7581,11 +7583,6 @@ neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1:
|
|||||||
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
|
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c"
|
||||||
integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==
|
integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==
|
||||||
|
|
||||||
next-tick@^1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
|
|
||||||
integrity sha1-yobR/ogoFpsBICCOPchCS524NCw=
|
|
||||||
|
|
||||||
nice-try@^1.0.4:
|
nice-try@^1.0.4:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||||
@@ -8292,10 +8289,10 @@ performance-now@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||||
|
|
||||||
picgo@^1.4.4:
|
picgo@^1.4.11:
|
||||||
version "1.4.4"
|
version "1.4.11"
|
||||||
resolved "https://registry.yarnpkg.com/picgo/-/picgo-1.4.4.tgz#4fd2d51793a7f90a144a816c60035ed1f2f76bf2"
|
resolved "https://registry.yarnpkg.com/picgo/-/picgo-1.4.11.tgz#759dde14232cd94b62aa2d3256669014f132babe"
|
||||||
integrity sha512-0rieY+qlyfLVhPdwXZSqwitNCifgWJohDnnfHE92tPmP738r35Ab0wfa7eh4LRuI8Gx376FTNdlTIUQ4mHEsxQ==
|
integrity sha512-OdhLpSycS/YjHgHSvWDh46eoW8hYPaMtwNQkW67QkNgji7VHU7rinKnaE5xmOhh6BsgFcgEt8bC+e2uKX3qXYA==
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk "^2.4.1"
|
chalk "^2.4.1"
|
||||||
commander "^2.17.0"
|
commander "^2.17.0"
|
||||||
@@ -8306,12 +8303,12 @@ picgo@^1.4.4:
|
|||||||
ejs "^2.6.1"
|
ejs "^2.6.1"
|
||||||
fs-extra "^6.0.1"
|
fs-extra "^6.0.1"
|
||||||
globby "^8.0.2"
|
globby "^8.0.2"
|
||||||
|
image-size "^0.8.3"
|
||||||
inquirer "^6.0.0"
|
inquirer "^6.0.0"
|
||||||
lodash-id "^0.14.0"
|
lodash-id "^0.14.0"
|
||||||
lowdb "^1.0.0"
|
lowdb "^1.0.0"
|
||||||
md5 "^2.2.1"
|
md5 "^2.2.1"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
probe-image-size "^4.0.0"
|
|
||||||
qiniu "^7.2.1"
|
qiniu "^7.2.1"
|
||||||
request "^2.87.0"
|
request "^2.87.0"
|
||||||
request-promise-native "^1.0.5"
|
request-promise-native "^1.0.5"
|
||||||
@@ -8785,18 +8782,6 @@ private@^0.1.6:
|
|||||||
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
|
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
|
||||||
integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
|
integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
|
||||||
|
|
||||||
probe-image-size@^4.0.0:
|
|
||||||
version "4.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-4.1.1.tgz#c836c53154b6dd04dbcf66af2bbd50087b15e1dc"
|
|
||||||
integrity sha512-42LqKZqTLxH/UvAZ2/cKhAsR4G/Y6B7i7fI2qtQu9hRBK4YjS6gqO+QRtwTjvojUx4+/+JuOMzLoFyRecT9qRw==
|
|
||||||
dependencies:
|
|
||||||
any-promise "^1.3.0"
|
|
||||||
deepmerge "^4.0.0"
|
|
||||||
inherits "^2.0.3"
|
|
||||||
next-tick "^1.0.0"
|
|
||||||
request "^2.83.0"
|
|
||||||
stream-parser "~0.3.1"
|
|
||||||
|
|
||||||
process-nextick-args@~2.0.0:
|
process-nextick-args@~2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||||
@@ -8923,6 +8908,11 @@ qiniu@^7.2.1:
|
|||||||
tunnel-agent "0.6.0"
|
tunnel-agent "0.6.0"
|
||||||
urllib "2.22.0"
|
urllib "2.22.0"
|
||||||
|
|
||||||
|
qrcode.vue@^1.7.0:
|
||||||
|
version "1.7.0"
|
||||||
|
resolved "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-1.7.0.tgz#c54b2934f0650c10d92785d08aaad36c55e0fc56"
|
||||||
|
integrity sha512-R7t6Y3fDDtcU7L4rtqwGUDP9xD64gJhIwpfjhRCTKmBoYF6SS49PIJHRJ048cse6OI7iwTwgyy2C46N9Ygoc6g==
|
||||||
|
|
||||||
qs@6.7.0:
|
qs@6.7.0:
|
||||||
version "6.7.0"
|
version "6.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||||
@@ -8961,6 +8951,13 @@ querystringify@^2.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
|
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
|
||||||
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
|
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
|
||||||
|
|
||||||
|
queue@6.0.1:
|
||||||
|
version "6.0.1"
|
||||||
|
resolved "https://registry.npmjs.org/queue/-/queue-6.0.1.tgz#abd5a5b0376912f070a25729e0b6a7d565683791"
|
||||||
|
integrity sha512-AJBQabRCCNr9ANq8v77RJEv73DPbn55cdTb+Giq4X0AVnNVZvMHlYp7XlQiN+1npCZj1DuSmaA2hYVUUDgxFDg==
|
||||||
|
dependencies:
|
||||||
|
inherits "~2.0.3"
|
||||||
|
|
||||||
quick-lru@^1.0.0:
|
quick-lru@^1.0.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8"
|
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8"
|
||||||
@@ -10109,13 +10106,6 @@ stream-http@^2.7.2:
|
|||||||
to-arraybuffer "^1.0.0"
|
to-arraybuffer "^1.0.0"
|
||||||
xtend "^4.0.0"
|
xtend "^4.0.0"
|
||||||
|
|
||||||
stream-parser@~0.3.1:
|
|
||||||
version "0.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/stream-parser/-/stream-parser-0.3.1.tgz#1618548694420021a1182ff0af1911c129761773"
|
|
||||||
integrity sha1-FhhUhpRCACGhGC/wrxkRwSl2F3M=
|
|
||||||
dependencies:
|
|
||||||
debug "2"
|
|
||||||
|
|
||||||
stream-shift@^1.0.0:
|
stream-shift@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
|
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
|
||||||
|
|||||||
Reference in New Issue
Block a user