| <template> | 
|   <div class="el-image"> | 
|     <slot v-if="loading" name="placeholder"> | 
|       <div class="el-image__placeholder"></div> | 
|     </slot> | 
|     <slot v-else-if="error" name="error"> | 
|       <div class="el-image__error">{{ t('el.image.error') }}</div> | 
|     </slot> | 
|     <img | 
|       v-else | 
|       class="el-image__inner" | 
|       v-bind="$attrs" | 
|       v-on="$listeners" | 
|       @click="clickHandler" | 
|       :src="src" | 
|       :style="imageStyle" | 
|       :class="{ 'el-image__inner--center': alignCenter, 'el-image__preview': preview }"> | 
|     <template v-if="preview"> | 
|       <image-viewer :z-index="zIndex" :initial-index="imageIndex" v-if="showViewer" :on-close="closeViewer" :url-list="previewSrcList"/> | 
|     </template> | 
|   </div> | 
| </template> | 
|   | 
| <script> | 
|   import ImageViewer from './image-viewer'; | 
|   import Locale from 'element-ui/src/mixins/locale'; | 
|   import { on, off, getScrollContainer, isInContainer } from 'element-ui/src/utils/dom'; | 
|   import { isString, isHtmlElement } from 'element-ui/src/utils/types'; | 
|   import throttle from 'throttle-debounce/throttle'; | 
|   | 
|   const isSupportObjectFit = () => document.documentElement.style.objectFit !== undefined; | 
|   | 
|   const ObjectFit = { | 
|     NONE: 'none', | 
|     CONTAIN: 'contain', | 
|     COVER: 'cover', | 
|     FILL: 'fill', | 
|     SCALE_DOWN: 'scale-down' | 
|   }; | 
|   | 
|   let prevOverflow = ''; | 
|   | 
|   export default { | 
|     name: 'ElImage', | 
|   | 
|     mixins: [Locale], | 
|     inheritAttrs: false, | 
|   | 
|     components: { | 
|       ImageViewer | 
|     }, | 
|   | 
|     props: { | 
|       src: String, | 
|       fit: String, | 
|       lazy: Boolean, | 
|       scrollContainer: {}, | 
|       previewSrcList: { | 
|         type: Array, | 
|         default: () => [] | 
|       }, | 
|       zIndex: { | 
|         type: Number, | 
|         default: 2000 | 
|       } | 
|     }, | 
|   | 
|     data() { | 
|       return { | 
|         loading: true, | 
|         error: false, | 
|         show: !this.lazy, | 
|         imageWidth: 0, | 
|         imageHeight: 0, | 
|         showViewer: false | 
|       }; | 
|     }, | 
|   | 
|     computed: { | 
|       imageStyle() { | 
|         const { fit } = this; | 
|         if (!this.$isServer && fit) { | 
|           return isSupportObjectFit() | 
|             ? { 'object-fit': fit } | 
|             : this.getImageStyle(fit); | 
|         } | 
|         return {}; | 
|       }, | 
|       alignCenter() { | 
|         return !this.$isServer && !isSupportObjectFit() && this.fit !== ObjectFit.FILL; | 
|       }, | 
|       preview() { | 
|         const { previewSrcList } = this; | 
|         return Array.isArray(previewSrcList) && previewSrcList.length > 0; | 
|       }, | 
|       imageIndex() { | 
|         let previewIndex = 0; | 
|         const srcIndex = this.previewSrcList.indexOf(this.src); | 
|         if (srcIndex >= 0) { | 
|           previewIndex = srcIndex; | 
|         } | 
|         return previewIndex; | 
|       } | 
|     }, | 
|   | 
|     watch: { | 
|       src(val) { | 
|         this.show && this.loadImage(); | 
|       }, | 
|       show(val) { | 
|         val && this.loadImage(); | 
|       } | 
|     }, | 
|   | 
|     mounted() { | 
|       if (this.lazy) { | 
|         this.addLazyLoadListener(); | 
|       } else { | 
|         this.loadImage(); | 
|       } | 
|     }, | 
|   | 
|     beforeDestroy() { | 
|       this.lazy && this.removeLazyLoadListener(); | 
|     }, | 
|   | 
|     methods: { | 
|       loadImage() { | 
|         if (this.$isServer) return; | 
|   | 
|         // reset status | 
|         this.loading = true; | 
|         this.error = false; | 
|   | 
|         const img = new Image(); | 
|         img.onload = e => this.handleLoad(e, img); | 
|         img.onerror = this.handleError.bind(this); | 
|   | 
|         // bind html attrs | 
|         // so it can behave consistently | 
|         Object.keys(this.$attrs) | 
|           .forEach((key) => { | 
|             const value = this.$attrs[key]; | 
|             img.setAttribute(key, value); | 
|           }); | 
|         img.src = this.src; | 
|       }, | 
|       handleLoad(e, img) { | 
|         this.imageWidth = img.width; | 
|         this.imageHeight = img.height; | 
|         this.loading = false; | 
|         this.error = false; | 
|       }, | 
|       handleError(e) { | 
|         this.loading = false; | 
|         this.error = true; | 
|         this.$emit('error', e); | 
|       }, | 
|       handleLazyLoad() { | 
|         if (isInContainer(this.$el, this._scrollContainer)) { | 
|           this.show = true; | 
|           this.removeLazyLoadListener(); | 
|         } | 
|       }, | 
|       addLazyLoadListener() { | 
|         if (this.$isServer) return; | 
|   | 
|         const { scrollContainer } = this; | 
|         let _scrollContainer = null; | 
|   | 
|         if (isHtmlElement(scrollContainer)) { | 
|           _scrollContainer = scrollContainer; | 
|         } else if (isString(scrollContainer)) { | 
|           _scrollContainer = document.querySelector(scrollContainer); | 
|         } else { | 
|           _scrollContainer = getScrollContainer(this.$el); | 
|         } | 
|   | 
|         if (_scrollContainer) { | 
|           this._scrollContainer = _scrollContainer; | 
|           this._lazyLoadHandler = throttle(200, this.handleLazyLoad); | 
|           on(_scrollContainer, 'scroll', this._lazyLoadHandler); | 
|           this.handleLazyLoad(); | 
|         } | 
|       }, | 
|       removeLazyLoadListener() { | 
|         const { _scrollContainer, _lazyLoadHandler } = this; | 
|   | 
|         if (this.$isServer || !_scrollContainer || !_lazyLoadHandler) return; | 
|   | 
|         off(_scrollContainer, 'scroll', _lazyLoadHandler); | 
|         this._scrollContainer = null; | 
|         this._lazyLoadHandler = null; | 
|       }, | 
|       /** | 
|        * simulate object-fit behavior to compatible with IE11 and other browsers which not support object-fit | 
|        */ | 
|       getImageStyle(fit) { | 
|         const { imageWidth, imageHeight } = this; | 
|         const { | 
|           clientWidth: containerWidth, | 
|           clientHeight: containerHeight | 
|         } = this.$el; | 
|   | 
|         if (!imageWidth || !imageHeight || !containerWidth || !containerHeight) return {}; | 
|   | 
|         const imageAspectRatio = imageWidth / imageHeight; | 
|         const containerAspectRatio = containerWidth / containerHeight; | 
|   | 
|         if (fit === ObjectFit.SCALE_DOWN) { | 
|           const isSmaller = imageWidth < containerWidth && imageHeight < containerHeight; | 
|           fit = isSmaller ? ObjectFit.NONE : ObjectFit.CONTAIN; | 
|         } | 
|   | 
|         switch (fit) { | 
|           case ObjectFit.NONE: | 
|             return { width: 'auto', height: 'auto' }; | 
|           case ObjectFit.CONTAIN: | 
|             return (imageAspectRatio < containerAspectRatio) ? { width: 'auto' } : { height: 'auto' }; | 
|           case ObjectFit.COVER: | 
|             return (imageAspectRatio < containerAspectRatio) ? { height: 'auto' } : { width: 'auto' }; | 
|           default: | 
|             return {}; | 
|         } | 
|       }, | 
|       clickHandler() { | 
|         // don't show viewer when preview is false | 
|         if (!this.preview) { | 
|           return; | 
|         } | 
|         // prevent body scroll | 
|         prevOverflow = document.body.style.overflow; | 
|         document.body.style.overflow = 'hidden'; | 
|         this.showViewer = true; | 
|       }, | 
|       closeViewer() { | 
|         document.body.style.overflow = prevOverflow; | 
|         this.showViewer = false; | 
|       } | 
|     } | 
|   }; | 
| </script> |