mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-06-02 21:19:36 +08:00
🐛 Fix(custom): fix drag upload of upload page
This commit is contained in:
@@ -1,444 +1,445 @@
|
||||
<template>
|
||||
<div class="upload-container">
|
||||
<!-- Header Card -->
|
||||
<div class="upload-card header-card">
|
||||
<div class="card-header">
|
||||
<div class="provider-section">
|
||||
<button
|
||||
class="provider-button"
|
||||
:title="t('pages.upload.uploadViewHint')"
|
||||
@click="handlePicBedNameClick(picBedName, picBedConfigName)"
|
||||
>
|
||||
<div class="provider-info">
|
||||
<span class="provider-name">{{ picBedName }}</span>
|
||||
<span class="provider-config">{{ picBedConfigName || 'Default' }}</span>
|
||||
</div>
|
||||
<ChevronDownIcon
|
||||
:size="16"
|
||||
class="provider-arrow"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button
|
||||
class="action-button secondary"
|
||||
@click="handleImageProcess"
|
||||
>
|
||||
<Settings :size="16" />
|
||||
<span>{{ t('pages.upload.imageProcessName') }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="action-button"
|
||||
@click="handleChangePicBed"
|
||||
>
|
||||
<DatabaseIcon :size="16" />
|
||||
<span>{{ t('pages.upload.changePicBed') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Upload Card -->
|
||||
<div class="upload-card main-card">
|
||||
<div
|
||||
class="upload-zone"
|
||||
:class="{ 'drag-active': dragover }"
|
||||
@drop.prevent="onDrop"
|
||||
@dragover.prevent="dragover = true"
|
||||
@dragleave.prevent="dragover = false"
|
||||
@click="openUplodWindow"
|
||||
>
|
||||
<div class="upload-content">
|
||||
<div class="upload-icon">
|
||||
<UploadCloudIcon :size="48" />
|
||||
</div>
|
||||
<div class="upload-text">
|
||||
<h3 class="upload-title">
|
||||
{{ t('pages.upload.dragFileToHere') }}
|
||||
</h3>
|
||||
<p class="upload-subtitle">
|
||||
{{ t('pages.upload.clickToUpload') }}
|
||||
</p>
|
||||
<div class="upload-formats">
|
||||
<span class="format-label">{{ t('pages.upload.uploadHint') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
id="file-uploader"
|
||||
ref="fileInput"
|
||||
type="file"
|
||||
multiple
|
||||
style="display: none"
|
||||
@change="onChange"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<transition name="progress">
|
||||
<div
|
||||
v-if="showProgress"
|
||||
class="progress-container"
|
||||
>
|
||||
<div class="progress-bar">
|
||||
<div
|
||||
class="progress-fill"
|
||||
:class="{ 'progress-error': showError }"
|
||||
:style="{ width: `${progress}%` }"
|
||||
/>
|
||||
</div>
|
||||
<span class="progress-text">
|
||||
{{ showError ? t('pages.upload.uploadFailed') : `${progress}%` }}
|
||||
</span>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions Card -->
|
||||
<div class="upload-card actions-card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">
|
||||
{{ t('pages.upload.quickUpload') }}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="quick-actions">
|
||||
<button
|
||||
class="quick-action-button"
|
||||
@click="uploadClipboardFiles"
|
||||
>
|
||||
<ClipboardIcon :size="20" />
|
||||
<span>{{ t('pages.upload.clipboardPicture') }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="quick-action-button"
|
||||
@click="uploadURLFiles"
|
||||
>
|
||||
<LinkIcon :size="20" />
|
||||
<span>{{ t('pages.upload.urlUpload') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Card -->
|
||||
<div class="upload-card settings-card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">
|
||||
{{ t('pages.upload.linkFormat') }}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="settings-content">
|
||||
<!-- Format Options -->
|
||||
<div class="setting-group">
|
||||
<label class="setting-label">{{ t('pages.upload.outputFormat') }}</label>
|
||||
<div class="format-buttons">
|
||||
<button
|
||||
v-for="(format, key) in pasteFormatList"
|
||||
:key="key"
|
||||
class="format-button"
|
||||
:class="{ active: pasteStyle === key }"
|
||||
:title="format"
|
||||
@click="updatePasteStyle(key)"
|
||||
>
|
||||
{{ key }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- URL Length Options -->
|
||||
<div class="setting-group">
|
||||
<label class="setting-label">{{ t('pages.upload.urlType.title') }}</label>
|
||||
<div class="url-toggle">
|
||||
<button
|
||||
class="toggle-button"
|
||||
:class="{ active: !useShortUrl }"
|
||||
@click="updateUrlType(false)"
|
||||
>
|
||||
<span>{{ t('pages.upload.urlType.normal') }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="toggle-button"
|
||||
:class="{ active: useShortUrl }"
|
||||
@click="updateUrlType(true)"
|
||||
>
|
||||
<span>{{ t('pages.upload.urlType.short') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Image Process Dialog -->
|
||||
<transition name="modal">
|
||||
<div
|
||||
v-if="imageProcessDialogVisible"
|
||||
class="modal-overlay"
|
||||
@click="imageProcessDialogVisible = false"
|
||||
>
|
||||
<div
|
||||
class="modal-container"
|
||||
@click.stop
|
||||
>
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">
|
||||
{{ t('pages.imageProcess.title') }}
|
||||
</h3>
|
||||
<button
|
||||
class="modal-close"
|
||||
@click="imageProcessDialogVisible = false"
|
||||
>
|
||||
<XIcon :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<ImageProcessSetting v-model="imageProcessDialogVisible" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { IpcRendererEvent } from 'electron'
|
||||
import { ChevronDownIcon, ClipboardIcon, DatabaseIcon, LinkIcon, Settings, UploadCloudIcon, XIcon } from 'lucide-vue-next'
|
||||
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import ImageProcessSetting from '@/components/ImageProcessSetting.vue'
|
||||
import useMessage from '@/hooks/useMessage'
|
||||
import { PICBEDS_PAGE } from '@/router/config'
|
||||
import $bus from '@/utils/bus'
|
||||
import { isUrl } from '@/utils/common'
|
||||
import { configPaths } from '@/utils/configPaths'
|
||||
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant'
|
||||
import { getConfig, saveConfig } from '@/utils/dataSender'
|
||||
import { useDragEventListeners } from '@/utils/drag'
|
||||
import { IPasteStyle, IRPCActionType } from '@/utils/enum'
|
||||
import { picBedGlobal, updatePicBedGlobal } from '@/utils/global'
|
||||
import type { IFileWithPath, IUploaderConfigItem } from '#/types/types'
|
||||
|
||||
useDragEventListeners()
|
||||
const $router = useRouter()
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
|
||||
const imageProcessDialogVisible = ref(false)
|
||||
const useShortUrl = ref(false)
|
||||
const dragover = ref(false)
|
||||
const progress = ref(0)
|
||||
const showProgress = ref(false)
|
||||
const showError = ref(false)
|
||||
const pasteStyle = ref('')
|
||||
const picBedName = ref('')
|
||||
const picBedConfigName = ref('')
|
||||
const fileInput = ref<HTMLInputElement>()
|
||||
|
||||
const pasteFormatList = ref<Record<string, string>>({
|
||||
[IPasteStyle.MARKDOWN]: '',
|
||||
[IPasteStyle.HTML]: '<img src="url"/>',
|
||||
[IPasteStyle.URL]: 'http://test.com/test.png',
|
||||
[IPasteStyle.UBB]: '[img]url[/img]',
|
||||
[IPasteStyle.CUSTOM]: ''
|
||||
})
|
||||
|
||||
watch(picBedGlobal, () => {
|
||||
getDefaultPicBed()
|
||||
})
|
||||
|
||||
const uploadProgressHandler = (_event: IpcRendererEvent, _progress: number) => {
|
||||
if (_progress !== -1) {
|
||||
showProgress.value = true
|
||||
progress.value = _progress
|
||||
} else {
|
||||
progress.value = 100
|
||||
showError.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const syncPicBedHandler = () => {
|
||||
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 = () => {
|
||||
imageProcessDialogVisible.value = true
|
||||
}
|
||||
|
||||
watch(progress, onProgressChange)
|
||||
|
||||
function onProgressChange (val: number) {
|
||||
if (val === 100) {
|
||||
setTimeout(() => {
|
||||
showProgress.value = false
|
||||
showError.value = false
|
||||
}, 1000)
|
||||
setTimeout(() => {
|
||||
progress.value = 0
|
||||
}, 1200)
|
||||
}
|
||||
}
|
||||
|
||||
async function handlePicBedNameClick (_picBedName: string, picBedConfigName: string | undefined) {
|
||||
const formatedpicBedConfigName = picBedConfigName || 'Default'
|
||||
const currentPicBed = await getConfig<string>(configPaths.picBed.current)
|
||||
const currentPicBedConfig = ((await getConfig<any[]>(`uploader.${currentPicBed}`)) as any) || {}
|
||||
const configList = await window.electron.triggerRPC<IUploaderConfigItem>(IRPCActionType.PICBED_GET_CONFIG_LIST, currentPicBed)
|
||||
const currentConfigList = configList?.configList ?? []
|
||||
const config = currentConfigList.find((item: any) => item._configName === formatedpicBedConfigName)
|
||||
$router.push({
|
||||
name: PICBEDS_PAGE,
|
||||
params: {
|
||||
type: currentPicBed,
|
||||
configId: config?._id || ''
|
||||
},
|
||||
query: {
|
||||
defaultConfigId: currentPicBedConfig.defaultId || ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
$bus.off(SHOW_INPUT_BOX_RESPONSE)
|
||||
window.electron.ipcRendererRemoveListener('uploadProgress', uploadProgressHandler)
|
||||
window.electron.ipcRendererRemoveListener('syncPicBed', syncPicBedHandler)
|
||||
})
|
||||
|
||||
function onDrop (e: DragEvent) {
|
||||
dragover.value = false
|
||||
|
||||
// send files first
|
||||
if (e.dataTransfer?.files?.length) {
|
||||
ipcSendFiles(e.dataTransfer.files)
|
||||
} 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)
|
||||
if (isUrl(str)) {
|
||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [{ path: str }])
|
||||
} else {
|
||||
message.error(t('pages.upload.dragValidPictureOrUrl'))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleURLDrag (items: DataTransferItemList, dataTransfer: DataTransfer) {
|
||||
// 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, [
|
||||
{
|
||||
path: urlMatch[1]
|
||||
}
|
||||
])
|
||||
} else {
|
||||
message.error(t('pages.upload.dragValidPictureOrUrl'))
|
||||
}
|
||||
}
|
||||
|
||||
function openUplodWindow () {
|
||||
fileInput.value?.click()
|
||||
}
|
||||
|
||||
function onChange (e: any) {
|
||||
ipcSendFiles(e.target.files)
|
||||
;(fileInput.value as HTMLInputElement).value = ''
|
||||
}
|
||||
|
||||
function ipcSendFiles (files: FileList) {
|
||||
const sendFiles: IFileWithPath[] = []
|
||||
Array.from(files).forEach(item => {
|
||||
const obj = {
|
||||
name: item.name,
|
||||
path: window.electron.showFilePath(item)
|
||||
}
|
||||
sendFiles.push(obj)
|
||||
})
|
||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, sendFiles)
|
||||
}
|
||||
|
||||
async function getPasteStyle () {
|
||||
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
|
||||
}
|
||||
|
||||
function updatePasteStyle (style: string) {
|
||||
pasteStyle.value = style
|
||||
saveConfig({
|
||||
[configPaths.settings.pasteStyle]: style || IPasteStyle.MARKDOWN
|
||||
})
|
||||
}
|
||||
|
||||
function updateUrlType (shortUrl: boolean) {
|
||||
useShortUrl.value = shortUrl
|
||||
saveConfig({
|
||||
[configPaths.settings.useShortUrl]: shortUrl
|
||||
})
|
||||
}
|
||||
|
||||
function uploadClipboardFiles () {
|
||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CLIPBOARD_FILES_FROM_UPLOAD_PAGE)
|
||||
}
|
||||
|
||||
async function uploadURLFiles () {
|
||||
const str = await navigator.clipboard.readText()
|
||||
$bus.emit(SHOW_INPUT_BOX, {
|
||||
value: isUrl(str) ? str : '',
|
||||
title: t('pages.upload.inputUrlTip'),
|
||||
placeholder: t('pages.upload.httpPrefixTip')
|
||||
})
|
||||
}
|
||||
|
||||
function handleInputBoxValue (val: string) {
|
||||
if (val === '') return
|
||||
if (isUrl(val)) {
|
||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [
|
||||
{
|
||||
path: val
|
||||
}
|
||||
])
|
||||
} else {
|
||||
message.error(t('pages.upload.inputValidUrl'))
|
||||
}
|
||||
}
|
||||
|
||||
async function getDefaultPicBed () {
|
||||
const currentPicBed = await getConfig<string>(configPaths.picBed.current)
|
||||
picBedGlobal.value.forEach(item => {
|
||||
if (item.type === currentPicBed) {
|
||||
picBedName.value = item.name
|
||||
}
|
||||
})
|
||||
picBedConfigName.value = (await getConfig<string>(`picBed.${currentPicBed}._configName`)) || ''
|
||||
}
|
||||
|
||||
async function handleChangePicBed () {
|
||||
window.electron.sendRPC(IRPCActionType.SHOW_UPLOAD_PAGE_MENU)
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'UploadPage'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped src="./css/UploadPage.css"></style>
|
||||
<template>
|
||||
<div class="upload-container">
|
||||
<!-- Header Card -->
|
||||
<div class="upload-card header-card">
|
||||
<div class="card-header">
|
||||
<div class="provider-section">
|
||||
<button
|
||||
class="provider-button"
|
||||
:title="t('pages.upload.uploadViewHint')"
|
||||
@click="handlePicBedNameClick(picBedName, picBedConfigName)"
|
||||
>
|
||||
<div class="provider-info">
|
||||
<span class="provider-name">{{ picBedName }}</span>
|
||||
<span class="provider-config">{{ picBedConfigName || 'Default' }}</span>
|
||||
</div>
|
||||
<ChevronDownIcon
|
||||
:size="16"
|
||||
class="provider-arrow"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button
|
||||
class="action-button secondary"
|
||||
@click="handleImageProcess"
|
||||
>
|
||||
<Settings :size="16" />
|
||||
<span>{{ t('pages.upload.imageProcessName') }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="action-button"
|
||||
@click="handleChangePicBed"
|
||||
>
|
||||
<DatabaseIcon :size="16" />
|
||||
<span>{{ t('pages.upload.changePicBed') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Upload Card -->
|
||||
<div class="upload-card main-card">
|
||||
<div
|
||||
id="upload-area"
|
||||
class="upload-zone"
|
||||
:class="{ 'drag-active': dragover }"
|
||||
@drop.prevent="onDrop"
|
||||
@dragover.prevent="dragover = true"
|
||||
@dragleave.prevent="dragover = false"
|
||||
@click="openUplodWindow"
|
||||
>
|
||||
<div class="upload-content">
|
||||
<div class="upload-icon">
|
||||
<UploadCloudIcon :size="48" />
|
||||
</div>
|
||||
<div class="upload-text">
|
||||
<h3 class="upload-title">
|
||||
{{ t('pages.upload.dragFileToHere') }}
|
||||
</h3>
|
||||
<p class="upload-subtitle">
|
||||
{{ t('pages.upload.clickToUpload') }}
|
||||
</p>
|
||||
<div class="upload-formats">
|
||||
<span class="format-label">{{ t('pages.upload.uploadHint') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
id="file-uploader"
|
||||
ref="fileInput"
|
||||
type="file"
|
||||
multiple
|
||||
style="display: none"
|
||||
@change="onChange"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<transition name="progress">
|
||||
<div
|
||||
v-if="showProgress"
|
||||
class="progress-container"
|
||||
>
|
||||
<div class="progress-bar">
|
||||
<div
|
||||
class="progress-fill"
|
||||
:class="{ 'progress-error': showError }"
|
||||
:style="{ width: `${progress}%` }"
|
||||
/>
|
||||
</div>
|
||||
<span class="progress-text">
|
||||
{{ showError ? t('pages.upload.uploadFailed') : `${progress}%` }}
|
||||
</span>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions Card -->
|
||||
<div class="upload-card actions-card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">
|
||||
{{ t('pages.upload.quickUpload') }}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="quick-actions">
|
||||
<button
|
||||
class="quick-action-button"
|
||||
@click="uploadClipboardFiles"
|
||||
>
|
||||
<ClipboardIcon :size="20" />
|
||||
<span>{{ t('pages.upload.clipboardPicture') }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="quick-action-button"
|
||||
@click="uploadURLFiles"
|
||||
>
|
||||
<LinkIcon :size="20" />
|
||||
<span>{{ t('pages.upload.urlUpload') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Card -->
|
||||
<div class="upload-card settings-card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">
|
||||
{{ t('pages.upload.linkFormat') }}
|
||||
</h4>
|
||||
</div>
|
||||
<div class="settings-content">
|
||||
<!-- Format Options -->
|
||||
<div class="setting-group">
|
||||
<label class="setting-label">{{ t('pages.upload.outputFormat') }}</label>
|
||||
<div class="format-buttons">
|
||||
<button
|
||||
v-for="(format, key) in pasteFormatList"
|
||||
:key="key"
|
||||
class="format-button"
|
||||
:class="{ active: pasteStyle === key }"
|
||||
:title="format"
|
||||
@click="updatePasteStyle(key)"
|
||||
>
|
||||
{{ key }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- URL Length Options -->
|
||||
<div class="setting-group">
|
||||
<label class="setting-label">{{ t('pages.upload.urlType.title') }}</label>
|
||||
<div class="url-toggle">
|
||||
<button
|
||||
class="toggle-button"
|
||||
:class="{ active: !useShortUrl }"
|
||||
@click="updateUrlType(false)"
|
||||
>
|
||||
<span>{{ t('pages.upload.urlType.normal') }}</span>
|
||||
</button>
|
||||
<button
|
||||
class="toggle-button"
|
||||
:class="{ active: useShortUrl }"
|
||||
@click="updateUrlType(true)"
|
||||
>
|
||||
<span>{{ t('pages.upload.urlType.short') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Image Process Dialog -->
|
||||
<transition name="modal">
|
||||
<div
|
||||
v-if="imageProcessDialogVisible"
|
||||
class="modal-overlay"
|
||||
@click="imageProcessDialogVisible = false"
|
||||
>
|
||||
<div
|
||||
class="modal-container"
|
||||
@click.stop
|
||||
>
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">
|
||||
{{ t('pages.imageProcess.title') }}
|
||||
</h3>
|
||||
<button
|
||||
class="modal-close"
|
||||
@click="imageProcessDialogVisible = false"
|
||||
>
|
||||
<XIcon :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<ImageProcessSetting v-model="imageProcessDialogVisible" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { IpcRendererEvent } from 'electron'
|
||||
import { ChevronDownIcon, ClipboardIcon, DatabaseIcon, LinkIcon, Settings, UploadCloudIcon, XIcon } from 'lucide-vue-next'
|
||||
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import ImageProcessSetting from '@/components/ImageProcessSetting.vue'
|
||||
import useMessage from '@/hooks/useMessage'
|
||||
import { PICBEDS_PAGE } from '@/router/config'
|
||||
import $bus from '@/utils/bus'
|
||||
import { isUrl } from '@/utils/common'
|
||||
import { configPaths } from '@/utils/configPaths'
|
||||
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant'
|
||||
import { getConfig, saveConfig } from '@/utils/dataSender'
|
||||
import { useDragEventListeners } from '@/utils/drag'
|
||||
import { IPasteStyle, IRPCActionType } from '@/utils/enum'
|
||||
import { picBedGlobal, updatePicBedGlobal } from '@/utils/global'
|
||||
import type { IFileWithPath, IUploaderConfigItem } from '#/types/types'
|
||||
|
||||
useDragEventListeners()
|
||||
const $router = useRouter()
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
|
||||
const imageProcessDialogVisible = ref(false)
|
||||
const useShortUrl = ref(false)
|
||||
const dragover = ref(false)
|
||||
const progress = ref(0)
|
||||
const showProgress = ref(false)
|
||||
const showError = ref(false)
|
||||
const pasteStyle = ref('')
|
||||
const picBedName = ref('')
|
||||
const picBedConfigName = ref('')
|
||||
const fileInput = ref<HTMLInputElement>()
|
||||
|
||||
const pasteFormatList = ref<Record<string, string>>({
|
||||
[IPasteStyle.MARKDOWN]: '',
|
||||
[IPasteStyle.HTML]: '<img src="url"/>',
|
||||
[IPasteStyle.URL]: 'http://test.com/test.png',
|
||||
[IPasteStyle.UBB]: '[img]url[/img]',
|
||||
[IPasteStyle.CUSTOM]: ''
|
||||
})
|
||||
|
||||
watch(picBedGlobal, () => {
|
||||
getDefaultPicBed()
|
||||
})
|
||||
|
||||
const uploadProgressHandler = (_event: IpcRendererEvent, _progress: number) => {
|
||||
if (_progress !== -1) {
|
||||
showProgress.value = true
|
||||
progress.value = _progress
|
||||
} else {
|
||||
progress.value = 100
|
||||
showError.value = true
|
||||
}
|
||||
}
|
||||
|
||||
const syncPicBedHandler = () => {
|
||||
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 = () => {
|
||||
imageProcessDialogVisible.value = true
|
||||
}
|
||||
|
||||
watch(progress, onProgressChange)
|
||||
|
||||
function onProgressChange (val: number) {
|
||||
if (val === 100) {
|
||||
setTimeout(() => {
|
||||
showProgress.value = false
|
||||
showError.value = false
|
||||
}, 1000)
|
||||
setTimeout(() => {
|
||||
progress.value = 0
|
||||
}, 1200)
|
||||
}
|
||||
}
|
||||
|
||||
async function handlePicBedNameClick (_picBedName: string, picBedConfigName: string | undefined) {
|
||||
const formatedpicBedConfigName = picBedConfigName || 'Default'
|
||||
const currentPicBed = await getConfig<string>(configPaths.picBed.current)
|
||||
const currentPicBedConfig = ((await getConfig<any[]>(`uploader.${currentPicBed}`)) as any) || {}
|
||||
const configList = await window.electron.triggerRPC<IUploaderConfigItem>(IRPCActionType.PICBED_GET_CONFIG_LIST, currentPicBed)
|
||||
const currentConfigList = configList?.configList ?? []
|
||||
const config = currentConfigList.find((item: any) => item._configName === formatedpicBedConfigName)
|
||||
$router.push({
|
||||
name: PICBEDS_PAGE,
|
||||
params: {
|
||||
type: currentPicBed,
|
||||
configId: config?._id || ''
|
||||
},
|
||||
query: {
|
||||
defaultConfigId: currentPicBedConfig.defaultId || ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
$bus.off(SHOW_INPUT_BOX_RESPONSE)
|
||||
window.electron.ipcRendererRemoveListener('uploadProgress', uploadProgressHandler)
|
||||
window.electron.ipcRendererRemoveListener('syncPicBed', syncPicBedHandler)
|
||||
})
|
||||
|
||||
function onDrop (e: DragEvent) {
|
||||
dragover.value = false
|
||||
|
||||
// send files first
|
||||
if (e.dataTransfer?.files?.length) {
|
||||
ipcSendFiles(e.dataTransfer.files)
|
||||
} 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)
|
||||
if (isUrl(str)) {
|
||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [{ path: str }])
|
||||
} else {
|
||||
message.error(t('pages.upload.dragValidPictureOrUrl'))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleURLDrag (items: DataTransferItemList, dataTransfer: DataTransfer) {
|
||||
// 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, [
|
||||
{
|
||||
path: urlMatch[1]
|
||||
}
|
||||
])
|
||||
} else {
|
||||
message.error(t('pages.upload.dragValidPictureOrUrl'))
|
||||
}
|
||||
}
|
||||
|
||||
function openUplodWindow () {
|
||||
fileInput.value?.click()
|
||||
}
|
||||
|
||||
function onChange (e: any) {
|
||||
ipcSendFiles(e.target.files)
|
||||
;(fileInput.value as HTMLInputElement).value = ''
|
||||
}
|
||||
|
||||
function ipcSendFiles (files: FileList) {
|
||||
const sendFiles: IFileWithPath[] = []
|
||||
Array.from(files).forEach(item => {
|
||||
const obj = {
|
||||
name: item.name,
|
||||
path: window.electron.showFilePath(item)
|
||||
}
|
||||
sendFiles.push(obj)
|
||||
})
|
||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, sendFiles)
|
||||
}
|
||||
|
||||
async function getPasteStyle () {
|
||||
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
|
||||
}
|
||||
|
||||
function updatePasteStyle (style: string) {
|
||||
pasteStyle.value = style
|
||||
saveConfig({
|
||||
[configPaths.settings.pasteStyle]: style || IPasteStyle.MARKDOWN
|
||||
})
|
||||
}
|
||||
|
||||
function updateUrlType (shortUrl: boolean) {
|
||||
useShortUrl.value = shortUrl
|
||||
saveConfig({
|
||||
[configPaths.settings.useShortUrl]: shortUrl
|
||||
})
|
||||
}
|
||||
|
||||
function uploadClipboardFiles () {
|
||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CLIPBOARD_FILES_FROM_UPLOAD_PAGE)
|
||||
}
|
||||
|
||||
async function uploadURLFiles () {
|
||||
const str = await navigator.clipboard.readText()
|
||||
$bus.emit(SHOW_INPUT_BOX, {
|
||||
value: isUrl(str) ? str : '',
|
||||
title: t('pages.upload.inputUrlTip'),
|
||||
placeholder: t('pages.upload.httpPrefixTip')
|
||||
})
|
||||
}
|
||||
|
||||
function handleInputBoxValue (val: string) {
|
||||
if (val === '') return
|
||||
if (isUrl(val)) {
|
||||
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [
|
||||
{
|
||||
path: val
|
||||
}
|
||||
])
|
||||
} else {
|
||||
message.error(t('pages.upload.inputValidUrl'))
|
||||
}
|
||||
}
|
||||
|
||||
async function getDefaultPicBed () {
|
||||
const currentPicBed = await getConfig<string>(configPaths.picBed.current)
|
||||
picBedGlobal.value.forEach(item => {
|
||||
if (item.type === currentPicBed) {
|
||||
picBedName.value = item.name
|
||||
}
|
||||
})
|
||||
picBedConfigName.value = (await getConfig<string>(`picBed.${currentPicBed}._configName`)) || ''
|
||||
}
|
||||
|
||||
async function handleChangePicBed () {
|
||||
window.electron.sendRPC(IRPCActionType.SHOW_UPLOAD_PAGE_MENU)
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'UploadPage'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped src="./css/UploadPage.css"></style>
|
||||
|
||||
Reference in New Issue
Block a user