Feature(custom): migrate all pages to tailwind

This commit is contained in:
Kuingsmile
2026-01-25 16:04:32 +08:00
parent d0853cca1f
commit 88a5a8087a
15 changed files with 462 additions and 1942 deletions

View File

@@ -25,6 +25,7 @@
- 优化相册页面卡片样式,边界更清晰,提升选择框视觉效果。 - 优化相册页面卡片样式,边界更清晰,提升选择框视觉效果。
- 优化多个页面在窄屏下的显示,避免内容溢出。 - 优化多个页面在窄屏下的显示,避免内容溢出。
- 文件浏览侧边栏名称超出宽度时支持滚动显示完整名称。 - 文件浏览侧边栏名称超出宽度时支持滚动显示完整名称。
- 优化了视频播放页面的显示效果。
- 相册页面支持显示已选数量、匹配的 URL 列表和记忆过滤器状态。 - 相册页面支持显示已选数量、匹配的 URL 列表和记忆过滤器状态。
- 支持浏览完整插件列表、查看详情及一键安装。 - 支持浏览完整插件列表、查看详情及一键安装。
- 新增新手引导页面,首次运行自动弹出。 - 新增新手引导页面,首次运行自动弹出。
@@ -38,6 +39,7 @@
- 修复了管理页面中排序下拉框显示异常的问题。 - 修复了管理页面中排序下拉框显示异常的问题。
- 修复了管理页面图床列表未正确高亮当前选中项的问题。 - 修复了管理页面图床列表未正确高亮当前选中项的问题。
- 修复了管理页面markdown预览内容没有正确渲染的问题。
- 修复了暗色模式下任务页面的显示异常。 - 修复了暗色模式下任务页面的显示异常。
- 修复了图床设置页面“设置为默认图床”按钮状态更新不及时的问题。 - 修复了图床设置页面“设置为默认图床”按钮状态更新不及时的问题。
- 修复了预处理设置页面中,图床水印独立设置按钮状态不同步的问题。 - 修复了预处理设置页面中,图床水印独立设置按钮状态不同步的问题。

View File

@@ -29,3 +29,4 @@
@utility no-drag-region { @utility no-drag-region {
-webkit-app-region: none; -webkit-app-region: none;
} }

View File

