Feature(custom): rewrite setting page, WIP

This commit is contained in:
Kuingsmile
2025-08-06 11:19:19 +08:00
parent 32c3eaba12
commit 4b8bfded1d
182 changed files with 5536 additions and 3322 deletions

View File

@@ -4,6 +4,7 @@
:key="pageReloadCount"
>
<router-view />
<UIServiceProvider />
</div>
</template>
@@ -11,11 +12,12 @@
import type { IConfig } from 'piclist'
import { onBeforeMount, onMounted } from 'vue'
import UIServiceProvider from '@/components/ui/UIServiceProvider.vue'
import { useStore } from '@/hooks/useStore'
import { getConfig } from '@/utils/dataSender'
import { pageReloadCount } from '@/utils/global'
import { useAppStore } from './hooks/appStore'
import { useAppStore } from './hooks/useAppStore'
const store = useStore()
const appStore = useAppStore()

View File

@@ -1,6 +1,6 @@
import { getRawData } from '@/utils/common'
import { IRPCActionType } from '#/types/enum'
import { IStringKeyMap } from '#/types/types'
import { IRPCActionType } from '@/utils/enum'
import type { IStringKeyMap } from '#/types/types'
export default class ALLApi {
static async delete (configMap: IStringKeyMap): Promise<boolean> {

View File

@@ -110,7 +110,7 @@ import { reactive, ref, toRefs, watch } from 'vue'
import { useRoute } from 'vue-router'
import { getConfig } from '@/utils/dataSender'
import { IPicGoPluginConfig, IStringKeyMap } from '#/types/types'
import type { IPicGoPluginConfig, IStringKeyMap } from '#/types/types'
interface IProps {
config: any[]

View File

@@ -83,7 +83,7 @@ import { cloneDeep, union } from 'lodash-es'
import { reactive, ref, watch } from 'vue'
import { getConfig } from '@/utils/dataSender'
import { IPicGoPluginConfig, IStringKeyMap } from '#/types/types'
import type { IPicGoPluginConfig, IStringKeyMap } from '#/types/types'
interface IProps {
config: any[]

View File

@@ -24,7 +24,7 @@ import { Loading } from '@element-plus/icons-vue'
import { computed, onMounted, ref, watch } from 'vue'
import { getFileIconPath } from '@/manage/utils/common'
import { IRPCActionType } from '#/types/enum'
import { IRPCActionType } from '@/utils/enum'
const preSignedUrl = ref('')

View File

@@ -3,7 +3,7 @@ import { ElIcon, ElImage } from 'element-plus'
import { computed, defineComponent, onMounted, ref, watch } from 'vue'
import { getFileIconPath } from '@/manage/utils/common'
import { IRPCActionType } from '#/types/enum'
import { IRPCActionType } from '@/utils/enum'
export default defineComponent({
props: {

View File

@@ -511,8 +511,8 @@ import type { IBuildInCompressOptions, IBuildInWaterMarkOptions } from 'piclist'
import { computed, onBeforeMount, reactive, ref, toRaw } from 'vue'
import { useI18n } from 'vue-i18n'
import { configPaths } from '@/utils/configPaths'
import { getConfig, saveConfig } from '@/utils/dataSender'
import { configPaths } from '#/utils/configPaths'
const { t } = useI18n()
const imageProcessDialogVisible = defineModel<boolean>()

View File

@@ -25,7 +25,7 @@ import { computed, onMounted, ref, watch } from 'vue'
import { getFileIconPath } from '@/manage/utils/common'
import { getAuthHeader } from '@/manage/utils/digestAuth'
import { formatEndpoint } from '#/utils/common'
import { formatEndpoint } from '@/utils/common'
const base64Url = ref('')
const success = ref(false)

View File

@@ -4,7 +4,7 @@ import { computed, defineComponent, onMounted, ref, watch } from 'vue'
import { getFileIconPath } from '@/manage/utils/common'
import { getAuthHeader } from '@/manage/utils/digestAuth'
import { formatEndpoint } from '#/utils/common'
import { formatEndpoint } from '@/utils/common'
export default defineComponent({
props: {

View File

@@ -32,8 +32,8 @@ import type { IpcRendererEvent } from 'electron'
import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue'
import $bus from '@/utils/bus'
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '#/events/constants'
import { IShowInputBoxOption } from '#/types/types'
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant'
import type { IShowInputBoxOption } from '#/types/types'
const inputBoxValue = ref('')
const showInputBoxVisible = ref(false)

View File

@@ -218,14 +218,14 @@ import { pick } from 'lodash-es'
import { BadgeInfoIcon, CheckIcon, ChevronDownIcon, CopyIcon, DatabaseIcon, FolderIcon, PieChartIcon, PlugIcon, Settings, UploadIcon } from 'lucide-vue-next'
import QrcodeVue from 'qrcode.vue'
import pkg from 'root/package.json'
import { SHOW_MAIN_PAGE_QRCODE } from 'root/src/universal/events/constants'
import { computed, nextTick, onBeforeMount, reactive, Ref, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import * as config from '@/router/config'
import { SHOW_MAIN_PAGE_QRCODE } from '@/utils/constant'
import { getConfig } from '@/utils/dataSender'
import { IRPCActionType } from '@/utils/enum'
import { picBedGlobal, updatePicBedGlobal } from '@/utils/global'
import { IRPCActionType } from '#/types/enum'
import ThemeSwitcher from './ui/ThemeSwitcher.vue'
const version = ref(pkg.version)

View File

@@ -11,10 +11,9 @@
</template>
<script lang="ts" setup>
import { IToolboxItemCheckStatus } from '#/types/enum'
interface IProps {
status: IToolboxItemCheckStatus
status: string
value: any
handlerText: string
handler: (value: any) => void | Promise<void>

View File

@@ -19,10 +19,10 @@
import { CircleCloseFilled, Loading, SuccessFilled } from '@element-plus/icons-vue'
import { computed } from 'vue'
import { IToolboxItemCheckStatus } from '#/types/enum'
import { IToolboxItemCheckStatus } from '@/utils/enum'
interface IProps {
status: IToolboxItemCheckStatus
status: string
}
const props = defineProps<IProps>()

View File

@@ -0,0 +1,342 @@
<template>
<div
v-if="isOpen"
class="messagebox-overlay"
@click="onCancel"
>
<div
class="messagebox-container"
@click.stop
>
<div class="messagebox-header">
<h3 class="messagebox-title">
{{ title }}
</h3>
<button
v-if="showClose"
class="messagebox-close"
@click="onCancel"
>
×
</button>
</div>
<div class="messagebox-content">
<div
v-if="type"
class="messagebox-icon"
>
<component
:is="iconComponent"
:size="48"
/>
</div>
<div class="messagebox-message">
<p>{{ message }}</p>
</div>
</div>
<div
v-if="center"
class="messagebox-actions center"
>
<button
class="messagebox-btn cancel-btn"
@click="onCancel"
>
{{ cancelButtonText }}
</button>
<button
class="messagebox-btn confirm-btn"
:class="confirmButtonClass"
@click="onConfirm"
>
{{ confirmButtonText }}
</button>
</div>
<div
v-else
class="messagebox-actions"
>
<button
class="messagebox-btn confirm-btn"
:class="confirmButtonClass"
@click="onConfirm"
>
{{ confirmButtonText }}
</button>
<button
class="messagebox-btn cancel-btn"
@click="onCancel"
>
{{ cancelButtonText }}
</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { AlertTriangle, CheckCircle, Info, XCircle } from 'lucide-vue-next'
import { computed } from 'vue'
interface Props {
isOpen: boolean
title?: string
message: string
type?: 'info' | 'success' | 'warning' | 'error'
confirmButtonText?: string
cancelButtonText?: string
showClose?: boolean
center?: boolean
}
interface Emits {
(e: 'confirm'): void
(e: 'cancel'): void
}
const props = withDefaults(defineProps<Props>(), {
title: 'Confirm',
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
showClose: true,
center: false,
type: undefined
})
const emit = defineEmits<Emits>()
const iconComponent = computed(() => {
switch (props.type) {
case 'warning':
return AlertTriangle
case 'info':
return Info
case 'success':
return CheckCircle
case 'error':
return XCircle
default:
return Info
}
})
const confirmButtonClass = computed(() => {
switch (props.type) {
case 'warning':
case 'error':
return 'danger'
case 'success':
return 'success'
default:
return 'primary'
}
})
const onConfirm = () => {
emit('confirm')
}
const onCancel = () => {
emit('cancel')
}
</script>
<script lang="ts">
export default {
name: 'ConfirmMessageBox'
}
</script>
<style scoped>
.messagebox-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 2000;
}
.messagebox-container {
background: white;
border-radius: 0.75rem;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
max-width: 32rem;
width: 90%;
max-height: 80vh;
overflow: hidden;
}
:root.dark .messagebox-container,
:root.auto.dark .messagebox-container {
background: rgb(31 41 55);
border: 1px solid rgb(55 65 81);
}
.messagebox-header {
padding: 1.5rem 1.5rem 0 1.5rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.messagebox-title {
font-size: 1.25rem;
font-weight: 600;
color: rgb(17 24 39);
margin: 0;
}
:root.dark .messagebox-title,
:root.auto.dark .messagebox-title {
color: rgb(243 244 246);
}
.messagebox-close {
background: none;
border: none;
font-size: 1.5rem;
color: rgb(107 114 128);
cursor: pointer;
padding: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 0.25rem;
}
.messagebox-close:hover {
background: rgb(243 244 246);
color: rgb(17 24 39);
}
:root.dark .messagebox-close,
:root.auto.dark .messagebox-close {
color: rgb(156 163 175);
}
:root.dark .messagebox-close:hover,
:root.auto.dark .messagebox-close:hover {
background: rgb(55 65 81);
color: rgb(243 244 246);
}
.messagebox-content {
padding: 1rem 1.5rem;
display: flex;
gap: 1rem;
align-items: flex-start;
}
.messagebox-icon {
flex-shrink: 0;
color: rgb(107 114 128);
}
.messagebox-icon svg[data-lucide="alert-triangle"] {
color: rgb(245 158 11);
}
.messagebox-icon svg[data-lucide="info"] {
color: rgb(59 130 246);
}
.messagebox-icon svg[data-lucide="check-circle"] {
color: rgb(34 197 94);
}
.messagebox-icon svg[data-lucide="x-circle"] {
color: rgb(239 68 68);
}
.messagebox-message {
flex: 1;
}
.messagebox-message p {
color: rgb(107 114 128);
line-height: 1.6;
margin: 0;
}
:root.dark .messagebox-message p,
:root.auto.dark .messagebox-message p {
color: rgb(156 163 175);
}
.messagebox-actions {
display: flex;
gap: 0.75rem;
padding: 0 1.5rem 1.5rem 1.5rem;
justify-content: flex-end;
}
.messagebox-actions.center {
justify-content: center;
}
.messagebox-btn {
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 500;
border: none;
cursor: pointer;
min-width: 4rem;
}
.cancel-btn {
background: rgb(243 244 246);
color: rgb(75 85 99);
border: 1px solid rgb(209 213 219);
}
.cancel-btn:hover {
background: rgb(229 231 235);
}
:root.dark .cancel-btn,
:root.auto.dark .cancel-btn {
background: rgb(55 65 81);
color: rgb(209 213 219);
border-color: rgb(75 85 99);
}
:root.dark .cancel-btn:hover,
:root.auto.dark .cancel-btn:hover {
background: rgb(75 85 99);
}
.confirm-btn.primary {
background: rgb(59 130 246);
color: white;
}
.confirm-btn.primary:hover {
background: rgb(37 99 235);
}
.confirm-btn.danger {
background: rgb(239 68 68);
color: white;
}
.confirm-btn.danger:hover {
background: rgb(220 38 38);
}
.confirm-btn.success {
background: rgb(34 197 94);
color: white;
}
.confirm-btn.success:hover {
background: rgb(22 163 74);
}
</style>

View File

@@ -0,0 +1,260 @@
<template>
<Teleport to="body">
<div class="message-container">
<TransitionGroup
name="message"
tag="div"
>
<div
v-for="message in messages"
:key="message.id"
class="message-toast"
:class="getMessageClass(message.type)"
>
<div class="message-icon">
<component
:is="getIconComponent(message.type)"
:size="20"
/>
</div>
<div class="message-content">
{{ message.message }}
</div>
<button
v-if="message.showClose"
class="message-close"
@click="removeMessage(message.id)"
>
<X :size="16" />
</button>
</div>
</TransitionGroup>
</div>
</Teleport>
</template>
<script setup lang="ts">
import { AlertTriangle, CheckCircle, Info, X, XCircle } from 'lucide-vue-next'
import { reactive } from 'vue'
export interface MessageOptions {
message: string
type?: 'success' | 'warning' | 'info' | 'error'
duration?: number
showClose?: boolean
}
interface MessageItem extends MessageOptions {
id: string
timer?: ReturnType<typeof setTimeout>
}
const messages = reactive<MessageItem[]>([])
const getIconComponent = (type: MessageOptions['type']) => {
switch (type) {
case 'success':
return CheckCircle
case 'warning':
return AlertTriangle
case 'error':
return XCircle
default:
return Info
}
}
const getMessageClass = (type: MessageOptions['type']) => {
return `message-${type || 'info'}`
}
const removeMessage = (id: string) => {
const index = messages.findIndex(msg => msg.id === id)
if (index > -1) {
const message = messages[index]
if (message.timer) {
clearTimeout(message.timer)
}
messages.splice(index, 1)
}
}
const addMessage = (options: MessageOptions) => {
const id = `message-${Date.now()}-${Math.random()}`
const duration = options.duration ?? 3000
const showClose = options.showClose ?? true
const message: MessageItem = {
id,
...options,
showClose
}
if (duration > 0) {
message.timer = setTimeout(() => {
removeMessage(id)
}, duration)
}
messages.push(message)
return id
}
// Expose methods for external use
const success = (message: string, options?: Partial<MessageOptions>) => {
return addMessage({ message, type: 'success', ...options })
}
const error = (message: string, options?: Partial<MessageOptions>) => {
return addMessage({ message, type: 'error', ...options })
}
const warning = (message: string, options?: Partial<MessageOptions>) => {
return addMessage({ message, type: 'warning', ...options })
}
const info = (message: string, options?: Partial<MessageOptions>) => {
return addMessage({ message, type: 'info', ...options })
}
defineExpose({
success,
error,
warning,
info,
addMessage,
removeMessage
})
</script>
<script lang="ts">
export default {
name: 'MessageToast'
}
</script>
<style scoped>
.message-container {
position: fixed;
top: 34px;
right: 20px;
z-index: 3000;
pointer-events: none;
}
.message-toast {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
margin-bottom: 0.5rem;
border-radius: 0.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
max-width: 24rem;
pointer-events: all;
background: white;
border: 1px solid rgb(229 231 235);
}
:root.dark .message-toast,
:root.auto.dark .message-toast {
background: rgb(31 41 55);
border-color: rgb(55 65 81);
}
.message-info {
border-left: 4px solid rgb(59 130 246);
}
.message-info .message-icon {
color: rgb(59 130 246);
}
.message-success {
border-left: 4px solid rgb(34 197 94);
}
.message-success .message-icon {
color: rgb(34 197 94);
}
.message-warning {
border-left: 4px solid rgb(245 158 11);
}
.message-warning .message-icon {
color: rgb(245 158 11);
}
.message-error {
border-left: 4px solid rgb(239 68 68);
}
.message-error .message-icon {
color: rgb(239 68 68);
}
.message-icon {
flex-shrink: 0;
}
.message-content {
flex: 1;
color: rgb(75 85 99);
font-size: 0.875rem;
line-height: 1.25rem;
}
:root.dark .message-content,
:root.auto.dark .message-content {
color: rgb(209 213 219);
}
.message-close {
background: none;
border: none;
color: rgb(107 114 128);
cursor: pointer;
padding: 0.25rem;
border-radius: 0.25rem;
display: flex;
align-items: center;
justify-content: center;
}
.message-close:hover {
background: rgb(243 244 246);
color: rgb(75 85 99);
}
:root.dark .message-close,
:root.auto.dark .message-close {
color: rgb(156 163 175);
}
:root.dark .message-close:hover,
:root.auto.dark .message-close:hover {
background: rgb(55 65 81);
color: rgb(209 213 219);
}
/* Transition animations */
.message-enter-active,
.message-leave-active {
transition: all 0.3s ease;
}
.message-enter-from {
opacity: 0;
transform: translateX(100%);
}
.message-leave-to {
opacity: 0;
transform: translateX(100%);
}
.message-move {
transition: transform 0.3s ease;
}
</style>

View File

@@ -3,7 +3,7 @@ import { Monitor, Moon, Sun } from 'lucide-vue-next'
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/hooks/appStore'
import { useAppStore } from '@/hooks/useAppStore'
const { t } = useI18n()
const appStore = useAppStore()

View File

@@ -78,7 +78,7 @@ import type { IpcRendererEvent } from 'electron'
import { MinusIcon, PinIcon, ShrinkIcon, XIcon } from 'lucide-vue-next'
import { onBeforeMount, onBeforeUnmount, ref } from 'vue'
import { IRPCActionType } from '#/types/enum'
import { IRPCActionType } from '@/utils/enum'
const isShowprogress = ref(false)
const progress = ref(0)

View File

@@ -0,0 +1,101 @@
<template>
<div>
<!-- MessageToast component -->
<MessageToast ref="messageRef" />
<!-- ConfirmMessageBox component -->
<ConfirmMessageBox
:is-open="confirmVisible"
:title="confirmOptions.title"
:message="confirmOptions.message"
:type="confirmOptions.type"
:confirm-button-text="confirmOptions.confirmButtonText"
:cancel-button-text="confirmOptions.cancelButtonText"
:show-close="confirmOptions.showClose"
:center="confirmOptions.center"
@confirm="handleConfirm"
@cancel="handleCancel"
/>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import useConfirm, { type ConfirmOptions } from '@/hooks/useConfirm'
import useMessage from '@/hooks/useMessage'
import ConfirmMessageBox from './ConfirmMessageBox.vue'
import MessageToast from './MessageToast.vue'
const messageRef = ref<InstanceType<typeof MessageToast> | null>(null)
const confirmVisible = ref(false)
const confirmOptions = reactive<ConfirmOptions>({
message: '',
title: 'Confirm',
type: 'info',
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
showClose: true,
center: false
})
let confirmResolve: ((value: boolean) => void) | null = null
const handleConfirm = () => {
confirmVisible.value = false
if (confirmResolve) {
confirmResolve(true)
confirmResolve = null
}
}
const handleCancel = () => {
confirmVisible.value = false
if (confirmResolve) {
confirmResolve(false)
confirmResolve = null
}
}
const showConfirm = (options: ConfirmOptions): Promise<boolean> => {
return new Promise((resolve) => {
Object.assign(confirmOptions, {
title: 'Confirm',
type: 'info',
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
showClose: true,
center: false,
...options
})
confirmResolve = resolve
confirmVisible.value = true
})
}
onMounted(() => {
// Initialize message service
const { setMessageService } = useMessage()
if (messageRef.value) {
setMessageService({
success: messageRef.value.success,
error: messageRef.value.error,
warning: messageRef.value.warning,
info: messageRef.value.info
})
}
// Initialize confirm service
const { setConfirmService } = useConfirm()
setConfirmService({
confirm: showConfirm
})
})
</script>
<script lang="ts">
export default {
name: 'UIServiceProvider'
}
</script>

View File

View File

View File

@@ -1,6 +1,7 @@
import { IRPCActionType } from 'root/src/universal/types/enum'
import { onMounted, onUnmounted } from 'vue'
import { IRPCActionType } from '@/utils/enum'
export function useATagClick () {
const handleATagClick = (e: MouseEvent) => {
if (e.target instanceof HTMLAnchorElement) {

View File

@@ -1,7 +1,7 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { IStringKeyMap } from '#/types/types'
import type { IStringKeyMap } from '#/types/types'
export const useAppStore = defineStore('app', () => {
const settings = ref<IStringKeyMap>({

View File

@@ -0,0 +1,38 @@
import { ref } from 'vue'
export interface ConfirmOptions {
title?: string
message: string
type?: 'info' | 'success' | 'warning' | 'error'
confirmButtonText?: string
cancelButtonText?: string
showClose?: boolean
center?: boolean
}
interface ConfirmService {
confirm: (options: ConfirmOptions) => Promise<boolean>
}
const confirmServiceRef = ref<ConfirmService | null>(null)
export function useConfirm () {
const setConfirmService = (service: ConfirmService) => {
confirmServiceRef.value = service
}
const confirm = (options: ConfirmOptions): Promise<boolean> => {
if (confirmServiceRef.value) {
return confirmServiceRef.value.confirm(options)
}
console.warn('Confirm service not initialized')
return Promise.resolve(false)
}
return {
setConfirmService,
confirm
}
}
export default useConfirm

View File

@@ -0,0 +1,60 @@
import { ref } from 'vue'
import type { MessageOptions } from '@/components/ui/MessageToast.vue'
interface MessageService {
success: (message: string, options?: Partial<MessageOptions>) => string
error: (message: string, options?: Partial<MessageOptions>) => string
warning: (message: string, options?: Partial<MessageOptions>) => string
info: (message: string, options?: Partial<MessageOptions>) => string
}
const messageServiceRef = ref<MessageService | null>(null)
export function useMessage () {
const setMessageService = (service: MessageService) => {
messageServiceRef.value = service
}
const success = (message: string, options?: Partial<MessageOptions>) => {
if (messageServiceRef.value) {
return messageServiceRef.value.success(message, options)
}
console.warn('Message service not initialized')
return ''
}
const error = (message: string, options?: Partial<MessageOptions>) => {
if (messageServiceRef.value) {
return messageServiceRef.value.error(message, options)
}
console.warn('Message service not initialized')
return ''
}
const warning = (message: string, options?: Partial<MessageOptions>) => {
if (messageServiceRef.value) {
return messageServiceRef.value.warning(message, options)
}
console.warn('Message service not initialized')
return ''
}
const info = (message: string, options?: Partial<MessageOptions>) => {
if (messageServiceRef.value) {
return messageServiceRef.value.info(message, options)
}
console.warn('Message service not initialized')
return ''
}
return {
setMessageService,
success,
error,
warning,
info
}
}
export default useMessage

View File

@@ -1,4 +1,4 @@
import { IRPCActionType } from '#/types/enum'
import { IRPCActionType } from '@/utils/enum'
export function setCurrentLanguage (lang: string) {
window.electron.sendRPC(IRPCActionType.SET_CURRENT_LANGUAGE, lang)

View File

@@ -128,6 +128,50 @@
"isResizeByPercentHint": "Higher priority",
"resizePercent": "Resize Percentage (Enter 50 for 50%)"
}
},
"settings": {
"title": "Settings",
"description": "Configure the PicList application",
"docs": "Documentation",
"clickToSet": "Click to set",
"system": {
"title": "General",
"languageAndAppearance": "Language and Appearance",
"chooseLanguage": "Choose Language",
"startMode": "Startup Mode",
"quietMode": "Quiet Mode",
"miniMode": "Mini Window",
"mainMode": "Main Window",
"noTrayMode": "Hide Tray",
"windowBehavior": "Window Behavior",
"isHideDock": "Hide Dock Icon",
"mainWindowSize": "Set Main Window Size (Requires Restart)",
"autoCloseMiniWindow": "Close Mini Window when Opening Main Window",
"autoCloseMainWindow": "Close Main Window when Opening Mini Window",
"miniWindowOnTop": "Mini Window Always on Top",
"isCustomMiniIcon": "Custom Mini Window Icon",
"customMiniIconPath": "Custom Mini Window Icon Path",
"startupAndShortcuts": "Startup and Shortcuts",
"autoLaunch": "Auto Launch",
"setShortCuts": "Set Shortcuts"
},
"sync": {
"title": "Configuration/Sync",
"syncConfiguration": "Sync Configuration",
"syncEndpointConfig": "Sync Endpoint Configuration",
"upDownloadSettings": "Upload/Download Settings",
"migrateFromPicGo": "Migrate from PicGo",
"fileManagement": "File Management"
},
"upload": {
"title": "Upload"
},
"advanced": {
"title": "Advanced"
},
"update": {
"title": "Update"
}
}
},
"OPEN_MAIN_WINDOW": "Open Main Window",
@@ -145,15 +189,8 @@
"TOOLBOX_RE_SCAN": "Re scanning",
"TOOLBOX_START_FIX": "Start fixing",
"TOOLBOX_SUCCESS_TIPS": "Congratulations, no problems were found",
"MANUAL_PAGE_OPEN_TIP": "Please select the way to open the manual",
"MANUAL_PAGE_OPEN_TIP_TITLE": "Tips",
"MANUAL_PAGE_OPEN_BY_BROWSER": "Browser",
"MANUAL_PAGE_OPEN_BY_BUILD_IN": "Built-in Window",
"MANUAL_PAGE_OPEN_SETTING_TIP": "Select the way to open the manual",
"UPLOAD_VIEW_HINT": "Click to open picbeds settings",
"REFRESH": "Refresh",
"MANUAL": "Manual",
"PICLIST_SETTINGS": "Settings",
"PLUGIN_SETTINGS": "Plugins",
"CHOOSE_PICBED": "Choose Picbed",
"COPY_PICBED_CONFIG": "Copy Picbed Config",
@@ -191,11 +228,6 @@
"SETTINGS_MIGRATE_FROM_PICGO_CONTENT": "Migrate from PicGo will overwrite your current settings and gallery, do you want to continue?",
"SETTINGS_MIGRATE_FROM_PICGO_SUCCESS": "Import succeed, please restart PicList",
"SETTINGS_MIGRATE_FROM_PICGO_FAILED": "Import failed",
"SETTINGS_START_MODE": "Default Start Mode",
"SETTINGS_START_MODE_MINI": "Mini Window",
"SETTINGS_START_MODE_MAIN": "Main Window",
"SETTINGS_START_MODE_NO_TRAY": "No Tray",
"SETTINGS_START_MODE_QUIET": "Quiet Mode",
"SETTINGS_CLICK_TO_OPEN": "Click to Open",
"SETTINGS_SET_LOG_FILE": "Set Log File",
"SETTINGS_CLICK_TO_SET": "Click to Set",
@@ -354,11 +386,6 @@
"SETTINGS_SYNC_MANAGE_CONFIG": "Manage configuration",
"SETTINGS_AUTO_IMPORT": "Auto import config in manage page",
"SETTINGS_AUTO_IMPORT_SELECT_PICBED": "Select picbed",
"SETTINGS_TAB_SYSTEM": "System",
"SETTINGS_TAB_SYNC_CONFIG": "Configuration",
"SETTINGS_TAB_UPLOAD": "Upload",
"SETTINGS_TAB_ADVANCED": "Advanced",
"SETTINGS_TAB_UPDATE": "Update",
"BUILTIN_CLIPBOARD_TIPS": "Use builtin clipboard function to upload instead of using scripts",
"SHORTCUT_NAME": "Shortcut Name",
"SHORTCUT_BIND": "Shortcut Binding",

View File

@@ -128,6 +128,55 @@
"isResizeByPercentHint": "优先级更高",
"resizePercent": "调整比例 (输入 50 表示 50%)"
}
},
"settings": {
"title": "设置",
"description": "配置 PicList 应用程序",
"docs": "文档",
"clickToSet": "点击设置",
"clickToOpen": "点击打开",
"system": {
"title": "通用",
"languageAndAppearance": "语言和外观",
"chooseLanguage": "选择语言",
"startMode": "启动模式",
"quietMode": "静默模式",
"miniMode": "迷你窗口",
"mainMode": "主窗口",
"noTrayMode": "隐藏托盘",
"windowBehavior": "窗口行为",
"isHideDock": "是否隐藏 Dock 图标",
"mainWindowSize": "设置主窗口大小(需重启)",
"autoCloseMiniWindow": "打开主窗口时关闭迷你窗口",
"autoCloseMainWindow": "打开迷你窗口时关闭主窗口",
"miniWindowOnTop": "迷你窗口置顶",
"isCustomMiniIcon": "是否自定义迷你窗口图标",
"customMiniIconPath": "自定义迷你窗口图标路径",
"startupAndShortcuts": "启动和快捷键",
"autoLaunch": "开机自启",
"setShortCuts": "设置快捷键"
},
"sync": {
"title": "配置/同步",
"syncConfiguration": "同步配置",
"syncEndpointConfig": "同步方案配置",
"upDownloadSettings": "上传下载配置文件",
"migrateFromPicGo": "从PicGo迁移",
"fileManagement": "文件管理",
"openConfigFile": "打开配置文件",
"openConfigFileDir": "打开配置文件目录",
"autoImportInManage": "管理页面自动导入配置"
},
"upload": {
"title": "上传",
"uploadBehavior": "上传行为"
},
"advanced": {
"title": "高级"
},
"update": {
"title": "更新"
}
}
},
"OPEN_MAIN_WINDOW": "打开主窗口",
@@ -145,15 +194,8 @@
"TOOLBOX_RE_SCAN": "重新扫描",
"TOOLBOX_START_FIX": "开始修复",
"TOOLBOX_SUCCESS_TIPS": "恭喜你,没有检查出问题",
"MANUAL_PAGE_OPEN_TIP": "请选择打开方式",
"MANUAL_PAGE_OPEN_TIP_TITLE": "Tips",
"MANUAL_PAGE_OPEN_BY_BROWSER": "浏览器",
"MANUAL_PAGE_OPEN_BY_BUILD_IN": "内置窗口",
"MANUAL_PAGE_OPEN_SETTING_TIP": "选择手册打开方式",
"UPLOAD_VIEW_HINT": "点击打开图床设置",
"REFRESH": "刷新",
"MANUAL": "手册",
"PICLIST_SETTINGS": "设置",
"PLUGIN_SETTINGS": "插件",
"CHOOSE_PICBED": "选择图床",
"COPY_PICBED_CONFIG": "复制图床配置",
@@ -191,11 +233,6 @@
"SETTINGS_MIGRATE_FROM_PICGO_CONTENT": "即将导入PicGo的配置文件和相册, 这将覆盖当前的配置文件和相册, 是否继续?",
"SETTINGS_MIGRATE_FROM_PICGO_SUCCESS": "导入成功, 请重启PicList生效",
"SETTINGS_MIGRATE_FROM_PICGO_FAILED": "导入失败",
"SETTINGS_START_MODE": "启动模式",
"SETTINGS_START_MODE_MINI": "mini窗口",
"SETTINGS_START_MODE_MAIN": "主窗口",
"SETTINGS_START_MODE_NO_TRAY": "隐藏托盘",
"SETTINGS_START_MODE_QUIET": "静默启动",
"SETTINGS_CLICK_TO_OPEN": "点击打开",
"SETTINGS_SET_LOG_FILE": "设置日志文件",
"SETTINGS_CLICK_TO_SET": "点击设置",
@@ -355,11 +392,6 @@
"SETTINGS_SYNC_MANAGE_CONFIG": "管理配置",
"SETTINGS_AUTO_IMPORT": "管理页面自动导入配置",
"SETTINGS_AUTO_IMPORT_SELECT_PICBED": "选择需要开启自动导入的图床",
"SETTINGS_TAB_SYSTEM": "系统设置",
"SETTINGS_TAB_SYNC_CONFIG": "同步与配置",
"SETTINGS_TAB_UPLOAD": "上传设置",
"SETTINGS_TAB_ADVANCED": "高级设置",
"SETTINGS_TAB_UPDATE": "更新",
"SHORTCUT_NAME": "快捷键名称",
"SHORTCUT_BIND": "快捷键绑定",
"SHORTCUT_STATUS": "状态",

View File

@@ -128,6 +128,50 @@
"isResizeByPercentHint": "優先級更高",
"resizePercent": "調整比例 (輸入 50 表示 50%)"
}
},
"settings": {
"title": "設定",
"description": "配置 PicList 應用程序",
"docs": "文檔",
"clickToSet": "點擊設置",
"system": {
"title": "通用",
"languageAndAppearance": "語言和外觀",
"chooseLanguage": "選擇語言",
"startMode": "啟動模式",
"quietMode": "靜默模式",
"miniMode": "迷你窗口",
"mainMode": "主窗口",
"noTrayMode": "隱藏托盤",
"windowBehavior": "窗口行為",
"isHideDock": "是否隱藏 Dock 圖標",
"mainWindowSize": "設置主窗口大小(需重啟)",
"autoCloseMiniWindow": "打開主窗口時關閉迷你窗口",
"autoCloseMainWindow": "打開迷你窗口時關閉主窗口",
"miniWindowOnTop": "迷你窗口置頂",
"isCustomMiniIcon": "是否自定義迷你窗口圖標",
"customMiniIconPath": "自定義迷你窗口圖標路徑",
"startupAndShortcuts": "啟動和快捷鍵",
"autoLaunch": "開機自啟",
"setShortCuts": "設置快捷鍵"
},
"sync": {
"title": "配置/同步",
"syncConfiguration": "同步配置",
"syncEndpointConfig": "同步方案配置",
"upDownloadSettings": "上傳下載配置文件",
"migrateFromPicGo": "從PicGo遷移",
"fileManagement": "文件管理"
},
"upload": {
"title": "上傳"
},
"advanced": {
"title": "高級"
},
"update": {
"title": "更新"
}
}
},
"OPEN_MAIN_WINDOW": "打開主視窗",
@@ -145,15 +189,8 @@
"TOOLBOX_RE_SCAN": "重新掃描",
"TOOLBOX_START_FIX": "開始修復",
"TOOLBOX_SUCCESS_TIPS": "恭喜你,沒有檢查出問題",
"MANUAL_PAGE_OPEN_TIP": "請選擇打開方式",
"MANUAL_PAGE_OPEN_TIP_TITLE": "Tips",
"MANUAL_PAGE_OPEN_BY_BROWSER": "瀏覽器",
"MANUAL_PAGE_OPEN_BY_BUILD_IN": "內置窗口",
"MANUAL_PAGE_OPEN_SETTING_TIP": "選擇打開手冊方式",
"UPLOAD_VIEW_HINT": "點擊打開圖床設定",
"REFRESH": "刷新",
"MANUAL": "手冊",
"PICLIST_SETTINGS": "設定",
"PLUGIN_SETTINGS": "插件",
"CHOOSE_PICBED": "選擇圖床",
"COPY_PICBED_CONFIG": "複製圖床設定",
@@ -191,11 +228,6 @@
"SETTINGS_MIGRATE_FROM_PICGO_CONTENT": "即將導入PicGo的設定文件和相冊, 這將會覆蓋當前的設定, 是否繼續?",
"SETTINGS_MIGRATE_FROM_PICGO_SUCCESS": "導入成功, 請重啟應用",
"SETTINGS_MIGRATE_FROM_PICGO_FAILED": "導入失敗",
"SETTINGS_START_MODE": "啟動模式",
"SETTINGS_START_MODE_MINI": "mini視窗",
"SETTINGS_START_MODE_MAIN": "主視窗",
"SETTINGS_START_MODE_QUIET": "靜默啟動",
"SETTINGS_START_MODE_NO_TRAY": "隐藏托盘",
"SETTINGS_CLICK_TO_OPEN": "點擊打開",
"SETTINGS_SET_LOG_FILE": "設定記錄檔案",
"SETTINGS_CLICK_TO_SET": "點擊設定",
@@ -355,11 +387,6 @@
"SETTINGS_SYNC_MANAGE_CONFIG": "管理配置",
"SETTINGS_AUTO_IMPORT": "管理頁面自動導入配置",
"SETTINGS_AUTO_IMPORT_SELECT_PICBED": "選擇需要開啟自動導入的圖床",
"SETTINGS_TAB_SYSTEM": "系統設置",
"SETTINGS_TAB_SYNC_CONFIG": "同步與配置",
"SETTINGS_TAB_UPLOAD": "上傳設置",
"SETTINGS_TAB_ADVANCED": "高級設置",
"SETTINGS_TAB_UPDATE": "更新",
"SHORTCUT_NAME": "快捷鍵名稱",
"SHORTCUT_BIND": "快捷鍵綁定",
"SHORTCUT_STATUS": "狀態",

View File

@@ -22,7 +22,7 @@ import router from '@/router'
import { store } from '@/store'
import db from '@/utils/db'
type MessageSchema = typeof en
type MessageSchema = typeof zhCN
window.electron.setVisualZoomLevelLimits(1, 1)
@@ -33,7 +33,7 @@ app.config.globalProperties.triggerRPC = window.electron.triggerRPC
app.config.globalProperties.sendRPC = window.electron.sendRPC
app.config.globalProperties.sendToMain = window.electron.sendToMain
const i18n = createI18n<MessageSchema, 'en' | 'zh-CN' | 'zh-TW'>({
const i18n = createI18n<[MessageSchema], 'en' | 'zh-CN' | 'zh-TW'>({
legacy: false,
locale: localStorage.getItem('currentLanguage') || 'zh-CN',
fallbackLocale: 'zh-CN',

View File

@@ -1589,11 +1589,11 @@ import {
import { getConfig, saveConfig } from '@/manage/utils/dataSender'
import { textFileExt } from '@/manage/utils/textfile'
import { videoExt } from '@/manage/utils/videofile'
import { IRPCActionType } from '#/types/enum'
import { IDownloadTask, IUploadTask } from '#/types/manage'
import { IStringKeyMap } from '#/types/types'
import { trimPath } from '#/utils/common'
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
import { trimPath } from '@/utils/common'
import { IRPCActionType } from '@/utils/enum'
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/utils/static'
import type { IDownloadTask, IUploadTask } from '#/types/manage'
import type { IStringKeyMap } from '#/types/types'
const { t } = useI18n()
/*

View File

@@ -273,10 +273,10 @@ import { useManageStore } from '@/manage/store/manageStore'
import { formObjToTableData } from '@/manage/utils/common'
import { supportedPicBedList } from '@/manage/utils/constants'
import { getConfig, removeConfig, saveConfig } from '@/manage/utils/dataSender'
import { formatEndpoint, isNeedToShorten, safeSliceF } from '@/utils/common'
import { getConfig as getPicBedsConfig } from '@/utils/dataSender'
import { IRPCActionType } from '#/types/enum'
import { IStringKeyMap, IUploaderConfigListItem } from '#/types/types'
import { formatEndpoint, isNeedToShorten, safeSliceF } from '#/utils/common'
import { IRPCActionType } from '@/utils/enum'
import type { IStringKeyMap, IUploaderConfigListItem } from '#/types/types'
const { t } = useI18n()
const manageStore = useManageStore()

View File

@@ -279,7 +279,6 @@ import {
Tools
} from '@element-plus/icons-vue'
import { ElNotification } from 'element-plus'
import { IRPCActionType } from 'root/src/universal/types/enum'
import { computed, onBeforeMount, reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
@@ -287,7 +286,8 @@ import { useRoute, useRouter } from 'vue-router'
import { useManageStore } from '@/manage/store/manageStore'
import { supportedPicBedList } from '@/manage/utils/constants'
import { newBucketConfig } from '@/manage/utils/newBucketConfig'
import { IStringKeyMap } from '#/types/types'
import { IRPCActionType } from '@/utils/enum'
import type { IStringKeyMap } from '#/types/types'
const { t } = useI18n()
const manageStore = useManageStore() as any

View File

@@ -225,7 +225,6 @@
<script lang="ts" setup>
import { Folder, InfoFilled } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { IRPCActionType } from 'root/src/universal/types/enum'
import { onBeforeMount, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
@@ -233,7 +232,8 @@ import DynamicSwitch from '@/manage/components/DynamicSwitch.vue'
import { fileCacheDbInstance } from '@/manage/store/bucketFileDb'
import { customRenameFormatTable, formatFileSize } from '@/manage/utils/common'
import { getConfig, saveConfig } from '@/manage/utils/dataSender'
import { IStringKeyMap } from '#/types/types'
import { IRPCActionType } from '@/utils/enum'
import type { IStringKeyMap } from '#/types/types'
const { t } = useI18n()
const form = ref<IStringKeyMap>({

View File

@@ -1,6 +1,6 @@
import Dexie, { Table } from 'dexie'
import { IStringKeyMap } from '#/types/types'
import type { IStringKeyMap } from '#/types/types'
/*
* create a database for bucket file cache

View File

@@ -1,7 +1,7 @@
import { defineStore } from 'pinia'
import { getConfig } from '@/manage/utils/dataSender'
import { IStringKeyMap } from '#/types/types'
import type { IStringKeyMap } from '#/types/types'
export const useManageStore = defineStore('manageConfig', {
state: () => {

View File

@@ -1,4 +1,4 @@
import { IStringKeyMap } from '#/types/types'
import type { IStringKeyMap } from '#/types/types'
const AliyunAreaCodeName: IStringKeyMap = {
'oss-cn-hangzhou': '华东1(杭州)',

View File

@@ -2,8 +2,19 @@ import { v4 as uuidv4 } from 'uuid'
import { getConfig } from '@/manage/utils/dataSender'
import { availableIconList } from '@/manage/utils/icon'
import { IStringKeyMap } from '#/types/types'
import { handleUrlEncode, isNeedToShorten, safeSliceF } from '#/utils/common'
import { isNeedToShorten, safeSliceF } from '@/utils/common'
import type { IStringKeyMap } from '#/types/types'
export const isUrlEncode = (url: string): boolean => {
url = url || ''
try {
return url !== decodeURI(url)
} catch {
return false
}
}
export const handleUrlEncode = (url: string): string => (isUrlEncode(url) ? url : encodeURI(url))
export function randomStringGenerator (length: number): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'

View File

@@ -3,7 +3,7 @@ import { createI18n } from 'vue-i18n'
import en from '@/i18n/locales/en.json'
import zhCN from '@/i18n/locales/zh-CN.json'
import zhTW from '@/i18n/locales/zh-TW.json'
import { IStringKeyMap } from '#/types/types'
import type { IStringKeyMap } from '#/types/types'
type MessageSchema = typeof en
const i18n = createI18n<MessageSchema, 'en' | 'zh-CN' | 'zh-TW'>({

View File

@@ -1,5 +1,5 @@
import { IRPCActionType } from '#/types/enum'
import { IObj } from '#/types/types'
import { IRPCActionType } from '@/utils/enum'
import type { IObj } from '#/types/types'
export function saveConfig (config: IObj | string, value?: any) {
const configObj = typeof config === 'string' ? { [config]: value } : config

View File

@@ -1,4 +1,4 @@
import { IStringKeyMap } from '#/types/types'
import type { IStringKeyMap } from '#/types/types'
const AUTH_KEY_VALUE_RE = /(\w+)=["']?([^'"]{1,10000})["']?/
let NC = 0

View File

@@ -3,7 +3,7 @@ import { createI18n } from 'vue-i18n'
import en from '@/i18n/locales/en.json'
import zhCN from '@/i18n/locales/zh-CN.json'
import zhTW from '@/i18n/locales/zh-TW.json'
import { IStringKeyMap } from '#/types/types'
import type { IStringKeyMap } from '#/types/types'
import { AliyunAreaCodeName, QiniuAreaCodeName, TencentAreaCodeName } from './bucketConfigCons'
type MessageSchema = typeof en

View File

@@ -475,13 +475,13 @@ import { onBeforeRouteUpdate } from 'vue-router'
import ALLApi from '@/apis/allApi'
import { customRenameFormatTable, customStrMatch, customStrReplace } from '@/manage/utils/common'
import { configPaths } from '@/utils/configPaths'
import { getConfig, saveConfig } from '@/utils/dataSender'
import $$db from '@/utils/db'
import { IPasteStyle, IRPCActionType } from '@/utils/enum'
import { picBedGlobal } from '@/utils/global'
import { IPasteStyle, IRPCActionType } from '#/types/enum'
import { ICheckBoxValueType, IGalleryItem, ImgInfo, IObj, IObjT } from '#/types/types'
import { configPaths } from '#/utils/configPaths'
import { picBedsCanbeDeleted } from '#/utils/static'
import { picBedsCanbeDeleted } from '@/utils/static'
import type { ICheckBoxValueType, IGalleryItem, ImgInfo, IObj, IObjT } from '#/types/types'
const { t } = useI18n()
type IResult<T> = T & {

View File

@@ -36,16 +36,16 @@
<script lang="ts" setup>
import type { IpcRendererEvent } from 'electron'
import { ElMessage as $message } from 'element-plus'
import { ElMessage } from 'element-plus'
import type { IConfig } from 'piclist'
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { isUrl } from '@/utils/common'
import { getConfig } from '@/utils/dataSender'
import { IRPCActionType } from '@/utils/enum'
import { osGlobal } from '@/utils/global'
import { IRPCActionType } from '#/types/enum'
import { IFileWithPath } from '#/types/types'
import { isUrl } from '#/utils/common'
import type { IFileWithPath } from '#/types/types'
const { t } = useI18n()
const logoPath = ref('')
@@ -117,7 +117,7 @@ function onDrop (e: DragEvent) {
if (isUrl(str)) {
window.electron.sendRPC(IRPCActionType.UPLOAD_CHOOSED_FILES, [{ path: str }])
} else {
$message.error(t('TIPS_DRAG_VALID_PICTURE_OR_URL'))
ElMessage.error(t('TIPS_DRAG_VALID_PICTURE_OR_URL'))
}
}
}
@@ -135,7 +135,7 @@ function handleURLDrag (items: DataTransferItemList, dataTransfer: DataTransfer)
}
])
} else {
$message.error(t('TIPS_DRAG_VALID_PICTURE_OR_URL'))
ElMessage.error(t('TIPS_DRAG_VALID_PICTURE_OR_URL'))
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -239,18 +239,18 @@ import { computed, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref, toR
import { useI18n } from 'vue-i18n'
import ConfigForm from '@/components/ConfigFormForPlugin.vue'
import { getConfig, saveConfig } from '@/utils/dataSender'
import { osGlobal, updatePicBedGlobal } from '@/utils/global'
import { handleStreamlinePluginName } from '@/utils/common'
import { configPaths } from '@/utils/configPaths'
import {
PICGO_CONFIG_PLUGIN,
PICGO_HANDLE_PLUGIN_DONE,
PICGO_HANDLE_PLUGIN_ING,
PICGO_TOGGLE_PLUGIN
} from '#/events/constants'
import { IRPCActionType } from '#/types/enum'
import { INPMSearchResultObject, IPicGoPlugin } from '#/types/types'
import { handleStreamlinePluginName } from '#/utils/common'
import { configPaths } from '#/utils/configPaths'
} from '@/utils/constant'
import { getConfig, saveConfig } from '@/utils/dataSender'
import { IRPCActionType } from '@/utils/enum'
import { osGlobal, updatePicBedGlobal } from '@/utils/global'
import type { INPMSearchResultObject, IPicGoPlugin } from '#/types/types'
const { t } = useI18n()
const $confirm = ElMessageBox.confirm

View File

@@ -56,7 +56,7 @@ import type { IpcRendererEvent } from 'electron'
import type { FormInstance } from 'element-plus'
import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue'
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME } from '#/events/constants'
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME } from '@/utils/constant'
const id = ref<string | null>(null)
const formRef = ref<FormInstance>()

View File

@@ -116,11 +116,11 @@
<script lang="ts" setup>
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
import { configPaths } from '@/utils/configPaths'
import { getConfig } from '@/utils/dataSender'
import { IRPCActionType } from '@/utils/enum'
import keyBinding from '@/utils/key-binding'
import { IRPCActionType } from '#/types/enum'
import { IShortKeyConfig, IShortKeyConfigs } from '#/types/types'
import { configPaths } from '#/utils/configPaths'
import type { IShortKeyConfig, IShortKeyConfigs } from '#/types/types'
const list = ref<IShortKeyConfig[]>([])
const keyBindingVisible = ref(false)

View File

@@ -100,14 +100,14 @@ import { useI18n } from 'vue-i18n'
import ToolboxHandler from '@/components/ToolboxHandler.vue'
import ToolboxStatusIcon from '@/components/ToolboxStatusIcon.vue'
import { IRPCActionType, IToolboxItemCheckStatus, IToolboxItemType } from '#/types/enum'
import { IToolboxCheckRes } from '#/types/rpc'
import { IToolboxMap } from '#/types/view'
import { IRPCActionType, IToolboxItemCheckStatus, IToolboxItemType } from '@/utils/enum'
import type { IToolboxCheckRes } from '#/types/rpc'
import type { IToolboxMap } from '#/types/view'
const { t } = useI18n()
const $confirm = ElMessageBox.confirm
const defaultLogo = ref('/roundLogo.png')
const activeTypes = ref<IToolboxItemType[]>([])
const activeTypes = ref<string[]>([])
const fixList = reactive<IToolboxMap>({
[IToolboxItemType.IS_CONFIG_FILE_BROKEN]: {
title: t('TOOLBOX_CHECK_CONFIG_FILE_BROKEN'),
@@ -139,7 +139,7 @@ const fixList = reactive<IToolboxMap>({
const progress = computed(() => {
const total = Object.keys(fixList).length
const done = Object.keys(fixList).filter(key => {
const status = fixList[key as IToolboxItemType].status
const status = fixList[key].status
return status !== IToolboxItemCheckStatus.INIT && status !== IToolboxItemCheckStatus.LOADING
}).length
return (done / total) * 100
@@ -147,22 +147,22 @@ const progress = computed(() => {
const isAllSuccess = computed(() => {
return Object.keys(fixList).every(key => {
const status = fixList[key as IToolboxItemType].status
const status = fixList[key].status
return status === IToolboxItemCheckStatus.SUCCESS
})
})
const isLoading = computed(() => {
return Object.keys(fixList).some(key => {
const status = fixList[key as IToolboxItemType].status
const status = fixList[key].status
return status === IToolboxItemCheckStatus.LOADING
})
})
const canFixLength = computed(() => {
return Object.keys(fixList).filter(key => {
const status = fixList[key as IToolboxItemType].status
return status === IToolboxItemCheckStatus.ERROR && !fixList[key as IToolboxItemType].hasNoFixMethod
const status = fixList[key].status
return status === IToolboxItemCheckStatus.ERROR && !fixList[key].hasNoFixMethod
}).length
})
@@ -182,9 +182,9 @@ window.electron.ipcRendererOn(IRPCActionType.TOOLBOX_CHECK_RES, toolboxCheckResH
const handleCheck = () => {
activeTypes.value = []
Object.keys(fixList).forEach(key => {
fixList[key as IToolboxItemType].status = IToolboxItemCheckStatus.LOADING
fixList[key as IToolboxItemType].msg = ''
fixList[key as IToolboxItemType].value = ''
fixList[key].status = IToolboxItemCheckStatus.LOADING
fixList[key].msg = ''
fixList[key].value = ''
})
window.electron.sendRPC(IRPCActionType.TOOLBOX_CHECK)
}
@@ -193,11 +193,11 @@ const handleFix = async () => {
const fixRes = await Promise.all(
Object.keys(fixList)
.filter(key => {
const status = fixList[key as IToolboxItemType].status
return status === IToolboxItemCheckStatus.ERROR && !fixList[key as IToolboxItemType].hasNoFixMethod
const status = fixList[key].status
return status === IToolboxItemCheckStatus.ERROR && !fixList[key].hasNoFixMethod
})
.map(async key => {
return window.electron.triggerRPC<IToolboxCheckRes>(IRPCActionType.TOOLBOX_CHECK_FIX, key as IToolboxItemType)
return window.electron.triggerRPC<IToolboxCheckRes>(IRPCActionType.TOOLBOX_CHECK_FIX, key)
})
)

View File

@@ -66,12 +66,12 @@ import type { IpcRendererEvent } from 'electron'
import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { handleUrlEncode } from '@/utils/common'
import { configPaths } from '@/utils/configPaths'
import { getConfig } from '@/utils/dataSender'
import $$db from '@/utils/db'
import { IPasteStyle, IRPCActionType, IWindowList } from '#/types/enum'
import { ImgInfo } from '#/types/types'
import { handleUrlEncode } from '#/utils/common'
import { configPaths } from '#/utils/configPaths'
import { IPasteStyle, IRPCActionType, IWindowList } from '@/utils/enum'
import type { ImgInfo } from '#/types/types'
const { t } = useI18n()
@@ -117,7 +117,7 @@ const formatCustomLink = (customLink: string, item: ImgInfo) => {
}
async function copyTheLink (item: ImgInfo) {
const pasteStyle = (await getConfig<IPasteStyle>(configPaths.settings.pasteStyle)) || IPasteStyle.MARKDOWN
const pasteStyle = (await getConfig<string>(configPaths.settings.pasteStyle)) || IPasteStyle.MARKDOWN
const customLink = await getConfig<string>(configPaths.settings.customLink)
const txt = await pasteTemplate(pasteStyle, item, customLink)
window.electron.clipboard.writeText(txt)
@@ -127,7 +127,7 @@ async function copyTheLink (item: ImgInfo) {
}
}
async function pasteTemplate (style: IPasteStyle, item: ImgInfo, customLink: string | undefined) {
async function pasteTemplate (style: string, item: ImgInfo, customLink: string | undefined) {
let url = item.url || item.imgUrl
if (item.type === 'aws-s3' || item.type === 'aws-s3-plist') {
url = item.imgUrl || item.url || ''
@@ -141,7 +141,7 @@ async function pasteTemplate (style: IPasteStyle, item: ImgInfo, customLink: str
}
notification.body = url
const _customLink = customLink || '![$fileName]($url)'
const tpl = {
const tpl: Record<string, string> = {
markdown: `![](${url})`,
HTML: `<img src="${url}"/>`,
URL: url,

View File

@@ -6,7 +6,7 @@
<div class="provider-section">
<button
class="provider-button"
:title="$t('pages.upload.uploadViewHint')"
:title="t('pages.upload.uploadViewHint')"
@click="handlePicBedNameClick(picBedName, picBedConfigName)"
>
<div class="provider-info">
@@ -25,14 +25,14 @@
@click="handleImageProcess"
>
<Settings :size="16" />
<span>{{ $t('pages.upload.imageProcessName') }}</span>
<span>{{ t('pages.upload.imageProcessName') }}</span>
</button>
<button
class="action-button"
@click="handleChangePicBed"
>
<DatabaseIcon :size="16" />
<span>{{ $t('pages.upload.changePicBed') }}</span>
<span>{{ t('pages.upload.changePicBed') }}</span>
</button>
</div>
</div>
@@ -54,13 +54,13 @@
</div>
<div class="upload-text">
<h3 class="upload-title">
{{ $t('pages.upload.dragFileToHere') }}
{{ t('pages.upload.dragFileToHere') }}
</h3>
<p class="upload-subtitle">
{{ $t('pages.upload.clickToUpload') }}
{{ t('pages.upload.clickToUpload') }}
</p>
<div class="upload-formats">
<span class="format-label">{{ $t('pages.upload.uploadHint') }}</span>
<span class="format-label">{{ t('pages.upload.uploadHint') }}</span>
</div>
</div>
</div>
@@ -88,7 +88,7 @@
/>
</div>
<span class="progress-text">
{{ showError ? $t('pages.upload.uploadFailed') : `${progress}%` }}
{{ showError ? t('pages.upload.uploadFailed') : `${progress}%` }}
</span>
</div>
</transition>
@@ -98,7 +98,7 @@
<div class="upload-card actions-card">
<div class="card-header">
<h4 class="card-title">
{{ $t('pages.upload.quickUpload') }}
{{ t('pages.upload.quickUpload') }}
</h4>
</div>
<div class="quick-actions">
@@ -107,14 +107,14 @@
@click="uploadClipboardFiles"
>
<ClipboardIcon :size="20" />
<span>{{ $t('pages.upload.clipboardPicture') }}</span>
<span>{{ t('pages.upload.clipboardPicture') }}</span>
</button>
<button
class="quick-action-button"
@click="uploadURLFiles"
>
<LinkIcon :size="20" />
<span>{{ $t('pages.upload.urlUpload') }}</span>
<span>{{ t('pages.upload.urlUpload') }}</span>
</button>
</div>
</div>
@@ -123,13 +123,13 @@
<div class="upload-card settings-card">
<div class="card-header">
<h4 class="card-title">
{{ $t('pages.upload.linkFormat') }}
{{ 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>
<label class="setting-label">{{ t('pages.upload.outputFormat') }}</label>
<div class="format-buttons">
<button
v-for="(format, key) in pasteFormatList"
@@ -146,21 +146,21 @@
<!-- URL Length Options -->
<div class="setting-group">
<label class="setting-label">{{ $t('pages.upload.urlType.title') }}</label>
<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>
<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>
<span>{{ t('pages.upload.urlType.short') }}</span>
</button>
</div>
</div>
@@ -180,7 +180,7 @@
>
<div class="modal-header">
<h3 class="modal-title">
{{ $t('pages.imageProcess.title') }}
{{ t('pages.imageProcess.title') }}
</h3>
<button
class="modal-close"
@@ -209,18 +209,18 @@ import { useRouter } from 'vue-router'
import ImageProcessSetting from '@/components/ImageProcessSetting.vue'
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 { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '#/events/constants'
import { IPasteStyle, IRPCActionType } from '#/types/enum'
import { IFileWithPath, IUploaderConfigItem } from '#/types/types'
import { isUrl } from '#/utils/common'
import { configPaths } from '#/utils/configPaths'
import type { IFileWithPath, IUploaderConfigItem } from '#/types/types'
const { t } = useI18n()
useDragEventListeners()
const $router = useRouter()
const { t } = useI18n()
const imageProcessDialogVisible = ref(false)
const useShortUrl = ref(false)
@@ -233,7 +233,7 @@ const picBedName = ref('')
const picBedConfigName = ref('')
const fileInput = ref<HTMLInputElement>()
const pasteFormatList = ref({
const pasteFormatList = ref<Record<string, string>>({
[IPasteStyle.MARKDOWN]: '![alt](url)',
[IPasteStyle.HTML]: '<img src="url"/>',
[IPasteStyle.URL]: 'http://test.com/test.png',
@@ -440,687 +440,4 @@ export default {
}
</script>
<style scoped>
/* Global scrolling behavior */
html, body {
overflow-x: hidden;
}
/* Container */
.upload-container {
padding: 1rem;
width: 100%;
margin: 0;
display: flex;
flex-direction: column;
gap: 1.25rem;
min-height: 100vh;
box-sizing: border-box;
overflow-y: auto;
}
/* Card Base */
.upload-card {
background: var(--color-surface);
border: 1px solid var(--color-border-secondary);
border-radius: var(--radius-xl);
overflow: hidden;
transition: var(--transition-medium);
box-shadow: var(--shadow-sm);
}
.upload-card:hover {
box-shadow: var(--shadow-md);
border-color: var(--color-border);
}
/* Compact cards styling */
.actions-card,
.settings-card {
border-radius: var(--radius-lg);
}
.actions-card .card-header,
.settings-card .card-header {
padding: 0.875rem 1.25rem;
}
/* Header Card */
.header-card .card-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 1.5rem;
border-bottom: 1px solid var(--color-border-secondary);
flex-wrap: wrap;
gap: 1rem;
}
.provider-section {
flex: 1;
}
.provider-button {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
background: var(--color-surface-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
cursor: pointer;
transition: var(--transition-fast);
font-family: inherit;
width: auto;
min-width: 200px;
flex-shrink: 0;
}
.provider-button:hover {
background: var(--color-surface);
border-color: var(--color-accent);
transform: translateY(-1px);
box-shadow: var(--shadow-sm);
}
.provider-info {
display: flex;
flex-direction: column;
align-items: flex-start;
flex: 1;
}
.provider-name {
font-size: 0.875rem;
font-weight: 600;
color: var(--color-text-primary);
line-height: 1.2;
}
.provider-config {
font-size: 0.75rem;
color: var(--color-text-secondary);
line-height: 1.2;
}
.provider-arrow {
color: var(--color-text-secondary);
transition: var(--transition-fast);
}
.provider-button:hover .provider-arrow {
color: var(--color-accent);
transform: rotate(180deg);
}
.header-actions {
display: flex;
align-items: center;
gap: 0.75rem;
flex-wrap: wrap;
}
.action-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.625rem 1rem;
background: var(--color-accent);
color: white;
border: none;
border-radius: var(--radius-md);
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: var(--transition-fast);
font-family: inherit;
}
.action-button:hover {
background: var(--color-accent-hover);
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.action-button.secondary {
background: var(--color-surface-elevated);
color: var(--color-text-primary);
border: 1px solid var(--color-border);
}
.action-button.secondary:hover {
background: var(--color-surface);
border-color: var(--color-accent);
color: var(--color-accent);
}
/* Main Upload Card */
.main-card {
min-height: 300px;
}
.upload-zone {
position: relative;
padding: 3rem 2rem;
cursor: pointer;
transition: var(--transition-medium);
border: 2px dashed var(--color-border);
border-radius: var(--radius-xl);
background: linear-gradient(135deg, var(--color-surface) 0%, var(--color-background-secondary) 100%);
margin: 1rem;
}
.upload-zone:hover,
.upload-zone.drag-active {
border-color: var(--color-accent);
background: linear-gradient(135deg, var(--color-surface-elevated) 0%, rgba(0, 122, 255, 0.05) 100%);
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
.upload-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 1.5rem;
text-align: center;
}
.upload-icon {
width: 80px;
height: 80px;
border-radius: 50%;
background: linear-gradient(135deg, var(--color-accent) 0%, rgba(0, 122, 255, 0.8) 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
transition: var(--transition-medium);
}
.upload-zone:hover .upload-icon,
.upload-zone.drag-active .upload-icon {
transform: scale(1.1);
}
.upload-text {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.upload-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-text-primary);
margin: 0;
letter-spacing: -0.025em;
}
.upload-subtitle {
font-size: 0.875rem;
color: var(--color-text-secondary);
margin: 0;
}
.upload-formats {
display: flex;
flex-direction: column;
gap: 0.25rem;
margin-top: 0.5rem;
}
.format-label {
font-size: 0.75rem;
font-weight: 500;
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.025em;
}
/* Progress */
.progress-container {
margin: 1rem 1.5rem;
padding: 1rem;
background: var(--color-surface-elevated);
border-radius: var(--radius-lg);
border: 1px solid var(--color-border-secondary);
}
.progress-bar {
height: 6px;
background: var(--color-border-secondary);
border-radius: 3px;
overflow: hidden;
margin-bottom: 0.5rem;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--color-accent) 0%, var(--color-primary) 100%);
border-radius: 3px;
transition: width var(--transition-medium);
}
.progress-fill.progress-error {
background: var(--color-danger);
}
.progress-text {
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text-secondary);
text-align: center;
display: block;
}
/* Quick Actions Card */
.card-header {
padding: 1rem 1.5rem;
border-bottom: 1px solid var(--color-border-secondary);
}
.card-title {
font-size: 0.9rem;
font-weight: 600;
color: var(--color-text-primary);
margin: 0;
letter-spacing: -0.025em;
}
.quick-actions {
padding: 1rem 1.5rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 0.75rem;
}
.quick-action-button {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
padding: 0.875rem 1rem;
background: var(--color-surface-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
cursor: pointer;
transition: var(--transition-medium);
font-family: inherit;
text-align: left;
}
.quick-action-button:hover {
background: var(--color-surface);
border-color: var(--color-accent);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.quick-action-button span {
font-size: 0.8rem;
font-weight: 500;
color: var(--color-text-primary);
}
/* Settings Card */
.settings-content {
padding: 1.25rem 1.5rem;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.setting-group {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
@media (min-width: 768px) {
.settings-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
align-items: start;
}
.quick-actions {
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
}
@media (min-width: 1024px) {
.upload-container {
padding: 1.5rem 2rem;
max-width: 1200px;
margin: 0 auto;
}
.quick-actions {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.settings-content {
gap: 2rem;
}
}
.setting-label {
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text-primary);
margin: 0;
}
.format-buttons {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(70px, 1fr));
gap: 0.4rem;
}
.format-button {
padding: 0.4rem 0.75rem;
background: var(--color-surface-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
font-size: 0.7rem;
font-weight: 500;
color: var(--color-text-secondary);
cursor: pointer;
transition: var(--transition-fast);
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
}
.format-button:hover {
border-color: var(--color-accent);
color: var(--color-text-primary);
}
.format-button.active {
background: var(--color-accent);
border-color: var(--color-accent);
color: white;
}
.url-toggle {
display: flex;
background: var(--color-surface-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
overflow: hidden;
width: 100%;
}
.toggle-button {
flex: 1;
padding: 0.625rem 0.875rem;
background: transparent;
border: none;
font-size: 0.8rem;
font-weight: 500;
color: var(--color-text-secondary);
cursor: pointer;
transition: var(--transition-fast);
font-family: inherit;
}
.toggle-button:hover {
color: var(--color-text-primary);
}
.toggle-button.active {
background: var(--color-accent);
color: white;
}
.toggle-button:first-child.active {
border-top-left-radius: calc(var(--radius-md) - 1px);
border-bottom-left-radius: calc(var(--radius-md) - 1px);
}
.toggle-button:last-child.active {
border-top-right-radius: calc(var(--radius-md) - 1px);
border-bottom-right-radius: calc(var(--radius-md) - 1px);
}
/* Modal */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
overflow-y: auto;
}
.modal-container {
background: var(--color-surface);
border-radius: var(--radius-2xl);
border: 1px solid var(--color-border);
box-shadow: var(--shadow-xl);
max-width: 90vw;
width: 80vw;
height: 80vh;
max-height: 90vh;
overflow: hidden;
margin: auto;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.5rem 2rem;
border-bottom: 1px solid var(--color-border-secondary);
}
.modal-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-text-primary);
margin: 0;
}
.modal-close {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: var(--color-surface-elevated);
border: 1px solid var(--color-border);
border-radius: 50%;
cursor: pointer;
transition: var(--transition-fast);
color: var(--color-text-secondary);
}
.modal-close:hover {
background: var(--color-surface);
border-color: var(--color-danger);
color: var(--color-danger);
}
.modal-content {
padding: 0.2rem;
overflow-y: auto;
max-height: calc(90vh - 120px);
scrollbar-width: none;
-ms-overflow-style: none;
}
.modal-content::-webkit-scrollbar {
display: none;
}
/* Transitions */
.progress-enter-active,
.progress-leave-active {
transition: all var(--transition-medium);
}
.progress-enter-from,
.progress-leave-to {
opacity: 0;
transform: translateY(-10px);
}
.modal-enter-active,
.modal-leave-active {
transition: all var(--transition-medium);
}
.modal-enter-from,
.modal-leave-to {
opacity: 0;
transform: scale(0.95);
}
/* Responsive Design */
@media (max-width: 768px) {
.upload-container {
padding: 0.75rem;
gap: 1rem;
}
.header-card .card-header {
flex-direction: column;
align-items: stretch;
}
.provider-section {
order: 1;
}
.header-actions {
order: 2;
justify-content: stretch;
}
.action-button {
flex: 1;
justify-content: center;
}
.upload-zone {
padding: 2rem 1rem;
margin: 0.75rem;
}
.upload-icon {
width: 60px;
height: 60px;
}
.quick-actions {
grid-template-columns: 1fr;
padding: 0.875rem 1rem;
}
.settings-content {
grid-template-columns: 1fr !important;
padding: 1rem 1.25rem;
}
.format-buttons {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));
}
.modal-overlay {
padding: 1rem;
}
.modal-header,
.modal-content {
padding: 1.5rem;
}
}
@media (max-width: 480px) {
.upload-container {
padding: 0.5rem;
}
.upload-zone {
margin: 0.5rem;
padding: 1.5rem 1rem;
}
.upload-title {
font-size: 1.125rem;
}
.quick-action-button {
padding: 0.75rem 0.875rem;
}
.action-button {
padding: 0.5rem 0.75rem;
font-size: 0.8rem;
}
.provider-button {
min-width: unset;
width: 100%;
}
}
/* Dark mode adjustments */
:root.dark .upload-zone,
:root.auto.dark .upload-zone {
background: linear-gradient(135deg, var(--color-background-secondary) 0%, var(--color-background-tertiary) 100%);
}
:root.dark .upload-zone:hover,
:root.dark .upload-zone.drag-active,
:root.auto.dark .upload-zone:hover,
:root.auto.dark .upload-zone.drag-active {
background: linear-gradient(135deg, var(--color-surface) 0%, rgba(0, 122, 255, 0.1) 100%);
}
/* Animation for upload icon */
@keyframes float {
0%, 100% {
transform: translateY(0px);
}
50% {
transform: translateY(-10px);
}
}
.upload-zone.drag-active .upload-icon {
animation: float 1.5s ease-in-out infinite;
}
/* Accessibility */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Focus styles for keyboard navigation */
.provider-button:focus-visible,
.action-button:focus-visible,
.quick-action-button:focus-visible,
.format-button:focus-visible,
.toggle-button:focus-visible,
.modal-close:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
.upload-zone:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 4px;
}
</style>
<style scoped src="./css/UploadPage.css"></style>

View File

@@ -99,10 +99,10 @@ import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
import { useStore } from '@/hooks/useStore'
import { PICBEDS_PAGE, UPLOADER_CONFIG_PAGE } from '@/router/config'
import { configPaths } from '@/utils/configPaths'
import { saveConfig } from '@/utils/dataSender'
import { IRPCActionType } from '#/types/enum'
import { IStringKeyMap, IUploaderConfigItem } from '#/types/types'
import { configPaths } from '#/utils/configPaths'
import { IRPCActionType } from '@/utils/enum'
import type { IStringKeyMap, IUploaderConfigItem } from '#/types/types'
const { t } = useI18n()
const router = useRouter()

View File

@@ -0,0 +1,676 @@
.piclist-settings {
padding: 1.5rem;
min-height: 100vh;
background: var(--color-background-secondary);
color: var(--color-text-primary);
overflow-y: auto;
scrollbar-width: none;
-ms-overflow-style: none;
}
.piclist-settings::-webkit-scrollbar {
display: none;
}
/* Header */
.settings-header {
display: flex;
justify-content: space-between;
align-items: center;
background: var(--color-surface);
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1.5rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border: 1px solid var(--color-border);
}
.header-content {
display: flex;
align-items: center;
gap: 1rem;
}
.header-icon {
color: var(--color-accent);
}
.settings-header h1 {
margin: 0;
font-size: 1.5rem;
font-weight: 600;
color: var(--color-text-primary);
}
.settings-header p {
margin: 0;
color: var(--color-text-secondary);
font-size: 0.875rem;
}
.header-actions {
display: flex;
gap: 0.75rem;
}
/* Tab Navigation */
.tab-navigation {
display: flex;
background: var(--color-background-primary);
border-radius: 12px;
padding: 0.25rem;
margin-bottom: 1.5rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border: 1px solid var(--color-border);
}
.tab-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
background: transparent;
border: none;
border-radius: 8px;
color: var(--color-text-secondary);
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
flex: 1;
justify-content: center;
}
.tab-button:hover {
color: var(--color-text-primary);
background: var(--color-background-primary);
}
.tab-button.active {
background: #409eff;
color: white;
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
}
/* Settings Content */
.settings-content {
display: flex;
flex-direction: column;
}
.tab-content {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.settings-section {
background: var(--color-background-primary);
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 2px 8px var(--color-border);
border: 1px solid var(--color-border);
}
.settings-section h2 {
margin: 0 0 0.5rem 0;
font-size: 1.125rem;
font-weight: 600;
color: var(--color-text-primary);
}
.settings-section p {
margin: 0 0 1.5rem 0;
color: var(--color-text-secondary);
font-size: 0.875rem;
}
/* Form Elements */
.form-group {
margin-bottom: 1.5rem;
}
.form-group:last-child {
margin-bottom: 0;
}
.form-group > label:not(.switch-label):not(.radio-option) {
display: block;
margin-bottom: 0.5rem;
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text-primary);
}
.form-input,
.form-textarea,
.form-select {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--color-border);
border-radius: 8px;
background: var(--color-background-primary);
color: var(--color-text-primary);
font-size: 0.875rem;
transition: all 0.2s ease;
box-sizing: border-box;
}
.form-input:focus,
.form-textarea:focus,
.form-select:focus {
outline: none;
border-color: var(--color-blue-common);
box-shadow: 0 0 0 2px var(--el-color-primary-light-9, rgba(64, 158, 255, 0.2));
}
.form-textarea {
resize: vertical;
min-height: 80px;
}
.form-range {
width: 100%;
height: 6px;
border-radius: 3px;
background: #e4e7ed;
outline: none;
margin-bottom: 0.5rem;
-webkit-appearance: none;
appearance: none;
}
.form-range::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #409eff;
cursor: pointer;
}
.form-range::-moz-range-thumb {
width: 20px;
height: 20px;
border-radius: 50%;
background: #409eff;
cursor: pointer;
border: none;
}
.range-value {
font-size: 0.875rem;
color: var(--color-text-secondary);
text-align: center;
}
/* Grid Layout */
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
/* Switch Component */
.switch-label {
display: flex;
align-items: center;
gap: 0.75rem;
cursor: pointer;
padding: 1rem;
border: 1px solid var(--color-border-secondary);
border-radius: 8px;
background: var(--color-background-tertiary);
transition: all 0.2s ease;
}
.switch-label:hover {
background: var(--color-background-secondary);
border-color: var(--color-border);
}
.switch-input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.switch-slider {
position: relative;
width: 44px;
height: 24px;
background: var(--color-border);
border-radius: 12px;
transition: background-color 0.3s;
flex-shrink: 0;
}
.switch-slider::before {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
background: white;
border-radius: 50%;
transition: transform 0.3s;
}
.switch-input:checked + .switch-slider {
background: #409eff;
}
.switch-input:checked + .switch-slider::before {
transform: translateX(20px);
}
.switch-content {
flex: 1;
}
.switch-title {
font-weight: 500;
color: var(--color-text-primary);
margin-bottom: 0.25rem;
}
.switch-description {
font-size: 0.75rem;
color: var(--color-text-secondary);
}
/* Radio Group */
.radio-group {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.radio-option {
display: flex;
align-items: center;
gap: 0.75rem;
cursor: pointer;
padding: 0.75rem;
border: 1px solid var(--color-border-secondary);
border-radius: 8px;
background: var(--color-background-tertiary);
transition: all 0.2s ease;
}
.radio-option:hover {
background: var(--color-background-secondary);
border-color: var(--color-border);
}
.radio-input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.radio-indicator {
position: relative;
width: 20px;
height: 20px;
border: 2px solid var(--color-border);
border-radius: 50%;
flex-shrink: 0;
}
.radio-input:checked + .radio-indicator {
border-color: #409eff;
}
.radio-input:checked + .radio-indicator::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 10px;
height: 10px;
background: #409eff;
border-radius: 50%;
transform: translate(-50%, -50%);
}
.radio-label {
font-weight: 500;
color: var(--color-text-primary);
}
/* Buttons */
.btn {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
border-radius: 8px;
border: none;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
min-width: fit-content;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.btn-primary {
background: #409eff;
color: white;
}
.btn-primary:hover:not(:disabled) {
background: #66b1ff;
}
.btn-secondary {
background: var(--color-background-tertiary);
color: var(--color-text-primary);
border: 1px solid var(--color-border);
}
.btn-secondary:hover:not(:disabled) {
background: var(--color-background-secondary);
}
.btn-danger {
background: #f56c6c;
color: white;
}
.btn-danger:hover:not(:disabled) {
background: #f78989;
}
/* Checkbox Group */
.checkbox-group {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
}
.checkbox-option {
display: flex;
align-items: center;
gap: 0.5rem;
cursor: pointer;
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-border-secondary);
border-radius: 6px;
background: var(--color-background-tertiary);
transition: all 0.2s ease;
}
.checkbox-option:hover {
background: var(--color-background-secondary);
border-color: var(--color-border);
}
.checkbox-input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.checkbox-indicator {
position: relative;
width: 16px;
height: 16px;
border: 2px solid var(--color-border);
border-radius: 4px;
flex-shrink: 0;
}
.checkbox-input:checked + .checkbox-indicator {
background: #409eff;
border-color: #409eff;
}
.checkbox-input:checked + .checkbox-indicator::after {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
color: white;
font-size: 10px;
transform: translate(-50%, -50%);
}
.checkbox-label {
font-size: 0.875rem;
color: var(--color-text-primary);
}
/* Input Groups */
.input-group {
display: flex;
gap: 0.5rem;
}
.input-group .form-input {
flex: 1;
}
.input-addon-btn {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
border: 1px solid var(--color-border);
border-radius: 8px;
background: var(--color-background-tertiary);
color: var(--color-text-secondary);
cursor: pointer;
transition: all 0.2s ease;
}
.input-addon-btn:hover {
background: var(--color-background-secondary);
color: var(--color-text-primary);
}
/* Dialog Overlay */
.dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.dialog {
background: var(--color-surface);
border-radius: 12px;
padding: 1.5rem;
max-width: 500px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
}
.dialog-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.dialog-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-text-primary);
}
.dialog-close {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: var(--color-text-secondary);
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
transition: all 0.2s ease;
}
.dialog-close:hover {
background: var(--color-background-secondary);
color: var(--color-text-primary);
}
.dialog-content {
margin-bottom: 1.5rem;
}
.dialog-footer {
display: flex;
justify-content: flex-end;
gap: 0.75rem;
}
.button-group {
display: flex;
gap: 0.75rem;
}
.button-group .btn {
flex: 1;
}
/* Notice Text */
.notice-text {
background: rgba(64, 158, 255, 0.1);
color: #409eff;
padding: 1rem;
border-radius: 8px;
margin-bottom: 1rem;
text-align: center;
font-size: 0.875rem;
}
/* Small text */
small {
font-size: 0.75rem;
color: var(--color-text-secondary);
}
/* Responsive Design */
@media (max-width: 768px) {
.piclist-settings {
padding: 1rem;
}
.tab-navigation {
flex-direction: column;
}
.tab-button {
justify-content: flex-start;
}
.form-grid {
grid-template-columns: 1fr;
}
.dialog {
width: 95%;
margin: 1rem;
}
}
/* Dark mode adjustments */
:root.dark .piclist-settings,
:root.auto.dark .piclist-settings {
background: var(--color-background-primary);
}
:root.dark .settings-header,
:root.dark .tab-navigation,
:root.dark .settings-section,
:root.auto.dark .settings-header,
:root.auto.dark .tab-navigation,
:root.auto.dark .settings-section {
background: var(--color-background-tertiary);
border-color: var(--color-border);
}
:root.dark .form-input,
:root.dark .form-textarea,
:root.dark .form-select,
:root.auto.dark .form-input,
:root.auto.dark .form-textarea,
:root.auto.dark .form-select {
background: var(--color-background-tertiary);
border-color: var(--color-border);
color: var(--color-text-primary);
}
:root.dark .switch-slider::before,
:root.auto.dark .switch-slider::before {
background: var(--color-surface);
}
:root.dark .btn-secondary,
:root.auto.dark .btn-secondary {
background: var(--color-background-tertiary);
border-color: var(--color-border);
color: var(--color-text-primary);
}
:root.dark .btn-secondary:hover,
:root.auto.dark .btn-secondary:hover {
background: var(--color-background-secondary);
}
:root.dark .switch-label,
:root.auto.dark .switch-label {
background: var(--color-background-tertiary);
border-color: var(--color-border);
}
:root.dark .switch-label:hover,
:root.auto.dark .switch-label:hover {
background: var(--color-background-secondary);
}
:root.dark .radio-option,
:root.dark .checkbox-option,
:root.auto.dark .radio-option,
:root.auto.dark .checkbox-option {
background: var(--color-background-tertiary);
border-color: var(--color-border);
}
:root.dark .radio-option:hover,
:root.dark .checkbox-option:hover,
:root.auto.dark .radio-option:hover,
:root.auto.dark .checkbox-option:hover {
background: var(--color-background-secondary);
}
:root.dark .dialog,
:root.auto.dark .dialog {
background: var(--color-background-tertiary);
}

View File

@@ -0,0 +1,682 @@
/* Global scrolling behavior */
html, body {
overflow-x: hidden;
}
/* Container */
.upload-container {
padding: 1rem;
width: 100%;
margin: 0;
display: flex;
flex-direction: column;
gap: 1.25rem;
min-height: 100vh;
box-sizing: border-box;
overflow-y: auto;
}
/* Card Base */
.upload-card {
background: var(--color-surface);
border: 1px solid var(--color-border-secondary);
border-radius: var(--radius-xl);
overflow: hidden;
transition: var(--transition-medium);
box-shadow: var(--shadow-sm);
}
.upload-card:hover {
box-shadow: var(--shadow-md);
border-color: var(--color-border);
}
/* Compact cards styling */
.actions-card,
.settings-card {
border-radius: var(--radius-lg);
}
.actions-card .card-header,
.settings-card .card-header {
padding: 0.875rem 1.25rem;
}
/* Header Card */
.header-card .card-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 1.5rem;
border-bottom: 1px solid var(--color-border-secondary);
flex-wrap: wrap;
gap: 1rem;
}
.provider-section {
flex: 1;
}
.provider-button {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
background: var(--color-surface-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
cursor: pointer;
transition: var(--transition-fast);
font-family: inherit;
width: auto;
min-width: 200px;
flex-shrink: 0;
}
.provider-button:hover {
background: var(--color-surface);
border-color: var(--color-accent);
transform: translateY(-1px);
box-shadow: var(--shadow-sm);
}
.provider-info {
display: flex;
flex-direction: column;
align-items: flex-start;
flex: 1;
}
.provider-name {
font-size: 0.875rem;
font-weight: 600;
color: var(--color-text-primary);
line-height: 1.2;
}
.provider-config {
font-size: 0.75rem;
color: var(--color-text-secondary);
line-height: 1.2;
}
.provider-arrow {
color: var(--color-text-secondary);
transition: var(--transition-fast);
}
.provider-button:hover .provider-arrow {
color: var(--color-accent);
transform: rotate(180deg);
}
.header-actions {
display: flex;
align-items: center;
gap: 0.75rem;
flex-wrap: wrap;
}
.action-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.625rem 1rem;
background: var(--color-accent);
color: white;
border: none;
border-radius: var(--radius-md);
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: var(--transition-fast);
font-family: inherit;
}
.action-button:hover {
background: var(--color-accent-hover);
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.action-button.secondary {
background: var(--color-surface-elevated);
color: var(--color-text-primary);
border: 1px solid var(--color-border);
}
.action-button.secondary:hover {
background: var(--color-surface);
border-color: var(--color-accent);
color: var(--color-accent);
}
/* Main Upload Card */
.main-card {
min-height: 300px;
}
.upload-zone {
position: relative;
padding: 3rem 2rem;
cursor: pointer;
transition: var(--transition-medium);
border: 2px dashed var(--color-border);
border-radius: var(--radius-xl);
background: linear-gradient(135deg, var(--color-surface) 0%, var(--color-background-secondary) 100%);
margin: 1rem;
}
.upload-zone:hover,
.upload-zone.drag-active {
border-color: var(--color-accent);
background: linear-gradient(135deg, var(--color-surface-elevated) 0%, rgba(0, 122, 255, 0.05) 100%);
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
.upload-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 1.5rem;
text-align: center;
}
.upload-icon {
width: 80px;
height: 80px;
border-radius: 50%;
background: linear-gradient(135deg, var(--color-accent) 0%, rgba(0, 122, 255, 0.8) 100%);
display: flex;
align-items: center;
justify-content: center;
color: white;
transition: var(--transition-medium);
}
.upload-zone:hover .upload-icon,
.upload-zone.drag-active .upload-icon {
transform: scale(1.1);
}
.upload-text {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.upload-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-text-primary);
margin: 0;
letter-spacing: -0.025em;
}
.upload-subtitle {
font-size: 0.875rem;
color: var(--color-text-secondary);
margin: 0;
}
.upload-formats {
display: flex;
flex-direction: column;
gap: 0.25rem;
margin-top: 0.5rem;
}
.format-label {
font-size: 0.75rem;
font-weight: 500;
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.025em;
}
/* Progress */
.progress-container {
margin: 1rem 1.5rem;
padding: 1rem;
background: var(--color-surface-elevated);
border-radius: var(--radius-lg);
border: 1px solid var(--color-border-secondary);
}
.progress-bar {
height: 6px;
background: var(--color-border-secondary);
border-radius: 3px;
overflow: hidden;
margin-bottom: 0.5rem;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--color-accent) 0%, var(--color-primary) 100%);
border-radius: 3px;
transition: width var(--transition-medium);
}
.progress-fill.progress-error {
background: var(--color-danger);
}
.progress-text {
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text-secondary);
text-align: center;
display: block;
}
/* Quick Actions Card */
.card-header {
padding: 1rem 1.5rem;
border-bottom: 1px solid var(--color-border-secondary);
}
.card-title {
font-size: 0.9rem;
font-weight: 600;
color: var(--color-text-primary);
margin: 0;
letter-spacing: -0.025em;
}
.quick-actions {
padding: 1rem 1.5rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 0.75rem;
}
.quick-action-button {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
padding: 0.875rem 1rem;
background: var(--color-surface-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
cursor: pointer;
transition: var(--transition-medium);
font-family: inherit;
text-align: left;
}
.quick-action-button:hover {
background: var(--color-surface);
border-color: var(--color-accent);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.quick-action-button span {
font-size: 0.8rem;
font-weight: 500;
color: var(--color-text-primary);
}
/* Settings Card */
.settings-content {
padding: 1.25rem 1.5rem;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.setting-group {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
@media (min-width: 768px) {
.settings-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
align-items: start;
}
.quick-actions {
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
}
@media (min-width: 1024px) {
.upload-container {
padding: 1.5rem 2rem;
max-width: 1200px;
margin: 0 auto;
}
.quick-actions {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.settings-content {
gap: 2rem;
}
}
.setting-label {
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text-primary);
margin: 0;
}
.format-buttons {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(70px, 1fr));
gap: 0.4rem;
}
.format-button {
padding: 0.4rem 0.75rem;
background: var(--color-surface-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
font-size: 0.7rem;
font-weight: 500;
color: var(--color-text-secondary);
cursor: pointer;
transition: var(--transition-fast);
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
}
.format-button:hover {
border-color: var(--color-accent);
color: var(--color-text-primary);
}
.format-button.active {
background: var(--color-accent);
border-color: var(--color-accent);
color: white;
}
.url-toggle {
display: flex;
background: var(--color-surface-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
overflow: hidden;
width: 100%;
}
.toggle-button {
flex: 1;
padding: 0.625rem 0.875rem;
background: transparent;
border: none;
font-size: 0.8rem;
font-weight: 500;
color: var(--color-text-secondary);
cursor: pointer;
transition: var(--transition-fast);
font-family: inherit;
}
.toggle-button:hover {
color: var(--color-text-primary);
}
.toggle-button.active {
background: var(--color-accent);
color: white;
}
.toggle-button:first-child.active {
border-top-left-radius: calc(var(--radius-md) - 1px);
border-bottom-left-radius: calc(var(--radius-md) - 1px);
}
.toggle-button:last-child.active {
border-top-right-radius: calc(var(--radius-md) - 1px);
border-bottom-right-radius: calc(var(--radius-md) - 1px);
}
/* Modal */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
overflow-y: auto;
}
.modal-container {
background: var(--color-surface);
border-radius: var(--radius-2xl);
border: 1px solid var(--color-border);
box-shadow: var(--shadow-xl);
max-width: 90vw;
width: 80vw;
height: 80vh;
max-height: 90vh;
overflow: hidden;
margin: auto;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.5rem 2rem;
border-bottom: 1px solid var(--color-border-secondary);
}
.modal-title {
font-size: 1.25rem;
font-weight: 600;
color: var(--color-text-primary);
margin: 0;
}
.modal-close {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: var(--color-surface-elevated);
border: 1px solid var(--color-border);
border-radius: 50%;
cursor: pointer;
transition: var(--transition-fast);
color: var(--color-text-secondary);
}
.modal-close:hover {
background: var(--color-surface);
border-color: var(--color-danger);
color: var(--color-danger);
}
.modal-content {
padding: 0.2rem;
overflow-y: auto;
max-height: calc(90vh - 120px);
scrollbar-width: none;
-ms-overflow-style: none;
}
.modal-content::-webkit-scrollbar {
display: none;
}
/* Transitions */
.progress-enter-active,
.progress-leave-active {
transition: all var(--transition-medium);
}
.progress-enter-from,
.progress-leave-to {
opacity: 0;
transform: translateY(-10px);
}
.modal-enter-active,
.modal-leave-active {
transition: all var(--transition-medium);
}
.modal-enter-from,
.modal-leave-to {
opacity: 0;
transform: scale(0.95);
}
/* Responsive Design */
@media (max-width: 768px) {
.upload-container {
padding: 0.75rem;
gap: 1rem;
}
.header-card .card-header {
flex-direction: column;
align-items: stretch;
}
.provider-section {
order: 1;
}
.header-actions {
order: 2;
justify-content: stretch;
}
.action-button {
flex: 1;
justify-content: center;
}
.upload-zone {
padding: 2rem 1rem;
margin: 0.75rem;
}
.upload-icon {
width: 60px;
height: 60px;
}
.quick-actions {
grid-template-columns: 1fr;
padding: 0.875rem 1rem;
}
.settings-content {
grid-template-columns: 1fr !important;
padding: 1rem 1.25rem;
}
.format-buttons {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(60px, 1fr));
}
.modal-overlay {
padding: 1rem;
}
.modal-header,
.modal-content {
padding: 1.5rem;
}
}
@media (max-width: 480px) {
.upload-container {
padding: 0.5rem;
}
.upload-zone {
margin: 0.5rem;
padding: 1.5rem 1rem;
}
.upload-title {
font-size: 1.125rem;
}
.quick-action-button {
padding: 0.75rem 0.875rem;
}
.action-button {
padding: 0.5rem 0.75rem;
font-size: 0.8rem;
}
.provider-button {
min-width: unset;
width: 100%;
}
}
/* Dark mode adjustments */
:root.dark .upload-zone,
:root.auto.dark .upload-zone {
background: linear-gradient(135deg, var(--color-background-secondary) 0%, var(--color-background-tertiary) 100%);
}
:root.dark .upload-zone:hover,
:root.dark .upload-zone.drag-active,
:root.auto.dark .upload-zone:hover,
:root.auto.dark .upload-zone.drag-active {
background: linear-gradient(135deg, var(--color-surface) 0%, rgba(0, 122, 255, 0.1) 100%);
}
/* Animation for upload icon */
@keyframes float {
0%, 100% {
transform: translateY(0px);
}
50% {
transform: translateY(-10px);
}
}
.upload-zone.drag-active .upload-icon {
animation: float 1.5s ease-in-out infinite;
}
/* Accessibility */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Focus styles for keyboard navigation */
.provider-button:focus-visible,
.action-button:focus-visible,
.quick-action-button:focus-visible,
.format-button:focus-visible,
.toggle-button:focus-visible,
.modal-close:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
.upload-zone:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 4px;
}

View File

@@ -99,11 +99,11 @@ import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import ConfigForm from '@/components/ConfigForm.vue'
import { configPaths } from '@/utils/configPaths'
import { getConfig } from '@/utils/dataSender'
import { II18nLanguage, IRPCActionType } from '#/types/enum'
import { IPicGoPluginConfig, IStringKeyMap, IUploaderConfigItem, IUploaderConfigListItem } from '#/types/types'
import { configPaths } from '#/utils/configPaths'
import { picBedManualUrlList } from '#/utils/static'
import { II18nLanguage, IRPCActionType } from '@/utils/enum'
import { picBedManualUrlList } from '@/utils/static'
import type { IPicGoPluginConfig, IStringKeyMap, IUploaderConfigItem, IUploaderConfigListItem } from '#/types/types'
const { t } = useI18n()
const type = ref('')

View File

@@ -1,7 +1,7 @@
import { App, InjectionKey, reactive, readonly, UnwrapRef } from 'vue'
import { configPaths } from '@/utils/configPaths'
import { saveConfig } from '@/utils/dataSender'
import { configPaths } from '#/utils/configPaths'
export interface IState {
defaultPicBed: string

View File

@@ -1,6 +1,6 @@
import mitt from 'mitt'
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '#/events/constants'
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant'
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
type IEvent = {

View File

@@ -16,3 +16,52 @@ export const getRawData = (args: any): any => {
}
return args
}
export const isUrl = (url: string): boolean => {
try {
return Boolean(new URL(url))
} catch {
return false
}
}
export const isUrlEncode = (url: string): boolean => {
url = url || ''
try {
return url !== decodeURI(url)
} catch {
return false
}
}
export const handleUrlEncode = (url: string): string => (isUrlEncode(url) ? url : encodeURI(url))
export const handleStreamlinePluginName = (name: string) => name.replace(/(@[^/]+\/)?picgo-plugin-/, '')
export const enforceNumber = (num: number | string) => (isNaN(+num) ? 0 : +num)
export function isNeedToShorten (alias: string, cutOff = 20) {
return [...alias].reduce((len, char) => len + (char.charCodeAt(0) > 255 ? 2 : 1), 0) > cutOff
}
export function safeSliceF (str: string, total: number) {
let result = ''
let totalLen = 0
for (const s of str) {
if (totalLen >= total) {
break
}
result += s
totalLen += s.charCodeAt(0) > 255 ? 2 : 1
}
return result
}
export const formatEndpoint = (endpoint: string, sslEnabled: boolean): string => {
const hasProtocol = /^https?:\/\//.test(endpoint)
if (!hasProtocol) {
return `${sslEnabled ? 'https' : 'http'}://${endpoint}`
}
return sslEnabled ? endpoint.replace(/^http:\/\//, 'https://') : endpoint.replace(/^https:\/\//, 'http://')
}
export const trimPath = (path: string) => path.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/')

View File

@@ -0,0 +1,188 @@
import type { IBuildInCompressOptions, IBuildInWaterMarkOptions } from 'piclist'
import type { IAliYunConfig, IAwsS3PListUserConfig, IGitHubConfig, IImgurConfig, ILocalConfig, ILskyConfig, IPicBedType, IQiniuConfig, IServerConfig, ISftpPlistConfig, IShortKeyConfig, ISMMSConfig, ISyncConfig, ITcYunConfig, IUploaderConfig, IUpYunConfig, IWebdavPlistConfig } from '#/types/types'
export type manualPageOpenType = 'window' | 'browser'
interface IPicGoPlugins {
[key: `picgo-plugin-${string}`]: boolean
}
export interface IConfigStruct {
picBed: {
uploader: string
current?: string
smms?: ISMMSConfig
qiniu?: IQiniuConfig
upyun?: IUpYunConfig
tcyun?: ITcYunConfig
github?: IGitHubConfig
aliyun?: IAliYunConfig
imgur?: IImgurConfig
webdavplist?: IWebdavPlistConfig
local?: ILocalConfig
sftpplist?: ISftpPlistConfig
lskyplist?: ILskyConfig
'aws-s3-plist': IAwsS3PListUserConfig
proxy?: string
transformer?: string
list: IPicBedType[]
[others: string]: any
}
settings: {
shortKey: {
[key: string]: IShortKeyConfig
}
logLevel: string[]
logPath: string
logFileSizeLimit: number
isAutoListenClipboard: boolean
isListeningClipboard: boolean
showUpdateTip: boolean
miniWindowPosition: [number, number]
miniWindowOntop: boolean
mainWindowWidth: number
mainWindowHeight: number
isHideDock: boolean
autoCloseMiniWindow: boolean
autoCloseMainWindow: boolean
isCustomMiniIcon: boolean
customMiniIcon: string
startMode: string
autoRename: boolean
deleteCloudFile: boolean
server: IServerConfig
serverKey: string
pasteStyle: string
aesPassword: string
rename: boolean
sync: ISyncConfig
tempDirPath: string
language: string
customLink: string
manualPageOpen: manualPageOpenType
encodeOutputURL: boolean
useShortUrl: boolean
shortUrlServer: string
c1nToken: string
cfWorkerHost: string
yourlsDomain: string
yourlsSignature: string
sinkDomain: string
sinkToken: string
isSilentNotice: boolean
proxy: string
registry: string
autoCopy: boolean
enableWebServer: boolean
webServerHost: string
webServerPort: number
webServerPath: string
deleteLocalFile: boolean
uploadResultNotification: boolean
uploadNotification: boolean
useBuiltinClipboard: boolean
autoStart: boolean
autoImport: boolean
autoImportPicBed: string[]
}
needReload: boolean
picgoPlugins: IPicGoPlugins
uploader: IUploaderConfig
buildIn: {
compress: IBuildInCompressOptions
watermark: IBuildInWaterMarkOptions
rename: {
enable: boolean
format: string
}
skipProcess: {
skipProcessExtList: string
}
}
debug: boolean
PICGO_ENV: string
}
export const configPaths = {
picBed: {
current: 'picBed.current',
uploader: 'picBed.uploader',
secondUploader: 'picBed.secondUploader',
secondUploaderId: 'picBed.secondUploaderId',
secondUploaderConfig: 'picBed.secondUploaderConfig',
proxy: 'picBed.proxy',
transformer: 'picBed.transformer',
list: 'picBed.list'
},
settings: {
shortKey: {
_path: 'settings.shortKey',
'picgo:upload': 'settings.shortKey[picgo:upload]'
},
logLevel: 'settings.logLevel',
logPath: 'settings.logPath',
logFileSizeLimit: 'settings.logFileSizeLimit',
isAutoListenClipboard: 'settings.isAutoListenClipboard',
isListeningClipboard: 'settings.isListeningClipboard',
showUpdateTip: 'settings.showUpdateTip',
miniWindowPosition: 'settings.miniWindowPosition',
miniWindowOntop: 'settings.miniWindowOntop',
isHideDock: 'settings.isHideDock',
mainWindowWidth: 'settings.mainWindowWidth',
mainWindowHeight: 'settings.mainWindowHeight',
autoCloseMiniWindow: 'settings.autoCloseMiniWindow',
autoCloseMainWindow: 'settings.autoCloseMainWindow',
isCustomMiniIcon: 'settings.isCustomMiniIcon',
customMiniIcon: 'settings.customMiniIcon',
startMode: 'settings.startMode',
autoRename: 'settings.autoRename',
deleteCloudFile: 'settings.deleteCloudFile',
server: 'settings.server',
serverKey: 'settings.serverKey',
pasteStyle: 'settings.pasteStyle',
aesPassword: 'settings.aesPassword',
rename: 'settings.rename',
sync: 'settings.sync',
tempDirPath: 'settings.tempDirPath',
language: 'settings.language',
customLink: 'settings.customLink',
manualPageOpen: 'settings.manualPageOpen',
encodeOutputURL: 'settings.encodeOutputURL',
useShortUrl: 'settings.useShortUrl',
shortUrlServer: 'settings.shortUrlServer',
c1nToken: 'settings.c1nToken',
cfWorkerHost: 'settings.cfWorkerHost',
yourlsDomain: 'settings.yourlsDomain',
yourlsSignature: 'settings.yourlsSignature',
sinkDomain: 'settings.sinkDomain',
sinkToken: 'settings.sinkToken',
isSilentNotice: 'settings.isSilentNotice',
proxy: 'settings.proxy',
registry: 'settings.registry',
autoCopy: 'settings.autoCopy',
enableWebServer: 'settings.enableWebServer',
webServerHost: 'settings.webServerHost',
webServerPort: 'settings.webServerPort',
webServerPath: 'settings.webServerPath',
deleteLocalFile: 'settings.deleteLocalFile',
uploadResultNotification: 'settings.uploadResultNotification',
uploadNotification: 'settings.uploadNotification',
useBuiltinClipboard: 'settings.useBuiltinClipboard',
autoStart: 'settings.autoStart',
autoImport: 'settings.autoImport',
autoImportPicBed: 'settings.autoImportPicBed',
enableSecondUploader: 'settings.enableSecondUploader'
},
needReload: 'needReload',
picgoPlugins: 'picgoPlugins',
uploader: 'uploader',
buildIn: {
compress: 'buildIn.compress',
watermark: 'buildIn.watermark',
rename: 'buildIn.rename',
skipProcess: 'buildIn.skipProcess'
},
debug: 'debug',
PICGO_ENV: 'PICGO_ENV'
}

View File

@@ -0,0 +1,14 @@
export const SHOW_INPUT_BOX = 'SHOW_INPUT_BOX'
export const SHOW_INPUT_BOX_RESPONSE = 'SHOW_INPUT_BOX_RESPONSE'
// picgo plugin
export const PICGO_CONFIG_PLUGIN = 'PICGO_CONFIG_PLUGIN'
export const PICGO_HANDLE_PLUGIN_ING = 'PICGO_HANDLE_PLUGIN_ING'
export const PICGO_HANDLE_PLUGIN_DONE = 'PICGO_HANDLE_PLUGIN_DONE'
export const PICGO_TOGGLE_PLUGIN = 'PICGO_TOGGLE_PLUGIN'
// picgo uploader
export const RENAME_FILE_NAME = 'RENAME_FILE_NAME'
export const GET_RENAME_FILE_NAME = 'GET_RENAME_FILE_NAME'
export const SHOW_MAIN_PAGE_QRCODE = 'SHOW_MAIN_PAGE_QRCODE'
// rpc
export const RPC_ACTIONS = 'RPC_ACTIONS'
export const RPC_ACTIONS_INVOKE = 'RPC_ACTIONS_INVOKE'

View File

@@ -1,9 +1,10 @@
import { IRPCActionType } from '#/types/enum'
import { IObj } from '#/types/types'
import { getRawData } from '@/utils/common'
import { IRPCActionType } from '@/utils/enum'
import type { IObj } from '#/types/types'
export function saveConfig (config: IObj | string, value?: any) {
const configObject = typeof config === 'string' ? { [config]: value } : config
window.electron.sendRPC(IRPCActionType.PICLIST_SAVE_CONFIG, configObject)
window.electron.sendRPC(IRPCActionType.PICLIST_SAVE_CONFIG, getRawData(configObject))
}
export async function getConfig<T> (key?: string): Promise<T | undefined> {

View File

@@ -1,5 +1,5 @@
import { IRPCActionType } from '#/types/enum'
import { IGalleryDB } from '#/types/extra-vue'
import { IRPCActionType } from '@/utils/enum'
import type { IGalleryDB } from '#/types/extra-vue'
interface IFilter {
orderBy?: 'asc' | 'desc'
@@ -23,7 +23,7 @@ interface IObject {
}
export class GalleryDB implements IGalleryDB {
async #actionHandler<T>(method: IRPCActionType, ...args: any[]): Promise<T | undefined> {
async #actionHandler<T>(method: string, ...args: any[]): Promise<T | undefined> {
return await window.electron.triggerRPC<T>(method, ...args)
}

163
src/renderer/utils/enum.ts Normal file
View File

@@ -0,0 +1,163 @@
export const IPasteStyle = {
MARKDOWN: 'markdown',
HTML: 'HTML',
URL: 'URL',
UBB: 'UBB',
CUSTOM: 'Custom'
}
export const IWindowList = {
SETTING_WINDOW: 'SETTING_WINDOW',
TRAY_WINDOW: 'TRAY_WINDOW',
MINI_WINDOW: 'MINI_WINDOW',
RENAME_WINDOW: 'RENAME_WINDOW',
TOOLBOX_WINDOW: 'TOOLBOX_WINDOW'
}
export const IRPCActionType = {
// system rpc
RELOAD_APP: 'RELOAD_APP',
OPEN_URL: 'OPEN_URL',
OPEN_FILE: 'OPEN_FILE',
HIDE_DOCK: 'HIDE_DOCK',
SET_CURRENT_LANGUAGE: 'SET_CURRENT_LANGUAGE',
OPEN_WINDOW: 'OPEN_WINDOW',
OPEN_MINI_WINDOW: 'OPEN_MINI_WINDOW',
CLOSE_WINDOW: 'CLOSE_WINDOW',
MINIMIZE_WINDOW: 'MINIMIZE_WINDOW',
SHOW_MINI_PAGE_MENU: 'SHOW_MINI_PAGE_MENU',
SHOW_MAIN_PAGE_MENU: 'SHOW_MAIN_PAGE_MENU',
SHOW_UPLOAD_PAGE_MENU: 'SHOW_UPLOAD_PAGE_MENU',
SHOW_SECOND_UPLOADER_MENU: 'SHOW_SECOND_UPLOADER_MENU',
SHOW_PLUGIN_PAGE_MENU: 'SHOW_PLUGIN_PAGE_MENU',
SET_MINI_WINDOW_POS: 'SET_MINI_WINDOW_POS',
MINI_WINDOW_ON_TOP: 'MINI_WINDOW_ON_TOP',
MAIN_WINDOW_ON_TOP: 'MAIN_WINDOW_ON_TOP',
UPDATE_MINI_WINDOW_ICON: 'UPDATE_MINI_WINDOW_ICON',
REFRESH_SETTING_WINDOW: 'REFRESH_SETTING_WINDOW',
// picbed RPC
PICBED_GET_PICBED_CONFIG: 'PICBED_GET_PICBED_CONFIG',
PICBED_GET_CONFIG_LIST: 'PICBED_GET_CONFIG_LIST',
PICBED_DELETE_CONFIG: 'PICBED_DELETE_CONFIG',
UPLOADER_CHANGE_CURRENT: 'UPLOADER_CHANGE_CURRENT',
UPLOADER_SELECT: 'UPLOADER_SELECT',
UPLOADER_UPDATE_CONFIG: 'UPLOADER_UPDATE_CONFIG',
UPLOADER_RESET_CONFIG: 'UPLOADER_RESET_CONFIG',
DELETE_ALL_API: 'DELETE_ALL_API',
// toolbox rpc
TOOLBOX_CHECK: 'TOOLBOX_CHECK',
TOOLBOX_CHECK_RES: 'TOOLBOX_CHECK_RES',
TOOLBOX_CHECK_FIX: 'TOOLBOX_CHECK_FIX',
// main app setting rpc
PICLIST_GET_CONFIG: 'PICLIST_GET_CONFIG',
PICLIST_GET_CONFIG_SYNC: 'PICLIST_GET_CONFIG_SYNC',
PICLIST_SAVE_CONFIG: 'PICLIST_SAVE_CONFIG',
PICLIST_OPEN_FILE: 'PICLIST_OPEN_FILE',
PICLIST_OPEN_DIRECTORY: 'PICLIST_OPEN_DIRECTORY',
PICLIST_AUTO_START: 'PICLIST_AUTO_START',
// shortkey setting rpc
SHORTKEY_UPDATE: 'SHORTKEY_UPDATE',
SHORTKEY_BIND_OR_UNBIND: 'SHORTKEY_BIND_OR_UNBIND',
SHORTKEY_TOGGLE_SHORTKEY_MODIFIED_MODE: 'SHORTKEY_TOGGLE_SHORTKEY_MODIFIED_MODE',
// configuration setting rpc
CONFIGURE_MIGRATE_FROM_PICGO: 'CONFIGURE_MIGRATE_FROM_PICGO',
CONFIGURE_UPLOAD_COMMON_CONFIG: 'CONFIGURE_UPLOAD_COMMON_CONFIG',
CONFIGURE_UPLOAD_MANAGE_CONFIG: 'CONFIGURE_UPLOAD_MANAGE_CONFIG',
CONFIGURE_UPLOAD_ALL_CONFIG: 'CONFIGURE_UPLOAD_ALL_CONFIG',
CONFIGURE_DOWNLOAD_COMMON_CONFIG: 'CONFIGURE_DOWNLOAD_COMMON_CONFIG',
CONFIGURE_DOWNLOAD_MANAGE_CONFIG: 'CONFIGURE_DOWNLOAD_MANAGE_CONFIG',
CONFIGURE_DOWNLOAD_ALL_CONFIG: 'CONFIGURE_DOWNLOAD_ALL_CONFIG',
// advanced setting rpc
ADVANCED_UPDATE_SERVER: 'ADVANCED_UPDATE_SERVER',
ADVANCED_STOP_WEB_SERVER: 'ADVANCED_STOP_WEB_SERVER',
ADVANCED_RESTART_WEB_SERVER: 'ADVANCED_RESTART_WEB_SERVER',
// upload and main page rpc
MAIN_GET_PICBED: 'MAIN_GET_PICBED',
UPLOAD_CLIPBOARD_FILES_FROM_UPLOAD_PAGE: 'UPLOAD_CLIPBOARD_FILES_FROM_UPLOAD_PAGE',
UPLOAD_CHOOSED_FILES: 'UPLOAD_CHOOSED_FILES',
// gallery rpc
GALLERY_PASTE_TEXT: 'GALLERY_PASTE_TEXT',
GALLERY_REMOVE_FILES: 'GALLERY_REMOVE_FILES',
GALLERY_GET_DB: 'GALLERY_GET_DB',
GALLERY_GET_BY_ID_DB: 'GALLERY_GET_BY_ID_DB',
GALLERY_UPDATE_BY_ID_DB: 'GALLERY_UPDATE_BY_ID_DB',
GALLERY_REMOVE_BY_ID_DB: 'GALLERY_REMOVE_BY_ID_DB',
GALLERY_INSERT_DB: 'GALLERY_INSERT_DB',
GALLERY_INSERT_DB_BATCH: 'GALLERY_INSERT_DB_BATCH',
// plugin rpc
PLUGIN_GET_LIST: 'PLUGIN_GET_LIST',
PLUGIN_INSTALL: 'PLUGIN_INSTALL',
PLUGIN_IMPORT_LOCAL: 'PLUGIN_IMPORT_LOCAL',
PLUGIN_UPDATE_ALL: 'PLUGIN_UPDATE_ALL',
// tray rpc
TRAY_SET_TOOL_TIP: 'TRAY_SET_TOOL_TIP',
TRAY_GET_SHORT_URL: 'TRAY_GET_SHORT_URL',
TRAY_UPLOAD_CLIPBOARD_FILES: 'TRAY_UPLOAD_CLIPBOARD_FILES',
// manage rpc
MANAGE_GET_CONFIG: 'MANAGE_GET_CONFIG',
MANAGE_SAVE_CONFIG: 'MANAGE_SAVE_CONFIG',
MANAGE_REMOVE_CONFIG: 'MANAGE_REMOVE_CONFIG',
MANAGE_GET_BUCKET_LIST: 'MANAGE_GET_BUCKET_LIST',
MANAGE_GET_BUCKET_LIST_BACKSTAGE: 'MANAGE_GET_BUCKET_LIST_BACKSTAGE',
MANAGE_GET_BUCKET_LIST_RECURSIVELY: 'MANAGE_GET_BUCKET_LIST_RECURSIVELY',
MANAGE_CREATE_BUCKET: 'MANAGE_CREATE_BUCKET',
MANAGE_GET_BUCKET_FILE_LIST: 'MANAGE_GET_BUCKET_FILE_LIST',
MANAGE_GET_BUCKET_DOMAIN: 'MANAGE_GET_BUCKET_DOMAIN',
MANAGE_SET_BUCKET_ACL_POLICY: 'MANAGE_SET_BUCKET_ACL_POLICY',
MANAGE_RENAME_BUCKET_FILE: 'MANAGE_RENAME_BUCKET_FILE',
MANAGE_DELETE_BUCKET_FILE: 'MANAGE_DELETE_BUCKET_FILE',
MANAGE_DELETE_BUCKET_FOLDER: 'MANAGE_DELETE_BUCKET_FOLDER',
MANAGE_GET_PRE_SIGNED_URL: 'MANAGE_GET_PRE_SIGNED_URL',
MANAGE_UPLOAD_BUCKET_FILE: 'MANAGE_UPLOAD_BUCKET_FILE',
MANAGE_DOWNLOAD_BUCKET_FILE: 'MANAGE_DOWNLOAD_BUCKET_FILE',
MANAGE_CREATE_BUCKET_FOLDER: 'MANAGE_CREATE_BUCKET_FOLDER',
MANAGE_OPEN_FILE_SELECT_DIALOG: 'MANAGE_OPEN_FILE_SELECT_DIALOG',
MANAGE_GET_UPLOAD_TASK_LIST: 'MANAGE_GET_UPLOAD_TASK_LIST',
MANAGE_GET_DOWNLOAD_TASK_LIST: 'MANAGE_GET_DOWNLOAD_TASK_LIST',
MANAGE_DELETE_UPLOADED_TASK: 'MANAGE_DELETE_UPLOADED_TASK',
MANAGE_DELETE_ALL_UPLOADED_TASK: 'MANAGE_DELETE_ALL_UPLOADED_TASK',
MANAGE_DELETE_DOWNLOADED_TASK: 'MANAGE_DELETE_DOWNLOADED_TASK',
MANAGE_DELETE_ALL_DOWNLOADED_TASK: 'MANAGE_DELETE_ALL_DOWNLOADED_TASK',
MANAGE_SELECT_DOWNLOAD_FOLDER: 'MANAGE_SELECT_DOWNLOAD_FOLDER',
MANAGE_GET_DEFAULT_DOWNLOAD_FOLDER: 'MANAGE_GET_DEFAULT_DOWNLOAD_FOLDER',
MANAGE_OPEN_DOWNLOADED_FOLDER: 'MANAGE_OPEN_DOWNLOADED_FOLDER',
MANAGE_OPEN_LOCAL_FILE: 'MANAGE_OPEN_LOCAL_FILE',
MANAGE_DOWNLOAD_FILE_FROM_URL: 'MANAGE_DOWNLOAD_FILE_FROM_URL',
MANAGE_CONVERT_PATH_TO_BASE64: 'MANAGE_CONVERT_PATH_TO_BASE64'
}
export const IToolboxItemType = {
IS_CONFIG_FILE_BROKEN: 'IS_CONFIG_FILE_BROKEN',
IS_GALLERY_FILE_BROKEN: 'IS_GALLERY_FILE_BROKEN',
HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD: 'HAS_PROBLEM_WITH_CLIPBOARD_PIC_UPLOAD',
HAS_PROBLEM_WITH_PROXY: 'HAS_PROBLEM_WITH_PROXY'
}
export const IToolboxItemCheckStatus = {
INIT: 'init',
LOADING: 'loading',
SUCCESS: 'success',
ERROR: 'error'
}
export const ISartMode = {
QUIET: 'quiet',
MINI: 'mini',
MAIN: 'main',
NO_TRAY: 'no-tray'
}
export const II18nLanguage = {
ZH_CN: 'zh-CN',
ZH_TW: 'zh-TW',
EN: 'en'
}

View File

@@ -1,5 +1,5 @@
import { IStringKeyMap } from '#/types/types'
import { RELEASE_URL, RELEASE_URL_BACKUP } from '#/utils/static'
import { RELEASE_URL, RELEASE_URL_BACKUP } from '@/utils/static'
import type { IStringKeyMap } from '#/types/types'
export const getLatestVersion = async (): Promise<string> => {
try {

View File

@@ -1,7 +1,7 @@
import { ref } from 'vue'
import { IRPCActionType } from '#/types/enum'
import { IPicBedType } from '#/types/types'
import { IRPCActionType } from '@/utils/enum'
import type { IPicBedType } from '#/types/types'
console.log('global.ts loaded', window.electron.platform)
const osGlobal = ref<string>(window.electron.platform)

View File

@@ -0,0 +1,71 @@
import { IStringKeyMap } from 'root/src/universal/types/types'
export const RELEASE_URL = 'https://api.github.com/repos/Kuingsmile/PicList/releases'
export const RELEASE_URL_BACKUP = 'https://release.piclist.cn'
export const cancelDownloadLoadingFileList = 'cancelDownloadLoadingFileList'
export const refreshDownloadFileTransferList = 'refreshDownloadFileTransferList'
export const picBedsCanbeDeleted = [
'aliyun',
'alist',
'alistplist',
'aws-s3',
'aws-s3-plist',
'dogecloud',
'github',
'huaweicloud-uploader',
'imgur',
'local',
'lskyplist',
'piclist',
'qiniu',
'sftpplist',
'smms',
'tcyun',
'upyun',
'webdavplist'
]
export const picBedManualUrlList: IStringKeyMap = {
zh_cn: {
advancedpiclist: 'https://piclist.cn/configure.html#%E9%AB%98%E7%BA%A7%E8%87%AA%E5%AE%9A%E4%B9%89',
aliyun: 'https://piclist.cn/configure.html#%E9%98%BF%E9%87%8C%E4%BA%91oss',
alistplist: 'https://piclist.cn/configure.html#alist',
'aws-s3': 'https://piclist.cn/configure.html#%E5%86%85%E7%BD%AEaws-s3',
'aws-s3-plist': 'https://piclist.cn/configure.html#%E5%86%85%E7%BD%AEaws-s3',
github: 'https://piclist.cn/configure.html#github%E5%9B%BE%E5%BA%8A',
githubPlus: 'https://piclist.cn/configure.html#github%E5%9B%BE%E5%BA%8A',
imgur: 'https://piclist.cn/configure.html#imgur',
lankong: 'https://github.com/hellodk34/picgo-plugin-lankong',
local: 'https://piclist.cn/configure.html#%E6%9C%AC%E5%9C%B0%E5%9B%BE%E5%BA%8A',
lskyplist: 'https://piclist.cn/configure.html#%E5%85%B0%E7%A9%BA%E5%9B%BE%E5%BA%8A',
tcyun: 'https://piclist.cn/configure.html#%E8%85%BE%E8%AE%AF%E4%BA%91cos',
piclist: 'https://piclist.cn/configure.html#piclist',
qiniu: 'https://piclist.cn/configure.html#%E4%B8%83%E7%89%9B%E4%BA%91',
sftpplist: 'https://piclist.cn/configure.html#%E5%86%85%E7%BD%AEsftp',
smms: 'https://piclist.cn/configure.html#sm-ms',
upyun: 'https://piclist.cn/configure.html#%E5%8F%88%E6%8B%8D%E4%BA%91',
webdavplist: 'https://piclist.cn/configure.html#webdav'
},
en: {
advancedpiclist: 'https://piclist.cn/en/configure.html#advanced',
aliyun: 'https://piclist.cn/en/configure.html#alibaba-cloud',
alistplist: 'https://piclist.cn/en/configure.html#alist',
'aws-s3': 'https://piclist.cn/en/configure.html#built-in-aws-s3',
'aws-s3-plist': 'https://piclist.cn/en/configure.html#built-in-aws-s3',
github: 'https://piclist.cn/en/configure.html#github',
githubPlus: 'https://piclist.cn/en/configure.html#github',
imgur: 'https://piclist.cn/en/configure.html#imgur',
lankong: 'https://github.com/hellodk34/picgo-plugin-lankong',
local: 'https://piclist.cn/en/configure.html#local-image-hosting',
lskyplist: 'https://piclist.cn/en/configure.html#lsky-pro',
tcyun: 'https://piclist.cn/en/configure.html#tencent-cloud-cos',
piclist: 'https://piclist.cn/en/configure.html#piclist',
qiniu: 'https://piclist.cn/en/configure.html#qiniu-cloud',
sftpplist: 'https://piclist.cn/en/configure.html#built-in-sftp',
smms: 'https://piclist.cn/en/configure.html#sm-ms',
upyun: 'https://piclist.cn/en/configure.html#upyun',
webdavplist: 'https://piclist.cn/en/configure.html#webdav'
}
}