ソースを参照

Merge branch 'next' of https://github.com/jdf2e/nutui into next

Drjnigfubo 4 年 前
コミット
53ff0b41c3

+ 70 - 0
src/packages/__VUE/countdown/__tests__/countdown.spec.ts

@@ -0,0 +1,70 @@
+import { config, mount } from '@vue/test-utils';
+import { nextTick, toRefs, reactive } from 'vue';
+import CountDown from '../index.vue';
+import Button from '../../button/index.vue';
+
+function sleep(delay = 0): Promise<void> {
+  return new Promise((resolve) => {
+    setTimeout(resolve, delay);
+  });
+}
+
+afterAll(() => {
+  config.global.components = {};
+});
+
+test('endTime props', async () => {
+  const wrapper = mount(CountDown, {
+    props: {
+      endTime: Date.now() + 1 * 1000
+    }
+  });
+  expect(wrapper.emitted('on-end')).toBeFalsy();
+  await sleep(1000);
+  expect(wrapper.emitted('on-end')).toBeTruthy();
+});
+
+test('show days props', async () => {
+  const wrapper = mount(CountDown, {
+    props: {
+      showDays: true
+    }
+  });
+  const prevSnapShot = wrapper.find('.nut-cd-dot');
+  await nextTick();
+  expect(prevSnapShot.text() == '天').toBe(true);
+});
+
+test('paused props', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-countdown': CountDown,
+      'nut-button': Button
+    },
+    template: `
+      <nut-countdown  :endTime="endTime" :paused="paused" />
+      <nut-button  @click="toggle">{{ paused ? 'start' : 'stop' }}</nut-button>
+    `,
+    setup() {
+      const state = reactive({
+        endTime: Date.now() + 50 * 1000,
+        paused: false
+      });
+
+      const toggle = () => {
+        state.paused = !state.paused;
+      };
+
+      return { ...toRefs(state), toggle };
+    }
+  });
+
+  const button = wrapper.find('.nut-button');
+  const prevSnapShot = wrapper.find('.nut-countdown').html();
+  await button.trigger('click');
+  await nextTick();
+  await sleep(1000);
+  const laterShapShot = wrapper.find('.nut-countdown').html();
+  expect(button.text() == 'start').toBe(true);
+  expect(prevSnapShot === laterShapShot).toBeTruthy();
+});

+ 26 - 0
src/packages/__VUE/infiniteloading/__tests__/__snapshots__/infiniteloading.spec.ts.snap

@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`infiniteloading base 1`] = `
+"<view class=\\"nut-infiniteloading\\">
+  <view class=\\"nut-infinite-top\\" style=\\"height: 0px; transition: height 0.2s cubic-bezier(0.25,0.1,0.25,1);\\">
+    <view class=\\"top-box\\"><img class=\\"nut-icon__img top-img\\" src=\\"https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png\\">
+      <view class=\\"top-text\\">松开刷新</view>
+    </view>
+  </view>
+  <view class=\\"nut-infinite-container\\">
+    <li class=\\"infiniteLi\\">0</li>
+    <li class=\\"infiniteLi\\">1</li>
+    <li class=\\"infiniteLi\\">2</li>
+    <li class=\\"infiniteLi\\">3</li>
+    <li class=\\"infiniteLi\\">4</li>
+    <li class=\\"infiniteLi\\">5</li>
+    <li class=\\"infiniteLi\\">6</li>
+    <li class=\\"infiniteLi\\">7</li>
+    <li class=\\"infiniteLi\\">8</li>
+    <li class=\\"infiniteLi\\">9</li>
+  </view>
+  <view class=\\"nut-infinite-bottom\\">
+    <!--v-if-->
+  </view>
+</view>"
+`;

+ 94 - 0
src/packages/__VUE/infiniteloading/__tests__/infiniteloading.spec.ts

@@ -0,0 +1,94 @@
+import { config, mount } from '@vue/test-utils';
+import InfiniteLoading from '../index.vue';
+import NutIcon from '../../icon/index.vue';
+import { trigger, triggerDrag } from '../../../utils/test/event';
+import { nextTick, toRefs, reactive, ref, onMounted } from 'vue';
+
+function sleep(delay = 0): Promise<void> {
+  return new Promise((resolve) => {
+    setTimeout(resolve, delay);
+  });
+}
+
+beforeAll(() => {
+  config.global.components = {
+    NutIcon
+  };
+});
+
+afterAll(() => {
+  config.global.components = {};
+});
+
+test('pull base', async () => {
+  const wrapper = mount(InfiniteLoading, {
+    props: {
+      isOpenRefresh: true
+    }
+  });
+  const track = wrapper.find('.nut-infiniteloading');
+  // pulling
+  trigger(track, 'touchstart', 0, 0);
+  trigger(track, 'touchmove', 0, 20);
+  expect(wrapper.html()).toMatchSnapshot();
+
+  // loading
+  trigger(track, 'touchend', 0, 100);
+  expect(wrapper.html()).toMatchSnapshot();
+
+  // still loading
+  triggerDrag(track, 0, 100);
+
+  expect(wrapper.emitted('refresh')).toBeTruthy();
+});
+
+test('infiniteloading base', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-infiniteloading': InfiniteLoading,
+      'nut-icon': NutIcon
+    },
+    template: `
+    <nut-infiniteloading
+      :has-more="hasMore"
+      @load-more="loadMore">
+        <li class="infiniteLi" v-for="(item, index) in defultList" :key="index">{{item}}</li>
+    </nut-infiniteloading>
+    `,
+    setup() {
+      const hasMore = ref(true);
+      const data = reactive({
+        defultList: []
+      });
+      const init = () => {
+        for (let i = 0; i < 10; i++) {
+          (data.defultList as any).push(i);
+        }
+      };
+      const loadMore = (done: any) => {
+        setTimeout(() => {
+          const curLen = data.defultList.length;
+          for (let i = curLen; i < curLen + 10; i++) {
+            (data.defultList as any).push(i);
+          }
+          if (data.defultList.length > 30) hasMore.value = false;
+          done();
+        }, 500);
+      };
+      onMounted(() => {
+        init();
+      });
+
+      return { ...toRefs(data), hasMore, loadMore };
+    }
+  });
+  const track = wrapper.find('.nut-infiniteloading');
+  await nextTick();
+  trigger(track, 'touchstart', 0, 0);
+  trigger(track, 'touchmove', 0, -100);
+  trigger(track, 'touchend', 0, -800);
+
+  triggerDrag(track, 0, -800);
+  await nextTick();
+  expect(wrapper.html()).toMatchSnapshot();
+});

