Skip to content

Commit 12160b0

Browse files
committed
feat(app): add image preview functionality- Add ImagePreview component for zooming in on images
- Implement useImagePreview composable to manage image data and initialization - Update Main.vue to include ImagePreview component - Modify useStaticClientAssets to initialize image preview - Update package.json dependencies
1 parent 4dbc299 commit 12160b0

File tree

7 files changed

+3819
-1773
lines changed

7 files changed

+3819
-1773
lines changed

apps/app/app.vue

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
<!--
2+
- GNU GENERAL PUBLIC LICENSE
3+
- Version 3, 29 June 2007
4+
-
5+
- Copyright (C) 2024 Terwer, Inc. <https://terwer.space/>
6+
- Everyone is permitted to copy and distribute verbatim copies
7+
- of this license document, but changing it is not allowed.
8+
-->
9+
110
<template>
2-
<NuxtPage/>
11+
<div>
12+
<NuxtPage />
13+
</div>
314
</template>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<!--
2+
- GNU GENERAL PUBLIC LICENSE
3+
- Version 3, 29 June 2007
4+
-
5+
- Copyright (C) 2024 Terwer, Inc. <https://terwer.space/>
6+
- Everyone is permitted to copy and distribute verbatim copies
7+
- of this license document, but changing it is not allowed.
8+
-->
9+
10+
<script setup lang="ts">
11+
import { ref } from "vue"
12+
import VueEasyLightbox from "vue-easy-lightbox"
13+
14+
const props = defineProps<{
15+
images: string[]
16+
index?: number
17+
}>()
18+
19+
const logger = createAppLogger("image-preview")
20+
const visibleRef = ref(false)
21+
const indexRef = ref(props.index ?? 0)
22+
const imgsRef = ref(props.images)
23+
24+
const show = (index: number) => {
25+
logger.debug("show method called with index:", index)
26+
indexRef.value = index
27+
visibleRef.value = true
28+
}
29+
30+
const onHide = () => {
31+
visibleRef.value = false
32+
}
33+
34+
// 确保在组件挂载后暴露方法
35+
defineExpose({
36+
show
37+
})
38+
</script>
39+
40+
<template>
41+
<vue-easy-lightbox
42+
:visible="visibleRef"
43+
:imgs="imgsRef"
44+
:index="indexRef"
45+
@hide="onHide"
46+
/>
47+
</template>
48+
49+
<style lang="stylus">
50+
.v-easy-lightbox
51+
&__wrapper
52+
background-color: rgba(0, 0, 0, 0.9) !important
53+
&__img
54+
max-width: 90vw !important
55+
max-height: 90vh !important
56+
object-fit: contain !important
57+
&__toolbar
58+
background-color: rgba(0, 0, 0, 0.5) !important
59+
&__btn
60+
color: #fff !important
61+
&:hover
62+
background-color: rgba(255, 255, 255, 0.1) !important
63+
</style>

apps/app/components/static/content/Main.vue

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@
88
-->
99

1010
<script setup lang="ts">
11-
import type AppConfig from "~/app.config"
11+
import { useImagePreview } from "~/composables/useImagePreview"
12+
import ImagePreview from "~/components/common/ImagePreview.vue"
1213
13-
const props = defineProps<{ post: any, setting: typeof AppConfig }>()
14+
const props = defineProps<{
15+
post: any
16+
setting?: any
17+
}>()
18+
19+
const { images, previewRef } = useImagePreview()
1420
1521
// 正文
1622
const editorDom = props.post.editorDom?.replaceAll("contenteditable=\"true\"", "contenteditable=\"false\"") ?? ""
@@ -56,6 +62,9 @@ const VNode = () =>
5662
<VNode />
5763
</div>
5864
</div>
65+
<client-only>
66+
<ImagePreview ref="previewRef" :images="images as any" />
67+
</client-only>
5968
</div>
6069
</template>
6170

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* GNU GENERAL PUBLIC LICENSE
3+
* Version 3, 29 June 2007
4+
*
5+
* Copyright (C) 2024 Terwer, Inc. <https://terwer.space/>
6+
* Everyone is permitted to copy and distribute verbatim copies
7+
* of this license document, but changing it is not allowed.
8+
*/
9+
10+
import { ref } from "vue"
11+
12+
type ImagePreviewInstance = {
13+
images: ReturnType<typeof ref<string[]>>
14+
previewRef: ReturnType<typeof ref>
15+
initImagePreview: (el: HTMLElement) => void
16+
}
17+
18+
// 创建一个全局单例
19+
let instance: ImagePreviewInstance | null = null
20+
21+
const createImagePreview = (): ImagePreviewInstance => {
22+
const logger = createAppLogger("use-image-preview")
23+
const images = ref<string[]>([])
24+
const previewRef = ref()
25+
// noinspection JSDeprecatedSymbols
26+
const isSSR = process.server
27+
28+
const initImagePreview = (el: HTMLElement) => {
29+
// 确保在客户端执行
30+
if (isSSR) {
31+
return
32+
}
33+
34+
const imgs = el.querySelectorAll("img")
35+
if (imgs && imgs.length > 0) {
36+
// 收集所有图片URL
37+
images.value = Array.from(imgs).map(img => img.src)
38+
logger.info(`Found ${images.value.length} images`)
39+
40+
// 为每个图片添加点击事件
41+
imgs.forEach((img, index) => {
42+
img.style.cursor = "zoom-in"
43+
img.addEventListener("click", () => {
44+
logger.info(`Clicking image ${index}, previewRef:`, previewRef.value)
45+
if (previewRef.value) {
46+
logger.info("Calling show method")
47+
previewRef.value.show(index)
48+
} else {
49+
logger.error("previewRef is not initialized")
50+
}
51+
})
52+
})
53+
} else {
54+
logger.info("No images found in the element")
55+
images.value = []
56+
}
57+
}
58+
59+
return {
60+
images,
61+
previewRef,
62+
initImagePreview
63+
}
64+
}
65+
66+
export const useImagePreview = () => {
67+
if (!instance) {
68+
instance = createImagePreview()
69+
}
70+
return instance
71+
}

