mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-07 06:22:46 +08:00
🐛 Fix(custom): fix drag upload of upload page
This commit is contained in:
@@ -1,277 +1,277 @@
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
import bus from '@core/bus'
|
import bus from '@core/bus'
|
||||||
import { CREATE_APP_MENU } from '@core/bus/constants'
|
import { CREATE_APP_MENU } from '@core/bus/constants'
|
||||||
import db from '@core/datastore'
|
import db from '@core/datastore'
|
||||||
import { app, BrowserWindow, Rectangle } from 'electron'
|
import { app, BrowserWindow, Rectangle } from 'electron'
|
||||||
|
|
||||||
import type { IWindowListItem } from '#/types/electron'
|
import type { IWindowListItem } from '#/types/electron'
|
||||||
import type { IBrowserWindowOptions } from '#/types/types'
|
import type { IBrowserWindowOptions } from '#/types/types'
|
||||||
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '~/events/constant'
|
import { TOGGLE_SHORTKEY_MODIFIED_MODE } from '~/events/constant'
|
||||||
import { T as $t } from '~/i18n'
|
import { T as $t } from '~/i18n'
|
||||||
import { configPaths } from '~/utils/configPaths'
|
import { configPaths } from '~/utils/configPaths'
|
||||||
import { IWindowList } from '~/utils/enum'
|
import { IWindowList } from '~/utils/enum'
|
||||||
|
|
||||||
import logo from '../../../../../resources/logo.png?asset'
|
import logo from '../../../../../resources/logo.png?asset'
|
||||||
|
|
||||||
const windowList = new Map<string, IWindowListItem>()
|
const windowList = new Map<string, IWindowListItem>()
|
||||||
|
|
||||||
const getDefaultWindowSizes = (): { width: number; height: number } => {
|
const getDefaultWindowSizes = (): { width: number; height: number } => {
|
||||||
const [mainWindowWidth, mainWindowHeight] = db.get([
|
const [mainWindowWidth, mainWindowHeight] = db.get([
|
||||||
configPaths.settings.mainWindowWidth,
|
configPaths.settings.mainWindowWidth,
|
||||||
configPaths.settings.mainWindowHeight
|
configPaths.settings.mainWindowHeight
|
||||||
])
|
])
|
||||||
return {
|
return {
|
||||||
width: mainWindowWidth || 1200,
|
width: mainWindowWidth || 1200,
|
||||||
height: mainWindowHeight || 800
|
height: mainWindowHeight || 800
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMiniWindowShape (win: BrowserWindow) {
|
function setMiniWindowShape (win: BrowserWindow) {
|
||||||
const radius = 32
|
const radius = 32
|
||||||
const shape: Rectangle[] = []
|
const shape: Rectangle[] = []
|
||||||
|
|
||||||
for (let y = -radius; y <= radius; y++) {
|
for (let y = -radius; y <= radius; y++) {
|
||||||
for (let x = -radius; x <= radius; x++) {
|
for (let x = -radius; x <= radius; x++) {
|
||||||
if (x * x + y * y <= radius * radius) {
|
if (x * x + y * y <= radius * radius) {
|
||||||
shape.push({ x: radius + x, y: radius + y, width: 1, height: 1 })
|
shape.push({ x: radius + x, y: radius + y, width: 1, height: 1 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
win.setShape(shape)
|
win.setShape(shape)
|
||||||
}
|
}
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||||
const preloadPath = fileURLToPath(new URL('../preload/index.mjs', import.meta.url))
|
const preloadPath = fileURLToPath(new URL('../preload/index.mjs', import.meta.url))
|
||||||
|
|
||||||
const { width: defaultWindowWidth, height: defaultWindowHeight } = getDefaultWindowSizes()
|
const { width: defaultWindowWidth, height: defaultWindowHeight } = getDefaultWindowSizes()
|
||||||
|
|
||||||
const trayWindowOptions = {
|
const trayWindowOptions = {
|
||||||
height: 350,
|
height: 350,
|
||||||
width: 196,
|
width: 196,
|
||||||
show: false,
|
show: false,
|
||||||
frame: false,
|
frame: false,
|
||||||
fullscreenable: false,
|
fullscreenable: false,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
vibrancy: 'ultra-dark',
|
vibrancy: 'ultra-dark',
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
preload: preloadPath,
|
preload: preloadPath,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
nodeIntegrationInWorker: true,
|
nodeIntegrationInWorker: true,
|
||||||
backgroundThrottling: false,
|
backgroundThrottling: false,
|
||||||
webSecurity: false
|
webSecurity: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingWindowOptions = {
|
const settingWindowOptions = {
|
||||||
height: defaultWindowHeight,
|
height: defaultWindowHeight,
|
||||||
width: defaultWindowWidth,
|
width: defaultWindowWidth,
|
||||||
show: false,
|
show: false,
|
||||||
frame: true,
|
frame: true,
|
||||||
center: true,
|
center: true,
|
||||||
fullscreenable: true,
|
fullscreenable: true,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
title: 'PicList',
|
title: 'PicList',
|
||||||
transparent: false,
|
transparent: false,
|
||||||
backgroundColor: '#ebeef5',
|
backgroundColor: '#ebeef5',
|
||||||
titleBarStyle: 'hidden',
|
titleBarStyle: 'hidden',
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
webviewTag: true,
|
webviewTag: true,
|
||||||
backgroundThrottling: false,
|
backgroundThrottling: false,
|
||||||
preload: preloadPath,
|
preload: preloadPath,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
nodeIntegrationInWorker: true,
|
nodeIntegrationInWorker: true,
|
||||||
webSecurity: false
|
webSecurity: false
|
||||||
}
|
}
|
||||||
} as IBrowserWindowOptions
|
} as IBrowserWindowOptions
|
||||||
|
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
settingWindowOptions.frame = false
|
settingWindowOptions.frame = false
|
||||||
settingWindowOptions.icon = '../../../../../resources/logo.png'
|
settingWindowOptions.icon = '../../../../../resources/logo.png'
|
||||||
}
|
}
|
||||||
|
|
||||||
const miniWindowOptions = {
|
const miniWindowOptions = {
|
||||||
height: 64,
|
height: 64,
|
||||||
width: 64,
|
width: 64,
|
||||||
show: process.platform === 'linux',
|
show: process.platform === 'linux',
|
||||||
frame: false,
|
frame: false,
|
||||||
fullscreenable: false,
|
fullscreenable: false,
|
||||||
skipTaskbar: true,
|
skipTaskbar: true,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
transparent: process.platform !== 'linux',
|
transparent: process.platform !== 'linux',
|
||||||
icon: logo,
|
icon: logo,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
preload: preloadPath,
|
preload: preloadPath,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
backgroundThrottling: false,
|
backgroundThrottling: false,
|
||||||
nodeIntegrationInWorker: true
|
nodeIntegrationInWorker: true
|
||||||
}
|
}
|
||||||
} as IBrowserWindowOptions
|
} as IBrowserWindowOptions
|
||||||
|
|
||||||
if (db.get(configPaths.settings.miniWindowOntop)) {
|
if (db.get(configPaths.settings.miniWindowOntop)) {
|
||||||
miniWindowOptions.alwaysOnTop = true
|
miniWindowOptions.alwaysOnTop = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const renameWindowOptions = {
|
const renameWindowOptions = {
|
||||||
height: 250,
|
height: 270,
|
||||||
width: 350,
|
width: 350,
|
||||||
show: true,
|
show: true,
|
||||||
fullscreenable: false,
|
fullscreenable: false,
|
||||||
icon: logo,
|
icon: logo,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
preload: preloadPath,
|
preload: preloadPath,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
nodeIntegrationInWorker: true,
|
nodeIntegrationInWorker: true,
|
||||||
backgroundThrottling: false
|
backgroundThrottling: false
|
||||||
}
|
}
|
||||||
} as IBrowserWindowOptions
|
} as IBrowserWindowOptions
|
||||||
|
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
renameWindowOptions.show = true
|
renameWindowOptions.show = true
|
||||||
renameWindowOptions.backgroundColor = '#3f3c37'
|
renameWindowOptions.backgroundColor = '#3f3c37'
|
||||||
renameWindowOptions.autoHideMenuBar = true
|
renameWindowOptions.autoHideMenuBar = true
|
||||||
renameWindowOptions.transparent = false
|
renameWindowOptions.transparent = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolboxWindowOptions = {
|
const toolboxWindowOptions = {
|
||||||
height: 450,
|
height: 450,
|
||||||
width: 800,
|
width: 800,
|
||||||
show: false,
|
show: false,
|
||||||
frame: true,
|
frame: true,
|
||||||
center: true,
|
center: true,
|
||||||
fullscreenable: false,
|
fullscreenable: false,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
title: `PicList ${$t('TOOLBOX')}`,
|
title: `PicList ${$t('TOOLBOX')}`,
|
||||||
backgroundColor: '#ebeef5',
|
backgroundColor: '#ebeef5',
|
||||||
icon: logo,
|
icon: logo,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
backgroundThrottling: false,
|
backgroundThrottling: false,
|
||||||
preload: preloadPath,
|
preload: preloadPath,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
nodeIntegrationInWorker: true,
|
nodeIntegrationInWorker: true,
|
||||||
webSecurity: false
|
webSecurity: false
|
||||||
}
|
}
|
||||||
} as IBrowserWindowOptions
|
} as IBrowserWindowOptions
|
||||||
|
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
toolboxWindowOptions.backgroundColor = '#3f3c37'
|
toolboxWindowOptions.backgroundColor = '#3f3c37'
|
||||||
toolboxWindowOptions.autoHideMenuBar = true
|
toolboxWindowOptions.autoHideMenuBar = true
|
||||||
toolboxWindowOptions.transparent = false
|
toolboxWindowOptions.transparent = false
|
||||||
}
|
}
|
||||||
|
|
||||||
windowList.set(IWindowList.TRAY_WINDOW, {
|
windowList.set(IWindowList.TRAY_WINDOW, {
|
||||||
isValid: process.platform !== 'linux',
|
isValid: process.platform !== 'linux',
|
||||||
multiple: false,
|
multiple: false,
|
||||||
options: () => trayWindowOptions,
|
options: () => trayWindowOptions,
|
||||||
callback (window) {
|
callback (window) {
|
||||||
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
|
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
|
||||||
window.loadURL(process.env.ELECTRON_RENDERER_URL)
|
window.loadURL(process.env.ELECTRON_RENDERER_URL)
|
||||||
} else {
|
} else {
|
||||||
window.loadFile(path.join(__dirname, '../render/index.html'))
|
window.loadFile(path.join(__dirname, '../render/index.html'))
|
||||||
}
|
}
|
||||||
window.on('blur', () => {
|
window.on('blur', () => {
|
||||||
window.hide()
|
window.hide()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
windowList.set(IWindowList.SETTING_WINDOW, {
|
windowList.set(IWindowList.SETTING_WINDOW, {
|
||||||
isValid: true,
|
isValid: true,
|
||||||
multiple: false,
|
multiple: false,
|
||||||
options: () => settingWindowOptions,
|
options: () => settingWindowOptions,
|
||||||
callback (window, windowManager) {
|
callback (window, windowManager) {
|
||||||
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
|
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
|
||||||
window.loadURL(`${process.env.ELECTRON_RENDERER_URL}#main-page/upload`)
|
window.loadURL(`${process.env.ELECTRON_RENDERER_URL}#main-page/upload`)
|
||||||
} else {
|
} else {
|
||||||
window.loadFile(path.join(__dirname, '../render/index.html'), {
|
window.loadFile(path.join(__dirname, '../render/index.html'), {
|
||||||
hash: 'main-page/upload'
|
hash: 'main-page/upload'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
window.on('closed', () => {
|
window.on('closed', () => {
|
||||||
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, false)
|
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, false)
|
||||||
if (process.platform === 'linux') {
|
if (process.platform === 'linux') {
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
app.quit()
|
app.quit()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
bus.emit(CREATE_APP_MENU)
|
bus.emit(CREATE_APP_MENU)
|
||||||
windowManager.create(IWindowList.MINI_WINDOW)
|
windowManager.create(IWindowList.MINI_WINDOW)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
windowList.set(IWindowList.MINI_WINDOW, {
|
windowList.set(IWindowList.MINI_WINDOW, {
|
||||||
isValid: process.platform !== 'darwin',
|
isValid: process.platform !== 'darwin',
|
||||||
multiple: false,
|
multiple: false,
|
||||||
options: () => miniWindowOptions,
|
options: () => miniWindowOptions,
|
||||||
callback (window) {
|
callback (window) {
|
||||||
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
|
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
|
||||||
window.loadURL(`${process.env.ELECTRON_RENDERER_URL}#mini-page`)
|
window.loadURL(`${process.env.ELECTRON_RENDERER_URL}#mini-page`)
|
||||||
} else {
|
} else {
|
||||||
window.loadFile(path.join(__dirname, '../render/index.html'), {
|
window.loadFile(path.join(__dirname, '../render/index.html'), {
|
||||||
hash: 'mini-page'
|
hash: 'mini-page'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
setMiniWindowShape(window)
|
setMiniWindowShape(window)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
windowList.set(IWindowList.RENAME_WINDOW, {
|
windowList.set(IWindowList.RENAME_WINDOW, {
|
||||||
isValid: true,
|
isValid: true,
|
||||||
multiple: true,
|
multiple: true,
|
||||||
options: () => renameWindowOptions,
|
options: () => renameWindowOptions,
|
||||||
async callback (window, windowManager) {
|
async callback (window, windowManager) {
|
||||||
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
|
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
|
||||||
window.loadURL(`${process.env.ELECTRON_RENDERER_URL}#rename-page`)
|
window.loadURL(`${process.env.ELECTRON_RENDERER_URL}#rename-page`)
|
||||||
} else {
|
} else {
|
||||||
window.loadFile(path.join(__dirname, '../render/index.html'), {
|
window.loadFile(path.join(__dirname, '../render/index.html'), {
|
||||||
hash: 'rename-page'
|
hash: 'rename-page'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const currentWindow = windowManager.getAvailableWindow(true)
|
const currentWindow = windowManager.getAvailableWindow(true)
|
||||||
if (currentWindow && currentWindow.isVisible()) {
|
if (currentWindow && currentWindow.isVisible()) {
|
||||||
const { x, y, width, height } = currentWindow.getBounds()
|
const { x, y, width, height } = currentWindow.getBounds()
|
||||||
const positionX = Math.floor(x + width / 2 - 150)
|
const positionX = Math.floor(x + width / 2 - 150)
|
||||||
const positionY = Math.floor(y + height / 2 - (height > 400 ? 88 : 0))
|
const positionY = Math.floor(y + height / 2 - (height > 400 ? 88 : 0))
|
||||||
window.setPosition(positionX, positionY, false)
|
window.setPosition(positionX, positionY, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
windowList.set(IWindowList.TOOLBOX_WINDOW, {
|
windowList.set(IWindowList.TOOLBOX_WINDOW, {
|
||||||
isValid: true,
|
isValid: true,
|
||||||
multiple: false,
|
multiple: false,
|
||||||
options: () => toolboxWindowOptions,
|
options: () => toolboxWindowOptions,
|
||||||
async callback (window, windowManager) {
|
async callback (window, windowManager) {
|
||||||
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
|
if (!app.isPackaged && process.env.ELECTRON_RENDERER_URL) {
|
||||||
window.loadURL(`${process.env.ELECTRON_RENDERER_URL}#toolbox-page`)
|
window.loadURL(`${process.env.ELECTRON_RENDERER_URL}#toolbox-page`)
|
||||||
} else {
|
} else {
|
||||||
window.loadFile(path.join(__dirname, '../render/index.html'), {
|
window.loadFile(path.join(__dirname, '../render/index.html'), {
|
||||||
hash: 'toolbox-page'
|
hash: 'toolbox-page'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const currentWindow = windowManager.getAvailableWindow(true)
|
const currentWindow = windowManager.getAvailableWindow(true)
|
||||||
if (currentWindow && currentWindow.isVisible()) {
|
if (currentWindow && currentWindow.isVisible()) {
|
||||||
const { x, y, width, height } = currentWindow.getBounds()
|
const { x, y, width, height } = currentWindow.getBounds()
|
||||||
const positionX = Math.floor(x + width / 2 - 400)
|
const positionX = Math.floor(x + width / 2 - 400)
|
||||||
const positionY = Math.floor(y + height / 2 - (height > 400 ? 225 : 0))
|
const positionY = Math.floor(y + height / 2 - (height > 400 ? 225 : 0))
|
||||||
window.setPosition(positionX, positionY, false)
|
window.setPosition(positionX, positionY, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default windowList
|
export default windowList
|
||||||
|
|||||||
@@ -1,335 +1,324 @@
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
|
|
||||||
import db from '@core/datastore'
|
import db from '@core/datastore'
|
||||||
import logger from '@core/picgo/logger'
|
import logger from '@core/picgo/logger'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { clipboard, dialog, Notification, Tray } from 'electron'
|
import { clipboard, Notification, Tray } from 'electron'
|
||||||
import FormData from 'form-data'
|
import FormData from 'form-data'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import { isReactive, isRef, toRaw, unref } from 'vue'
|
import { isReactive, isRef, toRaw, unref } from 'vue'
|
||||||
|
|
||||||
import type { IHTTPProxy, IPrivateShowNotificationOption, IShowMessageBoxResult, IStringKeyMap } from '#/types/types'
|
import type { IHTTPProxy, IPrivateShowNotificationOption, IStringKeyMap } from '#/types/types'
|
||||||
import { configPaths } from '~/utils/configPaths'
|
import { configPaths } from '~/utils/configPaths'
|
||||||
import { IShortUrlServer } from '~/utils/enum'
|
import { IShortUrlServer } from '~/utils/enum'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get raw data from reactive or ref
|
* get raw data from reactive or ref
|
||||||
*/
|
*/
|
||||||
export const getRawData = (args: any): any => {
|
export const getRawData = (args: any): any => {
|
||||||
if (isRef(args)) return unref(args)
|
if (isRef(args)) return unref(args)
|
||||||
if (isReactive(args)) return toRaw(args)
|
if (isReactive(args)) return toRaw(args)
|
||||||
if (Array.isArray(args)) return args.map(getRawData)
|
if (Array.isArray(args)) return args.map(getRawData)
|
||||||
if (typeof args === 'object' && args !== null) {
|
if (typeof args === 'object' && args !== null) {
|
||||||
const data = {} as Record<string, any>
|
const data = {} as Record<string, any>
|
||||||
for (const key in args) {
|
for (const key in args) {
|
||||||
data[key] = getRawData(args[key])
|
data[key] = getRawData(args[key])
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
const getExtension = (fileName: string) => path.extname(fileName).slice(1)
|
const getExtension = (fileName: string) => path.extname(fileName).slice(1)
|
||||||
|
|
||||||
export const isImage = (fileName: string) =>
|
export const isImage = (fileName: string) =>
|
||||||
['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'ico', 'svg', 'avif'].includes(getExtension(fileName))
|
['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'ico', 'svg', 'avif'].includes(getExtension(fileName))
|
||||||
|
|
||||||
export let tray: Tray
|
export let tray: Tray
|
||||||
|
|
||||||
export const setTray = (t: Tray) => {
|
export const setTray = (t: Tray) => {
|
||||||
tray = t
|
tray = t
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTray = () => tray
|
export const getTray = () => tray
|
||||||
|
|
||||||
export function setTrayToolTip (title: string): void {
|
export function setTrayToolTip (title: string): void {
|
||||||
if (tray) {
|
if (tray) {
|
||||||
tray.setToolTip(title)
|
tray.setToolTip(title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const handleCopyUrl = (str: string): void => {
|
export const handleCopyUrl = (str: string): void => {
|
||||||
if (db.get(configPaths.settings.autoCopy) !== false) {
|
if (db.get(configPaths.settings.autoCopy) !== false) {
|
||||||
clipboard.writeText(str)
|
clipboard.writeText(str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* show notification
|
* show notification
|
||||||
* @param options
|
* @param options
|
||||||
*/
|
*/
|
||||||
export const showNotification = (
|
export const showNotification = (
|
||||||
options: IPrivateShowNotificationOption = {
|
options: IPrivateShowNotificationOption = {
|
||||||
title: '',
|
title: '',
|
||||||
body: '',
|
body: '',
|
||||||
clickToCopy: false,
|
clickToCopy: false,
|
||||||
copyContent: '',
|
copyContent: '',
|
||||||
clickFn: () => {}
|
clickFn: () => {}
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title: options.title,
|
title: options.title,
|
||||||
body: options.body
|
body: options.body
|
||||||
// icon: options.icon || undefined
|
// icon: options.icon || undefined
|
||||||
})
|
})
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
if (options.clickToCopy) {
|
if (options.clickToCopy) {
|
||||||
clipboard.writeText(options.copyContent || options.body)
|
clipboard.writeText(options.copyContent || options.body)
|
||||||
}
|
}
|
||||||
if (options.clickFn) {
|
if (options.clickFn) {
|
||||||
options.clickFn()
|
options.clickFn()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
notification.once('click', handleClick)
|
notification.once('click', handleClick)
|
||||||
notification.once('close', () => {
|
notification.once('close', () => {
|
||||||
notification.removeListener('click', handleClick)
|
notification.removeListener('click', handleClick)
|
||||||
})
|
})
|
||||||
notification.show()
|
notification.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const showMessageBox = (options: any) => {
|
/**
|
||||||
return new Promise<IShowMessageBoxResult>(resolve => {
|
* macOS public.file-url will get encoded file path,
|
||||||
dialog.showMessageBox(options).then(res => {
|
* so we need to decode it
|
||||||
resolve({
|
*/
|
||||||
result: res.response,
|
export const ensureFilePath = (filePath: string, prefix = 'file://'): string => {
|
||||||
checkboxChecked: res.checkboxChecked
|
filePath = filePath.replace(prefix, '')
|
||||||
})
|
if (fs.existsSync(filePath)) {
|
||||||
})
|
return `${prefix}${filePath}`
|
||||||
})
|
}
|
||||||
}
|
filePath = decodeURIComponent(filePath)
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
/**
|
return `${prefix}${filePath}`
|
||||||
* macOS public.file-url will get encoded file path,
|
}
|
||||||
* so we need to decode it
|
return ''
|
||||||
*/
|
}
|
||||||
export const ensureFilePath = (filePath: string, prefix = 'file://'): string => {
|
|
||||||
filePath = filePath.replace(prefix, '')
|
/**
|
||||||
if (fs.existsSync(filePath)) {
|
* for builtin clipboard to get image path from clipboard
|
||||||
return `${prefix}${filePath}`
|
* @returns
|
||||||
}
|
*/
|
||||||
filePath = decodeURIComponent(filePath)
|
export const getClipboardFilePath = (): string => {
|
||||||
if (fs.existsSync(filePath)) {
|
// TODO: linux support
|
||||||
return `${prefix}${filePath}`
|
const img = clipboard.readImage()
|
||||||
}
|
const platform = process.platform
|
||||||
return ''
|
|
||||||
}
|
if (!img.isEmpty() && platform === 'darwin') {
|
||||||
|
let imgPath = clipboard.read('public.file-url') // will get file://xxx/xxx
|
||||||
/**
|
imgPath = ensureFilePath(imgPath)
|
||||||
* for builtin clipboard to get image path from clipboard
|
return imgPath ? imgPath.replace('file://', '') : ''
|
||||||
* @returns
|
}
|
||||||
*/
|
|
||||||
export const getClipboardFilePath = (): string => {
|
if (img.isEmpty() && platform === 'win32') {
|
||||||
// TODO: linux support
|
const imgPath = clipboard
|
||||||
const img = clipboard.readImage()
|
.readBuffer('FileNameW')
|
||||||
const platform = process.platform
|
?.toString('ucs2')
|
||||||
|
?.replace(RegExp(String.fromCharCode(0), 'g'), '')
|
||||||
if (!img.isEmpty() && platform === 'darwin') {
|
return imgPath || ''
|
||||||
let imgPath = clipboard.read('public.file-url') // will get file://xxx/xxx
|
}
|
||||||
imgPath = ensureFilePath(imgPath)
|
|
||||||
return imgPath ? imgPath.replace('file://', '') : ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
if (img.isEmpty() && platform === 'win32') {
|
const c1nApi = 'https://c1n.cn/link/short'
|
||||||
const imgPath = clipboard
|
|
||||||
.readBuffer('FileNameW')
|
const createC1NShortUrl = async (url: string) => {
|
||||||
?.toString('ucs2')
|
const c1nToken = db.get(configPaths.settings.c1nToken) || ''
|
||||||
?.replace(RegExp(String.fromCharCode(0), 'g'), '')
|
if (!c1nToken) {
|
||||||
return imgPath || ''
|
logger.warn('c1n token is not set')
|
||||||
}
|
return url
|
||||||
|
}
|
||||||
return ''
|
try {
|
||||||
}
|
const form = new FormData()
|
||||||
|
form.append('url', url)
|
||||||
const c1nApi = 'https://c1n.cn/link/short'
|
const res = await axios.post(c1nApi, form, {
|
||||||
|
headers: {
|
||||||
const generateC1NShortUrl = async (url: string) => {
|
token: c1nToken
|
||||||
const c1nToken = db.get(configPaths.settings.c1nToken) || ''
|
}
|
||||||
if (!c1nToken) {
|
})
|
||||||
logger.warn('c1n token is not set')
|
if (res.status >= 200 && res.status < 300 && res.data?.code === 0) {
|
||||||
return url
|
return res.data.data
|
||||||
}
|
}
|
||||||
try {
|
} catch (e: any) {
|
||||||
const form = new FormData()
|
logger.error(e)
|
||||||
form.append('url', url)
|
}
|
||||||
const res = await axios.post(c1nApi, form, {
|
return url
|
||||||
headers: {
|
}
|
||||||
token: c1nToken
|
|
||||||
}
|
const createYOURLSShortLink = async (url: string) => {
|
||||||
})
|
let domain = db.get(configPaths.settings.yourlsDomain) || ''
|
||||||
if (res.status >= 200 && res.status < 300 && res.data?.code === 0) {
|
const signature = db.get(configPaths.settings.yourlsSignature) || ''
|
||||||
return res.data.data
|
|
||||||
}
|
if (!domain || !signature) {
|
||||||
} catch (e: any) {
|
logger.warn('Yourls server or signature is not set')
|
||||||
logger.error(e)
|
return url
|
||||||
}
|
}
|
||||||
return url
|
if (!/^https?:\/\//.test(domain)) {
|
||||||
}
|
domain = `http://${domain}`
|
||||||
|
}
|
||||||
const generateYOURLSShortUrl = async (url: string) => {
|
const params = new URLSearchParams({
|
||||||
let domain = db.get(configPaths.settings.yourlsDomain) || ''
|
signature,
|
||||||
const signature = db.get(configPaths.settings.yourlsSignature) || ''
|
action: 'shorturl',
|
||||||
|
format: 'json',
|
||||||
if (!domain || !signature) {
|
url
|
||||||
logger.warn('Yourls server or signature is not set')
|
})
|
||||||
return url
|
try {
|
||||||
}
|
const res = await axios.get(`${domain}/yourls-api.php?${params.toString()}`)
|
||||||
if (!/^https?:\/\//.test(domain)) {
|
if (res.data?.shorturl) {
|
||||||
domain = `http://${domain}`
|
return res.data.shorturl
|
||||||
}
|
}
|
||||||
const params = new URLSearchParams({
|
} catch (e: any) {
|
||||||
signature,
|
if (e.response?.data?.message?.includes('already exists in database')) {
|
||||||
action: 'shorturl',
|
return e.response.data.shorturl
|
||||||
format: 'json',
|
}
|
||||||
url
|
logger.error(e)
|
||||||
})
|
}
|
||||||
try {
|
|
||||||
const res = await axios.get(`${domain}/yourls-api.php?${params.toString()}`)
|
return url
|
||||||
if (res.data?.shorturl) {
|
}
|
||||||
return res.data.shorturl
|
|
||||||
}
|
const createShortUrlForCFWorker = async (url: string) => {
|
||||||
} catch (e: any) {
|
let cfWorkerHost = db.get(configPaths.settings.cfWorkerHost) || ''
|
||||||
if (e.response?.data?.message?.includes('already exists in database')) {
|
cfWorkerHost = cfWorkerHost.replace(/\/$/, '')
|
||||||
return e.response.data.shorturl
|
if (!cfWorkerHost) {
|
||||||
}
|
logger.warn('CF Worker host is not set')
|
||||||
logger.error(e)
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
return url
|
try {
|
||||||
}
|
const res = await axios.post(cfWorkerHost, { url })
|
||||||
|
if (res.data?.status === 200 && res.data?.key?.startsWith('/')) {
|
||||||
const generateCFWORKERShortUrl = async (url: string) => {
|
return `${cfWorkerHost}${res.data.key}`
|
||||||
let cfWorkerHost = db.get(configPaths.settings.cfWorkerHost) || ''
|
}
|
||||||
cfWorkerHost = cfWorkerHost.replace(/\/$/, '')
|
} catch (e: any) {
|
||||||
if (!cfWorkerHost) {
|
logger.error(e)
|
||||||
logger.warn('CF Worker host is not set')
|
}
|
||||||
return url
|
|
||||||
}
|
return url
|
||||||
|
}
|
||||||
try {
|
|
||||||
const res = await axios.post(cfWorkerHost, { url })
|
const createShortUrlFromSink = async (url: string) => {
|
||||||
if (res.data?.status === 200 && res.data?.key?.startsWith('/')) {
|
let sinkDomain = db.get(configPaths.settings.sinkDomain) || ''
|
||||||
return `${cfWorkerHost}${res.data.key}`
|
const sinkToken = db.get(configPaths.settings.sinkToken) || ''
|
||||||
}
|
if (!sinkDomain || !sinkToken) {
|
||||||
} catch (e: any) {
|
logger.warn('Sink domain or token is not set')
|
||||||
logger.error(e)
|
return url
|
||||||
}
|
}
|
||||||
|
if (!/^https?:\/\//.test(sinkDomain)) {
|
||||||
return url
|
sinkDomain = `http://${sinkDomain}`
|
||||||
}
|
}
|
||||||
|
if (sinkDomain.endsWith('/')) {
|
||||||
const generateSinkShortUrl = async (url: string) => {
|
sinkDomain = sinkDomain.slice(0, -1)
|
||||||
let sinkDomain = db.get(configPaths.settings.sinkDomain) || ''
|
}
|
||||||
const sinkToken = db.get(configPaths.settings.sinkToken) || ''
|
try {
|
||||||
if (!sinkDomain || !sinkToken) {
|
const res = await axios.post(
|
||||||
logger.warn('Sink domain or token is not set')
|
`${sinkDomain}/api/link/create`,
|
||||||
return url
|
{ url },
|
||||||
}
|
{ headers: { Authorization: `Bearer ${sinkToken}` } }
|
||||||
if (!/^https?:\/\//.test(sinkDomain)) {
|
)
|
||||||
sinkDomain = `http://${sinkDomain}`
|
if (res.data?.link?.slug) {
|
||||||
}
|
return `${sinkDomain}/${res.data.link.slug}`
|
||||||
if (sinkDomain.endsWith('/')) {
|
}
|
||||||
sinkDomain = sinkDomain.slice(0, -1)
|
} catch (e: any) {
|
||||||
}
|
logger.error(e)
|
||||||
try {
|
}
|
||||||
const res = await axios.post(
|
return url
|
||||||
`${sinkDomain}/api/link/create`,
|
}
|
||||||
{ url },
|
|
||||||
{ headers: { Authorization: `Bearer ${sinkToken}` } }
|
export const generateShortUrl = async (url: string) => {
|
||||||
)
|
const server = db.get(configPaths.settings.shortUrlServer) || IShortUrlServer.C1N
|
||||||
if (res.data?.link?.slug) {
|
switch (server) {
|
||||||
return `${sinkDomain}/${res.data.link.slug}`
|
case IShortUrlServer.C1N:
|
||||||
}
|
return createC1NShortUrl(url)
|
||||||
} catch (e: any) {
|
case IShortUrlServer.YOURLS:
|
||||||
logger.error(e)
|
return createYOURLSShortLink(url)
|
||||||
}
|
case IShortUrlServer.CFWORKER:
|
||||||
return url
|
return createShortUrlForCFWorker(url)
|
||||||
}
|
case IShortUrlServer.SINK:
|
||||||
|
return createShortUrlFromSink(url)
|
||||||
export const generateShortUrl = async (url: string) => {
|
default:
|
||||||
const server = db.get(configPaths.settings.shortUrlServer) || IShortUrlServer.C1N
|
return url
|
||||||
switch (server) {
|
}
|
||||||
case IShortUrlServer.C1N:
|
}
|
||||||
return generateC1NShortUrl(url)
|
|
||||||
case IShortUrlServer.YOURLS:
|
export const isUrl = (url: string): boolean => {
|
||||||
return generateYOURLSShortUrl(url)
|
try {
|
||||||
case IShortUrlServer.CFWORKER:
|
return Boolean(new URL(url))
|
||||||
return generateCFWORKERShortUrl(url)
|
} catch {
|
||||||
case IShortUrlServer.SINK:
|
return false
|
||||||
return generateSinkShortUrl(url)
|
}
|
||||||
default:
|
}
|
||||||
return url
|
|
||||||
}
|
export const isUrlEncode = (url: string): boolean => {
|
||||||
}
|
url = url || ''
|
||||||
|
try {
|
||||||
export const isUrl = (url: string): boolean => {
|
return url !== decodeURI(url)
|
||||||
try {
|
} catch {
|
||||||
return Boolean(new URL(url))
|
return false
|
||||||
} catch {
|
}
|
||||||
return false
|
}
|
||||||
}
|
|
||||||
}
|
export const handleUrlEncode = (url: string): string => (isUrlEncode(url) ? url : encodeURI(url))
|
||||||
|
|
||||||
export const isUrlEncode = (url: string): boolean => {
|
export const handleUrlEncodeWithSetting = (url: string) =>
|
||||||
url = url || ''
|
db.get(configPaths.settings.encodeOutputURL) ? handleUrlEncode(url) : url
|
||||||
try {
|
|
||||||
return url !== decodeURI(url)
|
export const handleStreamlinePluginName = (name: string) => name.replace(/(@[^/]+\/)?picgo-plugin-/, '')
|
||||||
} catch {
|
export const simpleClone = (obj: any) => JSON.parse(JSON.stringify(obj))
|
||||||
return false
|
export const enforceNumber = (num: number | string) => (isNaN(+num) ? 0 : +num)
|
||||||
}
|
|
||||||
}
|
export const trimValues = <T extends IStringKeyMap>(
|
||||||
|
obj: T
|
||||||
export const handleUrlEncode = (url: string): string => (isUrlEncode(url) ? url : encodeURI(url))
|
): { [K in keyof T]: T[K] extends string ? string : T[K] } => {
|
||||||
|
return Object.fromEntries(
|
||||||
export const handleUrlEncodeWithSetting = (url: string) =>
|
Object.entries(obj).map(([key, value]) => [key, typeof value === 'string' ? value.trim() : value])
|
||||||
db.get(configPaths.settings.encodeOutputURL) ? handleUrlEncode(url) : url
|
) as { [K in keyof T]: T[K] extends string ? string : T[K] }
|
||||||
|
}
|
||||||
export const handleStreamlinePluginName = (name: string) => name.replace(/(@[^/]+\/)?picgo-plugin-/, '')
|
|
||||||
export const simpleClone = (obj: any) => JSON.parse(JSON.stringify(obj))
|
export const formatEndpoint = (endpoint: string, sslEnabled: boolean): string => {
|
||||||
export const enforceNumber = (num: number | string) => (isNaN(+num) ? 0 : +num)
|
const hasProtocol = /^https?:\/\//.test(endpoint)
|
||||||
|
if (!hasProtocol) {
|
||||||
export const trimValues = <T extends IStringKeyMap>(
|
return `${sslEnabled ? 'https' : 'http'}://${endpoint}`
|
||||||
obj: T
|
}
|
||||||
): { [K in keyof T]: T[K] extends string ? string : T[K] } => {
|
return sslEnabled ? endpoint.replace(/^http:\/\//, 'https://') : endpoint.replace(/^https:\/\//, 'http://')
|
||||||
return Object.fromEntries(
|
}
|
||||||
Object.entries(obj).map(([key, value]) => [key, typeof value === 'string' ? value.trim() : value])
|
|
||||||
) as { [K in keyof T]: T[K] extends string ? string : T[K] }
|
export const formatHttpProxy = (
|
||||||
}
|
proxy: string | undefined,
|
||||||
|
type: 'object' | 'string'
|
||||||
export const formatEndpoint = (endpoint: string, sslEnabled: boolean): string => {
|
): IHTTPProxy | undefined | string => {
|
||||||
const hasProtocol = /^https?:\/\//.test(endpoint)
|
if (!proxy) return undefined
|
||||||
if (!hasProtocol) {
|
if (/^https?:\/\//.test(proxy)) {
|
||||||
return `${sslEnabled ? 'https' : 'http'}://${endpoint}`
|
const { protocol, hostname, port } = new URL(proxy)
|
||||||
}
|
return type === 'string'
|
||||||
return sslEnabled ? endpoint.replace(/^http:\/\//, 'https://') : endpoint.replace(/^https:\/\//, 'http://')
|
? `${protocol}//${hostname}:${port}`
|
||||||
}
|
: {
|
||||||
|
host: hostname,
|
||||||
export const formatHttpProxy = (
|
port: Number(port),
|
||||||
proxy: string | undefined,
|
protocol: protocol.slice(0, -1)
|
||||||
type: 'object' | 'string'
|
}
|
||||||
): IHTTPProxy | undefined | string => {
|
}
|
||||||
if (!proxy) return undefined
|
const [host, port] = proxy.split(':')
|
||||||
if (/^https?:\/\//.test(proxy)) {
|
return type === 'string'
|
||||||
const { protocol, hostname, port } = new URL(proxy)
|
? `http://${host}:${port}`
|
||||||
return type === 'string'
|
: {
|
||||||
? `${protocol}//${hostname}:${port}`
|
host,
|
||||||
: {
|
port: port ? Number(port) : 80,
|
||||||
host: hostname,
|
protocol: 'http'
|
||||||
port: Number(port),
|
}
|
||||||
protocol: protocol.slice(0, -1)
|
}
|
||||||
}
|
|
||||||
}
|
export function encodeFilePath (filePath: string) {
|
||||||
const [host, port] = proxy.split(':')
|
return filePath.replace(/\\/g, '/').split('/').map(encodeURIComponent).join('/')
|
||||||
return type === 'string'
|
}
|
||||||
? `http://${host}:${port}`
|
|
||||||
: {
|
export const trimPath = (path: string) => path.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/')
|
||||||
host,
|
|
||||||
port: port ? Number(port) : 80,
|
|
||||||
protocol: 'http'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function encodeFilePath (filePath: string) {
|
|
||||||
return filePath.replace(/\\/g, '/').split('/').map(encodeURIComponent).join('/')
|
|
||||||
}
|
|
||||||
|
|
||||||
export const trimPath = (path: string) => path.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/')
|
|
||||||
|
|||||||
@@ -1,262 +1,263 @@
|
|||||||
import crypto from 'node:crypto'
|
import crypto from 'node:crypto'
|
||||||
import http, { AgentOptions } from 'node:http'
|
import http, { AgentOptions } from 'node:http'
|
||||||
import https from 'node:https'
|
import https from 'node:https'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import querystring from 'node:querystring'
|
import querystring from 'node:querystring'
|
||||||
|
|
||||||
import { DeleteObjectCommand, S3Client, S3ClientConfig } from '@aws-sdk/client-s3'
|
import type { S3ClientConfig } from '@aws-sdk/client-s3'
|
||||||
import { NodeHttpHandler } from '@smithy/node-http-handler'
|
import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3'
|
||||||
import axios from 'axios'
|
import { NodeHttpHandler } from '@smithy/node-http-handler'
|
||||||
import { ISftpPlistConfig } from 'piclist'
|
import axios from 'axios'
|
||||||
|
import type { ISftpPlistConfig } from 'piclist'
|
||||||
import type { IObj, IStringKeyMap } from '#/types/types'
|
|
||||||
import { getAgent } from '~/manage/utils/common'
|
import type { IObj, IStringKeyMap } from '#/types/types'
|
||||||
import SSHClient from '~/utils/sshClient'
|
import { getAgent } from '~/manage/utils/common'
|
||||||
|
import SSHClient from '~/utils/sshClient'
|
||||||
interface DogecloudTokenFull {
|
|
||||||
Credentials: {
|
interface DogecloudTokenFull {
|
||||||
accessKeyId: string
|
Credentials: {
|
||||||
secretAccessKey: string
|
accessKeyId: string
|
||||||
sessionToken: string
|
secretAccessKey: string
|
||||||
}
|
sessionToken: string
|
||||||
ExpiredAt: number
|
}
|
||||||
Buckets: {
|
ExpiredAt: number
|
||||||
name: string
|
Buckets: {
|
||||||
s3Bucket: string
|
name: string
|
||||||
s3Endpoint: string
|
s3Bucket: string
|
||||||
}[]
|
s3Endpoint: string
|
||||||
}
|
}[]
|
||||||
|
}
|
||||||
const dogeRegionMap: IStringKeyMap = {
|
|
||||||
'ap-shanghai': '0',
|
const dogeRegionMap: IStringKeyMap = {
|
||||||
'ap-beijing': '1',
|
'ap-shanghai': '0',
|
||||||
'ap-guangzhou': '2',
|
'ap-beijing': '1',
|
||||||
'ap-chengdu': '3'
|
'ap-guangzhou': '2',
|
||||||
}
|
'ap-chengdu': '3'
|
||||||
|
}
|
||||||
async function dogecloudApi (
|
|
||||||
apiPath: string,
|
async function dogecloudApi (
|
||||||
data = {},
|
apiPath: string,
|
||||||
jsonMode: boolean = false,
|
data = {},
|
||||||
accessKey: string,
|
jsonMode: boolean = false,
|
||||||
secretKey: string
|
accessKey: string,
|
||||||
) {
|
secretKey: string
|
||||||
const body = jsonMode ? JSON.stringify(data) : querystring.encode(data)
|
) {
|
||||||
const sign = crypto
|
const body = jsonMode ? JSON.stringify(data) : querystring.encode(data)
|
||||||
.createHmac('sha1', secretKey)
|
const sign = crypto
|
||||||
.update(Buffer.from(apiPath + '\n' + body, 'utf8'))
|
.createHmac('sha1', secretKey)
|
||||||
.digest('hex')
|
.update(Buffer.from(apiPath + '\n' + body, 'utf8'))
|
||||||
const authorization = `TOKEN ${accessKey}:${sign}`
|
.digest('hex')
|
||||||
try {
|
const authorization = `TOKEN ${accessKey}:${sign}`
|
||||||
const res = await axios.request({
|
try {
|
||||||
url: `https://api.dogecloud.com${apiPath}`,
|
const res = await axios.request({
|
||||||
method: 'POST',
|
url: `https://api.dogecloud.com${apiPath}`,
|
||||||
data: body,
|
method: 'POST',
|
||||||
responseType: 'json',
|
data: body,
|
||||||
headers: {
|
responseType: 'json',
|
||||||
'Content-Type': jsonMode ? 'application/json' : 'application/x-www-form-urlencoded',
|
headers: {
|
||||||
Authorization: authorization
|
'Content-Type': jsonMode ? 'application/json' : 'application/x-www-form-urlencoded',
|
||||||
}
|
Authorization: authorization
|
||||||
})
|
}
|
||||||
if (res.data.code !== 200) {
|
})
|
||||||
throw new Error('API Error')
|
if (res.data.code !== 200) {
|
||||||
}
|
throw new Error('API Error')
|
||||||
return res.data.data
|
}
|
||||||
} catch (err: any) {
|
return res.data.data
|
||||||
throw new Error('API Error')
|
} catch (err: any) {
|
||||||
}
|
throw new Error('API Error')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
async function getDogeToken (accessKey: string, secretKey: string): Promise<IObj | DogecloudTokenFull> {
|
|
||||||
try {
|
async function getDogeToken (accessKey: string, secretKey: string): Promise<IObj | DogecloudTokenFull> {
|
||||||
const data = await dogecloudApi(
|
try {
|
||||||
'/auth/tmp_token.json',
|
const data = await dogecloudApi(
|
||||||
{
|
'/auth/tmp_token.json',
|
||||||
channel: 'OSS_FULL',
|
{
|
||||||
scopes: ['*']
|
channel: 'OSS_FULL',
|
||||||
},
|
scopes: ['*']
|
||||||
true,
|
},
|
||||||
accessKey,
|
true,
|
||||||
secretKey
|
accessKey,
|
||||||
)
|
secretKey
|
||||||
return data
|
)
|
||||||
} catch (err: any) {
|
return data
|
||||||
console.log(err)
|
} catch (err: any) {
|
||||||
return {}
|
console.log(err)
|
||||||
}
|
return {}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
export async function removeFileFromS3InMain (configMap: IStringKeyMap, dogeMode: boolean = false) {
|
|
||||||
try {
|
export async function removeFileFromS3InMain (configMap: IStringKeyMap, dogeMode: boolean = false) {
|
||||||
const {
|
try {
|
||||||
url: rawUrl,
|
const {
|
||||||
type,
|
url: rawUrl,
|
||||||
config: { accessKeyID, secretAccessKey, bucketName, endpoint, pathStyleAccess, rejectUnauthorized, proxy }
|
type,
|
||||||
} = configMap
|
config: { accessKeyID, secretAccessKey, bucketName, endpoint, pathStyleAccess, rejectUnauthorized, proxy }
|
||||||
let {
|
} = configMap
|
||||||
imgUrl,
|
let {
|
||||||
config: { region }
|
imgUrl,
|
||||||
} = configMap
|
config: { region }
|
||||||
if (type === 'aws-s3' || type === 'aws-s3-plist') {
|
} = configMap
|
||||||
imgUrl = rawUrl || imgUrl || ''
|
if (type === 'aws-s3' || type === 'aws-s3-plist') {
|
||||||
}
|
imgUrl = rawUrl || imgUrl || ''
|
||||||
const url = new URL(!/^https?:\/\//.test(imgUrl) ? `http://${imgUrl}` : imgUrl)
|
}
|
||||||
let fileKey = url.pathname.replace(/^\/+/, '')
|
const url = new URL(!/^https?:\/\//.test(imgUrl) ? `http://${imgUrl}` : imgUrl)
|
||||||
if (pathStyleAccess) {
|
let fileKey = url.pathname.replace(/^\/+/, '')
|
||||||
fileKey = fileKey.replace(/^[^/]+\//, '')
|
if (pathStyleAccess) {
|
||||||
}
|
fileKey = fileKey.replace(/^[^/]+\//, '')
|
||||||
const endpointUrl: string | undefined = endpoint
|
}
|
||||||
? /^https?:\/\//.test(endpoint)
|
const endpointUrl: string | undefined = endpoint
|
||||||
? endpoint
|
? /^https?:\/\//.test(endpoint)
|
||||||
: `http://${endpoint}`
|
? endpoint
|
||||||
: undefined
|
: `http://${endpoint}`
|
||||||
if (endpointUrl && endpointUrl.includes('cloudflarestorage')) {
|
: undefined
|
||||||
region = region || 'auto'
|
if (endpointUrl && endpointUrl.includes('cloudflarestorage')) {
|
||||||
}
|
region = region || 'auto'
|
||||||
const sslEnabled = endpointUrl ? endpointUrl.startsWith('https') : true
|
}
|
||||||
const agent = getAgent(proxy, sslEnabled)
|
const sslEnabled = endpointUrl ? endpointUrl.startsWith('https') : true
|
||||||
const commonOptions: AgentOptions = {
|
const agent = getAgent(proxy, sslEnabled)
|
||||||
keepAlive: true,
|
const commonOptions: AgentOptions = {
|
||||||
keepAliveMsecs: 1000,
|
keepAlive: true,
|
||||||
scheduling: 'lifo' as 'lifo' | 'fifo' | undefined
|
keepAliveMsecs: 1000,
|
||||||
}
|
scheduling: 'lifo' as 'lifo' | 'fifo' | undefined
|
||||||
const extraOptions = sslEnabled ? { rejectUnauthorized: !!rejectUnauthorized } : {}
|
}
|
||||||
const handler = sslEnabled
|
const extraOptions = sslEnabled ? { rejectUnauthorized: !!rejectUnauthorized } : {}
|
||||||
? new NodeHttpHandler({
|
const handler = sslEnabled
|
||||||
httpsAgent: agent.https
|
? new NodeHttpHandler({
|
||||||
? agent.https
|
httpsAgent: agent.https
|
||||||
: new https.Agent({
|
? agent.https
|
||||||
...commonOptions,
|
: new https.Agent({
|
||||||
...extraOptions
|
...commonOptions,
|
||||||
})
|
...extraOptions
|
||||||
})
|
})
|
||||||
: new NodeHttpHandler({
|
})
|
||||||
httpAgent: agent.http
|
: new NodeHttpHandler({
|
||||||
? agent.http
|
httpAgent: agent.http
|
||||||
: new http.Agent({
|
? agent.http
|
||||||
...commonOptions,
|
: new http.Agent({
|
||||||
...extraOptions
|
...commonOptions,
|
||||||
})
|
...extraOptions
|
||||||
})
|
})
|
||||||
const s3Options: S3ClientConfig = {
|
})
|
||||||
credentials: {
|
const s3Options: S3ClientConfig = {
|
||||||
accessKeyId: accessKeyID,
|
credentials: {
|
||||||
secretAccessKey
|
accessKeyId: accessKeyID,
|
||||||
},
|
secretAccessKey
|
||||||
endpoint: endpointUrl,
|
},
|
||||||
tls: sslEnabled,
|
endpoint: endpointUrl,
|
||||||
forcePathStyle: pathStyleAccess,
|
tls: sslEnabled,
|
||||||
region,
|
forcePathStyle: pathStyleAccess,
|
||||||
requestHandler: handler
|
region,
|
||||||
}
|
requestHandler: handler
|
||||||
if (dogeMode) {
|
}
|
||||||
s3Options.credentials = {
|
if (dogeMode) {
|
||||||
accessKeyId: configMap.config.accessKeyID,
|
s3Options.credentials = {
|
||||||
secretAccessKey: configMap.config.secretAccessKey,
|
accessKeyId: configMap.config.accessKeyID,
|
||||||
sessionToken: configMap.config.sessionToken
|
secretAccessKey: configMap.config.secretAccessKey,
|
||||||
}
|
sessionToken: configMap.config.sessionToken
|
||||||
}
|
}
|
||||||
let result: any
|
}
|
||||||
try {
|
let result: any
|
||||||
fileKey = decodeURIComponent(fileKey)
|
try {
|
||||||
} catch (err: any) {}
|
fileKey = decodeURIComponent(fileKey)
|
||||||
try {
|
} catch (err: any) {}
|
||||||
const client = new S3Client(s3Options)
|
try {
|
||||||
const command = new DeleteObjectCommand({
|
const client = new S3Client(s3Options)
|
||||||
Bucket: bucketName,
|
const command = new DeleteObjectCommand({
|
||||||
Key: fileKey
|
Bucket: bucketName,
|
||||||
})
|
Key: fileKey
|
||||||
result = await client.send(command)
|
})
|
||||||
} catch (err: any) {
|
result = await client.send(command)
|
||||||
s3Options.region = 'us-east-1'
|
} catch (err: any) {
|
||||||
const client = new S3Client(s3Options)
|
s3Options.region = 'us-east-1'
|
||||||
const command = new DeleteObjectCommand({
|
const client = new S3Client(s3Options)
|
||||||
Bucket: bucketName,
|
const command = new DeleteObjectCommand({
|
||||||
Key: fileKey
|
Bucket: bucketName,
|
||||||
})
|
Key: fileKey
|
||||||
result = await client.send(command)
|
})
|
||||||
}
|
result = await client.send(command)
|
||||||
return result.$metadata.httpStatusCode === 204
|
}
|
||||||
} catch (err: any) {
|
return result.$metadata.httpStatusCode === 204
|
||||||
console.log(err)
|
} catch (err: any) {
|
||||||
return false
|
console.log(err)
|
||||||
}
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
export async function removeFileFromDogeInMain (configMap: IStringKeyMap) {
|
|
||||||
try {
|
export async function removeFileFromDogeInMain (configMap: IStringKeyMap) {
|
||||||
const {
|
try {
|
||||||
config: { bucketName, AccessKey, SecretKey }
|
const {
|
||||||
} = configMap
|
config: { bucketName, AccessKey, SecretKey }
|
||||||
const token = (await getDogeToken(AccessKey, SecretKey)) as DogecloudTokenFull
|
} = configMap
|
||||||
const bucket = token.Buckets?.find(item => item.name === bucketName || item.s3Bucket === bucketName)
|
const token = (await getDogeToken(AccessKey, SecretKey)) as DogecloudTokenFull
|
||||||
const newConfigMap = { ...configMap }
|
const bucket = token.Buckets?.find(item => item.name === bucketName || item.s3Bucket === bucketName)
|
||||||
newConfigMap.config = {
|
const newConfigMap = { ...configMap }
|
||||||
...newConfigMap.config,
|
newConfigMap.config = {
|
||||||
accessKeyID: token.Credentials?.accessKeyId,
|
...newConfigMap.config,
|
||||||
secretAccessKey: token.Credentials?.secretAccessKey,
|
accessKeyID: token.Credentials?.accessKeyId,
|
||||||
sessionToken: token.Credentials?.sessionToken,
|
secretAccessKey: token.Credentials?.secretAccessKey,
|
||||||
endpoint: bucket?.s3Endpoint,
|
sessionToken: token.Credentials?.sessionToken,
|
||||||
region: dogeRegionMap[bucket?.s3Endpoint?.split('.')[1] || 'ap-shanghai'],
|
endpoint: bucket?.s3Endpoint,
|
||||||
bucketName: bucket?.s3Bucket
|
region: dogeRegionMap[bucket?.s3Endpoint?.split('.')[1] || 'ap-shanghai'],
|
||||||
}
|
bucketName: bucket?.s3Bucket
|
||||||
return await removeFileFromS3InMain(newConfigMap, true)
|
}
|
||||||
} catch (err: any) {
|
return await removeFileFromS3InMain(newConfigMap, true)
|
||||||
console.log(err)
|
} catch (err: any) {
|
||||||
return false
|
console.log(err)
|
||||||
}
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
function createHuaweiAuthorization (
|
|
||||||
bucketName: string,
|
function createHuaweiAuthorization (
|
||||||
path: string,
|
bucketName: string,
|
||||||
fileName: string,
|
path: string,
|
||||||
accessKey: string,
|
fileName: string,
|
||||||
secretKey: string,
|
accessKey: string,
|
||||||
date: string = new Date().toUTCString()
|
secretKey: string,
|
||||||
) {
|
date: string = new Date().toUTCString()
|
||||||
const strToSign = `DELETE\n\n\n${date}\n/${bucketName}${path}/${fileName}`
|
) {
|
||||||
const singature = crypto.createHmac('sha1', secretKey).update(strToSign).digest('base64')
|
const strToSign = `DELETE\n\n\n${date}\n/${bucketName}${path}/${fileName}`
|
||||||
return `OBS ${accessKey}:${singature}`
|
const singature = crypto.createHmac('sha1', secretKey).update(strToSign).digest('base64')
|
||||||
}
|
return `OBS ${accessKey}:${singature}`
|
||||||
|
}
|
||||||
export async function removeFileFromHuaweiInMain (configMap: IStringKeyMap) {
|
|
||||||
const { fileName, config } = configMap
|
export async function removeFileFromHuaweiInMain (configMap: IStringKeyMap) {
|
||||||
const { accessKeyId, accessKeySecret, bucketName, endpoint } = config
|
const { fileName, config } = configMap
|
||||||
let path = config.path || '/'
|
const { accessKeyId, accessKeySecret, bucketName, endpoint } = config
|
||||||
path = `/${path.replace(/^\/+|\/+$/, '')}`
|
let path = config.path || '/'
|
||||||
path = path === '/' ? '' : path
|
path = `/${path.replace(/^\/+|\/+$/, '')}`
|
||||||
const date = new Date().toUTCString()
|
path = path === '/' ? '' : path
|
||||||
const authorization = createHuaweiAuthorization(bucketName, path, fileName, accessKeyId, accessKeySecret, date)
|
const date = new Date().toUTCString()
|
||||||
try {
|
const authorization = createHuaweiAuthorization(bucketName, path, fileName, accessKeyId, accessKeySecret, date)
|
||||||
const res = await axios.request({
|
try {
|
||||||
url: `https://${bucketName}.${endpoint}${encodeURI(path)}/${encodeURIComponent(fileName)}`,
|
const res = await axios.request({
|
||||||
method: 'DELETE',
|
url: `https://${bucketName}.${endpoint}${encodeURI(path)}/${encodeURIComponent(fileName)}`,
|
||||||
responseType: 'json',
|
method: 'DELETE',
|
||||||
headers: {
|
responseType: 'json',
|
||||||
Host: `${bucketName}.${endpoint}`,
|
headers: {
|
||||||
Date: date,
|
Host: `${bucketName}.${endpoint}`,
|
||||||
Authorization: authorization
|
Date: date,
|
||||||
}
|
Authorization: authorization
|
||||||
})
|
}
|
||||||
return res.status === 204
|
})
|
||||||
} catch (error) {
|
return res.status === 204
|
||||||
console.log(error)
|
} catch (error) {
|
||||||
return false
|
console.log(error)
|
||||||
}
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
export async function removeFileFromSFTPInMain (config: ISftpPlistConfig, fileName: string) {
|
|
||||||
try {
|
export async function removeFileFromSFTPInMain (config: ISftpPlistConfig, fileName: string) {
|
||||||
const client = SSHClient.instance
|
try {
|
||||||
await client.connect(config)
|
const client = SSHClient.instance
|
||||||
const uploadPath = `/${config.uploadPath || ''}/`.replace(/\/+/g, '/')
|
await client.connect(config)
|
||||||
const remote = path.join(uploadPath, fileName)
|
const uploadPath = `/${config.uploadPath || ''}/`.replace(/\/+/g, '/')
|
||||||
const deleteResult = await client.deleteFileSFTP(config, remote)
|
const remote = path.join(uploadPath, fileName)
|
||||||
client.close()
|
const deleteResult = await client.deleteFileSFTP(config, remote)
|
||||||
return deleteResult
|
client.close()
|
||||||
} catch (err: any) {
|
return deleteResult
|
||||||
console.log(err)
|
} catch (err: any) {
|
||||||
return false
|
console.log(err)
|
||||||
}
|
return false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,65 +1,66 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
id="app"
|
id="app"
|
||||||
:key="pageReloadCount"
|
:key="pageReloadCount"
|
||||||
>
|
>
|
||||||
<router-view />
|
<router-view />
|
||||||
<UIServiceProvider />
|
<UIServiceProvider />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { IConfig } from 'piclist'
|
import type { IConfig } from 'piclist'
|
||||||
import { onBeforeMount, onMounted } from 'vue'
|
import { onBeforeMount, onMounted } from 'vue'
|
||||||
|
|
||||||
import UIServiceProvider from '@/components/ui/UIServiceProvider.vue'
|
import UIServiceProvider from '@/components/ui/UIServiceProvider.vue'
|
||||||
import { useATagClick } from '@/hooks/useATagClick'
|
import { useATagClick } from '@/hooks/useATagClick'
|
||||||
import { useStore } from '@/hooks/useStore'
|
import { useStore } from '@/hooks/useStore'
|
||||||
import { getConfig } from '@/utils/dataSender'
|
import { getConfig } from '@/utils/dataSender'
|
||||||
import { pageReloadCount } from '@/utils/global'
|
import { pageReloadCount } from '@/utils/global'
|
||||||
|
|
||||||
import { useAppStore } from './hooks/useAppStore'
|
import { useAppStore } from './hooks/useAppStore'
|
||||||
useATagClick()
|
|
||||||
|
useATagClick()
|
||||||
const store = useStore()
|
|
||||||
const appStore = useAppStore()
|
const store = useStore()
|
||||||
|
const appStore = useAppStore()
|
||||||
onBeforeMount(async () => {
|
|
||||||
const config = await getConfig<IConfig>()
|
onBeforeMount(async () => {
|
||||||
if (config) {
|
const config = await getConfig<IConfig>()
|
||||||
store?.setDefaultPicBed(config?.picBed?.uploader || config?.picBed?.current || 'smms')
|
if (config) {
|
||||||
}
|
store?.setDefaultPicBed(config?.picBed?.uploader || config?.picBed?.current || 'smms')
|
||||||
})
|
}
|
||||||
|
})
|
||||||
onMounted(async () => {
|
|
||||||
try {
|
onMounted(async () => {
|
||||||
appStore.init()
|
try {
|
||||||
} catch (error) {
|
appStore.init()
|
||||||
console.error('Failed to load settings:', error)
|
} catch (error) {
|
||||||
}
|
console.error('Failed to load settings:', error)
|
||||||
})
|
}
|
||||||
|
})
|
||||||
</script>
|
|
||||||
|
</script>
|
||||||
<script lang="ts">
|
|
||||||
export default {
|
<script lang="ts">
|
||||||
name: 'PicGoApp'
|
export default {
|
||||||
}
|
name: 'PicGoApp'
|
||||||
</script>
|
}
|
||||||
|
</script>
|
||||||
<style lang="stylus">
|
|
||||||
body,
|
<style lang="stylus">
|
||||||
html
|
body,
|
||||||
padding 0
|
html
|
||||||
margin 0
|
padding 0
|
||||||
height 100%
|
margin 0
|
||||||
font-family "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif
|
height 100%
|
||||||
#app
|
font-family "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif
|
||||||
height 100%
|
#app
|
||||||
user-select none
|
height 100%
|
||||||
overflow hidden
|
user-select none
|
||||||
.el-button-group
|
overflow hidden
|
||||||
width 100%
|
.el-button-group
|
||||||
.el-button
|
width 100%
|
||||||
width 50%
|
.el-button
|
||||||
</style>
|
width 50%
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import { onMounted, onUnmounted } from 'vue'
|
import { onMounted, onUnmounted } from 'vue'
|
||||||
|
|
||||||
import { IRPCActionType } from '@/utils/enum'
|
import { IRPCActionType } from '@/utils/enum'
|
||||||
|
|
||||||
export function useATagClick () {
|
export function useATagClick () {
|
||||||
const handleATagClick = (e: MouseEvent) => {
|
const handleATagClick = (e: MouseEvent) => {
|
||||||
if (e.target instanceof HTMLAnchorElement) {
|
if (e.target instanceof HTMLAnchorElement) {
|
||||||
if (e.target.href) {
|
if (e.target.href) {
|
||||||
// avoid opening localhost development URLs in external browser
|
if (!e.target.href.startsWith('http://localhost:3000')) {
|
||||||
if (!e.target.href.startsWith('http://localhost:3000')) {
|
e.preventDefault()
|
||||||
e.preventDefault()
|
window.electron.sendRPC(IRPCActionType.OPEN_URL, e.target.href)
|
||||||
window.electron.sendRPC(IRPCActionType.OPEN_URL, e.target.href)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.addEventListener('click', handleATagClick)
|
document.addEventListener('click', handleATagClick)
|
||||||
})
|
})
|
||||||
onUnmounted(() => {
|
|
||||||
document.removeEventListener('click', handleATagClick)
|
onUnmounted(() => {
|
||||||
})
|
document.removeEventListener('click', handleATagClick)
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,59 +1,55 @@
|
|||||||
import 'video.js/dist/video-js.css'
|
import 'video.js/dist/video-js.css'
|
||||||
import 'highlight.js/styles/stackoverflow-light.css'
|
import 'highlight.js/styles/stackoverflow-light.css'
|
||||||
import 'highlight.js/lib/common'
|
import 'highlight.js/lib/common'
|
||||||
|
|
||||||
import hljsVuePlugin from '@highlightjs/vue-plugin'
|
import hljsVuePlugin from '@highlightjs/vue-plugin'
|
||||||
import VueVideoPlayer from '@videojs-player/vue'
|
import VueVideoPlayer from '@videojs-player/vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
import VueLazyLoad from 'vue3-lazyload'
|
import VueLazyLoad from 'vue3-lazyload'
|
||||||
|
|
||||||
import App from '@/App.vue'
|
import App from '@/App.vue'
|
||||||
import en from '@/i18n/locales/en.json'
|
import en from '@/i18n/locales/en.json'
|
||||||
import zhCN from '@/i18n/locales/zh-CN.json'
|
import zhCN from '@/i18n/locales/zh-CN.json'
|
||||||
import zhTW from '@/i18n/locales/zh-TW.json'
|
import zhTW from '@/i18n/locales/zh-TW.json'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
import { store } from '@/store'
|
import { store } from '@/store'
|
||||||
import db from '@/utils/db'
|
import db from '@/utils/db'
|
||||||
|
|
||||||
type MessageSchema = typeof zhCN
|
type MessageSchema = typeof zhCN
|
||||||
|
|
||||||
window.electron.setVisualZoomLevelLimits(1, 1)
|
window.electron.setVisualZoomLevelLimits(1, 1)
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.config.globalProperties.$$db = db
|
app.config.globalProperties.$$db = db
|
||||||
app.config.globalProperties.triggerRPC = window.electron.triggerRPC
|
app.config.globalProperties.triggerRPC = window.electron.triggerRPC
|
||||||
app.config.globalProperties.sendRPC = window.electron.sendRPC
|
app.config.globalProperties.sendRPC = window.electron.sendRPC
|
||||||
app.config.globalProperties.sendToMain = window.electron.sendToMain
|
app.config.globalProperties.sendToMain = window.electron.sendToMain
|
||||||
|
|
||||||
const i18n = createI18n<[MessageSchema], 'en' | 'zh-CN' | 'zh-TW'>({
|
const i18n = createI18n<[MessageSchema], 'en' | 'zh-CN' | 'zh-TW'>({
|
||||||
legacy: false,
|
legacy: false,
|
||||||
locale: localStorage.getItem('currentLanguage') || 'zh-CN',
|
locale: localStorage.getItem('currentLanguage') || 'zh-CN',
|
||||||
fallbackLocale: 'zh-CN',
|
fallbackLocale: 'zh-CN',
|
||||||
messages: {
|
messages: {
|
||||||
en,
|
en,
|
||||||
'zh-CN': zhCN,
|
'zh-CN': zhCN,
|
||||||
'zh-TW': zhTW
|
'zh-TW': zhTW
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
pinia.use(piniaPluginPersistedstate)
|
pinia.use(piniaPluginPersistedstate)
|
||||||
app.use(VueLazyLoad, {
|
app.use(VueLazyLoad, {
|
||||||
loading: '/loading.jpg',
|
loading: '/loading.jpg',
|
||||||
error: '/unknown-file-type.svg',
|
error: '/unknown-file-type.svg',
|
||||||
delay: 500
|
delay: 500
|
||||||
})
|
})
|
||||||
app.use(i18n)
|
app.use(i18n)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(store)
|
app.use(store)
|
||||||
app.use(pinia)
|
app.use(pinia)
|
||||||
app.use(hljsVuePlugin)
|
app.use(hljsVuePlugin)
|
||||||
app.use(VueVideoPlayer)
|
app.use(VueVideoPlayer)
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
||||||
export {
|
|
||||||
i18n
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,444 +1,445 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="upload-container">
|
<div class="upload-container">
|
||||||
<!-- Header Card -->
|
<!-- Header Card -->
|
||||||
<div class="upload-card header-card">
|
<div class="upload-card header-card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="provider-section">
|
<div class="provider-section">
|
||||||
<button
|
<button
|
||||||
class="provider-button"
|
class="provider-button"
|
||||||
:title="t('pages.upload.uploadViewHint')"
|
:title="t('pages.upload.uploadViewHint')"
|
||||||
@click="handlePicBedNameClick(picBedName, picBedConfigName)"
|
@click="handlePicBedNameClick(picBedName, picBedConfigName)"
|
||||||
>
|
>
|
||||||
<div class="provider-info">
|
<div class="provider-info">
|
||||||
<span class="provider-name">{{ picBedName }}</span>
|
<span class="provider-name">{{ picBedName }}</span>
|
||||||
<span class="provider-config">{{ picBedConfigName || 'Default' }}</span>
|
<span class="provider-config">{{ picBedConfigName || 'Default' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<ChevronDownIcon
|
<ChevronDownIcon
|
||||||
:size="16"
|
:size="16"
|
||||||
class="provider-arrow"
|
class="provider-arrow"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
<button
|
<button
|
||||||
class="action-button secondary"
|
class="action-button secondary"
|
||||||
@click="handleImageProcess"
|
@click="handleImageProcess"
|
||||||
>
|
>
|
||||||
<Settings :size="16" />
|
<Settings :size="16" />
|
||||||
<span>{{ t('pages.upload.imageProcessName') }}</span>
|
<span>{{ t('pages.upload.imageProcessName') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="action-button"
|
class="action-button"
|
||||||
@click="handleChangePicBed"
|
@click="handleChangePicBed"
|
||||||
>
|
>
|
||||||
<DatabaseIcon :size="16" />
|
<DatabaseIcon :size="16" />
|
||||||
<span>{{ t('pages.upload.changePicBed') }}</span>
|
<span>{{ t('pages.upload.changePicBed') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Main Upload Card -->
|
<!-- Main Upload Card -->
|
||||||
<div class="upload-card main-card">
|
<div class="upload-card main-card">
|
||||||
<div
|
<div
|
||||||
class="upload-zone"
|
id="upload-area"
|
||||||
:class="{ 'drag-active': dragover }"
|
class="upload-zone"
|
||||||
@drop.prevent="onDrop"
|
:class="{ 'drag-active': dragover }"
|
||||||
@dragover.prevent="dragover = true"
|
@drop.prevent="onDrop"
|
||||||
@dragleave.prevent="dragover = false"
|
@dragover.prevent="dragover = true"
|
||||||
@click="openUplodWindow"
|
@dragleave.prevent="dragover = false"
|
||||||
>
|
@click="openUplodWindow"
|
||||||
<div class="upload-content">
|
>
|
||||||
<div class="upload-icon">
|
<div class="upload-content">
|
||||||
<UploadCloudIcon :size="48" />
|
<div class="upload-icon">
|
||||||
</div>
|
<UploadCloudIcon :size="48" />
|
||||||
<div class="upload-text">
|
</div>
|
||||||
<h3 class="upload-title">
|
<div class="upload-text">
|
||||||
{{ t('pages.upload.dragFileToHere') }}
|
<h3 class="upload-title">
|
||||||
</h3>
|
{{ t('pages.upload.dragFileToHere') }}
|
||||||
<p class="upload-subtitle">
|
</h3>
|
||||||
{{ t('pages.upload.clickToUpload') }}
|
<p class="upload-subtitle">
|
||||||
</p>
|
{{ t('pages.upload.clickToUpload') }}
|
||||||
<div class="upload-formats">
|
</p>
|
||||||
<span class="format-label">{{ t('pages.upload.uploadHint') }}</span>
|
<div class="upload-formats">
|
||||||
</div>
|
<span class="format-label">{{ t('pages.upload.uploadHint') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input
|
</div>
|
||||||
id="file-uploader"
|
<input
|
||||||
ref="fileInput"
|
id="file-uploader"
|
||||||
type="file"
|
ref="fileInput"
|
||||||
multiple
|
type="file"
|
||||||
style="display: none"
|
multiple
|
||||||
@change="onChange"
|
style="display: none"
|
||||||
>
|
@change="onChange"
|
||||||
</div>
|
>
|
||||||
|
</div>
|
||||||
<!-- Progress Bar -->
|
|
||||||
<transition name="progress">
|
<!-- Progress Bar -->
|
||||||
<div
|
<transition name="progress">
|
||||||
v-if="showProgress"
|
<div
|
||||||
class="progress-container"
|
v-if="showProgress"
|
||||||
>
|
class="progress-container"
|
||||||
<div class="progress-bar">
|
>
|
||||||
<div
|
<div class="progress-bar">
|
||||||
class="progress-fill"
|
<div
|
||||||
:class="{ 'progress-error': showError }"
|
class="progress-fill"
|
||||||
:style="{ width: `${progress}%` }"
|
:class="{ 'progress-error': showError }"
|
||||||
/>
|
:style="{ width: `${progress}%` }"
|
||||||
</div>
|
/>
|
||||||
<span class="progress-text">
|
</div>
|
||||||
{{ showError ? t('pages.upload.uploadFailed') : `${progress}%` }}
|
<span class="progress-text">
|
||||||
</span>
|
{{ showError ? t('pages.upload.uploadFailed') : `${progress}%` }}
|
||||||
</div>
|
</span>
|
||||||
</transition>
|
</div>
|
||||||
</div>
|
</transition>
|
||||||
|
</div>
|
||||||
<!-- Quick Actions Card -->
|
|
||||||
<div class="upload-card actions-card">
|
<!-- Quick Actions Card -->
|
||||||
<div class="card-header">
|
<div class="upload-card actions-card">
|
||||||
<h4 class="card-title">
|
<div class="card-header">
|
||||||
{{ t('pages.upload.quickUpload') }}
|
<h4 class="card-title">
|
||||||
</h4>
|
{{ t('pages.upload.quickUpload') }}
|
||||||
</div>
|
</h4>
|
||||||
<div class="quick-actions">
|
</div>
|
||||||
<button
|
<div class="quick-actions">
|
||||||
class="quick-action-button"
|
<button
|
||||||
@click="uploadClipboardFiles"
|
class="quick-action-button"
|
||||||
>
|
@click="uploadClipboardFiles"
|
||||||
<ClipboardIcon :size="20" />
|
>
|
||||||
<span>{{ t('pages.upload.clipboardPicture') }}</span>
|
<ClipboardIcon :size="20" />
|
||||||
</button>
|
<span>{{ t('pages.upload.clipboardPicture') }}</span>
|
||||||
<button
|
</button>
|
||||||
class="quick-action-button"
|
<button
|
||||||
@click="uploadURLFiles"
|
class="quick-action-button"
|
||||||
>
|
@click="uploadURLFiles"
|
||||||
<LinkIcon :size="20" />
|
>
|
||||||
<span>{{ t('pages.upload.urlUpload') }}</span>
|
<LinkIcon :size="20" />
|
||||||
</button>
|
<span>{{ t('pages.upload.urlUpload') }}</span>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<!-- Settings Card -->
|
|
||||||
<div class="upload-card settings-card">
|
<!-- Settings Card -->
|
||||||
<div class="card-header">
|
<div class="upload-card settings-card">
|
||||||
<h4 class="card-title">
|
<div class="card-header">
|
||||||
{{ t('pages.upload.linkFormat') }}
|
<h4 class="card-title">
|
||||||
</h4>
|
{{ t('pages.upload.linkFormat') }}
|
||||||
</div>
|
</h4>
|
||||||
<div class="settings-content">
|
</div>
|
||||||
<!-- Format Options -->
|
<div class="settings-content">
|
||||||
<div class="setting-group">
|
<!-- Format Options -->
|
||||||
<label class="setting-label">{{ t('pages.upload.outputFormat') }}</label>
|
<div class="setting-group">
|
||||||
<div class="format-buttons">
|
<label class="setting-label">{{ t('pages.upload.outputFormat') }}</label>
|
||||||
<button
|
<div class="format-buttons">
|
||||||
v-for="(format, key) in pasteFormatList"
|
<button
|
||||||
:key="key"
|
v-for="(format, key) in pasteFormatList"
|
||||||
class="format-button"
|
:key="key"
|
||||||
:class="{ active: pasteStyle === key }"
|
class="format-button"
|
||||||
:title="format"
|
:class="{ active: pasteStyle === key }"
|
||||||
@click="updatePasteStyle(key)"
|
:title="format"
|
||||||
>
|
@click="updatePasteStyle(key)"
|
||||||
{{ key }}
|
>
|
||||||
</button>
|
{{ key }}
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<!-- URL Length Options -->
|
|
||||||
<div class="setting-group">
|
<!-- URL Length Options -->
|
||||||
<label class="setting-label">{{ t('pages.upload.urlType.title') }}</label>
|
<div class="setting-group">
|
||||||
<div class="url-toggle">
|
<label class="setting-label">{{ t('pages.upload.urlType.title') }}</label>
|
||||||
<button
|
<div class="url-toggle">
|
||||||
class="toggle-button"
|
<button
|
||||||
:class="{ active: !useShortUrl }"
|
class="toggle-button"
|
||||||
@click="updateUrlType(false)"
|
:class="{ active: !useShortUrl }"
|
||||||
>
|
@click="updateUrlType(false)"
|
||||||
<span>{{ t('pages.upload.urlType.normal') }}</span>
|
>
|
||||||
</button>
|
<span>{{ t('pages.upload.urlType.normal') }}</span>
|
||||||
<button
|
</button>
|
||||||
class="toggle-button"
|
<button
|
||||||
:class="{ active: useShortUrl }"
|
class="toggle-button"
|
||||||
@click="updateUrlType(true)"
|
:class="{ active: useShortUrl }"
|
||||||
>
|
@click="updateUrlType(true)"
|
||||||
<span>{{ t('pages.upload.urlType.short') }}</span>
|
>
|
||||||
</button>
|
<span>{{ t('pages.upload.urlType.short') }}</span>
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<!-- Image Process Dialog -->
|
|
||||||
<transition name="modal">
|
<!-- Image Process Dialog -->
|
||||||
<div
|
<transition name="modal">
|
||||||
v-if="imageProcessDialogVisible"
|
<div
|
||||||
class="modal-overlay"
|
v-if="imageProcessDialogVisible"
|
||||||
@click="imageProcessDialogVisible = false"
|
class="modal-overlay"
|
||||||
>
|
@click="imageProcessDialogVisible = false"
|
||||||
<div
|
>
|
||||||
class="modal-container"
|
<div
|
||||||
@click.stop
|
class="modal-container"
|
||||||
>
|
@click.stop
|
||||||
<div class="modal-header">
|
>
|
||||||
<h3 class="modal-title">
|
<div class="modal-header">
|
||||||
{{ t('pages.imageProcess.title') }}
|
<h3 class="modal-title">
|
||||||
</h3>
|
{{ t('pages.imageProcess.title') }}
|
||||||
<button
|
</h3>
|
||||||
class="modal-close"
|
<button
|
||||||
@click="imageProcessDialogVisible = false"
|
class="modal-close"
|
||||||
>
|
@click="imageProcessDialogVisible = false"
|
||||||
<XIcon :size="20" />
|
>
|
||||||
</button>
|
<XIcon :size="20" />
|
||||||
</div>
|
</button>
|
||||||
<div class="modal-content">
|
</div>
|
||||||
<ImageProcessSetting v-model="imageProcessDialogVisible" />
|
<div class="modal-content">
|
||||||
</div>
|
<ImageProcessSetting v-model="imageProcessDialogVisible" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</div>
|
||||||
</div>
|
</transition>
|
||||||
</template>
|
</div>
|
||||||
|
</template>
|
||||||
<script lang="ts" setup>
|
|
||||||
import type { IpcRendererEvent } from 'electron'
|
<script lang="ts" setup>
|
||||||
import { ChevronDownIcon, ClipboardIcon, DatabaseIcon, LinkIcon, Settings, UploadCloudIcon, XIcon } from 'lucide-vue-next'
|
import type { IpcRendererEvent } from 'electron'
|
||||||
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
|
import { ChevronDownIcon, ClipboardIcon, DatabaseIcon, LinkIcon, Settings, UploadCloudIcon, XIcon } from 'lucide-vue-next'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import ImageProcessSetting from '@/components/ImageProcessSetting.vue'
|
|
||||||
import useMessage from '@/hooks/useMessage'
|
import ImageProcessSetting from '@/components/ImageProcessSetting.vue'
|
||||||
import { PICBEDS_PAGE } from '@/router/config'
|
import useMessage from '@/hooks/useMessage'
|
||||||
import $bus from '@/utils/bus'
|
import { PICBEDS_PAGE } from '@/router/config'
|
||||||
import { isUrl } from '@/utils/common'
|
import $bus from '@/utils/bus'
|
||||||
import { configPaths } from '@/utils/configPaths'
|
import { isUrl } from '@/utils/common'
|
||||||
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant'
|
import { configPaths } from '@/utils/configPaths'
|
||||||
import { getConfig, saveConfig } from '@/utils/dataSender'
|
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant'
|
||||||
import { useDragEventListeners } from '@/utils/drag'
|
import { getConfig, saveConfig } from '@/utils/dataSender'
|
||||||
import { IPasteStyle, IRPCActionType } from '@/utils/enum'
|
import { useDragEventListeners } from '@/utils/drag'
|
||||||
import { picBedGlobal, updatePicBedGlobal } from '@/utils/global'
|
import { IPasteStyle, IRPCActionType } from '@/utils/enum'
|
||||||
import type { IFileWithPath, IUploaderConfigItem } from '#/types/types'
|
import { picBedGlobal, updatePicBedGlobal } from '@/utils/global'
|
||||||
|
import type { IFileWithPath, IUploaderConfigItem } from '#/types/types'
|
||||||
useDragEventListeners()
|
|
||||||
const $router = useRouter()
|
useDragEventListeners()
|
||||||
const { t } = useI18n()
|
const $router = useRouter()
|
||||||
const message = useMessage()
|
const { t } = useI18n()
|
||||||
|
const message = useMessage()
|
||||||
const imageProcessDialogVisible = ref(false)
|
|
||||||
const useShortUrl = ref(false)
|
const imageProcessDialogVisible = ref(false)
|
||||||
const dragover = ref(false)
|
const useShortUrl = ref(false)
|
||||||
const progress = ref(0)
|
const dragover = ref(false)
|
||||||
const showProgress = ref(false)
|
const progress = ref(0)
|
||||||
const showError = ref(false)
|
const showProgress = ref(false)
|
||||||
const pasteStyle = ref('')
|
const showError = ref(false)
|
||||||
const picBedName = ref('')
|
const pasteStyle = ref('')
|
||||||
const picBedConfigName = ref('')
|
const picBedName = ref('')
|
||||||
const fileInput = ref<HTMLInputElement>()
|
const picBedConfigName = ref('')
|
||||||
|
const fileInput = ref<HTMLInputElement>()
|
||||||
const pasteFormatList = ref<Record<string, string>>({
|
|
||||||
[IPasteStyle.MARKDOWN]: '',
|
const pasteFormatList = ref<Record<string, string>>({
|
||||||
[IPasteStyle.HTML]: '<img src="url"/>',
|
[IPasteStyle.MARKDOWN]: '',
|
||||||
[IPasteStyle.URL]: 'http://test.com/test.png',
|
[IPasteStyle.HTML]: '<img src="url"/>',
|
||||||
[IPasteStyle.UBB]: '[img]url[/img]',
|
[IPasteStyle.URL]: 'http://test.com/test.png',
|
||||||
[IPasteStyle.CUSTOM]: ''
|
[IPasteStyle.UBB]: '[img]url[/img]',
|
||||||
})
|
[IPasteStyle.CUSTOM]: ''
|
||||||
|
})
|
||||||
watch(picBedGlobal, () => {
|
|
||||||
getDefaultPicBed()
|
watch(picBedGlobal, () => {
|
||||||
})
|
getDefaultPicBed()
|
||||||
|
})
|
||||||
const uploadProgressHandler = (_event: IpcRendererEvent, _progress: number) => {
|
|
||||||
if (_progress !== -1) {
|
const uploadProgressHandler = (_event: IpcRendererEvent, _progress: number) => {
|
||||||
showProgress.value = true
|
if (_progress !== -1) {
|
||||||
progress.value = _progress
|
showProgress.value = true
|
||||||
} else {
|
progress.value = _progress
|
||||||
progress.value = 100
|
} else {
|
||||||
showError.value = true
|
progress.value = 100
|
||||||
}
|
showError.value = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const syncPicBedHandler = () => {
|
|
||||||
getDefaultPicBed()
|
const syncPicBedHandler = () => {
|
||||||
}
|
getDefaultPicBed()
|
||||||
|
}
|
||||||
onBeforeMount(() => {
|
|
||||||
updatePicBedGlobal()
|
onBeforeMount(() => {
|
||||||
window.electron.ipcRendererOn('uploadProgress', uploadProgressHandler)
|
updatePicBedGlobal()
|
||||||
getUseShortUrl()
|
window.electron.ipcRendererOn('uploadProgress', uploadProgressHandler)
|
||||||
getPasteStyle()
|
getUseShortUrl()
|
||||||
getDefaultPicBed()
|
getPasteStyle()
|
||||||
window.electron.ipcRendererOn('syncPicBed', syncPicBedHandler)
|
getDefaultPicBed()
|
||||||
$bus.on(SHOW_INPUT_BOX_RESPONSE, handleInputBoxValue)
|
window.electron.ipcRendererOn('syncPicBed', syncPicBedHandler)
|
||||||
})
|
$bus.on(SHOW_INPUT_BOX_RESPONSE, handleInputBoxValue)
|
||||||
|
})
|
||||||
const handleImageProcess = () => {
|
|
||||||
imageProcessDialogVisible.value = true
|
const handleImageProcess = () => {
|
||||||
}
|
imageProcessDialogVisible.value = true
|
||||||
|
}
|
||||||
watch(progress, onProgressChange)
|
|
||||||
|
watch(progress, onProgressChange)
|
||||||
function onProgressChange (val: number) {
|
|
||||||
if (val === 100) {
|
function onProgressChange (val: number) {
|
||||||
setTimeout(() => {
|
if (val === 100) {
|
||||||
showProgress.value = false
|
setTimeout(() => {
|
||||||
showError.value = false
|
showProgress.value = false
|
||||||
}, 1000)
|
showError.value = false
|
||||||
setTimeout(() => {
|
}, 1000)
|
||||||
progress.value = 0
|
setTimeout(() => {
|
||||||
}, 1200)
|
progress.value = 0
|
||||||
}
|
}, 1200)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
async function handlePicBedNameClick (_picBedName: string, picBedConfigName: string | undefined) {
|
|
||||||
const formatedpicBedConfigName = picBedConfigName || 'Default'
|
async function handlePicBedNameClick (_picBedName: string, picBedConfigName: string | undefined) {
|
||||||
const currentPicBed = await getConfig<string>(configPaths.picBed.current)
|
const formatedpicBedConfigName = picBedConfigName || 'Default'
|
||||||
const currentPicBedConfig = ((await getConfig<any[]>(`uploader.${currentPicBed}`)) as any) || {}
|
const currentPicBed = await getConfig<string>(configPaths.picBed.current)
|
||||||
const configList = await window.electron.triggerRPC<IUploaderConfigItem>(IRPCActionType.PICBED_GET_CONFIG_LIST, currentPicBed)
|
const currentPicBedConfig = ((await getConfig<any[]>(`uploader.${currentPicBed}`)) as any) || {}
|
||||||
const currentConfigList = configList?.configList ?? []
|
const configList = await window.electron.triggerRPC<IUploaderConfigItem>(IRPCActionType.PICBED_GET_CONFIG_LIST, currentPicBed)
|
||||||
const config = currentConfigList.find((item: any) => item._configName === formatedpicBedConfigName)
|
const currentConfigList = configList?.configList ?? []
|
||||||
$router.push({
|
const config = currentConfigList.find((item: any) => item._configName === formatedpicBedConfigName)
|
||||||
name: PICBEDS_PAGE,
|
$router.push({
|
||||||
params: {
|
name: PICBEDS_PAGE,
|
||||||
type: currentPicBed,
|
params: {
|
||||||
configId: config?._id || ''
|
type: currentPicBed,
|
||||||
},
|
configId: config?._id || ''
|
||||||
query: {
|
},
|
||||||
defaultConfigId: currentPicBedConfig.defaultId || ''
|
query: {
|
||||||
}
|
defaultConfigId: currentPicBedConfig.defaultId || ''
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
|
}
|
||||||
onBeforeUnmount(() => {
|
|
||||||
$bus.off(SHOW_INPUT_BOX_RESPONSE)
|
onBeforeUnmount(() => {
|
||||||
window.electron.ipcRendererRemoveListener('uploadProgress', uploadProgressHandler)
|
$bus.off(SHOW_INPUT_BOX_RESPONSE)
|
||||||
window.electron.ipcRendererRemoveListener('syncPicBed', syncPicBedHandler)
|
window.electron.ipcRendererRemoveListener('uploadProgress', uploadProgressHandler)
|
||||||
})
|
window.electron.ipcRendererRemoveListener('syncPicBed', syncPicBedHandler)
|
||||||
|
})
|
||||||
function onDrop (e: DragEvent) {
|
|
||||||
dragover.value = false
|
function onDrop (e: DragEvent) {
|
||||||
|
dragover.value = false
|
||||||
// send files first
|
|
||||||
if (e.dataTransfer?.files?.length) {
|
// send files first
|
||||||
ipcSendFiles(e.dataTransfer.files)
|
if (e.dataTransfer?.files?.length) {
|
||||||
} else if (e.dataTransfer?.items) {
|
ipcSendFiles(e.dataTransfer.files)
|
||||||
const items = e.dataTransfer.items
|
} else if (e.dataTransfer?.items) {
|
||||||
if (items.length === 2 && items[0].type === 'text/uri-list') {
|
const items = e.dataTransfer.items
|
||||||
handleURLDrag(items, e.dataTransfer)
|
if (items.length === 2 && items[0].type === 'text/uri-list') {
|
||||||
} else if (items[0].type === 'text/plain') {
|
handleURLDrag(items, e.dataTransfer)
|
||||||
const str = e.dataTransfer.getData(items[0].type)
|
} else if (items[0].type === 'text/plain') {
|
||||||
if (isUrl(str)) {
|
const str = e.dataTransfer.getData(items[0].type)
|
||||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [{ path: str }])
|
if (isUrl(str)) {
|
||||||
} else {
|
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [{ path: str }])
|
||||||
message.error(t('pages.upload.dragValidPictureOrUrl'))
|
} else {
|
||||||
}
|
message.error(t('pages.upload.dragValidPictureOrUrl'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
function handleURLDrag (items: DataTransferItemList, dataTransfer: DataTransfer) {
|
|
||||||
// text/html
|
function handleURLDrag (items: DataTransferItemList, dataTransfer: DataTransfer) {
|
||||||
// Use this data to get a more precise URL
|
// text/html
|
||||||
const urlString = dataTransfer.getData(items[1].type)
|
// Use this data to get a more precise URL
|
||||||
const urlMatch = urlString.match(/<img.*src="(.*?)"/)
|
const urlString = dataTransfer.getData(items[1].type)
|
||||||
if (urlMatch) {
|
const urlMatch = urlString.match(/<img.*src="(.*?)"/)
|
||||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [
|
if (urlMatch) {
|
||||||
{
|
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [
|
||||||
path: urlMatch[1]
|
{
|
||||||
}
|
path: urlMatch[1]
|
||||||
])
|
}
|
||||||
} else {
|
])
|
||||||
message.error(t('pages.upload.dragValidPictureOrUrl'))
|
} else {
|
||||||
}
|
message.error(t('pages.upload.dragValidPictureOrUrl'))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
function openUplodWindow () {
|
|
||||||
fileInput.value?.click()
|
function openUplodWindow () {
|
||||||
}
|
fileInput.value?.click()
|
||||||
|
}
|
||||||
function onChange (e: any) {
|
|
||||||
ipcSendFiles(e.target.files)
|
function onChange (e: any) {
|
||||||
;(fileInput.value as HTMLInputElement).value = ''
|
ipcSendFiles(e.target.files)
|
||||||
}
|
;(fileInput.value as HTMLInputElement).value = ''
|
||||||
|
}
|
||||||
function ipcSendFiles (files: FileList) {
|
|
||||||
const sendFiles: IFileWithPath[] = []
|
function ipcSendFiles (files: FileList) {
|
||||||
Array.from(files).forEach(item => {
|
const sendFiles: IFileWithPath[] = []
|
||||||
const obj = {
|
Array.from(files).forEach(item => {
|
||||||
name: item.name,
|
const obj = {
|
||||||
path: window.electron.showFilePath(item)
|
name: item.name,
|
||||||
}
|
path: window.electron.showFilePath(item)
|
||||||
sendFiles.push(obj)
|
}
|
||||||
})
|
sendFiles.push(obj)
|
||||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, sendFiles)
|
})
|
||||||
}
|
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, sendFiles)
|
||||||
|
}
|
||||||
async function getPasteStyle () {
|
|
||||||
pasteStyle.value = (await getConfig(configPaths.settings.pasteStyle)) || IPasteStyle.MARKDOWN
|
async function getPasteStyle () {
|
||||||
pasteFormatList.value.Custom = (await getConfig(configPaths.settings.customLink)) || ''
|
pasteStyle.value = (await getConfig(configPaths.settings.pasteStyle)) || IPasteStyle.MARKDOWN
|
||||||
}
|
pasteFormatList.value.Custom = (await getConfig(configPaths.settings.customLink)) || ''
|
||||||
|
}
|
||||||
async function getUseShortUrl () {
|
|
||||||
useShortUrl.value = (await getConfig(configPaths.settings.useShortUrl)) || false
|
async function getUseShortUrl () {
|
||||||
}
|
useShortUrl.value = (await getConfig(configPaths.settings.useShortUrl)) || false
|
||||||
|
}
|
||||||
function updatePasteStyle (style: string) {
|
|
||||||
pasteStyle.value = style
|
function updatePasteStyle (style: string) {
|
||||||
saveConfig({
|
pasteStyle.value = style
|
||||||
[configPaths.settings.pasteStyle]: style || IPasteStyle.MARKDOWN
|
saveConfig({
|
||||||
})
|
[configPaths.settings.pasteStyle]: style || IPasteStyle.MARKDOWN
|
||||||
}
|
})
|
||||||
|
}
|
||||||
function updateUrlType (shortUrl: boolean) {
|
|
||||||
useShortUrl.value = shortUrl
|
function updateUrlType (shortUrl: boolean) {
|
||||||
saveConfig({
|
useShortUrl.value = shortUrl
|
||||||
[configPaths.settings.useShortUrl]: shortUrl
|
saveConfig({
|
||||||
})
|
[configPaths.settings.useShortUrl]: shortUrl
|
||||||
}
|
})
|
||||||
|
}
|
||||||
function uploadClipboardFiles () {
|
|
||||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CLIPBOARD_FILES_FROM_UPLOAD_PAGE)
|
function uploadClipboardFiles () {
|
||||||
}
|
window.electron.sendRPC(IRPCActionType.UPLOAD_CLIPBOARD_FILES_FROM_UPLOAD_PAGE)
|
||||||
|
}
|
||||||
async function uploadURLFiles () {
|
|
||||||
const str = await navigator.clipboard.readText()
|
async function uploadURLFiles () {
|
||||||
$bus.emit(SHOW_INPUT_BOX, {
|
const str = await navigator.clipboard.readText()
|
||||||
value: isUrl(str) ? str : '',
|
$bus.emit(SHOW_INPUT_BOX, {
|
||||||
title: t('pages.upload.inputUrlTip'),
|
value: isUrl(str) ? str : '',
|
||||||
placeholder: t('pages.upload.httpPrefixTip')
|
title: t('pages.upload.inputUrlTip'),
|
||||||
})
|
placeholder: t('pages.upload.httpPrefixTip')
|
||||||
}
|
})
|
||||||
|
}
|
||||||
function handleInputBoxValue (val: string) {
|
|
||||||
if (val === '') return
|
function handleInputBoxValue (val: string) {
|
||||||
if (isUrl(val)) {
|
if (val === '') return
|
||||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [
|
if (isUrl(val)) {
|
||||||
{
|
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [
|
||||||
path: val
|
{
|
||||||
}
|
path: val
|
||||||
])
|
}
|
||||||
} else {
|
])
|
||||||
message.error(t('pages.upload.inputValidUrl'))
|
} else {
|
||||||
}
|
message.error(t('pages.upload.inputValidUrl'))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
async function getDefaultPicBed () {
|
|
||||||
const currentPicBed = await getConfig<string>(configPaths.picBed.current)
|
async function getDefaultPicBed () {
|
||||||
picBedGlobal.value.forEach(item => {
|
const currentPicBed = await getConfig<string>(configPaths.picBed.current)
|
||||||
if (item.type === currentPicBed) {
|
picBedGlobal.value.forEach(item => {
|
||||||
picBedName.value = item.name
|
if (item.type === currentPicBed) {
|
||||||
}
|
picBedName.value = item.name
|
||||||
})
|
}
|
||||||
picBedConfigName.value = (await getConfig<string>(`picBed.${currentPicBed}._configName`)) || ''
|
})
|
||||||
}
|
picBedConfigName.value = (await getConfig<string>(`picBed.${currentPicBed}._configName`)) || ''
|
||||||
|
}
|
||||||
async function handleChangePicBed () {
|
|
||||||
window.electron.sendRPC(IRPCActionType.SHOW_UPLOAD_PAGE_MENU)
|
async function handleChangePicBed () {
|
||||||
}
|
window.electron.sendRPC(IRPCActionType.SHOW_UPLOAD_PAGE_MENU)
|
||||||
</script>
|
}
|
||||||
|
</script>
|
||||||
<script lang="ts">
|
|
||||||
export default {
|
<script lang="ts">
|
||||||
name: 'UploadPage'
|
export default {
|
||||||
}
|
name: 'UploadPage'
|
||||||
</script>
|
}
|
||||||
|
</script>
|
||||||
<style scoped src="./css/UploadPage.css"></style>
|
|
||||||
|
<style scoped src="./css/UploadPage.css"></style>
|
||||||
|
|||||||
Reference in New Issue
Block a user