| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- <template>
- <view
- class="cl-image"
- :class="[pt.className]"
- :style="{
- width: getUnit(width),
- height: getUnit(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 ?? 20"
- :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"></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, getUnit } 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: 60
- },
- // 图片宽度
- width: {
- type: [String, Number] as PropType<string | number>,
- default: 60
- },
- // 是否显示加载状态
- 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>
|