@@ -175,7 +175,6 @@ function validateForm(): boolean {
} }
function clearFieldError(fieldName: string) { function clearFieldError(fieldName: string) {
console.log('Clearing error for field:', fieldName)
if (validationErrors[fieldName]) { if (validationErrors[fieldName]) {
delete validationErrors[fieldName] delete validationErrors[fieldName]
} }

View File

@@ -6,16 +6,14 @@
<span v-if="required" class="ml-1 text-danger">*</span> <span v-if="required" class="ml-1 text-danger">*</span>
</label> </label>
<slot name="title-extra"></slot> <slot name="title-extra"></slot>
<div v-if="tips" class="relative"> <div v-if="tips" class="group relative inline-block">
<div <div
class="flex h-[20px] w-[20px] cursor-pointer items-center justify-center rounded-full p-[2px] text-secondary hover:bg-bg-secondary hover:text-accent" class="flex h-[20px] w-[20px] cursor-pointer items-center justify-center rounded-full p-[2px] text-secondary hover:bg-bg-secondary hover:text-accent"
@click="toggleTooltip()"
> >
<Info :size="16" /> <Info :size="16" />
</div> </div>
<div <div
v-show="visibleTooltips" class="invisible absolute top-[125%] left-1/2 z-1000 w-max max-w-[200px] translate-x-[-50%] rounded-md border border-border bg-bg-tertiary p-2 text-center text-xs text-main opacity-0 shadow-md transition-opacity duration-300 group-hover:visible group-hover:opacity-100"
class="absolute top-full left-0 z-1000 max-w-[300px] min-w-[200px] rounded-md border border-border bg-bg-secondary p-3 text-xs leading-[1.4] text-main shadow-lg max-md:max-w-[250px] max-md:min-w-[150px]"
v-html="transformMarkdownToHTML(tips)" v-html="transformMarkdownToHTML(tips)"
/> />
</div> </div>
@@ -62,7 +60,6 @@ const [modelValue, modifiers] = defineModel<any>({
}) })
const type = ref('text') const type = ref('text')
const visibleTooltips = ref(false)
const { const {
isPassword = false, isPassword = false,
@@ -80,10 +77,6 @@ const {
tips?: string tips?: string
}>() }>()
function toggleTooltip() {
visibleTooltips.value = !visibleTooltips.value
}
function transformMarkdownToHTML(markdown: string) { function transformMarkdownToHTML(markdown: string) {
try { try {
return marked.parse(markdown) return marked.parse(markdown)

View File

@@ -29,16 +29,14 @@
</div> </div>
</label> </label>
<slot name="title-extra"></slot> <slot name="title-extra"></slot>
<div v-if="tips" class="relative"> <div v-if="tips" class="group relative inline-block">
<div <div
class="flex h-[20px] w-[20px] cursor-pointer items-center justify-center rounded-full p-[2px] text-secondary hover:bg-bg-secondary hover:text-accent" class="flex h-[20px] w-[20px] cursor-pointer items-center justify-center rounded-full p-[2px] text-secondary hover:bg-bg-secondary hover:text-accent"
@click="toggleTooltip()"
> >
<Info :size="16" /> <Info :size="16" />
</div> </div>
<div <div
v-show="visibleTooltips" class="invisible absolute top-[125%] left-1/2 z-1000 w-max max-w-[200px] translate-x-[-50%] rounded-md border border-border bg-bg-tertiary p-2 text-center text-xs text-main opacity-0 shadow-md transition-opacity duration-300 group-hover:visible group-hover:opacity-100"
class="absolute top-full left-0 z-1000 max-w-[300px] min-w-[200px] rounded-md border border-border bg-bg-secondary p-3 text-xs leading-[1.4] text-main shadow-lg max-md:max-w-[250px] max-md:min-w-[150px]"
v-html="transformMarkdownToHTML(tips)" v-html="transformMarkdownToHTML(tips)"
/> />
</div> </div>
@@ -48,12 +46,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Info } from 'lucide-vue-next' import { Info } from 'lucide-vue-next'
import { marked } from 'marked' import { marked } from 'marked'
import { onMounted, ref } from 'vue' import { onMounted } from 'vue'
const emit = defineEmits(['change']) const emit = defineEmits(['change'])
const visibleTooltips = ref(false)
const modelValue = defineModel<boolean>() const modelValue = defineModel<boolean>()
const { const {
title = '', title = '',
@@ -75,10 +71,6 @@ const {
tighter?: boolean tighter?: boolean
}>() }>()
function toggleTooltip() {
visibleTooltips.value = !visibleTooltips.value
}
function transformMarkdownToHTML(markdown: string) { function transformMarkdownToHTML(markdown: string) {
try { try {
return marked.parse(markdown) return marked.parse(markdown)

View File

@@ -8,7 +8,7 @@
} }
html { html {
@apply m-0 h-full p-0 w-full bg-transparent; @apply m-0 h-full w-full bg-transparent p-0;
} }
#app { #app {
@@ -180,14 +180,14 @@
} }
@keyframes slide-right { @keyframes slide-right {
from { from {
transform: translateX(100%); transform: translateX(100%);
opacity: 0; opacity: 0;
} }
to { to {
transform: translateX(0); transform: translateX(0);
opacity: 1; opacity: 1;
}
} }
} }
}

File diff suppressed because it is too large Load Diff

View File

@@ -40,16 +40,16 @@
> >
<div class="flex h-full w-full"> <div class="flex h-full w-full">
<div <div
class="flex min-h-0 max-w-[400px] min-w-[40px] flex-col border-r-2 border-r-border transition-all duration-100 ease-out" class="flex min-h-0 w-[40px] max-w-[400px] min-w-[40px] flex-col border-r-2 border-r-border transition-all duration-100 ease-out"
:style="{ width: sidebarWidth + 'px' }" :style="{ width: sidebarWidth + 'px' }"
> >
<div class="shrink-0 border-b-2 border-b-border-secondary p-2"> <div v-if="menuTitleMap[currentPicBedName]" class="shrink-0 border-b-2 border-b-border-secondary p-2">
<h3 class="m-0 text-center text-sm font-semibold text-secondary"> <h3 class="m-0 text-center text-sm font-semibold text-secondary">
{{ menuTitleMap[currentPicBedName] }} {{ menuTitleMap[currentPicBedName] }}
</h3> </h3>
</div> </div>
<div class="min-h-0 flex-1 overflow-y-auto p-2"> <div class="min-h-0 flex-1 overflow-y-auto">
<div v-if="isLoadingBucketList" class="flex flex-col items-center justify-center gap-2 p-8"> <div v-if="isLoadingBucketList" class="flex flex-col items-center justify-center gap-2 p-8">
<div <div
class="h-[25px] w-[25px] animate-spin rounded-full border-3 border-t-2 border-border border-t-accent" class="h-[25px] w-[25px] animate-spin rounded-full border-3 border-t-2 border-border border-t-accent"
@@ -287,7 +287,7 @@ const configMap = ref<any>(null)
const currentAlias = ref(route.query.alias as string) const currentAlias = ref(route.query.alias as string)
const currentPicBedName = ref(route.query.picBedName as string) const currentPicBedName = ref(route.query.picBedName as string)
const sidebarWidth = ref(160) const sidebarWidth = ref(120)
const isResizing = ref(false) const isResizing = ref(false)
let allPicBedConfigure = JSON.parse(route.query.allPicBedConfigure as string) let allPicBedConfigure = JSON.parse(route.query.allPicBedConfigure as string)

File diff suppressed because it is too large Load Diff

View File

@@ -242,12 +242,12 @@
</div> </div>
</div> </div>
<div class="flex shrink-0 flex-col justify-between"> <div class="flex min-w-0 shrink-0 flex-col justify-between">
<div <div
class="mb-1.5 overflow-hidden text-sm font-medium text-ellipsis whitespace-nowrap text-main" class="mb-1.5 w-full truncate text-center text-sm font-medium text-main"
:title="(item.fileName || '').toString().length > 30 ? item.fileName || '' : ''" :title="(item.fileName || '').toString().length > 30 ? item.fileName || '' : ''"
> >
<div class="text-center">{{ formatFileName(item.fileName || '') }}</div> {{ formatFileName(item.fileName || '') }}
</div> </div>
<div class="mr-2 flex items-center justify-between"> <div class="mr-2 flex items-center justify-between">

View File

@@ -5,7 +5,7 @@
:class="[osGlobal === 'linux' ? 'rounded-none bg-size-[100vh_100vw]' : 'rounded-full bg-size-[90vh_90vw]']" :class="[osGlobal === 'linux' ? 'rounded-none bg-size-[100vh_100vw]' : 'rounded-full bg-size-[90vh_90vw]']"
> >
<div <div
id="upload-area" ref="uploadArea"
class="h-full w-full transition-all duration-200 ease-in-out" class="h-full w-full transition-all duration-200 ease-in-out"
:class="{ :class="{
'bg-[rgba(0,0,0,0.3)]': dragover, 'bg-[rgba(0,0,0,0.3)]': dragover,
@@ -35,11 +35,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { IConfig } from 'piclist' import type { IConfig } from 'piclist'
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue' import { onBeforeMount, onBeforeUnmount, ref, useTemplateRef, watch } from 'vue'
import { osGlobal } from '@/hooks/useGlobal' 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 { useDragEventListeners } from '@/utils/drag'
import { IRPCActionType } from '@/utils/enum' import { IRPCActionType } from '@/utils/enum'
const logoPath = ref('') const logoPath = ref('')
@@ -51,6 +52,9 @@ const wX = ref(-1)
const wY = ref(-1) const wY = ref(-1)
const screenX = ref(-1) const screenX = ref(-1)
const screenY = ref(-1) const screenY = ref(-1)
const uploadArea = useTemplateRef<HTMLDivElement>('uploadArea')
useDragEventListeners(uploadArea)
let removeListeners: () => void = () => {} let removeListeners: () => void = () => {}

View File

@@ -135,7 +135,7 @@
small small
no-border no-border
:title="t('pages.settings.system.isHideDock')" :title="t('pages.settings.system.isHideDock')"
@change="handleHideDockChange(formOfSetting.isHideDock)" @change="handleHideDockChange"
/> />
</SettingCard> </SettingCard>
@@ -163,7 +163,7 @@
small small
no-border no-border
:title="t('pages.settings.system.miniWindowOnTop')" :title="t('pages.settings.system.miniWindowOnTop')"
@click="handleMiniWindowOntop(formOfSetting.miniWindowOntop)" @change="handleMiniWindowOntop"
/> />
</SettingCard> </SettingCard>
@@ -197,7 +197,7 @@
no-border no-border
:title="t('pages.settings.system.autoLaunch')" :title="t('pages.settings.system.autoLaunch')"
:description="t('pages.settings.system.autoLaunchDesc')" :description="t('pages.settings.system.autoLaunchDesc')"
@change="handleAutoStartChange(formOfSetting.autoStart)" @change="handleAutoStartChange"
/> />
</SettingCard> </SettingCard>
<CustomNavCard <CustomNavCard

View File

@@ -111,6 +111,7 @@
> >
<div <div
id="upload-area" id="upload-area"
ref="uploadArea"
class="group/upload relative flex h-full w-full cursor-pointer items-center justify-center rounded-xl border-2 border-dashed border-border bg-bg-secondary px-1 py-12 duration-medium ease-standard focus-visible:focus-ring focus-visible:outline-offset-4 max-md:px-4 max-md:py-8 max-xs:px-2 max-xs:py-6 [:hover,.drag-active]:border-accent [:hover,.drag-active]:bg-[linear-gradient(135deg,var(--color-surface-elevated)_0%,color-mix(in_srgb,var(--color-accent),transparent_95%)_100%)] [:hover,.drag-active]:shadow-lg [:hover,.drag-active&]:-translate-y-[2px]" class="group/upload relative flex h-full w-full cursor-pointer items-center justify-center rounded-xl border-2 border-dashed border-border bg-bg-secondary px-1 py-12 duration-medium ease-standard focus-visible:focus-ring focus-visible:outline-offset-4 max-md:px-4 max-md:py-8 max-xs:px-2 max-xs:py-6 [:hover,.drag-active]:border-accent [:hover,.drag-active]:bg-[linear-gradient(135deg,var(--color-surface-elevated)_0%,color-mix(in_srgb,var(--color-accent),transparent_95%)_100%)] [:hover,.drag-active]:shadow-lg [:hover,.drag-active&]:-translate-y-[2px]"
:class="{ 'drag-active': dragover }" :class="{ 'drag-active': dragover }"
@drop.prevent="onDrop" @drop.prevent="onDrop"
@@ -736,7 +737,8 @@ interface IUploadTaskQueueStatus {
} }
} }
useDragEventListeners() const uploadArea = useTemplateRef('uploadArea')
useDragEventListeners(uploadArea)
const $router = useRouter() const $router = useRouter()
const { t } = useI18n() const { t } = useI18n()
const message = useMessage() const message = useMessage()

View File

@@ -1,23 +1,26 @@
import { onBeforeUnmount, onMounted } from 'vue' import { onBeforeUnmount, onMounted, type Ref } from 'vue'
function disableDrag(e: DragEvent) { export function useDragEventListeners(dropZoneRef: Ref<HTMLElement | null>) {
const dropzone = document.getElementById('upload-area') function disableDrag(e: DragEvent) {
if (dropzone === null || !dropzone.contains(e.target as Node)) { const dropzone = dropZoneRef.value
e.preventDefault() if (!dropzone || !dropzone.contains(e.target as Node)) {
e.dataTransfer!.effectAllowed = 'none' e.preventDefault()
e.dataTransfer!.dropEffect = 'none' if (e.dataTransfer) {
e.dataTransfer.effectAllowed = 'none'
e.dataTransfer.dropEffect = 'none'
}
}
} }
}
export function useDragEventListeners() {
onMounted(() => { onMounted(() => {
window.addEventListener('dragenter', disableDrag, false) window.addEventListener('dragenter', disableDrag, false)
window.addEventListener('dragover', disableDrag) window.addEventListener('dragover', disableDrag, false)
window.addEventListener('drop', disableDrag) window.addEventListener('drop', disableDrag, false)
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
window.removeEventListener('dragenter', disableDrag, false) window.removeEventListener('dragenter', disableDrag, false)
window.removeEventListener('dragover', disableDrag) window.removeEventListener('dragover', disableDrag, false)
window.removeEventListener('drop', disableDrag) window.removeEventListener('drop', disableDrag, false)
}) })
} }

View File

@@ -42,7 +42,6 @@
| .amv | AMV视频文件 | .mpg | MPEG视频文件 | | .amv | AMV视频文件 | .mpg | MPEG视频文件 |
| .avi | AVI视频文件 | .mts | AVCHD视频文件 | | .avi | AVI视频文件 | .mts | AVCHD视频文件 |
| .flac | FLAC音频文件 | .ogg | Ogg Vorbis音频文件 | | .flac | FLAC音频文件 | .ogg | Ogg Vorbis音频文件 |
| .flv | Flash视频文件 | .ogv | Ogg Theora视频文件 |
| .m2ts | M2TS视频文件 | .vob | DVD视频文件 | | .m2ts | M2TS视频文件 | .vob | DVD视频文件 |
| .m4a | MPEG-4音频文件 | .wav | WAV音频文件 | | .m4a | MPEG-4音频文件 | .wav | WAV音频文件 |
| .m4v | MPEG-4视频文件 | .webm | WebM视频文件 | | .m4v | MPEG-4视频文件 | .webm | WebM视频文件 |