Browse Source

docs(Divider/Menu/Pagination/ImagePreview): 国际化、Divider/Menu部分缺失能力 (#1328)

* feat: add vertical divider

* fix: fix fixed menu style

* feat: internationalize Menu、Pagination、Divider and ImagePreview

* feat: 菜单组件增加点击遮照关闭

* feat: 增加props的版本号;增加菜单组件中data的国际化

* feat: add four variables for Divider in jdt
yangjinjun3 3 years ago
parent
commit
24e4ebd528

+ 1 - 1
src/packages/__VUE/divider/__tests__/__snapshots__/divider.spec.ts.snap

@@ -1,3 +1,3 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`slot: html should contain customer text 1`] = `"<div class=\\"nut-divider nut-divider-center nut-divider-hairline\\">customer text</div>"`;
+exports[`slot: html should contain custom text 1`] = `"<div class=\\"nut-divider nut-divider-center nut-divider-hairline\\">custom text</div>"`;

+ 16 - 6
src/packages/__VUE/divider/__tests__/divider.spec.ts

@@ -1,13 +1,13 @@
 import { mount } from '@vue/test-utils';
 import Divider from '../index.vue';
 
-test('slot: html should contain customer text', () => {
+test('slot: html should contain custom text', () => {
   const wrapper = mount(Divider, {
     slots: {
-      default: 'customer text'
+      default: 'custom text'
     }
   });
-  expect(wrapper.html()).toContain('customer text');
+  expect(wrapper.html()).toContain('custom text');
   expect(wrapper.html()).toMatchSnapshot();
 });
 
@@ -17,7 +17,7 @@ test('content-position props: classes should contain nut-divider-left', () => {
       contentPosition: 'left'
     },
     slots: {
-      default: 'customer text'
+      default: 'custom text'
     }
   });
   const divider: any = wrapper.find('.nut-divider');
@@ -30,7 +30,7 @@ test('dashed props: classes should contain nut-divider-dashed', () => {
       dashed: true
     },
     slots: {
-      default: 'customer text'
+      default: 'custom text'
     }
   });
 
@@ -38,7 +38,7 @@ test('dashed props: classes should contain nut-divider-dashed', () => {
   expect(divider.classes()).toContain('nut-divider-dashed');
 });
 