apps/app/package.json

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,31 @@
1111
},
1212
"dependencies": {
1313
"@element-plus/icons-vue": ">=0.2.6",
14-
"@element-plus/nuxt": "1.1.1",
15-
"@nuxtjs/i18n": "9.1.1",
16-
"@pinia/nuxt": "^0.9.0",
14+
"@element-plus/nuxt": "^1.1.3",
15+
"@nuxtjs/i18n": "9.5.5",
16+
"@pinia/nuxt": "^0.11.1",
1717
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
18-
"@vueuse/core": "^12.2.0",
19-
"cheerio": "^1.0.0",
18+
"@vueuse/core": "^13.3.0",
19+
"cheerio": "^1.1.0",
2020
"dayjs": "^1.11.13",
2121
"element-plus": ">=2",
2222
"highlight.js": "11.7.0",
2323
"js-base64": "^3.7.7",
2424
"lodash-unified": "^1.0.3",
25-
"nuxt": "^3.15.0",
26-
"pinia": "^2.3.0",
27-
"vue": "^3.5.13",
28-
"vue-router": "4.5.0",
29-
"zhi-common": "^1.33.1",
30-
"zhi-device": "^2.11.0",
25+
"nuxt": "3.17.5",
26+
"pinia": "^3.0.3",
27+
"vue": "^3.5.16",
28+
"vue-easy-lightbox": "^1.19.0",
29+
"vue-router": "4.5.1",
30+
"zhi-common": "^1.34.1",
31+
"zhi-device": "^2.12.0",
3132
"zhi-lib-base": "^0.8.0"
3233
},
3334
"devDependencies": {
34-
"@rushstack/eslint-patch": "^1.10.4",
35+
"@rushstack/eslint-patch": "^1.11.0",
3536
"stylus": "^0.64.0",
36-
"unplugin-auto-import": "^0.19.0",
37-
"unplugin-vue-components": "^0.28.0",
38-
"vite": "^5.4.11"
37+
"unplugin-auto-import": "^19.3.0",
38+
"unplugin-vue-components": "^28.7.0",
39+
"vite": "^6.3.4"
3940
}
4041
}

apps/app/plugins/libs/renderer/useStaticClientAssets.ts

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ import { StrUtil } from "zhi-common"
1111
import { Base64 } from "js-base64"
1212
import { useProviderMode } from "~/composables/useProviderMode"
1313
import { useBaseUrl } from "~/plugins/libs/renderer/useClientBaseUrl"
14+
import { useImagePreview } from "~/composables/useImagePreview"
1415

1516
/**
1617
* 处理客户端资源文件地址
1718
*/
1819
export const useStaticClientAssets = () => {
1920
const logger = createAppLogger("use-static-client-assets")
2021
const { getClientBaseUrl } = useBaseUrl()
22+
const { initImagePreview } = useImagePreview()
2123

2224
const addClientAssetsPrefix = (el: HTMLElement) => {
2325
const pageId = el.getAttribute("data-page-id") ?? ""
@@ -58,38 +60,10 @@ export const useStaticClientAssets = () => {
5860
})
5961
logger.info("The local image has been processed and the picture display has been repaired.")
6062
}
61-
}
62-
63-
// =========================================================================
64-
// const getImageBlob = async (url: string) => {
65-
// try {
66-
// const response = await fetch(url)
67-
// return await response.blob()
68-
// } catch (e: any) {
69-
// logger.error("image read error", e)
70-
// return ""
71-
// }
72-
// }
7363

74-
// const getImageAsBase64 = async (url) => {
75-
// try {
76-
// const response = await fetch(url)
77-
// const blob = await response.blob()
78-
// return await convertBlobToBase64(blob)
79-
// } catch (error) {
80-
// logger.error("image read error", e)
81-
// return ""
82-
// }
83-
// }
84-
85-
// const convertBlobToBase64 = (blob) => {
86-
// return new Promise((resolve, reject) => {
87-
// const reader = new FileReader()
88-
// reader.onloadend = () => resolve(reader.result)
89-
// reader.onerror = reject
90-
// reader.readAsDataURL(blob)
91-
// })
92-
// }
64+
// 初始化图片预览
65+
initImagePreview(el)
66+
}
9367

9468
return { addClientAssetsPrefix }
9569
}

0 commit comments

Comments
 (0)