Feature(custom): optimize UI of setting page

This commit is contained in:
Kuingsmile
2026-01-22 15:48:45 +08:00
parent d997348c3a
commit 16da28ef0f
9 changed files with 1241 additions and 3854 deletions

View File

@@ -28,12 +28,12 @@ const {
text,
disabled,
active = false,
icon,
icon = null,
iconSize = 16,
type = 'primary',
} = defineProps<{
text: string
icon: any
icon?: any
active?: boolean
iconSize?: number
disabled?: boolean

View File

@@ -6,6 +6,7 @@
<input
v-model="modelValue"
:type="type"
v-bind="$attrs"
class="box-border w-full rounded-md border border-border bg-bg-tertiary p-3 pr-10 text-sm text-main transition-all duration-200 ease-apple focus:border-accent focus:outline-none"
:placeholder="placeholder"
/>
@@ -26,20 +27,25 @@
<script setup lang="ts">
import { EyeClosedIcon, EyeIcon } from 'lucide-vue-next'
import { onMounted, ref } from 'vue'
const modelValue = defineModel<string>()
const modelValue = defineModel<any>()
const type = ref('text')
const {
isPassword = false,
title,
inputType = 'text',
placeholder,
} = defineProps<{
isPassword?: boolean
title: string
inputType?: string
placeholder: string
}>()
onMounted(() => {
if (isPassword) {
type.value = 'password'
} else {
type.value = inputType
}
})
</script>

View File

@@ -0,0 +1,78 @@
<template>
<div
class="fixed inset-0 z-1000 flex items-center justify-center overflow-y-auto bg-black/30"
:class="{ 'advanced-animation': enableAdvancedAnimation }"
@click.stop
>
<div
class="m-auto flex flex-col overflow-hidden rounded-lg border border-border-secondary bg-bg-tertiary shadow-xl"
:style="{
height: height || '85vh',
maxHeight: maxHeight || '85vh',
width: width || '90vw',
maxWidth: maxWidth || '90vw',
}"
@click.stop
>
<div class="flex items-center justify-between border border-border-secondary bg-bg-tertiary px-5 py-4 max-md:p-2">
<slot name="titleBar"></slot>
<h3 v-if="title !== ''" class="m-0 text-xl font-semibold text-main">
{{ title }}
</h3>
<span v-if="description !== ''" class="mt-1 text-xl font-semibold text-secondary">
{{ description }}
</span>
<button
class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full border border-border bg-surface-elevated text-secondary transition-all duration-fast ease-apple hover:scale-105 hover:border-danger hover:bg-danger hover:text-white focus-visible:focus-ring"
@click="visible = false"
>
<XIcon :size="20" />
</button>
</div>
<div
class="no-scrollbar h-[calc(90vh-90px)] flex-1 overflow-y-auto max-md:p-4"
:style="{ height: height ? 'calc(' + height + ' - 90px)' : 'calc(85vh - 90px)' }"
>
<slot></slot>
</div>
<div v-if="$slots.footer" class="flex justify-end gap-3 border-t border-border-secondary p-3">
<slot name="footer"></slot>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { XIcon } from 'lucide-vue-next'
import { onBeforeMount, ref } from 'vue'
import { getConfig } from '@/utils/dataSender'
const visible = defineModel<boolean>('visible')
const enableAdvancedAnimation = ref(false)
const {
title = '',
description = '',
height = '',
maxHeight = '',
width = '',
maxWidth = '',
} = defineProps<{
title?: string
description?: string
height?: string
width?: string
maxHeight?: string
maxWidth?: string
}>()
async function initConf() {
enableAdvancedAnimation.value = (await getConfig('settings.enableAdvancedAnimation')) || false
}
onBeforeMount(() => {
initConf()
})
</script>

View File

