🔨 Refactor(ts): change js -> ts

This commit is contained in:
Molunerfinn
2019-12-19 19:17:21 +08:00
parent 72e6e2aed5
commit 4e67a75be0
91 changed files with 8223 additions and 6755 deletions
-39
View File
@@ -1,39 +0,0 @@
{
"comments": false,
"env": {
"test": {
"presets": [
["env", {
"targets": { "node": 7 }
}],
"stage-0"
],
"plugins": ["istanbul"]
},
"main": {
"presets": [
["env", {
"targets": { "node": 7 }
}],
"stage-0"
]
},
"renderer": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
},
"production": {
"presets": [
["env", {
"modules": false
}],
"stage-0"
]
}
},
"plugins": ["transform-runtime"]
}
+14 -17
View File
@@ -1,26 +1,23 @@
module.exports = { module.exports = {
root: true, root: true,
parser: 'babel-eslint', globals: {
parserOptions: { __static: 'readonly'
sourceType: 'module'
}, },
env: { env: {
browser: true,
node: true node: true
}, },
extends: 'standard', parser: "vue-eslint-parser",
globals: { 'extends': [
__static: true 'plugin:vue/essential',
}, '@vue/standard',
plugins: [ '@vue/typescript'
'html'
], ],
'rules': { 'plugins': ['@typescript-eslint'],
// allow paren-less arrow functions rules: {
'arrow-parens': 0, 'no-console': process.env.NODE_ENV === 'production' ? 'off' : 'off',
// allow async-await 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
'generator-star-spacing': 0, },
// allow debugger during development parserOptions: {
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 parser: '@typescript-eslint/parser'
} }
} }
+4
View File
@@ -12,3 +12,7 @@ thumbs.db
!.gitkeep !.gitkeep
yarn-error.log yarn-error.log
docs/dist/ docs/dist/
# local env files
.env.local
.env.*.local
dist_electron/
+22 -1
View File
@@ -1,4 +1,25 @@
{ {
"eslint.enable": true, "eslint.enable": true,
"eslint.autoFixOnSave": true "eslint.alwaysShowStatus": true,
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"vue",
"typescriptreact"
],
"[stylus]": {
"editor.formatOnSave": true
},
"stylusSupremacy.insertSemicolons": false,
"stylusSupremacy.insertBraces": false,
"stylusSupremacy.insertNewLineBetweenSelectors": true,
"stylusSupremacy.insertParenthesisAroundIfCondition": false,
"stylusSupremacy.alwaysUseNoneOverZero": true,
"stylusSupremacy.alwaysUseZeroWithoutUnit": true,
"stylusSupremacy.sortProperties": "grouped",
"stylusSupremacy.quoteChar": "\"",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
} }
+18 -10
View File
@@ -1,13 +1,21 @@
The 996ICU License (996ICU) The MIT License (MIT)
Version 0.1, March 2019
PACKAGE is distributed under LICENSE with the following restriction: Copyright (c) 2017-present, Molunerfinn
The above license is only granted to entities that act in concordance Permission is hereby granted, free of charge, to any person obtaining a copy
with local labor laws. In addition, the following requirements must be of this software and associated documentation files (the "Software"), to deal
observed: in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
* The licencee must not, explicitly or implicitly, request or schedule The above copyright notice and this permission notice shall be included in
their employees to work more than 45 hours in any single week. all copies or substantial portions of the Software.
* The licencee must not, explicitly or implicitly, request or schedule
their employees to be at work consecutively for 10 hours. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+1 -1
View File
@@ -121,4 +121,4 @@ npm run build
[MIT](http://opensource.org/licenses/MIT) [MIT](http://opensource.org/licenses/MIT)
Copyright (c) 2017 - 2019 Molunerfinn Copyright (c) 2017 - 2019 Molunerfinn
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}
View File
View File
+49 -155
View File
@@ -1,35 +1,20 @@
{ {
"name": "picgo", "name": "picgo",
"version": "2.1.2", "version": "2.1.2",
"author": "Molunerfinn <marksz@teamsz.xyz>", "private": true,
"description": "Easy to upload your pic & copy to write",
"license": "MIT",
"main": "./dist/electron/main.js",
"scripts": { "scripts": {
"render": "webpack-dev-server --hot --colors --config .electron-vue/webpack.renderer.config.js --port 9080 --content-base app/dist", "serve": "vue-cli-service serve",
"build": "node .electron-vue/build.js && electron-builder", "build": "vue-cli-service build",
"release": "node .electron-vue/build.js && electron-builder", "lint": "vue-cli-service lint",
"build:dir": "node .electron-vue/build.js && electron-builder --dir", "electron:build": "vue-cli-service electron:build",
"build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js", "electron:serve": "vue-cli-service electron:serve",
"build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js", "postinstall": "electron-builder install-app-deps",
"dev": "node .electron-vue/dev-runner.js", "postuninstall": "electron-builder install-app-deps",
"e2e": "npm run pack && mocha test/e2e",
"lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src test",
"lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test",
"pack": "npm run pack:main && npm run pack:renderer",
"pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
"pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
"test": "npm run unit && npm run e2e",
"unit": "karma start test/unit/karma.conf.js",
"postinstall": "npm run lint:fix",
"build:docs": "cross-env NODE_ENV=production webpack -p --config .electron-vue/webpack.docs.config.js",
"docs": "webpack-dev-server --content-base docs/dist --config .electron-vue/webpack.docs.config.js --hot --inline",
"patch": "npm version patch && git push origin master && git push origin --tags",
"minor": "npm version minor && git push origin master && git push origin --tags",
"major": "npm version major && git push origin master && git push origin --tags",
"cz": "git-cz", "cz": "git-cz",
"bump": "bump-version" "bump": "bump-version",
"release": "electron:build"
}, },
"main": "background.js",
"husky": { "husky": {
"hooks": { "hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS" "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
@@ -44,144 +29,53 @@
} }
}, },
"commitlint": { "commitlint": {
"extends": [ "extends": ["./node_modules/@picgo/bump-version/commitlint-picgo"]
"./node_modules/@picgo/bump-version/commitlint-picgo"
]
},
"build": {
"productName": "PicGo",
"appId": "com.molunerfinn.picgo",
"directories": {
"output": "build"
},
"files": [
"dist/electron/**/*"
],
"dmg": {
"contents": [
{
"x": 410,
"y": 150,
"type": "link",
"path": "/Applications"
},
{
"x": 130,
"y": 150,
"type": "file"
}
]
},
"mac": {
"icon": "build/icons/icon.icns",
"extendInfo": {
"LSUIElement": 1
}
},
"win": {
"icon": "build/icons/icon.ico",
"target": "nsis"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
},
"linux": {
"icon": "build/icons/"
}
}, },
"dependencies": { "dependencies": {
"axios": "^0.19.0", "axios": "^0.19.0",
"dateformat": "^3.0.3", "core-js": "^3.3.2",
"element-ui": "^2.4.11", "element-ui": "^2.13.0",
"fix-path": "^2.1.0", "fix-path": "^2.1.0",
"fs-extra": "^4.0.2", "fs-extra": "^8.1.0",
"image-size": "^0.6.1", "keycode": "^2.2.0",
"keycode": "^2.1.9",
"lodash-id": "^0.14.0", "lodash-id": "^0.14.0",
"lowdb": "^1.0.0", "lowdb": "^1.0.0",
"md5": "^2.2.1",
"melody.css": "^1.0.2",
"picgo": "^1.3.7", "picgo": "^1.3.7",
"qiniu": "^7.1.1", "vue": "^2.6.10",
"vue": "^2.3.3", "vue-gallery": "^2.0.1",
"vue-electron": "^1.0.6",
"vue-gallery": "^1.2.4",
"vue-lazyload": "^1.2.6", "vue-lazyload": "^1.2.6",
"vue-router": "^2.5.3", "vue-router": "^3.1.3"
"vuex": "^2.3.1",
"ws": "3.3.1"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^7.5.2", "@commitlint/cli": "^8.2.0",
"@picgo/bump-version": "^1.0.2", "@picgo/bump-version": "^1.0.3",
"babel-core": "^6.25.0", "@types/fs-extra": "^8.0.1",
"babel-eslint": "^7.2.3", "@types/inquirer": "^6.5.0",
"babel-loader": "^7.1.1", "@types/lowdb": "^1.0.9",
"babel-plugin-istanbul": "^4.1.1", "@types/node": "10.17.6",
"babel-plugin-transform-runtime": "^6.23.0", "@types/request-promise-native": "^1.0.17",
"babel-preset-env": "^1.6.0", "@vue/cli-plugin-babel": "^4.0.0",
"babel-preset-stage-0": "^6.24.1", "@vue/cli-plugin-eslint": "^4.0.0",
"babel-register": "^6.24.1", "@vue/cli-plugin-router": "^4.0.0",
"babili-webpack-plugin": "^0.1.2", "@vue/cli-plugin-typescript": "^4.0.0",
"cfonts": "^1.1.3", "@vue/cli-service": "^4.0.0",
"chai": "^4.0.0", "@vue/eslint-config-standard": "^4.0.0",
"chalk": "^2.1.0", "@vue/eslint-config-typescript": "^4.0.0",
"commitizen": "^3.0.7", "commitizen": "^4.0.3",
"conventional-changelog": "^3.0.6", "conventional-changelog": "^3.1.18",
"copy-webpack-plugin": "^4.0.1", "cz-customizable": "^6.2.0",
"cross-env": "^5.0.5", "electron": "^6.0.0",
"css-loader": "^0.28.4", "eslint": "^5.16.0",
"cz-customizable": "^5.10.0", "eslint-plugin-vue": "^5.0.0",
"del": "^3.0.0", "husky": "^3.1.0",
"devtron": "^1.4.0", "stylus": "^0.54.7",
"electron": "^5.0.1", "stylus-loader": "^3.0.2",
"electron-builder": "^20.38.4", "typescript": "~3.5.3",
"electron-debug": "^1.4.0", "vue-cli-plugin-electron-builder": "^1.4.2",
"electron-devtools-installer": "^2.2.0", "vue-property-decorator": "^8.3.0",
"eslint": "^4.4.1", "vue-template-compiler": "^2.6.10"
"eslint-config-standard": "^10.2.1", },
"eslint-friendly-formatter": "^3.0.0", "resolutions": {
"eslint-loader": "^2.1.1", "**/@types/node": "10.17.6"
"eslint-plugin-html": "^3.1.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-node": "^5.1.1",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-standard": "^3.0.1",
"file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0",
"husky": "^1.3.1",
"inject-loader": "^3.0.0",
"inquirer": "^6.3.1",
"karma": "^1.3.0",
"karma-chai": "^0.1.0",
"karma-coverage": "^1.1.1",
"karma-electron": "^5.1.1",
"karma-mocha": "^1.2.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-spec-reporter": "^0.0.31",
"karma-webpack": "^2.0.1",
"mini-css-extract-plugin": "0.4.0",
"mocha": "^3.0.2",
"multispinner": "^0.2.1",
"node-loader": "^0.6.0",
"pug": "^2.0.0-rc.4",
"pug-loader": "^2.3.0",
"pug-plain-loader": "^1.0.0",
"require-dir": "^0.3.0",
"spectron": "^3.7.1",
"style-loader": "^0.23.1",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
"url-loader": "^1.1.2",
"vue-html-loader": "^1.2.4",
"vue-loader": "^15.4.2",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.4.2",
"webpack": "^4.15.1",
"webpack-cli": "^3.0.8",
"webpack-dev-server": "^3.1.4",
"webpack-hot-middleware": "^2.22.2",
"webpack-merge": "^4.1.3"
} }
} }
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+18
View File
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="referrer" content="never">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>PicGo</title>
</head>
<body>
<noscript>
<strong>We're sorry but picgo-new doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
View File
View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 497 B

After

Width:  |  Height:  |  Size: 497 B

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 915 B

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before

Width:  |  Height:  |  Size: 823 B

After

Width:  |  Height:  |  Size: 823 B

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before

Width:  |  Height:  |  Size: 777 B

After

Width:  |  Height:  |  Size: 777 B

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

