🔨 Refactor(custom): use new useTemplateRef of vue 3.5

This commit is contained in:
Kuingsmile
2026-01-03 09:45:55 +08:00
parent 34fde7a4e6
commit 21e79af1c6
14 changed files with 56 additions and 44 deletions

View File

@@ -93,7 +93,7 @@ try {
load: yaml.load, load: yaml.load,
}, },
mime: { mime: {
lookup: mime.getType, lookup: mime.getType.bind,
}, },
buffer: { buffer: {
from: Buffer.from, from: Buffer.from,

View File

@@ -4,8 +4,8 @@
<div class="tab-navigation"> <div class="tab-navigation">
<div class="tab-indicator" :style="tabIndicatorStyle" /> <div class="tab-indicator" :style="tabIndicatorStyle" />
<button <button
v-for="(tab, index) in tabs" v-for="tab in tabs"
:ref="el => setTabRef(el, index)" ref="tabRefs"
:key="tab.id" :key="tab.id"
class="tab-button" class="tab-button"
:class="{ active: activeTab === tab.id }" :class="{ active: activeTab === tab.id }"
@@ -759,7 +759,18 @@ import {
Sliders, Sliders,
} from 'lucide-vue-next' } from 'lucide-vue-next'
import type { IBuildInCompressOptions, IBuildInWaterMarkOptions } from 'piclist' import type { IBuildInCompressOptions, IBuildInWaterMarkOptions } from 'piclist'
import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref, toRaw, watch } from 'vue' import {
computed,
nextTick,
onBeforeMount,
onBeforeUnmount,
onMounted,
reactive,
ref,
toRaw,
useTemplateRef,
watch,
} from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { configPaths } from '@/utils/configPaths' import { configPaths } from '@/utils/configPaths'
@@ -772,14 +783,11 @@ const { t } = useI18n()
const activeTab = ref('general') const activeTab = ref('general')
// Tab indicator animation // Tab indicator animation
const tabRefs = ref<(HTMLElement | null)[]>([]) const tabRefs = useTemplateRef('tabRefs')
const tabIndicatorStyle = ref<Record<string, string>>({}) const tabIndicatorStyle = ref<Record<string, string>>({})
function setTabRef(el: any, index: number) {
tabRefs.value[index] = el
}
function updateTabIndicator() { function updateTabIndicator() {
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)
const activeTabEl = tabRefs.value[activeIndex] const activeTabEl = tabRefs.value[activeIndex]
if (activeTabEl) { if (activeTabEl) {

View File

@@ -55,7 +55,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { XIcon } from 'lucide-vue-next' import { XIcon } from 'lucide-vue-next'
import { nextTick, onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue' import { nextTick, onBeforeMount, onBeforeUnmount, reactive, ref, useTemplateRef } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import $bus from '@/utils/bus' import $bus from '@/utils/bus'
@@ -64,8 +64,8 @@ import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant'
const { t } = useI18n() const { t } = useI18n()
const inputBoxValue = ref('') const inputBoxValue = ref('')
const showInputBoxVisible = ref(false) const showInputBoxVisible = ref(false)
const inputRef = ref<HTMLInputElement>() const inputRef = useTemplateRef('inputRef')
const textareaRef = ref<HTMLTextAreaElement>() const textareaRef = useTemplateRef('textareaRef')
const inputBoxOptions = reactive({ const inputBoxOptions = reactive({
title: '', title: '',
placeholder: '', placeholder: '',

View File

@@ -1,7 +1,7 @@
<!-- 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: props.colorMode === 'white' }]">
<form ref="$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">
<label class="form-label">{{ t('pages.configForm.configName') }}</label> <label class="form-label">{{ t('pages.configForm.configName') }}</label>
@@ -147,7 +147,6 @@ const props = withDefaults(defineProps<IProps>(), {
}) })
const $route = useRoute() const $route = useRoute()
const $form = ref<HTMLFormElement>()
const { t } = useI18n() const { t } = useI18n()
const configList = ref<IPicGoPluginConfig[]>([]) const configList = ref<IPicGoPluginConfig[]>([])
@@ -159,7 +158,7 @@ const visibleTooltips = reactive<Record<string, boolean>>({})
watch( watch(
toRefs(props.config), toRefs(props.config),
(val: IPicGoPluginConfig[]) => { (val: IPicGoPluginConfig[]) => {
handleConfigChange(val) handleConfig(val)
}, },
{ {
deep: true, deep: true,
@@ -167,10 +166,6 @@ watch(
}, },
) )
function handleConfigChange(val: any) {
handleConfig(val)
}
function validateField(fieldName: string, value: any, _?: IPicGoPluginConfig): string | null { function validateField(fieldName: string, value: any, _?: IPicGoPluginConfig): string | null {
if (fieldName === '_configName') { if (fieldName === '_configName') {
if (!value || value.trim() === '') { if (!value || value.trim() === '') {

View File

@@ -24,7 +24,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue' import { computed, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue'
import { useVirtualGrid } from '@/hooks/useVirtualGrid' import { useVirtualGrid } from '@/hooks/useVirtualGrid'
@@ -59,7 +59,7 @@ const props = withDefaults(
}, },
) )
const containerRef = ref<HTMLElement | null>(null) const containerRef = useTemplateRef('containerRef')
const containerHeight = ref<number>(props.pageMode ? 0 : props.height) const containerHeight = ref<number>(props.pageMode ? 0 : props.height)
const containerWidth = ref<number>(0) const containerWidth = ref<number>(0)
const parentScrollListeners = ref<HTMLElement[]>([]) const parentScrollListeners = ref<HTMLElement[]>([])

View File

@@ -20,7 +20,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref } from 'vue' import { onMounted, reactive, ref, useTemplateRef } from 'vue'
import useConfirm, { type ConfirmOptions } from '@/hooks/useConfirm' import useConfirm, { type ConfirmOptions } from '@/hooks/useConfirm'
import useMessage from '@/hooks/useMessage' import useMessage from '@/hooks/useMessage'
@@ -28,7 +28,7 @@ import useMessage from '@/hooks/useMessage'
import ConfirmMessageBox from './ConfirmMessageBox.vue' import ConfirmMessageBox from './ConfirmMessageBox.vue'
import MessageToast from './MessageToast.vue' import MessageToast from './MessageToast.vue'
const messageRef = ref<InstanceType<typeof MessageToast> | null>(null) const messageRef = useTemplateRef('messageRef')
const confirmVisible = ref(false) const confirmVisible = ref(false)
const confirmOptions = reactive<ConfirmOptions>({ const confirmOptions = reactive<ConfirmOptions>({
message: '', message: '',

View File

@@ -1266,7 +1266,7 @@ import {
} from 'lucide-vue-next' } from 'lucide-vue-next'
import { marked } from 'marked' import { marked } from 'marked'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { computed, nextTick, onBeforeMount, onBeforeUnmount, reactive, ref, watch } from 'vue' import { computed, nextTick, onBeforeMount, onBeforeUnmount, reactive, ref, useTemplateRef, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
@@ -1335,8 +1335,8 @@ const isShowLoadingPage = ref(false)
const isShowImagePreview = ref(false) const isShowImagePreview = ref(false)
const layoutStyle = ref<'list' | 'grid'>('grid') const layoutStyle = ref<'list' | 'grid'>('grid')
// Refs for scroll handling // Refs for scroll handling
const virtualScrollerRef = ref() const virtualScrollerRef = useTemplateRef('virtualScrollerRef')
const bucketContainerRef = ref() const bucketContainerRef = useTemplateRef('bucketContainerRef')
// 全屏控制变量 // 全屏控制变量
const isContentFullscreen = ref(false) const isContentFullscreen = ref(false)
// 新增的UI控制变量 // 新增的UI控制变量
@@ -1443,7 +1443,7 @@ const videoPlayerHeaders = ref({})
// 创建文件夹相关 // 创建文件夹相关
const isShowCreateFolderDialog = ref(false) const isShowCreateFolderDialog = ref(false)
const newFolderName = ref('') const newFolderName = ref('')
const folderNameInput = ref() const folderNameInput = useTemplateRef('folderNameInput')
// 重命名相关 // 重命名相关
const isShowRenameFileIcon = computed(() => const isShowRenameFileIcon = computed(() =>
['tcyun', 'aliyun', 'qiniu', 'upyun', 's3plist', 'webdavplist', 'local', 'sftp'].includes(currentPicBedName.value), ['tcyun', 'aliyun', 'qiniu', 'upyun', 's3plist', 'webdavplist', 'local', 'sftp'].includes(currentPicBedName.value),
@@ -3036,8 +3036,9 @@ function toggleCopyDropdown(index: number, event?: MouseEvent) {
const viewportWidth = window.innerWidth const viewportWidth = window.innerWidth
const viewportHeight = window.innerHeight const viewportHeight = window.innerHeight
const container = bucketContainerRef.value?.$el || bucketContainerRef.value const container = bucketContainerRef.value
const containerRect = container?.getBoundingClientRect() const containerRect = container?.getBoundingClientRect()
console.log('containerRect', containerRect)
const dropdownWidth = 160 const dropdownWidth = 160
const shouldShowLeft = const shouldShowLeft =
rect.right > viewportWidth - dropdownWidth || rect.right > viewportWidth - dropdownWidth ||

View File

@@ -25,7 +25,7 @@
<div class="login-card tabs-card"> <div class="login-card tabs-card">
<div class="tabs-container"> <div class="tabs-container">
<div class="tabs-nav-wrapper"> <div class="tabs-nav-wrapper">
<div ref="tabsNav" class="tabs-nav"> <div class="tabs-nav">
<button <button
v-for="item in tabItems" v-for="item in tabItems"
:key="item.key" :key="item.key"
@@ -319,7 +319,6 @@ const { confirm } = useConfirm()
const activeName = ref('login') const activeName = ref('login')
const expandedConfigs = ref<string[]>([]) const expandedConfigs = ref<string[]>([])
const importDropdownOpen = ref(false) const importDropdownOpen = ref(false)
const tabsNav = ref<HTMLElement>()
const configResult: IStringKeyMap = reactive({}) const configResult: IStringKeyMap = reactive({})
const existingConfiguration = reactive({} as IStringKeyMap) const existingConfiguration = reactive({} as IStringKeyMap)

View File

@@ -36,7 +36,7 @@
<!-- Main Content Card --> <!-- Main Content Card -->
<div class="manage-card main-card"> <div class="manage-card main-card">
<div class="main-layout"> <div class="main-layout">
<div ref="sidebar" class="sidebar" :style="{ width: sidebarWidth + 'px' }"> <div class="sidebar" :style="{ width: sidebarWidth + 'px' }">
<div class="sidebar-header"> <div class="sidebar-header">
<h3 class="sidebar-title"> <h3 class="sidebar-title">
{{ menuTitleMap[currentPicBedName] }} {{ menuTitleMap[currentPicBedName] }}
@@ -92,7 +92,7 @@
<div class="resize-line" /> <div class="resize-line" />
</div> </div>
<div ref="contentArea" class="content-area"> <div class="content-area">
<router-view /> <router-view />
</div> </div>
</div> </div>
@@ -286,7 +286,6 @@ const message = useMessage()
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 contentArea = ref<HTMLElement>()
const sidebarWidth = ref(160) const sidebarWidth = ref(160)
const isResizing = ref(false) const isResizing = ref(false)

View File

@@ -501,7 +501,17 @@ import {
TrashIcon, TrashIcon,
XIcon, XIcon,
} from 'lucide-vue-next' } from 'lucide-vue-next'
import { computed, nextTick, onActivated, onBeforeMount, onBeforeUnmount, reactive, ref, watch } from 'vue' import {
computed,
nextTick,
onActivated,
onBeforeMount,
onBeforeUnmount,
reactive,
ref,
useTemplateRef,
watch,
} from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { onBeforeRouteUpdate } from 'vue-router' import { onBeforeRouteUpdate } from 'vue-router'
@@ -529,8 +539,8 @@ type IResult<T> = T & {
} }
const images = ref<ImgInfo[]>([]) const images = ref<ImgInfo[]>([])
const virtualScrollerRef = ref<InstanceType<typeof VirtualScroller>>() const virtualScrollerRef = useTemplateRef('virtualScrollerRef')
const previewImageRef = ref<HTMLImageElement>() const previewImageRef = useTemplateRef('previewImageRef')
const dialogVisible = ref(false) const dialogVisible = ref(false)
const imgInfo = reactive({ const imgInfo = reactive({
id: '', id: '',

View File

@@ -221,7 +221,7 @@ import {
XCircleIcon, XCircleIcon,
XIcon, XIcon,
} from 'lucide-vue-next' } from 'lucide-vue-next'
import { computed, onBeforeMount, onBeforeUnmount, reactive, ref, toRaw, watch } from 'vue' import { computed, onBeforeMount, onBeforeUnmount, reactive, ref, toRaw, useTemplateRef, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import ConfigForm from '@/components/UnifiedConfigForm.vue' import ConfigForm from '@/components/UnifiedConfigForm.vue'
@@ -248,7 +248,7 @@ const pluginNameList = ref<string[]>([])
const loading = ref(true) const loading = ref(true)
const needReload = ref(false) const needReload = ref(false)
const latestVersionMap = reactive<Record<string, string>>({}) const latestVersionMap = reactive<Record<string, string>>({})
const $configForm = ref<InstanceType<typeof ConfigForm> | null>(null) const $configForm = useTemplateRef('$configForm')
const strictSearch = useStorage('plugin-strict-search', true) const strictSearch = useStorage('plugin-strict-search', true)
function setSrc(e: Event) { function setSrc(e: Event) {

View File

@@ -42,14 +42,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { XIcon } from 'lucide-vue-next' import { XIcon } from 'lucide-vue-next'
import { nextTick, onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue' import { nextTick, onBeforeMount, onBeforeUnmount, reactive, ref, useTemplateRef } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME } from '@/utils/constant' import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME } from '@/utils/constant'
const { t } = useI18n() const { t } = useI18n()
const id = ref<string | null>(null) const id = ref<string | null>(null)
const fileNameInput = ref<HTMLInputElement>() const fileNameInput = useTemplateRef('fileNameInput')
const validationError = ref<string>('') const validationError = ref<string>('')
const form = reactive({ const form = reactive({

View File

@@ -162,7 +162,7 @@ import {
UploadCloudIcon, UploadCloudIcon,
XIcon, XIcon,
} from 'lucide-vue-next' } from 'lucide-vue-next'
import { onBeforeMount, onBeforeUnmount, ref, watch } from 'vue' import { 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'
@@ -192,7 +192,7 @@ const showError = ref(false)
const pasteStyle = ref('') const pasteStyle = ref('')
const picBedName = ref('') const picBedName = ref('')
const picBedConfigName = ref('') const picBedConfigName = ref('')
const fileInput = ref<HTMLInputElement>() const fileInput = useTemplateRef('fileInput')
const pasteFormatList = ref<Record<string, string>>({ const pasteFormatList = ref<Record<string, string>>({
[IPasteStyle.MARKDOWN]: '![alt](url)', [IPasteStyle.MARKDOWN]: '![alt](url)',

View File

@@ -133,7 +133,7 @@ import {
RotateCcw, RotateCcw,
Settings, Settings,
} from 'lucide-vue-next' } from 'lucide-vue-next'
import { onBeforeMount, ref } from 'vue' import { onBeforeMount, ref, useTemplateRef } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
@@ -156,7 +156,7 @@ const loading = ref(false)
const dropdownVisible = ref(false) const dropdownVisible = ref(false)
const $route = useRoute() const $route = useRoute()
const $router = useRouter() const $router = useRouter()
const $configForm = ref<InstanceType<typeof ConfigForm> | null>(null) const $configForm = useTemplateRef('$configForm')
type.value = $route.params.type as string type.value = $route.params.type as string