Browse Source

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

Drjnigfubo 4 years ago
parent
commit
8ecf357b32

+ 18 - 0
CHANGELOG.md

@@ -1,4 +1,22 @@
 
+## v3.1.2
+
+`2021-07-31`
+
+* :zap: feat: 新增选项卡 Tab 组件 @zhenyulei
+* :zap: feat: 手势滑动组件 Swipe @richard1015
+* :zap: feat: Address 地址组件规范10.0升级 @szg2008
+* :zap: feat(icon): fontClassName prop @richard1015
+* :zap: feat(cell): add round-radius prop #547 @richard1015
+* :zap: chore: 修改组件分类及排序 (#540) @ninidesign
+* :bug: fix(calender): 小程序场景无法滑动 bug (#534) @guoxiaoxiao
+* :bug: fix: taro的h5环境下shortpassword聚焦事件修复 (#541) @Drjingfubo
+* :bug: fix(collapse ): taro H5 环境适配 (#546) @Ymm0008
+* :bug: fix: run add comp script @richard1015
+* :bug: fix(drag): taro h5 touch bug (#549) @Drjingfubo
+* :bug: fix(notify): props msg type error @richard1015
+* :zap: docs: 官网轮播图片变更、gitee log添加、官网案例效果优化、版本显示  @ninidesign @richard1015
+
 ## v3.1.0
 `2021-07-12`
 ### 新特性

+ 1 - 1
jd/createComponentMode.js

@@ -80,7 +80,7 @@ function init() {
         type: 'input',
         name: 'sort',
         message:
-          '请选择组件分类(输入编号):1布局组件,2操作反馈,3基础组件,4导航组件,5数据录入,6业务组件',
+          '请选择组件分类(输入编号):1布局组件,2操作反馈,3基础组件,4导航组件,5数据录入,6特色组件',
         validate(value) {
           const pass = /^[1-6]$/.test(value);
           if (pass) {

+ 1 - 1
jd/generate-nutui.js

@@ -27,7 +27,7 @@ let installFunction = `function install(app: App) {
 }`;
 let fileStr = `${importStr}
 ${installFunction}
-export { ${packages.join(',')}  };
+export { install, ${packages.join(',')}  };
 export default { install, version:'${package.version}'};`;
 fs.outputFile(
   path.resolve(__dirname, '../src/packages/nutui.vue.ts'),

+ 2 - 2
package.json

@@ -1,6 +1,6 @@
 {
   "name": "@nutui/nutui",
-  "version": "3.1.1",
+  "version": "3.1.2",
   "description": "京东风格的轻量级移动端 Vue2、Vue3 组件库(支持小程序开发)",
   "main": "dist/nutui.umd.js",
   "module": "dist/nutui.es.js",
@@ -94,7 +94,7 @@
     "pinyin": "^2.10.2",
     "prettier": "^2.0.0",
     "standard-version": "^9.3.0",
-    "swiper": "5.3.6",
+    "swiper": "6.5.1",
     "transliteration": "^2.2.0",
     "ts-jest": "^26.5.5",
     "typescript": "^4.1.5",

+ 12 - 1
src/config.json

@@ -682,7 +682,7 @@
       ]
     },
     {
-      "name": "业务组件",
+      "name": "特色组件",
       "enName": "business",
       "packages": [
         {
@@ -695,6 +695,17 @@
           "sort": 1,
           "show": true,
           "author": "yangxiaolu3"
+        },
+        {
+          "version": "3.0.0",
+          "taro": true,
+          "name": "Barrage",
+          "type": "component",
+          "cName": "弹幕组件",
+          "desc": "用于视频弹幕等展示",
+          "sort": 2,
+          "show": true,
+          "author": "Ymm0008"
         }
       ]
     }

+ 15 - 7
src/packages/__VUE/address/index.taro.vue

@@ -422,13 +422,14 @@ export default create({
     const selectedExist = (item: RegionData) => {
       const copyExistAdd = props.existAddress as AddressList[];
       let prevExistAdd = {};
-
-      copyExistAdd.forEach((list, index) => {
-        if (list && (list as AddressList).selectedAddress) {
-          prevExistAdd = list;
-        }
-        (list as AddressList).selectedAddress = false;
-      });
+      if (copyExistAdd != null) {
+        copyExistAdd.forEach((list, index) => {
+          if (list && (list as AddressList).selectedAddress) {
+            prevExistAdd = list;
+          }
+          (list as AddressList).selectedAddress = false;
+        });
+      }
 
       item.selectedAddress = true;
 
@@ -620,6 +621,13 @@ export default create({
       }
     );
 
+    watch(
+      () => props.type,
+      (value) => {
+        privateType.value = value;
+      }
+    );
+
     return {
       classes,
       showPopup,

+ 15 - 6
src/packages/__VUE/address/index.vue

@@ -392,12 +392,14 @@ export default create({
       const copyExistAdd = props.existAddress as AddressList[];
       let prevExistAdd = {};
 
-      copyExistAdd.forEach((list, index) => {
-        if (list && (list as AddressList).selectedAddress) {
-          prevExistAdd = list;
-        }
-        (list as AddressList).selectedAddress = false;
-      });
+      if (copyExistAdd != null) {
+        copyExistAdd.forEach((list, index) => {
+          if (list && (list as AddressList).selectedAddress) {
+            prevExistAdd = list;
+          }
+          (list as AddressList).selectedAddress = false;
+        });
+      }
 
       item.selectedAddress = true;
 
@@ -589,6 +591,13 @@ export default create({
       }
     );
 
+    watch(
+      () => props.type,
+      (value) => {
+        privateType.value = value;
+      }
+    );
+
     return {
       showPopup,
       privateType,

+ 3 - 1
src/packages/__VUE/address/transformData.ts

@@ -14,7 +14,9 @@ export const transformData = (regionData: any[]) => {
 
   regionData = regionData.map((item: any) => {
     if (!item.name) return new Error('the data must includes `name` props');
-    let code = pinyin(item.name);
+    let code = pinyin(item.name, {
+      style: pinyin.STYLE_NORMAL
+    });
     return {
       ...item,
       firstCode: code[0][0].charAt(0).toUpperCase()

+ 57 - 0
src/packages/__VUE/barrage/demo.vue

@@ -0,0 +1,57 @@
+<template>
+  <div class="demo">
+    <h2>基础用法</h2>
+    <nut-cell>
+      <nut-barrage ref="danmu" :danmu="list"></nut-barrage>
+    </nut-cell>
+    <div class="test">
+      <nut-input label="文本" v-model="inputVal" />
+      <nut-button type="primary" @click="addDanmu">添加</nut-button>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { ref } from 'vue';
+import { createComponent } from '../../utils/create';
+const { createDemo } = createComponent('barrage');
+export default createDemo({
+  props: {},
+  setup() {
+    const inputVal = ref<any>('');
+    const danmu = ref<any>(null);
+    let list = ref([
+      '画美不看',
+      '不明觉厉',
+      '喜大普奔',
+      '男默女泪',
+      '累觉不爱',
+      '爷青结'
+    ]);
+    function addDanmu() {
+      danmu.value.add(inputVal.value);
+    }
+    return {
+      inputVal,
+      danmu,
+      list,
+      addDanmu
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.nut-cell,
+.nut-barrage {
+  padding: 20px 0;
+  height: 120px;
+}
+.test {
+  .nut-input {
+    width: 80%;
+  }
+  display: flex;
+  justify-content: space-between;
+}
+</style>

+ 63 - 0
src/packages/__VUE/barrage/doc.md

@@ -0,0 +1,63 @@
+# Barrage 组件
+
+### 介绍
+
+用于话语和词组的轮播展示,适用于视频中或其他类似需求中。
+
+### 安装
+
+``` javascript
+import { createApp } from 'vue';
+import { Barrage } from '@nutui/nutui';
+
+const app = createApp();
+app.use(Barrage);
+
+```
+
+## 代码演示
+
+### 基础用法1
+
+`Icon` 的 `name` 属性支持传入图标名称或图片链接。
+
+```html
+<nut-barrage ref="danmu" :danmu="list"></nut-barrage>
+```
+``` javascript
+  setup() {
+    const inputVal = ref<any>('');
+    const danmu = ref<any>(null);
+    let list = ref(["画美不看", "不明觉厉", "喜大普奔", "男默女泪", "累觉不爱", "爷青结"]); 
+    function addDanmu() {
+      danmu.value.add(inputVal.value);
+    }
+    return {
+      inputVal,
+      danmu,
+      list,
+      addDanmu
+    };
+  }
+```
+
+
+
+## API
+
+### Props
+
+| 参数         | 说明                             | 类型   | 默认值           |
+|--------------|----------------------------------|--------|------------------|
+| danmu         | 弹幕列表数据               | Array | []              |
+| frequency        | 可视区域内每个弹幕出现的时间间隔                         | Number | 200               |
+| speeds         | 每个弹幕的滚动时间 | Number |  2000               |
+| rows  | 弹幕行数,分几行展示     | Number | 1 |
+| top  | 弹幕垂直距离    | Number | 10 |
+| loop  | 是否循环播放     | Boolean | true |
+
+### Events
+
+| 事件名 | 说明           | 回调参数     |
+|--------|----------------|--------------|
+| add  | 添加数据 | - |

+ 47 - 0
src/packages/__VUE/barrage/index.scss

@@ -0,0 +1,47 @@
+.nut-barrage {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  .dmitem {
+    display: block;
+    position: absolute;
+    right: 0;
+    padding: 5px 25px;
+    border-radius: 50px;
+    font-size: 12px;
+    text-align: center;
+    color: #fff;
+    white-space: pre;
+    transform: translateX(100%);
+    background: linear-gradient(
+      to right,
+      rgba(26, 25, 25, 0.15),
+      rgba(0, 0, 0, 0.2)
+    );
+    &.move {
+      will-change: transform;
+      animation-name: moving;
+      animation-timing-function: linear;
+      animation-play-state: running;
+    }
+  }
+  @keyframes moving {
+    from {
+      transform: translateX(100%);
+    }
+    to {
+      transform: translateX(var(--move-distance));
+    }
+  }
+  @-webkit-keyframes moving {
+    from {
+      -webkit-transform: translateX(100%);
+    }
+    to {
+      -webkit-transform: translateX(var(--move-distance));
+    }
+  }
+}

+ 138 - 0
src/packages/__VUE/barrage/index.taro.vue

@@ -0,0 +1,138 @@
+<template>
+  <view class="dmBody" :class="classes">
+    <view class="dmContainer" id="dmContainer"></view>
+    <!-- <view v-for="(item, index) of danmuList" :key="'danmu'+index" class="dmitem">
+      {{item}}
+    </view> -->
+  </view>
+</template>
+<script lang="ts">
+import {
+  computed,
+  onMounted,
+  ref,
+  reactive,
+  toRefs,
+  watch,
+  nextTick
+} from 'vue';
+import Taro from '@tarojs/taro';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('barrage');
+
+export default create({
+  props: {
+    danmu: {
+      type: Array,
+      default: () => []
+    },
+    frequency: {
+      type: Number,
+      default: 200
+    },
+    speeds: {
+      type: Number,
+      default: 2000
+    },
+    rows: {
+      type: Number,
+      default: 3
+    },
+    top: {
+      type: Number,
+      default: 10
+    },
+    loop: {
+      type: Boolean,
+      default: true
+    }
+  },
+  emits: ['click'],
+
+  setup(props, { emit }) {
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true
+      };
+    });
+
+    let timer: number = 0;
+    const danmuList = ref<any[]>(props.danmu);
+    const rows = ref<number>(props.rows);
+    const top = ref<number>(props.top);
+    const index = ref<number>(0);
+    const speeds = props.speeds;
+
+    onMounted(() => {
+      run();
+    });
+
+    watch(
+      () => props.danmu,
+      (newValue, oldVlaue) => {
+        danmuList.value = [...newValue];
+      }
+    );
+
+    const add = (word: string) => {
+      const _index = index.value % danmuList.value.length;
+      danmuList.value.splice(_index, 0, word);
+    };
+
+    const run = () => {
+      clearInterval(timer);
+      timer = 0;
+      timer = setInterval(() => {
+        play();
+        run();
+      }, props.frequency);
+    };
+
+    const play = () => {
+      const query = Taro.createSelectorQuery();
+      let dmContainer: any = document.getElementById('dmContainer');
+      const _index = props.loop
+        ? index.value % danmuList.value.length
+        : index.value;
+      let el: any = document.createElement(`view`);
+      el.innerHTML = danmuList.value[_index] as string;
+      el.classList.add('dmitem');
+      dmContainer.appendChild(el);
+
+      let domList: Array<any> = [];
+      query.selectAll('.dmitem').boundingClientRect((recs: any) => {
+        domList = recs;
+      });
+
+      nextTick(() => {
+        query
+          .select('.dmBody')
+          .boundingClientRect((rec) => {
+            let danmuCWidth = rec.width;
+            // let width = domList[_index]['width'];
+            let height = domList[_index]['height'];
+            el.classList.add('move');
+            el.style.animationDuration = `${speeds}ms`;
+            el.style.top = (_index % rows.value) * (height + top.value) + 'px';
+            // el.style.width = width + 20 + 'px';
+            el.style.width = 'auto';
+            // el.style.left = "-"+(_index % rows.value) + 'px';
+            el.style.setProperty('--move-distance', `-${danmuCWidth}px`);
+            el.dataset.index = `${_index}`;
+            el.addEventListener('animationend', () => {
+              dmContainer.removeChild(el);
+            });
+            index.value++;
+          })
+          .exec();
+      });
+    };
+    return { classes, danmuList, add };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 128 - 0
src/packages/__VUE/barrage/index.vue

@@ -0,0 +1,128 @@
+<template>
+  <view ref="dmBody" :class="classes">
+    <view ref="dmContainer" class="dmContainer"></view>
+    <!-- <view v-for="(item, index) of danmuList" :key="'danmu'+index" class="dmitem">
+      {{item}}
+    </view> -->
+  </view>
+</template>
+<script lang="ts">
+import {
+  computed,
+  onMounted,
+  ref,
+  reactive,
+  toRefs,
+  watch,
+  nextTick
+} from 'vue';
+import { createComponent } from '../../utils/create';
+const { componentName, create } = createComponent('barrage');
+
+export default create({
+  props: {
+    danmu: {
+      type: Array,
+      default: () => []
+    },
+    frequency: {
+      type: Number,
+      default: 200
+    },
+    speeds: {
+      type: Number,
+      default: 2000
+    },
+    rows: {
+      type: Number,
+      default: 3
+    },
+    top: {
+      type: Number,
+      default: 10
+    },
+    loop: {
+      type: Boolean,
+      default: true
+    }
+  },
+  emits: ['click'],
+
+  setup(props, { emit }) {
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true
+      };
+    });
+
+    let dmBody = ref<HTMLDivElement>(document.createElement('div'));
+    let dmContainer = ref<HTMLDivElement>(document.createElement('div'));
+
+    let timer: number = 0;
+    const danmuList = ref<any[]>(props.danmu);
+    const rows = ref<number>(props.rows);
+    const top = ref<number>(props.top);
+    const index = ref<number>(0);
+    const speeds = props.speeds;
+    const danmuCWidth = ref(0);
+
+    onMounted(() => {
+      danmuCWidth.value = dmBody.value.offsetWidth;
+      run();
+    });
+
+    watch(
+      () => props.danmu,
+      (newValue, oldVlaue) => {
+        danmuList.value = [...newValue];
+      }
+    );
+
+    const add = (word: string) => {
+      const _index = index.value % danmuList.value.length;
+      danmuList.value.splice(_index, 0, word);
+    };
+
+    const run = () => {
+      clearInterval(timer);
+      timer = 0;
+      timer = setInterval(() => {
+        play();
+        run();
+      }, props.frequency);
+    };
+
+    const play = () => {
+      const _index = props.loop
+        ? index.value % danmuList.value.length
+        : index.value;
+      let el = document.createElement(`view`);
+      el.innerHTML = danmuList.value[_index] as string;
+      el.classList.add('dmitem');
+      dmContainer.value.appendChild(el);
+
+      nextTick(() => {
+        const width = el.offsetWidth;
+        const height = el.offsetHeight;
+        el.classList.add('move');
+        el.style.animationDuration = `${speeds}ms`;
+        el.style.top = (_index % rows.value) * (height + top.value) + 'px';
+        el.style.width = width + 20 + 'px';
+        // el.style.left = "-"+(_index % rows.value) + 'px';
+        el.style.setProperty('--move-distance', `-${danmuCWidth.value}px`);
+        el.dataset.index = `${_index}`;
+        el.addEventListener('animationend', () => {
+          dmContainer.value.removeChild(el);
+        });
+        index.value++;
+      });
+    };
+    return { classes, danmuList, dmBody, dmContainer, add };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 3 - 2
src/packages/__VUE/tab/demo.vue

@@ -16,9 +16,9 @@
       >
     </nut-tab>
 
-    <h2>defaultIndex设置默认显示tab</h2>
+    <h2>defaultIndex设置默认显示tab,iconType为half时切换选中icon样式</h2>
     <h2>switchTab监听切换tab返回事件</h2>
-    <nut-tab :default-index="1" @switch-tab="switchTab">
+    <nut-tab :default-index="1" @switch-tab="switchTab" icon-type="half">
       <nut-tab-panel tab-title="全部"
         ><p class="content">这里是页签全部内容</p></nut-tab-panel
       >
@@ -118,6 +118,7 @@
       :animated-time="500"
       :default-index="2"
       scrollType="scroll"
+      icon-type="half"
     >
       <nut-tab-panel tab-title="页签一"
         ><p class="content">这里是页签一内容</p></nut-tab-panel

+ 83 - 2
src/packages/__VUE/tab/index.scss

@@ -1,5 +1,5 @@
 .nutui-tab {
-  .tab-title {
+  .tab-title-scroll {
     width: 100%;
     height: 46px;
     overflow-x: scroll;
@@ -7,6 +7,15 @@
     flex-wrap: nowrap;
     scroll-behavior: smooth;
     background: #f5f5f5;
+  }
+  .tab-title {
+    width: 100%;
+    height: 46px;
+    display: flex;
+    flex-direction: row;
+    flex-wrap: nowrap;
+    scroll-behavior: smooth;
+    background: #f5f5f5;
     position: relative;
     &::-webkit-scrollbar {
       display: none;
@@ -45,6 +54,34 @@
       }
     }
   }
+  .tab-title-scroll {
+    overflow-x: scroll;
+  }
+  .half {
+    .nut-tab-active {
+      color: #1a1a1a;
+      font-weight: bold;
+      font-size: 16px;
+      position: relative;
+      &::after {
+        content: '';
+        display: none;
+      }
+      .world {
+        position: relative;
+        &::after {
+          content: '';
+          position: absolute;
+          right: -8px;
+          bottom: -5px;
+          width: 11px;
+          height: 10px;
+          background-image: url('https://img12.360buyimg.com/imagetools/jfs/t1/197875/38/105/620/60ffcd30E34877e77/54c3dd9fe0a5ab76.png');
+          background-size: 100% 100%;
+        }
+      }
+    }
+  }
   .tab-swiper {
     overflow: hidden;
     display: block;
@@ -57,12 +94,19 @@
 .vertical-tab {
   display: flex;
   height: 175px;
+  .tab-title-scroll {
+    width: 100px;
+    height: 175px;
+    display: flex;
+    flex-wrap: nowrap;
+    scroll-behavior: smooth;
+    background: #f5f5f5;
+  }
   .tab-title {
     width: 100px;
     height: 100%;
     display: flex;
     flex-direction: column;
-    overflow-y: scroll;
     &::-webkit-scrollbar {
       display: none;
     }
@@ -97,6 +141,34 @@
       }
     }
   }
+  .tab-title-scroll {
+    overflow-y: scroll;
+  }
+  .half {
+    .nut-tab-active {
+      color: #1a1a1a;
+      font-weight: bold;
+      font-size: 16px;
+      position: relative;
+      &::after {
+        content: '';
+        display: none;
+      }
+      .world {
+        position: relative;
+        &::after {
+          content: '';
+          position: absolute;
+          right: -15px;
+          bottom: -5px;
+          width: 11px;
+          height: 10px;
+          background-image: url('https://img12.360buyimg.com/imagetools/jfs/t1/197875/38/105/620/60ffcd30E34877e77/54c3dd9fe0a5ab76.png');
+          background-size: 100% 100%;
+        }
+      }
+    }
+  }
   .tab-swiper {
     overflow: hidden;
     display: block;
@@ -106,3 +178,12 @@
     box-sizing: border-box;
   }
 }
+
+.horize {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: nowrap;
+}
+.content {
+  padding: 10px;
+}

+ 90 - 49
src/packages/__VUE/tab/index.taro.vue

@@ -1,31 +1,42 @@
 <template>
   <view :class="[direction === 'vertical' ? 'vertical-tab' : 'nutui-tab']">
-    <view class="tab-title" ref="navlist">
-      <view
-        :class="[
-          'tab-title-box',
-          { 'nut-tab-active': activeIndex == index },
-          { 'tab-title-box-scroll': scrollType == 'scroll' }
-        ]"
-        v-for="(item, index) in titles"
-        :key="index"
-        @click="switchTitle(index, $event)"
-      >
-        {{ item.title }}
-        <TabTitle v-bind:slots="item.content" v-if="item.content"></TabTitle>
+    <scroll-view
+      :scroll-x="!scrollYDirection"
+      :scroll-y="scrollYDirection"
+      :scroll-left="scrollLeft"
+      :scroll-top="scrollTop"
+      class="tab-title-scroll"
+      :scroll-with-animation="true"
+    >
+      <view :class="['tab-title', randomTitleClass, iconType]">
+        <view
+          :class="[
+            'tab-title-box',
+            randomClass,
+            { 'nut-tab-active': activeIndex == index },
+            { 'tab-title-box-scroll': scrollType == 'scroll' }
+          ]"
+          v-for="(item, index) in titles"
+          :key="index"
+          @click="switchTitle(index, $event)"
+        >
+          <span class="world">{{ item.title }}</span>
+          <TabTitle v-bind:slots="item.content" v-if="item.content"></TabTitle>
+        </view>
+        <view class="underline"></view>
       </view>
-      <view class="underline"></view>
-    </view>
+    </scroll-view>
     <nut-swiper
-      :init-page="defaultIndex"
+      :current="activeIndex"
       :pagination-visible="false"
       :duration="animatedTime"
       pagination-color="#426543"
-      @change="changeTab"
+      @change="(current) => changeTab(current)"
       ref="nutuiSwiper"
       :touchable="!noSwiping"
-      :direction="direction"
+      :vertical="scrollYDirection"
       class="tab-swiper"
+      :circular="true"
     >
       <slot></slot>
     </nut-swiper>
@@ -34,8 +45,9 @@
 <script lang="ts">
 import { PropType, reactive, ref, onMounted, watch, VNode } from 'vue';
 import { createComponent } from '../../utils/create';
-import tabpanel from '../../__VUE/tabpanel/index.vue';
+import tabpanel from '../../__VUE/tabpanel/index.taro.vue';
 const { create } = createComponent('tab');
+import Taro from '@tarojs/taro';
 import TabTitle from './tabTitle';
 type TabDirection = 'horizontal' | 'vertical';
 
@@ -61,7 +73,7 @@ export default create({
     },
     direction: {
       type: String as PropType<TabDirection>,
-      default: 'horizontal'
+      default: 'false'
     },
     noSwiping: {
       type: Boolean,
@@ -70,6 +82,10 @@ export default create({
     scrollType: {
       type: String,
       default: 'flex'
+    },
+    iconType: {
+      type: String,
+      default: 'all'
     }
   },
   components: {
@@ -78,53 +94,69 @@ export default create({
   setup(props, ctx) {
     const titles: Array<DataTitle> = reactive([]);
     const isLock = ref(false);
+    const scrollLeft = ref('0px');
+    const scrollTop = ref('0px');
     const activeIndex = ref(props.defaultIndex);
     const navlist = ref<null | HTMLElement>(null);
     const nutuiSwiper = ref(null);
+    const arr = ref([]);
+    const scrollYDirection = ref(props.direction === 'vertical');
+    const randomClass = ref('tab-title-boxs-' + createHash());
+    const randomTitleClass = ref('tab-title-' + createHash());
     // 生成随机的id
     function createHash() {
       return Array.from(Array(10), () =>
         Math.floor(Math.random() * 36).toString(36)
       ).join('');
     }
-
     const swiperClassName = ref('swiper-' + createHash());
     //title点击后居中显示
     function centerTitle(index: number) {
-      if (navlist.value) {
-        const currEle = navlist.value.querySelectorAll('.tab-title-box')[
-          index
-        ] as HTMLElement;
-        if (props.direction === 'vertical') {
-          const currTitleTop = navlist.value.offsetTop;
-          const currTop = currEle.offsetTop;
-          const currHeight = currEle.offsetHeight;
-          const tapHeight = navlist.value.offsetHeight;
-          navlist.value.scroll(
-            0,
-            currTop - currTitleTop - tapHeight / 2 + currHeight / 2
-          );
-        } else {
-          const currLeft = currEle.offsetLeft;
-          const currWidth = currEle.offsetWidth;
-          const tapWidth = navlist.value.offsetWidth;
-          navlist.value.scroll(currLeft - tapWidth / 2 + currWidth / 2, 0);
-        }
+      let navlistValueTop = 0;
+      let navlistValueHeight = 0;
+      let navlistValuewidth = 0;
+      const query = Taro.createSelectorQuery();
+      if (props.direction === 'vertical') {
+        query
+          .select(`.${randomTitleClass.value}`)
+          .boundingClientRect()
+          .selectAll(`.${randomClass.value}`)
+          .boundingClientRect()
+          .exec((rects) => {
+            let navlistValueTop = rects[0].top;
+            let navlistValueHeight = rects[0].height;
+            const currTop = rects[1][index].top;
+            const currHeight = rects[1][index].height;
+            scrollTop.value =
+              currTop -
+              navlistValueTop -
+              navlistValueHeight / 2 +
+              currHeight / 2 +
+              'px';
+          });
+      } else {
+        query
+          .select(`.${randomTitleClass.value}`)
+          .boundingClientRect()
+          .selectAll(`.${randomClass.value}`)
+          .boundingClientRect()
+          .exec((rects) => {
+            let navlistValuewidth = rects[0].width;
+            const currLeft = rects[1][index].left;
+            const currWidth = rects[1][index].width;
+            scrollLeft.value =
+              currLeft - navlistValuewidth / 2 + currWidth / 2 + 'px';
+          });
       }
     }
-    const changeTab = (index: number) => {
-      console.log(index);
-      activeIndex.value = index;
-      centerTitle(index);
-
-      //
+    const changeTab = (current) => {
+      activeIndex.value = current.detail.current;
+      centerTitle(current.detail.current);
     };
     //切换tab
     function switchTitle(index: number) {
       activeIndex.value = index;
       centerTitle(index);
-      console.log(nutuiSwiper.value);
-      nutuiSwiper.value.to(index);
     }
     function initTitle() {
       titles.length = 0;
@@ -151,6 +183,9 @@ export default create({
     }
     onMounted(() => {
       initTitle();
+      let arrnew = [];
+      for (let i = 0; i < 100; i++) arrnew.push(i);
+      arr.value = arrnew;
     });
     watch(
       () => (ctx.slots.default ? ctx.slots.default() : ''),
@@ -165,7 +200,13 @@ export default create({
       activeIndex,
       switchTitle,
       changeTab,
-      nutuiSwiper
+      nutuiSwiper,
+      scrollLeft,
+      scrollTop,
+      arr,
+      randomClass,
+      scrollYDirection,
+      randomTitleClass
     };
   }
 });

+ 6 - 2
src/packages/__VUE/tab/index.vue

@@ -1,6 +1,6 @@
 <template>
   <view :class="[direction === 'vertical' ? 'vertical-tab' : 'nutui-tab']">
-    <view class="tab-title" ref="navlist">
+    <view :class="['tab-title', iconType, 'tab-title-scroll']" ref="navlist">
       <view
         :class="[
           'tab-title-box',
@@ -11,7 +11,7 @@
         :key="index"
         @click="switchTitle(index, $event)"
       >
-        {{ item.title }}
+        <span class="world">{{ item.title }}</span>
         <TabTitle v-bind:slots="item.content" v-if="item.content"></TabTitle>
       </view>
       <view class="underline"></view>
@@ -70,6 +70,10 @@ export default create({
     scrollType: {
       type: String,
       default: 'flex'
+    },
+    iconType: {
+      type: String,
+      default: 'all'
     }
   },
   components: {

+ 26 - 0
src/packages/__VUE/tabpanel/index.taro.vue

@@ -0,0 +1,26 @@
+<template>
+  <nut-swiper-item>
+    <slot></slot>
+  </nut-swiper-item>
+</template>
+<script lang="ts">
+import { createComponent } from '../../utils/create';
+const { create } = createComponent('tab-panel');
+
+export default create({
+  props: {
+    tabTitle: {
+      type: String,
+      default: ''
+    }
+  },
+
+  setup(props, ctx) {
+    //
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 1 - 1
src/sites/doc/views/Index.vue

@@ -156,7 +156,7 @@ export default defineComponent({
         padding: 10px 25px;
         height: 100%;
         cursor: pointer;
-        font-size: 20px;
+        font-size: 16px;
         color: #323232;
         text-align: center;
         border-radius: 4px;

+ 2 - 2
src/sites/doc/views/Main.vue

@@ -157,8 +157,8 @@ import Footer from '@/sites/doc/components/Footer.vue';
 import router from '../router';
 import { RefData } from '@/sites/assets/util/ref';
 import { ApiService } from '@/sites/service/ApiService';
-import 'swiper/css/swiper.min.css';
-import Swiper from 'swiper/js/swiper.min.js';
+import 'swiper/swiper.min.css';
+import Swiper from 'swiper/swiper-bundle.min.js';
 export default defineComponent({
   name: 'main',
   components: {

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

@@ -0,0 +1,23 @@
+{
+  "condition": {
+    "plugin": {
+      "list": []
+    },
+    "game": {
+      "list": []
+    },
+    "gamePlugin": {
+      "list": []
+    },
+    "miniprogram": {
+      "list": [
+        {
+          "name": "business/pages/barrage/index",
+          "pathName": "business/pages/barrage/index",
+          "query": "",
+          "scene": null
+        }
+      ]
+    }
+  }
+}

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

@@ -58,7 +58,7 @@ export default {
     },
     {
       root: 'business',
-      pages: ['pages/address/index']
+      pages: ['pages/address/index', 'pages/barrage/index']
     }
   ],
   window: {

+ 1 - 0
src/sites/mobile-taro/vue/src/business/pages/address/index.vue

@@ -124,6 +124,7 @@ export default {
   setup() {
     const address = reactive({
       province: [
+        { id: 10, name: '安徽' },
         { id: 1, name: '北京' },
         { id: 2, name: '广西' },
         { id: 3, name: '江西' },

+ 3 - 0
src/sites/mobile-taro/vue/src/business/pages/barrage/index.config.js

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

+ 55 - 0
src/sites/mobile-taro/vue/src/business/pages/barrage/index.vue

@@ -0,0 +1,55 @@
+<template>
+  <div class="demo">
+    <h2>基础用法</h2>
+    <nut-cell>
+      <nut-barrage ref="danmu" :danmu="list"></nut-barrage>
+    </nut-cell>
+    <div class="test">
+      <nut-input label="文本" v-model="inputVal" />
+      <nut-button type="primary" @click="addDanmu">添加</nut-button>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { ref } from 'vue';
+export default {
+  props: {},
+  setup() {
+    const inputVal = ref<any>('');
+    const danmu = ref<any>(null);
+    let list = ref([
+      '画美不看',
+      '不明觉厉',
+      '喜大普奔',
+      '男默女泪',
+      '累觉不爱',
+      '爷青结'
+    ]);
+    function addDanmu() {
+      danmu.value.add(inputVal.value);
+    }
+    return {
+      inputVal,
+      danmu,
+      list,
+      addDanmu
+    };
+  }
+};
+</script>
+
+<style lang="scss">
+.nut-cell,
+.nut-barrage {
+  padding: 20px 0;
+  height: 120px;
+}
+.test {
+  display: flex;
+  justify-content: space-between;
+  .nut-input {
+    width: 80%;
+  }
+}
+</style>

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

@@ -16,9 +16,9 @@
       >
     </nut-tab>
 
-    <h2>defaultIndex设置默认显示tab</h2>
+    <h2>defaultIndex设置默认显示tab,iconType为half时切换选中icon样式</h2>
     <h2>switchTab监听切换tab返回事件</h2>
-    <nut-tab :default-index="1" @switch-tab="switchTab">
+    <nut-tab :default-index="1" @switch-tab="switchTab" icon-type="half">
       <nut-tab-panel tab-title="全部"
         ><p class="content">这里是页签全部内容</p></nut-tab-panel
       >
@@ -49,22 +49,6 @@
       >
     </nut-tab>
 
-    <h2> 禁止tab内容滑动</h2>
-    <nut-tab :no-swiping="true">
-      <nut-tab-panel tab-title="全部"
-        ><p class="content">这里是页签全部内容</p></nut-tab-panel
-      >
-      <nut-tab-panel tab-title="待付款"
-        ><p class="content">这里是页签待付款内容</p></nut-tab-panel
-      >
-      <nut-tab-panel tab-title="待收获"
-        ><p class="content">这里是页签待收获内容</p></nut-tab-panel
-      >
-      <nut-tab-panel tab-title="已完成"
-        ><p class="content">这里是页签已完成内容</p></nut-tab-panel
-      >
-    </nut-tab>
-
     <h2>
       设置scrollType="scroll",标签栏可以在水平方向上滚动,切换时会自动将当前标签居中。</h2
     >

+ 4 - 2
src/sites/mobile/App.vue

@@ -38,9 +38,11 @@ export default defineComponent({
         const { hash } = window.top.location;
         if (!isMobile && route.hash != hash) {
           // window.top.location.replace(`${origin}${pathname}#/${route.hash}`);
-          title.value = route.name as string;
+          title.value =
+            (route?.meta?.ComponentName as string) || (route.name as string);
         } else {
-          title.value = route.name as string;
+          title.value =
+            (route?.meta?.ComponentName as string) || (route.name as string);
         }
       },
       {

+ 23 - 2
src/sites/mobile/router.ts

@@ -1,6 +1,7 @@
 import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
 import Index from './components/Index.vue';
 import IndexTaro from './components/IndexTaro.vue';
+import { nav } from '../../config.json';
 const routes: Array<RouteRecordRaw> = [
   {
     path: '/',
@@ -20,6 +21,20 @@ const routes: Array<RouteRecordRaw> = [
 //   });
 // });
 
+const findComponentName = (name: string) => {
+  for (const key in nav) {
+    if (Object.prototype.hasOwnProperty.call(nav, key)) {
+      const element = nav[key];
+      let idx = element.packages.findIndex(
+        (i) => i.name.toLowerCase() === name
+      );
+      if (idx !== -1) {
+        return element.packages[idx].name;
+      }
+    }
+  }
+};
+
 /** vite */
 const modulesPage = import.meta.glob('/src/packages/__VUE/**/demo.vue');
 
@@ -28,13 +43,19 @@ for (const path in modulesPage) {
   routes.push({
     path: '/' + name,
     component: modulesPage[path],
-    name
+    name,
+    meta: {
+      ComponentName: findComponentName(name)
+    }
   });
 
   routes.push({
     path: '/' + name + '-taro',
     component: IndexTaro,
-    name: name + '-taro'
+    name: name + '-taro',
+    meta: {
+      ComponentName: findComponentName(name)
+    }
   });
 }