+ 50 - 0
src/packages/__VUE/noticebar/__tests__/__snapshots__/noticebar.spec.ts.snap

@@ -0,0 +1,50 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`custon scroll list 1`] = `
+"<view class=\\"nut-noticebar\\">
+  <!--v-if-->
+  <view class=\\"nut-noticebar-vertical\\" style=\\"height: 50px;\\">
+    <view class=\\"horseLamp_list\\">
+      <div class=\\"custom-item\\" style=\\"height: 50px; line-height: 50px;\\">惊喜红包免费领</div>
+      <div class=\\"custom-item\\" style=\\"height: 50px; line-height: 50px;\\">爆款准点秒</div>
+      <div class=\\"custom-item\\" style=\\"height: 50px; line-height: 50px;\\">买超值优惠</div>
+      <div class=\\"custom-item\\" style=\\"height: 50px; line-height: 50px;\\">赢百万京豆</div>
+    </view>
+    <view class=\\"go\\">
+      <!--v-if-->
+    </view>
+  </view>
+</view>"
+`;
+
+exports[`icon custom 1`] = `
+"<view class=\\"nut-noticebar\\">
+  <view class=\\"nut-noticebar-page\\">
+    <view class=\\"left-icon\\" style=\\"background-image: url(https://img13.360buyimg.com/imagetools/jfs/t1/72082/2/3006/1197/5d130c8dE1c71bcd6/e48a3b60804c9775.png);\\">
+      <!--v-if-->
+    </view>
+    <view class=\\"wrap\\">
+      <view class=\\"content nut-ellipsis\\" style=\\"padding-left: 0px; animation-delay: 1s; animation-duration: 0s;\\"><a href=\\"https://www.jd.com\\">京东商城</a></view>
+    </view>
+    <!--v-if-->
+  </view>
+  <!--v-if-->
+</view>"
+`;
+
+exports[`vertical scroll 1`] = `
+"<view class=\\"nut-noticebar\\">
+  <!--v-if-->
+  <view class=\\"nut-noticebar-vertical\\" style=\\"height: 40px;\\">
+    <ul class=\\"horseLamp_list\\">
+      <li class=\\"horseLamp_list_item\\">惊喜红包免费领</li>
+      <li class=\\"horseLamp_list_item\\">爆款准点秒</li>
+      <li class=\\"horseLamp_list_item\\">买超值优惠</li>
+      <li class=\\"horseLamp_list_item\\">赢百万京豆</li>
+    </ul>
+    <view class=\\"go\\">
+      <!--v-if-->
+    </view>
+  </view>
+</view>"
+`;

+ 93 - 0
src/packages/__VUE/noticebar/__tests__/noticebar.spec.ts

@@ -0,0 +1,93 @@
+import { config, mount } from '@vue/test-utils';
+import { nextTick, ref, reactive } from 'vue';
+import NoticeBar from '../index.vue';
+import NutIcon from '../../icon/index.vue';
+
+beforeAll(() => {
+  config.global.components = {
+    NutIcon
+  };
+});
+
+afterAll(() => {
+  config.global.components = {};
+});
+
+test('close event', async () => {
+  const wrapper = mount(NoticeBar, {
+    props: {
+      text: '华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!',
+      direction: 'across',
+      closeMode: true
+    }
+  });
+  const closeDom = wrapper.find('.right-icon');
+  closeDom.trigger('click');
+  expect(wrapper.emitted('close')).toBeTruthy();
+});
+
+test('scrollable props', async () => {
+  const wrapper = mount(NoticeBar, {
+    props: {
+      text: '华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!',
+      direction: 'across',
+      scrollable: false
+    }
+  });
+  const closeDom = wrapper.find('.nut-ellipsis');
+  expect(closeDom.exists()).toBeTruthy();
+});
+
+test('icon custom', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-noticebar': NoticeBar
+    },
+    template: `
+        <nut-noticebar
+        left-icon="https://img13.360buyimg.com/imagetools/jfs/t1/72082/2/3006/1197/5d130c8dE1c71bcd6/e48a3b60804c9775.png"
+        :scrollable="false"
+      >
+        <a href="https://www.jd.com">京东商城</a>
+      </nut-noticebar>
+    `
+  });
+  await nextTick();
+  expect(wrapper.html()).toMatchSnapshot();
+});
+
+test('vertical scroll', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-noticebar': NoticeBar
+    },
+    template: `
+      <nut-noticebar direction='vertical'  :list="horseLamp" ></nut-noticebar>
+    `,
+    setup() {
+      const horseLamp = ref(['惊喜红包免费领', '爆款准点秒', '买超值优惠', '赢百万京豆']);
+      return { horseLamp };
+    }
+  });
+  await nextTick();
+  expect(wrapper.html()).toMatchSnapshot();
+});
+
+test('custon scroll list', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-noticebar': NoticeBar
+    },
+    template: `
+      <nut-noticebar direction='vertical' :height='50' :speed='10' :standTime='1000' :list="[]" >
+        <div class="custom-item" v-for="(item,index) in horseLamp" :key="item">{{item}}</div>
+      </nut-noticebar>
+    `,
+    setup() {
+      const horseLamp = ref(['惊喜红包免费领', '爆款准点秒', '买超值优惠', '赢百万京豆']);
+      return { horseLamp };
+    }
+  });
+  await nextTick();
+  expect(wrapper.html()).toMatchSnapshot();
+});

