Skip to content

Commit 2009296

Browse files
author
Tenny
committed
fix(Image): 修复无法传递自定义样式、支持自定义fallback
1 parent 9f67504 commit 2009296

File tree

4 files changed

+93
-43
lines changed

4 files changed

+93
-43
lines changed

docs/components/image.md

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const imgs = [
2222
<ClientOnly>
2323
<CodePreview class="mt-15">
2424
<textarea lang="vue-html">
25-
<nt-image src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg" width="100"></nt-image>
25+
<nt-image src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg" width="100px"></nt-image>
2626
</textarea>
2727
</CodePreview>
2828
</ClientOnly>
@@ -34,41 +34,41 @@ const imgs = [
3434
<ClientOnly>
3535
<CodePreview>
3636
<textarea lang="vue-html">
37-
<nt-image src="/neatui-vue/img1.svg" width="100" height="100" fit="fill"></nt-image>
38-
<nt-image src="/neatui-vue/img1.svg" width="100" height="100" fit="contain"></nt-image>
39-
<nt-image src="/neatui-vue/img1.svg" width="100" height="100" fit="cover"></nt-image>
40-
<nt-image src="/neatui-vue/img1.svg" width="100" height="100" fit="none"></nt-image>
41-
<nt-image src="/neatui-vue/img1.svg" width="100" height="100" fit="scale-down"></nt-image>
37+
<nt-image src="/neatui-vue/img1.svg" width="100px" height="100px" fit="fill"></nt-image>
38+
<nt-image src="/neatui-vue/img1.svg" width="100px" height="100px" fit="contain"></nt-image>
39+
<nt-image src="/neatui-vue/img1.svg" width="100px" height="100px" fit="cover"></nt-image>
40+
<nt-image src="/neatui-vue/img1.svg" width="100px" height="100px" fit="none"></nt-image>
41+
<nt-image src="/neatui-vue/img1.svg" width="100px" height="100px" fit="scale-down"></nt-image>
4242
</textarea>
4343
<template #preview>
4444
<div class="grid grid-cols-5">
4545
<Tooltip content="被替换的内容正好填充元素的内容框。整个对象将完全填充此框。如果对象的宽高比与内容框不相匹配,那么该对象将被拉伸以适应内容框">
4646
<div class="demo-img-item">
47-
<Image src="/neatui-vue/img1.svg" width="100" height="100" fit="fill"></Image>
47+
<Image src="/neatui-vue/img1.svg" width="100px" height="100px" fit="fill"></Image>
4848
<span class="mt-15">fill</span>
4949
</div>
5050
</Tooltip>
5151
<Tooltip content="被替换的内容将被缩放,以在填充元素的内容框时保持其宽高比。整个对象在填充盒子的同时保留其长宽比">
5252
<div class="demo-img-item">
53-
<Image src="/neatui-vue/img1.svg" width="100" height="100" fit="contain"></Image>
53+
<Image src="/neatui-vue/img1.svg" width="100px" height="100px" fit="contain"></Image>
5454
<span class="mt-15">contain</span>
5555
</div>
5656
</Tooltip>
5757
<Tooltip content="被替换的内容在保持其宽高比的同时填充元素的整个内容框。如果对象的宽高比与内容框不相匹配,该对象将被剪裁以适应内容框">
5858
<div class="demo-img-item">
59-
<Image src="/neatui-vue/img1.svg" width="100" height="100" fit="cover"></Image>
59+
<Image src="/neatui-vue/img1.svg" width="100px" height="100px" fit="cover"></Image>
6060
<span class="mt-15">cover</span>
6161
</div>
6262
</Tooltip>
6363
<Tooltip content="被替换的内容将保持其原有的尺寸">
6464
<div class="demo-img-item">
65-
<Image src="/neatui-vue/img1.svg" width="100" height="100" fit="none"></Image>
65+
<Image src="/neatui-vue/img1.svg" width="100px" height="100px" fit="none"></Image>
6666
<span class="mt-15">none</span>
6767
</div>
6868
</Tooltip>
6969
<Tooltip content="内容的尺寸与 none 或 contain 中的一个相同,取决于它们两个之间谁得到的对象尺寸会更小一些">
7070
<div class="demo-img-item">
71-
<Image src="/neatui-vue/img1.svg" width="100" height="100" fit="scale-down"></Image>
71+
<Image src="/neatui-vue/img1.svg" width="100px" height="100px" fit="scale-down"></Image>
7272
<span class="mt-15">scale-down</span>
7373
</div>
7474
</Tooltip>
@@ -84,25 +84,47 @@ const imgs = [
8484
<ClientOnly>
8585
<CodePreview>
8686
<textarea lang="vue-html">
87-
<nt-image src="/neatui-vue/img2.svg" width="100" height="100" placeholder="/neatui-vue/img_loading.png"></nt-image>
87+
<nt-image src="/neatui-vue/img2.svg" width="100px" height="100px" placeholder="/neatui-vue/img_loading.png"></nt-image>
8888
</textarea>
8989
<template #preview>
90-
<Image src="/neatui-vue/img2.svg" width="100" height="100" placeholder="/neatui-vue/img_loading.png"></Image>
90+
<Image src="/neatui-vue/img2.svg" width="100px" height="100px" placeholder="/neatui-vue/img_loading.png"></Image>
9191
</template>
9292
</CodePreview>
9393
</ClientOnly>
9494

9595
### 加载失败
9696

97-
加载失败显示图像占位符
97+
通过配置 `fallback` 配置加载失败显示图像占位符; 也可以传递 `custom-fallback` 启用自定义加载失败占位符, 然后配置 `slot-fallback` 来自定义加载失败显示.
9898

9999
<ClientOnly>
100100
<CodePreview>
101101
<textarea lang="vue-html">
102-
<nt-image src="/neatui-vue/img3.svg" width="100" height="100" placeholder="/neatui-vue/loading_error.png"></nt-image>
102+
<nt-image src="/neatui-vue/img3.svg" width="100px" height="100px" fallback="/neatui-vue/loading_error.png"></nt-image>
103+
<nt-image
104+
src="/neatui-vue/img3.svg"
105+
width="100px"
106+
height="100px"
107+
custom-fallback
108+
class="ml-10"
109+
>
110+
<template #fallback>
111+
<div style="height: 100%;background-color: gray;display:flex;justify-content:center;align-items:center;color:#fff;">Error</div>
112+
</template>
113+
</nt-image>
103114
</textarea>
104115
<template #preview>
105-
<Image src="/neatui-vue/img3.svg" width="100" height="100" placeholder="/neatui-vue/loading_error.png"></Image>
116+
<Image src="/neatui-vue/img3.svg" width="100px" height="100px" fallback="/neatui-vue/loading_error.png"></Image>
117+
<Image
118+
src="/neatui-vue/img3.svg"
119+
width="100px"
120+
height="100px"
121+
custom-fallback
122+
class="ml-10"
123+
>
124+
<template #fallback>
125+
<div style="height: 100%;background-color: gray;display:flex;justify-content:center;align-items:center;color:#fff;">Error</div>
126+
</template>
127+
</Image>
106128
</template>
107129
</CodePreview>
108130
</ClientOnly>
@@ -114,10 +136,10 @@ const imgs = [
114136
<ClientOnly>
115137
<CodePreview>
116138
<textarea lang="vue-html">
117-
<nt-image src="/neatui-vue/img2.svg" width="100" height="100" preview-disable></nt-image>
139+
<nt-image src="/neatui-vue/img2.svg" width="100px" height="100px" preview-disable></nt-image>
118140
</textarea>
119141
<template #preview>
120-
<Image src="/neatui-vue/img2.svg" width="100" height="100" preview-disable></Image>
142+
<Image src="/neatui-vue/img2.svg" width="100px" height="100px" preview-disable></Image>
121143
</template>
122144
</CodePreview>
123145
</ClientOnly>
@@ -138,11 +160,11 @@ const imgs = [
138160
</script>
139161
140162
<template>
141-
<nt-image v-for="src,index in imgs" :key="index" :src="src" :preview-src-list="imgs" :initial-index="index" width="100"></nt-image>
163+
<nt-image v-for="src,index in imgs" :key="index" :src="src" :preview-src-list="imgs" :initial-index="index" width="100px"></nt-image>
142164
</template>
143165
</textarea>
144166
<template #preview>
145-
<Image v-for="src,index in imgs" :key="index" :src="src" :preview-src-list="imgs" :initial-index="index" width="100"></Image>
167+
<Image v-for="src,index in imgs" :key="index" :src="src" :preview-src-list="imgs" :initial-index="index" width="100px"></Image>
146168
</template>
147169
</CodePreview>
148170
</ClientOnly>
@@ -154,10 +176,10 @@ const imgs = [
154176
<ClientOnly>
155177
<CodePreview>
156178
<textarea lang="vue-html">
157-
<nt-image src="/neatui-vue/img2.svg" width="100" height="100" loading="lazy"></nt-image>
179+
<nt-image src="/neatui-vue/img2.svg" width="100px" height="100px" loading="lazy"></nt-image>
158180
</textarea>
159181
<template #preview>
160-
<Image src="/neatui-vue/img2.svg" width="100" height="100" loading="lazy"></Image>
182+
<Image src="/neatui-vue/img2.svg" width="100px" height="100px" loading="lazy"></Image>
161183
</template>
162184
</CodePreview>
163185
</ClientOnly>

eslint.config.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import globals from 'globals';
22
import pluginJs from '@eslint/js';
33
import tseslint from 'typescript-eslint';
4-
import pluginVue, { rules } from 'eslint-plugin-vue';
4+
import pluginVue from 'eslint-plugin-vue';
55
import markdown from '@eslint/markdown';
66

77
export default [
@@ -15,4 +15,9 @@ export default [
1515
files: ['**/*.vue'],
1616
languageOptions: { parserOptions: { parser: tseslint.parser } },
1717
},
18+
{
19+
rules: {
20+
'vue/multi-word-component-names': 'off',
21+
},
22+
},
1823
];

src/components/Image.vue

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,28 @@
11
<template>
2-
<img
3-
:loading="loading"
4-
:src="actualSrc"
5-
:class="['nt-image', previewDisable ? '' : 'nt-image--preview']"
6-
:style="computedStyle"
7-
:alt="alt"
8-
@error="handleLoadError"
9-
@click="handleClick"
10-
/>
2+
<div class="nt-image" v-bind="$attrs">
3+
<img
4+
:loading="loading"
5+
:src="actualSrc"
6+
:class="[
7+
'nt-image',
8+
previewDisable ? '' : 'nt-image--preview',
9+
customFallback && isError ? 'nt-image-hide' : '',
10+
]"
11+
:style="computedStyle"
12+
:alt="alt"
13+
@error="handleLoadError"
14+
@click="handleClick"
15+
@load="handleLoad"
16+
/>
17+
<div
18+
v-if="customFallback && isError"
19+
class="nt-image"
20+
:style="computedStyle"
21+
>
22+
<slot name="fallback"></slot>
23+
</div>
24+
</div>
25+
1126
<ImagePreview
1227
v-model:show="showPreview"
1328
:url-list="previewSrcList || [src]"
@@ -19,6 +34,7 @@ import { computed, ref, watch, onUnmounted } from 'vue';
1934
import ImagePreview from './ImagePreview.vue';
2035
2136
const showPreview = ref(false);
37+
const isError = ref(false);
2238
2339
const props = withDefaults(
2440
defineProps<{
@@ -39,11 +55,13 @@ const props = withDefaults(
3955
/** 预览图片地址列表, 多图预览时使用 */
4056
previewSrcList?: string[];
4157
initialIndex?: number;
58+
customFallback?: boolean;
4259
}>(),
4360
{
4461
loading: 'eager',
4562
previewDisable: false,
4663
initialIndex: 0,
64+
customFallback: false,
4765
},
4866
);
4967
let img: HTMLImageElement | null;
@@ -80,18 +98,14 @@ function handleClick() {
8098
}
8199
}
82100
101+
function handleLoad() {
102+
isError.value = false;
103+
}
104+
83105
const computedStyle = computed(() => {
84106
return {
85-
width: props.width
86-
? props.width.endsWith('px')
87-
? props.width
88-
: `${props.width}px`
89-
: undefined,
90-
height: props.height
91-
? props.height.endsWith('px')
92-
? props.height
93-
: `${props.height}px`
94-
: undefined,
107+
width: props.width ? props.width : undefined,
108+
height: props.height ? props.height : undefined,
95109
objectFit: props.fit != null ? props.fit : undefined,
96110
};
97111
});
@@ -100,6 +114,7 @@ function handleLoadError(e: Event) {
100114
if (props.fallback != null) {
101115
(e.target as HTMLImageElement).src = props.fallback;
102116
}
117+
isError.value = true;
103118
}
104119
105120
function clearImg() {

style/image/index.css

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
.nt-image {
1+
.nt-image,
2+
.nt-image--inner {
23
vertical-align: middle;
34
display: inline-block;
45
}
6+
.nt-image {
7+
box-sizing: border-box;
8+
}
59

610
.nt-image--preview {
711
cursor: pointer;
812
}
13+
14+
.nt-image-hide {
15+
display: none;
16+
}

0 commit comments

Comments
 (0)