浏览代码

feat: imagePreview 图片预览 (#695)

* feat: imagePreview 图片预览

* feat: imagePreview 适配 taro
JackieScorpio 4 年之前
父节点
当前提交
32814bc456

+ 11 - 0
src/config.json

@@ -223,6 +223,17 @@
           "exportEmpty": true,
           "desc": "布局组件Row",
           "author": "undo"
+        },
+        {
+          "version": "3.0.0",
+          "name": "ImagePreview",
+          "type": "component",
+          "cName": "图片预览",
+          "desc": "图片预览",
+          "sort": 4,
+          "show": true,
+          "taro": true,
+          "author": "zongyue3"
         }
       ]
     },

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

@@ -0,0 +1,67 @@
+<template>
+  <div class="demo">
+    <h2>基础用法</h2>
+    <nut-imagepreview
+      :show="showPreview"
+      :images="dataImgItem"
+      @close="hideFn"
+    />
+    <nut-cell
+      isLink
+      title="展示图片预览"
+      :showIcon="true"
+      @click="showFn"
+    ></nut-cell>
+  </div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs } from 'vue';
+import { createComponent } from '../../utils/create';
+const { createDemo } = createComponent('imagepreview');
+export default createDemo({
+  props: {},
+  setup() {
+    const resData = reactive({
+      showPreview: false,
+      dataImgItem: [
+        {
+          imgSrc:
+            '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/18629/34/3378/144318/5c263f64Ef0e2bff0/0d650e0aa2e852ee.jpg'
+        },
+        {
+          imgSrc:
+            '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/26597/30/4870/174583/5c35c5d2Ed55eedc6/50e27870c25e7a82.png'
+        },
+        {
+          imgSrc:
+            '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/9542/17/12873/201687/5c3c4362Ea9eb757d/60026b40a9d60d85.jpg'
+        },
+        {
+          imgSrc:
+            '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/30042/36/427/82951/5c3bfdabE3faf2f66/9adca782661c988c.jpg'
+        }
+      ]
+    });
+
+    const showFn = () => {
+      resData.showPreview = true;
+    };
+
+    const hideFn = () => {
+      resData.showPreview = false;
+    };
+
+    return {
+      ...toRefs(resData),
+      showFn,
+      hideFn
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.demo {
+}
+</style>

+ 72 - 0
src/packages/__VUE/imagepreview/doc.md

@@ -0,0 +1,72 @@
+# imagepreview组件
+
+### 介绍
+    
+图片预览
+
+### 安装
+
+```javascript
+import { createApp } from 'vue';
+import { ImagePreview } from '@nutui/nutui';
+
+const app = createApp();
+app.use(ImagePreview);
+```
+
+### 基础用法
+
+```html
+<nut-imagepreview :show="showPreview" :images="dataImgItem" @close="hideFn" />
+<nut-cell isLink title="展示图片预览" :showIcon="true" @click="showFn"></nut-cell>
+```
+
+```javascript
+setup() {
+    const resData = reactive({
+        showPreview: false,
+        dataImgItem: [
+            {
+            imgSrc: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/18629/34/3378/144318/5c263f64Ef0e2bff0/0d650e0aa2e852ee.jpg',
+            },
+            {
+            imgSrc: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/26597/30/4870/174583/5c35c5d2Ed55eedc6/50e27870c25e7a82.png',
+            },
+            {
+            imgSrc: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/9542/17/12873/201687/5c3c4362Ea9eb757d/60026b40a9d60d85.jpg',
+            },
+            {
+            imgSrc: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/30042/36/427/82951/5c3bfdabE3faf2f66/9adca782661c988c.jpg',
+            },
+        ]
+    });
+
+    const showFn = () => {
+        resData.showPreview = true;
+    }
+
+    const hideFn = () => {
+        resData.showPreview = false;
+    }
+    
+    return {
+        ...toRefs(resData),
+        showFn,
+        hideFn
+    };
+},
+```
+    
+### Props
+
+| 字段 | 说明 | 类型 | 默认值
+|----- | ----- | ----- | ----- 
+| show | 是否展示预览图片 | Boolean | false
+| images | 预览图片数组 | Array<`String`> | []
+    
+### Events
+
+|字段|说明|回调参数|
+|--|--|--|
+|close|点击遮罩关闭图片预览时触发|无|
+    

+ 48 - 0
src/packages/__VUE/imagepreview/index.scss

@@ -0,0 +1,48 @@
+.nut-imagepreview {
+  &-swiper {
+    height: 327px;
+    width: 100vw;
+    background-color: transparent;
+  }
+
+  &-img {
+    width: 100%;
+    height: 100%;
+    object-fit: contain;
+  }
+
+  &-index {
+    position: fixed;
+    top: 50px;
+    text-align: center;
+    left: 0;
+    right: 0;
+    background: transparent;
+    color: #fff;
+
+    .arrow {
+      position: absolute;
+      left: 15px;
+      transform: rotateZ(180deg);
+    }
+  }
+
+  .popup-bg {
+    background: rgba(0, 0, 0, 0.9);
+  }
+
+  .popup-box {
+    height: 100%;
+    overflow: visible;
+    background-color: transparent;
+  }
+}
+.popup-center {
+  height: 100%;
+  background: transparent !important;
+  display: flex;
+  align-items: center;
+}
+.nut-swiper-item {
+  height: 327px !important;
+}

+ 72 - 0
src/packages/__VUE/imagepreview/index.taro.vue

@@ -0,0 +1,72 @@
+<template>
+  <view class="nut-imagepreview">
+    <nut-popup v-model:visible="showPop" @click="closePop">
+      <swiper
+        v-if="showPop"
+        :autoplay="true"
+        :interval="3000"
+        class="nut-imagepreview-swiper"
+        :circular="true"
+        :onChange="slideChangeEnd"
+      >
+        <swiper-item v-for="(item, index) in images" :key="index">
+          <img :src="item.imgSrc" class="nut-imagepreview-img" />
+        </swiper-item>
+      </swiper>
+      <view class="nut-imagepreview-index">
+        {{ active }} / {{ images.length }}
+      </view>
+    </nut-popup>
+  </view>
+</template>
+<script lang="ts">
+import { toRefs, reactive, watch } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('imagepreview');
+
+export default create({
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    images: {
+      type: Array,
+      default: () => []
+    }
+  },
+  components: {},
+
+  setup(props, { emit }) {
+    const { value, images } = toRefs(props);
+
+    const state = reactive({
+      showPop: value,
+      active: 1
+    });
+
+    const slideChangeEnd = function (page: any) {
+      state.active = page.detail.current + 1;
+    };
+
+    const closePop = function () {
+      state.showPop = false;
+      state.active = 1;
+      emit('close');
+    };
+
+    watch(
+      () => props.show,
+      (val) => {
+        state.showPop = val;
+      }
+    );
+
+    return {
+      ...toRefs(state),
+      slideChangeEnd,
+      closePop
+    };
+  }
+});
+</script>

+ 72 - 0
src/packages/__VUE/imagepreview/index.vue

@@ -0,0 +1,72 @@
+<template>
+  <view class="nut-imagepreview">
+    <nut-popup v-model:visible="showPop" @click="closePop">
+      <nut-swiper
+        v-if="showPop"
+        :auto-play="3000"
+        class="nut-imagepreview-swiper"
+        :loop="true"
+        direction="horizontal"
+        @change="slideChangeEnd"
+      >
+        <nut-swiper-item v-for="(item, index) in images" :key="index">
+          <img :src="item.imgSrc" class="nut-imagepreview-img" />
+        </nut-swiper-item>
+      </nut-swiper>
+      <view class="nut-imagepreview-index">
+        {{ active }} / {{ images.length }}
+      </view>
+    </nut-popup>
+  </view>
+</template>
+<script lang="ts">
+import { toRefs, reactive, watch } from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('imagepreview');
+
+export default create({
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    images: {
+      type: Array,
+      default: () => []
+    }
+  },
+  components: {},
+
+  setup(props, { emit }) {
+    const { value, images } = toRefs(props);
+
+    const state = reactive({
+      showPop: value,
+      active: 1
+    });
+
+    const slideChangeEnd = function (page: number) {
+      state.active = page + 1;
+    };
+
+    const closePop = function () {
+      state.showPop = false;
+      state.active = 1;
+      emit('close');
+    };
+
+    watch(
+      () => props.show,
+      (val) => {
+        state.showPop = val;
+      }
+    );
+
+    return {
+      ...toRefs(state),
+      slideChangeEnd,
+      closePop
+    };
+  }
+});
+</script>

+ 1 - 1
src/sites/mobile-taro/vue/src/app.config.ts

@@ -3,7 +3,7 @@ export default {
   subpackages: [
     {
       root: 'layout',
-      pages: ['pages/layout/index']
+      pages: ['pages/layout/index', 'pages/imagepreview/index']
     },
     {
       root: 'feedback',

+ 3 - 0
src/sites/mobile-taro/vue/src/layout/pages/imagepreview/index.config.ts

@@ -0,0 +1,3 @@
+export default {
+  navigationBarTitleText: 'ImagePreview'
+};

+ 65 - 0
src/sites/mobile-taro/vue/src/layout/pages/imagepreview/index.vue

@@ -0,0 +1,65 @@
+<template>
+  <div class="demo">
+    <h2>基础用法</h2>
+    <nut-imagepreview
+      :show="showPreview"
+      :images="dataImgItem"
+      @close="hideFn"
+    />
+    <nut-cell
+      isLink
+      title="展示图片预览"
+      :showIcon="true"
+      @click="showFn"
+    ></nut-cell>
+  </div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs } from 'vue';
+export default {
+  props: {},
+  setup() {
+    const resData = reactive({
+      showPreview: false,
+      dataImgItem: [
+        {
+          imgSrc:
+            '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/18629/34/3378/144318/5c263f64Ef0e2bff0/0d650e0aa2e852ee.jpg'
+        },
+        {
+          imgSrc:
+            '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/26597/30/4870/174583/5c35c5d2Ed55eedc6/50e27870c25e7a82.png'
+        },
+        {
+          imgSrc:
+            '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/9542/17/12873/201687/5c3c4362Ea9eb757d/60026b40a9d60d85.jpg'
+        },
+        {
+          imgSrc:
+            '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/30042/36/427/82951/5c3bfdabE3faf2f66/9adca782661c988c.jpg'
+        }
+      ]
+    });
+
+    const showFn = () => {
+      resData.showPreview = true;
+    };
+
+    const hideFn = () => {
+      resData.showPreview = false;
+    };
+
+    return {
+      ...toRefs(resData),
+      showFn,
+      hideFn
+    };
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.demo {
+}
+</style>