+196 -94
View File
@@ -1,6 +1,77 @@
// 'use strict'
// import { app, protocol, BrowserWindow } from 'electron'
// import {
// createProtocol,
// installVueDevtools
// } from 'vue-cli-plugin-electron-builder/lib'
// const isDevelopment = process.env.NODE_ENV !== 'production'
// // Keep a global reference of the window object, if you don't, the window will
// // be closed automatically when the JavaScript object is garbage collected.
// let win: BrowserWindow | null
// // Scheme must be registered before the app is ready
// protocol.registerSchemesAsPrivileged([{ scheme: 'app', privileges: { secure: true, standard: true } }])
// function createWindow () {
// // Create the browser window.
// win = new BrowserWindow({ width: 800,
// height: 600,
// webPreferences: {
// nodeIntegration: true
// } })
// if (process.env.WEBPACK_DEV_SERVER_URL) {
// // Load the url of the dev server if in development mode
// win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
// if (!process.env.IS_TEST) win.webContents.openDevTools()
// } else {
// createProtocol('app')
// // Load the index.html when not in development
// win.loadURL('app://./index.html')
// }
// win.on('closed', () => {
// win = null
// })
// }
// // Quit when all windows are closed.
// app.on('window-all-closed', () => {
// // On macOS it is common for applications and their menu bar
// // to stay active until the user quits explicitly with Cmd + Q
// if (process.platform !== 'darwin') {
// app.quit()
// }
// })
// app.on('activate', () => {
// // On macOS it's common to re-create a window in the app when the
// // dock icon is clicked and there are no other windows open.
// if (win === null) {
// createWindow()
// }
// })
// // This method will be called when Electron has finished
// // initialization and is ready to create browser windows.
// // Some APIs can only be used after this event occurs.
// app.on('ready', async () => {
// if (isDevelopment && !process.env.IS_TEST) {
// // Install Vue Devtools
// try {
// await installVueDevtools()
// } catch (e) {
// console.error('Vue Devtools failed to install:', e.toString())
// }
// }
// createWindow()
// })
'use strict' 'use strict'
import Uploader from './utils/uploader.js' import Uploader from '~/main/utils/uploader'
import { import {
app, app,
BrowserWindow, BrowserWindow,
@@ -11,54 +82,54 @@ import {
ipcMain, ipcMain,
globalShortcut, globalShortcut,
dialog, dialog,
systemPreferences systemPreferences,
WebContents,
IpcMainEvent,
protocol
} from 'electron' } from 'electron'
import db from '../datastore' import {
import beforeOpen from './utils/beforeOpen' createProtocol,
import pasteTemplate from './utils/pasteTemplate' installVueDevtools
import updateChecker from './utils/updateChecker' } from 'vue-cli-plugin-electron-builder/lib'
import { getPicBeds } from './utils/getPicBeds' import db from '#/datastore'
import pkg from '../../package.json' import beforeOpen from '~/main/utils/beforeOpen'
import picgoCoreIPC from './utils/picgoCoreIPC' import pasteTemplate from '#/utils/pasteTemplate'
import updateChecker from '~/main/utils/updateChecker'
import { getPicBeds } from '~/main/utils/getPicBeds'
import pkg from 'root/package.json'
import picgoCoreIPC from '~/main/utils/picgoCoreIPC'
import fixPath from 'fix-path' import fixPath from 'fix-path'
import { getUploadFiles } from './utils/handleArgv' import { getUploadFiles } from '~/main/utils/handleArgv'
import bus from './utils/eventBus' import bus from '~/main/utils/eventBus'
import { import {
updateShortKeyFromVersion212 updateShortKeyFromVersion212
} from './migrate/shortKeyUpdateHelper' } from '~/main/migrate/shortKeyUpdateHelper'
import { import {
shortKeyUpdater, shortKeyUpdater,
initShortKeyRegister initShortKeyRegister
} from './utils/shortKeyHandler' } from '~/main/utils/shortKeyHandler'
const isDevelopment = process.env.NODE_ENV !== 'production'
protocol.registerSchemesAsPrivileged([{ scheme: 'app', privileges: { secure: true, standard: true } }])
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
beforeOpen() beforeOpen()
} }
/**
* Set `__static` path to static files in production
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
*/
if (process.env.NODE_ENV !== 'development') {
global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
}
if (process.env.DEBUG_ENV === 'debug') {
global.__static = require('path').join(__dirname, '../../static').replace(/\\/g, '\\\\')
}
let window let window: BrowserWindow | null
let settingWindow let settingWindow: BrowserWindow | null
let miniWindow let miniWindow: BrowserWindow | null
let tray let tray: Tray | null
let menu let menu: Menu | null
let contextMenu let contextMenu: Menu | null
const winURL = process.env.NODE_ENV === 'development' const winURL = isDevelopment
? `http://localhost:9080` ? (process.env.WEBPACK_DEV_SERVER_URL as string)
: `file://${__dirname}/index.html` : `picgo://./index.html`
const settingWinURL = process.env.NODE_ENV === 'development' const settingWinURL = isDevelopment
? `http://localhost:9080/#setting/upload` ? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#setting/upload`
: `file://${__dirname}/index.html#setting/upload` : `picgo://./index.html#setting/upload`
const miniWinURL = process.env.NODE_ENV === 'development' const miniWinURL = isDevelopment
? `http://localhost:9080/#mini-page` ? `${(process.env.WEBPACK_DEV_SERVER_URL as string)}#mini-page`
: `file://${__dirname}/index.html#mini-page` : `picgo://./index.html#mini-page`
// fix the $PATH in macOS // fix the $PATH in macOS
fixPath() fixPath()
@@ -94,7 +165,7 @@ function createContextMenu () {
click () { click () {
if (settingWindow === null) { if (settingWindow === null) {
createSettingWindow() createSettingWindow()
settingWindow.show() settingWindow!.show()
} else { } else {
settingWindow.show() settingWindow.show()
settingWindow.focus() settingWindow.focus()
@@ -107,8 +178,10 @@ function createContextMenu () {
{ {
label: '选择默认图床', label: '选择默认图床',
type: 'submenu', type: 'submenu',
// @ts-ignore
submenu submenu
}, },
// @ts-ignore
{ {
label: '打开更新助手', label: '打开更新助手',
type: 'checkbox', type: 'checkbox',
@@ -125,6 +198,7 @@ function createContextMenu () {
app.exit(0) app.exit(0)
} }
}, },
// @ts-ignore
{ {
role: 'quit', role: 'quit',
label: '退出' label: '退出'
@@ -140,14 +214,15 @@ function createTray () {
window.hide() window.hide()
} }
createContextMenu() createContextMenu()
tray.popUpContextMenu(contextMenu) tray!.popUpContextMenu(contextMenu!)
}) })
tray.on('click', (event, bounds) => { tray.on('click', (event, bounds) => {
if (process.platform === 'darwin') { if (process.platform === 'darwin') {
let img = clipboard.readImage() let img = clipboard.readImage()
let obj = [] let obj: ImgInfo[] = []
if (!img.isEmpty()) { if (!img.isEmpty()) {
// 从剪贴板来的图片默认转为png // 从剪贴板来的图片默认转为png
// @ts-ignore
const imgUrl = 'data:image/png;base64,' + Buffer.from(img.toPNG(), 'binary').toString('base64') const imgUrl = 'data:image/png;base64,' + Buffer.from(img.toPNG(), 'binary').toString('base64')
obj.push({ obj.push({
width: img.getSize().width, width: img.getSize().width,
@@ -157,7 +232,7 @@ function createTray () {
} }
toggleWindow(bounds) toggleWindow(bounds)
setTimeout(() => { setTimeout(() => {
window.webContents.send('clipboardFiles', obj) window!.webContents.send('clipboardFiles', obj)
}, 0) }, 0)
} else { } else {
if (window) { if (window) {
@@ -165,7 +240,7 @@ function createTray () {
} }
if (settingWindow === null) { if (settingWindow === null) {
createSettingWindow() createSettingWindow()
settingWindow.show() settingWindow!.show()
} else { } else {
settingWindow.show() settingWindow.show()
settingWindow.focus() settingWindow.focus()
@@ -178,25 +253,25 @@ function createTray () {
tray.on('drag-enter', () => { tray.on('drag-enter', () => {
if (systemPreferences.isDarkMode()) { if (systemPreferences.isDarkMode()) {
tray.setImage(`${__static}/upload-dark.png`) tray!.setImage(`${__static}/upload-dark.png`)
} else { } else {
tray.setImage(`${__static}/upload.png`) tray!.setImage(`${__static}/upload.png`)
} }
}) })
tray.on('drag-end', () => { tray.on('drag-end', () => {
tray.setImage(`${__static}/menubar.png`) tray!.setImage(`${__static}/menubar.png`)
}) })
tray.on('drop-files', async (event, files) => { tray.on('drop-files', async (event: Event, files: string[]) => {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown' const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
const imgs = await new Uploader(files, window.webContents).upload() const imgs = await new Uploader(files, window!.webContents).upload()
if (imgs !== false) { if (imgs !== false) {
for (let i in imgs) { for (let i = 0; i < imgs.length; i++) {
clipboard.writeText(pasteTemplate(pasteStyle, imgs[i])) clipboard.writeText(pasteTemplate(pasteStyle, imgs[i]))
const notification = new Notification({ const notification = new Notification({
title: '上传成功', title: '上传成功',
body: imgs[i].imgUrl, body: imgs[i].imgUrl!,
icon: files[i] icon: files[i]
}) })
setTimeout(() => { setTimeout(() => {
@@ -204,7 +279,7 @@ function createTray () {
}, i * 100) }, i * 100)
db.insert('uploaded', imgs[i]) db.insert('uploaded', imgs[i])
} }
window.webContents.send('dragFiles', imgs) window!.webContents.send('dragFiles', imgs)
} }
}) })
// toggleWindow() // toggleWindow()
@@ -237,7 +312,7 @@ const createWindow = () => {
}) })
window.on('blur', () => { window.on('blur', () => {
window.hide() window!.hide()
}) })
return window return window
} }
@@ -246,7 +321,7 @@ const createMiniWidow = () => {
if (miniWindow) { if (miniWindow) {
return false return false
} }
let obj = { let obj: BrowserWindowOptions = {
height: 64, height: 64,
width: 64, width: 64,
show: process.platform === 'linux', show: process.platform === 'linux',
@@ -278,7 +353,7 @@ const createMiniWidow = () => {
} }
const createSettingWindow = () => { const createSettingWindow = () => {
const options = { const options: BrowserWindowOptions = {
height: 450, height: 450,
width: 800, width: 800,
show: false, show: false,
@@ -306,9 +381,9 @@ const createSettingWindow = () => {
} }
settingWindow = new BrowserWindow(options) settingWindow = new BrowserWindow(options)
settingWindow.loadURL(settingWinURL) settingWindow!.loadURL(settingWinURL)
settingWindow.on('closed', () => { settingWindow!.on('closed', () => {
bus.emit('toggleShortKeyModifiedMode', false) bus.emit('toggleShortKeyModifiedMode', false)
settingWindow = null settingWindow = null
if (process.platform === 'linux') { if (process.platform === 'linux') {
@@ -343,47 +418,48 @@ const createMenu = () => {
} }
] ]
}] }]
// @ts-ignore
menu = Menu.buildFromTemplate(template) menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu) Menu.setApplicationMenu(menu)
} }
} }
const toggleWindow = (bounds) => { const toggleWindow = (bounds: Bounds) => {
if (window.isVisible()) { if (window!.isVisible()) {
window.hide() window!.hide()
} else { } else {
showWindow(bounds) showWindow(bounds)
} }
} }
const showWindow = (bounds) => { const showWindow = (bounds: Bounds) => {
window.setPosition(bounds.x - 98 + 11, bounds.y, false) window!.setPosition(bounds.x - 98 + 11, bounds.y, false)
window.webContents.send('updateFiles') window!.webContents.send('updateFiles')
window.show() window!.show()
window.focus() window!.focus()
} }
const uploadClipboardFiles = async () => { const uploadClipboardFiles = async () => {
let win let win
if (miniWindow.isVisible()) { if (miniWindow!.isVisible()) {
win = miniWindow win = miniWindow
} else { } else {
win = settingWindow || window || createSettingWindow() win = settingWindow || window || createSettingWindow()
} }
let img = await new Uploader(undefined, win.webContents).upload() let img = await new Uploader(undefined, win!.webContents).upload()
if (img !== false) { if (img !== false) {
if (img.length > 0) { if (img.length > 0) {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown' const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
clipboard.writeText(pasteTemplate(pasteStyle, img[0])) clipboard.writeText(pasteTemplate(pasteStyle, img[0]))
const notification = new Notification({ const notification = new Notification({
title: '上传成功', title: '上传成功',
body: img[0].imgUrl, body: img[0].imgUrl!,
icon: img[0].imgUrl icon: img[0].imgUrl
}) })
notification.show() notification.show()
db.insert('uploaded', img[0]) db.insert('uploaded', img[0])
window.webContents.send('clipboardFiles', []) window!.webContents.send('clipboardFiles', [])
window.webContents.send('uploadFiles', img) window!.webContents.send('uploadFiles', img)
if (settingWindow) { if (settingWindow) {
settingWindow.webContents.send('updateGallery') settingWindow.webContents.send('updateGallery')
} }
@@ -397,17 +473,17 @@ const uploadClipboardFiles = async () => {
} }
} }
const uploadChoosedFiles = async (webContents, files) => { const uploadChoosedFiles = async (webContents: WebContents, files: FileWithPath[]) => {
const input = files.map(item => item.path) const input = files.map(item => item.path)
const imgs = await new Uploader(input, webContents).upload() const imgs = await new Uploader(input, webContents).upload()
if (imgs !== false) { if (imgs !== false) {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown' const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
let pasteText = '' let pasteText = ''
for (let i in imgs) { for (let i = 0; i < imgs.length; i++) {
pasteText += pasteTemplate(pasteStyle, imgs[i]) + '\r\n' pasteText += pasteTemplate(pasteStyle, imgs[i]) + '\r\n'
const notification = new Notification({ const notification = new Notification({
title: '上传成功', title: '上传成功',
body: imgs[i].imgUrl, body: imgs[i].imgUrl!,
icon: files[i].path icon: files[i].path
}) })
setTimeout(() => { setTimeout(() => {
@@ -416,7 +492,7 @@ const uploadChoosedFiles = async (webContents, files) => {
db.insert('uploaded', imgs[i]) db.insert('uploaded', imgs[i])
} }
clipboard.writeText(pasteText) clipboard.writeText(pasteText)
window.webContents.send('uploadFiles', imgs) window!.webContents.send('uploadFiles', imgs)
if (settingWindow) { if (settingWindow) {
settingWindow.webContents.send('updateGallery') settingWindow.webContents.send('updateGallery')
} }
@@ -426,36 +502,36 @@ const uploadChoosedFiles = async (webContents, files) => {
picgoCoreIPC(app, ipcMain) picgoCoreIPC(app, ipcMain)
// from macOS tray // from macOS tray
ipcMain.on('uploadClipboardFiles', async (evt, file) => { ipcMain.on('uploadClipboardFiles', async () => {
const img = await new Uploader(undefined, window.webContents).upload() const img = await new Uploader(undefined, window!.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])) clipboard.writeText(pasteTemplate(pasteStyle, img[0]))
const notification = new Notification({ const notification = new Notification({
title: '上传成功', title: '上传成功',
body: img[0].imgUrl, body: img[0].imgUrl!,
// icon: file[0] // icon: file[0]
icon: img[0].imgUrl icon: img[0].imgUrl
}) })
notification.show() notification.show()
db.insert('uploaded', img[0]) db.insert('uploaded', img[0])
window.webContents.send('clipboardFiles', []) window!.webContents.send('clipboardFiles', [])
if (settingWindow) { if (settingWindow) {
settingWindow.webContents.send('updateGallery') settingWindow.webContents.send('updateGallery')
} }
} }
window.webContents.send('uploadFiles') window!.webContents.send('uploadFiles')
}) })
ipcMain.on('uploadClipboardFilesFromUploadPage', () => { ipcMain.on('uploadClipboardFilesFromUploadPage', () => {
uploadClipboardFiles() uploadClipboardFiles()
}) })
ipcMain.on('uploadChoosedFiles', async (evt, files) => { ipcMain.on('uploadChoosedFiles', async (evt: IpcMainEvent, files: FileWithPath[]) => {
return uploadChoosedFiles(evt.sender, files) return uploadChoosedFiles(evt.sender, files)
}) })
ipcMain.on('updateShortKey', (evt, item, oldKey) => { ipcMain.on('updateShortKey', (evt: IpcMainEvent, item: ShortKeyConfig, oldKey: string) => {
shortKeyUpdater(globalShortcut, item, oldKey) shortKeyUpdater(globalShortcut, item, oldKey)
const notification = new Notification({ const notification = new Notification({
title: '操作成功', title: '操作成功',
@@ -464,7 +540,7 @@ ipcMain.on('updateShortKey', (evt, item, oldKey) => {
notification.show() notification.show()
}) })
ipcMain.on('updateCustomLink', (evt, oldLink) => { ipcMain.on('updateCustomLink', () => {
const notification = new Notification({ const notification = new Notification({
title: '操作成功', title: '操作成功',
body: '你的自定义链接格式已经修改成功' body: '你的自定义链接格式已经修改成功'
@@ -472,44 +548,46 @@ ipcMain.on('updateCustomLink', (evt, oldLink) => {
notification.show() notification.show()
}) })
ipcMain.on('autoStart', (evt, val) => { ipcMain.on('autoStart', (evt: IpcMainEvent, val: boolean) => {
app.setLoginItemSettings({ app.setLoginItemSettings({
openAtLogin: val openAtLogin: val
}) })
}) })
ipcMain.on('openSettingWindow', (evt) => { ipcMain.on('openSettingWindow', () => {
if (!settingWindow) { if (!settingWindow) {
createSettingWindow() createSettingWindow()
} else { } else {
settingWindow.show() settingWindow.show()
} }
miniWindow.hide() if (miniWindow) {
miniWindow.hide()
}
}) })
ipcMain.on('openMiniWindow', (evt) => { ipcMain.on('openMiniWindow', () => {
if (!miniWindow) { if (!miniWindow) {
createMiniWidow() createMiniWidow()
} }
miniWindow.show() miniWindow!.show()
miniWindow.focus() miniWindow!.focus()
settingWindow.hide() settingWindow!.hide()
}) })
// from mini window // from mini window
ipcMain.on('syncPicBed', (evt) => { ipcMain.on('syncPicBed', () => {
if (settingWindow) { if (settingWindow) {
settingWindow.webContents.send('syncPicBed') settingWindow.webContents.send('syncPicBed')
} }
}) })
ipcMain.on('getPicBeds', (evt) => { ipcMain.on('getPicBeds', (evt: IpcMainEvent) => {
const picBeds = getPicBeds(app) const picBeds = getPicBeds(app)
evt.sender.send('getPicBeds', picBeds) evt.sender.send('getPicBeds', picBeds)
evt.returnValue = picBeds evt.returnValue = picBeds
}) })
ipcMain.on('toggleShortKeyModifiedMode', (evt, val) => { ipcMain.on('toggleShortKeyModifiedMode', (evt: IpcMainEvent, val: boolean) => {
bus.emit('toggleShortKeyModifiedMode', val) bus.emit('toggleShortKeyModifiedMode', val)
}) })
@@ -548,14 +626,22 @@ if (!gotTheLock) {
} }
if (process.platform === 'win32') { if (process.platform === 'win32') {
app.setAppUserModelId(pkg.build.appId) app.setAppUserModelId('com.molunerfinn.picgo')
} }
if (process.env.XDG_CURRENT_DESKTOP && process.env.XDG_CURRENT_DESKTOP.includes('Unity')) { if (process.env.XDG_CURRENT_DESKTOP && process.env.XDG_CURRENT_DESKTOP.includes('Unity')) {
process.env.XDG_CURRENT_DESKTOP = 'Unity' process.env.XDG_CURRENT_DESKTOP = 'Unity'
} }
app.on('ready', () => { app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
await installVueDevtools()
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
createWindow() createWindow()
createSettingWindow() createSettingWindow()
if (process.platform === 'darwin' || process.platform === 'win32') { if (process.platform === 'darwin' || process.platform === 'win32') {
@@ -595,6 +681,7 @@ app.on('window-all-closed', () => {
}) })
app.on('activate', () => { app.on('activate', () => {
createProtocol('picgo')
if (window === null) { if (window === null) {
createWindow() createWindow()
} }
@@ -613,7 +700,7 @@ app.setLoginItemSettings({
}) })
function initEventCenter () { function initEventCenter () {
const eventList = { const eventList: any = {
'picgo:upload': uploadClipboardFiles 'picgo:upload': uploadClipboardFiles
} }
for (let i in eventList) { for (let i in eventList) {
@@ -621,6 +708,21 @@ function initEventCenter () {
} }
} }
// 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()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}
/** /**
* Auto Updater * Auto Updater
* *
-56
View File
@@ -1,56 +0,0 @@
import db from './index'
let picBed = [
{
type: 'weibo',
name: '微博图床',
visible: true
},
{
type: 'qiniu',
name: '七牛图床',
visible: true
},
{
type: 'tcyun',
name: '腾讯云COS',
visible: true
},
{
type: 'upyun',
name: '又拍云图床',
visible: true
},
{
type: 'github',
name: 'GitHub图床',
visible: true
},
{
type: 'smms',
name: 'SM.MS图床',
visible: true
},
{
type: 'aliyun',
name: '阿里云OSS',
visible: true
},
{
type: 'imgur',
name: 'Imgur图床',
visible: true
}
]
let picBedFromDB = db.get('picBed.list') || []
let oldLength = picBedFromDB.length
let newLength = picBed.length
if (oldLength !== newLength) {
for (let i = oldLength; i < newLength; i++) {
picBedFromDB.push(picBed[i])
}
}
export default picBedFromDB
-24
View File
@@ -1,24 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="referrer" content="never">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>PicGo</title>
<% if (htmlWebpackPlugin.options.nodeModules) { %>
<!-- Add `node_modules/` to global paths so `require` works properly in development -->
<script>
require('module').globalPaths.push("<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, '\\\\') %>")
</script>
<% } %>
</head>
<body>
<div id="app"></div>
<!-- Set `__static` path to static files in production -->
<script>
if (process.env.NODE_ENV !== 'development') window.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
</script>
<!-- webpack builds are automatically injected -->
</body>
</html>
+10 -18
View File
@@ -1,24 +1,16 @@
import Vue from 'vue' import Vue from 'vue'
import axios from 'axios' import App from './renderer/App.vue'
import router from './renderer/router'
import db from '#/datastore/index'
import ElementUI from 'element-ui' import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import App from './App'
import router from './router'
import store from './store'
import db from '../datastore/index'
import { webFrame } from 'electron' import { webFrame } from 'electron'
import './assets/fonts/iconfont.css' import 'element-ui/lib/theme-chalk/index.css'
import VueLazyLoad from 'vue-lazyload' import VueLazyLoad from 'vue-lazyload'
Vue.use(ElementUI)
Vue.use(VueLazyLoad)
webFrame.setVisualZoomLevelLimits(1, 1) webFrame.setVisualZoomLevelLimits(1, 1)
webFrame.setLayoutZoomLevelLimits(0, 0) webFrame.setLayoutZoomLevelLimits(0, 0)
if (!process.env.IS_WEB) Vue.use(require('vue-electron')) Vue.config.productionTip = false
Vue.http = Vue.prototype.$http = axios
Vue.prototype.$db = db
Vue.prototype.$builtInPicBed = [ Vue.prototype.$builtInPicBed = [
'smms', 'smms',
'weibo', 'weibo',
@@ -29,12 +21,12 @@ Vue.prototype.$builtInPicBed = [
'aliyun', 'aliyun',
'github' 'github'
] ]
Vue.config.productionTip = false Vue.prototype.$db = db
Vue.use(ElementUI)
Vue.use(VueLazyLoad)
/* eslint-disable no-new */
new Vue({ new Vue({
components: { App },
router, router,
store, render: h => h(App)
template: '<App/>'
}).$mount('#app') }).$mount('#app')
-24
View File
@@ -1,24 +0,0 @@
/**
* This file is used specifically and only for development. It installs
* `electron-debug` & `vue-devtools`. There shouldn't be any need to
* modify this file, but it can be used to extend your development
* environment.
*/
/* eslint-disable */
// Install `electron-debug` with `devtron`
require('electron-debug')({ showDevTools: false })
// Install `vue-devtools`
require('electron').app.on('ready', () => {
let installExtension = require('electron-devtools-installer')
installExtension.default(installExtension.VUEJS_DEVTOOLS)
.then(() => {})
.catch(err => {
console.log('Unable to install `vue-devtools`: \n', err)
})
})
// Require `main` process to boot app
require('./index')
@@ -1,8 +1,10 @@
import DB from '#/datastore'
// from v2.1.2 // from v2.1.2
const updateShortKeyFromVersion212 = (db, shortKeyConfig) => { const updateShortKeyFromVersion212 = (db: typeof DB, shortKeyConfig: ShortKeyConfigs | OldShortKeyConfigs) => {
let needUpgrade = false let needUpgrade = false
if (shortKeyConfig.upload) { if (shortKeyConfig.upload) {
needUpgrade = true needUpgrade = true
// @ts-ignore
shortKeyConfig['picgo:upload'] = { shortKeyConfig['picgo:upload'] = {
enable: true, enable: true,
key: shortKeyConfig.upload, key: shortKeyConfig.upload,
@@ -2,12 +2,6 @@ import fs from 'fs-extra'
import path from 'path' import path from 'path'
import os from 'os' import os from 'os'
import { remote, app } from 'electron' import { remote, app } from 'electron'
if (process.env.NODE_ENV !== 'development') {
global.__static = path.join(__dirname, '/static').replace(/\\/g, '\\\\')
}
if (process.env.DEBUG_ENV === 'debug') {
global.__static = path.join(__dirname, '../../../static').replace(/\\/g, '\\\\')
}
const APP = process.type === 'renderer' ? remote.app : app const APP = process.type === 'renderer' ? remote.app : app
const STORE_PATH = APP.getPath('userData') const STORE_PATH = APP.getPath('userData')
@@ -50,7 +44,7 @@ function resolveClipboardImageGenerator () {
}) })
} }
function diffFilesAndUpdate (filePath1, filePath2) { function diffFilesAndUpdate (filePath1: string, filePath2: string) {
let file1 = fs.readFileSync(filePath1) let file1 = fs.readFileSync(filePath1)
let file2 = fs.readFileSync(filePath2) let file2 = fs.readFileSync(filePath2)
@@ -1,23 +1,24 @@
import path from 'path' import path from 'path'
import db from '../../datastore' import db from '#/datastore'
import { App } from 'electron'
// 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
const getPicBeds = (app) => { const getPicBeds = (app: App) => {
const PicGo = requireFunc('picgo') const PicGo = requireFunc('picgo')
const STORE_PATH = app.getPath('userData') const STORE_PATH = app.getPath('userData')
const CONFIG_PATH = path.join(STORE_PATH, '/data.json') const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
const picgo = new PicGo(CONFIG_PATH) const picgo = new PicGo(CONFIG_PATH)
const picBedTypes = picgo.helper.uploader.getIdList() const picBedTypes = picgo.helper.uploader.getIdList()
const picBedFromDB = db.get('picBed.list') || [] const picBedFromDB = db.get('picBed.list') || []
const picBeds = picBedTypes.map(item => { const picBeds = picBedTypes.map((item: string) => {
const visible = picBedFromDB.find(i => i.type === item) // object or undefined const visible = picBedFromDB.find((i: PicBedType) => 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 PicBedType[]
picgo.cmd.program.removeAllListeners() picgo.cmd.program.removeAllListeners()
return picBeds return picBeds
} }
@@ -2,17 +2,23 @@ import {
dialog, dialog,
BrowserWindow, BrowserWindow,
clipboard, clipboard,
Notification Notification,
IpcMain,
WebContents
} from 'electron' } from 'electron'
import db from '../../datastore' import db from '#/datastore'
import Uploader from './uploader' import Uploader from './uploader'
import pasteTemplate from './pasteTemplate' import pasteTemplate from '#/utils/pasteTemplate'
import PicGoCore from '~/universal/types/picgo'
const WEBCONTENTS = Symbol('WEBCONTENTS') const WEBCONTENTS = Symbol('WEBCONTENTS')
const IPCMAIN = Symbol('IPCMAIN') const IPCMAIN = Symbol('IPCMAIN')
const PICGO = Symbol('PICGO') const PICGO = Symbol('PICGO')
class GuiApi { class GuiApi {
constructor (ipcMain, webcontents, picgo) { private [WEBCONTENTS]: WebContents
private [IPCMAIN]: IpcMain
private [PICGO]: PicGoCore
constructor (ipcMain: IpcMain, webcontents: WebContents, picgo: PicGoCore) {
this[WEBCONTENTS] = webcontents this[WEBCONTENTS] = webcontents
this[IPCMAIN] = ipcMain this[IPCMAIN] = ipcMain
this[PICGO] = picgo this[PICGO] = picgo
@@ -23,7 +29,7 @@ class GuiApi {
* @param {object} options * @param {object} options
* return type is string or '' * return type is string or ''
*/ */
showInputBox (options) { showInputBox (options: IShowInputBoxOption) {
if (options === undefined) { if (options === undefined) {
options = { options = {
title: '', title: '',
@@ -32,7 +38,7 @@ class GuiApi {
} }
this[WEBCONTENTS].send('showInputBox', options) this[WEBCONTENTS].send('showInputBox', options)
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this[IPCMAIN].once('showInputBox', (event, value) => { this[IPCMAIN].once('showInputBox', (event: Event, value: string) => {
resolve(value) resolve(value)
}) })
}) })
@@ -42,12 +48,12 @@ class GuiApi {
* for plugin show file explorer * for plugin show file explorer
* @param {object} options * @param {object} options
*/ */
showFileExplorer (options) { showFileExplorer (options: {}) {
if (options === undefined) { if (options === undefined) {
options = {} options = {}
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
dialog.showOpenDialog(BrowserWindow.fromWebContents(this[WEBCONTENTS]), options, filename => { dialog.showOpenDialog(BrowserWindow.fromWebContents(this[WEBCONTENTS]), options, (filename: string) => {
resolve(filename) resolve(filename)
}) })
}) })
@@ -57,16 +63,16 @@ class GuiApi {
* for plugin to upload file * for plugin to upload file
* @param {array} input * @param {array} input
*/ */
async upload (input) { async upload (input: []) {
const imgs = await new Uploader(input, this[WEBCONTENTS], this[PICGO]).upload() const imgs = await new Uploader(input, this[WEBCONTENTS], this[PICGO]).upload()
if (imgs !== false) { if (imgs !== false) {
const pasteStyle = db.get('settings.pasteStyle') || 'markdown' const pasteStyle = db.get('settings.pasteStyle') || 'markdown'
let pasteText = '' let pasteText = ''
for (let i in imgs) { for (let i = 0; i < imgs.length; i++) {
pasteText += pasteTemplate(pasteStyle, imgs[i]) + '\r\n' pasteText += pasteTemplate(pasteStyle, imgs[i]) + '\r\n'
const notification = new Notification({ const notification = new Notification({
title: '上传成功', title: '上传成功',
body: imgs[i].imgUrl, body: imgs[i].imgUrl as string,
icon: imgs[i].imgUrl icon: imgs[i].imgUrl
}) })
setTimeout(() => { setTimeout(() => {
@@ -110,13 +116,13 @@ class GuiApi {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
dialog.showMessageBox( dialog.showMessageBox(
BrowserWindow.fromWebContents(this[WEBCONTENTS]), BrowserWindow.fromWebContents(this[WEBCONTENTS]),
options, options
(result, checkboxChecked) => { ).then((res) => {
resolve({ resolve({
result, result: res.response,
checkboxChecked checkboxChecked: res.checkboxChecked
})
}) })
})
}) })
} }
} }
@@ -1,5 +1,9 @@
import path from 'path' import path from 'path'
import fs from 'fs-extra' import fs from 'fs-extra'
type ClipboardFileObject = {
path: string
}
type Result = ClipboardFileObject[]
const getUploadFiles = (argv = process.argv, cwd = process.cwd()) => { const getUploadFiles = (argv = process.argv, cwd = process.cwd()) => {
let files = argv.slice(1) let files = argv.slice(1)
if (files.length > 0 && files[0] === 'upload') { if (files.length > 0 && files[0] === 'upload') {
@@ -7,7 +11,7 @@ const getUploadFiles = (argv = process.argv, cwd = process.cwd()) => {
return null // for uploading images in clipboard return null // for uploading images in clipboard
} else if (files.length > 1) { } else if (files.length > 1) {
files = argv.slice(1) files = argv.slice(1)
let result = [] let result: Result = []
if (files.length > 0) { if (files.length > 0) {
result = files.map(item => { result = files.map(item => {
if (path.isAbsolute(item)) { if (path.isAbsolute(item)) {
@@ -24,7 +28,7 @@ const getUploadFiles = (argv = process.argv, cwd = process.cwd()) => {
return null return null
} }
} }
}).filter(item => item !== null) }).filter(item => item !== null) as Result
} }
return result return result
} }
@@ -1,16 +1,27 @@
import path from 'path' import path from 'path'
import GuiApi from './guiApi' import GuiApi from './guiApi'
import { dialog, shell } from 'electron' import { dialog, shell, IpcMain, IpcMainEvent, App } from 'electron'
import db from '../../datastore' import db from '#/datastore'
import PicGoCore from '~/universal/types/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
const PicGo = requireFunc('picgo') const PicGo = requireFunc('picgo') as typeof PicGoCore
const PluginHandler = requireFunc('picgo/dist/lib/PluginHandler').default const PluginHandler = requireFunc('picgo/dist/lib/PluginHandler').default
type PicGoNotice = {
title: string,
body: string[]
}
interface GuiMenuItem {
label: string
handle: (arg0: PicGoCore, arg1: GuiApi) => Promise<void>
}
// get uploader or transformer config // get uploader or transformer config
const getConfig = (name, type, ctx) => { const getConfig = (name: string, type: PicGoHelperType, ctx: PicGoCore) => {
let config = [] let config: any[] = []
if (name === '') { if (name === '') {
return config return config
} else { } else {
@@ -24,7 +35,7 @@ const getConfig = (name, type, ctx) => {
} }
} }
const handleConfigWithFunction = config => { const handleConfigWithFunction = (config: any[]) => {
for (let i in config) { for (let i in config) {
if (typeof config[i].default === 'function') { if (typeof config[i].default === 'function') {
config[i].default = config[i].default() config[i].default = config[i].default()
@@ -36,8 +47,8 @@ const handleConfigWithFunction = config => {
return config return config
} }
const handleGetPluginList = (ipcMain, STORE_PATH, CONFIG_PATH) => { const handleGetPluginList = (ipcMain: IpcMain, STORE_PATH: string, CONFIG_PATH: string) => {
ipcMain.on('getPluginList', event => { ipcMain.on('getPluginList', (event: IpcMainEvent) => {
const picgo = new PicGo(CONFIG_PATH) const picgo = new PicGo(CONFIG_PATH)
const pluginList = picgo.pluginLoader.getList() const pluginList = picgo.pluginLoader.getList()
const list = [] const list = []
@@ -57,7 +68,7 @@ const handleGetPluginList = (ipcMain, STORE_PATH, CONFIG_PATH) => {
gui = true gui = true
} }
} }
const obj = { const obj: IPicGoPlugin = {
name: pluginList[i].replace(/picgo-plugin-/, ''), name: pluginList[i].replace(/picgo-plugin-/, ''),
author: pluginPKG.author.name || pluginPKG.author, author: pluginPKG.author.name || pluginPKG.author,
description: pluginPKG.description, description: pluginPKG.description,
@@ -71,11 +82,11 @@ const handleGetPluginList = (ipcMain, STORE_PATH, CONFIG_PATH) => {
}, },
uploader: { uploader: {
name: uploaderName, name: uploaderName,
config: handleConfigWithFunction(getConfig(uploaderName, 'uploader', picgo)) config: handleConfigWithFunction(getConfig(uploaderName, PicGoHelperType.uploader, picgo))
}, },
transformer: { transformer: {
name: transformerName, name: transformerName,
config: handleConfigWithFunction(getConfig(uploaderName, 'transformer', picgo)) config: handleConfigWithFunction(getConfig(uploaderName, PicGoHelperType.transformer, picgo))
} }
}, },
enabled: picgo.getConfig(`picgoPlugins.${pluginList[i]}`), enabled: picgo.getConfig(`picgoPlugins.${pluginList[i]}`),
@@ -90,11 +101,11 @@ const handleGetPluginList = (ipcMain, STORE_PATH, CONFIG_PATH) => {
}) })
} }
const handlePluginInstall = (ipcMain, CONFIG_PATH) => { const handlePluginInstall = (ipcMain: IpcMain, CONFIG_PATH: string) => {
ipcMain.on('installPlugin', async (event, msg) => { ipcMain.on('installPlugin', async (event: IpcMainEvent, msg: string) => {
const picgo = new PicGo(CONFIG_PATH) const picgo = new PicGo(CONFIG_PATH)
const pluginHandler = new PluginHandler(picgo) const pluginHandler = new PluginHandler(picgo)
picgo.on('installSuccess', notice => { picgo.on('installSuccess', (notice: PicGoNotice) => {
event.sender.send('installSuccess', notice.body[0].replace(/picgo-plugin-/, '')) event.sender.send('installSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
}) })
picgo.on('failed', () => { picgo.on('failed', () => {
@@ -106,11 +117,11 @@ const handlePluginInstall = (ipcMain, CONFIG_PATH) => {
}) })
} }
const handlePluginUninstall = (ipcMain, CONFIG_PATH) => { const handlePluginUninstall = (ipcMain: IpcMain, CONFIG_PATH: string) => {
ipcMain.on('uninstallPlugin', async (event, msg) => { ipcMain.on('uninstallPlugin', async (event: IpcMainEvent, msg: string) => {
const picgo = new PicGo(CONFIG_PATH) const picgo = new PicGo(CONFIG_PATH)
const pluginHandler = new PluginHandler(picgo) const pluginHandler = new PluginHandler(picgo)
picgo.on('uninstallSuccess', notice => { picgo.on('uninstallSuccess', (notice: PicGoNotice) => {
event.sender.send('uninstallSuccess', notice.body[0].replace(/picgo-plugin-/, '')) event.sender.send('uninstallSuccess', notice.body[0].replace(/picgo-plugin-/, ''))
}) })
picgo.on('failed', () => { picgo.on('failed', () => {
@@ -121,8 +132,8 @@ const handlePluginUninstall = (ipcMain, CONFIG_PATH) => {
}) })
} }
const handlePluginUpdate = (ipcMain, CONFIG_PATH) => { const handlePluginUpdate = (ipcMain: IpcMain, CONFIG_PATH: string) => {
ipcMain.on('updatePlugin', async (event, msg) => { ipcMain.on('updatePlugin', async (event: IpcMainEvent, msg: string) => {
const picgo = new PicGo(CONFIG_PATH) const picgo = new PicGo(CONFIG_PATH)
const pluginHandler = new PluginHandler(picgo) const pluginHandler = new PluginHandler(picgo)
picgo.on('updateSuccess', notice => { picgo.on('updateSuccess', notice => {
@@ -141,15 +152,15 @@ const handleNPMError = () => {
title: '发生错误', title: '发生错误',
message: '请安装Node.js并重启PicGo再继续操作', message: '请安装Node.js并重启PicGo再继续操作',
buttons: ['Yes'] buttons: ['Yes']
}, (res) => { }).then((res) => {
if (res === 0) { if (res.response === 0) {
shell.openExternal('https://nodejs.org/') shell.openExternal('https://nodejs.org/')
} }
}) })
} }
const handleGetPicBedConfig = (ipcMain, CONFIG_PATH) => { const handleGetPicBedConfig = (ipcMain: IpcMain, CONFIG_PATH: string) => {
ipcMain.on('getPicBedConfig', (event, type) => { ipcMain.on('getPicBedConfig', (event: IpcMainEvent, type: string) => {
const picgo = new PicGo(CONFIG_PATH) const picgo = new PicGo(CONFIG_PATH)
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) {
@@ -162,13 +173,13 @@ const handleGetPicBedConfig = (ipcMain, CONFIG_PATH) => {
}) })
} }
const handlePluginActions = (ipcMain, CONFIG_PATH) => { const handlePluginActions = (ipcMain: IpcMain, CONFIG_PATH: string) => {
ipcMain.on('pluginActions', (event, name, label) => { ipcMain.on('pluginActions', (event: IpcMainEvent, name: string, label: string) => {
const picgo = new PicGo(CONFIG_PATH) const picgo = new PicGo(CONFIG_PATH)
const plugin = picgo.pluginLoader.getPlugin(`picgo-plugin-${name}`) const plugin = picgo.pluginLoader.getPlugin(`picgo-plugin-${name}`)
const guiApi = new GuiApi(ipcMain, event.sender, picgo) const guiApi = new GuiApi(ipcMain, event.sender, picgo)
if (plugin.guiMenu && plugin.guiMenu(picgo).length > 0) { if (plugin.guiMenu && plugin.guiMenu(picgo).length > 0) {
const menu = plugin.guiMenu(picgo) const menu: GuiMenuItem[] = plugin.guiMenu(picgo)
menu.forEach(item => { menu.forEach(item => {
if (item.label === label) { if (item.label === label) {
item.handle(picgo, guiApi) item.handle(picgo, guiApi)
@@ -178,8 +189,8 @@ const handlePluginActions = (ipcMain, CONFIG_PATH) => {
}) })
} }
const handleRemoveFiles = (ipcMain, CONFIG_PATH) => { const handleRemoveFiles = (ipcMain: IpcMain, CONFIG_PATH: string) => {
ipcMain.on('removeFiles', (event, files) => { ipcMain.on('removeFiles', (event: IpcMainEvent, files: ImgInfo[]) => {
const picgo = new PicGo(CONFIG_PATH) const picgo = new PicGo(CONFIG_PATH)
const guiApi = new GuiApi(ipcMain, event.sender, picgo) const guiApi = new GuiApi(ipcMain, event.sender, picgo)
setTimeout(() => { setTimeout(() => {
@@ -188,18 +199,18 @@ const handleRemoveFiles = (ipcMain, CONFIG_PATH) => {
}) })
} }
const handlePluginShortKeyRegister = (plugin) => { // const handlePluginShortKeyRegister = (plugin) => {
if (plugin.shortKeys && plugin.shortKeys.length > 0) { // if (plugin.shortKeys && plugin.shortKeys.length > 0) {
let shortKeyConfig = db.get('settings.shortKey') // let shortKeyConfig = db.get('settings.shortKey')
plugin.shortKeys.forEach(item => { // plugin.shortKeys.forEach(item => {
if (!shortKeyConfig[item.name]) { // if (!shortKeyConfig[item.name]) {
shortKeyConfig[item.name] = item // shortKeyConfig[item.name] = item
} // }
}) // })
} // }
} // }
export default (app, ipcMain) => { export default (app: App, ipcMain: IpcMain) => {
const STORE_PATH = app.getPath('userData') const STORE_PATH = app.getPath('userData')
const CONFIG_PATH = path.join(STORE_PATH, '/data.json') const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
handleGetPluginList(ipcMain, STORE_PATH, CONFIG_PATH) handleGetPluginList(ipcMain, STORE_PATH, CONFIG_PATH)
@@ -209,5 +220,5 @@ export default (app, ipcMain) => {
handleGetPicBedConfig(ipcMain, CONFIG_PATH) handleGetPicBedConfig(ipcMain, CONFIG_PATH)
handlePluginActions(ipcMain, CONFIG_PATH) handlePluginActions(ipcMain, CONFIG_PATH)
handleRemoveFiles(ipcMain, CONFIG_PATH) handleRemoveFiles(ipcMain, CONFIG_PATH)
handlePluginShortKeyRegister({}) // handlePluginShortKeyRegister({})
} }
@@ -1,4 +1,7 @@
import bus from './eventBus' import bus from './eventBus'
import {
GlobalShortcut
} from 'electron'
let isInModifiedMode = false // 修改快捷键模式 let isInModifiedMode = false // 修改快捷键模式
bus.on('toggleShortKeyModifiedMode', flag => { bus.on('toggleShortKeyModifiedMode', flag => {
isInModifiedMode = flag isInModifiedMode = flag
@@ -7,7 +10,7 @@ bus.on('toggleShortKeyModifiedMode', flag => {
* *
* @param {string} name * @param {string} name
*/ */
const shortKeyHandler = (name) => { const shortKeyHandler = (name: string) => {
if (isInModifiedMode) { if (isInModifiedMode) {
return return
} }
@@ -20,11 +23,8 @@ const shortKeyHandler = (name) => {
/** /**
* *
* @param {globalShortcut} globalShortcut
* @param {keyObject} item
* @param {string} oldKey
*/ */
const shortKeyUpdater = (globalShortcut, item, oldKey) => { const shortKeyUpdater = (globalShortcut: GlobalShortcut, item: ShortKeyConfig, oldKey: string) => {
// 如果提供了旧key,则解绑 // 如果提供了旧key,则解绑
if (oldKey) { if (oldKey) {
globalShortcut.unregister(oldKey) globalShortcut.unregister(oldKey)
@@ -39,7 +39,7 @@ const shortKeyUpdater = (globalShortcut, item, oldKey) => {
} }
// 初始化阶段的注册 // 初始化阶段的注册
const initShortKeyRegister = (globalShortcut, shortKeys) => { const initShortKeyRegister = (globalShortcut: GlobalShortcut, shortKeys: ShortKeyConfig[]) => {
let errorList = [] let errorList = []
for (let i in shortKeys) { for (let i in shortKeys) {
try { try {
@@ -1,10 +1,15 @@
import { dialog, shell } from 'electron' import { dialog, shell } from 'electron'
import db from '../../datastore' import db from '#/datastore'
import axios from 'axios' import axios from 'axios'
import pkg from '../../../package.json' import pkg from 'root/package.json'
const version = pkg.version const version = pkg.version
const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest' let release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest' const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
const isDevelopment = process.env.NODE_ENV !== 'production'
if (isDevelopment) {
release = `${release}?access_token=${process.env.GITHUB_TOKEN}`
}
const checkVersion = async () => { const checkVersion = async () => {
let showTip = db.get('settings.showUpdateTip') let showTip = db.get('settings.showUpdateTip')
@@ -13,7 +18,12 @@ const checkVersion = async () => {
showTip = true showTip = true
} }
if (showTip) { if (showTip) {
const res = await axios.get(release) let res: any
try {
res = await axios.get(release)
} catch (err) {
console.log(err)
}
if (res.status === 200) { if (res.status === 200) {
const latest = res.data.name const latest = res.data.name
const result = compareVersion2Update(version, latest) const result = compareVersion2Update(version, latest)
@@ -25,11 +35,11 @@ const checkVersion = async () => {
message: `发现新版本${latest},更新了很多功能,是否去下载最新的版本?`, message: `发现新版本${latest},更新了很多功能,是否去下载最新的版本?`,
checkboxLabel: '以后不再提醒', checkboxLabel: '以后不再提醒',
checkboxChecked: false checkboxChecked: false
}, (res, checkboxChecked) => { }).then(res => {
if (res === 0) { // if selected yes if (res.response === 0) { // if selected yes
shell.openExternal(downloadUrl) shell.openExternal(downloadUrl)
} }
db.set('settings.showUpdateTip', !checkboxChecked) db.set('settings.showUpdateTip', !res.checkboxChecked)
}) })
} }
} else { } else {
@@ -41,7 +51,7 @@ const checkVersion = async () => {
} }
// if true -> update else return false // if true -> update else return false
const compareVersion2Update = (current, latest) => { const compareVersion2Update = (current: string, latest: string) => {
const currentVersion = current.split('.').map(item => parseInt(item)) const currentVersion = current.split('.').map(item => parseInt(item))
const latestVersion = latest.split('.').map(item => parseInt(item)) const latestVersion = latest.split('.').map(item => parseInt(item))
@@ -2,20 +2,39 @@ import {
app, app,
Notification, Notification,
BrowserWindow, BrowserWindow,
ipcMain ipcMain,
WebContents
} from 'electron' } from 'electron'
import path from 'path' import path from 'path'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import PicGoCore from '~/universal/types/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
const PicGo = requireFunc('picgo') const PicGo = requireFunc('picgo') as typeof PicGoCore
const STORE_PATH = app.getPath('userData') const STORE_PATH = app.getPath('userData')
const CONFIG_PATH = path.join(STORE_PATH, '/data.json') const CONFIG_PATH = path.join(STORE_PATH, '/data.json')
const renameURL = process.env.NODE_ENV === 'development' ? `http://localhost:9080/#rename-page` : `file://${__dirname}/index.html#rename-page` const renameURL = process.env.NODE_ENV === 'development' ? `http://localhost:9080/#rename-page` : `file://${__dirname}/index.html#rename-page`
const createRenameWindow = (win) => { // type BrowserWindowOptions = {
let options = { // height: number,
// width: number,
// show: boolean,
// fullscreenable: boolean,
// resizable: boolean,
// vibrancy: string | any,
// webPreferences: {
// nodeIntegration: boolean,
// nodeIntegrationInWorker: boolean,
// backgroundThrottling: boolean
// },
// backgroundColor?: string
// autoHideMenuBar?: boolean
// transparent?: boolean
// }
const createRenameWindow = (win: BrowserWindow) => {
let options: BrowserWindowOptions = {
height: 175, height: 175,
width: 300, width: 300,
show: true, show: true,
@@ -55,7 +74,7 @@ const createRenameWindow = (win) => {
return window return window
} }
const waitForShow = (webcontent) => { const waitForShow = (webcontent: WebContents) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
webcontent.on('dom-ready', () => { webcontent.on('dom-ready', () => {
resolve() resolve()
@@ -63,9 +82,9 @@ const waitForShow = (webcontent) => {
}) })
} }
const waitForRename = (window, id) => { const waitForRename = (window: BrowserWindow, id: number): Promise<string|null> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
ipcMain.once(`rename${id}`, (evt, newName) => { ipcMain.once(`rename${id}`, (evt: Event, newName: string) => {
resolve(newName) resolve(newName)
window.hide() window.hide()
}) })
@@ -77,15 +96,18 @@ const waitForRename = (window, id) => {
} }
class Uploader { class Uploader {
constructor (img, webContents, picgo = undefined) { private picgo: PicGoCore
private webContents: WebContents
private img: undefined | string[]
constructor (img: undefined | string[], webContents: WebContents, picgo: PicGoCore | undefined = undefined) {
this.img = img this.img = img
this.webContents = webContents this.webContents = webContents
this.picgo = picgo this.picgo = picgo || new PicGo(CONFIG_PATH)
} }
upload () { upload (): Promise<ImgInfo[]|false> {
const win = BrowserWindow.fromWebContents(this.webContents) const win = BrowserWindow.fromWebContents(this.webContents)
const picgo = this.picgo || new PicGo(CONFIG_PATH) const picgo = this.picgo
picgo.config.debug = true picgo.config.debug = true
// for picgo-core // for picgo-core
picgo.config.PICGO_ENV = 'GUI' picgo.config.PICGO_ENV = 'GUI'
@@ -96,8 +118,8 @@ class Uploader {
const rename = picgo.getConfig('settings.rename') const rename = picgo.getConfig('settings.rename')
const autoRename = picgo.getConfig('settings.autoRename') const autoRename = picgo.getConfig('settings.autoRename')
await Promise.all(ctx.output.map(async (item, index) => { await Promise.all(ctx.output.map(async (item, index) => {
let name let name: undefined | string | null
let fileName let fileName: string | undefined
if (autoRename) { if (autoRename) {
fileName = dayjs().add(index, 'second').format('YYYYMMDDHHmmss') + item.extname fileName = dayjs().add(index, 'second').format('YYYYMMDDHHmmss') + item.extname
} else { } else {
@@ -137,7 +159,7 @@ class Uploader {
return new Promise((resolve) => { return new Promise((resolve) => {
picgo.on('finished', ctx => { picgo.on('finished', ctx => {
if (ctx.output.every(item => item.imgUrl)) { if (ctx.output.every((item: ImgInfo) => item.imgUrl)) {
resolve(ctx.output) resolve(ctx.output)
} else { } else {
resolve(false) resolve(false)
+3 -3
View File
@@ -5,9 +5,9 @@
</template> </template>
<script> <script>
export default { export default {
name: 'picgo' name: 'picgo'
} }
</script> </script>
<style lang="stylus"> <style lang="stylus">
+1 -1
View File
@@ -34,4 +34,4 @@ export default {
} }
</script> </script>
<style lang='stylus'> <style lang='stylus'>
</style> </style>
+53 -58
View File
@@ -58,68 +58,63 @@
</el-form> </el-form>
</div> </div>
</template> </template>
<script> <script lang="ts">
import {
Component,
Vue,
Prop,
Watch
} from 'vue-property-decorator'
import { cloneDeep, union } from 'lodash' import { cloneDeep, union } from 'lodash'
export default {
name: 'config-form', @Component({
props: { name: 'config-form'
config: Array, })
type: String, export default class extends Vue {
id: { @Prop(Array) readonly config: Array<any> = []
type: String, @Prop(String) readonly type: string = ''
default: '' @Prop(String) readonly id: string = ''
} configList = []
}, ruleForm = {}
data () { @Watch('config', {
return { deep: true,
configList: [], immediate: true
ruleForm: {} })
} handleConfigChange (val: any) {
}, this.ruleForm = Object.assign({}, {})
created () { const config = this.$db.get(`picBed.${this.id}`)
}, if (val.length > 0) {
watch: { this.configList = cloneDeep(val).map((item: any) => {
config: { let defaultValue = item.default !== undefined
deep: true, ? item.default : item.type === 'checkbox'
handler (val) { ? [] : null
this.ruleForm = Object.assign({}, {}) if (item.type === 'checkbox') {
const config = this.$db.get(`picBed.${this.id}`) const defaults = item.choices.filter((i: any) => {
if (val.length > 0) { return i.checked
this.configList = cloneDeep(val).map(item => { }).map((i: any) => i.value)
let defaultValue = item.default !== undefined defaultValue = union(defaultValue, defaults)
? item.default : item.type === 'checkbox'
? [] : null
if (item.type === 'checkbox') {
const defaults = item.choices.filter(i => {
return i.checked
}).map(i => i.value)
defaultValue = union(defaultValue, defaults)
}
if (config && config[item.name] !== undefined) {
defaultValue = config[item.name]
}
this.$set(this.ruleForm, item.name, defaultValue)
return item
})
} }
}, if (config && config[item.name] !== undefined) {
immediate: true defaultValue = config[item.name]
} }
}, this.$set(this.ruleForm, item.name, defaultValue)
methods: { return item
async validate () {
return new Promise((resolve, reject) => {
this.$refs.form.validate(valid => {
if (valid) {
resolve(this.ruleForm)
} else {
resolve(false)
return false
}
})
}) })
} }
} }
async validate () {
return new Promise((resolve, reject) => {
// @ts-ignore
this.$refs.form.validate((valid: boolean) => {
if (valid) {
resolve(this.ruleForm)
} else {
resolve(false)
return false
}
})
})
}
} }
</script> </script>
<style lang='stylus'> <style lang='stylus'>
@@ -139,4 +134,4 @@ export default {
.el-switch__label .el-switch__label
&.is-active &.is-active
color #409EFF color #409EFF
</style> </style>
+138 -139
View File
@@ -105,7 +105,7 @@
label="用占位符$url来表示url的位置" label="用占位符$url来表示url的位置"
prop="value" prop="value"
> >
<el-input <el-input
class="align-center" class="align-center"
v-model="customLink.value" v-model="customLink.value"
:autofocus="true" :autofocus="true"
@@ -136,161 +136,160 @@
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
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 } from 'electron' import { remote, ipcRenderer, IpcRendererEvent } from 'electron'
import db from '~/datastore' import db from '#/datastore'
import mixin from '@/utils/mixin' import mixin from '@/utils/mixin'
const { Menu, dialog, BrowserWindow } = remote const { Menu, dialog, BrowserWindow } = remote
export default { const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) => void) => {
if (!/\$url/.test(value)) {
return callback(new Error('必须含有$url'))
} else {
return callback()
}
}
@Component({
name: 'setting-page', name: 'setting-page',
mixins: [mixin], mixins: [mixin]
data () { })
const customLinkRule = (rule, value, callback) => { export default class extends Vue {
if (!/\$url/.test(value)) { version = process.env.NODE_ENV === 'production' ? pkg.version : 'Dev'
return callback(new Error('必须含有$url')) defaultActive = 'upload'
} else { menu: Electron.Menu | null = null
return callback() visible = false
} keyBindingVisible = false
} customLinkVisible = false
return { customLink = {
version: process.env.NODE_ENV === 'production' ? pkg.version : 'Dev', value: db.get('customLink') || '$url'
defaultActive: 'upload', }
menu: null, rules = {
visible: false, value: [
keyBindingVisible: false, { validator: customLinkRule, trigger: 'blur' }
customLinkVisible: false, ]
customLink: { }
value: db.get('customLink') || '$url' os = ''
}, shortKey: ShortKeyMap = {
rules: { upload: db.get('shortKey.upload')
value: [ }
{ validator: customLinkRule, trigger: 'blur' } picBed: PicBedType[] = []
] // for showInputBox
}, showInputBoxVisible = false
os: '', inputBoxValue = ''
shortKey: { inputBoxOptions = {
upload: db.get('shortKey.upload') title: '',
}, placeholder: ''
picBed: [], }
// for showInputBox
showInputBoxVisible: false,
inputBoxValue: '',
inputBoxOptions: {
title: '',
placeholder: ''
}
}
},
created () { created () {
this.os = process.platform this.os = process.platform
this.buildMenu() this.buildMenu()
this.$electron.ipcRenderer.send('getPicBeds') ipcRenderer.send('getPicBeds')
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds) ipcRenderer.on('getPicBeds', this.getPicBeds)
this.$electron.ipcRenderer.on('showInputBox', (evt, options) => { ipcRenderer.on('showInputBox', (evt: IpcRendererEvent, options: IShowInputBoxOption) => {
this.inputBoxValue = '' this.inputBoxValue = ''
this.inputBoxOptions.title = options.title || '' this.inputBoxOptions.title = options.title || ''
this.inputBoxOptions.placeholder = options.placeholder || '' this.inputBoxOptions.placeholder = options.placeholder || ''
this.showInputBoxVisible = true this.showInputBoxVisible = true
}) })
}, }
methods: { handleSelect (index: string) {
handleSelect (index) { const type = index.match(/picbeds-/)
const type = index.match(/picbeds-/) if (type === null) {
if (type === null) { this.$router.push({
name: index
})
} else {
const picBed = index.replace(/picbeds-/, '')
if (this.$builtInPicBed.includes(picBed)) {
this.$router.push({ this.$router.push({
name: index name: picBed
}) })
} else { } else {
const picBed = index.replace(/picbeds-/, '') this.$router.push({
if (this.$builtInPicBed.includes(picBed)) { name: 'others',
this.$router.push({ params: {
name: picBed type: picBed
}) }
} else { })
this.$router.push({ }
name: 'others', }
params: { }
type: picBed minimizeWindow () {
} const window = BrowserWindow.getFocusedWindow()
window!.minimize()
}
closeWindow () {
const window = BrowserWindow.getFocusedWindow()
window!.close()
}
buildMenu () {
const _this = this
const template = [
{
label: '关于',
click () {
dialog.showMessageBox({
title: 'PicGo',
message: 'PicGo',
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo`
}) })
} }
},
{
label: '赞助PicGo',
click () {
_this.visible = true
}
} }
}, ]
minimizeWindow () { this.menu = Menu.buildFromTemplate(template)
const window = BrowserWindow.getFocusedWindow() }
window.minimize() openDialog () {
}, // this.menu!.popup(remote.getCurrentWindow())
closeWindow () { this.menu!.popup()
const window = BrowserWindow.getFocusedWindow() }
window.close() keyDetect (type: string, event: KeyboardEvent) {
}, this.shortKey[type] = keyDetect(event).join('+')
buildMenu () { }
const _this = this cancelKeyBinding () {
const template = [ this.keyBindingVisible = false
{ this.shortKey = db.get('shortKey')
label: '关于', }
click () { cancelCustomLink () {
dialog.showMessageBox({ this.customLinkVisible = false
title: 'PicGo', this.customLink.value = db.get('customLink') || '$url'
message: 'PicGo', }
detail: `Version: ${pkg.version}\nAuthor: Molunerfinn\nGithub: https://github.com/Molunerfinn/PicGo` confirmCustomLink () {
}) // @ts-ignore
} this.$refs.customLink.validate((valid: boolean) => {
}, if (valid) {
{ db.set('customLink', this.customLink.value)
label: '赞助PicGo', this.customLinkVisible = false
click () { ipcRenderer.send('updateCustomLink')
_this.visible = true } else {
} return false
} }
] })
this.menu = Menu.buildFromTemplate(template) }
}, openMiniWindow () {
openDialog () { ipcRenderer.send('openMiniWindow')
this.menu.popup(remote.getCurrentWindow()) }
}, getPicBeds (event: IpcRendererEvent, picBeds: PicBedType[]) {
keyDetect (type, event) { this.picBed = picBeds
this.shortKey[type] = keyDetect(event).join('+') }
}, handleInputBoxClose () {
cancelKeyBinding () { ipcRenderer.send('showInputBox', this.inputBoxValue)
this.keyBindingVisible = false }
this.shortKey = db.get('shortKey') beforeRouteEnter (to: any, from: any, next: any) {
}, next((vm: this) => {
cancelCustomLink () {
this.customLinkVisible = false
this.customLink.value = db.get('customLink') || '$url'
},
confirmCustomLink () {
this.$refs.customLink.validate((valid) => {
if (valid) {
db.set('customLink', this.customLink.value)
this.customLinkVisible = false
this.$electron.ipcRenderer.send('updateCustomLink')
} else {
return false
}
})
},
openMiniWindow () {
this.$electron.ipcRenderer.send('openMiniWindow')
},
getPicBeds (event, picBeds) {
this.picBed = picBeds
},
handleInputBoxClose () {
this.$electron.ipcRenderer.send('showInputBox', this.inputBoxValue)
}
},
beforeRouteEnter: (to, from, next) => {
next(vm => {
vm.defaultActive = to.name vm.defaultActive = to.name
}) })
}, }
beforeDestroy () { beforeDestroy () {
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds) ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
this.$electron.ipcRenderer.removeAllListeners('showInputBox') ipcRenderer.removeAllListeners('showInputBox')
} }
} }
</script> </script>
@@ -360,7 +359,7 @@ $darwinBg = transparentify(#172426, #000, 0.7)
overflow-y auto overflow-y auto
width 170px width 170px
.el-icon-info.setting-window .el-icon-info.setting-window
position fixed position fixed
bottom 4px bottom 4px
left 4px left 4px
cursor poiter cursor poiter
@@ -384,7 +383,7 @@ $darwinBg = transparentify(#172426, #000, 0.7)
&:before &:before
content '' content ''
position absolute position absolute
width 3px width 3px
height 20px height 20px
right 0 right 0
top 18px top 18px
@@ -406,7 +405,7 @@ $darwinBg = transparentify(#172426, #000, 0.7)
padding-top 22px padding-top 22px
position relative position relative
z-index 10 z-index 10
.el-dialog__body .el-dialog__body
padding 20px padding 20px
.support .support
text-align center text-align center
@@ -424,4 +423,4 @@ $darwinBg = transparentify(#172426, #000, 0.7)
background #6f6f6f background #6f6f6f
*::-webkit-scrollbar-track *::-webkit-scrollbar-track
background-color transparent background-color transparent
</style> </style>
+223 -214
View File
@@ -72,7 +72,7 @@
:options="options" :options="options"
></gallerys> ></gallerys>
<el-col :span="6" v-for="(item, index) in images" :key="item.id" class="gallery-list__img"> <el-col :span="6" v-for="(item, index) in images" :key="item.id" class="gallery-list__img">
<div <div
class="gallery-list__item" class="gallery-list__item"
@click="zoomImage(index)" @click="zoomImage(index)"
> >
@@ -83,7 +83,7 @@
<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=" handleBarActive = true"></el-checkbox>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
</el-col> </el-col>
@@ -102,257 +102,266 @@
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script lang="ts">
// @ts-ignore
import gallerys from 'vue-gallery' import gallerys from 'vue-gallery'
import pasteStyle from '~/main/utils/pasteTemplate' import pasteStyle from '#/utils/pasteTemplate'
export default { import { Component, Vue } from 'vue-property-decorator'
import {
ipcRenderer,
clipboard,
IpcRendererEvent
} from 'electron'
@Component({
name: 'gallery', name: 'gallery',
components: { components: {
gallerys gallerys
}, }
data () { })
return { export default class extends Vue {
images: [], images: ImgInfo[] = []
idx: null, idx: null | number = null
options: { options = {
titleProperty: 'fileName', titleProperty: 'fileName',
urlProperty: 'imgUrl', urlProperty: 'imgUrl',
closeOnSlideClick: true closeOnSlideClick: true
}, }
dialogVisible: false, dialogVisible = false
imgInfo: { imgInfo = {
id: null, id: null,
imgUrl: '' imgUrl: ''
}, }
choosedList: {}, choosedList: ObjT<boolean> = {}
choosedPicBed: [], choosedPicBed: string[] = []
searchText: '', searchText = ''
handleBarActive: false, handleBarActive = false
pasteStyle: '', pasteStyle = ''
pasteStyleMap: { pasteStyleMap = {
Markdown: 'markdown', Markdown: 'markdown',
HTML: 'HTML', HTML: 'HTML',
URL: 'URL', URL: 'URL',
UBB: 'UBB', UBB: 'UBB',
Custom: 'Custom' Custom: 'Custom'
}, }
picBed: [] picBed: PicBedType[] = []
} beforeRouteEnter (to: any, from: any, next: any) {
}, next((vm: any) => {
beforeRouteEnter (to, from, next) {
next(vm => {
vm.getGallery() vm.getGallery()
vm.getPasteStyle() vm.getPasteStyle()
vm.getPicBeds() vm.getPicBeds()
}) })
}, }
created () { created () {
this.$electron.ipcRenderer.on('updateGallery', (event) => { ipcRenderer.on('updateGallery', (event: IpcRendererEvent) => {
this.$nextTick(() => { this.$nextTick(() => {
this.filterList = this.getGallery() this.filterList = this.getGallery()
}) })
}) })
this.$electron.ipcRenderer.send('getPicBeds') ipcRenderer.send('getPicBeds')
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds) ipcRenderer.on('getPicBeds', this.getPicBeds)
}, }
computed: { get filterList () {
filterList: { return this.getGallery()
get () { }
return this.getGallery() set filterList (val) {
}, this.images = val
set (val) { }
return this.val getPicBeds (event: IpcRendererEvent, picBeds: PicBedType[]) {
this.picBed = picBeds
}
getGallery () {
if (this.choosedPicBed.length > 0) {
let arr: ImgInfo[] = []
this.choosedPicBed.forEach(item => {
let obj: Obj = {
type: item
}
if (this.searchText) {
obj.fileName = this.searchText
}
// @ts-ignore
arr = arr.concat(this.$db.read().get('uploaded').filter(obj => {
return obj.fileName.indexOf(this.searchText) !== -1 && obj.type === item
}).reverse().value())
})
this.images = arr
} else {
if (this.searchText) {
let data = this.$db.read().get('uploaded')
// @ts-ignore
.filter(item => {
return item.fileName.indexOf(this.searchText) !== -1
}).reverse().value()
this.images = data
} else {
// @ts-ignore
this.images = this.$db.read().get('uploaded').slice().reverse().value()
} }
} }
}, return this.images
methods: { }
getPicBeds (event, picBeds) { zoomImage (index: number) {
this.picBed = picBeds this.idx = index
}, this.changeZIndexForGallery(true)
getGallery () { }
if (this.choosedPicBed.length > 0) { changeZIndexForGallery (isOpen: boolean) {
let arr = [] if (isOpen) {
this.choosedPicBed.forEach(item => { // @ts-ignore
let obj = { document.querySelector('.main-content.el-row').style.zIndex = 101
type: item } else {
} // @ts-ignore
if (this.searchText) { document.querySelector('.main-content.el-row').style.zIndex = 10
obj.fileName = this.searchText }
} }
arr = arr.concat(this.$db.read().get('uploaded').filter(obj => { handleClose () {
return obj.fileName.indexOf(this.searchText) !== -1 && obj.type === item this.idx = null
}).reverse().value()) this.changeZIndexForGallery(false)
}) }
this.images = arr copy (item: ImgInfo) {
} else { const style = this.$db.get('settings.pasteStyle') || 'markdown'
if (this.searchText) { const copyLink = pasteStyle(style, item)
let data = this.$db.read().get('uploaded') const obj = {
.filter(item => { title: '复制链接成功',
return item.fileName.indexOf(this.searchText) !== -1 body: copyLink,
}).reverse().value() icon: item.url || item.imgUrl
this.images = data }
} else { const myNotification = new Notification(obj.title, obj)
this.images = this.$db.read().get('uploaded').slice().reverse().value() clipboard.writeText(copyLink)
} myNotification.onclick = () => {
} return true
return this.images }
}, }
zoomImage (index) { remove (id: string) {
this.idx = index this.$confirm('此操作将把该图片移出相册, 是否继续?', '提示', {
this.changeZIndexForGallery(true) confirmButtonText: '确定',
}, cancelButtonText: '取消',
changeZIndexForGallery (isOpen) { type: 'warning'
if (isOpen) { }).then(() => {
document.querySelector('.main-content.el-row').style.zIndex = 101 const file = this.$db.get('uploaded').getById(id)
} else { // @ts-ignore
document.querySelector('.main-content.el-row').style.zIndex = 10 this.$db.read().get('uploaded').removeById(id).write()
} ipcRenderer.send('removeFiles', [file])
},
handleClose () {
this.idx = null
this.changeZIndexForGallery(false)
},
copy (item) {
const style = this.$db.get('settings.pasteStyle') || 'markdown'
const copyLink = pasteStyle(style, item)
const obj = { const obj = {
title: '复制链接成功', title: '操作结果',
body: copyLink, body: '删除成功'
icon: item.url || item.imgUrl
} }
const myNotification = new window.Notification(obj.title, obj) const myNotification = new Notification(obj.title, obj)
this.$electron.clipboard.writeText(copyLink)
myNotification.onclick = () => { myNotification.onclick = () => {
return true return true
} }
}, this.getGallery()
remove (id) { }).catch(() => {
this.$confirm('此操作将把该图片移出相册, 是否继续?', '提示', { return true
})
}
openDialog (item: ImgInfo) {
this.imgInfo.id = item.id
this.imgInfo.imgUrl = item.imgUrl as string
this.dialogVisible = true
}
confirmModify () {
this.$db.read().get('uploaded')
// @ts-ignore
.getById(this.imgInfo.id)
.assign({ imgUrl: this.imgInfo.imgUrl })
.write()
const obj = {
title: '修改图片URL成功',
body: this.imgInfo.imgUrl,
icon: this.imgInfo.imgUrl
}
const myNotification = new Notification(obj.title, obj)
myNotification.onclick = () => {
return true
}
this.dialogVisible = false
this.getGallery()
}
choosePicBed (type: string) {
let idx = this.choosedPicBed.indexOf(type)
if (idx !== -1) {
this.choosedPicBed.splice(idx, 1)
} else {
this.choosedPicBed.push(type)
}
}
cleanSearch () {
this.searchText = ''
}
isMultiple (obj: Obj) {
return Object.values(obj).some(item => item)
}
multiRemove () {
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
if (Object.values(this.choosedList).some(item => item)) {
this.$confirm('将删除刚才选中的图片,是否继续?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
const file = this.$db.get('uploaded').getById(id) let files: ImgInfo[] = []
this.$db.read().get('uploaded').removeById(id).write() Object.keys(this.choosedList).forEach(key => {
this.$electron.ipcRenderer.send('removeFiles', [file]) if (this.choosedList[key]) {
// @ts-ignore
const file = this.$db.read().get('uploaded').getById(key).value()
files.push(file)
// @ts-ignore
this.$db.read().get('uploaded').removeById(key).write()
}
})
this.choosedList = {}
this.getGallery()
const obj = { const obj = {
title: '操作结果', title: '操作结果',
body: '删除成功' body: '删除成功'
} }
const myNotification = new window.Notification(obj.title, obj) ipcRenderer.send('removeFiles', files)
const myNotification = new Notification(obj.title, obj)
myNotification.onclick = () => { myNotification.onclick = () => {
return true return true
} }
this.getGallery()
}).catch(() => { }).catch(() => {
return true return true
}) })
}, }
openDialog (item) { }
this.imgInfo.id = item.id multiCopy () {
this.imgInfo.imgUrl = item.imgUrl if (Object.values(this.choosedList).some(item => item)) {
this.dialogVisible = true let copyString = ''
}, const style = this.$db.get('settings.pasteStyle') || 'markdown'
confirmModify () { // choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
this.$db.read().get('uploaded') Object.keys(this.choosedList).forEach(key => {
.getById(this.imgInfo.id) if (this.choosedList[key]) {
.assign({imgUrl: this.imgInfo.imgUrl}) // @ts-ignore
.write() const item = this.$db.read().get('uploaded').getById(key).value()
copyString += pasteStyle(style, item) + '\n'
this.choosedList[key] = false
}
})
const obj = { const obj = {
title: '修改图片URL成功', title: '批量复制链接成功',
body: this.imgInfo.imgUrl, body: copyString
icon: this.imgInfo.imgUrl
} }
const myNotification = new window.Notification(obj.title, obj) const myNotification = new Notification(obj.title, obj)
clipboard.writeText(copyString)
myNotification.onclick = () => { myNotification.onclick = () => {
return true return true
} }
this.dialogVisible = false
this.getGallery()
},
choosePicBed (type) {
let idx = this.choosedPicBed.indexOf(type)
if (idx !== -1) {
this.choosedPicBed.splice(idx, 1)
} else {
this.choosedPicBed.push(type)
}
},
cleanSearch () {
this.searchText = ''
},
isMultiple (obj) {
return Object.values(obj).some(item => item)
},
multiRemove () {
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
if (Object.values(this.choosedList).some(item => item)) {
this.$confirm('将删除刚才选中的图片,是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let files = []
Object.keys(this.choosedList).forEach(key => {
if (this.choosedList[key]) {
const file = this.$db.read().get('uploaded').getById(key).value()
files.push(file)
this.$db.read().get('uploaded').removeById(key).write()
}
})
this.choosedList = {}
this.getGallery()
const obj = {
title: '操作结果',
body: '删除成功'
}
this.$electron.ipcRenderer.send('removeFiles', files)
const myNotification = new window.Notification(obj.title, obj)
myNotification.onclick = () => {
return true
}
}).catch(() => {
return true
})
}
},
multiCopy () {
if (Object.values(this.choosedList).some(item => item)) {
let copyString = ''
const style = this.$db.get('settings.pasteStyle') || 'markdown'
// choosedList -> { [id]: true or false }; true means choosed. false means not choosed.
Object.keys(this.choosedList).forEach(key => {
if (this.choosedList[key]) {
const item = this.$db.read().get('uploaded').getById(key).value()
copyString += pasteStyle(style, item) + '\n'
this.choosedList[key] = false
}
})
const obj = {
title: '批量复制链接成功',
body: copyString
}
const myNotification = new window.Notification(obj.title, obj)
this.$electron.clipboard.writeText(copyString)
myNotification.onclick = () => {
return true
}
}
},
toggleHandleBar () {
this.handleBarActive = !this.handleBarActive
},
getPasteStyle () {
this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
},
handlePasteStyleChange (val) {
this.$db.set('settings.pasteStyle', val)
this.pasteStyle = val
} }
}, }
toggleHandleBar () {
this.handleBarActive = !this.handleBarActive
}
getPasteStyle () {
this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
}
handlePasteStyleChange (val: string) {
this.$db.set('settings.pasteStyle', val)
this.pasteStyle = val
}
beforeDestroy () { beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('updateGallery') ipcRenderer.removeAllListeners('updateGallery')
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds) ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
} }
} }
</script> </script>
@@ -428,7 +437,7 @@ export default {
&-fake &-fake
position absolute position absolute
top 0 top 0
left 0 left 0
opacity 0 opacity 0
width 100% width 100%
z-index -1 z-index -1
@@ -456,4 +465,4 @@ export default {
margin-bottom 10px margin-bottom 10px
.el-input__inner .el-input__inner
border-radius 14px border-radius 14px
</style> </style>
+150 -145
View File
@@ -15,31 +15,35 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script lang="ts">
import mixin from '@/utils/mixin' import mixin from '@/utils/mixin'
export default { import { Component, Vue, Watch } from 'vue-property-decorator'
import {
ipcRenderer,
IpcRendererEvent,
remote
} from 'electron'
@Component({
name: 'mini-page', name: 'mini-page',
mixins: [mixin], mixins: [mixin]
data () { })
return { export default class extends Vue {
logo: 'static/squareLogo.png', logo = 'static/squareLogo.png'
dragover: false, dragover = false
progress: 0, progress = 0
showProgress: false, showProgress = false
showError: false, showError = false
dragging: false, dragging = false
wX: '', wX: number = -1
wY: '', wY: number = -1
screenX: '', screenX: number = -1
screenY: '', screenY: number = -1
menu: null, menu: Electron.Menu | null = null
os: '', os = ''
picBed: [] picBed: PicBedType[] = []
}
},
created () { created () {
this.os = process.platform this.os = process.platform
this.$electron.ipcRenderer.on('uploadProgress', (event, progress) => { ipcRenderer.on('uploadProgress', (event: IpcRendererEvent, progress: number) => {
if (progress !== -1) { if (progress !== -1) {
this.showProgress = true this.showProgress = true
this.progress = progress this.progress = progress
@@ -49,140 +53,141 @@ export default {
} }
}) })
this.getPicBeds() this.getPicBeds()
}, }
mounted () { mounted () {
window.addEventListener('mousedown', this.handleMouseDown, false) window.addEventListener('mousedown', this.handleMouseDown, false)
window.addEventListener('mousemove', this.handleMouseMove, false) window.addEventListener('mousemove', this.handleMouseMove, false)
window.addEventListener('mouseup', this.handleMouseUp, false) window.addEventListener('mouseup', this.handleMouseUp, false)
}, }
watch: {
progress (val) { @Watch('progress')
if (val === 100) { onProgressChange (val: number) {
setTimeout(() => { if (val === 100) {
this.showProgress = false setTimeout(() => {
this.showError = false this.showProgress = false
}, 1000) this.showError = false
setTimeout(() => { }, 1000)
this.progress = 0 setTimeout(() => {
}, 1200) this.progress = 0
}, 1200)
}
}
getPicBeds () {
this.picBed = ipcRenderer.sendSync('getPicBeds')
this.buildMenu()
}
onDrop (e: DragEvent) {
this.dragover = false
this.ipcSendFiles(e.dataTransfer!.files)
}
openUploadWindow () {
// @ts-ignore
document.getElementById('file-uploader').click()
}
onChange (e: any) {
this.ipcSendFiles(e.target.files)
// @ts-ignore
document.getElementById('file-uploader').value = ''
}
ipcSendFiles (files: FileList) {
let sendFiles: FileWithPath[] = []
Array.from(files).forEach((item, index) => {
let obj = {
name: item.name,
path: item.path
}
sendFiles.push(obj)
})
ipcRenderer.send('uploadChoosedFiles', sendFiles)
}
handleMouseDown (e: MouseEvent) {
this.dragging = true
this.wX = e.pageX
this.wY = e.pageY
this.screenX = e.screenX
this.screenY = e.screenY
}
handleMouseMove (e: MouseEvent) {
e.preventDefault()
e.stopPropagation()
if (this.dragging) {
const xLoc = e.screenX - this.wX
const yLoc = e.screenY - this.wY
remote.BrowserWindow.getFocusedWindow()!.setBounds({
x: xLoc,
y: yLoc,
width: 64,
height: 64
})
}
}
handleMouseUp (e: MouseEvent) {
this.dragging = false
if (this.screenX === e.screenX && this.screenY === e.screenY) {
if (e.button === 0) { // left mouse
this.openUploadWindow()
} else {
this.getPicBeds()
this.openContextMenu()
} }
} }
}, }
methods: { openContextMenu () {
getPicBeds () { this.menu!.popup()
this.picBed = this.$electron.ipcRenderer.sendSync('getPicBeds') }
this.buildMenu() buildMenu () {
}, const _this = this
onDrop (e) { const submenu = this.picBed.map(item => {
this.dragover = false return {
this.ipcSendFiles(e.dataTransfer.files) label: item.name,
}, type: 'radio',
openUploadWindow () { checked: this.$db.get('picBed.current') === item.type,
document.getElementById('file-uploader').click() click () {
}, _this.$db.set('picBed.current', item.type)
onChange (e) { ipcRenderer.send('syncPicBed')
this.ipcSendFiles(e.target.files)
document.getElementById('file-uploader').value = ''
},
ipcSendFiles (files) {
let sendFiles = []
Array.from(files).forEach((item, index) => {
let obj = {
name: item.name,
path: item.path
}
sendFiles.push(obj)
})
this.$electron.ipcRenderer.send('uploadChoosedFiles', sendFiles)
},
handleMouseDown (e) {
this.dragging = true
this.wX = e.pageX
this.wY = e.pageY
this.screenX = e.screenX
this.screenY = e.screenY
},
handleMouseMove (e) {
e.preventDefault()
e.stopPropagation()
if (this.dragging) {
const xLoc = e.screenX - this.wX
const yLoc = e.screenY - this.wY
this.$electron.remote.BrowserWindow.getFocusedWindow().setBounds({
x: xLoc,
y: yLoc,
width: 64,
height: 64
})
}
},
handleMouseUp (e) {
this.dragging = false
if (this.screenX === e.screenX && this.screenY === e.screenY) {
if (e.button === 0) { // left mouse
this.openUploadWindow()
} else {
this.getPicBeds()
this.openContextMenu()
} }
} }
}, })
openContextMenu () { const template = [
this.menu.popup(this.$electron.remote.getCurrentWindow()) {
}, label: '打开详细窗口',
buildMenu () { click () {
const _this = this ipcRenderer.send('openSettingWindow')
const submenu = this.picBed.map(item => {
return {
label: item.name,
type: 'radio',
checked: this.$db.get('picBed.current') === item.type,
click () {
_this.$db.set('picBed.current', item.type)
_this.$electron.ipcRenderer.send('syncPicBed')
}
} }
}) },
const template = [ {
{ label: '选择默认图床',
label: '打开详细窗口', type: 'submenu',
click () { submenu
_this.$electron.ipcRenderer.send('openSettingWindow') },
} {
}, label: '剪贴板图片上传',
{ click () {
label: '选择默认图床', ipcRenderer.send('uploadClipboardFilesFromUploadPage')
type: 'submenu',
submenu
},
{
label: '剪贴板图片上传',
click () {
_this.$electron.ipcRenderer.send('uploadClipboardFilesFromUploadPage')
}
},
{
label: '最小化窗口',
role: 'minimize'
},
{
label: '重启应用',
click () {
_this.$electron.remote.app.relaunch()
_this.$electron.remote.app.exit(0)
}
},
{
role: 'quit',
label: '退出'
} }
] },
this.menu = this.$electron.remote.Menu.buildFromTemplate(template) {
} label: '最小化窗口',
}, role: 'minimize'
},
{
label: '重启应用',
click () {
remote.app.relaunch()
remote.app.exit(0)
}
},
{
role: 'quit',
label: '退出'
}
]
// @ts-ignore
this.menu = remote.Menu.buildFromTemplate(template)
}
beforeDestroy () { beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('uploadProgress') ipcRenderer.removeAllListeners('uploadProgress')
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds) ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
window.removeEventListener('mousedown', this.handleMouseDown, false) window.removeEventListener('mousedown', this.handleMouseDown, false)
window.removeEventListener('mousemove', this.handleMouseMove, false) window.removeEventListener('mousemove', this.handleMouseMove, false)
window.removeEventListener('mouseup', this.handleMouseUp, false) window.removeEventListener('mouseup', this.handleMouseUp, false)
@@ -225,4 +230,4 @@ export default {
background rgba(0,0,0,0.3) background rgba(0,0,0,0.3)
#file-uploader #file-uploader
display none display none
</style> </style>
+235 -232
View File
@@ -141,7 +141,7 @@
<div class="custom-title"> <div class="custom-title">
用占位符 <b>$fileName</b> 来表示文件名的位置 用占位符 <b>$fileName</b> 来表示文件名的位置
</div> </div>
<el-input <el-input
class="align-center" class="align-center"
v-model="customLink.value" v-model="customLink.value"
:autofocus="true" :autofocus="true"
@@ -171,7 +171,7 @@
<el-form-item <el-form-item
label="代理地址" label="代理地址"
> >
<el-input <el-input
v-model="proxy" v-model="proxy"
:autofocus="true" :autofocus="true"
placeholder="例如:http://127.0.0.1:1080" placeholder="例如:http://127.0.0.1:1080"
@@ -241,32 +241,224 @@
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script lang="ts">
import keyDetect from 'utils/key-binding' import keyDetect from '@/utils/key-binding'
import pkg from 'root/package.json' import pkg from 'root/package.json'
import path from 'path' import path from 'path'
import {
ipcRenderer,
remote
} from 'electron'
import { Component, Vue } from 'vue-property-decorator'
import db from '#/datastore'
const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest' const release = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest' const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
export default { const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) => void) => {
name: 'picgo-setting', if (!/\$url/.test(value)) {
computed: { return callback(new Error('必须含有$url'))
needUpdate () { } else {
if (this.latestVersion) { return callback()
return this.compareVersion2Update(this.version, this.latestVersion) }
}
let logLevel = db.get('settings.logLevel')
if (!Array.isArray(logLevel)) {
if (logLevel && logLevel.length > 0) {
logLevel = [logLevel]
} else {
logLevel = ['all']
}
}
@Component({
name: 'picgo-setting'
})
export default class extends Vue {
form: ISettingForm = {
updateHelper: db.get('settings.showUpdateTip'),
showPicBedList: [],
autoStart: db.get('settings.autoStart') || false,
rename: db.get('settings.rename') || false,
autoRename: db.get('settings.autoRename') || false,
uploadNotification: db.get('settings.uploadNotification') || false,
miniWindowOntop: db.get('settings.miniWindowOntop') || false,
logLevel
}
picBed: PicBedType[] = []
logFileVisible = false
keyBindingVisible = false
customLinkVisible = false
checkUpdateVisible = false
proxyVisible = false
customLink = {
value: db.get('settings.customLink') || '$url'
}
shortKey: ShortKeyMap = {
upload: db.get('settings.shortKey.upload')
}
proxy = db.get('picBed.proxy') || ''
rules = {
value: [
{ validator: customLinkRule, trigger: 'blur' }
]
}
logLevel = {
all: '全部-All',
success: '成功-Success',
error: '错误-Error',
info: '普通-Info',
warn: '提醒-Warn',
none: '不记录日志-None'
}
version = pkg.version
latestVersion = ''
os = ''
get needUpdate () {
if (this.latestVersion) {
return this.compareVersion2Update(this.version, this.latestVersion)
} else {
return false
}
}
created () {
this.os = process.platform
ipcRenderer.send('getPicBeds')
ipcRenderer.on('getPicBeds', this.getPicBeds)
}
getPicBeds (event: Event, picBeds: PicBedType[]) {
this.picBed = picBeds
this.form.showPicBedList = this.picBed.map(item => {
if (item.visible) {
return item.name
}
}) as string[]
}
openFile (file: string) {
const { app, shell } = remote
const STORE_PATH = app.getPath('userData')
const FILE = path.join(STORE_PATH, `/${file}`)
shell.openItem(FILE)
}
openLogSetting () {
this.logFileVisible = true
}
keyDetect (type: string, event: KeyboardEvent) {
this.shortKey[type] = keyDetect(event).join('+')
}
cancelKeyBinding () {
this.keyBindingVisible = false
this.shortKey = db.get('settings.shortKey')
}
cancelCustomLink () {
this.customLinkVisible = false
this.customLink.value = db.get('settings.customLink') || '$url'
}
confirmCustomLink () {
// @ts-ignore
this.$refs.customLink.validate((valid: boolean) => {
if (valid) {
db.set('settings.customLink', this.customLink.value)
this.customLinkVisible = false
ipcRenderer.send('updateCustomLink')
} else { } else {
return false return false
} }
})
}
cancelProxy () {
this.proxyVisible = false
this.proxy = db.get('picBed.proxy') || undefined
}
confirmProxy () {
this.proxyVisible = false
db.set('picBed.proxy', this.proxy)
const successNotification = new Notification('设置代理', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
} }
}, }
data () { updateHelperChange (val: boolean) {
const customLinkRule = (rule, value, callback) => { db.set('settings.showUpdateTip', val)
if (!/\$url/.test(value)) { }
return callback(new Error('必须含有$url')) handleShowPicBedListChange (val: string[]) {
const list = this.picBed.map(item => {
if (!val.includes(item.name)) {
item.visible = false
} else { } else {
return callback() item.visible = true
}
return item
})
db.set('picBed.list', list)
ipcRenderer.send('getPicBeds')
}
handleAutoStartChange (val: boolean) {
db.set('settings.autoStart', val)
ipcRenderer.send('autoStart', val)
}
handleRename (val: boolean) {
db.set('settings.rename', val)
}
handleAutoRename (val: boolean) {
db.set('settings.autoRename', val)
}
compareVersion2Update (current: string, latest: string) {
const currentVersion = current.split('.').map(item => parseInt(item))
const latestVersion = latest.split('.').map(item => parseInt(item))
for (let i = 0; i < 3; i++) {
if (currentVersion[i] < latestVersion[i]) {
return true
}
if (currentVersion[i] > latestVersion[i]) {
return false
} }
} }
let logLevel = this.$db.get('settings.logLevel') return false
}
checkUpdate () {
this.checkUpdateVisible = true
this.$http.get(release)
.then(res => {
this.latestVersion = res.data.name
}).catch(err => {
console.log(err)
})
}
confirmCheckVersion () {
if (this.needUpdate) {
remote.shell.openExternal(downloadUrl)
}
this.checkUpdateVisible = false
}
cancelCheckVersion () {
this.checkUpdateVisible = false
}
handleUploadNotification (val: boolean) {
db.set('settings.uploadNotification', val)
}
handleMiniWindowOntop (val: boolean) {
db.set('settings.miniWindowOntop', val)
this.$message.info('需要重启生效')
}
confirmLogLevelSetting () {
if (this.form.logLevel.length === 0) {
return this.$message.error('请选择日志记录等级')
}
db.set('settings.logLevel', this.form.logLevel)
const successNotification = new Notification('设置日志', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
this.logFileVisible = false
}
cancelLogLevelSetting () {
this.logFileVisible = false
let logLevel = db.get('settings.logLevel')
if (!Array.isArray(logLevel)) { if (!Array.isArray(logLevel)) {
if (logLevel && logLevel.length > 0) { if (logLevel && logLevel.length > 0) {
logLevel = [logLevel] logLevel = [logLevel]
@@ -274,225 +466,36 @@ export default {
logLevel = ['all'] logLevel = ['all']
} }
} }
return { this.form.logLevel = logLevel
form: { }
updateHelper: this.$db.get('settings.showUpdateTip'), handleLevelDisabled (val: string) {
showPicBedList: [], let currentLevel = val
autoStart: this.$db.get('settings.autoStart') || false, let flagLevel
rename: this.$db.get('settings.rename') || false, let result = this.form.logLevel.some(item => {
autoRename: this.$db.get('settings.autoRename') || false, if (item === 'all' || item === 'none') {
uploadNotification: this.$db.get('settings.uploadNotification') || false, flagLevel = item
miniWindowOntop: this.$db.get('settings.miniWindowOntop') || false, }
logLevel return (item === 'all' || item === 'none')
}, })
picBed: [], if (result) {
logFileVisible: false, if (currentLevel !== flagLevel) {
keyBindingVisible: false,
customLinkVisible: false,
checkUpdateVisible: false,
proxyVisible: false,
customLink: {
value: this.$db.get('settings.customLink') || '$url'
},
shortKey: {
upload: this.$db.get('settings.shortKey.upload')
},
proxy: this.$db.get('picBed.proxy') || undefined,
rules: {
value: [
{ validator: customLinkRule, trigger: 'blur' }
]
},
logLevel: {
all: '全部-All',
success: '成功-Success',
error: '错误-Error',
info: '普通-Info',
warn: '提醒-Warn',
none: '不记录日志-None'
},
version: pkg.version,
latestVersion: '',
os: ''
}
},
created () {
this.os = process.platform
this.$electron.ipcRenderer.send('getPicBeds')
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds)
},
methods: {
getPicBeds (event, picBeds) {
this.picBed = picBeds
this.form.showPicBedList = this.picBed.map(item => {
if (item.visible) {
return item.name
}
})
},
openFile (file) {
const { app, shell } = this.$electron.remote
const STORE_PATH = app.getPath('userData')
const FILE = path.join(STORE_PATH, `/${file}`)
shell.openItem(FILE)
},
openLogSetting () {
this.logFileVisible = true
},
keyDetect (type, event) {
this.shortKey[type] = keyDetect(event).join('+')
},
cancelKeyBinding () {
this.keyBindingVisible = false
this.shortKey = this.$db.get('settings.shortKey')
},
cancelCustomLink () {
this.customLinkVisible = false
this.customLink.value = this.$db.get('settings.customLink') || '$url'
},
confirmCustomLink () {
this.$refs.customLink.validate((valid) => {
if (valid) {
this.$db.set('settings.customLink', this.customLink.value)
this.customLinkVisible = false
this.$electron.ipcRenderer.send('updateCustomLink')
} else {
return false
}
})
},
cancelProxy () {
this.proxyVisible = false
this.proxy = this.$db.get('picBed.proxy') || undefined
},
confirmProxy () {
this.proxyVisible = false
this.$db.set('picBed.proxy', this.proxy)
const successNotification = new window.Notification('设置代理', {
body: '设置成功'
})
successNotification.onclick = () => {
return true return true
} }
}, } else if (this.form.logLevel.length > 0) {
updateHelperChange (val) { if (val === 'all' || val === 'none') {
this.$db.set('settings.showUpdateTip', val)
},
handleShowPicBedListChange (val) {
const list = this.picBed.map(item => {
if (!val.includes(item.name)) {
item.visible = false
} else {
item.visible = true
}
return item
})
this.$db.set('picBed.list', list)
this.$electron.ipcRenderer.send('getPicBeds')
},
handleAutoStartChange (val) {
this.$db.set('settings.autoStart', val)
this.$electron.ipcRenderer.send('autoStart', val)
},
handleRename (val) {
this.$db.set('settings.rename', val)
},
handleAutoRename (val) {
this.$db.set('settings.autoRename', val)
},
compareVersion2Update (current, latest) {
const currentVersion = current.split('.').map(item => parseInt(item))
const latestVersion = latest.split('.').map(item => parseInt(item))
for (let i = 0; i < 3; i++) {
if (currentVersion[i] < latestVersion[i]) {
return true
}
if (currentVersion[i] > latestVersion[i]) {
return false
}
}
return false
},
checkUpdate () {
this.checkUpdateVisible = true
this.$http.get(release)
.then(res => {
this.latestVersion = res.data.name
}).catch(err => {
console.log(err)
})
},
confirmCheckVersion () {
if (this.needUpdate) {
this.$electron.remote.shell.openExternal(downloadUrl)
}
this.checkUpdateVisible = false
},
cancelCheckVersion () {
this.checkUpdateVisible = false
},
handleUploadNotification (val) {
this.$db.set('settings.uploadNotification', val)
},
handleMiniWindowOntop (val) {
this.$db.set('settings.miniWindowOntop', val)
this.$message('需要重启生效')
},
confirmLogLevelSetting () {
if (this.form.logLevel.length === 0) {
return this.$message.error('请选择日志记录等级')
}
this.$db.set('settings.logLevel', this.form.logLevel)
const successNotification = new window.Notification('设置日志', {
body: '设置成功'
})
successNotification.onclick = () => {
return true return true
} }
this.logFileVisible = false
},
cancelLogLevelSetting () {
this.logFileVisible = false
let logLevel = this.$db.get('settings.logLevel')
if (!Array.isArray(logLevel)) {
if (logLevel && logLevel.length > 0) {
logLevel = [logLevel]
} else {
logLevel = ['all']
}
}
this.form.logLevel = logLevel
},
handleLevelDisabled (val) {
let currentLevel = val
let flagLevel
let result = this.form.logLevel.some(item => {
if (item === 'all' || item === 'none') {
flagLevel = item
}
return (item === 'all' || item === 'none')
})
if (result) {
if (currentLevel !== flagLevel) {
return true
}
} else if (this.form.logLevel.length > 0) {
if (val === 'all' || val === 'none') {
return true
}
}
return false
},
goConfigPage () {
this.$electron.remote.shell.openExternal('https://picgo.github.io/PicGo-Doc/zh/guide/config.html#picgo设置')
},
goShortCutPage () {
this.$router.push('shortcut-page')
} }
}, return false
}
goConfigPage () {
remote.shell.openExternal('https://picgo.github.io/PicGo-Doc/zh/guide/config.html#picgo设置')
}
goShortCutPage () {
this.$router.push('shortcut')
}
beforeDestroy () { beforeDestroy () {
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds) ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
} }
} }
</script> </script>
@@ -515,7 +518,7 @@ export default {
overflow-x hidden overflow-x hidden
.setting-list .setting-list
.el-form .el-form
label label
line-height 32px line-height 32px
padding-bottom 0 padding-bottom 0
color #eee color #eee
@@ -549,4 +552,4 @@ export default {
margin-left 0 margin-left 0
.confirm-button .confirm-button
width 100% width 100%
</style> </style>
+265 -258
View File
@@ -93,58 +93,65 @@
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script lang="ts">
import ConfigForm from '@/components/ConfigForm' import {
Component,
Vue,
Watch
} from 'vue-property-decorator'
import ConfigForm from '@/components/ConfigForm.vue'
import { debounce } from 'lodash' import { debounce } from 'lodash'
export default { import {
ipcRenderer,
remote,
IpcRendererEvent
} from 'electron'
const { Menu } = remote
@Component({
name: 'plugin', name: 'plugin',
components: { components: {
ConfigForm ConfigForm
}, }
data () { })
return { export default class extends Vue {
searchText: '', searchText = ''
pluginList: [], pluginList: IPicGoPlugin[] = []
menu: null, menu: Electron.Menu | null = null
config: [], config: any[] = []
currentType: '', currentType = ''
configName: '', configName = ''
dialogVisible: false, dialogVisible = false
pluginNameList: [], pluginNameList: string[] = []
loading: true, loading = true
needReload: false, needReload = false
id: '', id = ''
os: '' os = ''
get npmSearchText () {
return this.searchText.match('picgo-plugin-')
? this.searchText
: this.searchText !== ''
? `picgo-plugin-${this.searchText}`
: this.searchText
}
@Watch('npmSearchText')
onNpmSearchTextChange (val: string) {
if (val) {
this.loading = true
this.pluginList = []
this.getSearchResult(val)
} else {
this.getPluginList()
} }
}, }
computed: {
npmSearchText () {
return this.searchText.match('picgo-plugin-')
? this.searchText
: this.searchText !== ''
? `picgo-plugin-${this.searchText}`
: this.searchText
}
},
watch: {
npmSearchText (val) {
if (val) {
this.loading = true
this.pluginList = []
this.getSearchResult(val)
} else {
this.getPluginList()
}
}
},
created () { created () {
this.os = process.platform this.os = process.platform
this.$electron.ipcRenderer.on('pluginList', (evt, list) => { ipcRenderer.on('pluginList', (evt: IpcRendererEvent, list: IPicGoPlugin[]) => {
this.pluginList = list this.pluginList = list
this.pluginNameList = list.map(item => item.name) this.pluginNameList = list.map(item => item.name)
this.loading = false this.loading = false
}) })
this.$electron.ipcRenderer.on('installSuccess', (evt, plugin) => { ipcRenderer.on('installSuccess', (evt: IpcRendererEvent, plugin: string) => {
this.loading = false this.loading = false
this.pluginList.forEach(item => { this.pluginList.forEach(item => {
if (item.name === plugin) { if (item.name === plugin) {
@@ -153,7 +160,7 @@ export default {
} }
}) })
}) })
this.$electron.ipcRenderer.on('updateSuccess', (evt, plugin) => { ipcRenderer.on('updateSuccess', (evt: IpcRendererEvent, plugin: string) => {
this.loading = false this.loading = false
this.pluginList.forEach(item => { this.pluginList.forEach(item => {
if (item.name === plugin) { if (item.name === plugin) {
@@ -165,7 +172,7 @@ export default {
this.handleReload() this.handleReload()
this.getPluginList() this.getPluginList()
}) })
this.$electron.ipcRenderer.on('uninstallSuccess', (evt, plugin) => { ipcRenderer.on('uninstallSuccess', (evt: IpcRendererEvent, plugin: string) => {
this.loading = false this.loading = false
this.pluginList = this.pluginList.filter(item => { this.pluginList = this.pluginList.filter(item => {
if (item.name === plugin) { // restore Uploader & Transformer after uninstalling if (item.name === plugin) { // restore Uploader & Transformer after uninstalling
@@ -184,241 +191,241 @@ export default {
this.getPluginList() this.getPluginList()
this.getSearchResult = debounce(this.getSearchResult, 50) this.getSearchResult = debounce(this.getSearchResult, 50)
this.needReload = this.$db.get('needReload') this.needReload = this.$db.get('needReload')
}, }
methods: { buildContextMenu (plugin: IPicGoPlugin) {
buildContextMenu (plugin) { const _this = this
const _this = this let menu = [{
let menu = [{ label: '启用插件',
label: '启用插件', enabled: !plugin.enabled,
enabled: !plugin.enabled, click () {
click () { _this.$db.set(`picgoPlugins.picgo-plugin-${plugin.name}`, true)
_this.$db.set(`picgoPlugins.picgo-plugin-${plugin.name}`, true) plugin.enabled = true
plugin.enabled = true _this.getPicBeds()
_this.getPicBeds() }
}, {
label: '禁用插件',
enabled: plugin.enabled,
click () {
_this.$db.set(`picgoPlugins.picgo-plugin-${plugin.name}`, false)
plugin.enabled = false
_this.getPicBeds()
if (plugin.config.transformer.name) {
_this.handleRestoreState('transformer', plugin.config.transformer.name)
} }
}, { if (plugin.config.uploader.name) {
label: '禁用插件', _this.handleRestoreState('uploader', plugin.config.uploader.name)
enabled: plugin.enabled,
click () {
_this.$db.set(`picgoPlugins.picgo-plugin-${plugin.name}`, false)
plugin.enabled = false
_this.getPicBeds()
if (plugin.config.transformer.name) {
_this.handleRestoreState('transformer', plugin.config.transformer.name)
}
if (plugin.config.uploader.name) {
_this.handleRestoreState('uploader', plugin.config.uploader.name)
}
}
}, {
label: '卸载插件',
click () {
_this.uninstallPlugin(plugin.name)
}
}, {
label: '更新插件',
click () {
_this.updatePlugin(plugin.name)
}
}]
for (let i in plugin.config) {
if (plugin.config[i].config.length > 0) {
const obj = {
label: `配置${i} - ${plugin.config[i].name}`,
click () {
_this.currentType = i
_this.configName = plugin.config[i].name
_this.dialogVisible = true
_this.config = plugin.config[i].config
}
}
menu.push(obj)
} }
} }
}, {
// handle transformer label: '卸载插件',
if (plugin.config.transformer.name) { click () {
let currentTransformer = this.$db.get('picBed.transformer') || 'path' _this.uninstallPlugin(plugin.name)
let pluginTransformer = plugin.config.transformer.name }
}, {
label: '更新插件',
click () {
_this.updatePlugin(plugin.name)
}
}]
for (let i in plugin.config) {
if (plugin.config[i].config.length > 0) {
const obj = { const obj = {
label: `${currentTransformer === pluginTransformer ? '禁用' : '启用'}transformer - ${plugin.config.transformer.name}`, label: `配置${i} - ${plugin.config[i].name}`,
click () { click () {
_this.toggleTransformer(plugin.config.transformer.name) _this.currentType = i
_this.configName = plugin.config[i].name
_this.dialogVisible = true
_this.config = plugin.config[i].config
} }
} }
menu.push(obj) menu.push(obj)
} }
}
// plugin custom menus // handle transformer
if (plugin.guiMenu) { if (plugin.config.transformer.name) {
let currentTransformer = this.$db.get('picBed.transformer') || 'path'
let pluginTransformer = plugin.config.transformer.name
const obj = {
label: `${currentTransformer === pluginTransformer ? '禁用' : '启用'}transformer - ${plugin.config.transformer.name}`,
click () {
_this.toggleTransformer(plugin.config.transformer.name)
}
}
menu.push(obj)
}
// plugin custom menus
if (plugin.guiMenu) {
menu.push({
// @ts-ignore
type: 'separator'
})
for (let i of plugin.guiMenu) {
menu.push({ menu.push({
type: 'separator' label: i.label,
click () {
ipcRenderer.send('pluginActions', plugin.name, i.label)
}
}) })
for (let i of plugin.guiMenu) {
menu.push({
label: i.label,
click () {
_this.$electron.ipcRenderer.send('pluginActions', plugin.name, i.label)
}
})
}
} }
}
this.menu = this.$electron.remote.Menu.buildFromTemplate(menu) this.menu = Menu.buildFromTemplate(menu)
this.menu.popup(this.$electron.remote.getCurrentWindow()) this.menu.popup()
}, }
getPluginList () { getPluginList () {
this.$electron.ipcRenderer.send('getPluginList') ipcRenderer.send('getPluginList')
}, }
getPicBeds () { getPicBeds () {
this.$electron.ipcRenderer.send('getPicBeds') ipcRenderer.send('getPicBeds')
}, }
installPlugin (item) { installPlugin (item: IPicGoPlugin) {
if (!item.gui) { if (!item.gui) {
this.$confirm('该插件未对可视化界面进行优化, 是否继续安装?', '提示', { this.$confirm('该插件未对可视化界面进行优化, 是否继续安装?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
item.ing = true item.ing = true
this.$electron.ipcRenderer.send('installPlugin', item.name) ipcRenderer.send('installPlugin', item.name)
}).catch(() => { }).catch(() => {
console.log('Install canceled') console.log('Install canceled')
}) })
} else { } else {
item.ing = true
ipcRenderer.send('installPlugin', item.name)
}
}
uninstallPlugin (val: string) {
this.pluginList.forEach(item => {
if (item.name === val) {
item.ing = true item.ing = true
this.$electron.ipcRenderer.send('installPlugin', item.name)
} }
}, })
uninstallPlugin (val) { ipcRenderer.send('uninstallPlugin', val)
this.pluginList.forEach(item => { }
if (item.name === val) { updatePlugin (val: string) {
item.ing = true this.pluginList.forEach(item => {
} if (item.name === val) {
}) item.ing = true
this.$electron.ipcRenderer.send('uninstallPlugin', val) }
}, })
updatePlugin (val) { ipcRenderer.send('updatePlugin', val)
this.pluginList.forEach(item => { }
if (item.name === val) { reloadApp () {
item.ing = true remote.app.relaunch()
} remote.app.exit(0)
}) }
this.$electron.ipcRenderer.send('updatePlugin', val) handleReload () {
}, this.$db.set('needReload', true)
reloadApp () { this.needReload = true
this.$electron.remote.app.relaunch() const successNotification = new Notification('更新成功', {
this.$electron.remote.app.exit(0) body: '请点击此通知重启应用以生效'
}, })
handleReload () { successNotification.onclick = () => {
this.$db.set('needReload', true) this.reloadApp()
this.needReload = true }
const successNotification = new window.Notification('更新成功', { }
body: '请点击此通知重启应用以生效' cleanSearch () {
this.searchText = ''
}
toggleTransformer (transformer: string) {
let currentTransformer = this.$db.get('picBed.transformer') || 'path'
if (currentTransformer === transformer) {
this.$db.set('picBed.transformer', 'path')
} else {
this.$db.set('picBed.transformer', transformer)
}
}
async handleConfirmConfig () {
// @ts-ignore
const result = await this.$refs.configForm.validate()
if (result !== false) {
switch (this.currentType) {
case 'plugin':
this.$db.set(`picgo-plugin-${this.configName}`, result)
break
case 'uploader':
this.$db.set(`picBed.${this.configName}`, result)
break
case 'transformer':
this.$db.set(`transformer.${this.configName}`, result)
break
}
const successNotification = new Notification('设置结果', {
body: '设置成功'
}) })
successNotification.onclick = () => { successNotification.onclick = () => {
this.reloadApp() return true
} }
}, this.dialogVisible = false
cleanSearch () { this.getPluginList()
this.searchText = ''
},
toggleTransformer (transformer) {
let currentTransformer = this.$db.get('picBed.transformer') || 'path'
if (currentTransformer === transformer) {
this.$db.set('picBed.transformer', 'path')
} else {
this.$db.set('picBed.transformer', transformer)
}
},
async handleConfirmConfig () {
const result = await this.$refs.configForm.validate()
if (result !== false) {
switch (this.currentType) {
case 'plugin':
this.$db.set(`picgo-plugin-${this.configName}`, result)
break
case 'uploader':
this.$db.set(`picBed.${this.configName}`, result)
break
case 'transformer':
this.$db.set(`transformer.${this.configName}`, result)
break
}
const successNotification = new window.Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
this.dialogVisible = false
this.getPluginList()
}
},
getSearchResult: function (val) {
// this.$http.get(`https://api.npms.io/v2/search?q=${val}`)
this.$http.get(`https://registry.npmjs.com/-/v1/search?text=${val}`)
.then(res => {
this.pluginList = res.data.objects.map(item => {
return this.handleSearchResult(item)
})
this.loading = false
})
.catch(err => {
console.log(err)
this.loading = false
})
},
handleSearchResult (item) {
const name = item.package.name.replace(/picgo-plugin-/, '')
let gui = false
if (item.package.keywords && item.package.keywords.length > 0) {
if (item.package.keywords.includes('picgo-gui-plugin')) {
gui = true
}
}
return {
name: name,
author: item.package.author.name,
description: item.package.description,
logo: `https://cdn.jsdelivr.net/npm/${item.package.name}/logo.png`,
config: {},
homepage: item.package.links ? item.package.links.homepage : '',
hasInstall: this.pluginNameList.some(plugin => plugin === item.package.name.replace(/picgo-plugin-/, '')),
version: item.package.version,
gui,
ing: false // installing or uninstalling
}
},
// restore Uploader & Transformer
handleRestoreState (item, name) {
if (item === 'uploader') {
const current = this.$db.get('picBed.current')
if (current === name) {
this.$db.set('picBed.current', 'smms')
}
}
if (item === 'transformer') {
const current = this.$db.get('picBed.transformer')
if (current === name) {
this.$db.set('picBed.transformer', 'path')
}
}
},
openHomepage (url) {
if (url) {
this.$electron.remote.shell.openExternal(url)
}
},
goAwesomeList () {
this.$electron.remote.shell.openExternal('https://github.com/PicGo/Awesome-PicGo')
} }
}, }
getSearchResult (val: string) {
// this.$http.get(`https://api.npms.io/v2/search?q=${val}`)
this.$http.get(`https://registry.npmjs.com/-/v1/search?text=${val}`)
.then((res: INPMSearchResult) => {
this.pluginList = res.data.objects.map((item: INPMSearchResultObject) => {
return this.handleSearchResult(item)
})
this.loading = false
})
.catch(err => {
console.log(err)
this.loading = false
})
}
handleSearchResult (item: INPMSearchResultObject) {
const name = item.package.name.replace(/picgo-plugin-/, '')
let gui = false
if (item.package.keywords && item.package.keywords.length > 0) {
if (item.package.keywords.includes('picgo-gui-plugin')) {
gui = true
}
}
return {
name: name,
author: item.package.author.name,
description: item.package.description,
logo: `https://cdn.jsdelivr.net/npm/${item.package.name}/logo.png`,
config: {},
homepage: item.package.links ? item.package.links.homepage : '',
hasInstall: this.pluginNameList.some(plugin => plugin === item.package.name.replace(/picgo-plugin-/, '')),
version: item.package.version,
gui,
ing: false // installing or uninstalling
}
}
// restore Uploader & Transformer
handleRestoreState (item: string, name: string) {
if (item === 'uploader') {
const current = this.$db.get('picBed.current')
if (current === name) {
this.$db.set('picBed.current', 'smms')
}
}
if (item === 'transformer') {
const current = this.$db.get('picBed.transformer')
if (current === name) {
this.$db.set('picBed.transformer', 'path')
}
}
}
openHomepage (url: string) {
if (url) {
remote.shell.openExternal(url)
}
}
goAwesomeList () {
remote.shell.openExternal('https://github.com/PicGo/Awesome-PicGo')
}
beforeDestroy () { beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('pluginList') ipcRenderer.removeAllListeners('pluginList')
this.$electron.ipcRenderer.removeAllListeners('installSuccess') ipcRenderer.removeAllListeners('installSuccess')
this.$electron.ipcRenderer.removeAllListeners('uninstallSuccess') ipcRenderer.removeAllListeners('uninstallSuccess')
this.$electron.ipcRenderer.removeAllListeners('updateSuccess') ipcRenderer.removeAllListeners('updateSuccess')
} }
} }
</script> </script>
@@ -562,4 +569,4 @@ $darwinBg = #172426
padding 10px 0 padding 10px 0
&.cut-width &.cut-width
width calc(100% - 48px) width calc(100% - 48px)
</style> </style>
+23 -22
View File
@@ -6,7 +6,7 @@
<el-form-item <el-form-item
label="文件改名" label="文件改名"
> >
<el-input <el-input
v-model="fileName" v-model="fileName"
size="small" size="small"
@keyup.enter.native="confirmName" @keyup.enter.native="confirmName"
@@ -21,33 +21,34 @@
</el-row> </el-row>
</div> </div>
</template> </template>
<script> <script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import mixin from '@/utils/mixin' import mixin from '@/utils/mixin'
export default { import {
ipcRenderer,
IpcRendererEvent
} from 'electron'
@Component({
name: 'rename-page', name: 'rename-page',
mixins: [mixin], mixins: [mixin]
data () { })
return { export default class extends Vue {
fileName: '', fileName: string = ''
id: null id: string | null = null
}
},
created () { created () {
this.$electron.ipcRenderer.on('rename', (event, name, id) => { ipcRenderer.on('rename', (event: IpcRendererEvent, name: string, id: string) => {
this.fileName = name this.fileName = name
this.id = id this.id = id
}) })
}, }
methods: { confirmName () {
confirmName () { ipcRenderer.send(`rename${this.id}`, this.fileName)
this.$electron.ipcRenderer.send(`rename${this.id}`, this.fileName) }
}, cancel () {
cancel () { ipcRenderer.send(`rename${this.id}`, null)
this.$electron.ipcRenderer.send(`rename${this.id}`, null) }
}
},
beforeDestroy () { beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('rename') ipcRenderer.removeAllListeners('rename')
} }
} }
</script> </script>
@@ -58,4 +59,4 @@ export default {
float right float right
.el-form-item__label .el-form-item__label
color #ddd color #ddd
</style> </style>
+181
View File
@@ -0,0 +1,181 @@
<template>
<div id="shortcut-page">
<div class="view-title">
快捷键设置
</div>
<el-row>
<el-col :span="20" :offset="2">
<el-table
:data="list"
size="mini"
>
<el-table-column
label="快捷键名称"
>
<template slot-scope="scope">
{{ scope.row.label ? scope.row.label : scope.row.name }}
</template>
</el-table-column>
<el-table-column
width="180px"
label="快捷键绑定"
prop="key"
>
</el-table-column>
<el-table-column
label="状态"
>
<template slot-scope="scope">
<el-tag
size="mini"
:type="scope.row.enable ? 'success' : 'danger'"
>
{{ scope.row.enable ? '已启用' : '已禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column
label="来源"
>
<template slot-scope="scope">
{{ calcOrigin(scope.row.name) }}
</template>
</el-table-column>
<el-table-column
label="操作"
>
<template slot-scope="scope">
<el-button
@click="toggleEnable(scope.row)"
size="mini"
:class="{
disabled: scope.row.enable
}"
type="text">
{{ scope.row.enable ? '禁用' : '启用' }}
</el-button>
<el-button
class="edit"
size="mini"
@click="openKeyBindingDialog(scope.row.name, scope.$index)"
type="text">
编辑
</el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
<el-dialog
title="修改上传快捷键"
:visible.sync="keyBindingVisible"
:modal-append-to-body="false"
>
<el-form
label-position="top"
label-width="80px"
>
<el-form-item
label="快捷上传"
>
<el-input
class="align-center"
@keydown.native.prevent="keyDetect($event)"
v-model="shortKey"
:autofocus="true"
></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="cancelKeyBinding" round>取消</el-button>
<el-button type="primary" @click="confirmKeyBinding" round>确定</el-button>
</span>
</el-dialog>
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator'
import keyDetect from '@/utils/key-binding'
import { ipcRenderer } from 'electron'
@Component({
name: 'shortcut-page'
})
export default class extends Vue {
list: ShortKeyConfig[] = []
keyBindingVisible = false
shortKeyName = ''
shortKey = ''
currentIndex = 0
created () {
const shortKeyConfig = this.$db.get('settings.shortKey') as ShortKeyConfigs
this.list = Object.keys(shortKeyConfig).map(item => shortKeyConfig[item])
}
@Watch('keyBindingVisible')
onKeyBindingVisibleChange (val: boolean) {
ipcRenderer.send('toggleShortKeyModifiedMode', val)
}
calcOrigin (item: string) {
const [origin] = item.split(':')
return origin
}
toggleEnable (item: ShortKeyConfig) {
const status = !item.enable
item.enable = status
this.$db.set(`settings.shortKey.${item.name}.enable`, status)
ipcRenderer.send('updateShortKey', item)
}
keyDetect (event: KeyboardEvent) {
this.shortKey = keyDetect(event).join('+')
}
openKeyBindingDialog (name: string, index: number) {
this.shortKeyName = name
this.shortKey = this.$db.get(`settings.shortKey.${name}.key`)
this.currentIndex = index
this.keyBindingVisible = true
}
cancelKeyBinding () {
this.keyBindingVisible = false
this.shortKey = this.$db.get(`settings.shortKey.${this.shortKeyName}.key`)
}
confirmKeyBinding () {
const oldKey = this.$db.get(`settings.shortKey.${this.shortKeyName}.key`)
this.$db.set(`settings.shortKey.${this.shortKeyName}.key`, this.shortKey)
const newKey = this.$db.get(`settings.shortKey.${this.shortKeyName}`)
ipcRenderer.send('updateShortKey', newKey, oldKey)
this.list[this.currentIndex].key = this.shortKey
this.keyBindingVisible = false
}
beforeDestroy () {
ipcRenderer.send('toggleShortKeyModifiedMode', false)
}
}
</script>
<style lang='stylus'>
#shortcut-page
.el-dialog__body
padding 10px 20px
.el-form-item
margin-bottom 0
.el-button
&.disabled
color: #F56C6C
&.edit
color: #67C23A
.el-table
background-color: transparent
color #ddd
thead
color #bbb
th,tr
background-color: transparent
&__body
tr.el-table__row--striped
td
background transparent
&--enable-row-hover
.el-table__body
tr:hover
&>td
background #333
</style>
+83 -84
View File
@@ -25,92 +25,91 @@
</div> </div>
</template> </template>
<script> <script lang="ts">
import mixin from '@/utils/mixin' import { Component, Vue } from 'vue-property-decorator'
import pasteTemplate from '~/main/utils/pasteTemplate' import mixin from '@/utils/mixin'
export default { import pasteTemplate from '#/utils/pasteTemplate'
name: 'tray-page', import { ipcRenderer, clipboard } from 'electron'
mixins: [mixin], @Component({
data () { name: 'tray-page',
return { mixins: [mixin]
files: [], })
notification: { export default class extends Vue {
title: '复制链接成功', files = []
body: '', notification = {
icon: '' title: '复制链接成功',
}, body: '',
clipboardFiles: [], icon: ''
uploadFlag: false }
} clipboardFiles: ImgInfo[] = []
}, uploadFlag = false
computed: { get reverseList () {
reverseList () { return this.files.slice().reverse()
return this.files.slice().reverse() }
} getData () {
}, // @ts-ignore
mounted () { this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
this.disableDragFile() }
this.getData() copyTheLink (item: ImgInfo) {
this.$electron.ipcRenderer.on('dragFiles', (event, files) => { this.notification.body = item.imgUrl!
files.forEach(item => { this.notification.icon = item.imgUrl!
this.$db.insert('uploaded', item) const myNotification = new Notification(this.notification.title, this.notification)
}) const pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value() clipboard.writeText(pasteTemplate(pasteStyle, item))
}) myNotification.onclick = () => {
this.$electron.ipcRenderer.on('clipboardFiles', (event, files) => { return true
this.clipboardFiles = files
})
this.$electron.ipcRenderer.on('uploadFiles', (event) => {
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
console.log(this.files)
this.uploadFlag = false
})
this.$electron.ipcRenderer.on('updateFiles', (event) => {
this.getData()
})
},
beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('dragFiles')
this.$electron.ipcRenderer.removeAllListeners('clipboardFiles')
this.$electron.ipcRenderer.removeAllListeners('uploadClipboardFiles')
this.$electron.ipcRenderer.removeAllListeners('updateFiles')
},
methods: {
getData () {
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
},
copyTheLink (item) {
this.notification.body = item.imgUrl
this.notification.icon = item.imgUrl
const myNotification = new window.Notification(this.notification.title, this.notification)
const pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
this.$electron.clipboard.writeText(pasteTemplate(pasteStyle, item))
myNotification.onclick = () => {
return true
}
},
calcHeight (width, height) {
return height * 160 / width
},
disableDragFile () {
window.addEventListener('dragover', (e) => {
e = e || event
e.preventDefault()
}, false)
window.addEventListener('drop', (e) => {
e = e || event
e.preventDefault()
}, false)
},
uploadClipboardFiles () {
if (this.uploadFlag) {
return
}
this.uploadFlag = true
this.$electron.ipcRenderer.send('uploadClipboardFiles')
}
} }
} }
calcHeight (width: number, height: number): number {
return height * 160 / width
}
disableDragFile () {
window.addEventListener('dragover', (e) => {
e = e || event
e.preventDefault()
}, false)
window.addEventListener('drop', (e) => {
e = e || event
e.preventDefault()
}, false)
}
uploadClipboardFiles () {
if (this.uploadFlag) {
return
}
this.uploadFlag = true
ipcRenderer.send('uploadClipboardFiles')
}
mounted () {
this.disableDragFile()
this.getData()
ipcRenderer.on('dragFiles', (event: Event, files: string[]) => {
files.forEach(item => {
this.$db.insert('uploaded', item)
})
// @ts-ignore
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
})
ipcRenderer.on('clipboardFiles', (event: Event, files: ImgInfo[]) => {
this.clipboardFiles = files
})
ipcRenderer.on('uploadFiles', (event: Event) => {
// @ts-ignore
this.files = this.$db.read().get('uploaded').slice().reverse().slice(0, 5).value()
console.log(this.files)
this.uploadFlag = false
})
ipcRenderer.on('updateFiles', (event: Event) => {
this.getData()
})
}
beforeDestroy () {
ipcRenderer.removeAllListeners('dragFiles')
ipcRenderer.removeAllListeners('clipboardFiles')
ipcRenderer.removeAllListeners('uploadClipboardFiles')
ipcRenderer.removeAllListeners('updateFiles')
}
}
</script> </script>
<style lang="stylus"> <style lang="stylus">
+109 -103
View File
@@ -20,12 +20,12 @@
<input type="file" id="file-uploader" @change="onChange" multiple> <input type="file" id="file-uploader" @change="onChange" multiple>
</div> </div>
</div> </div>
<el-progress <el-progress
:percentage="progress" :percentage="progress"
:show-text="false" :show-text="false"
class="upload-progress" class="upload-progress"
:class="{ 'show': showProgress }" :class="{ 'show': showProgress }"
:status="showError ? 'exception' : 'text'" :status="showError ? 'exception' : undefined"
></el-progress> ></el-progress>
<div class="paste-style"> <div class="paste-style">
<div class="el-col-16"> <div class="el-col-16">
@@ -55,23 +55,28 @@
</el-row> </el-row>
</div> </div>
</template> </template>
<script> <script lang="ts">
export default { import { Component, Vue, Watch } from 'vue-property-decorator'
name: 'upload', import {
data () { ipcRenderer,
return { IpcRendererEvent,
dragover: false, remote
progress: 0, } from 'electron'
showProgress: false, const { Menu } = remote
showError: false, @Component({
pasteStyle: '', name: 'upload'
picBed: [], })
picBedName: '', export default class extends Vue {
menu: null dragover = false
} progress = 0
}, showProgress = false
showError = false
pasteStyle = ''
picBed: PicBedType[] = []
picBedName = ''
menu: Electron.Menu | null= null
mounted () { mounted () {
this.$electron.ipcRenderer.on('uploadProgress', (event, progress) => { ipcRenderer.on('uploadProgress', (event: IpcRendererEvent, progress: number) => {
if (progress !== -1) { if (progress !== -1) {
this.showProgress = true this.showProgress = true
this.progress = progress this.progress = progress
@@ -82,93 +87,92 @@ export default {
}) })
this.getPasteStyle() this.getPasteStyle()
this.getDefaultPicBed() this.getDefaultPicBed()
this.$electron.ipcRenderer.on('syncPicBed', () => { ipcRenderer.on('syncPicBed', () => {
this.getDefaultPicBed() this.getDefaultPicBed()
}) })
this.$electron.ipcRenderer.send('getPicBeds') ipcRenderer.send('getPicBeds')
this.$electron.ipcRenderer.on('getPicBeds', this.getPicBeds) ipcRenderer.on('getPicBeds', this.getPicBeds)
}, }
watch: { @Watch('progress')
progress (val) { onProgressChange (val: number) {
if (val === 100) { if (val === 100) {
setTimeout(() => { setTimeout(() => {
this.showProgress = false this.showProgress = false
this.showError = false this.showError = false
}, 1000) }, 1000)
setTimeout(() => { setTimeout(() => {
this.progress = 0 this.progress = 0
}, 1200) }, 1200)
}
} }
}, }
beforeDestroy () { beforeDestroy () {
this.$electron.ipcRenderer.removeAllListeners('uploadProgress') ipcRenderer.removeAllListeners('uploadProgress')
this.$electron.ipcRenderer.removeAllListeners('syncPicBed') ipcRenderer.removeAllListeners('syncPicBed')
this.$electron.ipcRenderer.removeListener('getPicBeds', this.getPicBeds) ipcRenderer.removeListener('getPicBeds', this.getPicBeds)
}, }
methods: { onDrop (e: DragEvent) {
onDrop (e) { this.dragover = false
this.dragover = false this.ipcSendFiles(e.dataTransfer!.files)
this.ipcSendFiles(e.dataTransfer.files) }
}, openUplodWindow () {
openUplodWindow () { document.getElementById('file-uploader')!.click()
document.getElementById('file-uploader').click() }
}, onChange (e: any) {
onChange (e) { this.ipcSendFiles(e.target.files);
this.ipcSendFiles(e.target.files) (document.getElementById('file-uploader') as HTMLInputElement).value = ''
document.getElementById('file-uploader').value = '' }
}, ipcSendFiles (files: FileList) {
ipcSendFiles (files) { let sendFiles: FileWithPath[] = []
let sendFiles = [] Array.from(files).forEach((item, index) => {
Array.from(files).forEach((item, index) => { let obj = {
let obj = { name: item.name,
name: item.name, path: item.path
path: item.path }
sendFiles.push(obj)
})
ipcRenderer.send('uploadChoosedFiles', sendFiles)
}
getPasteStyle () {
this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
}
handlePasteStyleChange (val: string) {
this.$db.set('settings.pasteStyle', val)
}
uploadClipboardFiles () {
ipcRenderer.send('uploadClipboardFilesFromUploadPage')
}
getDefaultPicBed () {
const current: string = this.$db.get('picBed.current')
this.picBed.forEach(item => {
if (item.type === current) {
this.picBedName = item.name
}
})
}
getPicBeds (event: Event, picBeds: PicBedType[]) {
this.picBed = picBeds
this.getDefaultPicBed()
}
handleChangePicBed () {
this.buildMenu()
// this.menu.popup(remote.getCurrentWindow())
this.menu!.popup()
}
buildMenu () {
const _this = this
const submenu = this.picBed.map(item => {
return {
label: item.name,
type: 'radio',
checked: this.$db.get('picBed.current') === item.type,
click () {
_this.$db.set('picBed.current', item.type)
ipcRenderer.send('syncPicBed')
} }
sendFiles.push(obj) }
}) })
this.$electron.ipcRenderer.send('uploadChoosedFiles', sendFiles) // @ts-ignore
}, this.menu = Menu.buildFromTemplate(submenu)
getPasteStyle () {
this.pasteStyle = this.$db.get('settings.pasteStyle') || 'markdown'
},
handlePasteStyleChange (val) {
this.$db.set('settings.pasteStyle', val)
},
uploadClipboardFiles () {
this.$electron.ipcRenderer.send('uploadClipboardFilesFromUploadPage')
},
getDefaultPicBed () {
const current = this.$db.get('picBed.current')
this.picBed.forEach(item => {
if (item.type === current) {
this.picBedName = item.name
}
})
},
getPicBeds (event, picBeds) {
this.picBed = picBeds
this.getDefaultPicBed()
},
handleChangePicBed () {
this.buildMenu()
this.menu.popup(this.$electron.remote.getCurrentWindow())
},
buildMenu () {
const _this = this
const submenu = this.picBed.map(item => {
return {
label: item.name,
type: 'radio',
checked: this.$db.get('picBed.current') === item.type,
click () {
_this.$db.set('picBed.current', item.type)
_this.$electron.ipcRenderer.send('syncPicBed')
}
}
})
this.menu = this.$electron.remote.Menu.buildFromTemplate(submenu)
}
} }
} }
</script> </script>
@@ -235,4 +239,6 @@ export default {
border-radius 0 14px 14px 0 border-radius 0 14px 14px 0
.paste-upload .paste-upload
width 100% width 100%
</style> .el-icon-caret-bottom
cursor pointer
</style>
+4 -4
View File
@@ -5,7 +5,7 @@
<div class="view-title"> <div class="view-title">
阿里云OSS设置 阿里云OSS设置
</div> </div>
<el-form <el-form
ref="aliyun" ref="aliyun"
label-position="right" label-position="right"
label-width="120px" label-width="120px"
@@ -111,7 +111,7 @@ export default {
<style lang='stylus'> <style lang='stylus'>
#aliyun-view #aliyun-view
.el-form .el-form
label label
line-height 22px line-height 22px
padding-bottom 0 padding-bottom 0
color #eee color #eee
@@ -122,7 +122,7 @@ export default {
.el-radio-group .el-radio-group
width 100% width 100%
label label
width 25% width 25%
.el-radio-button__inner .el-radio-button__inner
width 100% width 100%
.el-radio-button:first-child .el-radio-button:first-child
@@ -146,4 +146,4 @@ export default {
transition .2s color ease-in-out transition .2s color ease-in-out
&:hover &:hover
color #409EFF color #409EFF
</style> </style>
+3 -3
View File
@@ -5,7 +5,7 @@
<div class="view-title"> <div class="view-title">
GitHub设置 GitHub设置
</div> </div>
<el-form <el-form
ref="github" ref="github"
label-position="right" label-position="right"
label-width="120px" label-width="120px"
@@ -102,7 +102,7 @@ export default {
<style lang='stylus'> <style lang='stylus'>
#github-view #github-view
.el-form .el-form
label label
line-height 22px line-height 22px
padding-bottom 0 padding-bottom 0
color #eee color #eee
@@ -111,7 +111,7 @@ export default {
.el-radio-group .el-radio-group
width 100% width 100%
label label
width 25% width 25%
.el-radio-button__inner .el-radio-button__inner
width 100% width 100%
.el-radio-button:first-child .el-radio-button:first-child
+4 -4
View File
@@ -5,7 +5,7 @@
<div class="view-title"> <div class="view-title">
Imgur图床设置 Imgur图床设置
</div> </div>
<el-form <el-form
ref="imgur" ref="imgur"
label-position="right" label-position="right"
label-width="120px" label-width="120px"
@@ -79,7 +79,7 @@ export default {
<style lang='stylus'> <style lang='stylus'>
#imgur-view #imgur-view
.el-form .el-form
label label
line-height 22px line-height 22px
padding-bottom 0 padding-bottom 0
color #eee color #eee
@@ -90,7 +90,7 @@ export default {
.el-radio-group .el-radio-group
width 100% width 100%
label label
width 25% width 25%
.el-radio-button__inner .el-radio-button__inner
width 100% width 100%
.el-radio-button:first-child .el-radio-button:first-child
@@ -114,4 +114,4 @@ export default {
transition .2s color ease-in-out transition .2s color ease-in-out
&:hover &:hover
color #409EFF color #409EFF
</style> </style>
+2 -2
View File
@@ -84,7 +84,7 @@ export default {
<style lang='stylus'> <style lang='stylus'>
#others-view #others-view
.el-form .el-form
label label
line-height 22px line-height 22px
padding-bottom 0 padding-bottom 0
color #eee color #eee
@@ -106,4 +106,4 @@ export default {
margin-bottom 10px margin-bottom 10px
.single .single
text-align center text-align center
</style> </style>
+4 -4
View File
@@ -5,7 +5,7 @@
<div class="view-title"> <div class="view-title">
七牛图床设置 七牛图床设置
</div> </div>
<el-form <el-form
ref="qiniu" ref="qiniu"
label-position="right" label-position="right"
label-width="120px" label-width="120px"
@@ -119,7 +119,7 @@ export default {
<style lang='stylus'> <style lang='stylus'>
#qiniu-view #qiniu-view
.el-form .el-form
label label
line-height 22px line-height 22px
padding-bottom 0 padding-bottom 0
color #eee color #eee
@@ -130,7 +130,7 @@ export default {
.el-radio-group .el-radio-group
width 100% width 100%
label label
width 25% width 25%
.el-radio-button__inner .el-radio-button__inner
width 100% width 100%
.el-radio-button:first-child .el-radio-button:first-child
@@ -141,4 +141,4 @@ export default {
.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
</style> </style>
+4 -4
View File
@@ -5,7 +5,7 @@
<div class="view-title"> <div class="view-title">
腾讯云COS设置 腾讯云COS设置
</div> </div>
<el-form <el-form
ref="tcyun" ref="tcyun"
label-position="right" label-position="right"
label-width="120px" label-width="120px"
@@ -137,7 +137,7 @@ export default {
<style lang='stylus'> <style lang='stylus'>
#tcyun-view #tcyun-view
.el-form .el-form
label label
line-height 22px line-height 22px
padding-bottom 0 padding-bottom 0
color #eee color #eee
@@ -148,7 +148,7 @@ export default {
.el-radio-group .el-radio-group
width 100% width 100%
label label
width 25% width 25%
.el-radio-button__inner .el-radio-button__inner
width 100% width 100%
.el-radio-button:first-child .el-radio-button:first-child
@@ -172,4 +172,4 @@ export default {
transition .2s color ease-in-out transition .2s color ease-in-out
&:hover &:hover
color #409EFF color #409EFF
</style> </style>
+4 -4
View File
@@ -5,7 +5,7 @@
<div class="view-title"> <div class="view-title">
又拍云设置 又拍云设置
</div> </div>
<el-form <el-form
ref="tcyun" ref="tcyun"
label-position="right" label-position="right"
label-width="120px" label-width="120px"
@@ -110,7 +110,7 @@ export default {
<style lang='stylus'> <style lang='stylus'>
#tcyun-view #tcyun-view
.el-form .el-form
label label
line-height 22px line-height 22px
padding-bottom 0 padding-bottom 0
color #eee color #eee
@@ -119,7 +119,7 @@ export default {
.el-radio-group .el-radio-group
width 100% width 100%
label label
width 25% width 25%
.el-radio-button__inner .el-radio-button__inner
width 100% width 100%
.el-radio-button:first-child .el-radio-button:first-child
@@ -130,4 +130,4 @@ export default {
.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
</style> </style>
+3 -3
View File
@@ -5,7 +5,7 @@
<div class="view-title"> <div class="view-title">
微博图床设置 微博图床设置
</div> </div>
<el-form <el-form
ref="weiboForm" ref="weiboForm"
label-position="right" label-position="right"
label-width="120px" label-width="120px"
@@ -123,7 +123,7 @@ export default {
left 60% left 60%
#weibo-view #weibo-view
.el-form .el-form
label label
line-height 22px line-height 22px
padding-bottom 0 padding-bottom 0
color #eee color #eee
@@ -148,4 +148,4 @@ export default {
transition .2s color ease-in-out transition .2s color ease-in-out
&:hover &:hover
color #409EFF color #409EFF
</style> </style>
-108
View File
@@ -1,108 +0,0 @@
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'tray-page',
component: require('@/pages/TrayPage').default
},
{
path: '/rename-page',
name: 'rename-page',
component: require('@/pages/RenamePage').default
},
{
path: '/mini-page',
name: 'mini-page',
component: require('@/pages/MiniPage').default
},
{
path: '/setting',
name: 'setting-page',
component: require('@/layouts/SettingPage').default,
children: [
{
path: 'upload',
component: require('@/pages/Upload').default,
name: 'upload'
},
{
path: 'weibo',
component: require('@/pages/picbeds/Weibo').default,
name: 'weibo'
},
{
path: 'qiniu',
component: require('@/pages/picbeds/Qiniu').default,
name: 'qiniu'
},
{
path: 'tcyun',
component: require('@/pages/picbeds/TcYun').default,
name: 'tcyun'
},
{
path: 'upyun',
component: require('@/pages/picbeds/UpYun').default,
name: 'upyun'
},
{
path: 'github',
component: require('@/pages/picbeds/GitHub').default,
name: 'github'
},
{
path: 'smms',
component: require('@/pages/picbeds/SMMS').default,
name: 'smms'
},
{
path: 'aliyun',
component: require('@/pages/picbeds/AliYun').default,
name: 'aliyun'
},
{
path: 'imgur',
component: require('@/pages/picbeds/Imgur').default,
name: 'imgur'
},
{
path: 'others/:type',
component: require('@/pages/picbeds/Others').default,
name: 'others'
},
{
path: 'gallery',
component: require('@/pages/Gallery').default,
name: 'gallery',
meta: {
keepAlive: true
}
},
{
path: 'setting',
component: require('@/pages/PicGoSetting').default,
name: 'setting'
},
{
path: 'plugin',
component: require('@/pages/Plugin').default,
name: 'plugin'
},
{
path: 'shortcut-page',
component: require('@/pages/ShortCutPage').default,
name: 'shortcut-page'
}
]
},
{
path: '*',
redirect: '/'
}
]
})
+108
View File
@@ -0,0 +1,108 @@
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'tray-page',
component: () => import(/* webpackChunkName: "tray" */ '@/pages/TrayPage.vue')
},
{
path: '/rename-page',
name: 'rename-page',
component: () => import(/* webpackChunkName: "RenamePage" */ '@/pages/RenamePage.vue')
},
{
path: '/mini-page',
name: 'mini-page',
component: () => import(/* webpackChunkName: "MiniPage" */ '@/pages/MiniPage.vue')
},
{
path: '/setting',
name: 'setting-page',
component: () => import(/* webpackChunkName: "SettingPage" */ '@/layouts/SettingPage.vue'),
children: [
{
path: 'upload',
component: () => import(/* webpackChunkName: "Upload" */ '@/pages/Upload.vue'),
name: 'upload'
},
{
path: 'weibo',
component: () => import(/* webpackChunkName: "Weibo" */ '@/pages/picbeds/Weibo.vue'),
name: 'weibo'
},
{
path: 'qiniu',
component: () => import(/* webpackChunkName: "Qiniu" */ '@/pages/picbeds/Qiniu.vue'),
name: 'qiniu'
},
{
path: 'tcyun',
component: () => import(/* webpackChunkName: "TcYun" */ '@/pages/picbeds/TcYun.vue'),
name: 'tcyun'
},
{
path: 'upyun',
component: () => import(/* webpackChunkName: "UpYun" */ '@/pages/picbeds/UpYun.vue'),
name: 'upyun'
},
{
path: 'github',
component: () => import(/* webpackChunkName: "GitHub" */ '@/pages/picbeds/GitHub.vue'),
name: 'github'
},
{
path: 'smms',
component: () => import(/* webpackChunkName: "SMMS" */ '@/pages/picbeds/SMMS.vue'),
name: 'smms'
},
{
path: 'aliyun',
component: () => import(/* webpackChunkName: "AliYun" */ '@/pages/picbeds/AliYun.vue'),
name: 'aliyun'
},
{
path: 'imgur',
component: () => import(/* webpackChunkName: "Imgur" */ '@/pages/picbeds/Imgur.vue'),
name: 'imgur'
},
{
path: 'others/:type',
component: () => import(/* webpackChunkName: "Other" */ '@/pages/picbeds/Others.vue'),
name: 'others'
},
{
path: 'gallery',
component: () => import(/* webpackChunkName: "Gallery" */ '@/pages/Gallery.vue'),
name: 'gallery',
meta: {
keepAlive: true
}
},
{
path: 'setting',
component: () => import(/* webpackChunkName: "setting" */ '@/pages/PicGoSetting.vue'),
name: 'setting'
},
{
path: 'plugin',
component: () => import(/* webpackChunkName: "Plugin" */ '@/pages/Plugin.vue'),
name: 'plugin'
},
{
path: 'shortcut',
component: () => import(/* webpackChunkName: "ShortkeyPage" */ '@/pages/ShortCut.vue'),
name: 'shortcut'
}
]
},
{
path: '*',
redirect: '/'
}
]
})
-20
View File
@@ -1,20 +0,0 @@
export default {
name: '',
data () {
return {
defaultPicBed: this.$db.get('picBed.current')
}
},
methods: {
setDefaultPicBed (type) {
this.$db.set('picBed.current', type)
this.defaultPicBed = type
const successNotification = new window.Notification('设置默认图床', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
}
}
}
+15
View File
@@ -0,0 +1,15 @@
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class extends Vue {
defaultPicBed = this.$db.get('picBed.current')
setDefaultPicBed (type: string) {
this.$db.set('picBed.current', type)
this.defaultPicBed = type
const successNotification = new Notification('设置默认图床', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
}
}
}
@@ -1,13 +1,13 @@
class LS { class LS {
get (name) { get (name: string) {
if (localStorage.getItem(name)) { if (localStorage.getItem(name)) {
return JSON.parse(localStorage.getItem(name)) return JSON.parse(localStorage.getItem(name) as string)
} else { } else {
return {} return {}
} }
} }
set (name, value) { set (name: string, value: any) {
return localStorage.setItem(name, JSON.stringify(value)) return localStorage.setItem(name, JSON.stringify(value))
} }
} }
@@ -1,6 +1,6 @@
import keycode from 'keycode' import keycode from 'keycode'
const isSpecialKey = (keyCode) => { const isSpecialKey = (keyCode: number) => {
const keyArr = [ const keyArr = [
16, // Shift 16, // Shift
17, // Ctrl 17, // Ctrl
@@ -12,14 +12,14 @@ const isSpecialKey = (keyCode) => {
return keyArr.includes(keyCode) return keyArr.includes(keyCode)
} }
const keyDetect = (event) => { const keyDetect = (event: KeyboardEvent) => {
const meta = process.platform === 'darwin' ? 'Cmd' : 'Super' const meta = process.platform === 'darwin' ? 'Cmd' : 'Super'
let specialKey = { let specialKey = {
Ctrl: event.ctrlKey, Ctrl: event.ctrlKey,
Shift: event.shiftKey, Shift: event.shiftKey,
Alt: event.altKey Alt: event.altKey,
[meta]: event.metaKey
} }
specialKey[meta] = event.metaKey
let pressKey = [] let pressKey = []
-25
View File
@@ -1,25 +0,0 @@
export default {
mounted () {
this.disableDragEvent()
},
methods: {
disableDragEvent () {
window.addEventListener('dragenter', this.disableDrag, false)
window.addEventListener('dragover', this.disableDrag)
window.addEventListener('drop', this.disableDrag)
},
disableDrag (e) {
const dropzone = document.getElementById('upload-area')
if (dropzone === null || !dropzone.contains(e.target)) {
e.preventDefault()
e.dataTransfer.effectAllowed = 'none'
e.dataTransfer.dropEffect = 'none'
}
}
},
beforeDestroy () {
window.removeEventListener('dragenter', this.disableDrag, false)
window.removeEventListener('dragover', this.disableDrag)
window.removeEventListener('drop', this.disableDrag)
}
}
+25
View File
@@ -0,0 +1,25 @@
import { Component, Vue } from 'vue-property-decorator'
@Component
export default class extends Vue {
mounted () {
this.disableDragEvent()
}
disableDragEvent () {
window.addEventListener('dragenter', this.disableDrag, false)
window.addEventListener('dragover', this.disableDrag)
window.addEventListener('drop', this.disableDrag)
}
disableDrag (e: DragEvent) {
const dropzone = document.getElementById('upload-area')
if (dropzone === null || !dropzone.contains(<Node>e.target)) {
e.preventDefault()
e.dataTransfer!.effectAllowed = 'none'
e.dataTransfer!.dropEffect = 'none'
}
}
beforeDestroy () {
window.removeEventListener('dragenter', this.disableDrag, false)
window.removeEventListener('dragover', this.disableDrag)
window.removeEventListener('drop', this.disableDrag)
}
}
@@ -1,17 +1,11 @@
import Datastore from 'lowdb' import Datastore from 'lowdb'
// @ts-ignore
import LodashId from 'lodash-id' import LodashId from 'lodash-id'
import FileSync from 'lowdb/adapters/FileSync' import FileSync from 'lowdb/adapters/FileSync'
import path from 'path' import path from 'path'
import fs from 'fs-extra' import fs from 'fs-extra'
import { remote, app } from 'electron' import { remote, app } from 'electron'
if (process.env.NODE_ENV !== 'development') {
global.__static = path.join(__dirname, '/static').replace(/\\/g, '\\\\')
}
if (process.env.DEBUG_ENV === 'debug') {
global.__static = path.join(__dirname, '../../static').replace(/\\/g, '\\\\')
}
const APP = process.type === 'renderer' ? remote.app : app const APP = process.type === 'renderer' ? remote.app : app
const STORE_PATH = APP.getPath('userData') const STORE_PATH = APP.getPath('userData')
@@ -22,6 +16,7 @@ if (process.type !== 'renderer') {
} }
class DB { class DB {
private db: Datastore.LowdbSync<Datastore.AdapterSync>
constructor () { constructor () {
const adapter = new FileSync(path.join(STORE_PATH, '/data.json')) const adapter = new FileSync(path.join(STORE_PATH, '/data.json'))
@@ -53,13 +48,14 @@ class DB {
get (key = '') { get (key = '') {
return this.read().get(key).value() return this.read().get(key).value()
} }
set (key, value) { set (key: string, value: any) {
return this.read().set(key, value).write() return this.read().set(key, value).write()
} }
has (key) { has (key: string) {
return this.read().has(key).value() return this.read().has(key).value()
} }
insert (key, value) { insert (key: string, value: any): void {
// @ts-ignore
return this.read().get(key).insert(value).write() return this.read().get(key).insert(value).write()
} }
} }
+12
View File
@@ -0,0 +1,12 @@
import VueRouter, { Route } from 'vue-router'
import db from '#/datastore'
import axios from 'axios'
declare module 'vue/types/vue' {
interface Vue {
$router: VueRouter,
$route: Route,
$db: typeof db
$http: typeof axios
$builtInPicBed: string[]
}
}
+2
View File
@@ -0,0 +1,2 @@
import PicGoCore from 'picgo'
export default PicGoCore
+13
View File
@@ -0,0 +1,13 @@
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any
}
}
}
+4
View File
@@ -0,0 +1,4 @@
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
+161
View File
@@ -0,0 +1,161 @@
// global
interface Obj {
[propName: string]: any
}
interface ObjT<T> {
[propName: string]: T
}
// Image && PicBed
interface ImgInfo {
buffer?: Buffer
base64Image?: string
fileName?: string
width?: number
height?: number
extname?: string
imgUrl?: string
[propName: string]: any
}
interface PicBedType {
type: string
name: string
visible: boolean
}
// Config Settings
interface ShortKeyConfig {
enable: boolean
key: string // 按键
name: string
label: string
}
interface ShortKeyConfigs {
[propName: string]: ShortKeyConfig
}
interface OldShortKeyConfigs {
upload: string
}
// Main process
interface BrowserWindowOptions {
height: number,
width: number,
show: boolean,
fullscreenable: boolean,
resizable: boolean,
webPreferences: {
nodeIntegration: boolean,
nodeIntegrationInWorker: boolean,
backgroundThrottling: boolean
webSecurity?: boolean
},
vibrancy?: string | any,
frame?: boolean
center?: boolean
title?: string
titleBarStyle?: string | any
backgroundColor?: string
autoHideMenuBar?: boolean
transparent?: boolean
icon?: string
skipTaskbar?: boolean
alwaysOnTop?: boolean
}
interface FileWithPath {
path: string
name?: string
}
interface Bounds {
x: number
y: number
}
declare enum PasteStyle {
MARKDOWN = 'markdown',
HTML = 'HTML',
URL = 'URL',
UBB = 'UBB',
CUSTOM = 'Custom'
}
// global value
declare var __static: string
// third-party
declare module 'fix-path' {
function fixPath(): void
export default fixPath
}
// PicGo Types
declare enum PicGoHelperType {
afterUploadPlugins = 'afterUploadPlugins',
beforeTransformPlugins = 'beforeTransformPlugins',
beforeUploadPlugins = 'beforeUploadPlugins',
uploader = 'uploader',
transformer = 'transformer'
}
interface IPicGoPlugin {
name: string
author: string
description: string
logo: string
version: string | number
gui: boolean
config: {
plugin: IPluginMenuConfig
uploader: IPluginMenuConfig
transformer: IPluginMenuConfig
[index: string]: IPluginMenuConfig
} | {
[propName: string]: any
}
enabled?: boolean
homepage: string
guiMenu?: any[]
ing: boolean
hasInstall?: boolean
}
interface IPluginMenuConfig {
name: string
config: any[]
}
interface INPMSearchResult {
data: {
objects: INPMSearchResultObject[]
}
}
interface INPMSearchResultObject {
package: {
name: string
scope: string
version: string
description: string
keywords: string[]
author: {
name: string
}
links: {
npm: string
homepage: string
}
}
}
// GuiApi
interface IShowInputBoxOption {
title: string
placeholder: string
}
+14
View File
@@ -0,0 +1,14 @@
interface ISettingForm {
updateHelper: boolean
showPicBedList: string[]
autoStart: boolean
rename: boolean
autoRename: boolean
uploadNotification: boolean
miniWindowOntop: boolean
logLevel: string[]
}
interface ShortKeyMap {
[propName: string]: string
}
@@ -1,13 +1,13 @@
import db from '../../datastore' import db from '#/datastore'
const formatCustomLink = (customLink, item) => { 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 let url = item.url || item.imgUrl
let formatObj = { const formatObj = {
url, url,
fileName fileName
} }
let keys = Object.keys(formatObj) const keys = Object.keys(formatObj) as ['url', 'fileName']
keys.forEach(item => { keys.forEach(item => {
if (customLink.indexOf(`$${item}`) !== -1) { if (customLink.indexOf(`$${item}`) !== -1) {
let reg = new RegExp(`\\$${item}`, 'g') let reg = new RegExp(`\\$${item}`, 'g')
@@ -17,7 +17,7 @@ const formatCustomLink = (customLink, item) => {
return customLink return customLink
} }
export default (style, item) => { export default (style: PasteStyle, item: ImgInfo) => {
let url = item.url || item.imgUrl let url = item.url || item.imgUrl
const customLink = db.get('settings.customLink') || '$url' const customLink = db.get('settings.customLink') || '$url'
const tpl = { const tpl = {
View File
+52
View File
@@ -0,0 +1,52 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"typeRoots": [
"./src/universal/types/*",
],
"paths": {
"@/*": [
"src/renderer/*"
],
"~/*": [
"src/*"
],
"root/*": [
"./*"
],
"#/*": [
"src/universal/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
+61
View File
@@ -0,0 +1,61 @@
const path = require('path')
function resolve (dir) {
return path.join(__dirname, dir)
}
module.exports = {
chainWebpack: config => {
config.resolve.alias
.set('@', resolve('src/renderer'))
.set('~', resolve('src'))
.set('root', resolve('./'))
.set('#', resolve('src/universal'))
},
pluginOptions: {
electronBuilder: {
chainWebpackMainProcess: config => {
config.resolve.alias
.set('@', resolve('src/renderer'))
.set('~', resolve('src'))
.set('root', resolve('./'))
.set('#', resolve('src/universal'))
},
builderOptions: {
productName: 'PicGo',
appId: 'com.molunerfinn.picgo',
dmg: {
contents: [
{
x: 410,
y: 150,
type: 'link',
path: '/Applications'
},
{
x: 130,
y: 150,
type: 'file'
}
]
},
mac: {
icon: 'build/icons/icon.icns',
extendInfo: {
LSUIElement: 1
}
},
win: {
icon: 'build/icons/icon.ico',
target: 'nsis'
},
nsis: {
shortcutName: 'PicGo',
oneClick: false,
allowToChangeInstallationDirectory: true
},
linux: {
icon: 'build/icons/'
}
}
}
}
}
+5753 -4753
View File
File diff suppressed because it is too large Load Diff