🚧 WIP(custom): v3.0.0 migrate to vite and esm

This commit is contained in:
Kuingsmile
2025-07-31 17:37:30 +08:00
parent cd76bc7c10
commit 054f4b4cff
597 changed files with 197292 additions and 13329 deletions

View File

@@ -2,32 +2,72 @@
<div id="plugin-view">
<div class="view-title">
{{ $T('PLUGIN_SETTINGS') }} -
<el-tooltip :content="pluginListToolTip" placement="right" :persistent="false" teleported>
<el-icon class="el-icon-goods" @click="goAwesomeList">
<el-tooltip
:content="pluginListToolTip"
placement="right"
:persistent="false"
teleported
>
<el-icon
class="el-icon-goods"
@click="goAwesomeList"
>
<Goods />
</el-icon>
</el-tooltip>
<el-tooltip :content="updateAllToolTip" placement="left" :persistent="false" teleported>
<el-icon class="el-icon-update" @click="handleUpdateAllPlugin">
<el-tooltip
:content="updateAllToolTip"
placement="left"
:persistent="false"
teleported
>
<el-icon
class="el-icon-update"
@click="handleUpdateAllPlugin"
>
<Refresh />
</el-icon>
</el-tooltip>
<el-tooltip :content="importLocalPluginToolTip" placement="left">
<el-icon class="el-icon-download" :persistent="false" teleported @click="handleImportLocalPlugin">
<el-tooltip
:content="importLocalPluginToolTip"
placement="left"
>
<el-icon
class="el-icon-download"
:persistent="false"
teleported
@click="handleImportLocalPlugin"
>
<Download />
</el-icon>
</el-tooltip>
</div>
<el-row class="handle-bar" :class="{ 'cut-width': pluginList.length > 6 }">
<el-input v-model="searchText" :placeholder="$T('PLUGIN_SEARCH_PLACEHOLDER')" size="small">
<el-row
class="handle-bar"
:class="{ 'cut-width': pluginList.length > 6 }"
>
<el-input
v-model="searchText"
:placeholder="$T('PLUGIN_SEARCH_PLACEHOLDER')"
size="small"
>
<template #suffix>
<el-icon class="el-input__icon" style="cursor: pointer" @click="cleanSearch">
<el-icon
class="el-input__icon"
style="cursor: pointer"
@click="cleanSearch"
>
<close />
</el-icon>
</template>
</el-input>
</el-row>
<el-row id="pluginList" v-loading="loading" :gutter="10" class="plugin-list">
<el-row
id="pluginList"
v-loading="loading"
:gutter="10"
class="plugin-list"
>
<el-col
v-for="item in pluginList"
:key="item.fullName"
@@ -38,11 +78,30 @@
:lg="pluginList.length === 1 ? 24 : 12"
:xl="pluginList.length === 1 ? 24 : 12"
>
<div class="plugin-item" :class="{ darwin: osGlobal === 'darwin' }">
<div v-if="!item.gui" class="cli-only-badge" title="CLI only">CLI</div>
<img class="plugin-item__logo" :src="item.logo" :onerror="defaultLogo" />
<div class="plugin-item__content" :class="{ disabled: !item.enabled }">
<div class="plugin-item__name" @click="openHomepage(item.homepage)">
<div
class="plugin-item"
:class="{ darwin: osGlobal === 'darwin' }"
>
<div
v-if="!item.gui"
class="cli-only-badge"
title="CLI only"
>
CLI
</div>
<img
class="plugin-item__logo"
:src="item.logo"
:onerror="defaultLogo"
>
<div
class="plugin-item__content"
:class="{ disabled: !item.enabled }"
>
<div
class="plugin-item__name"
@click="openHomepage(item.homepage)"
>
{{ item.name }} <small>{{ ' ' + item.version }}</small> &nbsp;
<!-- 升级提示 -->
<el-tag
@@ -55,7 +114,10 @@
new
</el-tag>
</div>
<div class="plugin-item__desc" :title="item.description">
<div
class="plugin-item__desc"
:title="item.description"
>
{{ item.description }}
</div>
<div class="plugin-item__info-bar">
@@ -65,26 +127,47 @@
<span class="plugin-item__config">
<template v-if="searchText">
<template v-if="!item.hasInstall">
<span v-if="!item.ing" class="config-button install" @click="installPlugin(item)">
<span
v-if="!item.ing"
class="config-button install"
@click="installPlugin(item)"
>
{{ $T('PLUGIN_INSTALL') }}
</span>
<span v-else-if="item.ing" class="config-button ing">
<span
v-else-if="item.ing"
class="config-button ing"
>
{{ $T('PLUGIN_INSTALLING') }}
</span>
</template>
<span v-else class="config-button ing">
<span
v-else
class="config-button ing"
>
{{ $T('PLUGIN_INSTALLED') }}
</span>
</template>
<template v-else>
<span v-if="item.ing" class="config-button ing">
<span
v-if="item.ing"
class="config-button ing"
>
{{ $T('PLUGIN_DOING_SOMETHING') }}
</span>
<template v-else>
<el-icon v-if="item.enabled" class="el-icon-setting" @click="buildContextMenu(item)">
<el-icon
v-if="item.enabled"
class="el-icon-setting"
@click="buildContextMenu(item)"
>
<Tools />
</el-icon>
<el-icon v-else class="el-icon-remove-outline" @click="buildContextMenu(item)">
<el-icon
v-else
class="el-icon-remove-outline"
@click="buildContextMenu(item)"
>
<Remove />
</el-icon>
</template>
@@ -95,8 +178,18 @@
</div>
</el-col>
</el-row>
<el-row v-show="needReload" class="reload-mask" :class="{ 'cut-width': pluginList.length > 6 }" justify="center">
<el-button type="primary" size="small" round @click="reloadApp">
<el-row
v-show="needReload"
class="reload-mask"
:class="{ 'cut-width': pluginList.length > 6 }"
justify="center"
>
<el-button
type="primary"
size="small"
round
@click="reloadApp"
>
{{ $T('TIPS_NEED_RELOAD') }}
</el-button>
</el-row>
@@ -111,12 +204,25 @@
width="70%"
append-to-body
>
<config-form :id="configName" ref="$configForm" :config="config" :type="currentType" color-mode="white" />
<config-form
:id="configName"
ref="$configForm"
:config="config"
:type="currentType"
color-mode="white"
/>
<template #footer>
<el-button round @click="dialogVisible = false">
<el-button
round
@click="dialogVisible = false"
>
{{ $T('CANCEL') }}
</el-button>
<el-button type="primary" round @click="handleConfirmConfig">
<el-button
type="primary"
round
@click="handleConfirmConfig"
>
{{ $T('CONFIRM') }}
</el-button>
</template>
@@ -125,26 +231,24 @@
</template>
<script lang="ts" setup>
import axios from 'axios'
import { ipcRenderer, IpcRendererEvent } from 'electron'
import { Close, Download, Goods, Refresh, Remove, Tools } from '@element-plus/icons-vue'
import type { IpcRendererEvent } from 'electron'
import { ElMessageBox } from 'element-plus'
import { debounce, DebouncedFunc } from 'lodash'
import { Close, Download, Refresh, Goods, Remove, Tools } from '@element-plus/icons-vue'
import { computed, ref, onBeforeMount, onBeforeUnmount, watch, onMounted, reactive, toRaw } from 'vue'
import { debounce, DebouncedFunc } from 'lodash-es'
import { computed, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref, toRaw, watch } from 'vue'
import ConfigForm from '@/components/ConfigFormForPlugin.vue'
import { T as $T } from '@/i18n/index'
import { sendRPC } from '@/utils/common'
import { getConfig, saveConfig } from '@/utils/dataSender'
import { osGlobal, updatePicBedGlobal } from '@/utils/global'
import {
PICGO_CONFIG_PLUGIN,
PICGO_HANDLE_PLUGIN_DONE,
PICGO_HANDLE_PLUGIN_ING,
PICGO_TOGGLE_PLUGIN,
PICGO_HANDLE_PLUGIN_DONE
PICGO_TOGGLE_PLUGIN
} from '#/events/constants'
import { IRPCActionType } from '#/types/enum'
import { INPMSearchResult, INPMSearchResultObject, IPicGoPlugin } from '#/types/types'
import { handleStreamlinePluginName } from '#/utils/common'
import { configPaths } from '#/utils/configPaths'
@@ -162,7 +266,7 @@ const latestVersionMap = reactive<{ [key: string]: string }>({})
const pluginListToolTip = $T('PLUGIN_LIST')
const importLocalPluginToolTip = $T('PLUGIN_IMPORT_LOCAL')
const updateAllToolTip = $T('PLUGIN_UPDATE_ALL')
const defaultLogo = ref(`this.src="file://${__static.replace(/\\/g, '/')}/roundLogo.png"`)
const defaultLogo = ref('this.src=\'/roundLogo.png\'')
const $configForm = ref<InstanceType<typeof ConfigForm> | null>(null)
const npmSearchText = computed(() => {
return searchText.value.match('picgo-plugin-')
@@ -193,120 +297,132 @@ watch(dialogVisible, (val: boolean) => {
}
})
async function getLatestVersionOfPlugIn(pluginName: string) {
async function getLatestVersionOfPlugIn (pluginName: string) {
try {
const res = await axios.get(`https://registry.npmjs.com/${pluginName}`)
const res = await window.node.axios.get(`https://registry.npmjs.com/${pluginName}`)
latestVersionMap[pluginName] = res.data['dist-tags'].latest
} catch (err) {
console.error(err)
}
}
const hideLoadingHandler = () => {
loading.value = false
}
const picgoHandlePluginDoneHandler = (_: IpcRendererEvent, fullName: string) => {
pluginList.value.forEach(item => {
if (item.fullName === fullName || item.name === fullName) {
item.ing = false
}
})
loading.value = false
}
const pluginListHandler = (_: IpcRendererEvent, list: IPicGoPlugin[]) => {
pluginList.value = list
pluginNameList.value = list.map(item => item.fullName)
for (const item of pluginList.value) {
getLatestVersionOfPlugIn(item.fullName)
}
loading.value = false
}
const installPluginHandler = (
_: IpcRendererEvent,
{
success,
body
}: {
success: boolean
body: string
}
) => {
loading.value = false
pluginList.value.forEach(item => {
if (item.fullName === body) {
item.ing = false
item.hasInstall = success
}
})
}
const updateSuccessHandler = (_: IpcRendererEvent, plugin: string) => {
loading.value = false
pluginList.value.forEach(item => {
if (item.fullName === plugin) {
item.ing = false
item.hasInstall = true
}
updatePicBedGlobal()
})
handleReload()
getPluginList()
}
const uninstallSuccessHandler = (_: IpcRendererEvent, plugin: string) => {
loading.value = false
pluginList.value = pluginList.value.filter(item => {
if (item.fullName === plugin) {
// restore Uploader & Transformer after uninstalling
if (item.config.transformer.name) {
handleRestoreState('transformer', item.config.transformer.name)
}
if (item.config.uploader.name) {
handleRestoreState('uploader', item.config.uploader.name)
}
updatePicBedGlobal()
}
return item.fullName !== plugin
})
pluginNameList.value = pluginNameList.value.filter(item => item !== plugin)
}
const picgoConfigPluginHandler = (_: IpcRendererEvent, _currentType: 'plugin' | 'transformer' | 'uploader', _configName: string, _config: any) => {
currentType.value = _currentType
configName.value = _configName
config.value = _config
dialogVisible.value = true
}
const picgoHandlePluginIngHandler = (_: IpcRendererEvent, fullName: string) => {
pluginList.value.forEach(item => {
if (item.fullName === fullName || item.name === fullName) {
item.ing = true
}
})
loading.value = true
}
const picgoTogglePluginHandler = (_: IpcRendererEvent, fullName: string, enabled: boolean) => {
const plugin = pluginList.value.find(item => item.fullName === fullName)
if (plugin) {
plugin.enabled = enabled
updatePicBedGlobal()
needReload.value = true
}
}
onBeforeMount(async () => {
ipcRenderer.on('hideLoading', () => {
loading.value = false
})
ipcRenderer.on(PICGO_HANDLE_PLUGIN_DONE, (_: IpcRendererEvent, fullName: string) => {
pluginList.value.forEach(item => {
if (item.fullName === fullName || item.name === fullName) {
item.ing = false
}
})
loading.value = false
})
ipcRenderer.on('pluginList', (_: IpcRendererEvent, list: IPicGoPlugin[]) => {
pluginList.value = list
pluginNameList.value = list.map(item => item.fullName)
for (const item of pluginList.value) {
getLatestVersionOfPlugIn(item.fullName)
}
loading.value = false
})
ipcRenderer.on(
'installPlugin',
(
_: IpcRendererEvent,
{
success,
body
}: {
success: boolean
body: string
}
) => {
loading.value = false
pluginList.value.forEach(item => {
if (item.fullName === body) {
item.ing = false
item.hasInstall = success
}
})
}
)
ipcRenderer.on('updateSuccess', (_: IpcRendererEvent, plugin: string) => {
loading.value = false
pluginList.value.forEach(item => {
if (item.fullName === plugin) {
item.ing = false
item.hasInstall = true
}
updatePicBedGlobal()
})
handleReload()
getPluginList()
})
ipcRenderer.on('uninstallSuccess', (_: IpcRendererEvent, plugin: string) => {
loading.value = false
pluginList.value = pluginList.value.filter(item => {
if (item.fullName === plugin) {
// restore Uploader & Transformer after uninstalling
if (item.config.transformer.name) {
handleRestoreState('transformer', item.config.transformer.name)
}
if (item.config.uploader.name) {
handleRestoreState('uploader', item.config.uploader.name)
}
updatePicBedGlobal()
}
return item.fullName !== plugin
})
pluginNameList.value = pluginNameList.value.filter(item => item !== plugin)
})
ipcRenderer.on(
PICGO_CONFIG_PLUGIN,
(_: IpcRendererEvent, _currentType: 'plugin' | 'transformer' | 'uploader', _configName: string, _config: any) => {
currentType.value = _currentType
configName.value = _configName
config.value = _config
dialogVisible.value = true
}
)
ipcRenderer.on(PICGO_HANDLE_PLUGIN_ING, (_: IpcRendererEvent, fullName: string) => {
pluginList.value.forEach(item => {
if (item.fullName === fullName || item.name === fullName) {
item.ing = true
}
})
loading.value = true
})
ipcRenderer.on(PICGO_TOGGLE_PLUGIN, (_: IpcRendererEvent, fullName: string, enabled: boolean) => {
const plugin = pluginList.value.find(item => item.fullName === fullName)
if (plugin) {
plugin.enabled = enabled
updatePicBedGlobal()
needReload.value = true
}
})
window.electron.ipcRendererOn('hideLoading', hideLoadingHandler)
window.electron.ipcRendererOn(PICGO_HANDLE_PLUGIN_DONE, picgoHandlePluginDoneHandler)
window.electron.ipcRendererOn('pluginList', pluginListHandler)
window.electron.ipcRendererOn('installPlugin', installPluginHandler)
window.electron.ipcRendererOn('updateSuccess', updateSuccessHandler)
window.electron.ipcRendererOn('uninstallSuccess', uninstallSuccessHandler)
window.electron.ipcRendererOn(PICGO_CONFIG_PLUGIN, picgoConfigPluginHandler)
window.electron.ipcRendererOn(PICGO_HANDLE_PLUGIN_ING, picgoHandlePluginIngHandler)
window.electron.ipcRendererOn(PICGO_TOGGLE_PLUGIN, picgoTogglePluginHandler)
getPluginList()
getSearchResult = debounce(_getSearchResult, 50)
needReload.value = (await getConfig<boolean>(configPaths.needReload)) || false
})
async function buildContextMenu(plugin: IPicGoPlugin) {
sendRPC(IRPCActionType.SHOW_PLUGIN_PAGE_MENU, plugin)
async function buildContextMenu (plugin: IPicGoPlugin) {
window.electron.sendRPC(IRPCActionType.SHOW_PLUGIN_PAGE_MENU, plugin)
}
function handleResize() {
function handleResize () {
const myDiv = document.getElementById('pluginList') as HTMLElement
const windowHeight = window.innerHeight
const newHeight = windowHeight * 0.75
@@ -317,11 +433,11 @@ onMounted(() => {
window.addEventListener('resize', handleResize)
})
function getPluginList() {
sendRPC(IRPCActionType.PLUGIN_GET_LIST)
function getPluginList () {
window.electron.sendRPC(IRPCActionType.PLUGIN_GET_LIST)
}
function installPlugin(item: IPicGoPlugin) {
function installPlugin (item: IPicGoPlugin) {
if (!item.gui) {
$confirm($T('TIPS_PLUGIN_NOT_GUI_IMPLEMENT'), $T('TIPS_NOTICE'), {
confirmButtonText: $T('CONFIRM'),
@@ -330,22 +446,22 @@ function installPlugin(item: IPicGoPlugin) {
})
.then(() => {
item.ing = true
sendRPC(IRPCActionType.PLUGIN_INSTALL, item.fullName)
window.electron.sendRPC(IRPCActionType.PLUGIN_INSTALL, item.fullName)
})
.catch(() => {
console.log('Install canceled')
})
} else {
item.ing = true
sendRPC(IRPCActionType.PLUGIN_INSTALL, item.fullName)
window.electron.sendRPC(IRPCActionType.PLUGIN_INSTALL, item.fullName)
}
}
function reloadApp() {
sendRPC(IRPCActionType.RELOAD_APP)
function reloadApp () {
window.electron.sendRPC(IRPCActionType.RELOAD_APP)
}
async function handleReload() {
async function handleReload () {
saveConfig({
needReload: true
})
@@ -358,11 +474,11 @@ async function handleReload() {
}
}
function cleanSearch() {
function cleanSearch () {
searchText.value = ''
}
async function handleConfirmConfig() {
async function handleConfirmConfig () {
const result = (await $configForm.value?.validate()) || false
if (result !== false) {
switch (currentType.value) {
@@ -393,8 +509,8 @@ async function handleConfirmConfig() {
}
}
function _getSearchResult(val: string) {
axios
function _getSearchResult (val: string) {
window.node.axios
.get(`https://registry.npmjs.com/-/v1/search?text=${val}`)
.then((res: INPMSearchResult) => {
pluginList.value = res.data.objects
@@ -412,7 +528,7 @@ function _getSearchResult(val: string) {
})
}
function handleSearchResult(item: INPMSearchResultObject) {
function handleSearchResult (item: INPMSearchResultObject) {
const pkg = item.package
const name = handleStreamlinePluginName(pkg.name)
let gui = false
@@ -437,7 +553,7 @@ function handleSearchResult(item: INPMSearchResultObject) {
}
// restore Uploader & Transformer
async function handleRestoreState(item: string, name: string) {
async function handleRestoreState (item: string, name: string) {
if (item === 'uploader') {
const current = await getConfig(configPaths.picBed.current)
if (current === name) {
@@ -457,33 +573,36 @@ async function handleRestoreState(item: string, name: string) {
}
}
function openHomepage(url: string) {
function openHomepage (url: string) {
if (url) {
sendRPC(IRPCActionType.OPEN_URL, url)
window.electron.sendRPC(IRPCActionType.OPEN_URL, url)
}
}
function goAwesomeList() {
sendRPC(IRPCActionType.OPEN_URL, 'https://github.com/PicGo/Awesome-PicGo')
function goAwesomeList () {
window.electron.sendRPC(IRPCActionType.OPEN_URL, 'https://github.com/PicGo/Awesome-PicGo')
}
function handleImportLocalPlugin() {
sendRPC(IRPCActionType.PLUGIN_IMPORT_LOCAL)
function handleImportLocalPlugin () {
window.electron.sendRPC(IRPCActionType.PLUGIN_IMPORT_LOCAL)
loading.value = true
}
function handleUpdateAllPlugin() {
sendRPC(IRPCActionType.PLUGIN_UPDATE_ALL, toRaw(pluginNameList.value))
function handleUpdateAllPlugin () {
window.electron.sendRPC(IRPCActionType.PLUGIN_UPDATE_ALL, toRaw(pluginNameList.value))
}
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize)
ipcRenderer.removeAllListeners('pluginList')
ipcRenderer.removeAllListeners('installPlugin')
ipcRenderer.removeAllListeners('uninstallSuccess')
ipcRenderer.removeAllListeners('updateSuccess')
ipcRenderer.removeAllListeners('hideLoading')
ipcRenderer.removeAllListeners(PICGO_HANDLE_PLUGIN_DONE)
window.electron.ipcRendererRemoveListener('pluginList', pluginListHandler)
window.electron.ipcRendererRemoveListener('installPlugin', installPluginHandler)
window.electron.ipcRendererRemoveListener('uninstallSuccess', uninstallSuccessHandler)
window.electron.ipcRendererRemoveListener('updateSuccess', updateSuccessHandler)
window.electron.ipcRendererRemoveListener('hideLoading', hideLoadingHandler)
window.electron.ipcRendererRemoveListener(PICGO_HANDLE_PLUGIN_DONE, picgoHandlePluginDoneHandler)
window.electron.ipcRendererRemoveListener(PICGO_CONFIG_PLUGIN, picgoConfigPluginHandler)
window.electron.ipcRendererRemoveListener(PICGO_HANDLE_PLUGIN_ING, picgoHandlePluginIngHandler)
window.electron.ipcRendererRemoveListener(PICGO_TOGGLE_PLUGIN, picgoTogglePluginHandler)
})
</script>
<script lang="ts">