浏览代码

Merge pull request #497 from Ymm0008/next

feat: taro collapse
love_forever 4 年之前
父节点
当前提交
88541ffbda

+ 1 - 1
package.json

@@ -72,7 +72,7 @@
   "devDependencies": {
     "@commitlint/cli": "^10.0.0",
     "@commitlint/config-conventional": "^10.0.0",
-    "@tarojs/taro": "^3.2.10",
+    "@tarojs/taro": "3.3.0-alpha.6",
     "@types/jest": "^26.0.22",
     "@types/node": "^14.14.31",
     "@vitejs/plugin-legacy": "^1.4.0",

+ 2 - 0
src/config.json

@@ -95,6 +95,7 @@
         },
         {
           "name": "Collapse",
+          "taro": true,
           "sort": 2,
           "cName": "折叠面板",
           "type": "component",
@@ -104,6 +105,7 @@
         },
         {
           "name": "CollapseItem",
+          "taro": true,
           "sort": 3,
           "cName": "折叠面板-item",
           "type": "component",

+ 0 - 2
src/packages/__VUE/collapse/demo.vue

@@ -93,5 +93,3 @@ export default createDemo({
   }
 });
 </script>
-
-<style lang="scss" scoped></style>

+ 101 - 0
src/packages/__VUE/collapse/index.taro.vue

