Skip to content

Commit b48062c

Browse files
committed
chore(forum): 优化图片上传和加载时的错误处理
1 parent 8b3b8e2 commit b48062c

File tree

5 files changed

+68
-64
lines changed

5 files changed

+68
-64
lines changed

.vitepress/theme/components/ui/image/Image.vue

Lines changed: 52 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,88 @@
11
<script setup lang="ts">
22
import { withBase } from 'vitepress'
3-
import mediumZoom, { type Zoom, type ZoomOptions } from 'medium-zoom'
4-
3+
import mediumZoom, { type ZoomOptions } from 'medium-zoom'
4+
import {
5+
onMounted,
6+
onUnmounted,
7+
ref,
8+
computed,
9+
useTemplateRef,
10+
watch,
11+
useAttrs,
12+
} from 'vue'
13+
import { cn } from '@/lib/utils'
514
import type { DefaultTheme } from 'vitepress/theme-without-fonts'
6-
import { onMounted, onUnmounted, useTemplateRef, watch } from 'vue'
715
816
interface Props {
917
image?: DefaultTheme.ThemeableImage
1018
alt?: string
1119
zoom?: ZoomOptions | false
20+
class?: string
1221
}
1322
14-
const {
15-
zoom: zoomConfig = {
16-
background: 'transparent',
17-
},
18-
} = defineProps<Props>()
23+
const { zoom: zoomConfig = { background: 'transparent' }, image } =
24+
defineProps<Props>()
1925
2026
defineOptions({ inheritAttrs: false })
2127
2228
const img = useTemplateRef('img')
29+
const loadFail = ref(false)
30+
const attrs = useAttrs()
31+
// 计算 `imgSrc`,动态监听 `image` 变化
32+
const imgSrc = computed(() =>
33+
withBase(
34+
(typeof image === 'string' ? image : image?.src) ||
35+
attrs?.src ||
36+
'https://assets.yuanshen.site/images/noImage.png',
37+
),
38+
)
2339
24-
const initZoom = () => {
25-
if (zoomConfig === false) return
26-
let zoom: Zoom | null = null
27-
28-
const getZoom = () => (zoom === null ? mediumZoom(zoomConfig) : zoom)
40+
// medium-zoom 只创建一次,避免重复初始化
41+
const zoom = zoomConfig === false ? null : mediumZoom(zoomConfig)
2942
30-
onMounted(() => {
31-
if (!img.value) return
43+
const initZoom = () => {
44+
if (!zoom || !img.value) return
45+
zoom.attach(img.value)
46+
}
3247
33-
getZoom().attach(img.value)
48+
onMounted(initZoom)
49+
onUnmounted(() => zoom?.detach())
3450
35-
watch(
36-
() => zoomConfig,
37-
(options) => {
38-
const zoom = getZoom()
39-
zoom.update(zoomConfig || {})
40-
},
41-
)
42-
})
51+
// 监听 zoomConfig 的变化,避免 Vue 的 shallow 监听失效
52+
watch(
53+
() => JSON.stringify(zoomConfig),
54+
() => zoom?.update(zoomConfig || {}),
55+
)
4356
44-
onUnmounted(() => {
45-
if (img.value) getZoom().detach()
46-
})
57+
const handleLoadError = () => {
58+
loadFail.value = true
4759
}
48-
49-
initZoom()
5060
</script>
5161

5262
<template>
53-
<template v-if="image">
63+
<img
64+
v-if="!loadFail"
65+
ref="img"
66+
v-bind="typeof image === 'string' ? $attrs : { ...image, ...$attrs }"
67+
:src="imgSrc"
68+
:alt="alt ?? (typeof image === 'string' ? '' : image?.alt || '')"
69+
:class="cn('VPImage', $props.class)"
70+
@error="handleLoadError"
71+
/>
72+
<template v-else>
5473
<img
55-
v-if="typeof image === 'string' || 'src' in image"
56-
ref="img"
57-
class="VPImage"
74+
:class="cn('VPImage bg-[var(--vp-c-bg-alt)]', $props.class)"
75+
:alt="alt ?? (typeof image === 'string' ? '' : image?.alt || '')"
5876
v-bind="typeof image === 'string' ? $attrs : { ...image, ...$attrs }"
59-
:src="withBase(typeof image === 'string' ? image : image.src)"
60-
:alt="alt ?? (typeof image === 'string' ? '' : image.alt || '')"
77+
src="https://assets.yuanshen.site/images/noImage.png"
6178
/>
62-
<template v-else>
63-
<Image
64-
class="dark"
65-
:image="image.dark"
66-
:alt="image.alt"
67-
v-bind="$attrs"
68-
/>
69-
<Image
70-
class="light"
71-
:image="image.light"
72-
:alt="image.alt"
73-
v-bind="$attrs"
74-
/>
75-
</template>
76-
</template>
77-
<template v-else>
78-
<img ref="img" class="VPImage" v-bind="$attrs" />
7979
</template>
8080
</template>
8181

8282
<style scoped>
8383
html:not(.dark) .VPImage.dark {
8484
display: none;
8585
}
86-
8786
.dark .VPImage.light {
8887
display: none;
8988
}

.vitepress/theme/components/ui/image/Images.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
>
1111
<Image
1212
class="object-cover flex-1"
13-
:class="{ 'flex-1': images.length === 3 && index === 0 }"
13+
:class="images.length === 3 && index === 0 ? 'flex-1' : ''"
1414
:data-image-index="index"
1515
:image="image"
1616
/>

src/components/forum/ForumTopic.vue

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,11 @@
5050
v-if="topic.content?.images"
5151
>
5252
<Image
53-
v-for="(img, index) in topic.content.images"
54-
:key="index"
55-
B
56-
:src="img.src"
53+
v-for="img in topic.content.images"
54+
:key="img.src"
55+
:image="img.src"
5756
:alt="img.alt"
58-
class="max-h-24 mr-4 rounded-sm"
57+
class="max-h-30 max-w-30 object-cover mr-4 rounded-sm"
5958
/>
6059
</div>
6160
</div>

src/components/forum/ForumTopicComment.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828

2929
<div class="topic-content-img flex mt-4" v-if="body?.images">
3030
<Image
31-
v-for="(img, ind) in body.images"
32-
:key="ind"
31+
v-for="img in body.images"
32+
:key="img.src"
3333
:src="img.src"
3434
:alt="img.alt"
3535
class="max-h-24 mr-4 rounded-sm"

src/components/forum/publish-topic-form/ForumPublishTopicForm.vue

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,12 @@ const switchTab = () => {
108108
109109
const handleSubmit = async () => {
110110
formData.value.body.images?.forEach((val, ind) => {
111-
if (val.status === 'uploading' || !val.url) return
111+
if (
112+
val.status === 'uploading' ||
113+
!val?.url ||
114+
val.url?.substring(3) === 'blob'
115+
)
116+
return
112117
uploadedImages.value.push(val.url)
113118
})
114119
@@ -128,8 +133,9 @@ watch(
128133
() => formData.value.body.images,
129134
() => {
130135
isUploading.value =
131-
formData.value.body.images?.some((val) => val.status === 'uploading') ??
132-
false
136+
formData.value.body.images?.some(
137+
(val) => val.status === 'uploading' || val.url?.substring(3) === 'blob',
138+
) ?? false
133139
},
134140
{
135141
deep: true,

0 commit comments

Comments
 (0)