| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- <template>
- <view
- class="nana-image-wrapper"
- :style="style"
- @click="handleClick"
- >
- <image
- :style="{
- width: style.width,
- height: style.height,
- }"
- :mode="($attrs.mode as any)"
- :lazyLoad="$attrs.lazyLoad"
- :fadeShow="$attrs.fadeShow"
- :webp="$attrs.webp"
- :show-menu-by-longpress="$attrs.showMenuByLongpress"
- :draggable="$attrs.draggable"
- :src="isErrorState ? failedImage : (src || defaultImage)"
- @loadstart="isLoadState = true"
- @load="isLoadState = false"
- @error="isErrorState = true; isLoadState = false"
- />
- <view v-if="showFailed && isErrorState" class="inner-view error">
- <!-- todo: failed -->
- <Text color="second" :text="src ? '暂无图片' : '加载失败'" />
- </view>
- <view v-if="showLoading && isLoadState" class="inner-view loading">
- <ActivityIndicator
- :color="themeContext.resolveThemeColor(loadingColor)"
- :size="themeContext.resolveThemeSize(loadingSize)"
- />
- </view>
- </view>
- </template>
- <script setup lang="ts">
- import { computed, onMounted, ref, watch } from 'vue';
- import { propGetThemeVar, useTheme } from '../theme/ThemeDefine';
- import ActivityIndicator from './ActivityIndicator.vue';
- import Text from './Text.vue';
- export interface ImageProps {
- /**
- * 图片地址
- */
- src?: string,
- /**
- * 加载失败图片地址
- */
- failedImage?: string,
- /**
- * 为空时图片地址
- */
- defaultImage?: string,
- /**
- * 是否显示加载中提示,默认是
- */
- showLoading?: boolean,
- /**
- * 是否显示加载失败提示,默认是
- */
- showFailed?: boolean,
- /**
- * 是否显示灰色占位,默认是
- */
- showGrey?: boolean,
- width?: string|number,
- height?: string|number,
- /**
- * 是否可以点击预览图片
- */
- clickPreview?: boolean,
- /**
- * 初始加载中状态
- */
- loading?: boolean,
- /**
- * 加载中圆圈颜色
- */
- loadingColor?: string,
- /**
- * 加载中圆圈颜色
- */
- loadingSize?: string|number,
- /**
- * 指定图片是否可以点击,默认否
- */
- touchable?: boolean,
- /**
- * 图片是否有圆角
- */
- round?: boolean,
- /**
- * 当round为true的圆角大小,默认是50%
- */
- radius?: string|number,
- /**
- * 内部样式
- */
- innerStyle?: object;
- }
- defineOptions({
- options: {
- virtualHost: true
- }
- })
- const props = withDefaults(defineProps<ImageProps>(), {
- src: '',
- failedImage: '',
- defaultImage: '',
- showLoading: true,
- showFailed: true,
- showGrey: () => propGetThemeVar('ImageShowGrey', false),
- loading: false,
- loadingColor: () => propGetThemeVar('ImageLoadingColor', 'border.default'),
- loadingSize: () => propGetThemeVar('ImageLoadingSize', 50),
- touchable: false,
- round: () => propGetThemeVar('ImageRound', false),
- radius: () => propGetThemeVar('ImageRadius', '50%'),
- })
- const emit = defineEmits([ 'click' ]);
- const isErrorState = ref(false);
- const isLoadState = ref(true);
- const themeContext = useTheme();
- const style = computed(() => {
- const o : Record<string, any> = {
- borderRadius: props.round ? themeContext.resolveThemeSize(props.radius) : '',
- backgroundColor: isErrorState.value || props.showGrey ? themeContext.resolveThemeColor('background.imageBox') : 'transparent',
- overflow: 'hidden',
- width: themeContext.resolveThemeSize(props.width),
- height: themeContext.resolveThemeSize(props.height),
- ...props.innerStyle,
- }
- return o;
- });
- function handleClick() {
- if (props.clickPreview) {
- uni.previewImage({
- urls: [ props.src ],
- })
- }
- emit('click');
- }
- function loadSrcState() {
- if (props.src) {
- isErrorState.value = false;
- isLoadState.value = true;
- } else {
- isErrorState.value = true;
- isLoadState.value = false;
- }
- }
- watch(() => props.src, (newVal, oldVal) => {
- if (newVal) {
- isErrorState.value = true;
- isLoadState.value = false;
- } else
- isErrorState.value = false;
- })
- onMounted(() => {
- loadSrcState();
- })
- </script>
- <style lang="scss">
- .nana-image-wrapper {
- position: relative;
- flex-shrink: 0;
- .inner-view {
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- }
- </style>
|