+ 36 - 9
src/packages/__VUE/noticebar/demo.vue

@@ -2,12 +2,20 @@
   <div class="demo">
     <h2>默认用法</h2>
     <nut-noticebar
-      :text="text"
+      text="华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!"
       :background="`rgba(251, 248, 220, 1)`"
       :color="`#D9500B`"
     ></nut-noticebar>
 
-    <h2>禁用滚动</h2>
+    <h2>滚动播放</h2>
+    <nut-noticebar
+      text="华为畅享9新品即将上市"
+      :scrollable="true"
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`"
+    >
+    </nut-noticebar>
+    <p />
     <nut-noticebar
       text="华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!"
       :scrollable="false"
@@ -16,14 +24,19 @@
     ></nut-noticebar>
 
     <h2>通告栏模式--关闭模式</h2>
+    <nut-noticebar :closeMode="true" @click="hello" :background="`rgba(251, 248, 220, 1)`" :color="`#D9500B`"
+      >华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!
+    </nut-noticebar>
+    <p />
     <nut-noticebar
       :closeMode="true"
+      right-icon="circle-close"
       @click="hello"
       :background="`rgba(251, 248, 220, 1)`"
       :color="`#D9500B`"
-      >华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI
-      WATCH等好礼,更多产品信息请持续关注!
+      >华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!
     </nut-noticebar>
+
     <h2>通告栏模式--链接模式</h2>
     <nut-noticebar
       left-icon="https://img13.360buyimg.com/imagetools/jfs/t1/72082/2/3006/1197/5d130c8dE1c71bcd6/e48a3b60804c9775.png"
@@ -33,6 +46,14 @@
       <a href="https://www.jd.com">京东商城</a>
     </nut-noticebar>
 
+    <h2>多行展示</h2>
+    <nut-noticebar
+      text="华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!"
+      wrapable
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`"
+    ></nut-noticebar>
+
     <h2>纵向滚动</h2>
     <div class="interstroll-list">
       <nut-noticebar
@@ -55,6 +76,8 @@
         :speed="10"
         :standTime="2000"
         :complexAm="true"
+        :background="`rgba(251, 248, 220, 1)`"
+        :color="`#D9500B`"
       ></nut-noticebar>
     </div>
     <h2>纵向自定义滚动内容</h2>
@@ -66,6 +89,8 @@
         :standTime="1000"
         :list="[]"
         @close="go"
+        :background="`rgba(251, 248, 220, 1)`"
+        :color="`#D9500B`"
       >
         <div
           class="custom-item"
@@ -85,6 +110,8 @@
         :list="horseLamp1"
         :speed="10"
         :standTime="1000"
+        :background="`rgba(251, 248, 220, 1)`"
+        :color="`#D9500B`"
       >
         <template v-slot:rightIcon>
           <nut-icon name="fabulous" color="#f0250f"> </nut-icon>
