From 3f71d2c19f63db7eca6d52685fe39e506264fd4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E6=A2=93=E5=BD=AD?= Date: Tue, 7 Apr 2026 14:13:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=BE=E7=89=87=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ImageViewer/index.vue | 68 ++++++++ src/components/PreviewImage/index.vue | 149 ++++++++++++++++++ src/directive/index.ts | 5 + src/directive/previewDirective.ts | 40 +++++ src/utils/imagePreview.ts | 71 +++++++++ src/views/blog/albums/pictures.vue | 60 +++---- src/views/marchant/certification/index.vue | 84 ++++++++-- .../match/manage/Steps/Step1BasicInfo.vue | 78 ++++++++- src/views/match/manage/work.vue | 12 +- src/views/match/points/config/index.vue | 5 +- src/views/match/user/general/index.vue | 12 +- src/views/match/user/unit/index.vue | 12 +- src/views/permission/account/index.vue | 57 ++++--- src/views/team/athlete/index.vue | 33 +++- src/views/user/registration/index.vue | 12 +- 15 files changed, 607 insertions(+), 91 deletions(-) create mode 100644 src/components/ImageViewer/index.vue create mode 100644 src/components/PreviewImage/index.vue create mode 100644 src/directive/previewDirective.ts create mode 100644 src/utils/imagePreview.ts diff --git a/src/components/ImageViewer/index.vue b/src/components/ImageViewer/index.vue new file mode 100644 index 0000000..1cc50bf --- /dev/null +++ b/src/components/ImageViewer/index.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/src/components/PreviewImage/index.vue b/src/components/PreviewImage/index.vue new file mode 100644 index 0000000..cbd0f92 --- /dev/null +++ b/src/components/PreviewImage/index.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/src/directive/index.ts b/src/directive/index.ts index 6ae5177..58120a2 100644 --- a/src/directive/index.ts +++ b/src/directive/index.ts @@ -2,12 +2,14 @@ import type { App } from 'vue'; import { authDirective } from '/@/directive/authDirective'; import { wavesDirective, dragDirective } from '/@/directive/customDirective'; import { debounceDirective } from './debounceDirective'; +import { previewDirective } from './previewDirective'; /** * 导出指令方法:v-xxx * @methods authDirective 用户权限指令,用法:v-auth * @methods wavesDirective 按钮波浪指令,用法:v-waves * @methods dragDirective 自定义拖动指令,用法:v-drag + * @methods previewDirective 图片预览指令,用法:v-preview */ export function directive(app: App) { // 用户权限指令 @@ -19,4 +21,7 @@ export function directive(app: App) { //防抖指令 debounceDirective(app); + + // 图片预览指令 + app.directive('preview', previewDirective); } diff --git a/src/directive/previewDirective.ts b/src/directive/previewDirective.ts new file mode 100644 index 0000000..91ef1ce --- /dev/null +++ b/src/directive/previewDirective.ts @@ -0,0 +1,40 @@ +import type { Directive, DirectiveBinding } from 'vue'; +import { useImagePreview } from '../utils/imagePreview'; + +const { previewSingleImage } = useImagePreview(); + +export const previewDirective: Directive = { + mounted(el: HTMLElement, binding: DirectiveBinding) { + const imgEl = el.tagName === 'IMG' ? el : el.querySelector('img'); + if (!imgEl) return; + + const urlList = binding.value?.urlList || []; + const disabled = binding.value?.disabled || false; + + if (!disabled) { + imgEl.style.cursor = 'pointer'; + imgEl.addEventListener('click', (e) => { + e.stopPropagation(); + const src = (imgEl as HTMLImageElement).src; + if (urlList.length > 0) { + const { showImagePreview } = useImagePreview(); + const index = urlList.indexOf(src); + showImagePreview({ + urlList, + initialIndex: index >= 0 ? index : 0, + }); + } else { + previewSingleImage(src); + } + }); + } + }, + + unmounted(el: HTMLElement) { + const imgEl = el.tagName === 'IMG' ? el : el.querySelector('img'); + if (!imgEl) return; + imgEl.removeEventListener('click', () => {}); + }, +}; + +export default previewDirective; diff --git a/src/utils/imagePreview.ts b/src/utils/imagePreview.ts new file mode 100644 index 0000000..76cede9 --- /dev/null +++ b/src/utils/imagePreview.ts @@ -0,0 +1,71 @@ +import { createApp, markRaw, h, ref, type App } from 'vue'; +import ImageViewer from '../components/ImageViewer/index.vue'; + +interface ImagePreviewOptions { + urlList: string[]; + initialIndex?: number; +} + +let imageViewerInstance: any = null; + +export const useImagePreview = () => { + const showImagePreview = (options: ImagePreviewOptions) => { + const { urlList, initialIndex = 0 } = options; + + if (!urlList || urlList.length === 0) { + return; + } + + if (imageViewerInstance) { + imageViewerInstance.unmount(); + imageViewerInstance = null; + } + + const container = document.createElement('div'); + document.body.appendChild(container); + + const app = createApp({ + components: { ImageViewer }, + setup() { + const visible = ref(true); + + const handleClose = () => { + visible.value = false; + setTimeout(() => { + app.unmount(); + if (document.body.contains(container)) { + document.body.removeChild(container); + } + imageViewerInstance = null; + }, 300); + }; + + return () => + h(ImageViewer, { + modelValue: visible.value, + 'onUpdate:modelValue': (val: boolean) => { + visible.value = val; + if (!val) { + handleClose(); + } + }, + urlList, + initialIndex, + onClose: handleClose, + }); + }, + }); + + imageViewerInstance = app; + app.mount(container); + }; + + const previewSingleImage = (url: string) => { + showImagePreview({ urlList: [url] }); + }; + + return { + showImagePreview, + previewSingleImage, + }; +}; diff --git a/src/views/blog/albums/pictures.vue b/src/views/blog/albums/pictures.vue index 28a3b05..92db644 100644 --- a/src/views/blog/albums/pictures.vue +++ b/src/views/blog/albums/pictures.vue @@ -17,20 +17,16 @@
- +
-
- - 查看 -
删除 @@ -55,18 +51,17 @@ -
+ + +