mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-07-11 23:51:27 +08:00
🐛 Fix(custom): fix memory leak issues
This commit is contained in:
@@ -1,96 +1,101 @@
|
|||||||
import crypto from 'node:crypto'
|
import crypto from 'node:crypto'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
|
|
||||||
import { clipboard, contextBridge, ipcRenderer, webFrame, webUtils } from 'electron'
|
import { clipboard, contextBridge, ipcRenderer, IpcRendererEvent, webFrame, webUtils } from 'electron'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import yaml from 'js-yaml'
|
import yaml from 'js-yaml'
|
||||||
import mime from 'mime-types'
|
import mime from 'mime-types'
|
||||||
import { isReactive, isRef, toRaw, unref } from 'vue'
|
import { isReactive, isRef, toRaw, unref } from 'vue'
|
||||||
|
|
||||||
import type { IpcRendererListener } from '#/types/electron'
|
export const getRawData = (args: any): any => {
|
||||||
|
if (isRef(args)) return unref(args)
|
||||||
export const getRawData = (args: any): any => {
|
if (isReactive(args)) return toRaw(args)
|
||||||
if (isRef(args)) return unref(args)
|
if (Array.isArray(args)) return args.map(getRawData)
|
||||||
if (isReactive(args)) return toRaw(args)
|
if (typeof args === 'object' && args !== null) {
|
||||||
if (Array.isArray(args)) return args.map(getRawData)
|
const data = {} as Record<string, any>
|
||||||
if (typeof args === 'object' && args !== null) {
|
for (const key in args) {
|
||||||
const data = {} as Record<string, any>
|
data[key] = getRawData(args[key])
|
||||||
for (const key in args) {
|
}
|
||||||
data[key] = getRawData(args[key])
|
return data
|
||||||
}
|
}
|
||||||
return data
|
return args
|
||||||
}
|
}
|
||||||
return args
|
|
||||||
}
|
function sendToMain (channel: string, ...args: any[]) {
|
||||||
|
ipcRenderer.send(channel, ...getRawData(args))
|
||||||
function sendToMain (channel: string, ...args: any[]) {
|
}
|
||||||
ipcRenderer.send(channel, ...getRawData(args))
|
|
||||||
}
|
function sendRPC (action: string, ...args: any[]): void {
|
||||||
|
ipcRenderer.send('RPC_ACTIONS', action, getRawData(args))
|
||||||
function sendRPC (action: string, ...args: any[]): void {
|
}
|
||||||
ipcRenderer.send('RPC_ACTIONS', action, getRawData(args))
|
|
||||||
}
|
async function triggerRPC<T> (action: string, ...args: any[]): Promise<T | undefined> {
|
||||||
|
return await ipcRenderer.invoke('RPC_ACTIONS_INVOKE', action, getRawData(args))
|
||||||
async function triggerRPC<T> (action: string, ...args: any[]): Promise<T | undefined> {
|
}
|
||||||
return await ipcRenderer.invoke('RPC_ACTIONS_INVOKE', action, getRawData(args))
|
|
||||||
}
|
function sendRpcSync (action: string, ...args: any[]): any {
|
||||||
|
return ipcRenderer.sendSync('RPC_ACTIONS', action, getRawData(args))
|
||||||
function sendRpcSync (action: string, ...args: any[]): any {
|
}
|
||||||
return ipcRenderer.sendSync('RPC_ACTIONS', action, getRawData(args))
|
|
||||||
}
|
try {
|
||||||
|
contextBridge.exposeInMainWorld('electron', {
|
||||||
try {
|
setVisualZoomLevelLimits: (min: number, max: number) => {
|
||||||
contextBridge.exposeInMainWorld('electron', {
|
webFrame.setVisualZoomLevelLimits(min, max)
|
||||||
setVisualZoomLevelLimits: (min: number, max: number) => {
|
},
|
||||||
webFrame.setVisualZoomLevelLimits(min, max)
|
clipboard: {
|
||||||
},
|
writeText: clipboard.writeText
|
||||||
clipboard: {
|
},
|
||||||
writeText: clipboard.writeText
|
platform: process.platform,
|
||||||
},
|
sendRpcSync,
|
||||||
platform: process.platform,
|
triggerRPC,
|
||||||
sendRpcSync,
|
sendToMain,
|
||||||
triggerRPC,
|
sendRPC,
|
||||||
sendToMain,
|
ipcRendererOn: (channel: string, listener: (...args: any[]) => void) => {
|
||||||
sendRPC,
|
const subscription = (_: IpcRendererEvent, ...args: any[]) => listener(...args)
|
||||||
ipcRendererOn: (channel: string, listener: IpcRendererListener) => {
|
ipcRenderer.on(channel, subscription)
|
||||||
ipcRenderer.on(channel, listener)
|
return () => {
|
||||||
},
|
ipcRenderer.removeListener(channel, subscription)
|
||||||
ipcRendererRemoveListener: (channel: string, listener: IpcRendererListener) => {
|
}
|
||||||
ipcRenderer.removeListener(channel, listener)
|
},
|
||||||
},
|
ipcRendererCountListeners: (channel: string): number => {
|
||||||
showFilePath (file: File) {
|
return ipcRenderer.listenerCount(channel)
|
||||||
return webUtils.getPathForFile(file)
|
},
|
||||||
}
|
ipcRendererRemoveAllListeners: (channel: string) => {
|
||||||
})
|
ipcRenderer.removeAllListeners(channel)
|
||||||
|
},
|
||||||
contextBridge.exposeInMainWorld('node', {
|
showFilePath (file: File) {
|
||||||
path: {
|
return webUtils.getPathForFile(file)
|
||||||
join: path.join,
|
}
|
||||||
dirname: path.dirname,
|
})
|
||||||
basename: path.basename,
|
|
||||||
normalize: path.normalize,
|
contextBridge.exposeInMainWorld('node', {
|
||||||
extname: path.extname,
|
path: {
|
||||||
sep: path.sep,
|
join: path.join,
|
||||||
posix: {
|
dirname: path.dirname,
|
||||||
sep: path.posix.sep
|
basename: path.basename,
|
||||||
}
|
normalize: path.normalize,
|
||||||
},
|
extname: path.extname,
|
||||||
fs: {
|
sep: path.sep,
|
||||||
remove: fs.remove,
|
posix: {
|
||||||
readFile: fs.readFile,
|
sep: path.posix.sep
|
||||||
statSync: fs.statSync
|
}
|
||||||
},
|
},
|
||||||
crypto: {
|
fs: {
|
||||||
randomBytes: crypto.randomBytes,
|
remove: fs.remove,
|
||||||
createHash: crypto.createHash
|
readFile: fs.readFile,
|
||||||
},
|
statSync: fs.statSync
|
||||||
yaml: {
|
},
|
||||||
load: yaml.load
|
crypto: {
|
||||||
},
|
randomBytes: crypto.randomBytes,
|
||||||
mime: {
|
createHash: crypto.createHash
|
||||||
lookup: mime.lookup
|
},
|
||||||
}
|
yaml: {
|
||||||
})
|
load: yaml.load
|
||||||
} catch (error) {
|
},
|
||||||
console.error(error)
|
mime: {
|
||||||
}
|
lookup: mime.lookup
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,7 +50,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { IpcRendererEvent } from 'electron'
|
|
||||||
import { X } from 'lucide-vue-next'
|
import { X } from 'lucide-vue-next'
|
||||||
import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue'
|
import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
@@ -67,12 +66,9 @@ const inputBoxOptions = reactive({
|
|||||||
placeholder: ''
|
placeholder: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeMount(() => {
|
let removeInputBoxListenerCallback: (() => void) = () => {}
|
||||||
window.electron.ipcRendererOn(SHOW_INPUT_BOX, ipcEventHandler)
|
|
||||||
$bus.on(SHOW_INPUT_BOX, initInputBoxValue)
|
|
||||||
})
|
|
||||||
|
|
||||||
function ipcEventHandler (_: IpcRendererEvent, options: IShowInputBoxOption) {
|
function handleIpcInputBoxEvent (options: IShowInputBoxOption) {
|
||||||
initInputBoxValue(options)
|
initInputBoxValue(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,16 +92,23 @@ function handleInputBoxConfirm () {
|
|||||||
$bus.emit(SHOW_INPUT_BOX_RESPONSE, inputBoxValue.value)
|
$bus.emit(SHOW_INPUT_BOX_RESPONSE, inputBoxValue.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
removeInputBoxListenerCallback = window.electron.ipcRendererOn(SHOW_INPUT_BOX, handleIpcInputBoxEvent)
|
||||||
|
$bus.on(SHOW_INPUT_BOX, initInputBoxValue)
|
||||||
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.electron.ipcRendererRemoveListener(SHOW_INPUT_BOX, ipcEventHandler)
|
removeInputBoxListenerCallback()
|
||||||
$bus.off(SHOW_INPUT_BOX)
|
$bus.off(SHOW_INPUT_BOX)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'InputBoxDialog'
|
name: 'InputBoxDialog'
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.inputbox-overlay {
|
.inputbox-overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
<nav class="navigation">
|
<nav class="navigation">
|
||||||
<div class="title-bar">
|
<div class="title-bar">
|
||||||
<div class="app-title">
|
<div class="app-title">
|
||||||
<div class="app-text">
|
<div
|
||||||
|
class="app-text"
|
||||||
|
@click="openGithubPage"
|
||||||
|
>
|
||||||
{{ t('app.title') }}
|
{{ t('app.title') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="app-version">
|
<div class="app-version">
|
||||||
@@ -79,18 +82,6 @@
|
|||||||
class="qr-dialog"
|
class="qr-dialog"
|
||||||
@close="qrcodeVisible = false"
|
@close="qrcodeVisible = false"
|
||||||
>
|
>
|
||||||
<TransitionChild
|
|
||||||
as="template"
|
|
||||||
enter="duration-300 ease-out"
|
|
||||||
enter-from="opacity-0"
|
|
||||||
enter-to="opacity-100"
|
|
||||||
leave="duration-200 ease-in"
|
|
||||||
leave-from="opacity-100"
|
|
||||||
leave-to="opacity-0"
|
|
||||||
>
|
|
||||||
<div class="dialog-overlay" />
|
|
||||||
</TransitionChild>
|
|
||||||
|
|
||||||
<div class="dialog-container">
|
<div class="dialog-container">
|
||||||
<TransitionChild
|
<TransitionChild
|
||||||
as="template"
|
as="template"
|
||||||
@@ -217,7 +208,7 @@ import { pick } from 'lodash-es'
|
|||||||
import { CheckIcon, ChevronDownIcon, CopyIcon, DatabaseIcon, FolderIcon, Info, PieChartIcon, PlugIcon, Settings, UploadIcon } from 'lucide-vue-next'
|
import { CheckIcon, ChevronDownIcon, CopyIcon, DatabaseIcon, FolderIcon, Info, PieChartIcon, PlugIcon, Settings, UploadIcon } from 'lucide-vue-next'
|
||||||
import QrcodeVue from 'qrcode.vue'
|
import QrcodeVue from 'qrcode.vue'
|
||||||
import pkg from 'root/package.json'
|
import pkg from 'root/package.json'
|
||||||
import { computed, nextTick, onBeforeMount, reactive, Ref, ref, watch } from 'vue'
|
import { computed, nextTick, onBeforeMount, onBeforeUnmount, reactive, Ref, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import useMessage from '@/hooks/useMessage'
|
import useMessage from '@/hooks/useMessage'
|
||||||
@@ -237,6 +228,8 @@ const qrcodeVisible = ref(false)
|
|||||||
const choosedPicBedForQRCode: Ref<string[]> = ref([])
|
const choosedPicBedForQRCode: Ref<string[]> = ref([])
|
||||||
const picBedConfigString = ref('')
|
const picBedConfigString = ref('')
|
||||||
|
|
||||||
|
let removeIpcListener: () => void = () => {}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => choosedPicBedForQRCode,
|
() => choosedPicBedForQRCode,
|
||||||
val => {
|
val => {
|
||||||
@@ -280,9 +273,17 @@ const navigationItems = computed(() => [
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
function openGithubPage () {
|
||||||
|
window.electron.sendRPC(IRPCActionType.OPEN_URL, 'https://github.com/Kuingsmile/PicList')
|
||||||
|
}
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
updatePicBedGlobal()
|
updatePicBedGlobal()
|
||||||
window.electron.ipcRendererOn(SHOW_MAIN_PAGE_QRCODE, qrCodeHandler)
|
removeIpcListener = window.electron.ipcRendererOn(SHOW_MAIN_PAGE_QRCODE, qrCodeHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
removeIpcListener()
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -333,6 +334,11 @@ onBeforeMount(() => {
|
|||||||
letter-spacing: -0.025em;
|
letter-spacing: -0.025em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-text:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--color-blue-common);
|
||||||
|
}
|
||||||
|
|
||||||
.app-version {
|
.app-version {
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -536,16 +542,11 @@ onBeforeMount(() => {
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
overflow-y: auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog-overlay {
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-container {
|
.dialog-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
@@ -561,7 +562,7 @@ onBeforeMount(() => {
|
|||||||
.dialog-panel {
|
.dialog-panel {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
background: var(--color-surface);
|
background: var(--color-background-primary);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
box-shadow: var(--shadow-md);
|
box-shadow: var(--shadow-md);
|
||||||
@@ -634,11 +635,11 @@ onBeforeMount(() => {
|
|||||||
right: 0;
|
right: 0;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
background: var(--color-surface);
|
background: var(--color-background-primary);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
box-shadow: var(--shadow-md);
|
box-shadow: var(--shadow-md);
|
||||||
max-height: 200px;
|
max-height: 300px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { IpcRendererEvent } from 'electron'
|
|
||||||
import { MinusIcon, PinIcon, ShrinkIcon, XIcon } from 'lucide-vue-next'
|
import { MinusIcon, PinIcon, ShrinkIcon, XIcon } from 'lucide-vue-next'
|
||||||
import { onBeforeMount, onBeforeUnmount, ref } from 'vue'
|
import { onBeforeMount, onBeforeUnmount, ref } from 'vue'
|
||||||
|
|
||||||
@@ -100,7 +99,8 @@ function openMiniWindow () {
|
|||||||
function closeWindow () {
|
function closeWindow () {
|
||||||
window.electron.sendRPC(IRPCActionType.CLOSE_WINDOW)
|
window.electron.sendRPC(IRPCActionType.CLOSE_WINDOW)
|
||||||
}
|
}
|
||||||
const uploadProcessHandler = (_event: IpcRendererEvent, data: { progress: number }) => {
|
|
||||||
|
const uploadProcessHandler = (data: { progress: number }) => {
|
||||||
isShowprogress.value = data.progress !== 100 && data.progress !== 0
|
isShowprogress.value = data.progress !== 100 && data.progress !== 0
|
||||||
progress.value = data.progress
|
progress.value = data.progress
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ onBeforeMount(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.electron.ipcRendererRemoveListener('updateProgress', uploadProcessHandler)
|
window.electron.ipcRendererRemoveAllListeners('updateProgress')
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,243 +1,244 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="mini-page">
|
<div id="mini-page">
|
||||||
<div
|
<div
|
||||||
id="upload-area"
|
id="upload-area"
|
||||||
:class="{
|
:class="{
|
||||||
'is-dragover': dragover,
|
'is-dragover': dragover,
|
||||||
uploading: isShowingProgress,
|
uploading: isShowingProgress,
|
||||||
linux: osGlobal === 'linux'
|
linux: osGlobal === 'linux'
|
||||||
}"
|
}"
|
||||||
:style="{ backgroundPosition: '0 ' + progress + '%' }"
|
:style="{ backgroundPosition: '0 ' + progress + '%' }"
|
||||||
@drop.prevent="onDrop"
|
@drop.prevent="onDrop"
|
||||||
@dragover.prevent="dragover = true"
|
@dragover.prevent="dragover = true"
|
||||||
@dragleave.prevent="dragover = false"
|
@dragleave.prevent="dragover = false"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
v-if="!dragover && !isShowingProgress"
|
v-if="!dragover && !isShowingProgress"
|
||||||
:src="logoPath ? logoPath : '/squareLogo.png'"
|
:src="logoPath ? logoPath : '/squareLogo.png'"
|
||||||
style="width: 100%; height: 100%; border-radius: 50%"
|
style="width: 100%; height: 100%; border-radius: 50%"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
@dragstart.prevent
|
@dragstart.prevent
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
id="upload-dragger"
|
id="upload-dragger"
|
||||||
@dblclick="openUploadWindow"
|
@dblclick="openUploadWindow"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
id="file-uploader"
|
id="file-uploader"
|
||||||
type="file"
|
type="file"
|
||||||
multiple
|
multiple
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { IpcRendererEvent } from 'electron'
|
import type { IConfig } from 'piclist'
|
||||||
import type { IConfig } from 'piclist'
|
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
|
||||||
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
|
|
||||||
|
import { isUrl } from '@/utils/common'
|
||||||
import { isUrl } from '@/utils/common'
|
import { getConfig } from '@/utils/dataSender'
|
||||||
import { getConfig } from '@/utils/dataSender'
|
import { IRPCActionType } from '@/utils/enum'
|
||||||
import { IRPCActionType } from '@/utils/enum'
|
import { osGlobal } from '@/utils/global'
|
||||||
import { osGlobal } from '@/utils/global'
|
import type { IFileWithPath } from '#/types/types'
|
||||||
import type { IFileWithPath } from '#/types/types'
|
|
||||||
|
const logoPath = ref('')
|
||||||
const logoPath = ref('')
|
const dragover = ref(false)
|
||||||
const dragover = ref(false)
|
const progress = ref(0)
|
||||||
const progress = ref(0)
|
const isShowingProgress = ref(false)
|
||||||
const isShowingProgress = ref(false)
|
const draggingState = ref(false)
|
||||||
const draggingState = ref(false)
|
const wX = ref(-1)
|
||||||
const wX = ref(-1)
|
const wY = ref(-1)
|
||||||
const wY = ref(-1)
|
const screenX = ref(-1)
|
||||||
const screenX = ref(-1)
|
const screenY = ref(-1)
|
||||||
const screenY = ref(-1)
|
|
||||||
|
let removeListeners: () => void = () => {}
|
||||||
async function initLogoPath () {
|
|
||||||
const config = await getConfig<IConfig>()
|
async function initLogoPath () {
|
||||||
if (config) {
|
const config = await getConfig<IConfig>()
|
||||||
if (config.settings?.isCustomMiniIcon && config.settings?.customMiniIcon) {
|
if (config) {
|
||||||
logoPath.value =
|
if (config.settings?.isCustomMiniIcon && config.settings?.customMiniIcon) {
|
||||||
'data:image/jpg;base64,' +
|
logoPath.value =
|
||||||
(await window.electron.triggerRPC(IRPCActionType.MANAGE_CONVERT_PATH_TO_BASE64, config.settings.customMiniIcon))
|
'data:image/jpg;base64,' +
|
||||||
}
|
(await window.electron.triggerRPC(IRPCActionType.MANAGE_CONVERT_PATH_TO_BASE64, config.settings.customMiniIcon))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const uploadProgressHandler = (_: IpcRendererEvent, _progress: number) => {
|
|
||||||
if (_progress !== -1) {
|
const uploadProgressHandler = (p: number) => {
|
||||||
isShowingProgress.value = true
|
if (p !== -1) {
|
||||||
progress.value = _progress
|
isShowingProgress.value = true
|
||||||
} else {
|
progress.value = p
|
||||||
progress.value = 100
|
} else {
|
||||||
}
|
progress.value = 100
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const updateMiniIconHandler = async () => {
|
|
||||||
await initLogoPath()
|
const updateMiniIconHandler = async () => {
|
||||||
}
|
await initLogoPath()
|
||||||
|
}
|
||||||
onBeforeMount(async () => {
|
|
||||||
await initLogoPath()
|
watch(progress, val => {
|
||||||
window.electron.ipcRendererOn('uploadProgress', uploadProgressHandler)
|
if (val === 100) {
|
||||||
window.electron.ipcRendererOn('updateMiniIcon', updateMiniIconHandler)
|
setTimeout(() => {
|
||||||
window.addEventListener('mousedown', handleMouseDown, false)
|
isShowingProgress.value = false
|
||||||
window.addEventListener('mousemove', handleMouseMove, false)
|
}, 1000)
|
||||||
window.addEventListener('mouseup', handleMouseUp, false)
|
setTimeout(() => {
|
||||||
})
|
progress.value = 0
|
||||||
|
}, 1200)
|
||||||
watch(progress, val => {
|
}
|
||||||
if (val === 100) {
|
})
|
||||||
setTimeout(() => {
|
|
||||||
isShowingProgress.value = false
|
function onDrop (e: DragEvent) {
|
||||||
}, 1000)
|
dragover.value = false
|
||||||
setTimeout(() => {
|
|
||||||
progress.value = 0
|
// send files first
|
||||||
}, 1200)
|
if (e.dataTransfer?.files?.length) {
|
||||||
}
|
ipcSendFiles(e.dataTransfer.files)
|
||||||
})
|
} else if (e.dataTransfer?.items) {
|
||||||
|
const items = e.dataTransfer.items
|
||||||
function onDrop (e: DragEvent) {
|
if (items.length === 2 && items[0].type === 'text/uri-list') {
|
||||||
dragover.value = false
|
handleURLDrag(items, e.dataTransfer)
|
||||||
|
} else if (items[0].type === 'text/plain') {
|
||||||
// send files first
|
const str = e.dataTransfer!.getData(items[0].type)
|
||||||
if (e.dataTransfer?.files?.length) {
|
if (isUrl(str)) {
|
||||||
ipcSendFiles(e.dataTransfer.files)
|
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [{ path: str }])
|
||||||
} else if (e.dataTransfer?.items) {
|
}
|
||||||
const items = e.dataTransfer.items
|
}
|
||||||
if (items.length === 2 && items[0].type === 'text/uri-list') {
|
}
|
||||||
handleURLDrag(items, e.dataTransfer)
|
}
|
||||||
} else if (items[0].type === 'text/plain') {
|
|
||||||
const str = e.dataTransfer!.getData(items[0].type)
|
function handleURLDrag (items: DataTransferItemList, dataTransfer: DataTransfer) {
|
||||||
if (isUrl(str)) {
|
// text/html
|
||||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [{ path: str }])
|
// Use this data to get a more precise URL
|
||||||
}
|
const urlString = dataTransfer.getData(items[1].type)
|
||||||
}
|
const urlMatch = urlString.match(/<img.*src="(.*?)"/)
|
||||||
}
|
if (urlMatch) {
|
||||||
}
|
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [
|
||||||
|
{
|
||||||
function handleURLDrag (items: DataTransferItemList, dataTransfer: DataTransfer) {
|
path: urlMatch[1]
|
||||||
// text/html
|
}
|
||||||
// Use this data to get a more precise URL
|
])
|
||||||
const urlString = dataTransfer.getData(items[1].type)
|
}
|
||||||
const urlMatch = urlString.match(/<img.*src="(.*?)"/)
|
}
|
||||||
if (urlMatch) {
|
|
||||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [
|
function openUploadWindow () {
|
||||||
{
|
// @ts-expect-error file-uploader
|
||||||
path: urlMatch[1]
|
document.getElementById('file-uploader').click()
|
||||||
}
|
}
|
||||||
])
|
|
||||||
}
|
function onChange (e: any) {
|
||||||
}
|
ipcSendFiles(e.target.files)
|
||||||
|
// @ts-expect-error file-uploader
|
||||||
function openUploadWindow () {
|
document.getElementById('file-uploader').value = ''
|
||||||
// @ts-expect-error file-uploader
|
}
|
||||||
document.getElementById('file-uploader').click()
|
|
||||||
}
|
function ipcSendFiles (files: FileList) {
|
||||||
|
const sendFiles: IFileWithPath[] = []
|
||||||
function onChange (e: any) {
|
Array.from(files).forEach(item => {
|
||||||
ipcSendFiles(e.target.files)
|
const obj = {
|
||||||
// @ts-expect-error file-uploader
|
name: item.name,
|
||||||
document.getElementById('file-uploader').value = ''
|
path: window.electron.showFilePath(item)
|
||||||
}
|
}
|
||||||
|
sendFiles.push(obj)
|
||||||
function ipcSendFiles (files: FileList) {
|
})
|
||||||
const sendFiles: IFileWithPath[] = []
|
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, sendFiles)
|
||||||
Array.from(files).forEach(item => {
|
}
|
||||||
const obj = {
|
|
||||||
name: item.name,
|
function handleMouseDown (e: MouseEvent) {
|
||||||
path: window.electron.showFilePath(item)
|
draggingState.value = true
|
||||||
}
|
wX.value = e.pageX
|
||||||
sendFiles.push(obj)
|
wY.value = e.pageY
|
||||||
})
|
screenX.value = e.screenX
|
||||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, sendFiles)
|
screenY.value = e.screenY
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMouseDown (e: MouseEvent) {
|
function handleMouseMove (e: MouseEvent) {
|
||||||
draggingState.value = true
|
e.preventDefault()
|
||||||
wX.value = e.pageX
|
e.stopPropagation()
|
||||||
wY.value = e.pageY
|
if (draggingState.value) {
|
||||||
screenX.value = e.screenX
|
const xLoc = e.screenX - wX.value
|
||||||
screenY.value = e.screenY
|
const yLoc = e.screenY - wY.value
|
||||||
}
|
window.electron.sendRPC(IRPCActionType.SET_MINI_WINDOW_POS, {
|
||||||
|
x: xLoc,
|
||||||
function handleMouseMove (e: MouseEvent) {
|
y: yLoc,
|
||||||
e.preventDefault()
|
width: 64,
|
||||||
e.stopPropagation()
|
height: 64
|
||||||
if (draggingState.value) {
|
})
|
||||||
const xLoc = e.screenX - wX.value
|
}
|
||||||
const yLoc = e.screenY - wY.value
|
}
|
||||||
window.electron.sendRPC(IRPCActionType.SET_MINI_WINDOW_POS, {
|
|
||||||
x: xLoc,
|
function handleMouseUp (e: MouseEvent) {
|
||||||
y: yLoc,
|
draggingState.value = false
|
||||||
width: 64,
|
if (screenX.value === e.screenX && screenY.value === e.screenY) {
|
||||||
height: 64
|
if (e.button === 0) {
|
||||||
})
|
// left mouse
|
||||||
}
|
openUploadWindow()
|
||||||
}
|
} else {
|
||||||
|
openContextMenu()
|
||||||
function handleMouseUp (e: MouseEvent) {
|
}
|
||||||
draggingState.value = false
|
}
|
||||||
if (screenX.value === e.screenX && screenY.value === e.screenY) {
|
}
|
||||||
if (e.button === 0) {
|
|
||||||
// left mouse
|
function openContextMenu () {
|
||||||
openUploadWindow()
|
window.electron.sendRPC(IRPCActionType.SHOW_MINI_PAGE_MENU)
|
||||||
} else {
|
}
|
||||||
openContextMenu()
|
|
||||||
}
|
onBeforeMount(async () => {
|
||||||
}
|
await initLogoPath()
|
||||||
}
|
removeListeners = window.electron.ipcRendererOn('uploadProgress', uploadProgressHandler)
|
||||||
|
window.electron.ipcRendererOn('updateMiniIcon', updateMiniIconHandler)
|
||||||
function openContextMenu () {
|
window.addEventListener('mousedown', handleMouseDown, false)
|
||||||
window.electron.sendRPC(IRPCActionType.SHOW_MINI_PAGE_MENU)
|
window.addEventListener('mousemove', handleMouseMove, false)
|
||||||
}
|
window.addEventListener('mouseup', handleMouseUp, false)
|
||||||
|
})
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.electron.ipcRendererRemoveListener('uploadProgress', uploadProgressHandler)
|
onBeforeUnmount(() => {
|
||||||
window.electron.ipcRendererRemoveListener('updateMiniIcon', updateMiniIconHandler)
|
removeListeners()
|
||||||
window.removeEventListener('mousedown', handleMouseDown, false)
|
window.electron.ipcRendererRemoveAllListeners('updateMiniIcon')
|
||||||
window.removeEventListener('mousemove', handleMouseMove, false)
|
window.removeEventListener('mousedown', handleMouseDown, false)
|
||||||
window.removeEventListener('mouseup', handleMouseUp, false)
|
window.removeEventListener('mousemove', handleMouseMove, false)
|
||||||
})
|
window.removeEventListener('mouseup', handleMouseUp, false)
|
||||||
</script>
|
})
|
||||||
<script lang="ts">
|
</script>
|
||||||
export default {
|
<script lang="ts">
|
||||||
name: 'MiniPage'
|
export default {
|
||||||
}
|
name: 'MiniPage'
|
||||||
</script>
|
}
|
||||||
<style lang="stylus">
|
</script>
|
||||||
#mini-page
|
<style lang="stylus">
|
||||||
color #FFF
|
#mini-page
|
||||||
height 100vh
|
color #FFF
|
||||||
width 100vw
|
height 100vh
|
||||||
border-radius 50%
|
width 100vw
|
||||||
text-align center
|
border-radius 50%
|
||||||
line-height 100vh
|
text-align center
|
||||||
font-size 40px
|
line-height 100vh
|
||||||
background-size 90vh 90vw
|
font-size 40px
|
||||||
background-position center center
|
background-size 90vh 90vw
|
||||||
background-repeat no-repeat
|
background-position center center
|
||||||
position relative
|
background-repeat no-repeat
|
||||||
box-sizing border-box
|
position relative
|
||||||
cursor pointer
|
box-sizing border-box
|
||||||
&.linux
|
cursor pointer
|
||||||
border-radius 0
|
&.linux
|
||||||
background-size 100vh 100vw
|
border-radius 0
|
||||||
#upload-area
|
background-size 100vh 100vw
|
||||||
height 100%
|
#upload-area
|
||||||
width 100%
|
height 100%
|
||||||
border-radius 50%
|
width 100%
|
||||||
&.linux
|
border-radius 50%
|
||||||
border-radius 0
|
&.linux
|
||||||
&.uploading
|
border-radius 0
|
||||||
background: linear-gradient(to top, #409EFF 50%, #fff 51%)
|
&.uploading
|
||||||
background-size 200%
|
background: linear-gradient(to top, #409EFF 50%, #fff 51%)
|
||||||
#upload-dragger
|
background-size 200%
|
||||||
height 100%
|
#upload-dragger
|
||||||
&.is-dragover
|
height 100%
|
||||||
background rgba(0,0,0,0.3)
|
&.is-dragover
|
||||||
#file-uploader
|
background rgba(0,0,0,0.3)
|
||||||
display none
|
#file-uploader
|
||||||
</style>
|
display none
|
||||||
|
</style>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,369 +1,369 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="rename-container">
|
<div class="rename-container">
|
||||||
<div class="rename-card">
|
<div class="rename-card">
|
||||||
<form @submit.prevent="confirmName">
|
<form @submit.prevent="confirmName">
|
||||||
<div class="form-content">
|
<div class="form-content">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-wrapper">
|
<div class="input-wrapper">
|
||||||
<input
|
<input
|
||||||
ref="fileNameInput"
|
ref="fileNameInput"
|
||||||
v-model="form.fileName"
|
v-model="form.fileName"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
:class="{ 'input-error': validationError }"
|
:class="{ 'input-error': validationError }"
|
||||||
:placeholder="t('pages.rename.placeholder')"
|
:placeholder="t('pages.rename.placeholder')"
|
||||||
autofocus
|
autofocus
|
||||||
@keyup.enter="confirmName"
|
@keyup.enter="confirmName"
|
||||||
@input="clearValidationError"
|
@input="clearValidationError"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
v-if="form.fileName"
|
v-if="form.fileName"
|
||||||
type="button"
|
type="button"
|
||||||
class="input-clear"
|
class="input-clear"
|
||||||
@click="clearFileName"
|
@click="clearFileName"
|
||||||
>
|
>
|
||||||
<XIcon :size="16" />
|
<XIcon :size="16" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="validationError"
|
v-if="validationError"
|
||||||
class="validation-error"
|
class="validation-error"
|
||||||
>
|
>
|
||||||
{{ validationError }}
|
{{ validationError }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
@click="cancel"
|
@click="cancel"
|
||||||
>
|
>
|
||||||
{{ $t('common.cancel') }}
|
{{ $t('common.cancel') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
:disabled="!form.fileName.trim()"
|
:disabled="!form.fileName.trim()"
|
||||||
>
|
>
|
||||||
{{ $t('common.confirm') }}
|
{{ $t('common.confirm') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { IpcRendererEvent } from 'electron'
|
import { XIcon } from 'lucide-vue-next'
|
||||||
import { XIcon } from 'lucide-vue-next'
|
import { nextTick, onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue'
|
||||||
import { nextTick, onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
|
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME } from '@/utils/constant'
|
||||||
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME } from '@/utils/constant'
|
|
||||||
|
const { t } = useI18n()
|
||||||
const { t } = useI18n()
|
const id = ref<string | null>(null)
|
||||||
const id = ref<string | null>(null)
|
const fileNameInput = ref<HTMLInputElement>()
|
||||||
const fileNameInput = ref<HTMLInputElement>()
|
const validationError = ref<string>('')
|
||||||
const validationError = ref<string>('')
|
|
||||||
|
const form = reactive({
|
||||||
const form = reactive({
|
fileName: '',
|
||||||
fileName: '',
|
originName: ''
|
||||||
originName: ''
|
})
|
||||||
})
|
|
||||||
|
const handleFileName = (newName: string, _originName: string, _id: string) => {
|
||||||
const handleFileName = (_: IpcRendererEvent, newName: string, _originName: string, _id: string) => {
|
form.fileName = newName
|
||||||
form.fileName = newName
|
form.originName = _originName
|
||||||
form.originName = _originName
|
id.value = _id
|
||||||
id.value = _id
|
nextTick(() => {
|
||||||
nextTick(() => {
|
fileNameInput.value?.focus()
|
||||||
fileNameInput.value?.focus()
|
fileNameInput.value?.select()
|
||||||
fileNameInput.value?.select()
|
})
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
window.electron.ipcRendererOn(RENAME_FILE_NAME, handleFileName)
|
||||||
window.electron.ipcRendererOn(RENAME_FILE_NAME, handleFileName)
|
|
||||||
|
function validateFileName (fileName: string): string {
|
||||||
onBeforeMount(() => {
|
if (!fileName.trim()) {
|
||||||
window.electron.sendToMain(GET_RENAME_FILE_NAME, '')
|
return 'File name is required'
|
||||||
})
|
}
|
||||||
|
const invalidChars = /[<>:"/\\|?*]/g
|
||||||
function validateFileName (fileName: string): string {
|
if (invalidChars.test(fileName)) {
|
||||||
if (!fileName.trim()) {
|
return 'File name contains invalid characters'
|
||||||
return 'File name is required'
|
}
|
||||||
}
|
const reservedNames = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i
|
||||||
const invalidChars = /[<>:"/\\|?*]/g
|
if (reservedNames.test(fileName.trim())) {
|
||||||
if (invalidChars.test(fileName)) {
|
return 'This is a reserved file name'
|
||||||
return 'File name contains invalid characters'
|
}
|
||||||
}
|
return ''
|
||||||
const reservedNames = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i
|
}
|
||||||
if (reservedNames.test(fileName.trim())) {
|
|
||||||
return 'This is a reserved file name'
|
function confirmName () {
|
||||||
}
|
const error = validateFileName(form.fileName)
|
||||||
return ''
|
if (error) {
|
||||||
}
|
validationError.value = error
|
||||||
|
return
|
||||||
function confirmName () {
|
}
|
||||||
const error = validateFileName(form.fileName)
|
|
||||||
if (error) {
|
window.electron.sendToMain(`${RENAME_FILE_NAME}${id.value}`, form.fileName)
|
||||||
validationError.value = error
|
}
|
||||||
return
|
|
||||||
}
|
function cancel () {
|
||||||
|
window.electron.sendToMain(`${RENAME_FILE_NAME}${id.value}`, form.originName)
|
||||||
window.electron.sendToMain(`${RENAME_FILE_NAME}${id.value}`, form.fileName)
|
}
|
||||||
}
|
|
||||||
|
function clearFileName () {
|
||||||
function cancel () {
|
form.fileName = ''
|
||||||
window.electron.sendToMain(`${RENAME_FILE_NAME}${id.value}`, form.originName)
|
validationError.value = ''
|
||||||
}
|
nextTick(() => {
|
||||||
|
fileNameInput.value?.focus()
|
||||||
function clearFileName () {
|
})
|
||||||
form.fileName = ''
|
}
|
||||||
validationError.value = ''
|
|
||||||
nextTick(() => {
|
function clearValidationError () {
|
||||||
fileNameInput.value?.focus()
|
if (validationError.value) {
|
||||||
})
|
validationError.value = ''
|
||||||
}
|
}
|
||||||
|
}
|
||||||
function clearValidationError () {
|
|
||||||
if (validationError.value) {
|
onBeforeMount(() => {
|
||||||
validationError.value = ''
|
window.electron.sendToMain(GET_RENAME_FILE_NAME, '')
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
onBeforeUnmount(() => {
|
window.electron.ipcRendererRemoveAllListeners(RENAME_FILE_NAME)
|
||||||
window.electron.ipcRendererRemoveListener(RENAME_FILE_NAME, handleFileName)
|
})
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'RenamePage'
|
name: 'RenamePage'
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.rename-container {
|
.rename-container {
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: var(--color-background-secondary);
|
background: var(--color-background-secondary);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rename-card {
|
.rename-card {
|
||||||
background: var(--color-background-primary);
|
background: var(--color-background-primary);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Form */
|
/* Form */
|
||||||
.form-content {
|
.form-content {
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group:last-child {
|
.form-group:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-label {
|
.form-label {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-wrapper {
|
.input-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input {
|
.form-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.875rem 1rem;
|
padding: 0.875rem 1rem;
|
||||||
padding-right: 2.5rem;
|
padding-right: 2.5rem;
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: var(--color-background-primary);
|
background: var(--color-background-primary);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input:focus {
|
.form-input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--color-blue-common);
|
border-color: var(--color-blue-common);
|
||||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input.input-error {
|
.form-input.input-error {
|
||||||
border-color: #f56c6c;
|
border-color: #f56c6c;
|
||||||
box-shadow: 0 0 0 2px rgba(245, 108, 108, 0.2);
|
box-shadow: 0 0 0 2px rgba(245, 108, 108, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-clear {
|
.input-clear {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0.75rem;
|
right: 0.75rem;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: none;
|
border: none;
|
||||||
background: var(--color-background-tertiary);
|
background: var(--color-background-tertiary);
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-clear:hover {
|
.input-clear:hover {
|
||||||
background: var(--color-background-secondary);
|
background: var(--color-background-secondary);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.validation-error {
|
.validation-error {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
color: #f56c6c;
|
color: #f56c6c;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actions */
|
/* Actions */
|
||||||
.form-actions {
|
.form-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
padding: 1rem 2rem 2rem;
|
padding: 1rem 2rem 2rem;
|
||||||
background: var(--color-background-tertiary);
|
background: var(--color-background-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Buttons */
|
/* Buttons */
|
||||||
.btn {
|
.btn {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
padding: 0.75rem 1.5rem;
|
padding: 0.75rem 1.5rem;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: none;
|
border: none;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
min-width: fit-content;
|
min-width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:disabled {
|
.btn:disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:hover:not(:disabled) {
|
.btn:hover:not(:disabled) {
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
background: #409eff;
|
background: #409eff;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary:hover:not(:disabled) {
|
.btn-primary:hover:not(:disabled) {
|
||||||
background: #66b1ff;
|
background: #66b1ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
background: var(--color-background-primary);
|
background: var(--color-background-primary);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary:hover:not(:disabled) {
|
.btn-secondary:hover:not(:disabled) {
|
||||||
background: var(--color-background-secondary);
|
background: var(--color-background-secondary);
|
||||||
border-color: var(--color-accent);
|
border-color: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive Design */
|
/* Responsive Design */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.rename-container {
|
.rename-container {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rename-card {
|
.rename-card {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-actions {
|
.form-actions {
|
||||||
padding: 1rem 1.5rem 1.5rem;
|
padding: 1rem 1.5rem 1.5rem;
|
||||||
flex-direction: column-reverse;
|
flex-direction: column-reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.rename-container {
|
.rename-container {
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-actions {
|
.form-actions {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Focus styles for accessibility */
|
/* Focus styles for accessibility */
|
||||||
.btn:focus-visible,
|
.btn:focus-visible,
|
||||||
.input-clear:focus-visible {
|
.input-clear:focus-visible {
|
||||||
outline: 2px solid var(--color-accent);
|
outline: 2px solid var(--color-accent);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input:focus-visible {
|
.form-input:focus-visible {
|
||||||
outline: 2px solid var(--color-accent);
|
outline: 2px solid var(--color-accent);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animation for error state */
|
/* Animation for error state */
|
||||||
@keyframes shake {
|
@keyframes shake {
|
||||||
0%, 100% {
|
0%, 100% {
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
}
|
}
|
||||||
25% {
|
25% {
|
||||||
transform: translateX(-4px);
|
transform: translateX(-4px);
|
||||||
}
|
}
|
||||||
75% {
|
75% {
|
||||||
transform: translateX(4px);
|
transform: translateX(4px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-error {
|
.input-error {
|
||||||
animation: shake 0.3s ease-in-out;
|
animation: shake 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark mode adjustments */
|
/* Dark mode adjustments */
|
||||||
:root.dark .rename-card,
|
:root.dark .rename-card,
|
||||||
:root.auto.dark .rename-card {
|
:root.auto.dark .rename-card {
|
||||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.3), 0 10px 10px -5px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.3), 0 10px 10px -5px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,284 +1,284 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="toolbox-container">
|
<div class="toolbox-container">
|
||||||
<!-- Header Card -->
|
<!-- Header Card -->
|
||||||
<div class="toolbox-card header-card">
|
<div class="toolbox-card header-card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<div class="header-content">
|
<div class="header-content">
|
||||||
<img
|
<img
|
||||||
class="header-logo"
|
class="header-logo"
|
||||||
:src="defaultLogo"
|
:src="defaultLogo"
|
||||||
alt="Toolbox Logo"
|
alt="Toolbox Logo"
|
||||||
>
|
>
|
||||||
<div class="header-text">
|
<div class="header-text">
|
||||||
<h1 class="header-title">
|
<h1 class="header-title">
|
||||||
{{ t('pages.toolbox.title') }}
|
{{ t('pages.toolbox.title') }}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="header-subtitle">
|
<p class="header-subtitle">
|
||||||
{{ t('pages.toolbox.description') }}
|
{{ t('pages.toolbox.description') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
<template v-if="progress !== 100">
|
<template v-if="progress !== 100">
|
||||||
<button
|
<button
|
||||||
class="action-button"
|
class="action-button"
|
||||||
:class="{ disabled: isLoading }"
|
:class="{ disabled: isLoading }"
|
||||||
:disabled="isLoading"
|
:disabled="isLoading"
|
||||||
@click="handleCheck"
|
@click="handleCheck"
|
||||||
>
|
>
|
||||||
<span>{{ t('pages.toolbox.startScan') }}</span>
|
<span>{{ t('pages.toolbox.startScan') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="isAllSuccess">
|
<template v-else-if="isAllSuccess">
|
||||||
<div class="success-tips">
|
<div class="success-tips">
|
||||||
{{ t('pages.toolbox.success') }}
|
{{ t('pages.toolbox.success') }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="!isAllSuccess">
|
<template v-else-if="!isAllSuccess">
|
||||||
<template v-if="canFixLength !== 0">
|
<template v-if="canFixLength !== 0">
|
||||||
<button
|
<button
|
||||||
class="action-button"
|
class="action-button"
|
||||||
@click="handleFix"
|
@click="handleFix"
|
||||||
>
|
>
|
||||||
<span>{{ t('pages.toolbox.startFix') }}</span>
|
<span>{{ t('pages.toolbox.startFix') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="cant-fix-container">
|
<div class="cant-fix-container">
|
||||||
<span class="cant-fix-text">{{ $t('pages.toolbox.autoFixFail') }}</span>
|
<span class="cant-fix-text">{{ $t('pages.toolbox.autoFixFail') }}</span>
|
||||||
<button
|
<button
|
||||||
class="action-button secondary small"
|
class="action-button secondary small"
|
||||||
@click="handleCheck"
|
@click="handleCheck"
|
||||||
>
|
>
|
||||||
<span>{{ t('pages.toolbox.reScan') }}</span>
|
<span>{{ t('pages.toolbox.reScan') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Progress Card -->
|
<!-- Progress Card -->
|
||||||
<div class="toolbox-card progress-card">
|
<div class="toolbox-card progress-card">
|
||||||
<div class="progress-container">
|
<div class="progress-container">
|
||||||
<div class="progress-bar">
|
<div class="progress-bar">
|
||||||
<div
|
<div
|
||||||
class="progress-fill"
|
class="progress-fill"
|
||||||
:style="{ width: `${progress}%` }"
|
:style="{ width: `${progress}%` }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span class="progress-text">{{ Math.round(progress) }}%</span>
|
<span class="progress-text">{{ Math.round(progress) }}%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Items Card -->
|
<!-- Items Card -->
|
||||||
<div class="toolbox-card items-card">
|
<div class="toolbox-card items-card">
|
||||||
<div class="items-list">
|
<div class="items-list">
|
||||||
<div
|
<div
|
||||||
v-for="(item, key) in fixList"
|
v-for="(item, key) in fixList"
|
||||||
:key="key"
|
:key="key"
|
||||||
class="item"
|
class="item"
|
||||||
:class="{
|
:class="{
|
||||||
'item-active': activeTypes.includes(key),
|
'item-active': activeTypes.includes(key),
|
||||||
'item-error': item.status === IToolboxItemCheckStatus.ERROR,
|
'item-error': item.status === IToolboxItemCheckStatus.ERROR,
|
||||||
'item-success': item.status === IToolboxItemCheckStatus.SUCCESS,
|
'item-success': item.status === IToolboxItemCheckStatus.SUCCESS,
|
||||||
'item-loading': item.status === IToolboxItemCheckStatus.LOADING
|
'item-loading': item.status === IToolboxItemCheckStatus.LOADING
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="item-header"
|
class="item-header"
|
||||||
@click="toggleItem(key)"
|
@click="toggleItem(key)"
|
||||||
>
|
>
|
||||||
<div class="item-title">
|
<div class="item-title">
|
||||||
<span>{{ item.title }}</span>
|
<span>{{ item.title }}</span>
|
||||||
<toolbox-status-icon :status="item.status" />
|
<toolbox-status-icon :status="item.status" />
|
||||||
</div>
|
</div>
|
||||||
<div class="item-chevron">
|
<div class="item-chevron">
|
||||||
<svg
|
<svg
|
||||||
width="16"
|
width="16"
|
||||||
height="16"
|
height="16"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
>
|
>
|
||||||
<polyline points="6,9 12,15 18,9" />
|
<polyline points="6,9 12,15 18,9" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<transition name="item-content">
|
<transition name="item-content">
|
||||||
<div
|
<div
|
||||||
v-if="activeTypes.includes(key)"
|
v-if="activeTypes.includes(key)"
|
||||||
class="item-content"
|
class="item-content"
|
||||||
>
|
>
|
||||||
<div class="item-message">
|
<div class="item-message">
|
||||||
{{ item.msg || '' }}
|
{{ item.msg || '' }}
|
||||||
</div>
|
</div>
|
||||||
<template v-if="item.handler && item.handlerText && item.value">
|
<template v-if="item.handler && item.handlerText && item.value">
|
||||||
<div class="item-actions">
|
<div class="item-actions">
|
||||||
<toolbox-handler
|
<toolbox-handler
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
:status="item.status"
|
:status="item.status"
|
||||||
:handler="item.handler"
|
:handler="item.handler"
|
||||||
:handler-text="item.handlerText"
|
:handler-text="item.handlerText"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onUnmounted, reactive, ref } from 'vue'
|
import { computed, onUnmounted, reactive, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import ToolboxHandler from '@/components/ToolboxHandler.vue'
|
import ToolboxHandler from '@/components/ToolboxHandler.vue'
|
||||||
import ToolboxStatusIcon from '@/components/ToolboxStatusIcon.vue'
|
import ToolboxStatusIcon from '@/components/ToolboxStatusIcon.vue'
|
||||||
import useConfirm from '@/hooks/useConfirm'
|
import useConfirm from '@/hooks/useConfirm'
|
||||||
import { IRPCActionType, IToolboxItemCheckStatus, IToolboxItemType } from '@/utils/enum'
|
import { IRPCActionType, IToolboxItemCheckStatus, IToolboxItemType } from '@/utils/enum'
|
||||||
import type { IToolboxCheckRes } from '#/types/rpc'
|
import type { IToolboxCheckRes } from '#/types/rpc'
|
||||||
import type { IToolboxMap } from '#/types/view'
|
import type { IToolboxMap } from '#/types/view'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const { confirm } = useConfirm()
|
const { confirm } = useConfirm()
|
||||||
const defaultLogo = ref('/roundLogo.png')
|
const defaultLogo = ref('/roundLogo.png')
|
||||||
const activeTypes = ref<string[]>([])
|
const activeTypes = ref<string[]>([])
|
||||||
const fixList = reactive<IToolboxMap>({
|
const fixList = reactive<IToolboxMap>({
|
||||||
[IToolboxItemType.IS_CONFIG_FILE_BROKEN]: {
|
[IToolboxItemType.IS_CONFIG_FILE_BROKEN]: {
|
||||||
title: t('pages.toolbox.checkConfigFileBroken'),
|
title: t('pages.toolbox.checkConfigFileBroken'),
|
||||||
status: IToolboxItemCheckStatus.INIT,
|
status: IToolboxItemCheckStatus.INIT,
|
||||||
handlerText: t('pages.toolbox.openConfigFile'),
|
handlerText: t('pages.toolbox.openConfigFile'),
|
||||||
handler (value: string) {
|
handler (value: string) {
|
||||||
window.electron.sendRPC(IRPCActionType.OPEN_FILE, value)
|
window.electron.sendRPC(IRPCActionType.OPEN_FILE, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[IToolboxItemType.IS_GALLERY_FILE_BROKEN]: {
|
[IToolboxItemType.IS_GALLERY_FILE_BROKEN]: {
|
||||||
title: t('pages.toolbox.checkGalleryFileBroken'),
|
title: t('pages.toolbox.checkGalleryFileBroken'),
|
||||||
status: IToolboxItemCheckStatus.INIT
|
status: IToolboxItemCheckStatus.INIT
|
||||||
},
|
},
|
||||||
[IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD]: {
|
[IToolboxItemType.HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD]: {
|
||||||
title: t('pages.toolbox.checkProblemWithClipboardPicUpload'), // picgo-image-clipboard folder
|
title: t('pages.toolbox.checkProblemWithClipboardPicUpload'), // picgo-image-clipboard folder
|
||||||
status: IToolboxItemCheckStatus.INIT,
|
status: IToolboxItemCheckStatus.INIT,
|
||||||
handlerText: t('pages.toolbox.openFilePath'),
|
handlerText: t('pages.toolbox.openFilePath'),
|
||||||
handler (value: string) {
|
handler (value: string) {
|
||||||
window.electron.sendRPC(IRPCActionType.OPEN_FILE, value)
|
window.electron.sendRPC(IRPCActionType.OPEN_FILE, value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[IToolboxItemType.HAS_PROBLEM_WITH_PROXY]: {
|
[IToolboxItemType.HAS_PROBLEM_WITH_PROXY]: {
|
||||||
title: t('pages.toolbox.checkProblemWithProxy'),
|
title: t('pages.toolbox.checkProblemWithProxy'),
|
||||||
status: IToolboxItemCheckStatus.INIT,
|
status: IToolboxItemCheckStatus.INIT,
|
||||||
hasNoFixMethod: true
|
hasNoFixMethod: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const progress = computed(() => {
|
const progress = computed(() => {
|
||||||
const total = Object.keys(fixList).length
|
const total = Object.keys(fixList).length
|
||||||
const done = Object.keys(fixList).filter(key => {
|
const done = Object.keys(fixList).filter(key => {
|
||||||
const status = fixList[key].status
|
const status = fixList[key].status
|
||||||
return status !== IToolboxItemCheckStatus.INIT && status !== IToolboxItemCheckStatus.LOADING
|
return status !== IToolboxItemCheckStatus.INIT && status !== IToolboxItemCheckStatus.LOADING
|
||||||
}).length
|
}).length
|
||||||
return (done / total) * 100
|
return (done / total) * 100
|
||||||
})
|
})
|
||||||
|
|
||||||
const isAllSuccess = computed(() => {
|
const isAllSuccess = computed(() => {
|
||||||
return Object.keys(fixList).every(key => {
|
return Object.keys(fixList).every(key => {
|
||||||
const status = fixList[key].status
|
const status = fixList[key].status
|
||||||
return status === IToolboxItemCheckStatus.SUCCESS
|
return status === IToolboxItemCheckStatus.SUCCESS
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const isLoading = computed(() => {
|
const isLoading = computed(() => {
|
||||||
return Object.keys(fixList).some(key => {
|
return Object.keys(fixList).some(key => {
|
||||||
const status = fixList[key].status
|
const status = fixList[key].status
|
||||||
return status === IToolboxItemCheckStatus.LOADING
|
return status === IToolboxItemCheckStatus.LOADING
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const canFixLength = computed(() => {
|
const canFixLength = computed(() => {
|
||||||
return Object.keys(fixList).filter(key => {
|
return Object.keys(fixList).filter(key => {
|
||||||
const status = fixList[key].status
|
const status = fixList[key].status
|
||||||
return status === IToolboxItemCheckStatus.ERROR && !fixList[key].hasNoFixMethod
|
return status === IToolboxItemCheckStatus.ERROR && !fixList[key].hasNoFixMethod
|
||||||
}).length
|
}).length
|
||||||
})
|
})
|
||||||
|
|
||||||
const toggleItem = (key: string) => {
|
const toggleItem = (key: string) => {
|
||||||
const index = activeTypes.value.indexOf(key)
|
const index = activeTypes.value.indexOf(key)
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
activeTypes.value.splice(index, 1)
|
activeTypes.value.splice(index, 1)
|
||||||
} else {
|
} else {
|
||||||
activeTypes.value.push(key)
|
activeTypes.value.push(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolboxCheckResHandler = (_event: any, { type, msg = '', status, value = '' }: IToolboxCheckRes) => {
|
const toolboxCheckResHandler = ({ type, msg = '', status, value = '' }: IToolboxCheckRes) => {
|
||||||
fixList[type].status = status
|
fixList[type].status = status
|
||||||
fixList[type].msg = msg
|
fixList[type].msg = msg
|
||||||
fixList[type].value = value
|
fixList[type].value = value
|
||||||
if (status === IToolboxItemCheckStatus.ERROR) {
|
if (status === IToolboxItemCheckStatus.ERROR) {
|
||||||
activeTypes.value.push(type)
|
activeTypes.value.push(type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.electron.ipcRendererOn(IRPCActionType.TOOLBOX_CHECK_RES, toolboxCheckResHandler)
|
window.electron.ipcRendererOn(IRPCActionType.TOOLBOX_CHECK_RES, toolboxCheckResHandler)
|
||||||
|
|
||||||
const handleCheck = () => {
|
const handleCheck = () => {
|
||||||
activeTypes.value = []
|
activeTypes.value = []
|
||||||
Object.keys(fixList).forEach(key => {
|
Object.keys(fixList).forEach(key => {
|
||||||
fixList[key].status = IToolboxItemCheckStatus.LOADING
|
fixList[key].status = IToolboxItemCheckStatus.LOADING
|
||||||
fixList[key].msg = ''
|
fixList[key].msg = ''
|
||||||
fixList[key].value = ''
|
fixList[key].value = ''
|
||||||
})
|
})
|
||||||
window.electron.sendRPC(IRPCActionType.TOOLBOX_CHECK)
|
window.electron.sendRPC(IRPCActionType.TOOLBOX_CHECK)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleFix = async () => {
|
const handleFix = async () => {
|
||||||
const fixRes = await Promise.all(
|
const fixRes = await Promise.all(
|
||||||
Object.keys(fixList)
|
Object.keys(fixList)
|
||||||
.filter(key => {
|
.filter(key => {
|
||||||
const status = fixList[key].status
|
const status = fixList[key].status
|
||||||
return status === IToolboxItemCheckStatus.ERROR && !fixList[key].hasNoFixMethod
|
return status === IToolboxItemCheckStatus.ERROR && !fixList[key].hasNoFixMethod
|
||||||
})
|
})
|
||||||
.map(async key => {
|
.map(async key => {
|
||||||
return window.electron.triggerRPC<IToolboxCheckRes>(IRPCActionType.TOOLBOX_CHECK_FIX, key)
|
return window.electron.triggerRPC<IToolboxCheckRes>(IRPCActionType.TOOLBOX_CHECK_FIX, key)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
fixRes
|
fixRes
|
||||||
.filter(item => item !== null)
|
.filter(item => item !== null)
|
||||||
.forEach(item => {
|
.forEach(item => {
|
||||||
if (item) {
|
if (item) {
|
||||||
fixList[item.type].status = item.status
|
fixList[item.type].status = item.status
|
||||||
fixList[item.type].msg = item.msg
|
fixList[item.type].msg = item.msg
|
||||||
fixList[item.type].value = item.value
|
fixList[item.type].value = item.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
confirm({
|
confirm({
|
||||||
title: t('pages.toolbox.notice'),
|
title: t('pages.toolbox.notice'),
|
||||||
message: t('pages.toolbox.fixDoneNeedReload'),
|
message: t('pages.toolbox.fixDoneNeedReload'),
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
confirmButtonText: t('common.confirm'),
|
confirmButtonText: t('common.confirm'),
|
||||||
cancelButtonText: t('common.cancel'),
|
cancelButtonText: t('common.cancel'),
|
||||||
center: true
|
center: true
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
if (!result) return
|
if (!result) return
|
||||||
window.electron.sendRPC(IRPCActionType.RELOAD_APP)
|
window.electron.sendRPC(IRPCActionType.RELOAD_APP)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
window.electron.ipcRendererRemoveListener(IRPCActionType.TOOLBOX_CHECK_RES, toolboxCheckResHandler)
|
window.electron.ipcRendererRemoveAllListeners(IRPCActionType.TOOLBOX_CHECK_RES)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'ToolBoxPage'
|
name: 'ToolBoxPage'
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped src="./css/ToolboxPage.css"></style>
|
<style scoped src="./css/ToolboxPage.css"></style>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
|||||||
<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
|
<EditIcon
|
||||||
:size="16"
|
:size="16"
|
||||||
class="provider-arrow"
|
class="provider-arrow"
|
||||||
/>
|
/>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
class="action-button"
|
class="action-button"
|
||||||
@click="handleChangePicBed"
|
@click="handleChangePicBed"
|
||||||
>
|
>
|
||||||
<DatabaseIcon :size="16" />
|
<ArrowLeftRightIcon :size="16" />
|
||||||
<span>{{ t('pages.upload.changePicBed') }}</span>
|
<span>{{ t('pages.upload.changePicBed') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -173,7 +173,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="imageProcessDialogVisible"
|
v-if="imageProcessDialogVisible"
|
||||||
class="modal-overlay"
|
class="modal-overlay"
|
||||||
@click="imageProcessDialogVisible = false"
|
@click.stop
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="modal-container"
|
class="modal-container"
|
||||||
@@ -200,8 +200,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { IpcRendererEvent } from 'electron'
|
import { ArrowLeftRightIcon, ClipboardIcon, EditIcon, LinkIcon, Settings, UploadCloudIcon, XIcon } from 'lucide-vue-next'
|
||||||
import { ChevronDownIcon, ClipboardIcon, DatabaseIcon, LinkIcon, Settings, UploadCloudIcon, XIcon } from 'lucide-vue-next'
|
|
||||||
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
|
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
@@ -247,30 +246,23 @@ watch(picBedGlobal, () => {
|
|||||||
getDefaultPicBed()
|
getDefaultPicBed()
|
||||||
})
|
})
|
||||||
|
|
||||||
const uploadProgressHandler = (_event: IpcRendererEvent, _progress: number) => {
|
let removeUploadProgressListenerCallback: (() => void) = () => {}
|
||||||
if (_progress !== -1) {
|
let removeSyncPicBedListenerCallback: (() => void) = () => {}
|
||||||
|
|
||||||
|
function uploadProgressHandler (p: number): void {
|
||||||
|
if (p !== -1) {
|
||||||
showProgress.value = true
|
showProgress.value = true
|
||||||
progress.value = _progress
|
progress.value = p
|
||||||
} else {
|
} else {
|
||||||
progress.value = 100
|
progress.value = 100
|
||||||
showError.value = true
|
showError.value = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncPicBedHandler = () => {
|
function syncPicBedHandler (): void {
|
||||||
getDefaultPicBed()
|
getDefaultPicBed()
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
updatePicBedGlobal()
|
|
||||||
window.electron.ipcRendererOn('uploadProgress', uploadProgressHandler)
|
|
||||||
getUseShortUrl()
|
|
||||||
getPasteStyle()
|
|
||||||
getDefaultPicBed()
|
|
||||||
window.electron.ipcRendererOn('syncPicBed', syncPicBedHandler)
|
|
||||||
$bus.on(SHOW_INPUT_BOX_RESPONSE, handleInputBoxValue)
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleImageProcess = () => {
|
const handleImageProcess = () => {
|
||||||
imageProcessDialogVisible.value = true
|
imageProcessDialogVisible.value = true
|
||||||
}
|
}
|
||||||
@@ -308,12 +300,6 @@ async function handlePicBedNameClick (_picBedName: string, picBedConfigName: str
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
$bus.off(SHOW_INPUT_BOX_RESPONSE)
|
|
||||||
window.electron.ipcRendererRemoveListener('uploadProgress', uploadProgressHandler)
|
|
||||||
window.electron.ipcRendererRemoveListener('syncPicBed', syncPicBedHandler)
|
|
||||||
})
|
|
||||||
|
|
||||||
function onDrop (e: DragEvent) {
|
function onDrop (e: DragEvent) {
|
||||||
dragover.value = false
|
dragover.value = false
|
||||||
|
|
||||||
@@ -434,6 +420,23 @@ async function getDefaultPicBed () {
|
|||||||
async function handleChangePicBed () {
|
async function handleChangePicBed () {
|
||||||
window.electron.sendRPC(IRPCActionType.SHOW_UPLOAD_PAGE_MENU)
|
window.electron.sendRPC(IRPCActionType.SHOW_UPLOAD_PAGE_MENU)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
$bus.off(SHOW_INPUT_BOX_RESPONSE)
|
||||||
|
removeUploadProgressListenerCallback()
|
||||||
|
removeSyncPicBedListenerCallback()
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
updatePicBedGlobal()
|
||||||
|
getUseShortUrl()
|
||||||
|
getPasteStyle()
|
||||||
|
getDefaultPicBed()
|
||||||
|
removeUploadProgressListenerCallback = window.electron.ipcRendererOn('uploadProgress', uploadProgressHandler)
|
||||||
|
removeSyncPicBedListenerCallback = window.electron.ipcRendererOn('syncPicBed', syncPicBedHandler)
|
||||||
|
$bus.on(SHOW_INPUT_BOX_RESPONSE, handleInputBoxValue)
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
@@ -1,204 +1,203 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="config-container">
|
<div class="config-container">
|
||||||
<!-- Header Card -->
|
<!-- Header Card -->
|
||||||
<div class="config-card header-card">
|
<div class="config-card header-card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h1 class="page-title">
|
<h1 class="page-title">
|
||||||
{{ t('pages.uploaderConfig.title') }}
|
{{ t('pages.uploaderConfig.title') }}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Config Items Card -->
|
<!-- Config Items Card -->
|
||||||
<div class="config-card main-card">
|
<div class="config-card main-card">
|
||||||
<div class="config-grid">
|
<div class="config-grid">
|
||||||
<div
|
<div
|
||||||
v-for="item in curConfigList"
|
v-for="item in curConfigList"
|
||||||
:key="item._id"
|
:key="item._id"
|
||||||
:class="`config-item ${defaultConfigId === item._id ? 'selected' : ''}`"
|
:class="`config-item ${defaultConfigId === item._id ? 'selected' : ''}`"
|
||||||
@click="() => selectItem(item._id)"
|
@click="() => selectItem(item._id)"
|
||||||
>
|
>
|
||||||
<div class="config-content">
|
<div class="config-content">
|
||||||
<div class="config-name">
|
<div class="config-name">
|
||||||
{{ item._configName }}
|
{{ item._configName }}
|
||||||
</div>
|
</div>
|
||||||
<div class="config-update-time">
|
<div class="config-update-time">
|
||||||
{{ formatTime(item._updatedAt) }}
|
{{ formatTime(item._updatedAt) }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="defaultConfigId === item._id"
|
v-if="defaultConfigId === item._id"
|
||||||
class="default-badge"
|
class="default-badge"
|
||||||
>
|
>
|
||||||
{{ t('pages.uploaderConfig.selected') }}
|
{{ t('pages.uploaderConfig.selected') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-actions">
|
<div class="config-actions">
|
||||||
<button
|
<button
|
||||||
class="action-btn edit-btn"
|
class="action-btn edit-btn"
|
||||||
:title="t('pages.uploaderConfig.edit')"
|
:title="t('pages.uploaderConfig.edit')"
|
||||||
@click.stop="openEditPage(item._id)"
|
@click.stop="openEditPage(item._id)"
|
||||||
>
|
>
|
||||||
<Edit :size="16" />
|
<Edit :size="16" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="action-btn delete-btn"
|
class="action-btn delete-btn"
|
||||||
:class="curConfigList.length <= 1 ? 'disabled' : ''"
|
:class="curConfigList.length <= 1 ? 'disabled' : ''"
|
||||||
:title="t('pages.uploaderConfig.delete')"
|
:title="t('pages.uploaderConfig.delete')"
|
||||||
:disabled="curConfigList.length <= 1"
|
:disabled="curConfigList.length <= 1"
|
||||||
@click.stop="() => deleteConfig(item._id)"
|
@click.stop="() => deleteConfig(item._id)"
|
||||||
>
|
>
|
||||||
<Trash2 :size="16" />
|
<Trash2 :size="16" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Add New Config Button -->
|
<!-- Add New Config Button -->
|
||||||
<div
|
<div
|
||||||
class="config-item config-item-add"
|
class="config-item config-item-add"
|
||||||
@click="addNewConfig"
|
@click="addNewConfig"
|
||||||
>
|
>
|
||||||
<div class="add-content">
|
<div class="add-content">
|
||||||
<Plus :size="32" />
|
<Plus :size="32" />
|
||||||
<span class="add-text">{{ t('pages.uploaderConfig.addNew') }}</span>
|
<span class="add-text">{{ t('pages.uploaderConfig.addNew') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Actions Card -->
|
<!-- Actions Card -->
|
||||||
<div class="config-card actions-card">
|
<div class="config-card actions-card">
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<button
|
<button
|
||||||
class="primary-button"
|
class="primary-button"
|
||||||
:disabled="store?.state.defaultPicBed === type"
|
:disabled="store?.state.defaultPicBed === type"
|
||||||
@click="setDefaultPicBed(type)"
|
@click="setDefaultPicBed(type)"
|
||||||
>
|
>
|
||||||
<DatabaseIcon :size="16" />
|
<DatabaseIcon :size="16" />
|
||||||
<span>{{ t('pages.uploaderConfig.setAsDefault') }}</span>
|
<span>{{ t('pages.uploaderConfig.setAsDefault') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { DatabaseIcon, Edit, Plus, Trash2 } from 'lucide-vue-next'
|
import { DatabaseIcon, Edit, Plus, Trash2 } from 'lucide-vue-next'
|
||||||
import { onBeforeMount, ref } from 'vue'
|
import { onBeforeMount, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
|
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
import useConfirm from '@/hooks/useConfirm'
|
import useConfirm from '@/hooks/useConfirm'
|
||||||
import useMessage from '@/hooks/useMessage'
|
import useMessage from '@/hooks/useMessage'
|
||||||
import { useStore } from '@/hooks/useStore'
|
import { useStore } from '@/hooks/useStore'
|
||||||
import { PICBEDS_PAGE, UPLOADER_CONFIG_PAGE } from '@/router/config'
|
import { PICBEDS_PAGE, UPLOADER_CONFIG_PAGE } from '@/router/config'
|
||||||
import { configPaths } from '@/utils/configPaths'
|
import { configPaths } from '@/utils/configPaths'
|
||||||
import { saveConfig } from '@/utils/dataSender'
|
import { saveConfig } from '@/utils/dataSender'
|
||||||
import { IRPCActionType } from '@/utils/enum'
|
import { IRPCActionType } from '@/utils/enum'
|
||||||
import type { IStringKeyMap, IUploaderConfigItem } from '#/types/types'
|
import type { IStringKeyMap, IUploaderConfigItem } from '#/types/types'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const { confirm } = useConfirm()
|
const { confirm } = useConfirm()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
const type = ref('')
|
const type = ref('')
|
||||||
const curConfigList = ref<IStringKeyMap[]>([])
|
const curConfigList = ref<IStringKeyMap[]>([])
|
||||||
const defaultConfigId = ref('')
|
const defaultConfigId = ref('')
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
|
||||||
async function selectItem (id: string) {
|
async function selectItem (id: string) {
|
||||||
await window.electron.triggerRPC<void>(IRPCActionType.UPLOADER_SELECT, type.value, id)
|
await window.electron.triggerRPC<void>(IRPCActionType.UPLOADER_SELECT, type.value, id)
|
||||||
if (store?.state.defaultPicBed === type.value) {
|
if (store?.state.defaultPicBed === type.value) {
|
||||||
window.electron.sendRPC(
|
window.electron.sendRPC(
|
||||||
IRPCActionType.TRAY_SET_TOOL_TIP,
|
IRPCActionType.TRAY_SET_TOOL_TIP,
|
||||||
`${type.value} ${curConfigList.value.find(item => item._id === id)?._configName || ''}`
|
`${type.value} ${curConfigList.value.find(item => item._id === id)?._configName || ''}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
defaultConfigId.value = id
|
defaultConfigId.value = id
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeRouteUpdate((to, _, next) => {
|
onBeforeRouteUpdate((to, _, next) => {
|
||||||
if (to.params.type && to.name === UPLOADER_CONFIG_PAGE) {
|
if (to.params.type && to.name === UPLOADER_CONFIG_PAGE) {
|
||||||
type.value = to.params.type as string
|
type.value = to.params.type as string
|
||||||
getCurrentConfigList()
|
getCurrentConfigList()
|
||||||
}
|
}
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
type.value = route.params.type as string
|
type.value = route.params.type as string
|
||||||
getCurrentConfigList()
|
getCurrentConfigList()
|
||||||
})
|
})
|
||||||
|
|
||||||
async function getCurrentConfigList () {
|
async function getCurrentConfigList () {
|
||||||
const configList = await window.electron.triggerRPC<IUploaderConfigItem>(IRPCActionType.PICBED_GET_CONFIG_LIST, type.value)
|
const configList = await window.electron.triggerRPC<IUploaderConfigItem>(IRPCActionType.PICBED_GET_CONFIG_LIST, type.value)
|
||||||
curConfigList.value = configList?.configList ?? []
|
curConfigList.value = configList?.configList ?? []
|
||||||
defaultConfigId.value = configList?.defaultId ?? ''
|
defaultConfigId.value = configList?.defaultId ?? ''
|
||||||
}
|
}
|
||||||
|
|
||||||
function openEditPage (configId: string) {
|
function openEditPage (configId: string) {
|
||||||
router.push({
|
router.push({
|
||||||
name: PICBEDS_PAGE,
|
name: PICBEDS_PAGE,
|
||||||
params: {
|
params: {
|
||||||
type: type.value,
|
type: type.value,
|
||||||
configId
|
configId
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
defaultConfigId: defaultConfigId.value
|
defaultConfigId: defaultConfigId.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTime (time: number): string {
|
function formatTime (time: number): string {
|
||||||
return dayjs(time).format('YYYY-MM-DD HH:mm')
|
return dayjs(time).format('YYYY-MM-DD HH:mm')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteConfig (id: string) {
|
async function deleteConfig (id: string) {
|
||||||
confirm({
|
const result = await confirm({
|
||||||
title: t('pages.uploaderConfig.deleteTitle'),
|
title: t('pages.uploaderConfig.deleteTitle'),
|
||||||
message: t('pages.uploaderConfig.deleteConfirm'),
|
message: t('pages.uploaderConfig.deleteConfirm'),
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
confirmButtonText: t('common.confirm'),
|
confirmButtonText: t('common.confirm'),
|
||||||
cancelButtonText: t('common.cancel'),
|
cancelButtonText: t('common.cancel'),
|
||||||
center: true
|
center: true
|
||||||
}).then(async result => {
|
})
|
||||||
if (!result) return
|
if (!result) return
|
||||||
const res = await window.electron.triggerRPC<IUploaderConfigItem>(IRPCActionType.PICBED_DELETE_CONFIG, type.value, id)
|
const res = await window.electron.triggerRPC<IUploaderConfigItem>(IRPCActionType.PICBED_DELETE_CONFIG, type.value, id)
|
||||||
if (!res) return
|
if (!res) return
|
||||||
curConfigList.value = res.configList
|
curConfigList.value = res.configList
|
||||||
defaultConfigId.value = res.defaultId
|
defaultConfigId.value = res.defaultId
|
||||||
message.success(t('pages.uploaderConfig.deleteSuccess'))
|
message.success(t('pages.uploaderConfig.deleteSuccess'))
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
function addNewConfig () {
|
||||||
function addNewConfig () {
|
router.push({
|
||||||
router.push({
|
name: PICBEDS_PAGE,
|
||||||
name: PICBEDS_PAGE,
|
params: {
|
||||||
params: {
|
type: type.value,
|
||||||
type: type.value,
|
configId: ''
|
||||||
configId: ''
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
function setDefaultPicBed (type: string) {
|
||||||
function setDefaultPicBed (type: string) {
|
saveConfig({
|
||||||
saveConfig({
|
[configPaths.picBed.current]: type,
|
||||||
[configPaths.picBed.current]: type,
|
[configPaths.picBed.uploader]: type
|
||||||
[configPaths.picBed.uploader]: type
|
})
|
||||||
})
|
|
||||||
|
store?.setDefaultPicBed(type)
|
||||||
store?.setDefaultPicBed(type)
|
const currentConfigName = curConfigList.value.find(item => item._id === defaultConfigId.value)?._configName
|
||||||
const currentConfigName = curConfigList.value.find(item => item._id === defaultConfigId.value)?._configName
|
window.electron.sendRPC(IRPCActionType.TRAY_SET_TOOL_TIP, `${type} ${currentConfigName || ''}`)
|
||||||
window.electron.sendRPC(IRPCActionType.TRAY_SET_TOOL_TIP, `${type} ${currentConfigName || ''}`)
|
message.success(t('pages.uploaderConfig.setSuccess'))
|
||||||
message.success(t('pages.uploaderConfig.setSuccess'))
|
}
|
||||||
}
|
</script>
|
||||||
</script>
|
<script lang="ts">
|
||||||
<script lang="ts">
|
export default {
|
||||||
export default {
|
name: 'UploaderConfigPage'
|
||||||
name: 'UploaderConfigPage'
|
}
|
||||||
}
|
</script>
|
||||||
</script>
|
|
||||||
|
<style scoped src="./css/UploaderConfigPage.css"></style>
|
||||||
<style scoped src="./css/UploaderConfigPage.css"></style>
|
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ html, body {
|
|||||||
|
|
||||||
.provider-button:hover .provider-arrow {
|
.provider-button:hover .provider-arrow {
|
||||||
color: var(--color-accent);
|
color: var(--color-accent);
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-actions {
|
.header-actions {
|
||||||
|
|||||||
0
src/renderer/utils/ipcListenerManager.ts
Normal file
0
src/renderer/utils/ipcListenerManager.ts
Normal file
148
src/universal/types/shims-tsx.d.ts
vendored
148
src/universal/types/shims-tsx.d.ts
vendored
@@ -1,74 +1,74 @@
|
|||||||
// src/global.d.ts
|
// src/global.d.ts
|
||||||
|
|
||||||
import crypto from 'node:crypto'
|
import crypto from 'node:crypto'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
|
|
||||||
import { clipboard } from 'electron'
|
import { clipboard } from 'electron'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import yaml from 'js-yaml'
|
import yaml from 'js-yaml'
|
||||||
import mime from 'mime-types'
|
import mime from 'mime-types'
|
||||||
import { VNode } from 'vue'
|
import { VNode } from 'vue'
|
||||||
|
|
||||||
import { IpcRendererListener } from '#/types/electron'
|
import { ILocales, ILocalesKey } from '#/types/i18n'
|
||||||
import { ILocales, ILocalesKey } from '#/types/i18n'
|
import { IStringKeyMap } from '#/types/types'
|
||||||
import { IStringKeyMap } from '#/types/types'
|
|
||||||
|
declare global {
|
||||||
declare global {
|
|
||||||
|
export namespace JSX {
|
||||||
export namespace JSX {
|
export interface Element extends VNode {}
|
||||||
export interface Element extends VNode {}
|
export interface IntrinsicElements {
|
||||||
export interface IntrinsicElements {
|
[elem: string]: any
|
||||||
[elem: string]: any
|
}
|
||||||
}
|
}
|
||||||
}
|
export interface Window {
|
||||||
export interface Window {
|
electron: {
|
||||||
electron: {
|
platform: string
|
||||||
platform: string
|
setVisualZoomLevelLimits: (min: number, max: number) => void
|
||||||
setVisualZoomLevelLimits: (min: number, max: number) => void
|
sendRpcSync: (action: string, ...args: any[]) => any
|
||||||
sendRpcSync: (action: string, ...args: any[]) => any
|
triggerRPC: <T>(action: string, ...args: any[]) => Promise<T | undefined>
|
||||||
triggerRPC: <T>(action: string, ...args: any[]) => Promise<T | undefined>
|
sendToMain: (channel: string, ...args: any[]) => void
|
||||||
sendToMain: (channel: string, ...args: any[]) => void
|
sendRPC: (action: string, ...args: any[]) => void
|
||||||
sendRPC: (action: string, ...args: any[]) => void
|
ipcRendererOn: (channel: string, listener: (...args: any[]) => void) => () => void
|
||||||
ipcRendererOn: (channel: string, listener: IpcRendererListener) => void
|
ipcRendererCountListeners: (channel: string) => number
|
||||||
ipcRendererRemoveListener: (channel: string, listener: IpcRendererListener) => void
|
ipcRendererRemoveAllListeners: (channel: string) => void
|
||||||
clipboard: {
|
clipboard: {
|
||||||
writeText: typeof clipboard.writeText
|
writeText: typeof clipboard.writeText
|
||||||
},
|
},
|
||||||
showFilePath: (file: File) => string
|
showFilePath: (file: File) => string
|
||||||
}
|
}
|
||||||
node: {
|
node: {
|
||||||
path: {
|
path: {
|
||||||
join: typeof path.join
|
join: typeof path.join
|
||||||
dirname: typeof path.dirname
|
dirname: typeof path.dirname
|
||||||
basename: typeof path.basename
|
basename: typeof path.basename
|
||||||
normalize: typeof path.normalize
|
normalize: typeof path.normalize
|
||||||
extname: typeof path.extname
|
extname: typeof path.extname
|
||||||
sep: typeof path.sep,
|
sep: typeof path.sep,
|
||||||
posix: {
|
posix: {
|
||||||
sep: typeof path.posix.sep
|
sep: typeof path.posix.sep
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
crypto: {
|
crypto: {
|
||||||
randomBytes: typeof crypto.randomBytes
|
randomBytes: typeof crypto.randomBytes
|
||||||
createHash: typeof crypto.createHash
|
createHash: typeof crypto.createHash
|
||||||
}
|
}
|
||||||
fs: {
|
fs: {
|
||||||
remove: typeof fs.remove
|
remove: typeof fs.remove
|
||||||
readFile: typeof fs.readFile
|
readFile: typeof fs.readFile
|
||||||
statSync: typeof fs.statSync
|
statSync: typeof fs.statSync
|
||||||
}
|
}
|
||||||
|
|
||||||
yaml: {
|
yaml: {
|
||||||
load: typeof yaml.load
|
load: typeof yaml.load
|
||||||
}
|
}
|
||||||
mime: {
|
mime: {
|
||||||
lookup: typeof mime.lookup
|
lookup: typeof mime.lookup
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
i18n: {
|
i18n: {
|
||||||
setLocales: (lang: string, locales: ILocales) => void
|
setLocales: (lang: string, locales: ILocales) => void
|
||||||
translate: (key: ILocalesKey, args?: IStringKeyMap) => string
|
translate: (key: ILocalesKey, args?: IStringKeyMap) => string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user