179 lines
3.2 KiB
Vue
179 lines
3.2 KiB
Vue
![]() |
<template>
|
||
|
<view
|
||
|
class="cl-image"
|
||
|
:style="[imgStyle, baseStyle]"
|
||
|
:class="[
|
||
|
{
|
||
|
'is-round': round,
|
||
|
'is-error': !src || isError,
|
||
|
},
|
||
|
]"
|
||
|
@tap="onPreview"
|
||
|
>
|
||
|
<view class="cl-image__placeholder" v-if="!src">
|
||
|
<slot name="placeholder">
|
||
|
<text class="icon cl-icon-image"></text>
|
||
|
</slot>
|
||
|
</view>
|
||
|
|
||
|
<view class="cl-image__error" v-else-if="isError">
|
||
|
<slot name="error">
|
||
|
<text class="icon cl-icon-image-error"></text>
|
||
|
</slot>
|
||
|
</view>
|
||
|
|
||
|
<template v-else>
|
||
|
<view class="cl-image__loading" v-if="isLoading">
|
||
|
<cl-loading />
|
||
|
</view>
|
||
|
|
||
|
<image
|
||
|
class="cl-image__target"
|
||
|
:src="src"
|
||
|
:mode="mode"
|
||
|
:lazy-load="lazyLoad"
|
||
|
:webp="webp"
|
||
|
:show-menu-by-longpress="showMenuByLongpress"
|
||
|
@error="handleError"
|
||
|
@load="handleLoad"
|
||
|
/>
|
||
|
|
||
|
<slot></slot>
|
||
|
</template>
|
||
|
</view>
|
||
|
</template>
|
||
|
|
||
|
<script lang="ts">
|
||
|
import { type PropType, computed, defineComponent, ref, watch } from "vue";
|
||
|
import { isNumber, isArray, isString, isNaN } from "lodash-es";
|
||
|
import { parseRpx } from "/@/cool/utils";
|
||
|
import { useStyle } from "../../hooks";
|
||
|
|
||
|
export default defineComponent({
|
||
|
name: "cl-image",
|
||
|
|
||
|
props: {
|
||
|
// 图片地址
|
||
|
src: String,
|
||
|
// 图片裁剪、缩放的模式
|
||
|
mode: {
|
||
|
type: String,
|
||
|
default: "aspectFill",
|
||
|
},
|
||
|
// 图片大小
|
||
|
size: {
|
||
|
type: [String, Number, Array],
|
||
|
default: "100%",
|
||
|
},
|
||
|
// 是否圆角
|
||
|
round: Boolean,
|
||
|
// 当前预览值
|
||
|
previewCurrent: String,
|
||
|
// 预览列表
|
||
|
previewList: Array as PropType<string[]>,
|
||
|
// 懒加载
|
||
|
lazyLoad: Boolean,
|
||
|
fadeShow: {
|
||
|
type: Boolean,
|
||
|
default: true,
|
||
|
},
|
||
|
webp: Boolean,
|
||
|
showMenuByLongpress: Boolean,
|
||
|
},
|
||
|
|
||
|
setup(props, { emit }) {
|
||
|
// 是否加载失败
|
||
|
const isError = ref(false);
|
||
|
|
||
|
// 是否加载中
|
||
|
const isLoading = ref(false);
|
||
|
|
||
|
// 样式
|
||
|
const imgStyle = computed(() => {
|
||
|
const [height, width] = size.value;
|
||
|
|
||
|
let minWidth = "0";
|
||
|
let minHeight = "0";
|
||
|
|
||
|
// 不是有效高
|
||
|
if (isNaN(parseInt(height))) {
|
||
|
minHeight = width;
|
||
|
}
|
||
|
|
||
|
// 不是有效宽
|
||
|
if (isNaN(parseInt(width))) {
|
||
|
minWidth = height;
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
height,
|
||
|
width,
|
||
|
minWidth,
|
||
|
minHeight,
|
||
|
};
|
||
|
});
|
||
|
|
||
|
// 尺寸
|
||
|
const size = computed(() => {
|
||
|
let size: any = ["100%", "100%"];
|
||
|
|
||
|
if (isString(props.size) || isNumber(props.size)) {
|
||
|
size = [props.size, props.size];
|
||
|
} else if (isArray(props.size)) {
|
||
|
size = props.size;
|
||
|
}
|
||
|
|
||
|
return size.map(parseRpx);
|
||
|
});
|
||
|
|
||
|
// 加载失败
|
||
|
function handleError(e: Event) {
|
||
|
isError.value = true;
|
||
|
isLoading.value = false;
|
||
|
emit("error", e);
|
||
|
}
|
||
|
|
||
|
// 加载成功
|
||
|
function handleLoad(e: Event) {
|
||
|
isError.value = false;
|
||
|
isLoading.value = false;
|
||
|
emit("load", e);
|
||
|
}
|
||
|
|
||
|
// 点击是否预览图片
|
||
|
function onPreview(e: Event) {
|
||
|
if (props.previewList) {
|
||
|
uni.previewImage({
|
||
|
urls: props.previewList || [],
|
||
|
current: props.previewCurrent || props.src,
|
||
|
});
|
||
|
|
||
|
e.stopPropagation();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
watch(
|
||
|
() => props.src,
|
||
|
(val) => {
|
||
|
isLoading.value = !!val;
|
||
|
isError.value = false;
|
||
|
},
|
||
|
{
|
||
|
immediate: true,
|
||
|
},
|
||
|
);
|
||
|
|
||
|
return {
|
||
|
isError,
|
||
|
isLoading,
|
||
|
size,
|
||
|
imgStyle,
|
||
|
handleError,
|
||
|
handleLoad,
|
||
|
onPreview,
|
||
|
...useStyle(),
|
||
|
};
|
||
|
},
|
||
|
});
|
||
|
</script>
|