@@ -128,10 +155,10 @@ export default createDemo({
 .demo {
   padding-bottom: 30px !important;
 
-  .interstroll-list {
-    padding: 0 10px;
-    background: rgba(251, 248, 220, 1);
-    color: #d9500b;
-  }
+  // .interstroll-list {
+  //   padding: 0 10px;
+  //   background: rgba(251, 248, 220, 1);
+  //   color: #d9500b;
+  // }
 }
 </style>

+ 64 - 16
src/packages/__VUE/noticebar/doc.md

@@ -28,38 +28,56 @@ app.use(NoticeBar);
 
 ```html
 <template>
-  <nut-noticebar text="华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!"></nut-noticebar>
+  <nut-noticebar
+      text="华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!"
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`"
+    ></nut-noticebar>
 </template>
 
 ```
 :::
-### 禁用滚动
-文字内容多于一行时,可通过scrollable参数控制是否开启滚动
+
+### 滚动播放
+
+通知栏的内容长度溢出时会自动开启滚动播放,可通过 scrollable 属性可以控制该行为
 
 :::demo
 
 ```html
 <template>
+  <nut-noticebar text="华为畅享9新品即将上市" :scrollable="true" :background="`rgba(251, 248, 220, 1)`" :color="`#D9500B`"></nut-noticebar>
+
   <nut-noticebar
       text="华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!"
       :scrollable="false"
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`"
     ></nut-noticebar>
 </template>
 
 ```
 :::
 
+
 ### 通告栏模式--关闭模式
 
 :::demo
 
 ```html
 <template>
+  <nut-noticebar :closeMode="true" :background="`rgba(251, 248, 220, 1)`" :color="`#D9500B`">
+    华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!
+  </nut-noticebar>
+ 
   <nut-noticebar
       :closeMode="true"
-      @click="hello"
-    >华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!
-    </nut-noticebar>
+      right-icon="circle-close"
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`"
+      >
+      华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!
+  </nut-noticebar>
 </template>
 
 ```
@@ -80,11 +98,38 @@ app.use(NoticeBar);
 </template>
 ```
 :::
+
+### 多行展示
+
+文字较长时,可以通过设置 wrapable 属性来开启多行展示。默认为不滚动,可以通过设置 scrollable 控制为滚动。
+
+:::demo
+```html
+<template>
+  <nut-noticebar
+      text="华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!"
+      wrapable
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`"
+    ></nut-noticebar>
+</template>
+```
+:::
+
 ### 纵向滚动
 :::demo
 ```html
 <template>
-    <nut-noticebar direction='vertical' :list="horseLamp1" :speed='10' :standTime='1000'  @click='go' :closeMode="true"></nut-noticebar>
+  <nut-noticebar 
+      direction='vertical' 
+      :list="horseLamp1" 
+      :speed='10' 
+      :standTime='1000' 
+      @click='go' 
+      :closeMode="true" 
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`">
+  </nut-noticebar>
 </template>
 
 <script>
@@ -108,7 +153,7 @@ app.use(NoticeBar);
 :::demo
 ```html
 <template>
-    <nut-noticebar direction='vertical' :list="horseLamp2" :speed='10' :standTime='2000' :complexAm='true'></nut-noticebar>
+  <nut-noticebar direction='vertical' :list="horseLamp2" :speed='10' :standTime='2000' :complexAm='true' :background="`rgba(251, 248, 220, 1)`" :color="`#D9500B`"></nut-noticebar>
 </template>
 
 <script>
@@ -128,7 +173,7 @@ app.use(NoticeBar);
 :::demo
 ```html
 <template>
-    <nut-noticebar direction='vertical' :height='50' :speed='10' :standTime='1000' :list="[]"  @close='go'>
+    <nut-noticebar direction='vertical' :height='50' :speed='10' :standTime='1000' :list="[]"  @close='go' :background="`rgba(251, 248, 220, 1)`" :color="`#D9500B`">
       <div class="custom-item" :data-index='index' v-for="(item,index) in horseLamp3" :key="index">{{item}}</div>
     </nut-noticebar>
 </template>
@@ -150,14 +195,9 @@ app.use(NoticeBar);
 :::demo
 ```html
 <template>
-    <nut-noticebar direction='vertical' :list="horseLamp1" :speed='10' :standTime='1000' >
+    <nut-noticebar direction='vertical' :list="horseLamp1" :speed='10' :standTime='1000' :background="`rgba(251, 248, 220, 1)`" :color="`#D9500B`">
       <template v-slot:rightIcon>
-        <nut-icon 
-          type="trolley" 
-          color="#f0250f"
-        >
-        </nut-icon>
-
+        <nut-icon type="trolley" color="#f0250f"></nut-icon>
       </template>
     </nut-noticebar>
 </template>
@@ -183,6 +223,7 @@ app.use(NoticeBar);
 | text       | 提示的信息                                                 | String        | 空     |
 | closeMode  | 是否启用关闭模式                                           | Boolean       | false  |
 | leftIcon   | close为没有左边icon,其他为自定义的图片链接,没有为默认图片 | String        | 空     |
+| rightIcon   | closeMode 模式下,默认为 ‘close’,其他模式下,没有为默认图片 | String        | 空     |
 | color      | 导航栏的文字颜色                                           | String        | 空     |
 | background | 导航栏的背景颜色                                           | String        | 空     |
 | delay      | 延时多少秒                                                 | String/Number | 1      |
@@ -200,6 +241,13 @@ app.use(NoticeBar);
 | height          | 每一个滚动列的高度(px),注意:在使用 slot 插槽定义滚动单元时,按照实际高度修改此值                 | Number | 40              |
 | closeMode  | 是否启用右侧关闭图标,可以通过slot[name=rightIcon]自定义图标                                   | Boolean       | false  |
 
+### Slots
+
+| 参数         | 说明                             | 
+|--------------|----------------------------------|
+| default         | 通知文本的内容               | 
+| right-icon        | 自定义右侧图标    | 
+| left-icon        | 自定义左侧图标    | 
 ### Event
 
 | 字段  | 说明             | 回调参数     |

+ 11 - 5
src/packages/__VUE/noticebar/index.scss

@@ -1,13 +1,15 @@
 .nut-noticebar-page {
-  width: 100%;
+  // width: 100%;
   display: flex;
+  padding: 0px $noticebar-box-padding-right 0 $noticebar-box-padding-left;
   height: $noticebar-height;
   font-size: $noticebar-font-size;
   position: relative;
   align-items: center;
   &.wrapable {
     height: auto;
-    padding: 8px 16px;
+    padding: $noticebar-wrapable-padding-bottom $noticebar-wrapable-padding-right $noticebar-wrapable-padding-bottom
+      $noticebar-wrapable-padding-left;
     .wrap {
       height: auto;
       .content {
@@ -25,7 +27,8 @@
   .left-icon {
     height: $noticebar-left-icon-width;
     min-width: $noticebar-left-icon-width;
-    margin: 0 5px 0 10px;
+    margin: $noticebar-lefticon-padding-top $noticebar-lefticon-padding-right $noticebar-lefticon-padding-bottom
+      $noticebar-lefticon-padding-left;
     background-size: 100% 100%;
   }
   .right-icon {
@@ -33,9 +36,11 @@
     align-items: center;
     justify-content: center;
     width: $noticebar-right-icon-width;
-    margin: 0 10px 0 5px;
+    margin: $noticebar-righticon-padding-top $noticebar-righticon-padding-right $noticebar-righticon-padding-bottom
+      $noticebar-righticon-padding-left;
   }
   .wrap {
+    display: flex;
     flex: 1;
     height: $noticebar-line-height;
     line-height: $noticebar-line-height;
@@ -72,7 +77,7 @@
 
 @keyframes nut-notice-bar-play-infinite {
   to {
-    transform: translate3d(-100%, 0, 0);
+    transform: translateX(-100%);
   }
 }
 // 垂直方向的滚动
@@ -90,6 +95,7 @@
   height: $noticebar-height;
   font-size: $noticebar-font-size;
   overflow: hidden;
+  padding: 0px $noticebar-box-padding-right 0 $noticebar-box-padding-left;
 
   .horseLamp_list {
     margin: 0;

+ 30 - 18
src/packages/__VUE/noticebar/index.taro.vue

@@ -9,13 +9,13 @@
       v-if="direction == 'across'"
     >
       <view class="left-icon" v-if="iconShow" :style="{ 'background-image': `url(${iconBg})` }">
-        <nut-icon name="notice" size="16" :color="color" v-if="!iconBg"></nut-icon>
+        <slot name="left-icon"><nut-icon name="notice" size="16" :color="color" v-if="!iconBg"></nut-icon></slot>
       </view>
-      <view ref="wrap" class="wrap">
+      <view ref="wrap" :class="`wrap wrap${id}`">
         <view
           ref="content"
           class="content"
-          :class="[animationClass, { 'nut-ellipsis': !scrollable && !wrapable }]"
+          :class="[animationClass, { 'nut-ellipsis': isEllipsis }, `content${id}`]"
           :style="contentStyle"
           @animationend="onAnimationEnd"
           @webkitAnimationEnd="onAnimationEnd"
@@ -23,8 +23,8 @@
           <slot>{{ text }}</slot>
         </view>
       </view>
-      <view v-if="closeMode" class="right-icon" @click.stop="onClickIcon">
-        <nut-icon name="close" size="11" :color="color"></nut-icon>
+      <view v-if="closeMode || rightIcon" class="right-icon" @click.stop="onClickIcon">
+        <slot name="right-icon"> <nut-icon :name="rightIcon ? rightIcon : 'close'" :color="color"></nut-icon></slot>
       </view>
     </view>
 
@@ -121,13 +121,14 @@ export default create({
       default: false
     },
     leftIcon: { type: String, default: '' },
+    rightIcon: { type: String, default: '' },
     color: {
       type: String,
       default: '#F9911B'
     },
     background: {
       type: String,
-      default: 'rgba(254,250,216,1)'
+      default: ''
     },
     delay: {
       type: [String, Number],
@@ -135,7 +136,7 @@ export default create({
     },
     scrollable: {
       type: Boolean,
-      default: true
+      default: null
     },
     speed: {
       type: Number,
@@ -168,7 +169,9 @@ export default create({
       scrollList: [],
       distance: 0,
       timer: null,
-      keepAlive: false
+      keepAlive: false,
+      isCanScroll: null,
+      id: Math.round(Math.random() * 100000)
     });
 
     const classes = computed(() => {
@@ -178,6 +181,14 @@ export default create({
       };
     });
 
+    const isEllipsis = computed(() => {
+      if (state.isCanScroll == null) {
+        return false && !props.wrapable;
+      } else {
+        return !state.isCanScroll && !props.wrapable;
+      }
+    });
+
     const iconShow = computed(() => {
       if (props.leftIcon == 'close') {
         return false;
@@ -202,9 +213,9 @@ export default create({
 
     const contentStyle = computed(() => {
       return {
-        paddingLeft: state.firstRound ? 0 : state.wrapWidth + 'px',
         animationDelay: (state.firstRound ? props.delay : 0) + 's',
-        animationDuration: state.duration + 's'
+        animationDuration: state.duration + 's',
+        transform: `translateX(${state.firstRound ? 0 : state.wrapWidth + 'px'})`
       };
     });
 
@@ -258,17 +269,19 @@ export default create({
         let offsetWidth = 0;
 
         Taro.createSelectorQuery()
-          .select('.wrap')
+          .select(`.wrap${state.id}`)
           .boundingClientRect((rect) => {
             if (rect.width > 0) wrapWidth = rect.width;
           })
           .exec();
         Taro.createSelectorQuery()
-          .select(`.content`)
+          .select(`.content${state.id}`)
           .boundingClientRect((rect) => {
             if (rect.width > 0) offsetWidth = rect.width;
 
-            if (props.scrollable && offsetWidth > wrapWidth) {
+            state.isCanScroll = props.scrollable == null ? offsetWidth > wrapWidth : props.scrollable;
+
+            if (state.isCanScroll) {
               state.wrapWidth = wrapWidth;
               state.offsetWidth = offsetWidth;
 
@@ -277,7 +290,6 @@ export default create({
             } else {
               state.animationClass = '';
             }
-            // contentStyle()
           })
           .exec();
       }, 100);
@@ -287,7 +299,9 @@ export default create({
     };
 
     const onClickIcon = (event: Event) => {
-      state.showNoticeBar = !props.closeMode;
+      if (props.closeMode) {
+        state.showNoticeBar = !props.closeMode;
+      }
       emit('close', event);
     };
 
@@ -346,7 +360,6 @@ export default create({
     };
 
     onMounted(() => {
-      // console.log(props.direction);
       if (props.direction == 'vertical') {
         if (slots.default) {
           state.scrollList = [].concat(slots.default()[0].children as any);
@@ -354,8 +367,6 @@ export default create({
           state.scrollList = [].concat(props.list as any);
         }
 
-        // console.log(state.scrollList);
-
         setTimeout(() => {
           props.complexAm ? startRoll() : startRollEasy();
         }, props.standTime);
@@ -382,6 +393,7 @@ export default create({
     return {
       ...toRefs(props),
       ...toRefs(state),
+      isEllipsis,
       classes,
       iconShow,
       barStyle,

+ 43 - 15
src/packages/__VUE/noticebar/index.vue

@@ -9,13 +9,13 @@
       v-if="direction == 'across'"
     >
       <view class="left-icon" v-if="iconShow" :style="{ 'background-image': `url(${iconBg})` }">
-        <nut-icon name="notice" size="16" :color="color" v-if="!iconBg"></nut-icon>
+        <slot name="left-icon"><nut-icon name="notice" size="16" :color="color" v-if="!iconBg"></nut-icon></slot>
       </view>
       <view ref="wrap" class="wrap">
         <view
           ref="content"
           class="content"
-          :class="[animationClass, { 'nut-ellipsis': !scrollable && !wrapable }]"
+          :class="[animationClass, { 'nut-ellipsis': isEllipsis }]"
           :style="contentStyle"
           @animationend="onAnimationEnd"
           @webkitAnimationEnd="onAnimationEnd"
@@ -23,8 +23,8 @@
           <slot>{{ text }}</slot>
         </view>
       </view>
-      <view v-if="closeMode" class="right-icon" @click.stop="onClickIcon">
-        <nut-icon name="close" :color="color"></nut-icon>
+      <view v-if="closeMode || rightIcon" class="right-icon" @click.stop="onClickIcon">
+        <slot name="right-icon"> <nut-icon :name="rightIcon ? rightIcon : 'close'" :color="color"></nut-icon></slot>
       </view>
     </view>
 
@@ -33,7 +33,7 @@
         <view class="horseLamp_list" :style="horseLampStyle">
           <ScrollItem
             v-for="(item, index) in scrollList"
-            v-bind:key="index"
+            :key="index"
             :style="{ height: height + 'px', 'line-height': height + 'px' }"
             :item="item"
           ></ScrollItem>
@@ -82,6 +82,21 @@ import {
 import { createComponent } from '../../utils/create';
 const { componentName, create } = createComponent('noticebar');
 
+interface StateProps {
+  wrapWidth: number;
+  firstRound: boolean;
+  duration: number;
+  offsetWidth: number;
+  showNoticeBar: boolean;
+  animationClass: string;
+
+  animate: boolean;
+  scrollList: [];
+  distance: number;
+  timer: null;
+  keepAlive: boolean;
+  isCanScroll: null | boolean;
+}
 export default create({
   props: {
     // 滚动方向  across 横向 vertical 纵向
@@ -120,6 +135,7 @@ export default create({
       default: false
     },
     leftIcon: { type: String, default: '' },
+    rightIcon: { type: String, default: '' },
     color: {
       type: String,
       default: ''
@@ -134,7 +150,7 @@ export default create({
     },
     scrollable: {
       type: Boolean,
-      default: true
+      default: null
     },
     speed: {
       type: Number,
@@ -144,6 +160,7 @@ export default create({
   components: {
     ScrollItem: function (props) {
       props.item.props.style = props.style;
+      props.item.key = props.key;
       return h(props.item);
     }
   },
@@ -155,7 +172,7 @@ export default create({
     const wrap = ref<null | HTMLElement>(null);
     const content = ref<null | HTMLElement>(null);
 
-    const state = reactive({
+    const state = reactive<StateProps>({
       wrapWidth: 0,
       firstRound: true,
       duration: 0,
@@ -167,7 +184,8 @@ export default create({
       scrollList: [],
       distance: 0,
       timer: null,
-      keepAlive: false
+      keepAlive: false,
+      isCanScroll: null
     });
 
     const classes = computed(() => {
@@ -177,6 +195,14 @@ export default create({
       };
     });
 
+    const isEllipsis = computed(() => {
+      if (state.isCanScroll == null) {
+        return false && !props.wrapable;
+      } else {
+        return !state.isCanScroll && !props.wrapable;
+      }
+    });
+
     const iconShow = computed(() => {
       if (props.leftIcon == 'close') {
         return false;
@@ -201,9 +227,9 @@ export default create({
 
     const contentStyle = computed(() => {
       return {
-        paddingLeft: state.firstRound ? 0 : state.wrapWidth + 'px',
         animationDelay: (state.firstRound ? props.delay : 0) + 's',
-        animationDuration: state.duration + 's'
+        animationDuration: state.duration + 's',
+        transform: `translateX(${state.firstRound ? 0 : state.wrapWidth + 'px'})`
       };
     });
     const iconBg = computed(() => {
@@ -257,7 +283,9 @@ export default create({
 
         const offsetWidth = content.value.getBoundingClientRect().width;
 
-        if (props.scrollable && offsetWidth > wrapWidth) {
+        state.isCanScroll = props.scrollable == null ? offsetWidth > wrapWidth : props.scrollable;
+
+        if (state.isCanScroll) {
           state.wrapWidth = wrapWidth;
           state.offsetWidth = offsetWidth;
 
@@ -273,7 +301,9 @@ export default create({
     };
 
     const onClickIcon = (event: Event) => {
-      state.showNoticeBar = !props.closeMode;
+      if (props.closeMode) {
+        state.showNoticeBar = !props.closeMode;
+      }
       emit('close', event);
     };
 
@@ -332,7 +362,6 @@ export default create({
     };
 
     onMounted(() => {
-      // console.log(props.direction);
       if (props.direction == 'vertical') {
         if (slots.default) {
           state.scrollList = [].concat(slots.default()[0].children as any);
@@ -340,8 +369,6 @@ export default create({
           state.scrollList = [].concat(props.list as any);
         }
 
-        // console.log(state.scrollList);
-
         setTimeout(() => {
           props.complexAm ? startRoll() : startRollEasy();
         }, props.standTime);
@@ -368,6 +395,7 @@ export default create({
     return {
       ...toRefs(props),
       ...toRefs(state),
+      isEllipsis,
       classes,
       iconShow,
       barStyle,

+ 8 - 0
src/packages/__VUE/picker/__tests__/__snapshots__/picker.spec.ts.snap

@@ -0,0 +1,8 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`base 1`] = `
+"<view class=\\"nut-picker\\" modelvisible=\\"true\\">
+  <!--teleport start-->
+  <!--teleport end-->
+</view>"
+`;

+ 40 - 0
src/packages/__VUE/picker/__tests__/picker.spec.ts

@@ -0,0 +1,40 @@
+import { config, mount } from '@vue/test-utils';
+import Picker from '../index.vue';
+import NutIcon from '../../icon/index.vue';
+import NutPupup from '../../popup/index.vue';
+import NutPickerColumn from '../Column.vue';
+import { nextTick, toRefs, reactive, ref, onMounted } from 'vue';
+
+beforeAll(() => {
+  config.global.components = {
+    NutIcon,
+    NutPupup,
+    NutPickerColumn
+  };
+});
+
+afterAll(() => {
+  config.global.components = {};
+});
+
+const simpleColumn = ['1990', '1991', '1992', '1993', '1994', '1995'];
+const columns = [
+  {
+    values: ['vip', 'normal'],
+    className: 'column1'
+  },
+  {
+    values: simpleColumn,
+    className: 'column2'
+  }
+];
+
+test('base', async () => {
+  const wrapper = mount(Picker, {
+    props: {
+      modelVisible: true,
+      listData: []
+    }
+  });
+  await nextTick();
+});

+ 2 - 2
src/packages/__VUE/picker/demo.vue

@@ -19,7 +19,7 @@
       @close="close"
     >
     </nut-picker>
-    <nut-picker
+    <!-- <nut-picker
       v-model:visible="show1"
       :list-data="listData1"
       title="城市选择"
@@ -38,7 +38,7 @@
       title="地址选择"
       @change="onChange"
       @confirm="(val) => confirm(4, val)"
-    ></nut-picker>
+    ></nut-picker> -->
   </div>
 </template>
 <script lang="ts">

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

@@ -101,6 +101,7 @@ export default create({
     });
 
     const columnList = computed(() => {
+      console.log('初始化', dataType.value);
       if (dataType.value === 'text') {
         return [{ values: state.formattedColumns, defaultIndex: state.defaultIndex }];
       } else if (dataType.value === 'multipleColumns') {

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

@@ -19,7 +19,7 @@ app.use(Signature);
     
 ## 代码演示
     
-### 基础用法1
+### 基础用法
 
 :::demo
 

+ 1 - 8
src/packages/__VUE/tag/__test__/__snapshots__/tag.spec.ts.snap

@@ -1,17 +1,10 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`should hide tag when the show prop is false 1`] = `
-"<view class=\\"nut-tag nut-tag--default\\" show=\\"false\\">
+"<view  class=\\"nut-tag nut-tag--default\\" show=\\"false\\">
   <!--v-if-->
 </view>"
 `;
-
-exports[`should render border-color correctly 1`] = `
-"<view class=\\"nut-tag nut-tag--default nut-tag--plain\\" style=\\"background: red; color: blue;\\">
-  <!--v-if-->
-</view>"
-`;
-
 exports[`should render textColor correctly 1`] = `
 "<view class=\\"nut-tag nut-tag--default nut-tag--plain\\" style=\\"background: red; color: blue;\\">
   <!--v-if-->

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

@@ -320,6 +320,23 @@ $noticebar-height: 40px !default;
 $noticebar-line-height: 24px !default;
 $noticebar-left-icon-width: 16px !default;
 $noticebar-right-icon-width: 16px !default;
+$noticebar-box-padding-left: 16px !default;
+$noticebar-box-padding-right: 16px !default;
+
+$noticebar-wrapable-padding-left: 16px !default;
+$noticebar-wrapable-padding-right: 16px !default;
+$noticebar-wrapable-padding-top: 16px !default;
+$noticebar-wrapable-padding-bottom: 16px !default;
+
+$noticebar-lefticon-padding-left: 0px !default;
+$noticebar-lefticon-padding-right: 10px !default;
+$noticebar-lefticon-padding-top: 0px !default;
+$noticebar-lefticon-padding-bottom: 0px !default;
+
+$noticebar-righticon-padding-left: 10px !default;
+$noticebar-righticon-padding-right: 0px !default;
+$noticebar-righticon-padding-top: 0px !default;
+$noticebar-righticon-padding-bottom: 0px !default;
 
 // TimeSelect
 $timeselect-title-font-size: $font-size-2 !default;

+ 61 - 0
src/packages/utils/test/event.ts

@@ -0,0 +1,61 @@
+import Vue from 'vue';
+
+function getTouch(el: HTMLElement | Window, x: number, y: number) {
+  return {
+    identifier: Date.now(),
+    target: el,
+    pageX: x,
+    pageY: y,
+    clientX: x,
+    clientY: y,
+    radiusX: 2.5,
+    radiusY: 2.5,
+    rotationAngle: 10,
+    force: 0.5
+  };
+}
+
+// Trigger pointer/touch event
+export function trigger(wrapper: any, eventName: string, x = 0, y = 0, options: any = {}) {
+  const el = 'element' in wrapper ? wrapper.element : wrapper;
+  const touchList = options.touchList || [getTouch(el, x, y)];
+
+  if (options.x || options.y) {
+    touchList.push(getTouch(el, options.x, options.y));
+  }
+
+  const event = document.createEvent('CustomEvent');
+  event.initCustomEvent(eventName, true, true, {});
+
+  Object.assign(event, {
+    clientX: x,
+    clientY: y,
+    touches: touchList,
+    targetTouches: touchList,
+    changedTouches: touchList
+  });
+
+  el.dispatchEvent(event);
+}
+
+// simulate drag gesture
+export function triggerDrag(el: any, relativeX = 0, relativeY = 0): void {
+  let x = relativeX;
+  let y = relativeY;
+  let startX = 0;
+  let startY = 0;
+  if (relativeX < 0) {
+    startX = Math.abs(relativeX);
+    x = 0;
+  }
+  if (relativeY < 0) {
+    startY = Math.abs(relativeY);
+    y = 0;
+  }
+  trigger(el, 'touchstart', startX, startY);
+  trigger(el, 'touchmove', x / 4, y / 4);
+  trigger(el, 'touchmove', x / 3, y / 3);
+  trigger(el, 'touchmove', x / 2, y / 2);
+  trigger(el, 'touchmove', x, y);
+  trigger(el, 'touchend', x, y);
+}

+ 2 - 2
src/sites/mobile-taro/vue/project.private.config.json

@@ -25,8 +25,8 @@
           "scene": null
         },
         {
-          "name": "dentry/pages/picker/index",
-          "pathName": "dentry/pages/picker/index",
+          "name": "feedback/pages/noticebar/index",
+          "pathName": "feedback/pages/noticebar/index",
           "query": "",
           "scene": null
         },

+ 85 - 40
src/sites/mobile-taro/vue/src/feedback/pages/noticebar/index.vue

@@ -1,27 +1,59 @@
 <template>
   <div class="demo">
     <h2>默认用法</h2>
-    <nut-noticebar :text="text"></nut-noticebar>
+    <nut-noticebar
+      text="华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!"
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`"
+    ></nut-noticebar>
 
-    <h2>禁用滚动</h2>
+    <h2>滚动播放</h2>
+    <nut-noticebar
+      text="华为畅享9新品即将上市"
+      :scrollable="true"
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`"
+    >
+    </nut-noticebar>
+    <p />
     <nut-noticebar
       text="华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!"
       :scrollable="false"
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`"
     ></nut-noticebar>
 
     <h2>通告栏模式--关闭模式</h2>
-    <nut-noticebar :closeMode="true" @click="hello"
-      >华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI
-      WATCH等好礼,更多产品信息请持续关注!
+    <nut-noticebar :closeMode="true" @click="hello" :background="`rgba(251, 248, 220, 1)`" :color="`#D9500B`"
+      >华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!
     </nut-noticebar>
+    <p />
+    <nut-noticebar
+      :closeMode="true"
+      right-icon="circle-close"
+      @click="hello"
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`"
+      >华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!
+    </nut-noticebar>
+
     <h2>通告栏模式--链接模式</h2>
     <nut-noticebar
       left-icon="https://img13.360buyimg.com/imagetools/jfs/t1/72082/2/3006/1197/5d130c8dE1c71bcd6/e48a3b60804c9775.png"
-      :scrollable="false"
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`"
     >
       <a href="https://www.jd.com">京东商城</a>
     </nut-noticebar>
 
+    <h2>多行展示</h2>
+    <nut-noticebar
+      text="华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!"
+      wrapable
+      :background="`rgba(251, 248, 220, 1)`"
+      :color="`#D9500B`"
+    ></nut-noticebar>
+
     <h2>纵向滚动</h2>
     <div class="interstroll-list">
       <nut-noticebar
@@ -31,48 +63,61 @@
         :standTime="1000"
         @click="go"
         :closeMode="true"
+        :background="`rgba(251, 248, 220, 1)`"
+        :color="`#D9500B`"
       ></nut-noticebar>
     </div>
 
     <h2>纵向复杂滚动动画</h2>
-    <nut-noticebar
-      direction="vertical"
-      :list="horseLamp2"
-      :speed="10"
-      :standTime="2000"
-      :complexAm="true"
-    ></nut-noticebar>
-
+    <div class="interstroll-list">
+      <nut-noticebar
+        direction="vertical"
+        :list="horseLamp2"
+        :speed="10"
+        :standTime="2000"
+        :complexAm="true"
+        :background="`rgba(251, 248, 220, 1)`"
+        :color="`#D9500B`"
+      ></nut-noticebar>
+    </div>
     <h2>纵向自定义滚动内容</h2>
-    <nut-noticebar
-      direction="vertical"
-      :height="50"
-      :speed="10"
-      :standTime="1000"
-      :list="[]"
-      @close="go"
-    >
-      <div
-        class="custom-item"
-        :data-index="index"
-        v-for="(item, index) in horseLamp3"
-        style="height: 50px; line-height: 50px"
-        :key="index"
-        >{{ item }}</div
+    <div class="interstroll-list">
+      <nut-noticebar
+        direction="vertical"
+        :height="50"
+        :speed="10"
+        :standTime="1000"
+        :list="[]"
+        @close="go"
+        :background="`rgba(251, 248, 220, 1)`"
+        :color="`#D9500B`"
       >
-    </nut-noticebar>
+        <div
+          class="custom-item"
+          :data-index="index"
+          v-for="(item, index) in horseLamp3"
+          style="height: 50px; line-height: 50px"
+          :key="index"
+          >{{ item }}</div
+        >
+      </nut-noticebar>
+    </div>
 
     <h2>纵向自定义右侧图标</h2>
-    <nut-noticebar
-      direction="vertical"
-      :list="horseLamp1"
-      :speed="10"
-      :standTime="1000"
-    >
-      <template v-slot:rightIcon>
-        <nut-icon name="fabulous" color="#f0250f"> </nut-icon>
-      </template>
-    </nut-noticebar>
+    <div class="interstroll-list">
+      <nut-noticebar
+        direction="vertical"
+        :list="horseLamp1"
+        :speed="10"
+        :standTime="1000"
+        :background="`rgba(251, 248, 220, 1)`"
+        :color="`#D9500B`"
+      >
+        <template v-slot:rightIcon>
+          <nut-icon name="fabulous" color="#f0250f"> </nut-icon>
+        </template>
+      </nut-noticebar>
+    </div>
   </div>
 </template>