@@ -0,0 +1,101 @@
+<template>
+  <view>
+    <slot></slot>
+  </view>
+</template>
+<script lang="ts">
+import { provide } from 'vue';
+import { createComponent } from '@/packages/utils/create';
+import collapseitem from '@/packages/__VUE/collapseitem/index.taro.vue';
+const { create } = createComponent('collapse');
+export default create({
+  children: [collapseitem],
+  props: {
+    active: {
+      type: [String, Number, Array]
+    },
+    accordion: {
+      type: Boolean
+    },
+    // expandIconPosition: {
+    //   type: String,
+    //   default: 'right'
+    // },
+    titleIcon: {
+      type: String,
+      default: ''
+    },
+    titleIconSize: {
+      type: String,
+      default: '16px'
+    },
+    titleIconColor: {
+      type: String,
+      default: ''
+    },
+    titleIconPosition: {
+      type: String,
+      default: 'left'
+    },
+    icon: {
+      type: String,
+      default: ''
+    },
+    iconSize: {
+      type: String,
+      default: '16px'
+    },
+    iconColor: {
+      type: String,
+      default: ''
+    },
+    rotate: {
+      type: [String, Number],
+      default: 180
+    }
+  },
+  emits: ['update:active', 'change'],
+  setup(props, { emit }) {
+    const changeVal = (val: string | number | Array<string | number>) => {
+      emit('update:active', val);
+      emit('change', val);
+    };
+
+    const changeValAry = (name: string) => {
+      const activeItem: any =
+        props.active instanceof Object
+          ? Object.values(props.active)
+          : props.active;
+      let index = -1;
+      activeItem.forEach((item: string | number, idx: number) => {
+        if (String(item) == String(name)) {
+          index = idx;
+        }
+      });
+      index > -1 ? activeItem.splice(index, 1) : activeItem.push(name);
+      changeVal(activeItem);
+    };
+
+    const isExpanded = (name: string | number | Array<string | number>) => {
+      const { accordion, active } = props;
+      if (accordion) {
+        return typeof active === 'number' || typeof active === 'string'
+          ? active == name
+          : false;
+      }
+    };
+
+    provide('collapseParent', {
+      children: [],
+      props,
+      changeValAry,
+      changeVal,
+      isExpanded
+    });
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 284 - 0
src/packages/__VUE/collapseitem/index.taro.vue

@@ -0,0 +1,284 @@
+<template>
+  <view :class="classes">
+    <view
+      :class="[
+        'collapse-item',
+        { 'item-expanded': openExpanded },
+        { 'nut-collapse-item-disabled': disabled }
+      ]"
+      @click="toggleOpen"
+    >
+      <view class="collapse-title">
+        <view>
+          <view class="collapse-title-value">
+            <nut-icon
+              v-if="titleIcon"
+              :name="titleIcon"
+              :size="titleIconSize"
+              :color="titleIconColor"
+              :class="[
+                titleIconPosition == 'left' ? 'titleIconLeft' : 'titleIconRight'
+              ]"
+            ></nut-icon>
+            <view v-html="title"></view>
+          </view>
+        </view>
+      </view>
+      <view v-if="subTitle" v-html="subTitle" class="subTitle"></view>
+      <nut-icon
+        v-if="icon"
+        :name="icon"
+        :size="iconSize"
+        :color="iconColor"
+        :class="[
+          'collapse-icon',
+          { 'col-expanded': openExpanded },
+          { 'collapse-icon-disabled': disabled }
+        ]"
+        :style="iconStyle"
+      ></nut-icon>
+    </view>
+    <view class="collapse-wrapper" ref="wrapperRef">
+      <view class="collapse-content" ref="contentRef">
+        <slot></slot>
+      </view>
+    </view>
+  </view>
+</template>
+<script lang="ts">
+import {
+  reactive,
+  inject,
+  toRefs,
+  onMounted,
+  ref,
+  nextTick,
+  computed,
+  watch,
+  getCurrentInstance,
+  ComponentInternalInstance
+} from 'vue';
+import Taro, {
+  eventCenter,
+  getCurrentInstance as getCurrentInstanceTaro
+} from '@tarojs/taro';
+import { createComponent } from '@/packages/utils/create';
+const { create, componentName } = createComponent('collapse-item');
+
+export default create({
+  props: {
+    title: {
+      type: String,
+      default: ''
+    },
+    subTitle: {
+      type: String,
+      default: ''
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    name: {
+      type: [Number, String],
+      default: -1,
+      required: true
+    },
+    collapseRef: {
+      type: Object
+    }
+  },
+  setup(props) {
+    const collapse: any = inject('collapseParent');
+    const parent: any = reactive(collapse);
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true,
+        // [`${prefixCls}-left`]: parent.props.classDirection === 'left',
+        [`${prefixCls}-icon`]: parent.props.icon
+      };
+    });
+    const relation = (child: ComponentInternalInstance): void => {
+      if (child.proxy) {
+        parent.children.push(child.proxy);
+      }
+    };
+    relation(getCurrentInstance() as ComponentInternalInstance);
+    const proxyData = reactive({
+      icon: parent.props.icon,
+      iconSize: parent.props.iconSize,
+      iconColor: parent.props.iconColor,
+      openExpanded: false,
+      // classDirection: 'right',
+      iconStyle: {
+        transform: 'rotate(0deg)',
+        marginTop: parent.props.iconHeght
+          ? '-' + parent.props.iconHeght / 2 + 'px'
+          : '-10px'
+      }
+    });
+
+    const titleIconStyle = reactive({
+      titleIcon: parent.props.titleIcon,
+      titleIconSize: parent.props.titleIconSize,
+      titleIconColor: parent.props.titleIconColor,
+      titleIconPosition: parent.props.titleIconPosition
+      // titleIconWH: {
+      //   width: '13px',
+      //   height: '13px'
+      // }
+    });
+
+    // 获取 Dom 元素
+    const wrapperRef: any = ref(null);
+    const contentRef: any = ref(null);
+
+    // 清除 willChange 减少性能浪费
+    const onTransitionEnd = () => {
+      const wrapperRefEle: any = document.getElementsByClassName(
+        'collapse-wrapper'
+      )[0];
+      wrapperRefEle.style.willChange = 'auto';
+    };
+
+    // 手风琴模式
+    const animation = () => {
+      const wrapperRefEle: any = wrapperRef.value;
+      const contentRefEle: any = contentRef.value;
+      if (!wrapperRefEle || !contentRefEle) {
+        return;
+      }
+      const offsetHeight = contentRefEle.offsetHeight;
+      if (offsetHeight) {
+        const contentHeight = `${offsetHeight}px`;
+        wrapperRefEle.style.willChange = 'height';
+        wrapperRefEle.style.height = !proxyData.openExpanded
+          ? 0
+          : contentHeight;
+        if (parent.props.icon && !proxyData.openExpanded) {
+          proxyData.iconStyle['transform'] = 'rotate(0deg)';
+        } else {
+          proxyData.iconStyle['transform'] =
+            'rotate(' + parent.props.rotate + 'deg)';
+        }
+      }
+      if (!proxyData.openExpanded) {
+        onTransitionEnd();
+      }
+    };
+
+    const open = () => {
+      proxyData.openExpanded = !proxyData.openExpanded;
+      animation();
+    };
+
+    const defaultOpen = () => {
+      open();
+      if (parent.props.icon) {
+        proxyData['iconStyle']['transform'] =
+          'rotate(' + parent.props.rotate + 'deg)';
+      }
+    };
+
+    const currentName = computed(() => props.name);
+    const toggleOpen = () => {
+      console.log('点击了——————-----');
+      console.log(document.getElementsByClassName('collapse-content')[0]);
+
+      if (parent.props.accordion) {
+        parent.children.forEach((item: any, index: number) => {
+          if (currentName.value == item.name) {
+            item.changeOpen(!item.openExpanded);
+          } else {
+            item.changeOpen(false);
+            item.animation();
+          }
+        });
+        nextTick(() => {
+          parent.changeVal(currentName.value);
+          animation();
+        });
+      } else {
+        parent.changeValAry(props.name);
+        open();
+      }
+    };
+    // 更改子组件展示
+    const changeOpen = (bol: boolean) => {
+      proxyData.openExpanded = bol;
+    };
+
+    const expanded = computed(() => {
+      if (parent) {
+        return parent.isExpanded(props.name);
+      }
+      return null;
+    });
+
+    watch(expanded, (value, oldValue) => {
+      if (value) {
+        proxyData.openExpanded = true;
+      }
+    });
+
+    onMounted(() => {
+      const { name } = props;
+      const active = parent && parent.props.active;
+
+      if (typeof active == 'number' || typeof active == 'string') {
+        if (name == active) {
+          defaultOpen();
+        }
+      } else if (Object.values(active) instanceof Array) {
+        const f = Object.values(active).filter(item => item == name);
+        if (f.length > 0) {
+          defaultOpen();
+        }
+      }
+
+      // 获取 DOM 元素
+      eventCenter.once((getCurrentInstanceTaro() as any).router.onReady, () => {
+        let a = document.getElementsByClassName('collapse-wrapper')[0];
+        console.log(a);
+
+        // const query = Taro.createSelectorQuery()
+        // query.select('.collapse-wrapper').boundingClientRect()
+        // query.exec(res => {
+        //   console.log(res, 'res')
+        // })
+        // console.log('onReady')
+      });
+
+      // proxyData.classDirection = parent.props.expandIconPosition;
+      // if (parent.props.icon && parent.props.icon != 'none') {
+      //   proxyData.iconStyle['background-image'] =
+      //     'url(' + parent.props.icon + ')';
+      // }
+      // if (parent.props.iconWidth && parent.props.icon != 'none') {
+      //   proxyData.iconStyle['width'] = parent.props.conWidth;
+      // }
+      // if (parent.props.iconHeght && parent.props.icon != 'none') {
+      //   proxyData.iconStyle['height'] = parent.props.iconHeight;
+      // }
+    });
+
+    return {
+      classes,
+      ...toRefs(proxyData),
+      ...toRefs(parent.props),
+      ...toRefs(titleIconStyle),
+      wrapperRef,
+      contentRef,
+      open,
+      toggleOpen,
+      changeOpen,
+      animation
+    };
+  }
+});
+</script>
+
+<style lang="scss">
+@import './index.scss';
+</style>

