Feature(custom): imporve page loading performance and remove duplicated init call

This commit is contained in:
Kuingsmile
2026-01-10 10:10:00 +08:00
parent a12ff2a52c
commit a569068678
22 changed files with 1092 additions and 1076 deletions

View File

@@ -136,7 +136,7 @@ const buildMainPageMenu = (win: BrowserWindow) => {
} }
const buildSecondPicBedMenu = () => { const buildSecondPicBedMenu = () => {
const picBeds = getPicBeds() const picBeds = getPicBeds().picBeds
const secondUploader = picgo.getConfig(configPaths.picBed.secondUploader) const secondUploader = picgo.getConfig(configPaths.picBed.secondUploader)
const defaultSecondUploaderId = picgo.getConfig(configPaths.picBed.secondUploaderId) const defaultSecondUploaderId = picgo.getConfig(configPaths.picBed.secondUploaderId)
const currentPicBedName = picBeds.find(item => item.type === secondUploader)?.name const currentPicBedName = picBeds.find(item => item.type === secondUploader)?.name
@@ -189,7 +189,7 @@ const buildSecondPicBedMenu = () => {
} }
const buildPicBedListMenu = () => { const buildPicBedListMenu = () => {
const picBeds = getPicBeds() const picBeds = getPicBeds().picBeds
const currentPicBed = picgo.getConfig(configPaths.picBed.uploader) const currentPicBed = picgo.getConfig(configPaths.picBed.uploader)
const currentPicBedName = picBeds.find(item => item.type === currentPicBed)?.name const currentPicBedName = picBeds.find(item => item.type === currentPicBed)?.name
const picBedConfigList = picgo.getConfig<IUploaderConfig>('uploader') const picBedConfigList = picgo.getConfig<IUploaderConfig>('uploader')

View File

@@ -4,6 +4,13 @@ import { configPaths } from '~/utils/configPaths'
const getPicBeds = () => { const getPicBeds = () => {
const picBedTypes = picgo.helper.uploader.getIdList() const picBedTypes = picgo.helper.uploader.getIdList()
const defaultPicBed =
picgo.getConfig<string>(configPaths.picBed.uploader) ||
picgo.getConfig<string>(configPaths.picBed.current) ||
'smms'
const defaultConfig = picgo.getConfig<IStringKeyMap>(`picBed.${defaultPicBed}`) || {}
const defaultId = defaultConfig._id || ''
const defaultConfigName = defaultConfig._configName || ''
const picBedFromDB = picgo.getConfig<IPicBedType[]>(configPaths.picBed.list) || [] const picBedFromDB = picgo.getConfig<IPicBedType[]>(configPaths.picBed.list) || []
const picBeds = picBedTypes const picBeds = picBedTypes
.map((item: string) => { .map((item: string) => {
@@ -20,7 +27,7 @@ const getPicBeds = () => {
} }
return 0 return 0
}) as IPicBedType[] }) as IPicBedType[]
return picBeds return { picBeds, defaultPicBed, defaultId, defaultConfigName }
} }
export default getPicBeds export default getPicBeds

View File

@@ -6,26 +6,20 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import type { IConfig } from 'piclist'
import { onBeforeMount, onMounted } from 'vue' import { onBeforeMount, onMounted } from 'vue'
import UIServiceProvider from '@/components/ui/UIServiceProvider.vue' import UIServiceProvider from '@/components/ui/UIServiceProvider.vue'
import { useAppStore } from '@/hooks/useAppStore' import { useAppStore } from '@/hooks/useAppStore'
import { useATagClick } from '@/hooks/useATagClick' import { useATagClick } from '@/hooks/useATagClick'
import { useStore } from '@/hooks/useStore' import { pageReloadCount, usePicBed } from '@/hooks/useGlobal'
import { getConfig } from '@/utils/dataSender'
import { pageReloadCount } from '@/utils/global'
useATagClick() useATagClick()
const store = useStore()
const appStore = useAppStore() const appStore = useAppStore()
const { updatePicBeds } = usePicBed()
onBeforeMount(async () => { onBeforeMount(() => {
const config = await getConfig<IConfig>() updatePicBeds()
if (config) {
store?.setDefaultPicBed(config?.picBed?.uploader || config?.picBed?.current || 'smms')
}
}) })
onMounted(async () => { onMounted(async () => {

View File

@@ -21,26 +21,6 @@
<transition name="fade-slide" mode="out-in"> <transition name="fade-slide" mode="out-in">
<!-- General Settings Tab --> <!-- General Settings Tab -->
<div v-if="activeTab === 'general'" key="general" class="tab-content"> <div v-if="activeTab === 'general'" key="general" class="tab-content">
<div class="settings-section">
<div class="section-header">
<div class="section-icon">
<FileText :size="20" />
</div>
<div class="section-title-group">
<h2>{{ $t('pages.imageProcess.general.skipProcessExtList') }}</h2>
</div>
</div>
<div class="form-group">
<textarea
v-model="skipProcessForm.skipProcessExtList"
class="form-textarea"
rows="3"
:placeholder="'zip,rar,7z,tar,gz'"
/>
<small>{{ $t('pages.imageProcess.general.skipProcessExtListPlaceholder') }}</small>
</div>
</div>
<div class="settings-section"> <div class="settings-section">
<div class="section-header"> <div class="section-header">
<div class="section-icon"> <div class="section-icon">
@@ -62,6 +42,7 @@
</label> </label>
<PerPicbedSetting <PerPicbedSetting
v-if="!configId"
:map-field="compressForm.isRemoveExifMap" :map-field="compressForm.isRemoveExifMap"
:default-value="defaultCompressSetting.isRemoveExif" :default-value="defaultCompressSetting.isRemoveExif"
field-name="isRemoveExif" field-name="isRemoveExif"
@@ -892,6 +873,29 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Skip Process Tab -->
<div v-else-if="activeTab === 'skipProcess'" key="skipProcess" class="tab-content">
<div class="settings-section">
<div class="section-header">
<div class="section-icon">
<FileText :size="20" />
</div>
<div class="section-title-group">
<h2>{{ $t('pages.imageProcess.general.skipProcessExtList') }}</h2>
</div>
</div>
<div class="form-group">
<textarea
v-model="skipProcessForm.skipProcessExtList"
class="form-textarea"
rows="3"
:placeholder="'zip,rar,7z,tar,gz'"
/>
<small>{{ $t('pages.imageProcess.general.skipProcessExtListPlaceholder') }}</small>
</div>
</div>
</div>
</transition> </transition>
</div> </div>
</div> </div>
@@ -933,7 +937,6 @@ import { useI18n } from 'vue-i18n'
import { configPaths } from '@/utils/configPaths' import { configPaths } from '@/utils/configPaths'
import { getConfig, saveConfig } from '@/utils/dataSender' import { getConfig, saveConfig } from '@/utils/dataSender'
import { updatePicBedGlobal } from '@/utils/global'
import PerPicbedSetting from './PerPicbedSetting.vue' import PerPicbedSetting from './PerPicbedSetting.vue'
@@ -944,6 +947,13 @@ const activeTab = ref('general')
const tabRefs = useTemplateRef('tabRefs') const tabRefs = useTemplateRef('tabRefs')
const tabIndicatorStyle = ref<Record<string, string>>({}) const tabIndicatorStyle = ref<Record<string, string>>({})
interface IProps {
// 传递配置ID以加载特定配置
configId: string
}
const { configId } = defineProps<IProps>()
function updateTabIndicator() { function updateTabIndicator() {
if (!tabRefs.value || tabRefs.value.length === 0) return if (!tabRefs.value || tabRefs.value.length === 0) return
const activeIndex = tabs.value.findIndex(tab => tab.id === activeTab.value) const activeIndex = tabs.value.findIndex(tab => tab.id === activeTab.value)
@@ -985,6 +995,16 @@ const tabs = computed(() => [
label: t('pages.imageProcess.transformSettings'), label: t('pages.imageProcess.transformSettings'),
icon: RotateCw, icon: RotateCw,
}, },
{
id: 'skipProcess',
label: t('pages.imageProcess.skipProcessSettings'),
icon: FileText,
},
{
id: 'rename',
label: t('pages.imageProcess.renameSettings'),
icon: Sliders,
},
]) ])
const waterMarkPositionMap = new Map([ const waterMarkPositionMap = new Map([
@@ -1193,13 +1213,10 @@ function safeSetMapValue(form: any, fieldName: string, picbedType: string, value
} }
} }
onBeforeMount(async () => { onBeforeMount(() => {
await updatePicBedGlobal() initData().then(() => {
await initData()
setTimeout(() => {
isInitialized.value = true isInitialized.value = true
}, 100) })
}) })
watch( watch(

View File

@@ -120,7 +120,7 @@
> >
<ListboxOptions class="listbox-options"> <ListboxOptions class="listbox-options">
<ListboxOption <ListboxOption
v-for="picbed in picBedGlobal" v-for="picbed in picBedG"
:key="picbed.type" :key="picbed.type"
v-slot="{ active, selected }" v-slot="{ active, selected }"
:value="picbed.type" :value="picbed.type"
@@ -173,6 +173,7 @@ import {
TransitionChild, TransitionChild,
TransitionRoot, TransitionRoot,
} from '@headlessui/vue' } from '@headlessui/vue'
import { useStorage } from '@vueuse/core'
import { pick } from 'lodash-es' import { pick } from 'lodash-es'
import { import {
BriefcaseBusiness, BriefcaseBusiness,
@@ -194,21 +195,23 @@ import { computed, nextTick, onBeforeMount, onBeforeUnmount, reactive, Ref, ref,
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { usePicBed } from '@/hooks/useGlobal'
import useMessage from '@/hooks/useMessage' import useMessage from '@/hooks/useMessage'
import * as config from '@/router/config' import * as config from '@/router/config'
import { SHOW_MAIN_PAGE_QRCODE } from '@/utils/constant' import { SHOW_MAIN_PAGE_QRCODE } from '@/utils/constant'
import { getConfig } from '@/utils/dataSender' import { getConfig } from '@/utils/dataSender'
import { IRPCActionType } from '@/utils/enum' import { IRPCActionType } from '@/utils/enum'
import { picBedGlobal, updatePicBedGlobal } from '@/utils/global'
import ThemeSwitcher from './ui/ThemeSwitcher.vue' import ThemeSwitcher from './ui/ThemeSwitcher.vue'
const version = ref(pkg.version) const version = ref(pkg.version)
const isCollapsed = ref(false) const isCollapsed = useStorage('navigation-collapsed', false)
const { t } = useI18n() const { t } = useI18n()
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
const message = useMessage() const message = useMessage()
const { picBedG } = usePicBed()
const routerConfig = reactive(config) const routerConfig = reactive(config)
const qrcodeVisible = ref(false) const qrcodeVisible = ref(false)
const choosedPicBedForQRCode: Ref<string[]> = ref([]) const choosedPicBedForQRCode: Ref<string[]> = ref([])
@@ -216,11 +219,6 @@ const picBedConfigString = ref('')
let removeIpcListener: () => void = () => {} let removeIpcListener: () => void = () => {}
// Save collapsed state to localStorage when it changes
watch(isCollapsed, newValue => {
localStorage.setItem('navigation-collapsed', JSON.stringify(newValue))
})
watch( watch(
() => choosedPicBedForQRCode, () => choosedPicBedForQRCode,
val => { val => {
@@ -235,7 +233,7 @@ watch(
{ deep: true }, { deep: true },
) )
const visiblePicBeds = computed(() => picBedGlobal.value.filter(item => item.visible)) const visiblePicBeds = computed(() => picBedG.value.filter(item => item.visible))
const qrCodeHandler = () => { const qrCodeHandler = () => {
qrcodeVisible.value = true qrcodeVisible.value = true
@@ -279,13 +277,6 @@ function openGithubPage() {
} }
onBeforeMount(() => { onBeforeMount(() => {
// Load collapsed state from localStorage
const savedState = localStorage.getItem('navigation-collapsed')
if (savedState !== null) {
isCollapsed.value = JSON.parse(savedState)
}
updatePicBedGlobal()
removeIpcListener = window.electron.ipcRendererOn(SHOW_MAIN_PAGE_QRCODE, qrCodeHandler) removeIpcListener = window.electron.ipcRendererOn(SHOW_MAIN_PAGE_QRCODE, qrCodeHandler)
}) })
@@ -294,523 +285,4 @@ onBeforeUnmount(() => {
}) })
</script> </script>
<style scoped> <style scoped src="./css/NavigationPage.css"></style>
.navigation {
display: flex;
overflow: hidden;
border-right: 1px solid rgb(229 231 235);
width: 150px;
height: 100vh;
background: var(--color-background-secondary);
transition: width 0.3s ease;
flex-direction: column;
}
.navigation.collapsed {
width: 60px;
}
:root.dark .navigation,
:root.auto.dark .navigation {
border-right-color: var(--color-background-secondary);
background: var(--color-background-secondary);
}
.title-bar {
position: relative;
display: flex;
justify-content: center;
align-items: center;
border-bottom: 1px solid var(--color-border);
padding: 1.25rem 1rem;
background: var(--color-background-secondary);
}
.navigation.collapsed .title-bar {
padding: 1rem 0.5rem;
}
.collapse-button {
position: absolute;
top: 50%;
right: 8px;
display: flex;
justify-content: center;
align-items: center;
border: none;
border-radius: 4px;
padding: 4px;
color: var(--color-text-primary);
background: transparent;
transition: all 0.2s ease;
transform: translateY(-50%);
cursor: pointer;
}
.collapse-button:hover {
color: var(--color-text-primary);
background: var(--color-surface-elevated);
}
.navigation.collapsed .collapse-button {
position: static;
transform: none;
}
:root.dark .title-bar,
:root.auto.dark .title-bar {
border-bottom-color: var(--color-border);
background: var(--color-background-secondary);
}
.app-title {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
}
.app-text {
font-size: 16px;
font-weight: 700;
color: var(--color-text-primary);
letter-spacing: -0.025em;
}
.app-text:hover {
cursor: pointer;
color: var(--color-blue-common);
}
.app-version {
border: 1px solid var(--color-border);
border-radius: 12px;
padding: 3px 8px;
font-size: 10px;
font-weight: 500;
color: var(--color-text-secondary);
background: var(--color-surface-elevated);
}
.theme-section {
display: flex;
justify-content: center;
align-items: center;
border-bottom: 1px solid var(--color-border);
padding: 0.75rem;
}
:root.dark .theme-section,
:root.auto.dark .theme-section {
border-bottom-color: var(--color-border);
}
.nav-menu {
overflow-y: auto;
padding: 1rem 0;
min-height: 0;
flex: 1;
}
.nav-item {
display: flex;
justify-content: center;
align-items: center;
padding: 0.75rem 1rem;
font-size: 0.875rem;
font-weight: 500;
text-decoration: none;
color: rgb(75 85 99);
transition: all 0.2s ease;
gap: 0.75rem;
cursor: pointer;
}
.navigation.collapsed .nav-item {
justify-content: center;
padding: 0.75rem 0.5rem;
gap: 0;
}
.navigation.collapsed .nav-label {
display: none;
}
:root.dark .nav-item,
:root.auto.dark .nav-item {
color: rgb(209 213 219);
}
.nav-item:hover {
color: rgb(17 24 39);
background: rgb(243 244 246);
}
:root.dark .nav-item:hover,
:root.auto.dark .nav-item:hover {
color: rgb(243 244 246);
background: rgb(55 65 81);
}
.nav-item.router-link-active {
border-right: 3px solid rgb(99 102 241);
color: rgb(99 102 241);
background: rgb(239 246 255);
}
:root.dark .nav-item.router-link-active,
:root.auto.dark .nav-item.router-link-active {
border-right-color: rgb(129 140 248);
color: rgb(129 140 248);
background: rgb(30 58 138 / 20%);
}
.nav-icon-container {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 20px;
height: 20px;
flex-shrink: 0;
}
.sidebar-footer {
border-top: 1px solid var(--color-border);
padding: 12px;
}
.footer-button {
position: fixed;
bottom: 4px;
left: 4px;
border: none;
border-radius: 6px;
padding: 8px;
color: var(--color-text-secondary);
background: transparent;
cursor: pointer;
}
.footer-button:hover {
color: var(--color-text-primary);
background: var(--color-surface-elevated);
}
.nav-submenu {
position: relative;
justify-content: center;
margin-top: 4px;
}
.submenu-trigger {
position: relative;
display: flex;
justify-content: center;
align-items: center;
border: none;
padding: 0.75rem 1rem;
width: 100%;
font-size: 0.875rem;
font-weight: 500;
text-decoration: none;
color: rgb(75 85 99);
background: transparent;
transition: all 0.2s ease;
gap: 0.75rem;
cursor: pointer;
}
:root.dark .submenu-trigger,
:root.auto.dark .submenu-trigger {
color: rgb(209 213 219);
}
.submenu-trigger:hover {
color: rgb(17 24 39);
background: rgb(243 244 246);
}
:root.dark .submenu-trigger:hover,
:root.auto.dark .submenu-trigger:hover {
color: rgb(243 244 246);
background: rgb(55 65 81);
}
.submenu-trigger .nav-icon-container {
flex-shrink: 0;
}
.submenu-trigger span {
flex-shrink: 0;
}
.submenu-arrow {
position: absolute;
right: 1rem;
transition: transform 0.2s ease;
flex-shrink: 0;
}
.rotate-180 {
transform: rotate(180deg);
}
.submenu-panel {
display: flex;
margin-top: 2px;
padding-left: 2.75rem;
flex-direction: column;
gap: 4px;
}
.submenu-item {
display: flex;
align-items: center;
border-radius: 6px;
padding: 0.5rem 1rem;
font-size: 0.8125rem;
font-weight: 500;
text-decoration: none;
color: var(--color-text-secondary);
transition: all 0.2s ease;
cursor: pointer;
}
.submenu-item:hover {
color: var(--color-text-primary);
background: var(--color-surface-elevated);
}
.collapsed-picbed {
cursor: default;
}
.collapsed-picbed:hover {
color: rgb(17 24 39);
background: rgb(243 244 246);
}
:root.dark .collapsed-picbed:hover,
:root.auto.dark .collapsed-picbed:hover {
color: rgb(243 244 246);
background: rgb(55 65 81);
}
.qr-dialog {
position: fixed;
z-index: 50;
display: flex;
justify-content: center;
align-items: center;
overflow-y: auto;
inset: 0;
}
.dialog-container {
position: fixed;
z-index: 50;
display: flex;
justify-content: center;
align-items: center;
overflow-y: auto;
padding: 16px;
min-height: 100vh;
inset: 0;
}
.dialog-panel {
overflow: hidden;
border: 1px solid var(--color-border);
border-radius: 16px;
width: 100%;
max-width: 500px;
background: var(--color-background-primary);
box-shadow: var(--shadow-md);
}
.dialog-title {
margin: 0;
padding: 20px 24px 0;
font-size: 18px;
font-weight: 600;
color: var(--color-text-primary);
}
.dialog-content {
padding: 20px 24px;
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
margin-bottom: 8px;
font-size: 14px;
font-weight: 500;
color: var(--color-text-primary);
}
.listbox-container {
position: relative;
}
.listbox-button {
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
padding: 12px 16px;
width: 100%;
font-size: 14px;
color: var(--color-text-primary);
background: var(--color-surface);
transition: var(--transition);
cursor: pointer;
}
.listbox-button:hover {
border-color: var(--color-accent);
}
.placeholder {
color: var(--color-text-secondary);
}
.selected-count {
color: var(--color-text-primary);
}
.listbox-arrow {
color: var(--color-text-secondary);
}
.listbox-options {
position: absolute;
top: 100%;
right: 0;
left: 0;
z-index: 10;
overflow-y: auto;
margin-top: 4px;
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
max-height: 300px;
background: var(--color-background-primary);
box-shadow: var(--shadow-md);
}
.listbox-option {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
font-size: 14px;
color: var(--color-text-primary);
transition: var(--transition);
cursor: pointer;
}
.listbox-option.active {
background: var(--color-surface-elevated);
}
.listbox-option.selected {
color: white;
background: var(--color-accent);
}
.copy-button {
display: flex;
align-items: center;
margin-top: 12px;
border: none;
border-radius: var(--border-radius);
padding: 10px 16px;
font-size: 14px;
font-weight: 500;
color: white;
background: var(--color-accent);
transition: var(--transition);
gap: 8px;
cursor: pointer;
}
.copy-button:hover {
background: var(--color-accent-hover);
}
.qr-container {
display: flex;
justify-content: center;
padding: 20px 0;
}
.qr-code {
overflow: hidden;
border-radius: var(--border-radius);
box-shadow: var(--shadow-sm);
}
.dialog-actions {
display: flex;
justify-content: flex-end;
padding: 0 24px 20px;
gap: 12px;
}
.cancel-button {
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
padding: 10px 20px;
font-size: 14px;
color: var(--color-text-primary);
background: var(--color-surface-elevated);
transition: var(--transition);
cursor: pointer;
}
.cancel-button:hover {
background: var(--color-border);
}
/* Responsive Design */
@media (width <= 768px) {
.navigation {
width: 60px;
}
.nav-label {
display: none;
}
.app-title {
display: none;
}
.collapse-button {
display: none;
}
}
/* Scrollbar Styling */
::-webkit-scrollbar {
display: none;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
border-radius: 0;
background: var(--color-border);
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-text-secondary);
}
</style>

View File

@@ -112,9 +112,10 @@ import { Settings } from 'lucide-vue-next'
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { picBedGlobal } from '@/utils/global' import { usePicBed } from '@/hooks/useGlobal'
const { t } = useI18n() const { t } = useI18n()
const { picBedG } = usePicBed()
interface SelectOption { interface SelectOption {
value: string | number value: string | number
@@ -166,7 +167,7 @@ const emit = defineEmits<{
const showSettings = ref(false) const showSettings = ref(false)
const availablePicbeds = computed(() => { const availablePicbeds = computed(() => {
return picBedGlobal.value.map(picbed => ({ return picBedG.value.map(picbed => ({
type: picbed.type, type: picbed.type,
name: picbed.name, name: picbed.name,
})) }))

View File

@@ -1,6 +1,6 @@
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<template> <template>
<div id="config-form" :class="[{ white: props.colorMode === 'white' }]"> <div id="config-form" :class="[{ white: colorMode === 'white' }]">
<form class="config-form" @submit.prevent> <form class="config-form" @submit.prevent>
<!-- Config Name Field --> <!-- Config Name Field -->
<div class="form-group required"> <div class="form-group required">
@@ -125,14 +125,14 @@
import { cloneDeep, union } from 'lodash-es' import { cloneDeep, union } from 'lodash-es'
import { ChevronDownIcon, Info } from 'lucide-vue-next' import { ChevronDownIcon, Info } from 'lucide-vue-next'
import { marked } from 'marked' import { marked } from 'marked'
import { reactive, ref, toRefs, watch } from 'vue' import { reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { getConfig } from '@/utils/dataSender' import { getConfig } from '@/utils/dataSender'
interface IProps { interface IProps {
config: any[] config: IPicGoPluginConfig[]
type: 'uploader' | 'transformer' | 'plugin' type: 'uploader' | 'transformer' | 'plugin'
id: string id: string
colorMode?: 'white' | 'dark' colorMode?: 'white' | 'dark'
@@ -140,11 +140,14 @@ interface IProps {
showTooltips?: boolean showTooltips?: boolean
} }
const props = withDefaults(defineProps<IProps>(), { const {
colorMode: undefined, config: configProp,
mode: 'picbed', type,
showTooltips: true, id,
}) colorMode = undefined,
mode = 'picbed',
showTooltips = true,
} = defineProps<IProps>()
const $route = useRoute() const $route = useRoute()
const { t } = useI18n() const { t } = useI18n()
@@ -156,9 +159,9 @@ const visibleTooltips = reactive<Record<string, boolean>>({})
// Watch for config changes // Watch for config changes
watch( watch(
toRefs(props.config), () => configProp,
(val: IPicGoPluginConfig[]) => { newVal => {
handleConfig(val) handleConfig(newVal)
}, },
{ {
deep: true, deep: true,
@@ -191,10 +194,7 @@ function validateForm(): boolean {
errors[config.name] = error errors[config.name] = error
} }
}) })
for (const key in validationErrors) delete validationErrors[key]
Object.keys(validationErrors).forEach(key => {
delete validationErrors[key]
})
Object.assign(validationErrors, errors) Object.assign(validationErrors, errors)
@@ -256,15 +256,15 @@ function transformMarkdownToHTML(markdown: string) {
} }
function getConfigType() { function getConfigType() {
switch (props.type) { switch (type) {
case 'plugin': { case 'plugin': {
return props.id return id
} }
case 'uploader': { case 'uploader': {
return `picBed.${props.id}` return `picBed.${id}`
} }
case 'transformer': { case 'transformer': {
return `transformer.${props.id}` return `transformer.${id}`
} }
default: default:
return 'unknown' return 'unknown'
@@ -273,14 +273,14 @@ function getConfigType() {
async function handleConfig(val: IPicGoPluginConfig[]) { async function handleConfig(val: IPicGoPluginConfig[]) {
const config = await getCurConfigFormData() const config = await getCurConfigFormData()
const configId = props.mode === 'picbed' ? $route.params.configId : null const configId = mode === 'picbed' ? $route.params.configId : null
Object.assign(ruleForm, config) Object.assign(ruleForm, config)
if (val.length > 0) { if (val.length > 0) {
configList.value = cloneDeep(val).map(item => { configList.value = cloneDeep(val).map(item => {
// For plugin mode, don't check configId // For plugin mode, don't check configId
if (props.mode === 'plugin' || !configId) { if (mode === 'plugin' || !configId) {
let defaultValue = item.default !== undefined ? item.default : item.type === 'checkbox' ? [] : null let defaultValue = item.default !== undefined ? item.default : item.type === 'checkbox' ? [] : null
if (item.type === 'checkbox') { if (item.type === 'checkbox') {
@@ -314,11 +314,11 @@ async function handleConfig(val: IPicGoPluginConfig[]) {
} }
async function getCurConfigFormData() { async function getCurConfigFormData() {
if (props.mode === 'plugin') { if (mode === 'plugin') {
return (await getConfig<IStringKeyMap>(`${props.id}`)) || {} return (await getConfig<IStringKeyMap>(`${id}`)) || {}
} else { } else {
const configId = $route.params.configId const configId = $route.params.configId
const curTypeConfigList = (await getConfig<IStringKeyMap[]>(`uploader.${props.id}.configList`)) || [] const curTypeConfigList = (await getConfig<IStringKeyMap[]>(`uploader.${id}.configList`)) || []
return curTypeConfigList.find(i => i._id === configId) || {} return curTypeConfigList.find(i => i._id === configId) || {}
} }
} }
@@ -339,386 +339,4 @@ defineExpose({
}) })
</script> </script>
<style scoped> <style scoped src="./css/UnifiedConfigForm.css"></style>
#config-form {
width: 100%;
}
.config-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
/* Form Groups */
.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.form-group.required .form-label::after {
content: ' *';
color: var(--color-error, #ef4444);
}
.form-label-wrapper {
display: flex;
align-items: center;
gap: 0.5rem;
}
.form-label {
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text-primary);
line-height: 1.25;
}
.form-control {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
/* Tooltip Styles */
.tooltip-wrapper {
position: relative;
}
.info-icon {
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
padding: 2px;
color: var(--color-text-secondary);
transition: var(--transition-fast);
cursor: pointer;
}
.info-icon:hover {
color: var(--color-accent);
background: rgb(0 122 255 / 10%);
}
.tooltip-content {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 0.75rem;
min-width: 200px;
max-width: 300px;
font-size: 0.75rem;
color: var(--color-text-primary);
background: var(--color-surface-elevated);
box-shadow: var(--shadow-lg);
line-height: 1.4;
}
/* Input Styles */
.form-input {
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 0.75rem 1rem;
width: 100%;
font-size: 0.875rem;
font-family: inherit;
color: var(--color-text-primary);
background: var(--color-surface-elevated);
transition: var(--transition-fast);
}
.form-input:focus {
border-color: var(--color-accent);
outline: none;
box-shadow: 0 0 0 2px rgb(0 122 255 / 20%);
}
.form-input::placeholder {
color: var(--color-text-secondary);
}
.form-input.error {
border-color: var(--color-error, #ef4444);
}
.form-input.error:focus {
box-shadow: 0 0 0 2px rgb(239 68 68 / 20%);
}
/* Select Styles */
.select-wrapper {
position: relative;
}
.form-select {
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 0.75rem 2.5rem 0.75rem 1rem;
width: 100%;
font-size: 0.875rem;
font-family: inherit;
color: var(--color-text-primary);
background: var(--color-surface-elevated);
transition: var(--transition-fast);
appearance: none;
cursor: pointer;
}
.form-select:focus {
border-color: var(--color-accent);
outline: none;
box-shadow: 0 0 0 2px rgb(0 122 255 / 20%);
}
.form-select.error {
border-color: var(--color-error, #ef4444);
}
.form-select.error:focus {
box-shadow: 0 0 0 2px rgb(239 68 68 / 20%);
}
.select-arrow {
position: absolute;
top: 50%;
right: 1rem;
color: var(--color-text-secondary);
transition: var(--transition-fast);
transform: translateY(-50%);
pointer-events: none;
}
.select-wrapper:hover .select-arrow,
.form-select:focus + .select-arrow {
color: var(--color-accent);
}
/* Checkbox Group Styles */
.checkbox-group {
display: flex;
flex-direction: column;
gap: 0.75rem;
padding: 0.5rem 0;
}
.checkbox-item {
display: flex;
align-items: center;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 0.75rem;
cursor: pointer;
font-size: 0.875rem;
color: var(--color-text-primary);
transition: var(--transition-fast);
}
.checkbox-label:hover {
color: var(--color-accent);
}
.checkbox-input {
position: absolute;
opacity: 0;
cursor: pointer;
}
.checkbox-custom {
position: relative;
border: 2px solid var(--color-border);
border-radius: var(--radius-sm);
width: 1.25rem;
height: 1.25rem;
background: var(--color-surface-elevated);
transition: var(--transition-fast);
flex-shrink: 0;
}
.checkbox-custom::after {
position: absolute;
top: 0;
left: 3px;
border: solid white;
border-width: 0 2px 2px 0;
width: 6px;
height: 10px;
opacity: 0;
transition: var(--transition-fast);
content: '';
transform: rotate(45deg);
}
.checkbox-input:checked + .checkbox-custom {
border-color: var(--color-accent);
background: var(--color-accent);
}
.checkbox-input:checked + .checkbox-custom::after {
opacity: 1;
}
.checkbox-input:focus + .checkbox-custom {
box-shadow: 0 0 0 2px rgb(0 122 255 / 20%);
}
.checkbox-text {
flex: 1;
}
/* Switch Styles */
.switch-label {
display: flex;
align-items: center;
gap: 1rem;
cursor: pointer;
font-size: 0.875rem;
color: var(--color-text-primary);
}
.switch-input {
position: absolute;
opacity: 0;
cursor: pointer;
}
.switch-slider {
position: relative;
border-radius: 0.75rem;
width: 3rem;
height: 1.5rem;
background: var(--color-border);
transition: var(--transition-fast);
flex-shrink: 0;
}
.switch-button {
position: absolute;
top: 2px;
left: 2px;
border-radius: 50%;
width: 1.25rem;
height: 1.25rem;
background: white;
box-shadow: var(--shadow-sm);
transition: var(--transition-fast);
}
.switch-input:checked + .switch-slider {
background: var(--color-accent);
}
.switch-input:checked + .switch-slider .switch-button {
transform: translateX(1.5rem);
}
.switch-input:focus + .switch-slider {
box-shadow: 0 0 0 2px rgb(0 122 255 / 20%);
}
.switch-text {
font-weight: 500;
color: var(--color-text-secondary);
}
.switch-input:checked ~ .switch-text {
color: var(--color-accent);
}
/* Error Message */
.error-message {
margin-top: 0.25rem;
font-size: 0.75rem;
color: var(--color-error, #ef4444);
}
/* White theme adjustments */
.white .form-input,
.white .form-select {
border-color: #dddddd;
background: white;
}
.white .form-input:focus,
.white .form-select:focus {
border-color: var(--color-accent);
}
.white .checkbox-custom {
border-color: #dddddd;
background: white;
}
.white .switch-slider {
background: #dddddd;
}
.white .tooltip-content {
border-color: #dddddd;
background: white;
}
/* Responsive Design */
@media (width <= 768px) {
.config-form {
gap: 1.25rem;
}
.form-input,
.form-select {
padding: 0.625rem 0.875rem;
}
.form-select {
padding-right: 2.25rem;
}
.tooltip-content {
min-width: 150px;
max-width: 250px;
}
}
/* Dark mode adjustments */
:root.dark .form-input,
:root.auto.dark .form-input,
:root.dark .form-select,
:root.auto.dark .form-select {
border-color: var(--color-border);
background: var(--color-surface-elevated);
}
:root.dark .checkbox-custom,
:root.auto.dark .checkbox-custom {
border-color: var(--color-border);
background: var(--color-surface-elevated);
}
:root.dark .switch-slider,
:root.auto.dark .switch-slider {
background: var(--color-border);
}
:root.dark .tooltip-content,
:root.auto.dark .tooltip-content {
border-color: var(--color-border);
background: var(--color-surface-elevated);
}
/* Focus styles for accessibility */
.form-input:focus-visible,
.form-select:focus-visible,
.checkbox-input:focus-visible + .checkbox-custom,
.switch-input:focus-visible + .switch-slider,
.info-icon:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
</style>

View File

@@ -0,0 +1,518 @@
.navigation {
display: flex;
overflow: hidden;
border-right: 1px solid rgb(229 231 235);
width: 150px;
height: 100vh;
background: var(--color-background-secondary);
transition: width 0.3s ease;
flex-direction: column;
}
.navigation.collapsed {
width: 60px;
}
:root.dark .navigation,
:root.auto.dark .navigation {
border-right-color: var(--color-background-secondary);
background: var(--color-background-secondary);
}
.title-bar {
position: relative;
display: flex;
justify-content: center;
align-items: center;
border-bottom: 1px solid var(--color-border);
padding: 1.25rem 1rem;
background: var(--color-background-secondary);
}
.navigation.collapsed .title-bar {
padding: 1rem 0.5rem;
}
.collapse-button {
position: absolute;
top: 50%;
right: 8px;
display: flex;
justify-content: center;
align-items: center;
border: none;
border-radius: 4px;
padding: 4px;
color: var(--color-text-primary);
background: transparent;
transition: all 0.2s ease;
transform: translateY(-50%);
cursor: pointer;
}
.collapse-button:hover {
color: var(--color-text-primary);
background: var(--color-surface-elevated);
}
.navigation.collapsed .collapse-button {
position: static;
transform: none;
}
:root.dark .title-bar,
:root.auto.dark .title-bar {
border-bottom-color: var(--color-border);
background: var(--color-background-secondary);
}
.app-title {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
}
.app-text {
font-size: 16px;
font-weight: 700;
color: var(--color-text-primary);
letter-spacing: -0.025em;
}
.app-text:hover {
cursor: pointer;
color: var(--color-blue-common);
}
.app-version {
border: 1px solid var(--color-border);
border-radius: 12px;
padding: 3px 8px;
font-size: 10px;
font-weight: 500;
color: var(--color-text-secondary);
background: var(--color-surface-elevated);
}
.theme-section {
display: flex;
justify-content: center;
align-items: center;
border-bottom: 1px solid var(--color-border);
padding: 0.75rem;
}
:root.dark .theme-section,
:root.auto.dark .theme-section {
border-bottom-color: var(--color-border);
}
.nav-menu {
overflow-y: auto;
padding: 1rem 0;
min-height: 0;
flex: 1;
}
.nav-item {
display: flex;
justify-content: center;
align-items: center;
padding: 0.75rem 1rem;
font-size: 0.875rem;
font-weight: 500;
text-decoration: none;
color: rgb(75 85 99);
transition: all 0.2s ease;
gap: 0.75rem;
cursor: pointer;
}
.navigation.collapsed .nav-item {
justify-content: center;
padding: 0.75rem 0.5rem;
gap: 0;
}
.navigation.collapsed .nav-label {
display: none;
}
:root.dark .nav-item,
:root.auto.dark .nav-item {
color: rgb(209 213 219);
}
.nav-item:hover {
color: rgb(17 24 39);
background: rgb(243 244 246);
}
:root.dark .nav-item:hover,
:root.auto.dark .nav-item:hover {
color: rgb(243 244 246);
background: rgb(55 65 81);
}
.nav-item.router-link-active {
border-right: 3px solid rgb(99 102 241);
color: rgb(99 102 241);
background: rgb(239 246 255);
}
:root.dark .nav-item.router-link-active,
:root.auto.dark .nav-item.router-link-active {
border-right-color: rgb(129 140 248);
color: rgb(129 140 248);
background: rgb(30 58 138 / 20%);
}
.nav-icon-container {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 20px;
height: 20px;
flex-shrink: 0;
}
.sidebar-footer {
border-top: 1px solid var(--color-border);
padding: 12px;
}
.footer-button {
position: fixed;
bottom: 4px;
left: 4px;
border: none;
border-radius: 6px;
padding: 8px;
color: var(--color-text-secondary);
background: transparent;
cursor: pointer;
}
.footer-button:hover {
color: var(--color-text-primary);
background: var(--color-surface-elevated);
}
.nav-submenu {
position: relative;
justify-content: center;
margin-top: 4px;
}
.submenu-trigger {
position: relative;
display: flex;
justify-content: center;
align-items: center;
border: none;
padding: 0.75rem 1rem;
width: 100%;
font-size: 0.875rem;
font-weight: 500;
text-decoration: none;
color: rgb(75 85 99);
background: transparent;
transition: all 0.2s ease;
gap: 0.75rem;
cursor: pointer;
}
:root.dark .submenu-trigger,
:root.auto.dark .submenu-trigger {
color: rgb(209 213 219);
}
.submenu-trigger:hover {
color: rgb(17 24 39);
background: rgb(243 244 246);
}
:root.dark .submenu-trigger:hover,
:root.auto.dark .submenu-trigger:hover {
color: rgb(243 244 246);
background: rgb(55 65 81);
}
.submenu-trigger .nav-icon-container {
flex-shrink: 0;
}
.submenu-trigger span {
flex-shrink: 0;
}
.submenu-arrow {
position: absolute;
right: 1rem;
transition: transform 0.2s ease;
flex-shrink: 0;
}
.rotate-180 {
transform: rotate(180deg);
}
.submenu-panel {
display: flex;
margin-top: 2px;
padding-left: 2.75rem;
flex-direction: column;
gap: 4px;
}
.submenu-item {
display: flex;
align-items: center;
border-radius: 6px;
padding: 0.5rem 1rem;
font-size: 0.8125rem;
font-weight: 500;
text-decoration: none;
color: var(--color-text-secondary);
transition: all 0.2s ease;
cursor: pointer;
}
.submenu-item:hover {
color: var(--color-text-primary);
background: var(--color-surface-elevated);
}
.collapsed-picbed {
cursor: default;
}
.collapsed-picbed:hover {
color: rgb(17 24 39);
background: rgb(243 244 246);
}
:root.dark .collapsed-picbed:hover,
:root.auto.dark .collapsed-picbed:hover {
color: rgb(243 244 246);
background: rgb(55 65 81);
}
.qr-dialog {
position: fixed;
z-index: 50;
display: flex;
justify-content: center;
align-items: center;
overflow-y: auto;
inset: 0;
}
.dialog-container {
position: fixed;
z-index: 50;
display: flex;
justify-content: center;
align-items: center;
overflow-y: auto;
padding: 16px;
min-height: 100vh;
inset: 0;
}
.dialog-panel {
overflow: hidden;
border: 1px solid var(--color-border);
border-radius: 16px;
width: 100%;
max-width: 500px;
background: var(--color-background-primary);
box-shadow: var(--shadow-md);
}
.dialog-title {
margin: 0;
padding: 20px 24px 0;
font-size: 18px;
font-weight: 600;
color: var(--color-text-primary);
}
.dialog-content {
padding: 20px 24px;
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
margin-bottom: 8px;
font-size: 14px;
font-weight: 500;
color: var(--color-text-primary);
}
.listbox-container {
position: relative;
}
.listbox-button {
display: flex;
justify-content: space-between;
align-items: center;
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
padding: 12px 16px;
width: 100%;
font-size: 14px;
color: var(--color-text-primary);
background: var(--color-surface);
transition: var(--transition);
cursor: pointer;
}
.listbox-button:hover {
border-color: var(--color-accent);
}
.placeholder {
color: var(--color-text-secondary);
}
.selected-count {
color: var(--color-text-primary);
}
.listbox-arrow {
color: var(--color-text-secondary);
}
.listbox-options {
position: absolute;
top: 100%;
right: 0;
left: 0;
z-index: 10;
overflow-y: auto;
margin-top: 4px;
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
max-height: 300px;
background: var(--color-background-primary);
box-shadow: var(--shadow-md);
}
.listbox-option {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
font-size: 14px;
color: var(--color-text-primary);
transition: var(--transition);
cursor: pointer;
}
.listbox-option.active {
background: var(--color-surface-elevated);
}
.listbox-option.selected {
color: white;
background: var(--color-accent);
}
.copy-button {
display: flex;
align-items: center;
margin-top: 12px;
border: none;
border-radius: var(--border-radius);
padding: 10px 16px;
font-size: 14px;
font-weight: 500;
color: white;
background: var(--color-accent);
transition: var(--transition);
gap: 8px;
cursor: pointer;
}
.copy-button:hover {
background: var(--color-accent-hover);
}
.qr-container {
display: flex;
justify-content: center;
padding: 20px 0;
}
.qr-code {
overflow: hidden;
border-radius: var(--border-radius);
box-shadow: var(--shadow-sm);
}
.dialog-actions {
display: flex;
justify-content: flex-end;
padding: 0 24px 20px;
gap: 12px;
}
.cancel-button {
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
padding: 10px 20px;
font-size: 14px;
color: var(--color-text-primary);
background: var(--color-surface-elevated);
transition: var(--transition);
cursor: pointer;
}
.cancel-button:hover {
background: var(--color-border);
}
/* Responsive Design */
@media (width <= 768px) {
.navigation {
width: 60px;
}
.nav-label {
display: none;
}
.app-title {
display: none;
}
.collapse-button {
display: none;
}
}
/* Scrollbar Styling */
::-webkit-scrollbar {
display: none;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
border-radius: 0;
background: var(--color-border);
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-text-secondary);
}

View File

@@ -0,0 +1,381 @@
#config-form {
width: 100%;
}
.config-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
/* Form Groups */
.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.form-group.required .form-label::after {
content: ' *';
color: var(--color-error, #ef4444);
}
.form-label-wrapper {
display: flex;
align-items: center;
gap: 0.5rem;
}
.form-label {
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text-primary);
line-height: 1.25;
}
.form-control {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
/* Tooltip Styles */
.tooltip-wrapper {
position: relative;
}
.info-icon {
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
padding: 2px;
color: var(--color-text-secondary);
transition: var(--transition-fast);
cursor: pointer;
}
.info-icon:hover {
color: var(--color-accent);
background: rgb(0 122 255 / 10%);
}
.tooltip-content {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 0.75rem;
min-width: 200px;
max-width: 300px;
font-size: 0.75rem;
color: var(--color-text-primary);
background: var(--color-surface-elevated);
box-shadow: var(--shadow-lg);
line-height: 1.4;
}
/* Input Styles */
.form-input {
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 0.75rem 1rem;
width: 100%;
font-size: 0.875rem;
font-family: inherit;
color: var(--color-text-primary);
background: var(--color-surface-elevated);
transition: var(--transition-fast);
}
.form-input:focus {
border-color: var(--color-accent);
outline: none;
box-shadow: 0 0 0 2px rgb(0 122 255 / 20%);
}
.form-input::placeholder {
color: var(--color-text-secondary);
}
.form-input.error {
border-color: var(--color-error, #ef4444);
}
.form-input.error:focus {
box-shadow: 0 0 0 2px rgb(239 68 68 / 20%);
}
/* Select Styles */
.select-wrapper {
position: relative;
}
.form-select {
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 0.75rem 2.5rem 0.75rem 1rem;
width: 100%;
font-size: 0.875rem;
font-family: inherit;
color: var(--color-text-primary);
background: var(--color-surface-elevated);
transition: var(--transition-fast);
appearance: none;
cursor: pointer;
}
.form-select:focus {
border-color: var(--color-accent);
outline: none;
box-shadow: 0 0 0 2px rgb(0 122 255 / 20%);
}
.form-select.error {
border-color: var(--color-error, #ef4444);
}
.form-select.error:focus {
box-shadow: 0 0 0 2px rgb(239 68 68 / 20%);
}
.select-arrow {
position: absolute;
top: 50%;
right: 1rem;
color: var(--color-text-secondary);
transition: var(--transition-fast);
transform: translateY(-50%);
pointer-events: none;
}
.select-wrapper:hover .select-arrow,
.form-select:focus + .select-arrow {
color: var(--color-accent);
}
/* Checkbox Group Styles */
.checkbox-group {
display: flex;
flex-direction: column;
gap: 0.75rem;
padding: 0.5rem 0;
}
.checkbox-item {
display: flex;
align-items: center;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 0.75rem;
cursor: pointer;
font-size: 0.875rem;
color: var(--color-text-primary);
transition: var(--transition-fast);
}
.checkbox-label:hover {
color: var(--color-accent);
}
.checkbox-input {
position: absolute;
opacity: 0;
cursor: pointer;
}
.checkbox-custom {
position: relative;
border: 2px solid var(--color-border);
border-radius: var(--radius-sm);
width: 1.25rem;
height: 1.25rem;
background: var(--color-surface-elevated);
transition: var(--transition-fast);
flex-shrink: 0;
}
.checkbox-custom::after {
position: absolute;
top: 0;
left: 3px;
border: solid white;
border-width: 0 2px 2px 0;
width: 6px;
height: 10px;
opacity: 0;
transition: var(--transition-fast);
content: '';
transform: rotate(45deg);
}
.checkbox-input:checked + .checkbox-custom {
border-color: var(--color-accent);
background: var(--color-accent);
}
.checkbox-input:checked + .checkbox-custom::after {
opacity: 1;
}
.checkbox-input:focus + .checkbox-custom {
box-shadow: 0 0 0 2px rgb(0 122 255 / 20%);
}
.checkbox-text {
flex: 1;
}
/* Switch Styles */
.switch-label {
display: flex;
align-items: center;
gap: 1rem;
cursor: pointer;
font-size: 0.875rem;
color: var(--color-text-primary);
}
.switch-input {
position: absolute;
opacity: 0;
cursor: pointer;
}
.switch-slider {
position: relative;
border-radius: 0.75rem;
width: 3rem;
height: 1.5rem;
background: var(--color-border);
transition: var(--transition-fast);
flex-shrink: 0;
}
.switch-button {
position: absolute;
top: 2px;
left: 2px;
border-radius: 50%;
width: 1.25rem;
height: 1.25rem;
background: white;
box-shadow: var(--shadow-sm);
transition: var(--transition-fast);
}
.switch-input:checked + .switch-slider {
background: var(--color-accent);
}
.switch-input:checked + .switch-slider .switch-button {
transform: translateX(1.5rem);
}
.switch-input:focus + .switch-slider {
box-shadow: 0 0 0 2px rgb(0 122 255 / 20%);
}
.switch-text {
font-weight: 500;
color: var(--color-text-secondary);
}
.switch-input:checked ~ .switch-text {
color: var(--color-accent);
}
/* Error Message */
.error-message {
margin-top: 0.25rem;
font-size: 0.75rem;
color: var(--color-error, #ef4444);
}
/* White theme adjustments */
.white .form-input,
.white .form-select {
border-color: #dddddd;
background: white;
}
.white .form-input:focus,
.white .form-select:focus {
border-color: var(--color-accent);
}
.white .checkbox-custom {
border-color: #dddddd;
background: white;
}
.white .switch-slider {
background: #dddddd;
}
.white .tooltip-content {
border-color: #dddddd;
background: white;
}
/* Responsive Design */
@media (width <= 768px) {
.config-form {
gap: 1.25rem;
}
.form-input,
.form-select {
padding: 0.625rem 0.875rem;
}
.form-select {
padding-right: 2.25rem;
}
.tooltip-content {
min-width: 150px;
max-width: 250px;
}
}
/* Dark mode adjustments */
:root.dark .form-input,
:root.auto.dark .form-input,
:root.dark .form-select,
:root.auto.dark .form-select {
border-color: var(--color-border);
background: var(--color-surface-elevated);
}
:root.dark .checkbox-custom,
:root.auto.dark .checkbox-custom {
border-color: var(--color-border);
background: var(--color-surface-elevated);
}
:root.dark .switch-slider,
:root.auto.dark .switch-slider {
background: var(--color-border);
}
:root.dark .tooltip-content,
:root.auto.dark .tooltip-content {
border-color: var(--color-border);
background: var(--color-surface-elevated);
}
/* Focus styles for accessibility */
.form-input:focus-visible,
.form-select:focus-visible,
.checkbox-input:focus-visible + .checkbox-custom,
.switch-input:focus-visible + .switch-slider,
.info-icon:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}

View File

@@ -43,8 +43,8 @@
import { MinusIcon, PinIcon, ShrinkIcon, XIcon } from 'lucide-vue-next' import { MinusIcon, PinIcon, ShrinkIcon, XIcon } from 'lucide-vue-next'
import { computed, onBeforeMount, onBeforeUnmount, ref } from 'vue' import { computed, onBeforeMount, onBeforeUnmount, ref } from 'vue'
import { osGlobal } from '@/hooks/useGlobal'
import { IRPCActionType } from '@/utils/enum' import { IRPCActionType } from '@/utils/enum'
import { osGlobal } from '@/utils/global'
const isShowprogress = ref(false) const isShowprogress = ref(false)
const progress = ref(0) const progress = ref(0)

View File

@@ -0,0 +1,44 @@
import { readonly, ref } from 'vue'
import { IRPCActionType } from '@/utils/enum'
const osGlobal = ref<string>(window.electron.platform)
const pageReloadCount = ref(0)
interface getPicBedType {
picBeds: IPicBedType[]
defaultPicBed: string
defaultConfigName: string
defaultId: string
}
const _picBeds = ref<IPicBedType[]>([])
const _defaultPicBed = ref<string>('')
const _defaultConfigName = ref<string>('')
const _defaultPicBedId = ref<string>('')
export function usePicBed() {
const updatePicBeds = async () => {
console.log('Updating pic beds in global hook...')
const result = await window.electron.triggerRPC<getPicBedType>(IRPCActionType.MAIN_GET_PICBED)
if (result) {
_picBeds.value = result.picBeds
_defaultPicBed.value = result.defaultPicBed
_defaultConfigName.value = result.defaultConfigName
_defaultPicBedId.value = result.defaultId
}
}
return {
picBedG: readonly(_picBeds),
defaultPicBedG: readonly(_defaultPicBed),
defaultConfigNameG: readonly(_defaultConfigName),
defaultIdG: readonly(_defaultPicBedId),
updatePicBeds,
}
}
async function updatePageReloadCount() {
pageReloadCount.value++
}
export { osGlobal, pageReloadCount, updatePageReloadCount }

View File

@@ -1,7 +0,0 @@
import { inject } from 'vue'
import { storeKey } from '@/store'
export const useStore = () => {
return inject(storeKey) ?? null
}

View File

@@ -107,6 +107,8 @@
"description": "Configure settings for each PicBed individually", "description": "Configure settings for each PicBed individually",
"title": "Per-PicBed Settings" "title": "Per-PicBed Settings"
}, },
"renameSettings": "Rename",
"skipProcessSettings": "Skip Process",
"title": "Image Processing Settings", "title": "Image Processing Settings",
"transform": { "transform": {
"description": "Adjust image size, rotation, flipping, etc.", "description": "Adjust image size, rotation, flipping, etc.",

View File

@@ -107,6 +107,8 @@
"description": "为每个图床单独配置设置", "description": "为每个图床单独配置设置",
"title": "图床独立设置" "title": "图床独立设置"
}, },
"renameSettings": "重命名",
"skipProcessSettings": "文件跳过",
"title": "图片处理设置", "title": "图片处理设置",
"transform": { "transform": {
"description": "调整图片大小、旋转、翻转等", "description": "调整图片大小、旋转、翻转等",

View File

@@ -107,6 +107,8 @@
"description": "為每個圖床單獨配置設置", "description": "為每個圖床單獨配置設置",
"title": "圖床獨立設置" "title": "圖床獨立設置"
}, },
"renameSettings": "重命名",
"skipProcessSettings": "文件跳過",
"title": "圖片處理設置", "title": "圖片處理設置",
"transform": { "transform": {
"description": "調整圖片大小、旋轉、翻轉等", "description": "調整圖片大小、旋轉、翻轉等",

View File

@@ -74,7 +74,7 @@
<ChevronDownIcon :size="16" /> <ChevronDownIcon :size="16" />
</button> </button>
<div v-show="picBedDropdownOpen" class="multiselect-dropdown"> <div v-show="picBedDropdownOpen" class="multiselect-dropdown">
<label v-for="item in picBedGlobal" :key="item.type" class="multiselect-option"> <label v-for="item in picBedG" :key="item.type" class="multiselect-option">
<input v-model="choosedPicBed" type="checkbox" :value="item.type" /> <input v-model="choosedPicBed" type="checkbox" :value="item.type" />
{{ item.name }} {{ item.name }}
</label> </label>
@@ -514,6 +514,7 @@ import { onBeforeRouteUpdate } from 'vue-router'
import ALLApi from '@/apis/allApi' import ALLApi from '@/apis/allApi'
import VirtualScroller from '@/components/VirtualScroller.vue' import VirtualScroller from '@/components/VirtualScroller.vue'
import useConfirm from '@/hooks/useConfirm' import useConfirm from '@/hooks/useConfirm'
import { usePicBed } from '@/hooks/useGlobal'
import useMessage from '@/hooks/useMessage' import useMessage from '@/hooks/useMessage'
import { customStrMatch, customStrReplace } from '@/manage/utils/common' import { customStrMatch, customStrReplace } from '@/manage/utils/common'
import { getRawData } from '@/utils/common' import { getRawData } from '@/utils/common'
@@ -521,12 +522,12 @@ import { configPaths } from '@/utils/configPaths'
import { getConfig, saveConfig } from '@/utils/dataSender' import { getConfig, saveConfig } from '@/utils/dataSender'
import $$db from '@/utils/db' import $$db from '@/utils/db'
import { IPasteStyle, IRPCActionType } from '@/utils/enum' import { IPasteStyle, IRPCActionType } from '@/utils/enum'
import { picBedGlobal } from '@/utils/global'
import { picBedsCanbeDeleted } from '@/utils/static' import { picBedsCanbeDeleted } from '@/utils/static'
const { t } = useI18n() const { t } = useI18n()
const message = useMessage() const message = useMessage()
const { confirm } = useConfirm() const { confirm } = useConfirm()
const { picBedG } = usePicBed()
type IResult<T> = T & { type IResult<T> = T & {
id: string id: string

View File

@@ -30,10 +30,10 @@
import type { IConfig } from 'piclist' import type { IConfig } from 'piclist'
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue' import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'
import { osGlobal } from '@/hooks/useGlobal'
import { isUrl } from '@/utils/common' import { isUrl } from '@/utils/common'
import { getConfig } from '@/utils/dataSender' import { getConfig } from '@/utils/dataSender'
import { IRPCActionType } from '@/utils/enum' import { IRPCActionType } from '@/utils/enum'
import { osGlobal } from '@/utils/global'
const logoPath = ref('') const logoPath = ref('')
const dragover = ref(false) const dragover = ref(false)

View File

@@ -348,7 +348,7 @@
<span>{{ t('pages.settings.upload.autoImportPicBed') }}</span> <span>{{ t('pages.settings.upload.autoImportPicBed') }}</span>
</div> </div>
<div class="checkbox-group compact"> <div class="checkbox-group compact">
<label v-for="item in picBedGlobal" :key="item.type" class="checkbox-option"> <label v-for="item in picBedG" :key="item.type" class="checkbox-option">
<input <input
v-model="formOfSetting.autoImportPicBed" v-model="formOfSetting.autoImportPicBed"
type="checkbox" type="checkbox"
@@ -676,7 +676,7 @@
</div> </div>
<div class="picbed-checkbox-grid"> <div class="picbed-checkbox-grid">
<label v-for="item in picBedGlobal" :key="item.name" class="picbed-checkbox-card"> <label v-for="item in picBedG" :key="item.name" class="picbed-checkbox-card">
<input v-model="showPicBedList" type="checkbox" :value="item.name" class="checkbox-input" /> <input v-model="showPicBedList" type="checkbox" :value="item.name" class="checkbox-input" />
<span class="checkbox-indicator" /> <span class="checkbox-indicator" />
<span class="checkbox-label">{{ item.name }}</span> <span class="checkbox-label">{{ item.name }}</span>
@@ -696,7 +696,7 @@
</div> </div>
<div class="picbed-checkbox-grid"> <div class="picbed-checkbox-grid">
<label v-for="item in picBedGlobal" :key="`gallery-${item.name}`" class="picbed-checkbox-card"> <label v-for="item in picBedG" :key="`gallery-${item.name}`" class="picbed-checkbox-card">
<input v-model="galleryPicBedFilterList" type="checkbox" :value="item.type" class="checkbox-input" /> <input v-model="galleryPicBedFilterList" type="checkbox" :value="item.type" class="checkbox-input" />
<span class="checkbox-indicator" /> <span class="checkbox-indicator" />
<span class="checkbox-label">{{ item.name }}</span> <span class="checkbox-label">{{ item.name }}</span>
@@ -1747,7 +1747,7 @@
<button class="dialog-close" @click="imageProcessDialogVisible = false">X</button> <button class="dialog-close" @click="imageProcessDialogVisible = false">X</button>
</div> </div>
<div class="dialog-content"> <div class="dialog-content">
<ImageProcessSetting v-model="imageProcessDialogVisible" /> <ImageProcessSetting :config-id="''" />
</div> </div>
</div> </div>
</div> </div>
@@ -1784,6 +1784,7 @@ import { useRouter } from 'vue-router'
import ImageProcessSetting from '@/components/ImageProcessSetting.vue' import ImageProcessSetting from '@/components/ImageProcessSetting.vue'
import useConfirm from '@/hooks/useConfirm' import useConfirm from '@/hooks/useConfirm'
import { osGlobal, usePicBed } from '@/hooks/useGlobal'
import useMessage from '@/hooks/useMessage' import useMessage from '@/hooks/useMessage'
import { setCurrentLanguage } from '@/i18n' import { setCurrentLanguage } from '@/i18n'
import { SHORTKEY_PAGE } from '@/router/config' import { SHORTKEY_PAGE } from '@/router/config'
@@ -1792,12 +1793,13 @@ import { configPaths } from '@/utils/configPaths'
import { getConfig, saveConfig } from '@/utils/dataSender' import { getConfig, saveConfig } from '@/utils/dataSender'
import { II18nLanguage, IRPCActionType, ISartMode } from '@/utils/enum' import { II18nLanguage, IRPCActionType, ISartMode } from '@/utils/enum'
import { getLatestVersion } from '@/utils/getLatestVersion' import { getLatestVersion } from '@/utils/getLatestVersion'
import { osGlobal, picBedGlobal, updatePicBedGlobal } from '@/utils/global'
const { t, locale } = useI18n() const { t, locale } = useI18n()
const $router = useRouter() const $router = useRouter()
const { confirm } = useConfirm() const { confirm } = useConfirm()
const message = useMessage() const message = useMessage()
const { picBedG, updatePicBeds } = usePicBed()
const activeName = ref<'system' | 'sync' | 'upload' | 'advanced' | 'update'>('system') const activeName = ref<'system' | 'sync' | 'upload' | 'advanced' | 'update'>('system')
const showPicBedList = ref<string[]>([]) const showPicBedList = ref<string[]>([])
const galleryPicBedFilterList = ref<string[]>([]) const galleryPicBedFilterList = ref<string[]>([])
@@ -2073,7 +2075,7 @@ async function initData() {
const config = (await getConfig<IConfig>()) || ({} as IConfig) const config = (await getConfig<IConfig>()) || ({} as IConfig)
const settings = config.settings || {} const settings = config.settings || {}
const picBed = config.picBed const picBed = config.picBed
showPicBedList.value = picBedGlobal.value.filter(item => item.visible).map(item => item.name) showPicBedList.value = picBedG.value.filter(item => item.visible).map(item => item.name)
galleryPicBedFilterList.value = settings.galleryPicBedFilter || [] galleryPicBedFilterList.value = settings.galleryPicBedFilter || []
formKeys.forEach(key => { formKeys.forEach(key => {
;(formOfSetting.value as any)[key] = settings[key] ?? formOfSetting.value[key] ;(formOfSetting.value as any)[key] = settings[key] ?? formOfSetting.value[key]
@@ -2225,9 +2227,9 @@ watch(galleryPicBedFilterList, val => {
}) })
function handleShowPicBedListChange(val: ICheckBoxValueType[]) { function handleShowPicBedListChange(val: ICheckBoxValueType[]) {
const list = picBedGlobal.value.map(item => ({ ...item, visible: val.includes(item.name) })) const list = picBedG.value.map(item => ({ ...item, visible: val.includes(item.name) }))
saveConfig({ [configPaths.picBed.list]: list }) saveConfig({ [configPaths.picBed.list]: list })
updatePicBedGlobal() updatePicBeds()
} }
function handleGalleryPicBedFilterChange(val: ICheckBoxValueType[]) { function handleGalleryPicBedFilterChange(val: ICheckBoxValueType[]) {

View File

@@ -225,6 +225,7 @@ import { computed, onBeforeMount, onBeforeUnmount, reactive, ref, toRaw, useTemp
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ConfigForm from '@/components/UnifiedConfigForm.vue' import ConfigForm from '@/components/UnifiedConfigForm.vue'
import { usePicBed } from '@/hooks/useGlobal'
import { getRawData, handleStreamlinePluginName } from '@/utils/common' import { getRawData, handleStreamlinePluginName } from '@/utils/common'
import { configPaths } from '@/utils/configPaths' import { configPaths } from '@/utils/configPaths'
import { import {
@@ -235,9 +236,9 @@ import {
} from '@/utils/constant' } from '@/utils/constant'
import { getConfig, saveConfig } from '@/utils/dataSender' import { getConfig, saveConfig } from '@/utils/dataSender'
import { IRPCActionType } from '@/utils/enum' import { IRPCActionType } from '@/utils/enum'
import { updatePicBedGlobal } from '@/utils/global'
const { t } = useI18n() const { t } = useI18n()
const { updatePicBeds } = usePicBed()
const searchText = ref('') const searchText = ref('')
const pluginList = ref<IPicGoPlugin[]>([]) const pluginList = ref<IPicGoPlugin[]>([])
const config = ref<any[]>([]) const config = ref<any[]>([])
@@ -332,7 +333,7 @@ const updateSuccessHandler = (plugin: string) => {
item.ing = false item.ing = false
item.hasInstall = true item.hasInstall = true
} }
updatePicBedGlobal() updatePicBeds()
}) })
handleReload() handleReload()
getPluginList() getPluginList()
@@ -349,7 +350,7 @@ const uninstallSuccessHandler = (plugin: string) => {
if (item.config.uploader.name) { if (item.config.uploader.name) {
handleRestoreState('uploader', item.config.uploader.name) handleRestoreState('uploader', item.config.uploader.name)
} }
updatePicBedGlobal() updatePicBeds()
} }
return item.fullName !== plugin return item.fullName !== plugin
}) })
@@ -379,7 +380,7 @@ const picgoTogglePluginHandler = (fullName: string, enabled: boolean) => {
const plugin = pluginList.value.find(item => item.fullName === fullName) const plugin = pluginList.value.find(item => item.fullName === fullName)
if (plugin) { if (plugin) {
plugin.enabled = enabled plugin.enabled = enabled
updatePicBedGlobal() updatePicBeds()
needReload.value = true needReload.value = true
} }
} }

View File

@@ -7,11 +7,11 @@
<button <button
class="provider-button" class="provider-button"
:title="t('pages.upload.uploadViewHint')" :title="t('pages.upload.uploadViewHint')"
@click="handlePicBedNameClick(picBedName, picBedConfigName)" @click="handlePicBedNameClick(picBedName)"
> >
<div class="provider-info"> <div class="provider-info">
<span class="provider-name">{{ picBedName }}</span> <span class="provider-name">{{ picBedName }}</span>
<span class="provider-config">{{ picBedConfigName || 'Default' }}</span> <span class="provider-config">{{ defaultConfigNameG || 'Default' }}</span>
</div> </div>
<EditIcon :size="16" class="provider-arrow" /> <EditIcon :size="16" class="provider-arrow" />
</button> </button>
@@ -144,7 +144,7 @@
</button> </button>
</div> </div>
<div class="modal-content"> <div class="modal-content">
<ImageProcessSetting v-model="imageProcessDialogVisible" /> <ImageProcessSetting :config-id="PicBedId" />
</div> </div>
</div> </div>
</div> </div>
@@ -162,11 +162,12 @@ import {
UploadCloudIcon, UploadCloudIcon,
XIcon, XIcon,
} from 'lucide-vue-next' } from 'lucide-vue-next'
import { onBeforeMount, onBeforeUnmount, ref, useTemplateRef, watch } from 'vue' import { computed, onBeforeMount, onBeforeUnmount, ref, useTemplateRef, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import ImageProcessSetting from '@/components/ImageProcessSetting.vue' import ImageProcessSetting from '@/components/ImageProcessSetting.vue'
import { usePicBed } from '@/hooks/useGlobal'
import useMessage from '@/hooks/useMessage' import useMessage from '@/hooks/useMessage'
import { PICBEDS_PAGE } from '@/router/config' import { PICBEDS_PAGE } from '@/router/config'
import $bus from '@/utils/bus' import $bus from '@/utils/bus'
@@ -176,12 +177,12 @@ import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant'
import { getConfig, saveConfig } from '@/utils/dataSender' import { getConfig, saveConfig } from '@/utils/dataSender'
import { useDragEventListeners } from '@/utils/drag' import { useDragEventListeners } from '@/utils/drag'
import { IPasteStyle, IRPCActionType } from '@/utils/enum' import { IPasteStyle, IRPCActionType } from '@/utils/enum'
import { picBedGlobal, updatePicBedGlobal } from '@/utils/global'
useDragEventListeners() useDragEventListeners()
const $router = useRouter() const $router = useRouter()
const { t } = useI18n() const { t } = useI18n()
const message = useMessage() const message = useMessage()
const { picBedG, defaultPicBedG, defaultConfigNameG, defaultIdG, updatePicBeds } = usePicBed()
const imageProcessDialogVisible = ref(false) const imageProcessDialogVisible = ref(false)
const useShortUrl = ref(false) const useShortUrl = ref(false)
@@ -189,11 +190,18 @@ const dragover = ref(false)
const progress = ref(0) const progress = ref(0)
const showProgress = ref(false) const showProgress = ref(false)
const showError = ref(false) const showError = ref(false)
const pasteStyle = ref('') const pasteStyle = ref(IPasteStyle.MARKDOWN)
const picBedName = ref('') const PicBedId = ref('')
const picBedConfigName = ref('')
const fileInput = useTemplateRef('fileInput') const fileInput = useTemplateRef('fileInput')
const picBedName = computed(() => {
if (!picBedG.value || picBedG.value.length === 0) {
return ''
}
const target = picBedG.value.find(item => item.type === defaultPicBedG.value)
return target ? target.name : defaultPicBedG.value
})
const pasteFormatList = ref<Record<string, string>>({ const pasteFormatList = ref<Record<string, string>>({
[IPasteStyle.MARKDOWN]: '![alt](url)', [IPasteStyle.MARKDOWN]: '![alt](url)',
[IPasteStyle.HTML]: '<img src="url"/>', [IPasteStyle.HTML]: '<img src="url"/>',
@@ -202,9 +210,9 @@ const pasteFormatList = ref<Record<string, string>>({
[IPasteStyle.CUSTOM]: '', [IPasteStyle.CUSTOM]: '',
}) })
watch(picBedGlobal, () => { function syncPicBedHandler(): void {
getDefaultPicBed() updatePicBeds()
}) }
let removeUploadProgressListenerCallback: () => void = () => {} let removeUploadProgressListenerCallback: () => void = () => {}
let removeSyncPicBedListenerCallback: () => void = () => {} let removeSyncPicBedListenerCallback: () => void = () => {}
@@ -219,10 +227,6 @@ function uploadProgressHandler(p: number): void {
} }
} }
function syncPicBedHandler(): void {
getDefaultPicBed()
}
const handleImageProcess = () => { const handleImageProcess = () => {
imageProcessDialogVisible.value = true imageProcessDialogVisible.value = true
} }
@@ -241,21 +245,13 @@ function onProgressChange(val: number) {
} }
} }
async function handlePicBedNameClick(_picBedName: string, picBedConfigName: string | undefined) { async function handlePicBedNameClick(_picBedName: string) {
const formatedpicBedConfigName = picBedConfigName || 'Default' const currentPicBedConfig = ((await getConfig<any[]>(`uploader.${defaultPicBedG.value}`)) as any) || {}
const currentPicBed = await getConfig<string>(configPaths.picBed.current)
const currentPicBedConfig = ((await getConfig<any[]>(`uploader.${currentPicBed}`)) as any) || {}
const configList = await window.electron.triggerRPC<IUploaderConfigItem>(
IRPCActionType.PICBED_GET_CONFIG_LIST,
currentPicBed,
)
const currentConfigList = configList?.configList ?? []
const config = currentConfigList.find((item: any) => item._configName === formatedpicBedConfigName)
$router.push({ $router.push({
name: PICBEDS_PAGE, name: PICBEDS_PAGE,
params: { params: {
type: currentPicBed, type: defaultPicBedG.value,
configId: config?._id || '', configId: defaultIdG.value,
}, },
query: { query: {
defaultConfigId: currentPicBedConfig.defaultId || '', defaultConfigId: currentPicBedConfig.defaultId || '',
@@ -400,16 +396,6 @@ function handleInputBoxValue(val: string) {
} }
} }
async function getDefaultPicBed() {
const currentPicBed = await getConfig<string>(configPaths.picBed.current)
picBedGlobal.value.forEach(item => {
if (item.type === currentPicBed) {
picBedName.value = item.name
}
})
picBedConfigName.value = (await getConfig<string>(`picBed.${currentPicBed}._configName`)) || ''
}
async function handleChangePicBed() { async function handleChangePicBed() {
window.electron.sendRPC(IRPCActionType.SHOW_UPLOAD_PAGE_MENU) window.electron.sendRPC(IRPCActionType.SHOW_UPLOAD_PAGE_MENU)
} }
@@ -421,13 +407,11 @@ onBeforeUnmount(() => {
}) })
onBeforeMount(() => { onBeforeMount(() => {
updatePicBedGlobal()
getUseShortUrl()
getPasteStyle()
getDefaultPicBed()
removeUploadProgressListenerCallback = window.electron.ipcRendererOn('uploadProgress', uploadProgressHandler) removeUploadProgressListenerCallback = window.electron.ipcRendererOn('uploadProgress', uploadProgressHandler)
removeSyncPicBedListenerCallback = window.electron.ipcRendererOn('syncPicBed', syncPicBedHandler) removeSyncPicBedListenerCallback = window.electron.ipcRendererOn('syncPicBed', syncPicBedHandler)
$bus.on(SHOW_INPUT_BOX_RESPONSE, handleInputBoxValue) $bus.on(SHOW_INPUT_BOX_RESPONSE, handleInputBoxValue)
getUseShortUrl()
getPasteStyle()
}) })
</script> </script>

View File

@@ -20,11 +20,7 @@
</div> </div>
</div> </div>
<div class="header-actions"> <div class="header-actions">
<button <button class="btn btn-primary btn-glow" :disabled="defaultPicBedG === type" @click="setDefaultPicBed(type)">
class="btn btn-primary btn-glow"
:disabled="store?.state.defaultPicBed === type"
@click="setDefaultPicBed(type)"
>
<Star :size="16" /> <Star :size="16" />
<span>{{ t('pages.uploaderConfig.setAsDefault') }}</span> <span>{{ t('pages.uploaderConfig.setAsDefault') }}</span>
</button> </button>
@@ -130,8 +126,8 @@ import { useI18n } from 'vue-i18n'
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router' import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
import useConfirm from '@/hooks/useConfirm' import useConfirm from '@/hooks/useConfirm'
import { usePicBed } from '@/hooks/useGlobal'
import useMessage from '@/hooks/useMessage' import useMessage from '@/hooks/useMessage'
import { useStore } from '@/hooks/useStore'
import { PICBEDS_PAGE, UPLOADER_CONFIG_PAGE } from '@/router/config' import { PICBEDS_PAGE, UPLOADER_CONFIG_PAGE } from '@/router/config'
import $bus from '@/utils/bus' import $bus from '@/utils/bus'
import { configPaths } from '@/utils/configPaths' import { configPaths } from '@/utils/configPaths'
@@ -144,15 +140,15 @@ const message = useMessage()
const { confirm } = useConfirm() const { confirm } = useConfirm()
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const { defaultPicBedG } = usePicBed()
const type = ref('') const type = ref('')
const curConfigList = ref<IStringKeyMap[]>([]) const curConfigList = ref<IStringKeyMap[]>([])
const defaultConfigId = ref('') const defaultConfigId = ref('')
const store = useStore()
async function selectItem(id: string) { async function selectItem(id: string) {
await window.electron.triggerRPC<void>(IRPCActionType.UPLOADER_SELECT, type.value, id) await window.electron.triggerRPC<void>(IRPCActionType.UPLOADER_SELECT, type.value, id)
if (store?.state.defaultPicBed === type.value) { if (defaultPicBedG.value === type.value) {
window.electron.sendRPC( window.electron.sendRPC(
IRPCActionType.TRAY_SET_TOOL_TIP, IRPCActionType.TRAY_SET_TOOL_TIP,
`${type.value} ${curConfigList.value.find(item => item._id === id)?._configName || ''}`, `${type.value} ${curConfigList.value.find(item => item._id === id)?._configName || ''}`,
@@ -276,7 +272,6 @@ function setDefaultPicBed(type: string) {
[configPaths.picBed.uploader]: type, [configPaths.picBed.uploader]: type,
}) })
store?.setDefaultPicBed(type)
const currentConfigName = curConfigList.value.find(item => item._id === defaultConfigId.value)?._configName const currentConfigName = curConfigList.value.find(item => item._id === defaultConfigId.value)?._configName
window.electron.sendRPC(IRPCActionType.TRAY_SET_TOOL_TIP, `${type} ${currentConfigName || ''}`) window.electron.sendRPC(IRPCActionType.TRAY_SET_TOOL_TIP, `${type} ${currentConfigName || ''}`)
message.success(t('pages.uploaderConfig.setSuccess')) message.success(t('pages.uploaderConfig.setSuccess'))

View File

@@ -1,18 +0,0 @@
import { ref } from 'vue'
import { IRPCActionType } from '@/utils/enum'
const osGlobal = ref<string>(window.electron.platform)
const picBedGlobal = ref<IPicBedType[]>([])
const pageReloadCount = ref(0)
async function updatePicBedGlobal() {
picBedGlobal.value = (await window.electron.triggerRPC<IPicBedType[]>(IRPCActionType.MAIN_GET_PICBED))!
}
async function updatePageReloadCount() {
pageReloadCount.value++
}
export { osGlobal, pageReloadCount, picBedGlobal, updatePageReloadCount, updatePicBedGlobal }