PicList/src/renderer/pages/Plugin.vue

646 lines
18 KiB
Vue
Raw Normal View History

2018-09-17 04:52:01 -04:00
<template>
<div id="plugin-view">
<div class="view-title">
插件设置 -
<el-tooltip :content="pluginListToolTip" placement="right">
<i class="el-icon-goods" @click="goAwesomeList"></i>
</el-tooltip>
<el-tooltip :content="importLocalPluginToolTip" placement="left">
<i class="el-icon-download" @click="handleImportLocalPlugin"/>
</el-tooltip>
2018-09-17 04:52:01 -04:00
</div>
2018-09-28 05:31:22 -04:00
<el-row class="handle-bar" :class="{ 'cut-width': pluginList.length > 6 }">
2018-09-17 04:52:01 -04:00
<el-input
v-model="searchText"
2019-01-31 03:40:08 -05:00
placeholder="搜索npm上的PicGo插件或者点击上方按钮查看优秀插件列表"
2018-09-17 04:52:01 -04:00
size="small"
2018-09-28 05:31:22 -04:00
>
<i slot="suffix" class="el-input__icon el-icon-close" v-if="searchText" @click="cleanSearch" style="cursor: pointer"></i>
</el-input>
2018-09-17 04:52:01 -04:00
</el-row>
2018-09-28 05:31:22 -04:00
<el-row :gutter="10" class="plugin-list" v-loading="loading">
<el-col :span="12" v-for="item in pluginList" :key="item.fullName">
2018-12-28 07:30:47 -05:00
<div class="plugin-item" :class="{ 'darwin': os === 'darwin' }">
2019-01-11 04:22:33 -05:00
<div class="cli-only-badge" v-if="!item.gui" title="CLI only">CLI</div>
2018-09-28 05:31:22 -04:00
<img class="plugin-item__logo" :src="item.logo"
:onerror="defaultLogo"
2018-09-28 05:31:22 -04:00
>
2018-12-18 05:58:31 -05:00
<div
class="plugin-item__content"
:class="{ disabled: !item.enabled }"
>
<div class="plugin-item__name" @click="openHomepage(item.homepage)">
2018-10-10 05:25:44 -04:00
{{ item.name }} <small>{{ ' ' + item.version }}</small>
2018-09-17 04:52:01 -04:00
</div>
<div class="plugin-item__desc" :title="item.description">
{{ item.description }}
2018-09-17 04:52:01 -04:00
</div>
<div class="plugin-item__info-bar">
<span class="plugin-item__author">
{{ item.author }}
2018-09-17 04:52:01 -04:00
</span>
<span class="plugin-item__config" >
2018-09-28 05:31:22 -04:00
<template v-if="searchText">
<template v-if="!item.hasInstall">
2018-12-18 05:58:31 -05:00
<span class="config-button install" v-if="!item.ing" @click="installPlugin(item)">
安装
</span>
2018-12-18 05:58:31 -05:00
<span v-else-if="item.ing" class="config-button ing">
安装中
</span>
</template>
2018-12-18 05:58:31 -05:00
<span v-else class="config-button ing">
已安装
</span>
</template>
<template v-else>
2018-12-18 05:58:31 -05:00
<span v-if="item.ing" class="config-button ing">
2018-12-20 09:30:08 -05:00
进行中
2018-09-28 05:31:22 -04:00
</span>
2018-12-18 05:58:31 -05:00
<template v-else>
<i
v-if="item.enabled"
class="el-icon-setting"
@click="buildContextMenu(item)"
></i>
<i
2018-12-28 07:30:47 -05:00
v-else
2018-12-18 05:58:31 -05:00
class="el-icon-remove-outline"
@click="buildContextMenu(item)"
></i>
</template>
2018-09-28 05:31:22 -04:00
</template>
2018-09-17 04:52:01 -04:00
</span>
</div>
</div>
</div>
</el-col>
</el-row>
2018-12-24 03:05:30 -05:00
<el-row v-show="needReload" class="reload-mask" :class="{ 'cut-width': pluginList.length > 6 }">
<el-button type="primary" @click="reloadApp" size="mini" round>重启以生效</el-button>
</el-row>
2018-09-19 05:27:09 -04:00
<el-dialog
:visible.sync="dialogVisible"
:modal-append-to-body="false"
:title="`配置${configName}`"
width="70%"
>
<config-form
:config="config"
:type="currentType"
:id="configName"
2018-09-19 05:27:09 -04:00
ref="configForm"
>
</config-form>
<span slot="footer">
<el-button @click="dialogVisible = false" round>取消</el-button>
<el-button type="primary" @click="handleConfirmConfig" round>确定</el-button>
</span>
</el-dialog>
2018-09-17 04:52:01 -04:00
</div>
</template>
2019-12-19 06:17:21 -05:00
<script lang="ts">
import {
Component,
Vue,
Watch
} from 'vue-property-decorator'
import ConfigForm from '@/components/ConfigForm.vue'
2018-09-28 05:31:22 -04:00
import { debounce } from 'lodash'
2019-12-19 06:17:21 -05:00
import {
ipcRenderer,
remote,
IpcRendererEvent
} from 'electron'
import { handleStreamlinePluginName } from '~/universal/utils/common'
2019-12-19 06:17:21 -05:00
const { Menu } = remote
@Component({
2018-09-17 04:52:01 -04:00
name: 'plugin',
2018-09-19 05:27:09 -04:00
components: {
ConfigForm
2019-12-19 06:17:21 -05:00
}
})
export default class extends Vue {
searchText = ''
pluginList: IPicGoPlugin[] = []
menu: Electron.Menu | null = null
config: any[] = []
currentType = ''
configName = ''
dialogVisible = false
pluginNameList: string[] = []
loading = true
needReload = false
pluginListToolTip = '插件列表'
importLocalPluginToolTip = '导入本地插件'
2019-12-19 06:17:21 -05:00
id = ''
os = ''
defaultLogo: string = 'this.src="https://cdn.jsdelivr.net/gh/Molunerfinn/PicGo@dev/public/roundLogo.png"'
2019-12-19 06:17:21 -05:00
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()
}
2019-12-19 06:17:21 -05:00
}
@Watch('dialogVisible')
onDialogVisible (val: boolean) {
if (val) {
// @ts-ignore
document.querySelector('.main-content.el-row').style.zIndex = 101
} else {
// @ts-ignore
document.querySelector('.main-content.el-row').style.zIndex = 10
}
}
created () {
2018-12-28 07:30:47 -05:00
this.os = process.platform
ipcRenderer.on('hideLoading', () => {
this.loading = false
})
2019-12-19 06:17:21 -05:00
ipcRenderer.on('pluginList', (evt: IpcRendererEvent, list: IPicGoPlugin[]) => {
2018-12-18 05:58:31 -05:00
this.pluginList = list
this.pluginNameList = list.map(item => item.fullName)
2018-09-28 05:31:22 -04:00
this.loading = false
})
ipcRenderer.on('installPlugin', (evt: IpcRendererEvent, { success, body }: {
success: boolean,
body: string
}) => {
this.loading = false
this.pluginList.forEach(item => {
if (item.fullName === body) {
2018-12-18 05:58:31 -05:00
item.ing = false
item.hasInstall = success
}
})
})
2019-12-19 06:17:21 -05:00
ipcRenderer.on('updateSuccess', (evt: IpcRendererEvent, plugin: string) => {
2018-12-20 09:30:08 -05:00
this.loading = false
this.pluginList.forEach(item => {
if (item.fullName === plugin) {
2018-12-20 09:30:08 -05:00
item.ing = false
item.hasInstall = true
}
2018-12-24 03:05:30 -05:00
this.getPicBeds()
2018-12-20 09:30:08 -05:00
})
2018-12-24 03:05:30 -05:00
this.handleReload()
2018-12-20 09:30:08 -05:00
this.getPluginList()
})
2019-12-19 06:17:21 -05:00
ipcRenderer.on('uninstallSuccess', (evt: IpcRendererEvent, plugin: string) => {
this.loading = false
2018-12-18 05:58:31 -05:00
this.pluginList = this.pluginList.filter(item => {
if (item.fullName === plugin) { // restore Uploader & Transformer after uninstalling
2018-12-18 05:58:31 -05:00
if (item.config.transformer.name) {
2018-12-24 03:05:30 -05:00
this.handleRestoreState('transformer', item.config.transformer.name)
2018-12-18 05:58:31 -05:00
}
if (item.config.uploader.name) {
2018-12-24 03:05:30 -05:00
this.handleRestoreState('uploader', item.config.uploader.name)
2018-12-18 05:58:31 -05:00
}
2018-12-24 03:05:30 -05:00
this.getPicBeds()
}
return item.fullName !== plugin
})
2018-12-18 05:58:31 -05:00
this.pluginNameList = this.pluginNameList.filter(item => item !== plugin)
})
this.getPluginList()
2018-12-18 05:58:31 -05:00
this.getSearchResult = debounce(this.getSearchResult, 50)
2019-09-11 07:30:08 -04:00
this.needReload = this.$db.get('needReload')
2019-12-19 06:17:21 -05:00
}
buildContextMenu (plugin: IPicGoPlugin) {
const _this = this
let menu = [{
label: '启用插件',
enabled: !plugin.enabled,
click () {
_this.letPicGoSaveData({
[`picgoPlugins.${plugin.fullName}`]: true
})
2019-12-19 06:17:21 -05:00
plugin.enabled = true
_this.getPicBeds()
_this.needReload = true
2019-12-19 06:17:21 -05:00
}
}, {
label: '禁用插件',
enabled: plugin.enabled,
click () {
_this.letPicGoSaveData({
[`picgoPlugins.${plugin.fullName}`]: false
})
2019-12-19 06:17:21 -05:00
plugin.enabled = false
_this.getPicBeds()
if (plugin.config.transformer.name) {
_this.handleRestoreState('transformer', plugin.config.transformer.name)
2018-12-20 09:30:08 -05:00
}
2019-12-19 06:17:21 -05:00
if (plugin.config.uploader.name) {
_this.handleRestoreState('uploader', plugin.config.uploader.name)
2018-09-19 05:27:09 -04:00
}
_this.needReload = true
2018-09-19 05:27:09 -04:00
}
2019-12-19 06:17:21 -05:00
}, {
label: '卸载插件',
click () {
_this.uninstallPlugin(plugin.fullName)
2019-12-19 06:17:21 -05:00
}
}, {
label: '更新插件',
click () {
_this.updatePlugin(plugin.fullName)
2019-12-19 06:17:21 -05:00
}
}]
for (let i in plugin.config) {
if (plugin.config[i].config.length > 0) {
2019-01-01 10:44:00 -05:00
const obj = {
label: `配置${i} - ${plugin.config[i].fullName || plugin.config[i].name}`,
2019-01-01 10:44:00 -05:00
click () {
2019-12-19 06:17:21 -05:00
_this.currentType = i
_this.configName = plugin.config[i].fullName || plugin.config[i].name
2019-12-19 06:17:21 -05:00
_this.dialogVisible = true
_this.config = plugin.config[i].config
2019-01-01 10:44:00 -05:00
}
}
menu.push(obj)
}
2019-12-19 06:17:21 -05:00
}
2019-01-11 04:22:33 -05:00
2019-12-19 06:17:21 -05:00
// handle transformer
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)
2019-01-11 08:13:16 -05:00
}
2019-01-11 04:22:33 -05:00
}
2019-12-19 06:17:21 -05:00
menu.push(obj)
}
2019-01-11 04:22:33 -05:00
2019-12-19 06:17:21 -05:00
// plugin custom menus
if (plugin.guiMenu) {
menu.push({
// @ts-ignore
type: 'separator'
})
for (let i of plugin.guiMenu) {
menu.push({
label: i.label,
click () {
ipcRenderer.send('pluginActions', plugin.fullName, i.label)
2019-12-19 06:17:21 -05:00
}
2019-01-11 04:22:33 -05:00
})
}
2019-12-19 06:17:21 -05:00
}
this.menu = Menu.buildFromTemplate(menu)
this.menu.popup()
}
getPluginList () {
ipcRenderer.send('getPluginList')
}
getPicBeds () {
ipcRenderer.send('getPicBeds')
}
installPlugin (item: IPicGoPlugin) {
if (!item.gui) {
this.$confirm('该插件未对可视化界面进行优化, 是否继续安装?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
item.ing = true
ipcRenderer.send('installPlugin', item.fullName)
2019-12-19 06:17:21 -05:00
}).catch(() => {
console.log('Install canceled')
2018-12-24 03:05:30 -05:00
})
2019-12-19 06:17:21 -05:00
} else {
item.ing = true
ipcRenderer.send('installPlugin', item.fullName)
2019-12-19 06:17:21 -05:00
}
}
uninstallPlugin (val: string) {
this.pluginList.forEach(item => {
if (item.name === val) {
item.ing = true
2019-01-01 10:44:00 -05:00
}
2019-12-19 06:17:21 -05:00
})
this.loading = true
2019-12-19 06:17:21 -05:00
ipcRenderer.send('uninstallPlugin', val)
}
updatePlugin (val: string) {
this.pluginList.forEach(item => {
if (item.fullName === val) {
2019-12-19 06:17:21 -05:00
item.ing = true
2018-09-20 02:49:20 -04:00
}
2019-12-19 06:17:21 -05:00
})
this.loading = true
2019-12-19 06:17:21 -05:00
ipcRenderer.send('updatePlugin', val)
}
reloadApp () {
remote.app.relaunch()
remote.app.exit(0)
}
handleReload () {
this.$db.set('needReload', true)
this.needReload = true
const successNotification = new Notification('更新成功', {
body: '请点击此通知重启应用以生效'
})
successNotification.onclick = () => {
this.reloadApp()
}
}
cleanSearch () {
this.searchText = ''
}
toggleTransformer (transformer: string) {
let currentTransformer = this.$db.get('picBed.transformer') || 'path'
if (currentTransformer === transformer) {
this.letPicGoSaveData({
'picBed.transformer': 'path'
})
2019-12-19 06:17:21 -05:00
} else {
this.letPicGoSaveData({
'picBed.transformer': transformer
})
2019-12-19 06:17:21 -05:00
}
}
async handleConfirmConfig () {
// @ts-ignore
const result = await this.$refs.configForm.validate()
if (result !== false) {
switch (this.currentType) {
case 'plugin':
this.letPicGoSaveData({
[`${this.configName}`]: result
})
2019-12-19 06:17:21 -05:00
break
case 'uploader':
this.letPicGoSaveData({
[`picBed.${this.configName}`]: result
})
2019-12-19 06:17:21 -05:00
break
case 'transformer':
this.letPicGoSaveData({
[`transformer.${this.configName}`]: result
})
2019-12-19 06:17:21 -05:00
break
}
2019-12-19 06:17:21 -05:00
const successNotification = new Notification('设置结果', {
body: '设置成功'
})
successNotification.onclick = () => {
return true
2018-09-28 05:31:22 -04:00
}
2019-12-19 06:17:21 -05:00
this.dialogVisible = false
this.getPluginList()
}
}
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
.filter((item:INPMSearchResultObject) => {
return item.package.name.includes('picgo-plugin-')
})
.map((item: INPMSearchResultObject) => {
return this.handleSearchResult(item)
})
2019-12-19 06:17:21 -05:00
this.loading = false
})
.catch(err => {
console.log(err)
this.loading = false
})
}
handleSearchResult (item: INPMSearchResultObject) {
const name = handleStreamlinePluginName(item.package.name)
2019-12-19 06:17:21 -05:00
let gui = false
if (item.package.keywords && item.package.keywords.length > 0) {
if (item.package.keywords.includes('picgo-gui-plugin')) {
gui = true
2018-12-18 05:58:31 -05:00
}
2019-12-19 06:17:21 -05:00
}
return {
name: name,
fullName: item.package.name,
2019-12-19 06:17:21 -05:00
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),
2019-12-19 06:17:21 -05:00
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.letPicGoSaveData({
'picBed.current': 'smms',
'picBed.uploader': 'smms'
})
2018-12-18 05:58:31 -05:00
}
2019-12-19 06:17:21 -05:00
}
if (item === 'transformer') {
const current = this.$db.get('picBed.transformer')
if (current === name) {
this.letPicGoSaveData({
'picBed.transformer': 'path'
})
}
2018-09-17 04:52:01 -04:00
}
2019-12-19 06:17:21 -05:00
}
openHomepage (url: string) {
if (url) {
remote.shell.openExternal(url)
}
}
goAwesomeList () {
remote.shell.openExternal('https://github.com/PicGo/Awesome-PicGo')
}
letPicGoSaveData (data: IObj) {
ipcRenderer.send('picgoSaveData', data)
}
handleImportLocalPlugin () {
ipcRenderer.send('importLocalPlugin')
this.loading = true
}
2018-10-09 05:11:24 -04:00
beforeDestroy () {
2019-12-19 06:17:21 -05:00
ipcRenderer.removeAllListeners('pluginList')
ipcRenderer.removeAllListeners('installPlugin')
2019-12-19 06:17:21 -05:00
ipcRenderer.removeAllListeners('uninstallSuccess')
ipcRenderer.removeAllListeners('updateSuccess')
ipcRenderer.removeAllListeners('hideLoading')
2018-09-17 04:52:01 -04:00
}
}
</script>
<style lang='stylus'>
2018-12-28 07:30:47 -05:00
$darwinBg = #172426
2018-09-17 04:52:01 -04:00
#plugin-view
2018-09-28 05:31:22 -04:00
position relative
2018-09-17 04:52:01 -04:00
padding 0 20px 0
2018-12-18 05:58:31 -05:00
.el-loading-mask
background-color rgba(0, 0, 0, 0.8)
2018-09-28 05:31:22 -04:00
.plugin-list
height: 339px;
box-sizing: border-box;
padding: 8px 15px;
overflow-y: auto;
overflow-x: hidden;
position: absolute;
top: 70px;
left: 5px;
transition: all 0.2s ease-in-out 0.1s;
width: 100%
.el-loading-mask
left: 20px
width: calc(100% - 40px)
.view-title
color #eee
font-size 20px
text-align center
margin 10px auto
position relative
2019-01-31 03:40:08 -05:00
i.el-icon-goods
font-size 20px
vertical-align middle
cursor pointer
transition color .2s ease-in-out
&:hover
color #49B1F5
i.el-icon-download
position absolute
right 0
top 8px
font-size 20px
vertical-align middle
cursor pointer
transition color .2s ease-in-out
&:hover
color #49B1F5
2018-09-17 04:52:01 -04:00
.handle-bar
margin-bottom 20px
2018-09-28 05:31:22 -04:00
&.cut-width
padding-right: 8px
2018-09-17 04:52:01 -04:00
.el-input__inner
border-radius 0
.plugin-item
box-sizing border-box
height 80px
background #444
padding 8px
user-select text
transition all .2s ease-in-out
margin-bottom 10px
2019-01-11 04:22:33 -05:00
position relative
.cli-only-badge
position absolute
right 0px
top 0
font-size 12px
padding 3px 8px
background #49B1F5
color #eee
2018-12-28 07:30:47 -05:00
&.darwin
background transparentify($darwinBg, #000, 0.75)
&:hover
background transparentify($darwinBg, #000, 0.85)
2018-09-17 04:52:01 -04:00
&:hover
background #333
&__logo
width 64px
height 64px
float left
&__content
float left
width calc(100% - 72px)
2018-09-17 04:52:01 -04:00
height 64px
2018-12-18 05:58:31 -05:00
color #ddd
2018-09-17 04:52:01 -04:00
margin-left 8px
display flex
flex-direction column
justify-content space-between
2018-12-18 05:58:31 -05:00
&.disabled
color #aaa
2018-09-17 04:52:01 -04:00
&__name
font-size 16px
height 22px
line-height 22px
2018-09-19 05:27:09 -04:00
// font-weight 600
font-weight 600
cursor pointer
transition all .2s ease-in-out
&:hover
color: #1B9EF3
2018-09-17 04:52:01 -04:00
&__desc
font-size 14px
height 21px
line-height 21px
overflow hidden
text-overflow ellipsis
white-space nowrap
&__info-bar
font-size 14px
height 21px
line-height 28px
position relative
2018-09-17 04:52:01 -04:00
&__author
overflow hidden
text-overflow ellipsis
white-space nowrap
&__config
float right
font-size 16px
cursor pointer
transition all .2s ease-in-out
&:hover
color: #1B9EF3
2018-09-28 05:31:22 -04:00
.config-button
font-size 12px
color #ddd
background #222
padding 1px 8px
height 18px
line-height 18px
text-align center
position absolute
top 4px
right 20px
2018-09-28 05:31:22 -04:00
transition all .2s ease-in-out
&.reload
right 0px
2018-12-18 05:58:31 -05:00
&.ing
right 0px
2018-09-28 05:31:22 -04:00
&.install
right 0px
&:hover
background: #1B9EF3
color #fff
2018-12-24 03:05:30 -05:00
.reload-mask
position absolute
width calc(100% - 40px)
bottom -320px
text-align center
background rgba(0,0,0,0.4)
padding 10px 0
&.cut-width
width calc(100% - 48px)
2019-12-19 06:17:21 -05:00
</style>