@@ -1,9 +1,12 @@
<template>
<div class="w-full rounded-lg border border-border bg-bg-secondary p-6 shadow-sm">
<div class="mb-2 flex items-start gap-3">
<div class="mb-2 flex h-[30px] w-[30px] shrink-0 items-center justify-center rounded-lg bg-accent text-white">
<div
v-if="icon"
class="mb-2 flex h-[30px] w-[30px] shrink-0 items-center justify-center rounded-lg bg-accent text-white"
>
<slot name="icon">
<component :is="icon" v-if="icon" :size="iconSize" />
<component :is="icon" :size="iconSize" />
</slot>
</div>
<div>
@@ -27,15 +30,15 @@
<script setup lang="ts">
const {
title,
title = '',
description = '',
icon,
icon = null,
iconSize = 20,
onlyOneRow = false,
} = defineProps<{
title: string
title?: string
description?: string
icon: any
icon?: any
iconSize?: number
onlyOneRow?: boolean
}>()

View File

@@ -384,48 +384,32 @@
<!-- Edit URL Modal -->
<transition name="modal">
<div
<CustomModal
v-if="dialogVisible"
class="modal-overlay"
:class="{ 'advanced-animation': enableAdvancedAnimation }"
@click="dialogVisible = false"
v-model:visible="dialogVisible"
:height="'auto'"
:width="'40%'"
:title="t('pages.gallery.changeImageUrl')"
>
<div class="modal-container" @click.stop>
<div class="modal-header">
<h3 class="m-0 text-xl font-semibold text-main">{{ t('pages.gallery.changeImageUrl') }}</h3>
<button class="modal-close-btn" @click="dialogVisible = false">
<XIcon :size="20" />
</button>
</div>
<div class="p-6">
<div class="p-2">
<input v-model="imgInfo.imgUrl" type="text" class="form-input" placeholder="Enter new URL" />
</div>
<div class="modal-footer">
<button class="btn-secondary" @click="dialogVisible = false">
{{ t('common.cancel') }}
</button>
<button class="btn-primary" @click="confirmModify">
{{ t('common.confirm') }}
</button>
</div>
</div>
</div>
<template #footer>
<CustomButton :type="'secondary'" :text="t('common.cancel')" @click="dialogVisible = false" />
<CustomButton :type="'primary'" :text="t('common.confirm')" @click="confirmModify" />
</template>
</CustomModal>
</transition>
<!-- Batch Rename Modal -->
<transition name="modal">
<div
<CustomModal
v-if="isShowBatchRenameDialog"
class="modal-overlay"
:class="{ 'advanced-animation': enableAdvancedAnimation }"
v-model:visible="isShowBatchRenameDialog"
:height="'auto'"
:width="'700px'"
:title="t('pages.gallery.batchEditUrl')"
>
<div class="modal-container" @click.stop>
<div class="modal-header">
<h3 class="m-0 text-xl font-semibold text-main">{{ t('pages.gallery.batchEditUrl') }}</h3>
<button class="modal-close-btn" @click="isShowBatchRenameDialog = false">
<XIcon :size="20" />
</button>
</div>
<div class="p-6">
<div class="mb-6 last:mb-0">
<label class="form-label">
@@ -443,9 +427,7 @@
v-if="showMatchedUrls && matchedUrls.length > 0"
class="absolute z-1000 mt-2 max-h-[300px] max-w-[650px] overflow-hidden rounded-md border border-border-secondary bg-bg-tertiary p-0 shadow-md"
>
<div
class="border-b border-b-border-secondary bg-bg-secondary px-4 py-3 text-sm font-semibold text-main"
>
<div class="border-b border-b-border-secondary bg-bg-secondary px-4 py-3 text-sm font-semibold text-main">
Matched URLs ({{ matchedUrls.length }}):
</div>
<div class="max-h-[240px] overflow-auto p-2">
@@ -532,16 +514,11 @@
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn-secondary" @click="isShowBatchRenameDialog = false">
{{ t('common.cancel') }}
</button>
<button class="btn-primary" @click="handleBatchRename()">
{{ t('common.confirm') }}
</button>
</div>
</div>
</div>
<template #footer>
<CustomButton :type="'secondary'" :text="t('common.cancel')" @click="isShowBatchRenameDialog = false" />
<CustomButton :type="'primary'" :text="t('common.confirm')" @click="handleBatchRename" />
</template>
</CustomModal>
</transition>
</div>
</template>
@@ -582,6 +559,8 @@ import { useI18n } from 'vue-i18n'
import { onBeforeRouteUpdate } from 'vue-router'
import ALLApi from '@/apis/allApi'
import CustomButton from '@/components/common/customButton.vue'
import CustomModal from '@/components/common/customModal.vue'
import MultiSelect from '@/components/common/multiSelect.vue'
import SingleSelect from '@/components/common/singleSelect.vue'
import VirtualScroller from '@/components/VirtualScroller.vue'