+ 4 - 1
src/packages/__VUE/collapseitem/index.vue

@@ -136,6 +136,9 @@ export default create({
         'collapse-wrapper'
       )[0];
       wrapperRefEle.style.willChange = 'auto';
+
+      // const query = wx.createSelectorQuery();
+      // query.select('#productServe').boundingClientRect();
     };
 
     // 手风琴模式
@@ -259,6 +262,6 @@ export default create({
 });
 </script>
 
-<style lang="scss" scoped>
+<style lang="scss">
 @import './index.scss';
 </style>

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

@@ -115,7 +115,7 @@ export default create({
     }
     watch(
       () => props.visible,
-      value => {
+      (value) => {
         show.value = value;
       }
     );

+ 153 - 0
src/packages/countup/demo.vue

@@ -0,0 +1,153 @@
+<template>
+  <div class="demo">
+    <h2>基础用法</h2>
+    <div class="show-demo">
+      <nut-countup :init-num="0" :end-num="200"></nut-countup>
+      <nut-countup
+        :init-num="150.0"
+        :end-num="0.0"
+        :speed="6.18"
+        :to-fixed="2"
+      ></nut-countup>
+      <nut-countup
+        :init-num="500.0"
+        :end-num="0.0"
+        :speed="17"
+        :to-fixed="2"
+      ></nut-countup>
+    </div>
+    <h2>数字滚动</h2>
+    <div class="show-demo">
+      <nut-countup
+        :scrolling="true"
+        :init-num="17.618"
+        :during="600"
+      ></nut-countup>
+    </div>
+    <h2>自定义滚动图片展示</h2>
+    <div class="show-demo">
+      <nut-countup
+        :custom-change-num="customNumber"
+        :custom-bg-img="bgImage"
+        :custom-spac-num="11"
+        :num-width="33"
+        :num-height="47"
+        :during="8000"
+      >
+      </nut-countup>
+    </div>
+    <h2>抽奖(模拟滚动抽奖)</h2>
+    <div class="show-demo">
+      <nut-countup
+        ref="countupMachine"
+        type="machine"
+        :machine-num="machineNum"
+        :machine-prize-num="5"
+        :machine-prize-level="prizeLevel"
+        :custom-bg-img="bgImage2"
+        :num-width="100"
+        :num-height="100"
+        :during="3000"
+        @scroll-end="scrollAniEnd"
+      >
+      </nut-countup>
+      <div class="btnBtn">
+        <nut-button type="danger" @click="startRole" :disabled="startFlag"
+          >抽奖</nut-button
+        >
+        <nut-button type="danger" @click="startRole2" :disabled="startFlag"
+          >不中奖</nut-button
+        >
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { ref } from 'vue';
+import { createComponent } from '@/utils/create';
+import { reactive, toRefs } from '@vue/reactivity';
+import { onMounted } from '@vue/runtime-core';
+const { createDemo } = createComponent('countup');
+export default createDemo({
+  props: {},
+  setup() {
+    const data = reactive({
+      customNumber: 618,
+      bgImage:
+        'https://img10.360buyimg.com/imagetools/jfs/t1/133024/3/2251/2646/5ee7549aE8dc02d7e/de6901b6c72db396.png',
+      startFlag: false,
+      startFlag2: false,
+      machineNum: 3,
+      bgImage2:
+        'https://img10.360buyimg.com/imagetools/jfs/t1/121466/20/6784/28830/5f06e7f2Edbb8998c/9bdd9e7b24dff9fe.png',
+      prizeLevel: 0
+    });
+    const scrollAniEnd = (val: boolean) => {
+      console.log(val ? '恭喜中奖!!!' : '很遗憾!没有中奖');
+      setTimeout(() => {
+        data.startFlag = false;
+      }, 300);
+    };
+    const run = () => {
+      let timer = null;
+      timer = setInterval(() => {
+        data.customNumber = Math.floor(Math.random() * (700 - 100 + 1) + 100);
+      }, 5000);
+    };
+    const startRole = () => {
+      data.prizeLevel = Math.floor(Math.random() * 5 + 1);
+      console.log(data.prizeLevel, '中奖');
+      data.startFlag = true;
+      // 实际运行情况下,开始滚动函数,需要放在奖品回调函数之后,如果异步操作会影响转动结果
+      setTimeout(() => {
+        (countupMachine.value as any).machineLuck();
+      }, 500);
+    };
+    const countupMachine = ref(null);
+    const startRole2 = () => {
+      data.prizeLevel = -1;
+      console.log(data.prizeLevel, '不中奖');
+      data.startFlag = true;
+      setTimeout(() => {
+        (countupMachine.value as any).machineLuck();
+      }, 500);
+    };
+    onMounted(() => {
+      run();
+    });
+    return {
+      ...toRefs(data),
+      scrollAniEnd,
+      startRole,
+      startRole2,
+      countupMachine
+    };
+  }
+});
+</script>
+
+<style></style>
+<style lang="scss" scoped>
+#app .demo {
+  padding: 57px 0 0 0;
+}
+.show-demo {
+  background: #ffffff;
+  padding: 0 20px;
+}
+h2 {
+  padding: 0 20px;
+}
+.btnBtn {
+  text-align: center;
+  .nut-button {
+    margin-right: 20px;
+  }
+}
+.disabledClick {
+  pointer-events: none !important;
+  cursor: not-allowed !important;
+  opacity: 0.6 !important;
+}
+</style>

+ 34 - 0
src/packages/countup/doc.md

@@ -0,0 +1,34 @@
+#  countup组件
+
+    ### 介绍
+    
+    基于 xxxxxxx
+    
+    ### 安装
+    
+    
+    
+    ## 代码演示
+    
+    ### 基础用法1
+    
+
+    
+    ## API
+    
+    ### Props
+    
+    | 参数         | 说明                             | 类型   | 默认值           |
+    |--------------|----------------------------------|--------|------------------|
+    | name         | 图标名称或图片链接               | String | -                |
+    | color        | 图标颜色                         | String | -                |
+    | size         | 图标大小,如 '20px' '2em' '2rem' | String | -                |
+    | class-prefix | 类名前缀,用于使用自定义图标     | String | 'nutui-iconfont' |
+    | tag          | HTML 标签                        | String | 'i'              |
+    
+    ### Events
+    
+    | 事件名 | 说明           | 回调参数     |
+    |--------|----------------|--------------|
+    | click  | 点击图标时触发 | event: Event |
+    

+ 67 - 0
src/packages/countup/index.scss

@@ -0,0 +1,67 @@
+.nut-countup {
+  display: inline-block;
+  width: 100%;
+  padding: 5px;
+  color: #000;
+  font-weight: bold;
+  font-family: PingFangSC-Regular;
+  // text-align:center;
+  .run-number {
+    display: inline-block;
+    width: 100%;
+    margin: 0 auto;
+    padding: 0;
+    overflow: hidden;
+    // height: 50px;
+    // line-height: 50px;
+    text-align: center;
+    font-weight: bold;
+    position: relative;
+    .numberItem {
+      position: absolute;
+      transition: none;
+      list-style: none;
+      // transition: all linear .1s;
+      // animation: myMove 1s linear infinite;
+      // animation-fill-mode: forwards;
+      // @keyframes myMove {
+      //     0% {
+      //       transform: translateY(0);
+      //     }
+      //     100% {
+      //       transform: translateY(-50px);
+      //     }
+      // }
+      .itemSpan {
+        display: block;
+        font-family: PingFangSC-Regular;
+      }
+    }
+  }
+  .pointstyl {
+    position: absolute;
+    display: block;
+  }
+  .run-number-machine-img {
+    overflow: hidden;
+    .run-number-machine-img-li {
+      float: left;
+      background-position: center 0;
+      background-repeat: repeat-y;
+      background-attachment: scroll;
+    }
+  }
+  .run-number-img {
+    position: relative;
+    display: inline-block;
+    .run-number-img-li {
+      position: absolute;
+      display: block;
+      transition: none;
+      display: inline-block;
+      background-position: 0 0;
+      background-repeat: no-repeat;
+      font-family: PingFangSC-Regular;
+    }
+  }
+}

+ 700 - 0
src/packages/countup/index.vue

@@ -0,0 +1,700 @@
+<template>
+  <view class="nut-countup">
+    <template v-if="customBgImg != ''">
+      <template v-if="type == 'machine'">
+        <view
+          class="run-number-machine-img"
+          :style="{ height: numHeight + 'px' }"
+        >
+          <view
+            class="run-number-machine-img-li"
+            ref="run-number-machine-img-li"
+            v-for="(val, index) of machineNum"
+            :key="'mImg' + index"
+            :style="{
+              width: numWidth + 'px',
+              height: numHeight + 'px',
+              backgroundImage: 'url(' + customBgImg + ')',
+              backgroundPositionY: prizeY[index] + 'px'
+            }"
+          ></view>
+          <!-- backgroundPositionY: prizeLevelTrun + 'px', -->
+        </view>
+      </template>
+      <template v-else>
+        <view class="run-number-img" :style="{ height: numHeight + 'px' }">
+          <view
+            class="run-number-img-li"
+            v-for="(val, index) of num_total_len"
+            :key="'cImg' + index"
+            :style="{
+              width: numWidth + 'px',
+              height: numHeight + 'px',
+              left:
+                numWidth *
+                  (index > num_total_len - pointNum - 1
+                    ? index == num_total_len - pointNum
+                      ? index * 1.5
+                      : index * 1.3
+                    : index) +
+                'px',
+              backgroundImage: 'url(' + customBgImg + ')',
+              backgroundPosition:
+                '0 ' +
+                -(
+                  String(relNum)[index] * numHeight +
+                  customSpacNum * String(relNum)[index]
+                ) +
+                'px',
+              transition: 'all linear ' + during / 10 + 'ms'
+            }"
+          ></view>
+          <view
+            v-if="pointNum > 0"
+            class="pointstyl"
+            :style="{
+              width: numWidth / 2 + 'px',
+              bottom: 0,
+              left: numWidth * (num_total_len - pointNum) * 1.1 + 'px',
+              fontSize: '30px'
+            }"
+            >.</view
+          >
+        </view>
+      </template>
+    </template>
+    <template v-else>
+      <view
+        v-if="scrolling"
+        class="run-number"
+        :style="{ height: numHeight + 'px', lineHeight: numHeight + 'px' }"
+      >
+        <view
+          ref="numberItem"
+          class="numberItem"
+          v-for="(val, index) of num_total_len"
+          :key="val"
+          :style="{
+            top: topNumber(index),
+            left:
+              numWidth *
+                (index > num_total_len - pointNum - 1 ? index * 1.1 : index) +
+              'px'
+          }"
+          :turn-number="turnNumber(index)"
+        >
+          <view
+            v-for="(item, idx) of to0_10"
+            class="itemSpan"
+            :key="'dote' + idx"
+            :style="{
+              width: numWidth + 'px',
+              height: numHeight + 'px',
+              lineHeight: numHeight + 'px'
+            }"
+          >
+            {{ item }}
+          </view>
+        </view>
+        <view
+          v-if="pointNum > 0"
+          class="pointstyl"
+          :style="{
+            width: numWidth / 3 + 'px',
+            height: numHeight + 'px',
+            lineHeight: numHeight + 'px',
+            top: 0,
+            left: numWidth * (num_total_len - pointNum) + 'px'
+          }"
+          >.</view
+        >
+      </view>
+      <template v-else>
+        {{ current }}
+      </template>
+    </template>
+  </view>
+</template>
+<script lang="ts">
+import {
+  reactive,
+  toRefs,
+  ref,
+  onMounted,
+  onUnmounted,
+  nextTick,
+  watch
+} from 'vue';
+import { createComponent } from '@/utils/create';
+import { useExtend } from '@/utils/useRelation/useRelation';
+const { componentName, create } = createComponent('countup');
+
+interface IData {
+  valFlag: boolean;
+  current: number;
+  sortFlag: string;
+  initDigit1: number;
+  initDigit2: number;
+  to0_10: number[];
+  to10_0: Array<number>;
+  timer: null | any;
+  totalCount: number; //正整数
+  pointNum: number; //小数位
+  numberVal: number; //数字
+  num_total_len: number; //数字长度
+  relNum: number; //去除小数点
+  customNumber: number;
+  prizeLevelTrun: number;
+  prizeY: Array<any>;
+  prizeYPrev: Array<any>;
+  // machineTransition: 'none',
+  finshMachine: number;
+  notPrize: Array<any>;
+  typeMachine: string;
+}
+
+export default create({
+  props: {
+    initNum: {
+      type: Number,
+      default: 0
+    },
+    endNum: {
+      type: Number,
+      default: 0
+    },
+    speed: {
+      type: Number,
+      default: 1
+    },
+    toFixed: {
+      type: Number,
+      default: 0
+    },
+    during: {
+      type: Number,
+      default: 1000
+    },
+    startFlag: {
+      type: Boolean,
+      default: true
+    },
+    // 数字滚动
+    numWidth: {
+      type: Number,
+      default: 20
+    },
+    numHeight: {
+      type: Number,
+      default: 20
+    },
+    scrolling: {
+      type: Boolean,
+      default: false
+    },
+    // 自定义图片
+    customBgImg: {
+      type: String,
+      default: ''
+    },
+    customSpacNum: {
+      type: Number,
+      default: 0
+    },
+    customChangeNum: {
+      type: Number,
+      default: 1
+    },
+    // 抽奖
+    type: {
+      type: String,
+      default: ''
+    },
+    machineNum: {
+      type: Number,
+      default: 3
+    },
+    machinePrizeNum: {
+      type: Number,
+      default: 0
+    },
+    machinePrizeLevel: {
+      type: Number,
+      default: 0
+    },
+    machineTrunMore: {
+      type: Number,
+      default: 0
+    }
+  },
+  components: {},
+  emits: ['click', 'scroll-end'],
+  setup(props, { emit }) {
+    const data: IData = reactive({
+      valFlag: false,
+      current: 0,
+      sortFlag: 'add',
+      initDigit1: 0,
+      initDigit2: 0,
+      to0_10: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
+      to10_0: [0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1],
+      timer: null,
+      totalCount: 0, //正整数
+      pointNum: 0, //小数位
+      numberVal: 0, //数字
+      num_total_len: 0, //数字长度
+      relNum: 0, //去除小数点
+      customNumber: 1,
+      prizeLevelTrun: 0,
+      prizeY: [],
+      prizeYPrev: [],
+      // machineTransition: 'none',
+      finshMachine: 0,
+      notPrize: [],
+      typeMachine: ''
+    });
+    const { startFlag, scrolling, customBgImg, type } = reactive(props);
+    watch(
+      () => props.customChangeNum,
+      (count, prevCount) => {
+        clearIntervalTime();
+        // data.customNumber = count;
+        countGo(0);
+      }
+    );
+    watch(
+      () => props.machinePrizeLevel,
+      (count, prevCount) => {
+        data.prizeLevelTrun = count;
+      }
+    );
+    watch(
+      () => props.initNum,
+      (count, prevCount) => {
+        data.current = count;
+        data.valFlag = false;
+        valChange();
+      }
+    );
+    watch(
+      () => props.endNum,
+      (count, prevCount) => {
+        data.current = props.initNum;
+        data.valFlag = false;
+        valChange();
+      }
+    );
+    const valChange = () => {
+      if (data.valFlag) {
+        return false;
+      }
+      if (startFlag) {
+        if (scrolling || customBgImg) {
+          if (type != 'machine') {
+            countGo();
+          }
+        } else {
+          countChange();
+          setTimeout(() => {
+            data.valFlag = true;
+          }, 300);
+        }
+      }
+    };
+    // 清空定时器
+    const clearIntervalTime = () => {
+      clearInterval(Number(data.timer));
+      data.timer = null;
+    };
+    // 精确计算
+    const calculation = (num1: number, num2: number, type: string) => {
+      const num1Digits = (num1.toString().split('.')[1] || '').length;
+      const num2Digits = (num2.toString().split('.')[1] || '').length;
+      const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits));
+      if (type == '-') {
+        const n = Number((num1 * baseNum - num2 * baseNum).toFixed(0));
+        return n / baseNum;
+      } else {
+        const m = Number((num1 * baseNum + num2 * baseNum).toFixed(0));
+        return m / baseNum;
+      }
+    };
+    // 数字滚动-top值
+    const topNumber = (index: number) => {
+      let { num_total_len, pointNum, initDigit1, initDigit2, sortFlag } = data;
+      let idx1 =
+        sortFlag == 'add'
+          ? String(initDigit2)[index - (num_total_len - pointNum)]
+          : 10 - Number(String(initDigit2)[index - (num_total_len - pointNum)]);
+      let idx2 =
+        sortFlag == 'add'
+          ? String(initDigit1)[index]
+          : 10 - Number(String(initDigit1)[index]);
+      let num =
+        index > num_total_len - pointNum - 1
+          ? -idx1 * 100 + '%'
+          : index <= String(initDigit1).length - 1
+          ? -idx2 * 100 + '%'
+          : 0;
+      if (num == '-1000%') {
+        num = 0;
+      }
+      return num;
+    };
+    // 数字滚动-到哪里了
+    const turnNumber = (index: number) => {
+      let { num_total_len, pointNum, initDigit1, initDigit2, sortFlag } = data;
+      let idx1 = String(initDigit2)[index - (num_total_len - pointNum)];
+      let num =
+        index > num_total_len - pointNum - 1
+          ? idx1
+            ? idx1
+            : 0
+          : index <= String(initDigit1).length - 1
+          ? String(initDigit1)[index]
+          : 0;
+      return num;
+    };
+    // 基本用法
+    const countChange = () => {
+      let { endNum, initNum, speed, toFixed } = props;
+      let countTimer = setInterval(() => {
+        if (initNum > endNum) {
+          //减少
+          if (data.current <= endNum || data.current <= speed) {
+            //数字减小,有可能导致current小于speed
+            data.current = Number(endNum.toFixed(toFixed));
+            clearInterval(countTimer);
+            emit('scroll-end');
+            data.valFlag = false;
+          } else {
+            data.current = Number(
+              (
+                parseFloat(String(data.current)) - parseFloat(String(speed))
+              ).toFixed(toFixed)
+            );
+          }
+        } else {
+          //增加
+          if (data.current >= endNum) {
+            data.current = Number(endNum.toFixed(toFixed));
+            clearInterval(countTimer);
+            emit('scroll-end');
+            data.valFlag = false;
+          } else {
+            data.current = Number(
+              (
+                parseFloat(String(data.current)) + parseFloat(String(speed))
+              ).toFixed(toFixed)
+            );
+          }
+        }
+      }, props.during);
+    };
+    const countGo = (flag?: number): void => {
+      let { initNum, endNum, toFixed, customBgImg } = props;
+      if (customBgImg) {
+        initNum = props.customChangeNum;
+      }
+      // --------------
+      let startNumber1, startNumber2, endNumber1, endNumber2;
+      if (initNum != 0) {
+        if (toFixed != 0) {
+          initNum = Number(initNum.toFixed(toFixed));
+        }
+        if (String(initNum).indexOf('.') > -1) {
+          startNumber1 = String(initNum).split('.')[0].length;
+          startNumber2 = String(initNum).split('.')[1].length;
+        } else {
+          startNumber1 = String(initNum).length;
+          startNumber2 = 0;
+        }
+      } else {
+        startNumber1 = 1;
+        startNumber2 = 0;
+      }
+      if (endNum != 0) {
+        if (toFixed != 0) {
+          endNum = Number(endNum.toFixed(toFixed));
+        }
+        if (String(endNum).indexOf('.') > -1) {
+          endNumber1 = String(endNum).split('.')[0].length;
+          endNumber2 = String(endNum).split('.')[1].length;
+        } else {
+          endNumber1 = String(endNum).length;
+          endNumber2 = 0;
+        }
+      } else {
+        endNumber1 = 1;
+        endNumber2 = 0;
+      }
+      let len1 = startNumber1 >= endNumber1 ? startNumber1 : endNumber1;
+      let len2 = startNumber2 >= endNumber2 ? startNumber2 : endNumber2;
+      data.num_total_len = len1 + len2;
+
+      data.pointNum = len2;
+
+      // --------------
+      if (initNum > endNum) {
+        //减少
+        data.sortFlag = 'reduce';
+        data.to0_10 = [0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
+        data.totalCount = calculation(initNum, endNum, '-');
+        data.numberVal = Number(String(initNum));
+      } else {
+        //增加
+        data.sortFlag = 'add';
+        data.to0_10 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
+        data.totalCount = calculation(endNum, initNum, '-');
+        data.numberVal = Number(String(endNum));
+      }
+      //将小数位数计算后,补0
+      var unit = 1;
+      for (let i = 0; i < data.pointNum; i++) {
+        unit *= 10;
+      }
+      var rel_big = data.numberVal * unit; // 去除小数点后的数,unit几个零表示有几个小数
+      data.relNum = rel_big;
+      // this.totalCount = rel_big;
+      if (toFixed != 0) {
+        //计算小数点后的位数,小数位
+        data.pointNum = String(data.numberVal).split('.')[1]
+          ? String(data.numberVal).split('.')[1].length
+          : 0;
+        //数字长度
+        data.num_total_len = String(rel_big).length;
+      }
+      if (String(initNum).indexOf('.') > -1) {
+        let n = String(initNum).split('.');
+        data.initDigit1 = Number(n[0]);
+        data.initDigit2 = Number(n[1]);
+      } else {
+        data.initDigit1 = initNum;
+        data.initDigit2 = 0;
+      }
+      if (scrolling && !customBgImg) {
+        // 数字都是从小加到大的,所以我们循环转动最后一个数字,传入最后一个数字的DOM
+        nextTick(() => {
+          let refsDom: HTMLCollectionOf<Element> = document.getElementsByClassName(
+            'numberItem'
+          );
+          let element = refsDom[data.num_total_len - 1];
+          runTurn(element);
+        });
+      } else {
+        if (flag !== 0) {
+          imgNumberScroll();
+        }
+      }
+    };
+    const runTurn = (el: Element) => {
+      clearIntervalTime();
+      var m = 1;
+      if (data.pointNum != 0) {
+        m = 1 / Math.pow(10, data.pointNum);
+      }
+      //设置定时器
+      (data.timer as any) = setInterval(() => {
+        runStep(el);
+        data.totalCount = calculation(data.totalCount, m, '-');
+        // that.totalCount--;
+        if (data.totalCount <= 0) {
+          clearIntervalTime();
+          emit('scroll-end');
+          data.valFlag = false;
+        }
+      }, props.during);
+    };
+
+    const runStep = (el: any) => {
+      let currentTurn = el.getAttribute('turn-number');
+      let turningNum: number;
+      if (data.sortFlag == 'add') {
+        turningNum = parseInt(String(currentTurn)) + 1;
+      } else {
+        turningNum =
+          parseInt(String(currentTurn)) - 1 >= 0
+            ? parseInt(String(currentTurn)) - 1
+            : 9;
+      }
+      el.setAttribute('turn-number', String(turningNum));
+      if (
+        el.style.transition == 'none 0s ease 0s' ||
+        turningNum == 1 ||
+        !el.style.transition
+      ) {
+        el.style.transition = `all linear ${props.during}ms`;
+      }
+      if (turningNum == 10 || (data.sortFlag == 'reduce' && turningNum == 0)) {
+        var timeOut: any = null;
+        // el.style.top = `-${turningNum * 100}%`;
+        el.style.top = `-${
+          data.sortFlag == 'add' ? turningNum * 100 : (10 - turningNum) * 100
+        }%`;
+        el.setAttribute('turn-number', '0');
+        timeOut = setTimeout(() => {
+          timeOut && clearTimeout(timeOut);
+          el.style.transition = 'none';
+          el.style.top = '0';
+          // 前面数字的滚动,用于递增
+          if (turningNum == 10) {
+            if (el.previousSibling) {
+              runStep(el.previousSibling as HTMLElement);
+            }
+          }
+        }, 0.975 * props.during);
+      } else {
+        // el.style.top = `-${(10-turningNum)*100}%`;
+        el.style.top = `-${
+          data.sortFlag == 'add' ? turningNum * 100 : (10 - turningNum) * 100
+        }%`;
+      }
+      // 用于递减的时候
+      if (el.style.top == '-100%' && data.sortFlag == 'reduce') {
+        runStep(el.previousSibling as HTMLElement);
+      }
+    };
+    // 自定义图片
+    const imgNumberScroll = () => {
+      let m = 1;
+      if (data.pointNum != 0) {
+        m = Math.pow(10, data.pointNum);
+      }
+      nextTick(() => {
+        let f = document.getElementsByClassName('run-number-img')[0];
+        // setTimeout(() => {
+        //   data.relNum = calculation(data.relNum, m * props.speed, '+');
+        // }, props.during);
+        f.addEventListener('webkitTransitionEnd', () => {
+          emit('scroll-end');
+          console.log('ooopppsss');
+          data.valFlag = false;
+          // setTimeout(() => {
+          //   data.relNum = calculation(data.relNum, m * props.speed, '+');
+          // }, props.during);
+        });
+      });
+    };
+    // 不中奖设置随机数
+    const generateRandom = () => {
+      data.notPrize = [];
+      while (data.notPrize.length < 3) {
+        var rand: number = Math.floor(
+          Math.random() * props.machinePrizeNum + 1
+        );
+        if (data.notPrize.indexOf(rand) == -1) {
+          data.notPrize.push(rand);
+        }
+      }
+    };
+    // 抽奖
+    const machineLuck = () => {
+      const machineTrunMoreNum =
+        props.machineTrunMore < 0 ? 0 : props.machineTrunMore;
+      let distance = props.numHeight * props.machinePrizeNum; // 所有奖品的高度,雪碧图的高度
+      if (data.prizeLevelTrun < 0) {
+        generateRandom();
+      }
+      for (let i = 0; i < props.machineNum; i++) {
+        setTimeout(() => {
+          let turn =
+            distance * (i + 1 + parseFloat(String(machineTrunMoreNum)));
+          if (data.prizeYPrev.length != 0) {
+            // this.machineTransition = 'none';
+            // console.log(this.prizeYPrev[i]-(this.numHeight * this.machinePrizeNum));
+            // this.$set(data.prizeY, i, data.prizeYPrev[i]);
+            data.prizeY[i] = data.prizeYPrev[i];
+          }
+          let local = data.prizeYPrev[i] ? data.prizeYPrev[i] : 0;
+          let newLocation =
+            turn +
+            local +
+            (props.machinePrizeNum - data.prizeLevelTrun + 1) *
+              props.numHeight +
+            (distance - local);
+          if (data.prizeLevelTrun < 0) {
+            newLocation += props.numHeight * data.notPrize[i];
+          }
+          scrollTime(
+            i,
+            // parseFloat((this.machinePrizeNum-(this.prizeLevelTrun-1))*this.numHeight + turn + local),
+            newLocation,
+            local
+          );
+        }, 500 * i);
+      }
+    };
+    useExtend({ machineLuck });
+    const scrollTime = (index: number, total: number, num: number) => {
+      // this.machineTransition = `all linear ${this.during/this.machinePrizeNum}ms`;
+      let t: any = setInterval(() => {
+        if (num <= total) {
+          num += 10;
+          data.prizeY[index] = parseFloat(String(num));
+        } else {
+          clearInterval(t);
+          t = null;
+          data.finshMachine += 1;
+          data.prizeY[index] = total;
+          // 动画未完成的时候触发了判断,需要加个延时或者监听最后一个动画执行结束,保证在动画执行结束
+          // this.$nextTick(() => {
+          //     var f = document.getElementsByClassName('run-number-machine-img-li');
+          //     f[f.length-1].addEventListener('webkitTransitionEnd', () => {
+          //         setTimeout(() => {
+          //             if(this.finshMachine == this.machineNum) {
+          //                 this.finshMachine = 0;
+          //             }
+          //         },200)
+          //     });
+          // })
+          if (data.finshMachine == props.machineNum) {
+            let distance = props.numHeight * props.machinePrizeNum;
+            data.prizeYPrev = [];
+            let prevAry = JSON.parse(JSON.stringify(data.prizeY));
+            prevAry.forEach((item: any) => {
+              let n = item;
+              while (n > distance) {
+                n -= distance;
+              }
+              data.prizeYPrev.push(n);
+            });
+            setTimeout(() => {
+              data.finshMachine = 0;
+              if (data.prizeLevelTrun < 0) {
+                emit('scroll-end', false);
+                data.valFlag = false;
+              } else {
+                emit('scroll-end', true);
+                data.valFlag = false;
+              }
+            }, 130);
+          }
+        }
+      }, 30);
+    };
+
+    onMounted(() => {
+      data.current = props.initNum;
+      nextTick(() => {
+        valChange();
+      });
+    });
+
+    onUnmounted(() => {
+      clearIntervalTime();
+      data.timer = null;
+    });
+
+    return {
+      ...toRefs(data),
+      ...toRefs(reactive(props)),
+      topNumber,
+      turnNumber
+    };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 1 - 1
src/packages/utils/create/component.ts

@@ -8,7 +8,7 @@ export function createComponent(name: string) {
       _component.name = componentName;
       _component.install = (vue: App) => {
         vue.component(_component.name as string, _component);
-        _component?.children?.length &&
+        _component.children.length &&
           _component.children.forEach((item: any) => {
             vue.component(item.name as string, item);
           });

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

@@ -40,5 +40,7 @@
     "minifyWXSS": true
   },
   "compileType": "miniprogram",
+  "simulatorType": "wechat",
+  "simulatorPluginLibVersion": {},
   "condition": {}
 }

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

@@ -1,5 +1,6 @@
 export default {
   pages: [
+    'pages/collapse/index',
     'pages/shortpassword/index',
     'pages/textarea/index',
     'pages/calendar/index',

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

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

+ 93 - 0
src/sites/mobile-taro/vue/src/pages/collapse/index.vue

@@ -0,0 +1,93 @@
+<template>
+  <div class="demo full">
+    <h2>基本用法</h2>
+    <nut-collapse v-model:active="active1" icon="down-arrow">
+      <nut-collapse-item :title="title1" :name="1">
+        京东“厂直优品计划”首推“政府优品馆” 3年覆盖80%镇级政府
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2">
+        京东到家:教师节期间 创意花束销量增长53倍
+      </nut-collapse-item>
+      <nut-collapse-item :title="title3" :name="3" disabled>
+      </nut-collapse-item>
+    </nut-collapse>
+    <h2>无icon样式</h2>
+    <nut-collapse v-model:active="active4" :accordion="true">
+      <nut-collapse-item :title="title1" :name="1">
+        2020年中国数字游戏市场规模超2786亿元
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2">
+        基于区块链技术的取证APP在浙江省杭州市发布
+      </nut-collapse-item>
+    </nut-collapse>
+    <h2>手风琴</h2>
+    <nut-collapse v-model:active="active2" :accordion="true" icon="down-arrow">
+      <nut-collapse-item :title="title1" :name="1">
+        华为终端操作系统EMUI 11发布,9月11日正式开启
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2" :sub-title="subTitle">
+        中国服务机器人市场已占全球市场超1/4
+      </nut-collapse-item>
+      <nut-collapse-item :title="title3" :name="3">
+        QuestMobile:90后互联网用户规模超越80后达3.62亿
+      </nut-collapse-item>
+    </nut-collapse>
+    <h2>自定义折叠图标</h2>
+
+    <nut-collapse
+      v-model:active="active3"
+      :accordion="true"
+      icon="arrow-right2"
+      rotate="90"
+    >
+      <nut-collapse-item :title="title1" :name="1">
+        京东数科IPO将引入“绿鞋机制”
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2" sub-title="文本内容">
+        世界制造业大会开幕,阿里巴巴与安徽合作再升级
+      </nut-collapse-item>
+    </nut-collapse>
+    <h2>自定义标题图标</h2>
+    <nut-collapse
+      v-model:active="active5"
+      title-icon="issue"
+      title-icon-color="red"
+      title-icon-size="20px"
+      title-icon-position="left"
+      icon="down-arrow"
+      :accordion="true"
+    >
+      <nut-collapse-item :title="title1" :name="1">
+        “森亿智能”获4亿元D轮融资
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2" sub-title="文本内容">
+        快看漫画与全球潮玩集合店X11达成战略合作
+      </nut-collapse-item>
+    </nut-collapse>
+  </div>
+</template>
+<script lang="ts">
+import { reactive, toRefs } from 'vue';
+export default {
+  setup(props, context) {
+    const data = reactive({
+      active1: [1, '2'],
+      active2: 1,
+      active3: 1,
+      active4: 1,
+      active5: 1,
+      title1: '标题1',
+      title2: '标题2',
+      title3: '标题3',
+      subTitle: '副标题'
+    });
+    const change = (name: string) => {
+      console.log(`点击了name是${name}的面板`);
+    };
+    return {
+      change,
+      ...toRefs(data)
+    };
+  }
+};
+</script>

+ 7 - 0
src/utils/useRelation/useRelation.ts

@@ -0,0 +1,7 @@
+import { getCurrentInstance } from 'vue';
+export function useExtend<T>(apis: T) {
+  const instance = getCurrentInstance();
+  if (instance) {
+    Object.assign(instance.proxy, apis);
+  }
+}