Browse Source

feat: imagepreview 部分功能补齐 (#1412)

* feat: image新增单元测试

* feat: ellipsis添加单元测试

* feat: imagepreview 添加

* fix: popop单元测试修改

* docs: 添加版本号
yangxiaolu1993 3 years ago
parent
commit
83a7574fb3

+ 3 - 0
src/packages/__VUE/imagepreview/demo.vue

@@ -61,6 +61,9 @@ export default createDemo({
       showPreview4: false,
       imgData: [
         {
+          src: 'https://fastly.jsdelivr.net/npm/@vant/assets/apple-4.jpeg'
+        },
+        {
           src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/18629/34/3378/144318/5c263f64Ef0e2bff0/0d650e0aa2e852ee.jpg'
         },
         {

+ 6 - 0
src/packages/__VUE/imagepreview/doc.en-US.md

@@ -309,6 +309,11 @@ app.use(ImagePreview);
 | pagination-visible | Whether to show pagination    | Boolean | false |
 | pagination-color   | Pagination color    | String  | '#fff'  |
 | content-close   | Click image to exit preview    | Boolean  | false  |
+| show-index`v3.1.22`   | Whether to show index    | Boolean  | true  |
+| closeable`v3.1.22`   | Whether to show close icon    | Boolean  | false  |
+| close-icon`v3.1.22`   | Close icon name    | String  | ‘circle-close’  |
+| close-icon-position`v3.1.22`   |  Close icon position,can be set to `top-left`   | String  | ‘top-right’  |  
+| before-close`v3.1.22`  | Callback function before close   | (active: number) => boolean | Promise<`boolean`>  | -  | 
 
 
     
@@ -317,4 +322,5 @@ app.use(ImagePreview);
 |Event|Description|Arguments|
 |--|--|--|
 |close|Emitted when closing ImagePreview|-|
+|change`v3.1.22`|Emitted when current image changed|无|
     

+ 6 - 1
src/packages/__VUE/imagepreview/doc.md

@@ -309,12 +309,17 @@ app.use(ImagePreview);
 | pagination-visible | 分页指示器是否展示    | Boolean | false |
 | pagination-color   | 分页指示器选中的颜色    | String  | '#fff'  |
 | content-close   | 点击图片可以退出预览    | Boolean  | false  |
+| show-index`v3.1.22`  | 是否显示页码    | Boolean  | true  |
+| closeable`v3.1.22`  | 是否显示关闭图标    | Boolean  | false  |
+| close-icon`v3.1.22`   | 关闭图片名称或图片链接    | String  | ‘circle-close’  |
+| close-icon-position`v3.1.22`   | 关闭图标位置,可选值:top-left   | String  | ‘top-right’  |  
+| before-close`v3.1.22`  | 关闭前的回调函数,返回 false 可阻止关闭,支持返回 Promise   | (active: number) => boolean | Promise<`boolean`>  | -  | 
 
 
-    
 ### Events
 
 |字段|说明|回调参数|
 |--|--|--|
 |close|点击遮罩关闭图片预览时触发|无|
+|change`v3.1.22`|切换图片时触发| index:当前图片索引|
     

+ 21 - 2
src/packages/__VUE/imagepreview/index.scss

@@ -1,6 +1,8 @@
 .nut-imagepreview {
+  height: 100%;
+  width: 100%;
   &-swiper {
-    height: 327px;
+    height: 100%;
     width: 100vw;
     background-color: transparent;
   }
@@ -28,6 +30,13 @@
     }
   }
 
+  &-close-icon {
+    position: fixed;
+    z-index: 2002;
+    top: 50px;
+    right: 15px;
+  }
+
   .popup-bg {
     background: rgba(0, 0, 0, 0.9);
   }
@@ -45,5 +54,15 @@
   align-items: center;
 }
 .nut-imagepreview-swiper .nut-swiper-item {
-  height: 327px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+
+  .nut-video {
+    height: auto;
+    video {
+      object-fit: contain;
+    }
+  }
 }

+ 68 - 8
src/packages/__VUE/imagepreview/index.taro.vue

@@ -1,6 +1,7 @@
 <template>
   <nut-popup pop-class="custom-pop" v-model:visible="showPop" @click="onClose" style="width: 100%">
-    <view class="nut-imagepreview" @click.stop="closeOnImg" @touchstart.capture="onTouchStart">
+    <!-- @click.stop="closeOnImg" -->
+    <view class="nut-imagepreview" @touchstart.capture="onTouchStart">
       <nut-swiper
         v-if="showPop"
         :auto-play="autoplay"
@@ -17,21 +18,26 @@
           <nut-video :source="item.source" :options="item.options"></nut-video>
         </nut-swiper-item> -->
         <nut-swiper-item v-for="(item, index) in images" :key="index">
-          <img :src="item.src" class="nut-imagepreview-img" />
+          <image mode="aspectFit" :src="item.src" class="nut-imagepreview-img" />
         </nut-swiper-item>
       </nut-swiper>
     </view>
     <!-- <view class="nut-imagepreview-index"> {{ active }} / {{ images.length + videos.length }} </view> -->
-    <view class="nut-imagepreview-index"> {{ active }} / {{ images.length }} </view>
+    <view class="nut-imagepreview-index" v-if="showIndex"> {{ active }} / {{ images.length }} </view>
+    <view class="nut-imagepreview-close-icon" @click="handleCloseIcon" :style="styles" v-if="closeable"
+      ><nut-icon :name="closeIcon" color="#ffffff"></nut-icon
+    ></view>
   </nut-popup>
 </template>
 <script lang="ts">
-import { toRefs, reactive, watch, onMounted } from 'vue';
+import { toRefs, reactive, watch, onMounted, computed } from 'vue';
 import { createComponent } from '@/packages/utils/create';
 import Popup from '../popup/index.taro.vue';
 // import Video from '../video/index.vue';
 import Swiper from '../swiper/index.taro.vue';
 import SwiperItem from '../swiperitem/index.taro.vue';
+import Icon from '../icon/index.taro.vue';
+import { isPromise } from '@/packages/utils/util.ts';
 const { componentName, create } = createComponent('imagepreview');
 
 export default create({
@@ -67,14 +73,32 @@ export default create({
     autoplay: {
       type: [Number, String],
       default: 3000
-    }
+    },
+    showIndex: {
+      type: Boolean,
+      default: true
+    },
+    closeable: {
+      type: Boolean,
+      default: false
+    },
+    closeIcon: {
+      type: String,
+      default: 'circle-close'
+    },
+    closeIconPosition: {
+      type: String,
+      default: 'top-right' // top-right  top-left
+    },
+    beforeClose: Function
   },
-  emits: ['close'],
+  emits: ['close', 'change'],
   components: {
     [Popup.name]: Popup,
     // [Video.name]: Video,
     [Swiper.name]: Swiper,
-    [SwiperItem.name]: SwiperItem
+    [SwiperItem.name]: SwiperItem,
+    [Icon.name]: Icon
   },
 
   setup(props, { emit }) {
@@ -100,8 +124,19 @@ export default create({
       lastTouchEndTime: 0 // 用来辅助监听双击
     });
 
+    const styles = computed(() => {
+      let style = {};
+      if (props.closeIconPosition == 'top-right') {
+        style.right = '10px';
+      } else {
+        style.left = '10px';
+      }
+      return style;
+    });
+
     const slideChangeEnd = function (page: number) {
       state.active = page + 1;
+      emit('change', state.active);
     };
 
     const closeOnImg = () => {
@@ -112,6 +147,24 @@ export default create({
     };
 
     const onClose = () => {
+      if (props.beforeClose) {
+        const returnVal = props.beforeClose.apply(null, state.active);
+
+        if (isPromise(returnVal)) {
+          returnVal.then((value) => {
+            if (value) {
+              closeDone();
+            }
+          });
+        } else if (returnVal) {
+          closeDone();
+        }
+      } else {
+        closeDone();
+      }
+    };
+    // 执行关闭
+    const closeDone = () => {
       state.showPop = false;
       state.store.scale = 1;
       scaleNow();
@@ -236,6 +289,11 @@ export default create({
       }
     );
 
+    // 点击关闭按钮
+    const handleCloseIcon = () => {
+      onClose();
+    };
+
     onMounted(() => {
       // 初始化页码
       state.active = props.initNo;
@@ -253,7 +311,9 @@ export default create({
       onTouchMove,
       onTouchEnd,
       getDistance,
-      scaleNow
+      scaleNow,
+      styles,
+      handleCloseIcon
     };
   }
 });

+ 65 - 7
src/packages/__VUE/imagepreview/index.vue

@@ -6,6 +6,7 @@
     @click="onClose"
     style="width: 100%"
   >
+    <!-- @click.stop="closeOnImg" @touchstart.capture="onTouchStart" -->
     <view class="nut-imagepreview" @click.stop="closeOnImg" @touchstart.capture="onTouchStart">
       <nut-swiper
         v-if="showPop"
@@ -27,16 +28,21 @@
         </nut-swiper-item>
       </nut-swiper>
     </view>
-    <view class="nut-imagepreview-index"> {{ active }} / {{ images.length + videos.length }} </view>
+    <view class="nut-imagepreview-index" v-if="showIndex"> {{ active }} / {{ images.length + videos.length }} </view>
+    <view class="nut-imagepreview-close-icon" @click="handleCloseIcon" :style="styles" v-if="closeable"
+      ><nut-icon :name="closeIcon" color="#ffffff"></nut-icon
+    ></view>
   </nut-popup>
 </template>
 <script lang="ts">
-import { toRefs, reactive, watch, onMounted, ref } from 'vue';
+import { toRefs, reactive, watch, onMounted, ref, computed } from 'vue';
 import { createComponent } from '@/packages/utils/create';
 import Popup from '../popup/index.vue';
 import Video from '../video/index.vue';
 import Swiper from '../swiper/index.vue';
 import SwiperItem from '../swiperitem/index.vue';
+import Icon from '../icon/index.vue';
+import { isPromise } from '@/packages/utils/util.ts';
 const { componentName, create } = createComponent('imagepreview');
 
 export default create({
@@ -55,7 +61,7 @@ export default create({
     },
     contentClose: {
       type: Boolean,
-      default: false
+      default: true
     },
     initNo: {
       type: Number,
@@ -76,14 +82,32 @@ export default create({
     isWrapTeleport: {
       type: Boolean,
       default: false
-    }
+    },
+    showIndex: {
+      type: Boolean,
+      default: true
+    },
+    closeable: {
+      type: Boolean,
+      default: false
+    },
+    closeIcon: {
+      type: String,
+      default: 'circle-close'
+    },
+    closeIconPosition: {
+      type: String,
+      default: 'top-right' // top-right  top-left
+    },
+    beforeClose: Function
   },
-  emits: ['close'],
+  emits: ['close', 'change'],
   components: {
     [Popup.name]: Popup,
     [Video.name]: Video,
     [Swiper.name]: Swiper,
-    [SwiperItem.name]: SwiperItem
+    [SwiperItem.name]: SwiperItem,
+    [Icon.name]: Icon
   },
 
   setup(props, { emit }) {
@@ -109,8 +133,19 @@ export default create({
       lastTouchEndTime: 0 // 用来辅助监听双击
     });
 
+    const styles = computed(() => {
+      let style = {};
+      if (props.closeIconPosition == 'top-right') {
+        style.right = '10px';
+      } else {
+        style.left = '10px';
+      }
+      return style;
+    });
+
     const slideChangeEnd = function (page: number) {
       state.active = page + 1;
+      emit('change', state.active);
     };
 
     const closeOnImg = () => {
@@ -121,6 +156,24 @@ export default create({
     };
 
     const onClose = () => {
+      if (props.beforeClose) {
+        const returnVal = props.beforeClose.apply(null, state.active);
+
+        if (isPromise(returnVal)) {
+          returnVal.then((value) => {
+            if (value) {
+              closeDone();
+            }
+          });
+        } else if (returnVal) {
+          closeDone();
+        }
+      } else {
+        closeDone();
+      }
+    };
+    // 执行关闭
+    const closeDone = () => {
       state.showPop = false;
       state.store.scale = 1;
       scaleNow();
@@ -227,6 +280,10 @@ export default create({
         scaleNow();
       }
     };
+    // 点击关闭按钮
+    const handleCloseIcon = () => {
+      onClose();
+    };
 
     const init = () => {
       state.eleImg = document.querySelector('.nut-imagepreview') as any;
@@ -259,7 +316,8 @@ export default create({
       onTouchMove,
       onTouchEnd,
       getDistance,
-      scaleNow
+      scaleNow,
+      styles
     };
   }
 });

+ 1 - 1
src/packages/__VUE/overlay/doc.md

@@ -134,7 +134,7 @@ app.use(OverLay);
 | duration               | 动画时长,单位秒 | String, Number | `0.3`    |
 | overlay-class          | 自定义遮罩类名   | String         | -      |
 | overlay-style          | 自定义遮罩样式   | CSSProperties  | -      |
-| lock-scroll            | 背景是否锁定(`小程序不支持`)     | Boolean        | `true`  |
+| lock-scroll            | 背景是否锁定(`小程序不支持`)     | Boolean        | `false`  |
 | close-on-click-overlay | 是否点击遮罩关闭 | Boolean        | `true`   |
 
 ### Events

+ 1 - 1
src/packages/__VUE/overlay/index.taro.vue

@@ -28,7 +28,7 @@ const overlayProps = {
   },
   lockScroll: {
     type: Boolean,
-    default: true
+    default: false
   },
   overlayStyle: {
     type: Object as PropType<CSSProperties>

+ 1 - 1
src/packages/__VUE/popup/__tests__/popup.spec.ts

@@ -65,7 +65,7 @@ test('should lock scroll when showed', async () => {
   });
 
   await wrapper.setProps({ visible: true });
-  expect(document.body.classList.contains('nut-overflow-hidden')).toBeTruthy();
+  expect(document.body.classList.contains('nut-overflow-hidden')).toBeFalsy();
 });
 
 test('should not render overlay when overlay prop is false', () => {