File diff suppressed because it is too large Load Diff

View File

@@ -245,53 +245,22 @@
</div>
<!-- Image Process Dialog -->
<transition name="modal">
<div
<CustomModal
v-if="imageProcessDialogVisible"
class="fixed inset-0 z-1000 flex items-center justify-center overflow-y-auto bg-black/30"
:class="{ 'advanced-animation': enableAdvancedAnimation }"
@click.stop
>
<div
class="m-auto flex h-[85vh] w-[90vw] flex-col overflow-hidden rounded-2xl border border-border-secondary bg-bg-tertiary shadow-xl"
@click.stop
>
<div
class="flex items-center justify-between border border-border-secondary bg-bg-tertiary px-5 py-4 max-md:p-2"
>
<h3 class="m-0 text-xl font-semibold text-main">
{{ t('pages.imageProcess.title') }}
</h3>
<span class="mt-1 text-xl font-semibold text-secondary">
{{
v-model:visible="imageProcessDialogVisible"
:title="t('pages.imageProcess.title')"
:description="
PicBedId === '' ? t('pages.imageProcess.subtitle-Global') : t('pages.imageProcess.subtitle-PerPicbed')
}}
</span>
<button
class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full border border-border bg-surface-elevated text-secondary transition-all duration-fast ease-apple hover:scale-105 hover:border-danger hover:bg-danger hover:text-white focus-visible:focus-ring"
@click="imageProcessDialogVisible = false"
"
>
<XIcon :size="20" />
</button>
</div>
<div class="no-scrollbar h-[calc(90vh-90px)] flex-1 overflow-y-auto max-md:p-4">
<ImageProcessSetting :config-id="PicBedId" :current-picbed-name="defaultPicBedG" />
</div>
</div>
</div>
</CustomModal>
</transition>
<!-- Task Queue Manager Modal -->
<transition name="modal">
<div
v-if="taskDialogVisible"
class="fixed inset-0 z-1000 flex items-center justify-center overflow-y-auto bg-black/50 p-4 max-md:p-4"
:class="{ 'advanced-animation': enableAdvancedAnimation }"
>
<div
class="m-auto flex h-[85vh] max-h-[85vh] w-[90vw] max-w-[90vw] flex-col overflow-hidden rounded-2xl border border-border-secondary bg-bg-tertiary shadow-xl max-md:max-h-[90vh] max-md:w-[95vw]"
@click.stop
>
<div class="flex items-center justify-between border border-border-secondary bg-bg-tertiary px-5 py-4">
<CustomModal v-if="taskDialogVisible" v-model:visible="taskDialogVisible">
<template #titleBar>
<div class="flex flex-row items-center gap-4">
<h3 class="flex items-center gap-2.5 bg-clip-text text-xl font-bold tracking-tight text-main">
{{ t('pages.upload.taskQueue.title') }}
@@ -305,13 +274,7 @@
}}
</span>
</div>
<button
class="flex h-8 w-8 cursor-pointer items-center justify-center rounded-full border border-border bg-surface-elevated text-secondary transition-all duration-fast ease-apple hover:scale-105 hover:border-danger hover:bg-danger hover:text-white focus-visible:focus-ring"
@click="taskDialogVisible = false"
>
<XIcon :size="20" />
</button>
</div>
</template>
<div class="no-scrollbar max-h-[calc(90vh-90px)] overflow-y-auto">
<!-- Action Bar -->
@@ -514,11 +477,7 @@
<button class="filter-tab" :class="{ active: taskFilter === 'all' }" @click="taskFilter = 'all'">
{{ t('pages.upload.taskQueue.filterAll') }}
</button>
<button
class="filter-tab"
:class="{ active: taskFilter === 'pending' }"
@click="taskFilter = 'pending'"
>
<button class="filter-tab" :class="{ active: taskFilter === 'pending' }" @click="taskFilter = 'pending'">
{{ t('pages.upload.taskQueue.filterPending') }}
</button>
<button
@@ -679,8 +638,7 @@
</button>
</div>
</div>
</div>
</div>
</CustomModal>
</transition>
</div>
</template>
@@ -718,6 +676,7 @@ import { computed, onBeforeMount, onBeforeUnmount, reactive, ref, useTemplateRef
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import CustomModal from '@/components/common/customModal.vue'
import ImageProcessSetting from '@/components/ImageProcessSetting.vue'
import { usePicBed } from '@/hooks/useGlobal'
import useMessage from '@/hooks/useMessage'
@@ -787,7 +746,6 @@ const progress = ref(0)
const showProgress = ref(false)
const showError = ref(false)
const pasteStyle = ref(IPasteStyle.MARKDOWN)
const enableAdvancedAnimation = ref(false)
const PicBedId = ref('')
const fileInput = useTemplateRef('fileInput')
const uploadInterval = ref(1000)
@@ -987,7 +945,6 @@ function ipcSendFiles(files: FileList) {
async function initConf() {
const settingConfig = await getConfig<any>('settings')
enableAdvancedAnimation.value = settingConfig?.enableAdvancedAnimation || false
pasteStyle.value = settingConfig?.pasteStyle || IPasteStyle.MARKDOWN
pasteFormatList.value.Custom = settingConfig?.customLink || '![$fileName]($url)'
useShortUrl.value = settingConfig?.useShortUrl || false

View File

@@ -1,6 +1,6 @@
<template>
<div
class="relative z-1 no-scrollbar flex h-full w-full flex-col items-center justify-start gap-4 overflow-auto rounded-xl border-none px-4 py-6 shadow-sm"
class="relative z-1 flex h-full w-full flex-col items-center justify-start gap-4 rounded-xl border-none px-4 py-6 shadow-sm"
>
<div
class="flex w-full items-center justify-between gap-4 rounded-2xl border border-border-secondary px-6 py-2 shadow-md"
@@ -34,11 +34,10 @@
<!-- Config Grid -->
<div
class="no-scrollbar flex w-full flex-1 items-start gap-4 overflow-auto rounded-2xl border border-border-secondary px-4 py-6 shadow-md"
>
<div
class="no-scrollbar grid h-auto w-full grid-cols-[repeat(auto-fill,minmax(300px,1fr))] gap-5 overflow-auto border-none p-1 max-md:grid-cols-1 max-md:gap-4 xl:grid-cols-[repeat(auto-fill,minmax(325px,1fr))]"
class="flex w-full flex-1 items-center gap-4 overflow-hidden rounded-2xl border border-border-secondary px-4 py-6 shadow-md"
>
<div class="no-scrollbar h-full w-full overflow-auto rounded-sm">
<div class="grid w-full grid-cols-[repeat(auto-fill,minmax(300px,1fr))] gap-5 border-none p-1 max-md:gap-4">
<!-- Config Items -->
<div
v-for="(item, index) in curConfigList"
@@ -139,6 +138,7 @@
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>

File diff suppressed because it is too large Load Diff