225 lines
3.8 KiB
Plaintext
225 lines
3.8 KiB
Plaintext
<template>
|
|
<view
|
|
class="cl-image"
|
|
:class="[pt.className]"
|
|
:style="{
|
|
width: parseRpx(width!),
|
|
height: parseRpx(height!)
|
|
}"
|
|
>
|
|
<view
|
|
class="cl-image__error"
|
|
:class="[
|
|
{
|
|
'is-dark': isDark
|
|
},
|
|
pt.error?.className
|
|
]"
|
|
v-if="isError"
|
|
>
|
|
<slot name="error">
|
|
<cl-icon
|
|
:name="pt.error?.name ?? 'close-line'"
|
|
:size="pt.error?.size ?? 40"
|
|
:pt="{
|
|
className: parseClass(['!text-surface-400', pt.error?.className])
|
|
}"
|
|
></cl-icon>
|
|
</slot>
|
|
</view>
|
|
|
|
<view
|
|
class="cl-image__loading"
|
|
:class="[
|
|
{
|
|
'is-dark': isDark
|
|
},
|
|
pt.loading?.className
|
|
]"
|
|
v-else-if="isLoading && showLoading"
|
|
>
|
|
<slot name="loading">
|
|
<cl-loading
|
|
:loading="true"
|
|
:pt="{ icon: { className: '!text-surface-400' } }"
|
|
></cl-loading>
|
|
</slot>
|
|
</view>
|
|
|
|
<image
|
|
class="cl-image__inner"
|
|
:class="[pt.inner?.className]"
|
|
:src="src"
|
|
:mode="mode"
|
|
:lazy-load="lazyLoad"
|
|
:webp="webp"
|
|
:show-menu-by-longpress="showMenuByLongpress"
|
|
@load="onLoad"
|
|
@error="onError"
|
|
@tap="onTap"
|
|
/>
|
|
|
|
<slot></slot>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, ref, type PropType } from "vue";
|
|
import type { PassThroughProps } from "../../types";
|
|
import { isDark, isEmpty, parseClass, parsePt, parseRpx } from "@/cool";
|
|
import type { ClIconProps } from "../cl-icon/props";
|
|
|
|
defineOptions({
|
|
name: "cl-image"
|
|
});
|
|
|
|
const props = defineProps({
|
|
// 透传样式
|
|
pt: {
|
|
type: Object,
|
|
default: () => ({})
|
|
},
|
|
// 图片源
|
|
src: {
|
|
type: String,
|
|
default: ""
|
|
},
|
|
// 图片裁剪、缩放的模式
|
|
mode: {
|
|
type: String as PropType<
|
|
| "scaleToFill"
|
|
| "aspectFit"
|
|
| "aspectFill"
|
|
| "widthFix"
|
|
| "heightFix"
|
|
| "top"
|
|
| "bottom"
|
|
| "center"
|
|
| "left"
|
|
| "right"
|
|
| "top left"
|
|
| "top right"
|
|
| "bottom left"
|
|
| "bottom right"
|
|
>,
|
|
default: "aspectFill"
|
|
},
|
|
// 是否显示边框
|
|
border: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 是否预览
|
|
preview: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 预览图片列表
|
|
previewList: {
|
|
type: Array as PropType<string[]>,
|
|
default: () => []
|
|
},
|
|
// 图片高度
|
|
height: {
|
|
type: [String, Number] as PropType<string | number>,
|
|
default: 120
|
|
},
|
|
// 图片宽度
|
|
width: {
|
|
type: [String, Number] as PropType<string | number>,
|
|
default: 120
|
|
},
|
|
// 是否显示加载状态
|
|
showLoading: {
|
|
type: Boolean,
|
|
default: true
|
|
},
|
|
// 是否懒加载
|
|
lazyLoad: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 图片显示动画效果
|
|
fadeShow: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 是否解码webp格式
|
|
webp: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 是否长按显示菜单
|
|
showMenuByLongpress: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
});
|
|
|
|
// 事件定义
|
|
const emit = defineEmits(["load", "error"]);
|
|
|
|
// 透传样式类型
|
|
type PassThrough = {
|
|
className?: string;
|
|
inner?: PassThroughProps;
|
|
error?: ClIconProps;
|
|
loading?: PassThroughProps;
|
|
};
|
|
|
|
// 解析透传样式
|
|
const pt = computed(() => parsePt<PassThrough>(props.pt));
|
|
|
|
// 加载状态
|
|
const isLoading = ref(true);
|
|
|
|
// 加载失败状态
|
|
const isError = ref(false);
|
|
|
|
// 图片加载成功
|
|
function onLoad(e: UniEvent) {
|
|
isLoading.value = false;
|
|
isError.value = false;
|
|
emit("load", e);
|
|
}
|
|
|
|
// 图片加载失败
|
|
function onError(e: UniEvent) {
|
|
isLoading.value = false;
|
|
isError.value = true;
|
|
emit("error", e);
|
|
}
|
|
|
|
// 图片点击
|
|
function onTap() {
|
|
if (props.preview) {
|
|
const urls = isEmpty(props.previewList) ? [props.src] : props.previewList;
|
|
|
|
uni.previewImage({
|
|
urls,
|
|
current: props.src
|
|
});
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.cl-image {
|
|
@apply relative flex flex-row items-center justify-center;
|
|
|
|
&__inner {
|
|
@apply w-full h-full rounded-xl;
|
|
}
|
|
|
|
&__loading,
|
|
&__error {
|
|
@apply absolute h-full w-full bg-surface-200 rounded-xl;
|
|
@apply flex flex-col items-center justify-center;
|
|
|
|
&.is-dark {
|
|
@apply bg-surface-700;
|
|
}
|
|
}
|
|
}
|
|
</style>
|