-test('customer style: element color should be rgb(25, 137, 250) etc', () => {
+test('custom style: element color should be rgb(25, 137, 250) etc', () => {
   const wrapper = mount(Divider, {
     props: {
       style: { color: '#1989fa', borderColor: '#1989fa', padding: '0 16px' }
@@ -58,3 +58,13 @@ test('hairline props: classes should contain nut-divider-hairline default, after
   await wrapper.setProps({ hairline: false });
   expect(wrapper.classes('nut-divider-hairline')).toBe(false);
 });
+
+test('direction props: classes should contain nut-divider-vertical', () => {
+  const wrapper = mount(Divider, {
+    props: {
+      direction: 'vertical'
+    }
+  });
+  const divider: any = wrapper.find('.nut-divider');
+  expect(divider.classes()).toContain('nut-divider-vertical');
+});

+ 43 - 12
src/packages/__VUE/divider/demo.vue

@@ -1,26 +1,57 @@
 <template>
   <div class="demo full bg-w">
-    <h2>基础用法</h2>
+    <h2>{{ translate('basic') }}</h2>
     <nut-divider />
-    <h2>展示文本</h2>
-    <nut-divider>文本</nut-divider>
-    <h2>内容位置</h2>
-    <nut-divider content-position="left">文本</nut-divider>
-    <nut-divider content-position="right">文本</nut-divider>
-    <h2>虚线</h2>
-    <nut-divider dashed>文本</nut-divider>
-    <h2>自定义样式</h2>
-    <nut-divider :style="{ color: '#1989fa', borderColor: '#1989fa', padding: '0 16px' }">文本</nut-divider>
+    <h2>{{ translate('withText') }}</h2>
+    <nut-divider>{{ translate('text') }}</nut-divider>
+    <h2>{{ translate('contentPosition') }}</h2>
+    <nut-divider content-position="left">{{ translate('text') }}</nut-divider>
+    <nut-divider content-position="right">{{ translate('text') }}</nut-divider>
+    <h2>{{ translate('dashed') }}</h2>
+    <nut-divider dashed>{{ translate('text') }}</nut-divider>
+    <h2>{{ translate('customStyle') }}</h2>
+    <nut-divider :style="{ color: '#1989fa', borderColor: '#1989fa', padding: '0 16px' }">{{
+      translate('text')
+    }}</nut-divider>
+    <h2>{{ translate('verticalDivider') }}</h2>
+    <div :style="{ fontSize: '14px' }">
+      {{ translate('text') }}
+      <nut-divider direction="vertical" />
+      <a href="#">{{ translate('text') }}</a>
+      <nut-divider direction="vertical" />
+      <a href="#">{{ translate('text') }}</a>
+    </div>
   </div>
 </template>
 
 <script lang="ts">
 import { createComponent } from '@/packages/utils/create';
-const { createDemo } = createComponent('divider');
+const { createDemo, translate } = createComponent('divider');
+import { useTranslate } from '@/sites/assets/util/useTranslate';
+useTranslate({
+  'zh-CN': {
+    basic: '基本用法',
+    withText: '展示文本',
+    contentPosition: '内容位置',
+    dashed: '虚线',
+    customStyle: '自定义样式',
+    verticalDivider: '垂直分割线',
+    text: '文本'
+  },
+  'en-US': {
+    basic: 'Basic Usage',
+    withText: 'With Text',
+    contentPosition: 'Content Position',
+    dashed: 'Dashed',
+    customStyle: 'Custom Style',
+    verticalDivider: 'Vertical Divider',
+    text: 'text'
+  }
+});
 export default createDemo({
   props: {},
   setup() {
-    return {};
+    return { translate };
   }
 });
 </script>

+ 140 - 0
src/packages/__VUE/divider/doc.en-US.md

@@ -0,0 +1,140 @@
+# Divider
+
+### Intro
+    
+Separate content into multiple areas.
+
+### Install
+``` javascript
+import { createApp } from 'vue';
+// vue
+import { Divider } from '@nutui/nutui';
+// taro
+import { Divider } from '@nutui/nutui-taro';
+
+const app = createApp();
+app.use(Divider);
+```
+
+
+### Basic Usage
+
+Default render one horizontal divider line.
+
+:::demo
+
+``` html
+<template>
+    <nut-cell>
+        <nut-divider />
+    </nut-cell>
+</template>
+```
+
+:::
+
+### With Text
+
+Insert text into divider with default slot.
+
+:::demo
+
+``` html
+<template>
+    <nut-cell>
+        <nut-divider>text</nut-divider>
+    </nut-cell>
+</template>
+```
+
+:::
+
+### Content Position
+
+Set Content Position with content-position attribute.
+
+:::demo
+
+``` html
+<template>
+    <nut-cell>
+        <nut-divider content-position="left">text</nut-divider>
+    </nut-cell>
+    <nut-cell>
+        <nut-divider content-position="right">text</nut-divider>
+    </nut-cell>
+</template>
+```
+
+:::
+
+### Dashed
+
+Render dashed divider line with dashed attribute.
+
+
+:::demo
+
+``` html
+<template>
+    <nut-cell>
+        <nut-divider dashed>text</nut-divider>
+    </nut-cell>
+</template>
+```
+
+:::
+
+### Custom Style
+
+User can custom divider style with style attribute.
+
+:::demo
+
+``` html
+<template>
+    <nut-cell>
+        <nut-divider :style="{ color: '#1989fa', borderColor: '#1989fa', padding: '0 16px' }">text</nut-divider>
+    </nut-cell>
+</template>
+```
+
+:::
+
+### Vertical Divider
+
+:::demo
+
+``` html
+<template>
+    <nut-cell>
+        <div :style="{fontSize: '14px'}">
+            text
+            <nut-divider direction="vertical" />
+            <a href="#">text</a>
+            <nut-divider direction="vertical" />
+            <a href="#">text</a>
+        </div>
+    </nut-cell>
+</template>
+```
+
+:::
+
+## API
+
+### Props
+
+| Attribute         | Description                             | Type   | Default           |
+|--------------|----------------------------------|--------|------------------|
+| dashed         | 	Whether to use dashed border             | Boolean | false                |
+| hairline        | Whether to use hairline                         | Boolean | true                |
+| content-position        | Content position, can be set to left or right                       | String | center                |
+| direction `v3.1.21`         | The direction of divider             | String | 'horizontal'                |
+
+### Slots
+
+| Name | Description           | 
+|--------|----------------|
+| default  | Default slot | 
+    

+ 22 - 1
src/packages/__VUE/divider/doc.md

@@ -37,7 +37,7 @@ app.use(Divider);
 
 ### 展示文本
 
-通过插槽可以分割线中间插入内容。
+通过插槽可以分割线中间插入内容。
 
 :::demo
 
@@ -102,6 +102,26 @@ app.use(Divider);
 
 :::
 
+### 垂直分割线
+
+:::demo
+
+``` html
+<template>
+    <nut-cell>
+        <div :style="{fontSize: '14px'}">
+            文本
+            <nut-divider direction="vertical" />
+            <a href="#">文本</a>
+            <nut-divider direction="vertical" />
+            <a href="#">文本</a>
+        </div>
+    </nut-cell>
+</template>
+```
+
+:::
+
 ## API
 
 ### Props
@@ -111,6 +131,7 @@ app.use(Divider);
 | dashed         | 是否使用虚线               | Boolean | false                |
 | hairline        | 是否使用 0.5px 线                         | Boolean | true                |
 | content-position        | 内容位置,可选值为left right                         | String | center                |
+| direction `v3.1.21`         | 水平还是垂直类型               | String | 'horizontal'                |
 
 ### Slots
 

+ 9 - 0
src/packages/__VUE/divider/index.scss

@@ -51,4 +51,13 @@
       transform: scaleY(0.5);
     }
   }
+
+  &.nut-divider-vertical {
+    position: relative;
+    top: $divider-vertical-top;
+    display: inline-block;
+    height: $divider-vertical-height;
+    border-left: 1px solid $divider-vertical-border-left;
+    margin: $divider-vertical-margin;
+  }
 }

+ 30 - 10
src/packages/__VUE/divider/index.taro.vue

@@ -1,11 +1,11 @@
 <template>
-  <view :class="classes">
+  <view :class="classes" v-if="direction === 'horizontal'">
     <slot></slot>
   </view>
+  <view :class="classes" v-else></view>
 </template>
 <script lang="ts">
-import { login } from '@tarojs/taro';
-import { computed, onMounted } from 'vue';
+import { computed } from 'vue';
 import { createComponent } from '@/packages/utils/create';
 const { componentName, create } = createComponent('divider');
 
@@ -22,6 +22,10 @@ export default create({
     hairline: {
       type: Boolean,
       default: true
+    },
+    direction: {
+      type: String,
+      default: 'horizontal'
     }
   },
   components: {},
@@ -29,14 +33,30 @@ export default create({
   setup(props, context) {
     const classes = computed(() => {
       const prefixCls = componentName;
-      return {
-        [prefixCls]: true,
-        [`${prefixCls}-center`]: context.slots.default,
-        [`${prefixCls}-left`]: props.contentPosition === 'left',
-        [`${prefixCls}-right`]: props.contentPosition === 'right',
-        [`${prefixCls}-dashed`]: props.dashed,
-        [`${prefixCls}-hairline`]: props.hairline
+
+      let defaultClassesObj = {
+        [prefixCls]: true
       };
+
+      let classesObj = {};
+
+      if (props.direction === 'horizontal') {
+        classesObj = {
+          ...defaultClassesObj,
+          [`${prefixCls}-center`]: context.slots.default,
+          [`${prefixCls}-left`]: props.contentPosition === 'left',
+          [`${prefixCls}-right`]: props.contentPosition === 'right',
+          [`${prefixCls}-dashed`]: props.dashed,
+          [`${prefixCls}-hairline`]: props.hairline
+        };
+      } else {
+        classesObj = {
+          ...defaultClassesObj,
+          [`${prefixCls}-vertical`]: props.direction === 'vertical'
+        };
+      }
+
+      return classesObj;
     });
 
     return { classes };

+ 31 - 10
src/packages/__VUE/divider/index.vue

@@ -1,10 +1,11 @@
 <template>
-  <div :class="classes">
+  <view :class="classes" v-if="direction === 'horizontal'">
     <slot></slot>
-  </div>
+  </view>
+  <view :class="classes" v-else></view>
 </template>
 <script lang="ts">
-import { computed, onMounted } from 'vue';
+import { computed } from 'vue';
 import { createComponent } from '@/packages/utils/create';
 const { componentName, create } = createComponent('divider');
 
@@ -21,6 +22,10 @@ export default create({
     hairline: {
       type: Boolean,
       default: true
+    },
+    direction: {
+      type: String,
+      default: 'horizontal'
     }
   },
   components: {},
@@ -28,14 +33,30 @@ export default create({
   setup(props, context) {
     const classes = computed(() => {
       const prefixCls = componentName;
-      return {
-        [prefixCls]: true,
-        [`${prefixCls}-center`]: context.slots.default,
-        [`${prefixCls}-left`]: props.contentPosition === 'left',
-        [`${prefixCls}-right`]: props.contentPosition === 'right',
-        [`${prefixCls}-dashed`]: props.dashed,
-        [`${prefixCls}-hairline`]: props.hairline
+
+      let defaultClassesObj = {
+        [prefixCls]: true
       };
+
+      let classesObj = {};
+
+      if (props.direction === 'horizontal') {
+        classesObj = {
+          ...defaultClassesObj,
+          [`${prefixCls}-center`]: context.slots.default,
+          [`${prefixCls}-left`]: props.contentPosition === 'left',
+          [`${prefixCls}-right`]: props.contentPosition === 'right',
+          [`${prefixCls}-dashed`]: props.dashed,
+          [`${prefixCls}-hairline`]: props.hairline
+        };
+      } else {
+        classesObj = {
+          ...defaultClassesObj,
+          [`${prefixCls}-vertical`]: props.direction === 'vertical'
+        };
+      }
+
+      return classesObj;
     });
 
     return { classes };

+ 32 - 12
src/packages/__VUE/imagepreview/demo.vue

@@ -1,14 +1,14 @@
 <template>
   <div class="demo">
-    <h2>基础用法</h2>
+    <h2>{{ translate('basic') }}</h2>
     <nut-imagepreview :show="showPreview1" :images="imgData" @close="hideFn(1)" />
-    <nut-cell isLink title="展示图片预览" :showIcon="true" @click="showFn(1)"></nut-cell>
+    <nut-cell isLink :title="translate('showPreview')" :showIcon="true" @click="showFn(1)"></nut-cell>
 
-    <h2>设置初始页码</h2>
+    <h2>{{ translate('withInitNo') }}</h2>
     <nut-imagepreview :show="showPreview2" :images="imgData" :content-close="true" :init-no="3" @close="hideFn(2)" />
-    <nut-cell isLink title="设置初始页码的图片预览" :showIcon="true" @click="showFn(2)"></nut-cell>
+    <nut-cell isLink :title="translate('withInitNo')" :showIcon="true" @click="showFn(2)"></nut-cell>
 
-    <h2>设置轮播指示器及颜色</h2>
+    <h2>{{ translate('withPagination') }}</h2>
     <nut-imagepreview
       :show="showPreview3"
       :images="imgData"
@@ -16,22 +16,41 @@
       pagination-color="red"
       @close="hideFn(3)"
     />
-    <nut-cell isLink title="设置轮播指示器及颜色的图片预览" :showIcon="true" @click="showFn(3)"></nut-cell>
+    <nut-cell isLink :title="translate('withPagination')" :showIcon="true" @click="showFn(3)"></nut-cell>
 
-    <h2>视频、图片预览</h2>
+    <h2>{{ translate('withVideos') }}</h2>
     <nut-imagepreview :show="showPreview4" :videos="videoData" :images="imgData" @close="hideFn(4)" />
-    <nut-cell isLink title="视频、图片预览" :showIcon="true" @click="showFn(4)"></nut-cell>
+    <nut-cell isLink :title="translate('withVideos')" :showIcon="true" @click="showFn(4)"></nut-cell>
 
-    <h2>函数式调用</h2>
-    <nut-cell isLink title="函数式调用的图片预览" :showIcon="true" @click="fnShow"></nut-cell>
+    <h2>{{ translate('functionalCall') }}</h2>
+    <nut-cell isLink :title="translate('functionalCall')" :showIcon="true" @click="fnShow"></nut-cell>
   </div>
 </template>
 
 <script lang="ts">
 import { reactive, toRefs } from 'vue';
 import { createComponent } from '@/packages/utils/create';
-const { createDemo } = createComponent('imagepreview');
+const { createDemo, translate } = createComponent('imagepreview');
+import { useTranslate } from '@/sites/assets/util/useTranslate';
 import { ImagePreview } from '@/packages/nutui.vue';
+useTranslate({
+  'zh-CN': {
+    basic: '基本用法',
+    withInitNo: '设置初始页码',
+    showPreview: '展示图片预览',
+    withPagination: '设置轮播指示器及颜色',
+    withVideos: '视频、图片预览',
+    functionalCall: '函数式调用'
+  },
+  'en-US': {
+    basic: 'Basic Usage',
+    withInitNo: 'With Init No',
+    showPreview: 'Show Preview',
+    withPagination: 'With Pagination',
+    withVideos: 'With Videos',
+    functionalCall: 'Functional Call'
+  }
+});
 export default createDemo({
   props: {},
   setup() {
@@ -101,7 +120,8 @@ export default createDemo({
       ...toRefs(resData),
       showFn,
       hideFn,
-      fnShow
+      fnShow,
+      translate
     };
   }
 });

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

@@ -0,0 +1,320 @@
+# ImagePreview
+
+### Intro
+
+Support full screen preview videos and images, support functional call.
+
+### Install
+
+```javascript
+import { createApp, reactive, toRefs } from 'vue';
+
+import { ImagePreview } from '@nutui/nutui';
+
+
+const app = createApp();
+app.use(ImagePreview);
+```
+
+### Basic Usage
+:::demo
+```html
+<template>
+    <nut-imagepreview :show="showPreview" :images="imgData" @close="hideFn"/>
+    <nut-cell isLink title="Show preview" :showIcon="true" @click="showFn"></nut-cell>
+</template>
+
+<script lang="ts">
+  import { reactive, toRefs } from 'vue';
+  export default {
+    setup() {
+        const resData = reactive({
+            showPreview: false,
+            imgData: [
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/18629/34/3378/144318/5c263f64Ef0e2bff0/0d650e0aa2e852ee.jpg',
+                },
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/26597/30/4870/174583/5c35c5d2Ed55eedc6/50e27870c25e7a82.png',
+                },
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/9542/17/12873/201687/5c3c4362Ea9eb757d/60026b40a9d60d85.jpg',
+                },
+                {
+                    src: '//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>
+```
+:::
+
+### With Init No
+:::demo
+```html
+<template>
+    <nut-imagepreview :show="showPreview" :images="imgData" :content-close="true" :init-no="3" @close="hideFn"/>
+    <nut-cell isLink title="With init no" :showIcon="true" @click="showFn"></nut-cell>
+</template>
+<script lang="ts">
+  import { reactive, toRefs } from 'vue';
+  export default {
+    setup() {
+        const resData = reactive({
+            showPreview: false,
+            imgData: [
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/18629/34/3378/144318/5c263f64Ef0e2bff0/0d650e0aa2e852ee.jpg',
+                },
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/26597/30/4870/174583/5c35c5d2Ed55eedc6/50e27870c25e7a82.png',
+                },
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/9542/17/12873/201687/5c3c4362Ea9eb757d/60026b40a9d60d85.jpg',
+                },
+                {
+                    src: '//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>
+```
+:::
+
+### With Pagination
+:::demo
+```html
+<template>
+    <nut-imagepreview
+        :show="showPreview"
+        :images="imgData"
+        :pagination-visible="true"
+        pagination-color="red"
+        @close="hideFn"
+    />
+    <nut-cell isLink title="With pagination" :showIcon="true" @click="showFn"></nut-cell>
+</template>
+<script lang="ts">
+  import { reactive, toRefs } from 'vue';
+  export default {
+    setup() {
+        const resData = reactive({
+            showPreview: false,
+            imgData: [
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/18629/34/3378/144318/5c263f64Ef0e2bff0/0d650e0aa2e852ee.jpg',
+                },
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/26597/30/4870/174583/5c35c5d2Ed55eedc6/50e27870c25e7a82.png',
+                },
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/9542/17/12873/201687/5c3c4362Ea9eb757d/60026b40a9d60d85.jpg',
+                },
+                {
+                    src: '//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>
+```
+:::
+
+### With Videos
+#### Preview videos not support in taro.
+
+:::demo
+```html
+<template>
+    <nut-imagepreview
+        :show="showPreview"
+        :images="imgData"
+        :videos="videoData"      
+        @close="hideFn"
+    />
+    <nut-cell isLink title="With videos" :showIcon="true" @click="showFn"></nut-cell>
+</template>
+<script lang="ts">
+  import { reactive, toRefs } from 'vue';
+  export default {
+    setup() {
+        const resData = reactive({
+            showPreview: false,
+            imgData: [
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/18629/34/3378/144318/5c263f64Ef0e2bff0/0d650e0aa2e852ee.jpg',
+                },
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/26597/30/4870/174583/5c35c5d2Ed55eedc6/50e27870c25e7a82.png',
+                },
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/9542/17/12873/201687/5c3c4362Ea9eb757d/60026b40a9d60d85.jpg',
+                },
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/30042/36/427/82951/5c3bfdabE3faf2f66/9adca782661c988c.jpg',
+                },
+            ],
+            videoData: [
+                {
+                    source: {
+                        src: 'https://storage.jd.com/about/big-final.mp4?Expires=3730193075&AccessKey=3LoYX1dQWa6ZXzQl&Signature=ViMFjz%2BOkBxS%2FY1rjtUVqbopbJI%3D',
+                        type: 'video/mp4'
+                    },
+                    options: {
+                        muted: true,
+                        controls: true
+                    }
+                },
+                {
+                    source: {
+                        src: 'https://storage.jd.com/about/big-final.mp4?Expires=3730193075&AccessKey=3LoYX1dQWa6ZXzQl&Signature=ViMFjz%2BOkBxS%2FY1rjtUVqbopbJI%3D',
+                        type: 'video/mp4'
+                    },
+                    options: {
+                        muted: true,
+                        controls: true
+                    }
+                }
+            ],
+        });
+
+        const showFn = () => {
+            resData.showPreview = true;
+        }
+
+        const hideFn = () => {
+            resData.showPreview = false;
+        }
+        
+        return {
+            ...toRefs(resData),
+            showFn,
+            hideFn
+        };
+    }
+  };
+</script>
+```
+:::
+                
+### Functional Call
+#### Functional call not support in taro.
+
+:::demo
+```html
+<template>
+    <nut-cell isLink title="Functional call" :showIcon="true" @click="fnShow"></nut-cell>
+</template>
+
+<script lang="ts">
+  import { ImagePreview } from '@nutui/nutui';
+  import { reactive, toRefs } from 'vue';
+  export default {
+    setup() {
+        const resData = reactive({
+            imgData: [
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/18629/34/3378/144318/5c263f64Ef0e2bff0/0d650e0aa2e852ee.jpg',
+                },
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/26597/30/4870/174583/5c35c5d2Ed55eedc6/50e27870c25e7a82.png',
+                },
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/9542/17/12873/201687/5c3c4362Ea9eb757d/60026b40a9d60d85.jpg',
+                },
+                {
+                    src: '//m.360buyimg.com/mobilecms/s750x366_jfs/t1/30042/36/427/82951/5c3bfdabE3faf2f66/9adca782661c988c.jpg',
+                },
+            ]
+        });
+
+        const onClose = () => {
+        console.log('imagepreview closed');
+        };
+
+        const fnShow = () => {
+        ImagePreview({
+            show: true,
+            images: resData.imgData,
+            onClose
+        })
+        };
+        
+        return {
+            ...toRefs(resData),
+            fnShow
+        };
+    }
+  };
+</script>
+```
+:::
+    
+### Props
+
+| Attribute | Description | Type | Default
+|----- | ----- | ----- | ----- 
+| show | Whether to show preview | Boolean | false
+| videos | Videos Array(Videos are before images, not support in taro) | Array<`Object`> | []
+| images | Images array | Array<`String`> | []
+| autoplay | Autoplay time, zero means not autoplay | Number、String  | 3000  |
+| init-no | Init no | Number | 1
+| pagination-visible | Whether to show pagination    | Boolean | false |
+| pagination-color   | Pagination color    | String  | '#fff'  |
+| content-close   | Click image to exit preview    | Boolean  | false  |
+
+
+    
+### Events
+
+|Event|Description|Arguments|
+|--|--|--|
+|close|Emitted when closing ImagePreview|-|
+    

+ 29 - 7
src/packages/__VUE/menu/__tests__/menu.spec.ts

@@ -74,12 +74,12 @@ test('menu item options props: html should contain options3 text', () => {
   expect(wrapper.html()).toContain('全部商品');
 });
 
-test('menu item customer text: nut-menu-item__content html should contain customer text', () => {
+test('menu item custom text: nut-menu-item__content html should contain custom text', () => {
   const component = {
     template: `<nut-menu>
       <nut-menu-item v-model="value1" :options="options1" />
       <nut-menu-item>
-        <div>customer text</div>
+        <div>custom text</div>
       </nut-menu-item>
     </nut-menu>`,
     data() {
@@ -92,7 +92,7 @@ test('menu item customer text: nut-menu-item__content html should contain custom
 
   const wrapper = mount(component);
 
-  expect(wrapper.html()).toContain('customer text');
+  expect(wrapper.html()).toContain('custom text');
 });
 
 test('menu item disabled props: nut-menu__item classes should contain disabled', async () => {
@@ -111,21 +111,21 @@ test('menu item disabled props: nut-menu__item classes should contain disabled',
   expect(barItem.classes()).toContain('disabled');
 });
 
-test('menu item title props: nut-menu__title-text html should contain customer title', async () => {
+test('menu item title props: nut-menu__title-text html should contain custom title', async () => {
   const wrapper = mount(Menu, {
     slots: {
       default: h(MenuItem, {
-        title: 'customer title',
+        title: 'custom title',
         options: options1
       })
     }
   });
   await nextTick();
 
-  expect(wrapper.find('.nut-menu__title-text').html()).toContain('customer title');
+  expect(wrapper.find('.nut-menu__title-text').html()).toContain('custom title');
 });
 
-test('menu item title icon props: nut-menu__title-text html should contain customer title', async () => {
+test('menu item title icon props: nut-menu__title-text html should contain custom title', async () => {
   const wrapper = mount(Menu, {
     slots: {
       default: h(MenuItem, {
@@ -193,3 +193,25 @@ test('menu item change props: value2 should be b after click', async () => {
 
   expect(wrapper.vm.value2).toBe('b');
 });
+
+test('menu close-on-click-overlay props: ', async () => {
+  const wrapper = mount(Menu, {
+    props: {
+      closeOnClickOverlay: false
+    },
+    slots: {
+      default: h(MenuItem, {
+        modelValue: 0,
+        options: options1
+      })
+    }
+  });
+  await nextTick();
+  wrapper.find('.nut-menu__item').trigger('click');
+  await nextTick();
+
+  wrapper.find('.nut-overlay').trigger('click');
+  await nextTick();
+
+  expect(wrapper.find<HTMLElement>('.nut-overlay').element.style.display).toEqual('none');
+});

+ 125 - 49
src/packages/__VUE/menu/demo.vue

@@ -1,80 +1,152 @@
 <template>
   <div class="demo full">
-    <h2>基础用法</h2>
+    <h2>{{ translate('basic') }}</h2>
     <nut-menu>
-      <nut-menu-item v-model="state.value1" :options="state.options1" />
-      <nut-menu-item v-model="state.value2" @change="handleChange" :options="state.options2" />
+      <nut-menu-item v-model="state.value1" :options="options1" />
+      <nut-menu-item v-model="state.value2" @change="handleChange" :options="options2" />
     </nut-menu>
-    <h2>自定义菜单内容</h2>
+    <h2>{{ translate('customMenuContent') }}</h2>
     <nut-menu>
-      <nut-menu-item v-model="state.value1" :options="state.options1" />
-      <nut-menu-item title="筛选" ref="item">
+      <nut-menu-item v-model="state.value1" :options="options1" />
+      <nut-menu-item :title="translate('screen')" ref="item">
         <div :style="{ display: 'flex', flex: 1, 'justify-content': 'space-between', 'align-items': 'center' }">
-          <div>自定义内容</div>
-          <nut-button @click="onConfirm">关闭</nut-button>
+          <div :style="{ marginRight: '10px' }">{{ translate('customContent') }}</div>
+          <nut-button @click="onConfirm">{{ translate('confirm') }}</nut-button>
         </div>
       </nut-menu-item>
     </nut-menu>
-    <h2>一行两列</h2>
+    <h2>{{ translate('twoColsInOneLine') }}</h2>
     <nut-menu>
-      <nut-menu-item v-model="state.value3" :cols="2" :options="state.options3" />
+      <nut-menu-item v-model="state.value3" :cols="2" :options="options3" />
     </nut-menu>
-    <h2>自定义选中态颜色</h2>
+    <h2>{{ translate('customActiveColor') }}</h2>
     <nut-menu active-color="green">
-      <nut-menu-item v-model="state.value1" :options="state.options1" />
-      <nut-menu-item v-model="state.value2" @change="handleChange" :options="state.options2" />
+      <nut-menu-item v-model="state.value1" :options="options1" />
+      <nut-menu-item v-model="state.value2" @change="handleChange" :options="options2" />
     </nut-menu>
-    <h2>禁用菜单</h2>
+    <h2>{{ translate('disableMenu') }}</h2>
     <nut-menu>
-      <nut-menu-item disabled v-model="state.value1" :options="state.options1" />
-      <nut-menu-item disabled v-model="state.value2" @change="handleChange" :options="state.options2" />
+      <nut-menu-item disabled v-model="state.value1" :options="options1" />
+      <nut-menu-item disabled v-model="state.value2" @change="handleChange" :options="options2" />
     </nut-menu>
   </div>
 </template>
 
 <script lang="ts">
-import { reactive, ref } from 'vue';
+import { reactive, ref, computed } from 'vue';
 import { createComponent } from '@/packages/utils/create';
-const { createDemo } = createComponent('menu');
+const { createDemo, translate } = createComponent('menu');
+import { useTranslate } from '@/sites/assets/util/useTranslate';
+useTranslate({
+  'zh-CN': {
+    basic: '基本用法',
+    customMenuContent: '自定义菜单内容',
+    customContent: '自定义内容',
+    screen: '筛选',
+    confirm: '确认',
+    twoColsInOneLine: '一行两列',
+    customActiveColor: '自定义选中态颜色',
+    disableMenu: '禁用菜单',
+    allProducts: '全部商品',
+    newProducts: '新款商品',
+    activityProducts: '活动商品',
+    defaultSort: '默认排序',
+    praiseSort: '好评排序',
+    salesVolumeSort: '销量排序',
+    product1: '家庭清洁/纸品',
+    product2: '个人护理',
+    product3: '美妆护肤',
+    product4: '食品饮料',
+    product5: '家用电器',
+    product6: '母婴',
+    product7: '数码',
+    product8: '电脑、办公',
+    product9: '运动户外',
+    product10: '厨具',
+    product11: '医疗保健',
+    product12: '酒类',
+    product13: '生鲜',
+    product14: '家具',
+    product15: '传统滋补',
+    product16: '汽车用品',
+    product17: '家居日用'
+  },
+  'en-US': {
+    basic: 'Basic Usage',
+    customMenuContent: 'Custom Menu Content',
+    customContent: 'Custom content',
+    screen: 'Screen',
+    confirm: 'Confirm',
+    twoColsInOneLine: 'Two Cols In One Line',
+    customActiveColor: 'Custom Active Color',
+    disableMenu: 'Disable Menu',
+    allProducts: 'All Products',
+    newProducts: 'New Products',
+    activityProducts: 'Activity Products',
+    defaultSort: 'Default Sort',
+    praiseSort: 'Praise Sort',
+    salesVolumeSort: 'Sales Volume Sort',
+    product1: 'Product1',
+    product2: 'product2',
+    product3: 'product3',
+    product4: 'product4',
+    product5: 'product5',
+    product6: 'product6',
+    product7: 'product7',
+    product8: 'product8',
+    product9: 'product9',
+    product10: 'product10',
+    product11: 'product11',
+    product12: 'product12',
+    product13: 'product13',
+    product14: 'product14',
+    product15: 'product15',
+    product16: 'Product1',
+    product17: 'Product1'
+  }
+});
 export default createDemo({
   props: {},
   setup() {
     const state = reactive({
-      options1: [
-        { text: '全部商品', value: 0 },
-        { text: '新款商品', value: 1 },
-        { text: '活动商品', value: 2 }
-      ],
-      options2: [
-        { text: '默认排序', value: 'a' },
-        { text: '好评排序', value: 'b' },
-        { text: '销量排序', value: 'c' }
-      ],
-      options3: [
-        { text: '全部商品', value: 0 },
-        { text: '家庭清洁/纸品', value: 1 },
-        { text: '个人护理', value: 2 },
-        { text: '美妆护肤', value: 3 },
-        { text: '食品饮料', value: 4 },
-        { text: '家用电器', value: 5 },
-        { text: '母婴', value: 6 },
-        { text: '数码', value: 7 },
-        { text: '电脑、办公', value: 8 },
-        { text: '运动户外', value: 9 },
-        { text: '厨具', value: 10 },
-        { text: '医疗保健', value: 11 },
-        { text: '酒类', value: 12 },
-        { text: '生鲜', value: 13 },
-        { text: '家具', value: 14 },
-        { text: '传统滋补', value: 15 },
-        { text: '汽车用品', value: 16 },
-        { text: '家居日用', value: 17 }
-      ],
       value1: 0,
       value2: 'a',
       value3: 0
     });
 
+    const options1 = computed(() => [
+      { text: translate('allProducts'), value: 0 },
+      { text: translate('newProducts'), value: 1 },
+      { text: translate('activityProducts'), value: 2 }
+    ]);
+
+    const options2 = computed(() => [
+      { text: translate('defaultSort'), value: 'a' },
+      { text: translate('praiseSort'), value: 'b' },
+      { text: translate('salesVolumeSort'), value: 'c' }
+    ]);
+
+    const options3 = computed(() => [
+      { text: translate('allProducts'), value: 0 },
+      { text: translate('product1'), value: 1 },
+      { text: translate('product2'), value: 2 },
+      { text: translate('product3'), value: 3 },
+      { text: translate('product4'), value: 4 },
+      { text: translate('product5'), value: 5 },
+      { text: translate('product6'), value: 6 },
+      { text: translate('product7'), value: 7 },
+      { text: translate('product8'), value: 8 },
+      { text: translate('product9'), value: 9 },
+      { text: translate('product10'), value: 10 },
+      { text: translate('product11'), value: 11 },
+      { text: translate('product12'), value: 12 },
+      { text: translate('product13'), value: 13 },
+      { text: translate('product14'), value: 14 },
+      { text: translate('product15'), value: 15 },
+      { text: translate('product16'), value: 16 },
+      { text: translate('product17'), value: 17 }
+    ]);
+
     const item = ref('');
 
     const onConfirm = () => {
@@ -88,8 +160,12 @@ export default createDemo({
     return {
       state,
       item,
+      options1,
+      options2,
+      options3,
       onConfirm,
-      handleChange
+      handleChange,
+      translate
     };
   }
 });

+ 313 - 0
src/packages/__VUE/menu/doc.en-US.md

@@ -0,0 +1,313 @@
+# Menu
+
+### Intro
+
+The menu list that pops down downwards.
+
+### Install
+
+``` javascript
+import { createApp } from 'vue';
+// vue
+import { Menu, MenuItem } from '@nutui/nutui';
+// taro
+import { Menu, MenuItem } from '@nutui/nutui-taro';
+const app = createApp();
+app.use(Menu);
+
+```
+
+### Basic Usage
+
+:::demo
+
+```html
+<template>
+  <nut-menu>
+    <nut-menu-item v-model="state.value1" :options="state.options1" />
+    <nut-menu-item v-model="state.value2" @change="handleChange" :options="state.options2" />
+  </nut-menu>
+</template>
+
+<script>
+import { reactive, ref } from 'vue';
+
+export default {
+  setup() {
+    const state = reactive({
+      options1: [
+        { text: 'Option1', value: 0 },
+        { text: 'Option2', value: 1 },
+        { text: 'Option3', value: 2 }
+      ],
+      options2: [
+        { text: 'Option A', value: 'a' },
+        { text: 'Option B', value: 'b' },
+        { text: 'Option C', value: 'c' },
+      ],
+      value1: 0,
+      value2: 'a'
+    });
+
+    const handleChange = val => {
+      console.log('val', val);
+    }
+
+    return {
+      state,
+      handleChange
+    };
+  }
+}
+</script>
+```
+
+:::
+
+### Custom Menu Content
+Popup can be closed with toggle method in menu instance.
+
+:::demo
+
+```html
+<template>
+  <nut-menu>
+    <nut-menu-item v-model="state.value1" :options="state.options1" />
+    <nut-menu-item title="Screen" ref="item">
+      <div :style="{display: 'flex', flex: 1, 'justify-content': 'space-between', 'align-items': 'center'}">
+        <div :style="{ marginRight: '10px'}">Custom content</div>
+        <nut-button @click="onConfirm">Confirm</nut-button>
+      </div>
+    </nut-menu-item>
+  </nut-menu>
+</template>
+
+<script>
+import { reactive, ref } from 'vue';
+
+export default {
+  setup() {
+    const state = reactive({
+      options1: [
+        { text: 'Option1', value: 0 },
+        { text: 'Option2', value: 1 },
+        { text: 'Option3', value: 2 }
+      ],
+      value1: 0
+    });
+
+    const item = ref('');
+
+    const onConfirm = () => {
+      item.value.toggle();
+    }
+
+    return {
+      state,
+      item,
+      onConfirm
+    };
+  }
+}
+</script>
+```
+
+:::
+
+### Two Cols In One Line
+
+:::demo
+
+```html
+<template>
+  <nut-menu>
+    <nut-menu-item v-model="state.value3" :cols="2" :options="state.options3" />
+  </nut-menu>
+</template>
+
+<script>
+import { reactive, ref } from 'vue';
+
+export default {
+  setup() {
+    const state = reactive({
+      options3: [
+        { text: 'Option1', value: 0 },
+        { text: 'Option2', value: 1 },
+        { text: 'Option3', value: 2 },
+        { text: 'Option4', value: 3 },
+        { text: 'Option5', value: 4 },
+        { text: 'Option6', value: 5 },
+        { text: 'Option7', value: 6 },
+        { text: 'Option8', value: 7 },
+        { text: 'Option9', value: 8 },
+        { text: 'Option10', value: 9 },
+        { text: 'Option11', value: 10 },
+        { text: 'Option12', value: 11 },
+        { text: 'Option13', value: 12 },
+        { text: 'Option14', value: 13 },
+        { text: 'Option15', value: 14 },
+        { text: 'Option16', value: 15 },
+        { text: 'Option17', value: 16 },
+        { text: 'Option18', value: 17 },
+      ],
+      value3: 0
+    });
+
+    return {
+      state
+    };
+  }
+}
+</script>
+```
+
+:::
+
+### Custom Active Color
+
+:::demo
+
+```html
+<template>
+  <nut-menu active-color="green">
+    <nut-menu-item v-model="state.value1" :options="state.options1" />
+    <nut-menu-item v-model="state.value2" @change="handleChange" :options="state.options2" />
+  </nut-menu>
+</template>
+
+<script>
+import { reactive, ref } from 'vue';
+
+export default {
+  setup() {
+    const state = reactive({
+      options1: [
+        { text: 'Option1', value: 0 },
+        { text: 'Option2', value: 1 },
+        { text: 'Option3', value: 2 }
+      ],
+      options2: [
+        { text: 'Option A', value: 'a' },
+        { text: 'Option B', value: 'b' },
+        { text: 'Option C', value: 'c' },
+      ],
+      value1: 0,
+      value2: 'a'
+    });
+
+    const handleChange = val => {
+      console.log('val', val);
+    }
+
+    return {
+      state,
+      handleChange
+    };
+  }
+}
+</script>
+```
+
+:::
+
+### Disable Menu
+
+:::demo
+
+```html
+<template>
+  <nut-menu>
+    <nut-menu-item disabled v-model="state.value1" :options="state.options1" />
+    <nut-menu-item disabled v-model="state.value2" @change="handleChange" :options="state.options2" />
+  </nut-menu>
+</template>
+
+<script>
+import { reactive, ref } from 'vue';
+
+export default {
+  setup() {
+    const state = reactive({
+      options1: [
+        { text: 'Option1', value: 0 },
+        { text: 'Option2', value: 1 },
+        { text: 'Option3', value: 2 }
+      ],
+      options2: [
+        { text: 'Option A', value: 'a' },
+        { text: 'Option B', value: 'b' },
+        { text: 'Option C', value: 'c' },
+      ],
+      options3: [
+        { text: 'Option1', value: 0 },
+        { text: 'Option2', value: 1 },
+        { text: 'Option3', value: 2 },
+        { text: 'Option4', value: 3 },
+        { text: 'Option5', value: 4 },
+        { text: 'Option6', value: 5 },
+        { text: 'Option7', value: 6 },
+        { text: 'Option8', value: 7 },
+        { text: 'Option9', value: 8 },
+        { text: 'Option10', value: 9 },
+        { text: 'Option11', value: 10 },
+        { text: 'Option12', value: 11 },
+        { text: 'Option13', value: 12 },
+        { text: 'Option14', value: 13 },
+        { text: 'Option15', value: 14 },
+        { text: 'Option16', value: 15 },
+        { text: 'Option17', value: 16 },
+        { text: 'Option18', value: 17 },
+      ],
+      value1: 0,
+      value2: 'a',
+      value3: 0
+    });
+
+    const item = ref('');
+
+    const onConfirm = () => {
+      item.value.toggle();
+    }
+
+    const handleChange = val => {
+      console.log('val', val);
+    }
+
+    return {
+      state,
+      item,
+      onConfirm,
+      handleChange
+    };
+  }
+}
+</script>
+```
+
+:::
+
+## API
+
+### Menu Props
+
+| Attribute         | Description                             | Type   | Default           |
+|--------------|----------------------------------|--------|------------------|
+| active-color         | Active color of title and option     | String | #F2270C               |
+| close-on-click-overlay `v3.1.21`        | Whether to close when overlay is clicked     | Boolean | true               |
+
+### MenuItem Props
+
+| Attribute         | Description                             | Type   | Default           |
+|--------------|----------------------------------|--------|------------------|
+| title         | Item title     | String | 当前选中项文字               |
+| options         | Options     | Array | -                |
+| disabled         | Whether to disable dropdown item     | Boolean | false                |
+| cols         | Display how many options in one line     | Number | 1                |
+| title-icon         | Custome title icon     | String | 'down-arrow'                |
+
+### MenuItem Events
+
+| Event | Description           | Arguments     |
+|--------|----------------|--------------|
+| change  | Emitted select option changed | Selected value |

+ 3 - 2
src/packages/__VUE/menu/doc.md

@@ -77,8 +77,8 @@ export default {
     <nut-menu-item v-model="state.value1" :options="state.options1" />
     <nut-menu-item title="筛选" ref="item">
       <div :style="{display: 'flex', flex: 1, 'justify-content': 'space-between', 'align-items': 'center'}">
-        <div>自定义内容</div>
-        <nut-button @click="onConfirm">关闭</nut-button>
+        <div :style="{ marginRight: '10px'}">自定义内容</div>
+        <nut-button @click="onConfirm">确认</nut-button>
       </div>
     </nut-menu-item>
   </nut-menu>
@@ -296,6 +296,7 @@ export default {
 | 参数         | 说明                             | 类型   | 默认值           |
 |--------------|----------------------------------|--------|------------------|
 | active-color         | 选项的选中态图标颜色     | String | #F2270C               |
+| close-on-click-overlay `v3.1.21`        | 是否在点击遮罩层后关闭菜单     | Boolean | true               |
 
 ### MenuItem Props
 

+ 4 - 0
src/packages/__VUE/menu/index.vue

@@ -36,6 +36,10 @@ export default create({
     duration: {
       type: [Number, String],
       default: 0
+    },
+    closeOnClickOverlay: {
+      type: Boolean,
+      default: true
     }
   },
   setup(props, { emit, slots }) {

+ 0 - 1
src/packages/__VUE/menuitem/index.scss

@@ -27,7 +27,6 @@
 }
 
 .nut-menu__overlay {
-  position: absolute;
   top: auto !important;
 }
 

+ 1 - 0
src/packages/__VUE/menuitem/index.vue

@@ -19,6 +19,7 @@
       :overlay="parent.props.overlay"
       @closed="handleClose"
       :isWrapTeleport="false"
+      :close-on-click-overlay="parent.props.closeOnClickOverlay"
     >
       <view class="nut-menu-item__content">
         <view

+ 22 - 6
src/packages/__VUE/pagination/demo.vue

@@ -1,12 +1,12 @@
 <template>
   <div class="demo">
-    <h2>基础用法</h2>
+    <h2>{{ translate('basic') }}</h2>
     <nut-pagination v-model="currentPage" :total-items="25" :items-per-page="5" @change="pageChange" />
-    <h2>简单模式</h2>
+    <h2>{{ translate('simpleMode') }}</h2>
     <nut-pagination v-model="currentPage1" :page-count="12" mode="simple" @change="pageChange" />
-    <h2>显示省略号</h2>
+    <h2>{{ translate('showEllipses') }}</h2>
     <nut-pagination v-model="currentPage2" :total-items="125" :show-page-size="3" force-ellipses @change="pageChange" />
-    <h2>自定义按钮</h2>
+    <h2>{{ translate('customButton') }}</h2>
     <nut-pagination v-model="currentPage3" :total-items="500" :show-page-size="5" @change="pageChange">
       <template #prev-text>
         <nut-icon name="left" size="10px" />
@@ -24,7 +24,22 @@
 <script lang="ts">
 import { ref, reactive, toRefs } from 'vue';
 import { createComponent } from '@/packages/utils/create';
-const { createDemo } = createComponent('pagination');
+const { createDemo, translate } = createComponent('pagination');
+import { useTranslate } from '@/sites/assets/util/useTranslate';
+useTranslate({
+  'zh-CN': {
+    basic: '基本用法',
+    simpleMode: '简单模式',
+    showEllipses: '显示省略号',
+    customButton: '自定义按钮'
+  },
+  'en-US': {
+    basic: 'Basic Usage',
+    simpleMode: 'Simple Mode',
+    showEllipses: 'Show ellipses',
+    customButton: 'Custom Button'
+  }
+});
 export default createDemo({
   setup() {
     const state = reactive({
@@ -39,7 +54,8 @@ export default createDemo({
 
     return {
       ...toRefs(state),
-      pageChange
+      pageChange,
+      translate
     };
   }
 });

+ 187 - 0
src/packages/__VUE/pagination/doc.en-US.md

@@ -0,0 +1,187 @@
+#  Pagination
+
+### Intro
+    
+When the amount of data is too much, use pagination to separate the data.
+    
+### Install
+```javascript
+import { createApp } from 'vue';
+//vue
+import { Pagination,Icon } from '@nutui/nutui';
+//taro
+import { Pagination,Icon } from '@nutui/nutui-taro';
+
+const app = createApp();
+app.use(Pagination).use(Icon);
+```    
+    
+### Basic Usage
+Bind current page with v-model.
+:::demo
+```html
+<template>
+  <nut-pagination v-model="currentPage" :total-items="25" :items-per-page="5" @change="pageChange" />
+</template>
+<script lang="ts">
+import { ref, reactive, toRefs } from 'vue';
+export default {
+  setup() {
+    const state = reactive({
+      currentPage: 1,
+    });
+    const pageChange = (value: number) => {
+      console.log(value);
+    };
+
+    return {
+      ...toRefs(state),
+      pageChange
+    };
+  }
+};
+</script>
+<style lang="scss" scoped>
+  .nut-pagination {
+    margin-left:20px;
+  }
+</style>
+```  
+:::
+### Simple mode
+Pagination can be switched to simple mode with simple mode attribute, and pagination cann't display specific page buttons.
+:::demo
+```html
+<template>
+  <nut-pagination v-model="currentPage1" :page-count="12" mode="simple" @change="pageChange" />
+</template>
+<script lang="ts">
+import { ref, reactive, toRefs } from 'vue';
+export default {
+  setup() {
+    const state = reactive({
+      currentPage1: 1,
+    });
+    const pageChange = (value: number) => {
+      console.log(value);
+    };
+
+    return {
+      ...toRefs(state),
+      pageChange
+    };
+  }
+};
+</script>
+<style lang="scss" scoped>
+  .nut-pagination {
+    margin-left:20px;
+  }
+</style>
+```
+:::
+### Show ellipses 
+The ellipses button will display after with force-ellipses attribute, click it can jump quickly.
+:::demo
+```html
+<template>
+  <nut-pagination v-model="currentPage2" :total-items="125" :show-page-size="3"  @change="pageChange"  force-ellipses/>
+</template>
+<script lang="ts">
+import { ref, reactive, toRefs } from 'vue';
+export default {
+  setup() {
+    const state = reactive({
+      currentPage2: 1,
+    });
+    const pageChange = (value: number) => {
+      console.log(value);
+    };
+
+    return {
+      ...toRefs(state),
+      pageChange
+    };
+  }
+};
+</script>
+<style lang="scss" scoped>
+  .nut-pagination {
+    margin-left:20px;
+  }
+</style>
+```
+:::
+### Custom Button
+Custom pagination button content with pre-text slot、next-text slot and so on.
+:::demo
+```html
+<template>
+  <nut-pagination v-model="currentPage3" :total-items="500"  @change="pageChange"  :show-page-size="5">
+      <template #prev-text>
+          <nut-icon name="left" size="10px" />
+      </template>
+      <template #next-text>
+          <nut-icon name="right" size="10px" />
+      </template>
+      <template #page="{ item }">
+          {{ item.number == 3 ? 'hot' : item.text }}
+      </template>
+  </nut-pagination>
+</template>
+<script lang="ts">
+import { ref, reactive, toRefs } from 'vue';
+export default {
+  setup() {
+    const state = reactive({
+      currentPage3: 1,
+    });
+    const pageChange = (value: number) => {
+      console.log(value);
+    };
+
+    return {
+      ...toRefs(state),
+      pageChange
+    };
+  }
+};
+</script>
+<style lang="scss" scoped>
+  .nut-pagination {
+    margin-left:20px;
+  }
+</style>
+``` 
+:::
+
+    
+## API
+    
+### Props
+    
+| Attribute           | Description                     | Type          | Default            |
+|----------------|--------------------------|---------------|-------------------|
+| v-model        | Current page number                 | Number        | 1                 |
+| mode           | Mode, can be set to simple or multi | String        | multi             |
+| prev-text      | Previous text           | String        | Previous            |
+| next-text      | Next text           | String        | Next            |
+| page-count     | The total number of pages                   | String,Number | '' |
+| total-items    | Total items                 | String,Number | 0                 |
+| items-per-page | Item number per page               | String,Number | 10                |
+| show-page-size | Count of page size to show           | String,Number | 5                 |
+| force-ellipses | Whether to show ellipses           | Boolean       | false             |
+    
+### Events
+    
+| Event | Description           | Arguments |
+|--------|----------------|----------|
+| change | Emitted when current page changed | value    |
+
+### Slots
+    
+| Name      | Description                 | Arguments |
+|-----------|----------------------|------|
+| prev-text | Custom prev text | -    |
+| next-text | Custom next text | -    |
+| page      | Custom pagination item           | -    |

+ 4 - 0
src/packages/styles/variables-jdb.scss

@@ -192,6 +192,10 @@ $divider-text-color: #909ca4 !default;
 $divider-line-height: 2px !default;
 $divider-before-margin-right: 16px !default;
 $divider-after-margin-left: 16px !default;
+$divider-vertical-height: 12px !default;
+$divider-vertical-top: 2px !default;
+$divider-vertical-border-left: rgba(0, 0, 0, 0.06) !default;
+$divider-vertical-margin: 0 8px !default;
 
 // icon
 

+ 4 - 1
src/packages/styles/variables-jdt.scss

@@ -107,10 +107,13 @@ $cell-group-background-color: $white !default;
 $divider-margin: 16px 0 !default;
 $divider-text-font-size: 16px !default;
 $divider-text-color: rgba(0, 0, 0, 0.3) !default;
-$divider-line-color: rgba(0, 0, 0, 0.3) !default;
 $divider-line-height: 2px !default;
 $divider-before-margin-right: 16px !default;
 $divider-after-margin-left: 16px !default;
+$divider-vertical-height: 12px !default;
+$divider-vertical-top: 2px !default;
+$divider-vertical-border-left: rgba(0, 0, 0, 0.06) !default;
+$divider-vertical-margin: 0 8px !default;
 
 // icon
 

+ 4 - 0
src/packages/styles/variables.scss

@@ -123,6 +123,10 @@ $divider-text-color: #909ca4 !default;
 $divider-line-height: 2px !default;
 $divider-before-margin-right: 16px !default;
 $divider-after-margin-left: 16px !default;
+$divider-vertical-height: 12px !default;
+$divider-vertical-top: 2px !default;
+$divider-vertical-border-left: rgba(0, 0, 0, 0.06) !default;
+$divider-vertical-margin: 0 8px !default;
 
 // icon
 

+ 8 - 0
src/sites/mobile-taro/vue/src/layout/pages/divider/index.vue

@@ -11,6 +11,14 @@
     <nut-divider dashed>文本</nut-divider>
     <h2>自定义样式</h2>
     <nut-divider :style="{ color: '#1989fa', borderColor: '#1989fa', padding: '0 16px' }">文本</nut-divider>
+    <h2>垂直分割线</h2>
+    <div :style="{ fontSize: '14px' }">
+      文本
+      <nut-divider direction="vertical" />
+      <a href="#">文本</a>
+      <nut-divider direction="vertical" />
+      <a href="#">文本</a>
+    </div>
   </div>
 </template>
 

+ 2 - 2
src/sites/mobile-taro/vue/src/nav/pages/menu/index.vue

@@ -10,8 +10,8 @@
       <nut-menu-item v-model="state.value1" :options="state.options1" />
       <nut-menu-item title="筛选" ref="item">
         <div :style="{ display: 'flex', flex: 1, 'justify-content': 'space-between', 'align-items': 'center' }">
-          <div>自定义内容</div>
-          <nut-button @click="onConfirm">关闭</nut-button>
+          <div :style="{ marginRight: '10px' }">自定义内容</div>
+          <nut-button @click="onConfirm">确认</nut-button>
         </div>
       </nut-menu-item>
     </nut-menu>