Browse Source

Merge branch 'v2-dev' of https://github.com/jdf2e/nutui into v2-dev

shenqistart 5 years ago
parent
commit
d493c7b921

+ 20 - 0
CHANGELOG.md

@@ -1,3 +1,23 @@
+## 2.2.6
+
+`2020-06-19`
+
+* :sparkles: feat(CountUp):新增数字滚动组件 @Ymm0008
+* :sparkles: upd(LuckDraw):优化抽奖组件,增加单独设置颜色区块、奖品列表 @Ymm0008
+* :sparkles: upd(Uploader):优化内部代码 @linrufeng
+* :sparkles: upd(LeftSlip):新增禁用属性 @vickyYE
+* :sparkles: upd(Popup):z-index层级处理、禁止滑动逻辑优化、测试用例补充 @yangkaixuan
+* :sparkles: upd(TabBar):优化默认选中页签激活状态 @zhenyulei
+* :sparkles: upd(Tabs):新增滚动居中功能 @zhenyulei
+* :bug: fix(Uploader):修复文件上传组件可以调用移动端摄像头 #212 @linrufeng
+* :bug: fix(SearchBar):修复 input 边框样式 @yangxiaolu1993
+* :bug: fix(TextBox): 修复 输入时 waring @guoxiao158
+* :bug: fix(SubSideNavBar):修复 offset 计算错误问题 @szg2008
+* :bug: fix(ImagePicker):修复 多张上传触发多次emit的问题 @szg2008
+* :bug: fix(Swiper):销毁钩子内计时器清空无效 #262 @richard1015
+* :bug: fix(Calendar):增加监听默认值修改功能 @irisSong
+* :bug: fix(Switch):新增内嵌文字标签,如ON|OFF或开|关 @zjyau
+
 ## 2.2.5
 
 `2020-05-25`

+ 3 - 3
package.json

@@ -1,6 +1,6 @@
 {
   "name": "@nutui/nutui",
-  "version": "2.2.5",
+  "version": "2.2.6",
   "description": "一套轻量级移动端Vue组件库",
   "typings": "dist/types/index.d.ts",
   "main": "dist/nutui.js",
@@ -48,7 +48,7 @@
     "@babel/plugin-transform-runtime": "^7.9.6",
     "@commitlint/cli": "^8.0.0",
     "@commitlint/config-conventional": "^8.0.0",
-    "@nutui/cli": "^0.2.1",
+    "@nutui/cli": "^0.2.2",
     "babel-plugin-istanbul": "^6.0.0",
     "gsap": "^3.2.6",
     "husky": "^3.0.0",
@@ -79,4 +79,4 @@
     "instrument": false,
     "sourceMap": false
   }
-}
+}

+ 9 - 0
src/config.json

@@ -642,6 +642,15 @@
       "sort": "2",
       "showDemo": true,
       "author": "wangyue217"
+    },
+    {
+      "name": "CountUp",
+      "type": "component",
+      "chnName": "数字滚动",
+      "desc": "用于数据展示",
+      "sort": "0",
+      "showDemo": true,
+      "author": "yumingming"
     }
   ]
 }

+ 4 - 1
src/nutui.js

@@ -126,6 +126,8 @@ import Address from './packages/address/index.js';
 import './packages/address/address.scss';
 import Notify from "./packages/notify/index.js";
 import "./packages/notify/notify.scss";
+import CountUp from "./packages/countup/index.js";
+import "./packages/countup/countup.scss";
 
 const packages = {
   Cell,
@@ -188,7 +190,8 @@ const packages = {
   SideNavBarItem: SideNavBarItem,
   Drag: Drag,
   Address: Address,
-  Notify: Notify
+  Notify: Notify,
+  CountUp: CountUp
 };
 
 const components = {};

+ 50 - 0
src/packages/countup/countup.scss

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

+ 437 - 0
src/packages/countup/countup.vue

@@ -0,0 +1,437 @@
+<template>
+    <div class="nut-countup">
+        <template v-if="customBgImg != ''">
+            <ul class="run-number-img" :style="{height: numHeight+'px'}">
+                <li
+                    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'
+                    }"
+                ></li>
+                <div
+                    v-if="pointNum > 0"
+                    class="pointstyl"
+                    :style="{
+                        width: numWidth/2 +'px',
+                        bottom: 0, 
+                        left: (numWidth*(num_total_len-pointNum)*1.1)+'px',
+                        'fontSize': '30px'
+                    }"
+                >.</div>
+            </ul>
+        </template>
+        <template v-else>
+            <ul
+                v-if="scrolling"
+                class="run-number"
+                :style="{height: numHeight+'px'}"
+            >
+                <li
+                    ref="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)"
+                >
+                    <span
+                        v-for="(item,idx) of to0_10"
+                        :key="'dote'+idx"
+                        :style="{width: numWidth+'px',height: numHeight+'px',lineHeight: numHeight+'px'}"
+                    >
+                        {{item}}
+                    </span>
+                </li>
+                <div
+                    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'
+                    }"
+                >.</div>
+            </ul>
+            <template v-else>
+                {{current}}
+            </template>
+        </template>
+    </div>
+</template>
+<script>
+export default {
+    name:'nut-countup',
+    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
+        }
+    },
+    data() {
+        return {
+            current: this.initNum,
+            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,
+        };
+    },
+    computed: {
+        
+    },
+    watch: {
+       customChangeNum:function(n,o) {
+           this.customNumber = n;
+           this.countGo();
+       } 
+    },
+    mounted(){
+    	if(this.startFlag){
+            if (this.scrolling || this.customBgImg) {
+                this.countGo();
+            }else {
+                this.countChange();
+            }
+        }
+    },
+    beforeDestroy() {
+        clearInterval(this.timer);        
+        this.timer = null;
+    },
+    methods: {
+        // 清空定时器
+        clearInterval() {
+            clearInterval(this.timer);
+            this.timer = null;
+        },
+        // 精确计算
+        calculation(num1, num2, type) {
+            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 == '-'){
+                return (num1 * baseNum - num2 * baseNum).toFixed(0) / baseNum;
+            }else {
+                return (num1 * baseNum + num2 * baseNum).toFixed(0) / baseNum;
+            }
+        },
+        // 基本用法
+    	countChange(){
+            let {endNum, initNum, speed, toFixed} = this;
+    		let countTimer = setInterval(()=>{
+    			if(initNum > endNum){//减少
+    				if(this.current <= endNum || this.current <= speed){//数字减小,有可能导致current小于speed
+	    				this.current = endNum.toFixed(toFixed);
+                        clearInterval(countTimer);
+                        this.$emit('scroll-end');
+					}else{
+                        this.current = (parseFloat(this.current) - parseFloat(speed)).toFixed(toFixed);
+					}
+    			}else{//增加
+    				if(this.current >= endNum){
+    					this.current = endNum.toFixed(toFixed);
+                        clearInterval(countTimer);
+                        this.$emit('scroll-end');
+    				}else{
+    					this.current = (parseFloat(this.current) + parseFloat(speed)).toFixed(toFixed);
+    				}
+    			}
+    		},this.during);
+        },
+        // 数字滚动-top值
+        topNumber(index) {
+            let {num_total_len, pointNum, initDigit1, initDigit2, sortFlag} = this;
+            let idx1 = sortFlag == 'add' ? initDigit2[index-(num_total_len-pointNum)] : (10 - initDigit2[index-(num_total_len-pointNum)]);
+            let idx2 = sortFlag == 'add' ? initDigit1[index] : (10 - initDigit1[index]);
+            let num = index > num_total_len-pointNum-1 ? -(idx1) * 100 + '%' : 
+                    (index <= initDigit1.length - 1 ? -(idx2) * 100 + '%':0);
+            if(num == '-1000%') {num = 0}
+            return num;
+        },
+        // 数字滚动-到哪里了
+        turnNumber(index) {
+            let {num_total_len, pointNum, initDigit1, initDigit2, sortFlag} = this;
+            let idx1 = initDigit2[index-(num_total_len-pointNum)];
+            let num = index > num_total_len-pointNum-1 ? (idx1?idx1:0): 
+                    (index <= initDigit1.length - 1 ? initDigit1[index]:0);
+            return num;
+        },
+        countGo() {
+            let val = null;
+            if (this.toFixed != 0) {
+                // val = this.endNum.toFixed(this.toFixed);
+                // this.initNum = this.initNum.toFixed(this.toFixed);
+                // this.endNum = this.endNum.toFixed(this.toFixed);
+            }
+            let {initNum, endNum, toFixed, customBgImg} = this;
+            if (customBgImg) {
+                initNum = this.customNumber;
+            }
+            // --------------
+            let startNumber1,startNumber2,endNumber1,endNumber2;
+            if(initNum != 0) {
+                if (toFixed != 0) {
+                    initNum = 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 = 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;
+            this.num_total_len = len1 + len2;
+            this.pointNum = len2;
+            // --------------
+            if(initNum > endNum){
+                //减少
+                this.sortFlag = 'reduce';
+                this.to0_10 = [0,9,8,7,6,5,4,3,2,1,0];
+                this.totalCount = this.calculation(initNum,endNum,'-');
+                this.numberVal = String(initNum);
+            }else {
+                //增加
+                this.sortFlag = 'add';
+                this.to0_10 = [0,1,2,3,4,5,6,7,8,9,0];
+                this.totalCount = this.calculation(endNum,initNum,'-');
+                this.numberVal = String(endNum);
+            }
+            //将小数位数计算后,补0
+            var unit = 1;
+            for(let i = 0; i < this.pointNum; i++){
+                unit *= 10
+            }
+            var rel_big = this.numberVal*unit;// 去除小数点后的数,unit几个零表示有几个小数
+            this.relNum = rel_big;
+            // this.totalCount = rel_big;
+            if (toFixed != 0) {
+                //计算小数点后的位数,小数位
+                this.pointNum = this.numberVal.split('.')[1] ? this.numberVal.split('.')[1].length:0;
+                //数字长度
+                this.num_total_len = String(rel_big).length;
+            }
+            if(String(initNum).indexOf('.') > -1) {
+                let n = String(initNum).split('.');
+                this.initDigit1 = n[0];
+                this.initDigit2 = n[1];
+            }else {
+                this.initDigit1 = String(initNum);
+                this.initDigit2 = '0';
+            }
+            if(this.scrolling && !customBgImg) {
+                this.$nextTick(() => {
+                    // 数字都是从小加到大的,所以我们循环转动最后一个数字,传入最后一个数字的DOM
+                    let element = this.$refs.numberItem[this.num_total_len - 1];
+                    this.runTurn(element);
+                });
+            }else {
+                this.imgNumberScroll();
+            }
+        },
+        runTurn(el) {
+            let that = this;
+            that.clearInterval();
+            var m = 1;
+            if(this.pointNum!=0){
+                m = 1/(Math.pow(10,this.pointNum));
+            }
+            //设置定时器
+            that.timer = setInterval(() => {
+                that.runStep(el);
+                that.totalCount=that.calculation(that.totalCount,m,'-');
+                // that.totalCount--;
+                if(that.totalCount <= 0) {
+                    that.clearInterval();
+                    this.$emit('scroll-end');
+                }
+            }, that.during);
+        },
+        runStep(el) {
+            let that = this;
+            let currentTurn = el.getAttribute('turn-number');
+            let turningNum = null;
+            if (that.sortFlag == 'add') {
+                turningNum = parseInt(currentTurn) + 1;
+            }else {
+                turningNum = parseInt(currentTurn) - 1 >= 0 ? (parseInt(currentTurn) - 1) : 9;
+            }
+            // if(el.getAttribute('flag')) {el.removeAttribute('flag');}
+            // let flag = parseInt(currentTurn) - 1;
+            // el.setAttribute('flag', flag);
+            // if(turningNum == 0) {
+            //     el.setAttribute('flag', -1);
+            // }
+            el.setAttribute('turn-number', turningNum);
+            if(el.style.transition == 'none 0s ease 0s' || turningNum == 1 || !el.style.transition) {
+                el.style.transition = `all linear ${that.during}ms`;
+            }
+            if(turningNum == 10 ||(that.sortFlag == 'reduce' && turningNum == 0)) {
+                var timeOut = null;
+                // el.style.top = `-${turningNum * 100}%`;
+                el.style.top = `-${that.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) {
+                            that.runStep(el.previousSibling);
+                        }
+                    }
+                }, 0.975 * that.during);
+            }else {
+                // el.style.top = `-${(10-turningNum)*100}%`;
+                el.style.top = `-${that.sortFlag == 'add'?turningNum*100:(10-turningNum)*100}%`;
+            }
+            // 用于递减的时候
+            if(el.style.top == '-100%' && that.sortFlag == 'reduce') {
+                that.runStep(el.previousSibling);
+            }
+        },
+        // 自定义图片
+        imgNumberScroll() {
+            let that = this;
+            var m = 1;
+            if(that.pointNum!=0){
+                m = (Math.pow(10,that.pointNum));
+            }
+            this.$nextTick(() => {
+                var f = that.$el.querySelector('.run-number-img');
+                setTimeout(() => {
+                    that.relNum = that.calculation(that.relNum, m * that.speed, '+');
+                }, that.during)
+                f.addEventListener('webkitTransitionEnd', () => {
+                    this.$emit('scroll-end');
+                    setTimeout(() => {
+                        that.relNum = that.calculation(that.relNum, m * that.speed, '+');
+                    }, that.during);
+                });
+            })
+            
+            // that.timer = setInterval(() => {
+            //     that.relNum = that.calculation(that.relNum, Math.floor(Math.random() * (20 - 4 + 1) + 4), '+');;
+            // }, that.during)
+            // let that = this;
+            // that.clearInterval();
+            // var m = 1;
+            // if(that.pointNum!=0){
+            //     m = (Math.pow(10,that.pointNum));
+            // }
+            // that.timer = setInterval(() => {
+                // if(that.sortFlag == 'add') {
+                //     that.relNum = that.calculation(that.relNum, m * that.speed, '+');
+                //     if(that.relNum >= that.endNum*m) {
+                //         that.relNum = that.endNum*m;
+                //         that.clearInterval();
+                //         this.$emit('scroll-end');
+                //     }
+                // }else {
+                //     that.relNum = that.calculation(that.relNum, m * that.speed, '-');
+                //     if(that.relNum <= 0) {
+                //         that.relNum = String(0).repeat(that.num_total_len);
+                //         that.clearInterval();
+                //         this.$emit('scroll-end');
+                //     }
+                // }
+                // that.relNum = that.calculation(that.relNum, m * that.speed, that.sortFlag == 'add'?'+':'-');
+                // if(that.relNum <= 0) {
+                //     that.relNum = String(0).repeat(that.num_total_len);
+                //     that.clearInterval();
+                //     this.$emit('scroll-end');
+                // }
+                // if(that.relNum >= that.endNum*m) {
+                //     that.relNum = that.endNum*m;
+                //     that.clearInterval();
+                // }
+            // }, that.during);
+        }
+    }
+}
+</script>

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

@@ -0,0 +1,69 @@
+<template>
+    <div class="demo-list">
+      <h4>基本用法</h4>
+      <div class="show-demo">
+        <nut-countup :init-num='0' :end-num='200'></nut-countup>
+
+        <nut-countup
+        :init-num='150.00' 
+        :end-num='0.00' 
+        :speed='6.18' 
+        :to-fixed='2'>
+        </nut-countup>
+
+        <nut-countup :init-num='1000.00' 
+        :end-num='0.00'
+        :speed='17'  
+        :to-fixed='2'>
+        </nut-countup>
+      </div>
+      <h4>数字滚动</h4>
+      <div class="show-demo">
+        <nut-countup :scrolling="true" :init-num='17.618' :during="600">
+        </nut-countup>
+      </div>
+      <h4>自定义滚动图片展示</h4>
+      <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="5000"
+        >
+        </nut-countup>
+      </div>
+    </div>
+</template>
+
+<script>
+export default {
+  components: {
+
+  },
+  data() {
+    return {
+        customNumber: 618, 
+        bgImage: 'https://img10.360buyimg.com/imagetools/jfs/t1/133024/3/2251/2646/5ee7549aE8dc02d7e/de6901b6c72db396.png',
+    };
+  },
+  methods: {
+      run() {
+        let timer = null;
+        timer = setInterval(() => {
+            this.customNumber = Math.floor(Math.random() * (700 - 100 + 1) + 100);
+        }, 5000);
+      }
+  },
+  mounted() {
+      this.run();
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.show-demo {
+  background: #ffffff;
+}
+</style>

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

@@ -0,0 +1,82 @@
+# CountUp 数字滚动
+
+## 基本用法
+
+```html
+<nut-countup :init-num='0' :end-num='200'></nut-countup>
+
+<nut-countup :init-num='150.00' :end-num='0.00' :speed='2.62' :to-fixed='2'></nut-countup>
+
+<nut-countup :init-num='1000.00' :end-num='0.00' :speed='6.3' :start-flag='startNum' :to-fixed='2'></nut-countup>
+```
+
+## 数字滚动
+
+```html
+<nut-countup :scrolling="true" :init-num='17.618' :during="600"></nut-countup>
+```
+
+## 自定义数字图片展示
+
+```html
+<nut-countup
+    :custom-change-num="customNumber"
+    :custom-bg-img="bgImage"
+    :custom-spac-num="11"
+    :num-width="33"
+    :num-height="47"
+    :during="5000"
+>
+</nut-countup>
+```
+```javascript
+export default {
+    data() {
+        return {
+            customNumber: 618, 
+            bgImage: 'https://img10.360buyimg.com/imagetools/jfs/t1/133024/3/2251/2646/5ee7549aE8dc02d7e/de6901b6c72db396.png'
+        };
+    },
+    methods: {
+        run() {
+            let timer = null;
+            timer = setInterval(() => {
+                this.customNumber = Math.floor(Math.random() * (700 - 100 + 1) + 100);
+            }, 5000);
+        }
+    },
+    mounted() {
+        this.run();
+    }
+};
+```
+
+
+## Prop
+
+| 字段 | 说明 | 类型 | 默认值
+|----- | ----- | ----- | ----- 
+| init-num | 初始数字 | Number | 0
+| end-num | 结束数字 | Number | 0
+| speed | 间隔数字,目前仅支持基本用法 | Number | 1
+| to-fixed | 保留小数点后几位 | Number | 以传入的数字为准
+| start-flag | 触发数字滚动的标识 | Boolean | true
+| during | 滚动一次运行时间 | Number | 1000
+| num-width | 数字宽度,常用于自定义无缝滚动 | Number | 20
+| num-height | 数字高度,常用于自定义无缝滚动 | Number | 20
+| scrolling | 用于数字滚动展示 | Boolean | false
+| custom | 用于自定义图片数字滚动 | Boolean | false
+| custom-change-num | 要变化的数字(用于自定义图片,initNum\endNum在此无效) | Number | 1
+| custom-bg-img | 自定义图片(建议使用雪碧图实现) | - | -
+| custom-spac-num | 图片中数字之间可能会存在间距 | Number | 0
+
+
+
+
+
+
+## Event
+
+| 字段 | 说明 | 回调参数
+|----- | ----- | -----
+| scroll-end | 滚动结束后回调函数 | - 

+ 8 - 0
src/packages/countup/index.js

@@ -0,0 +1,8 @@
+import CountUp from './countup.vue';
+import './countup.scss';
+
+CountUp.install = function(Vue) {
+  Vue.component(CountUp.name, CountUp);
+};
+
+export default CountUp

+ 1 - 1
src/packages/elevator/elevator.scss

@@ -1,6 +1,6 @@
 .nut-elevator {
   position: relative;
-  top: 40px;
+  // top: 40px;
   width: 100%;
 }
 .nut-main {

+ 13 - 9
src/packages/elevator/elevator.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="nut-elevator" :style="{ height: wrapHeight + 'px' }">
+  <div class="nut-elevator" :style="{ height: wrapHeight + 'px' }" v-if="dataArray.length>0">
     <div class="nut-main" :style="{ height: wrapHeight + 'px' }">
       <ul class="nut-elevator-ul" id="nut-elevator-ul">
         <li v-for="item in dataArray" v-bind:key="item.title" class="nut-list-title">
@@ -82,14 +82,23 @@ export default {
       currBox: false
     };
   },
+  watch:{
+    dataArray(val){
+      if(val.length>0){
+        this.$nextTick(()=>{
+            this.initPage();
+        })
+      }
+    }
+  },
   mounted() {
-    this.initPage();
+    if(this.dataArray.length>0 ){
+      this.initPage();
+    }   
   },
   methods: {
     initPage() {
-      let fontSize = this.getFontSize();
       let innerHeight = document.documentElement.clientHeight;
-      //this.wrapHeight = (innerHeight/fontSize-1);
       this.wrapHeight = innerHeight - this.otherHeight;
       let initIndex = this.dataArray[this.initIndex].title;
       document.getElementById(initIndex).scrollIntoView();
@@ -122,13 +131,11 @@ export default {
       titleBox.scrollIntoView();
     },
     onPointerEnd(e) {
-      let fontSize = this.getFontSize();
       let dataArrayLength = this.dataArray.length;
       let navHeight = document.getElementById('nut-elevator-nav').clientHeight;
       let navTop = document.getElementById('nut-elevator-nav').offsetTop;
       let navOffsetTop = navTop - navHeight / 2; //nav距离顶部的距离
       let eTop = e.type.indexOf('touch') !== -1 ? e.changedTouches[0].clientY : e.clientY;
-      //let navIndex =parseInt((eTop - navOffsetTop)/this.navHeight/fontSize);
       let navIndex = parseInt((eTop - navOffsetTop) / this.navHeight);
       setTimeout(() => {
         this.currBox = false;
@@ -139,15 +146,12 @@ export default {
     },
     onPointerMove(e) {
       e.preventDefault();
-      let fontSize = this.getFontSize();
       let dataArrayLength = this.dataArray.length;
       let navHeight = document.getElementById('nut-elevator-nav').clientHeight;
       let navTop = document.getElementById('nut-elevator-nav').offsetTop;
       let navOffsetTop = navTop - navHeight / 2; //nav距离顶部的距离
       let eTop = e.type.indexOf('touch') !== -1 ? e.touches[0].clientY : e.clientY;
-      //let navIndex =parseInt((eTop - navOffsetTop)/this.navHeight/fontSize);
       let navIndex = parseInt((eTop - navOffsetTop) / this.navHeight);
-
       if (navIndex < dataArrayLength && navIndex >= 0) {
         this.moveFun(this.dataArray[navIndex].title, navIndex);
         this.currBox = true;

+ 2 - 1
src/packages/luckdraw/demo.vue

@@ -53,6 +53,7 @@ export default {
         },
         {
           id: 'blue',
+          prizeColor: 'rgb(251, 219, 216)',
           prizeName: '蓝牙耳机',
           prizeImg: 'https://img13.360buyimg.com/imagetools/jfs/t1/91864/11/15108/139003/5e6f146dE1c7b511d/1ddc5aa6e502060a.jpg'
         },
@@ -63,6 +64,7 @@ export default {
         },
         {
           id: 'fruit',
+          prizeColor: 'rgba(246, 142, 46, 0.5)',
           prizeName: '迪士尼苹果',
           prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/108308/11/8890/237603/5e6f157eE489cccf1/26e0437cfd93b9c8.png'
         },
@@ -122,7 +124,6 @@ export default {
     },
     // 已经转动完转盘触发的函数
     endTurns() {
-      console.log(123);
       // 提示中奖
       this.$dialog({
         content: `恭喜中奖!!!${this.prizeList[this.prizeIndex].prizeName}`,

+ 9 - 7
src/packages/luckdraw/doc.md

@@ -45,32 +45,34 @@ export default {
           {
             id: 'xiaomi',
             prizeName: '小米手机',
-            prizeImg: 'https://img14.360buyimg.com/imagetools/jfs/t1/104165/34/15186/96522/5e6f1435E46bc0cb0/d4e878a15bfd9362.png',
+            prizeImg: 'https://img14.360buyimg.com/imagetools/jfs/t1/104165/34/15186/96522/5e6f1435E46bc0cb0/d4e878a15bfd9362.png'
           },
           {
             id: 'blue',
+            prizeColor: 'rgb(251, 219, 216)',
             prizeName: '蓝牙耳机',
-            prizeImg: 'https://img13.360buyimg.com/imagetools/jfs/t1/91864/11/15108/139003/5e6f146dE1c7b511d/1ddc5aa6e502060a.jpg',
+            prizeImg: 'https://img13.360buyimg.com/imagetools/jfs/t1/91864/11/15108/139003/5e6f146dE1c7b511d/1ddc5aa6e502060a.jpg'
           },
           {
             id: 'apple',
             prizeName: 'apple watch',
-            prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/105385/19/15140/111093/5e6f1506E48bd0dfb/829a98a8cdb4c27f.png',
+            prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/105385/19/15140/111093/5e6f1506E48bd0dfb/829a98a8cdb4c27f.png'
           },
           {
             id: 'fruit',
+            prizeColor: 'rgba(246, 142, 46, 0.5)',
             prizeName: '迪士尼苹果',
-            prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/108308/11/8890/237603/5e6f157eE489cccf1/26e0437cfd93b9c8.png',
+            prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/108308/11/8890/237603/5e6f157eE489cccf1/26e0437cfd93b9c8.png'
           },
           {
             id: 'fish',
             prizeName: '海鲜套餐',
-            prizeImg: 'https://img14.360buyimg.com/imagetools/jfs/t1/90507/38/15165/448364/5e6f15b4E5df0c718/4bd4c3d375eec312.png',
+            prizeImg: 'https://img14.360buyimg.com/imagetools/jfs/t1/90507/38/15165/448364/5e6f15b4E5df0c718/4bd4c3d375eec312.png'
           },
           {
             id: 'thanks',
             prizeName: '谢谢参与',
-            prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/96116/38/15085/5181/5e6f15d1E48e31d30/71353b61dff705d4.png',
+            prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/96116/38/15085/5181/5e6f15d1E48e31d30/71353b61dff705d4.png'
           }
         ],
         turnsNumber: 5, // 转动圈数
@@ -138,7 +140,7 @@ export default {
 | prize-list | 奖品列表 | Array | -
 | turns-number | 转动的圈数 | Number | 5
 | turns-time | 从开始转动到结束所用时间 | Number | 5(单位是秒)
-| style-opt | 转盘中的样式,包括每个扇区的背景颜色,扇区的边框颜色 | Object | {prizeBgColors: [],borderColor: ''}
+| style-opt | 转盘中的样式,包括每个扇区的背景颜色(在每条数据中页可单独设置prizeColor),扇区的边框颜色 | Object | {prizeBgColors: [],borderColor: ''}
 | pointerStyle | 转盘中指针的样式,包括背景图片、大小等 | Object | {width: '80px',height: '80px'}
 
 

+ 10 - 1
src/packages/luckdraw/luckdraw.vue

@@ -53,6 +53,11 @@ export default {
       default: 5
     }
   },
+  watch: {
+    prizeList: function(newVal, oldVal) {
+      this.init();
+    }
+  },
   data() {
     return {
       winningPrize: 0,
@@ -94,7 +99,11 @@ export default {
       ctx.strokeStyle = borderColor; // 设置画图线的颜色
       for (let index = 0; index < prizeNum; index++) {
         const angle = index * baseAngle;
-        ctx.fillStyle = prizeBgColors[index]; //设置每个扇形区域的颜色
+        if(this.prizeList[index]['prizeColor']) {
+          ctx.fillStyle = this.prizeList[index]['prizeColor']; //设置每个扇形区域的颜色,根据每条数据中单独设置的优先
+        }else {
+          ctx.fillStyle = prizeBgColors[index]; //设置每个扇形区域的颜色
+        }
         ctx.beginPath(); //开始绘制
         // 标准圆弧:arc(x,y,radius,startAngle,endAngle,anticlockwise)
         ctx.arc(canvasW * 0.5, canvasH * 0.5, outRadius, angle, angle + baseAngle, false);

+ 2 - 2
src/packages/popup/index.js

@@ -1,7 +1,7 @@
 import Popup from "./popup.vue";
 import "./popup.scss";
-import { overlayProps ,getProps} from "./overlay/overlay-manager";
-
+import { getProps} from "./overlay/overlay-manager";
+import { overlayProps } from './overlay/overlay.vue';
 Popup.install = function(Vue) {
   Vue.component(Popup.name, Popup);
 };

+ 8 - 6
src/packages/popup/overlay/overlay-manager.js

@@ -42,15 +42,15 @@ const overlayManager = {
 
   //打开遮罩层
   openModal(vm, config) {
-    let { zIndex, duration, className, customStyle } = config;
-
+    let { zIndex, duration, overlayClass, overlayStyle} = config;
+ 
     modalStack.push({
       vm,
       config: {
         zIndex,
         duration,
-        className,
-        customStyle,
+        overlayClass,
+        overlayStyle,
       },
     });
 
@@ -105,8 +105,10 @@ const overlayProps = {
     default: "",
   },
   overlayStyle: {
-    type: String,
-    default: "",
+    type: Object,
+    default: function () {
+      return null
+    },
   },
   zIndex: {
     type: Number

+ 4 - 3
src/packages/popup/overlay/overlay.vue

@@ -2,7 +2,7 @@
   <transition name="popup-fade">
     <div
       @touchmove.stop="touchmove"
-      :style="{ animationDuration: `${duration}s`, overlayStyle, zIndex }"
+      :style="{ animationDuration: `${duration}s`, ...overlayStyle, zIndex }"
       v-show="value"
       class="popup-bg nut-mask"
       :class="overlayClass"
@@ -36,8 +36,8 @@ const overlayProps = {
     default: "",
   },
   overlayStyle: {
-    type: String,
-    default: "",
+    type: Object,
+    default: ()=>{},
   },
   zIndex: {
     type: Number
@@ -47,6 +47,7 @@ export { overlayProps };
 export default {
   name: "nut-popup-overlay",
   props: overlayProps,
+   
   methods: {
     touchmove(e) {
       if (this.lockScroll) {

+ 2 - 4
src/packages/popup/popup.scss

@@ -77,8 +77,7 @@ $popup-close-icon-margin: 16px;
   overflow-y: auto;
   background-color: #fff;
   transition: transform 0.3s;
-  -webkit-overflow-scrolling: touch;
-  z-index: 100;
+  -webkit-overflow-scrolling: touch; 
 }
 @keyframes nut-fade-in {
   from {
@@ -144,8 +143,7 @@ $popup-close-icon-margin: 16px;
   left: 0;
   width: 100%;
   height: 100%;
-  background-color: rgba(0, 0, 0, 0.7);
-  z-index: 99;
+  background-color: rgba(0, 0, 0, 0.7); 
 }
 
 @keyframes nut-fade-in {

+ 3 - 2
src/packages/popup/popup.vue

@@ -25,7 +25,8 @@
 import Vue from 'vue';
 import Icon from '../icon/icon.vue';
 import touchMixins from '../../mixins/touch.js';
-import { overlayManager, overlayProps } from './overlay/overlay-manager.js';
+import { overlayManager } from './overlay/overlay-manager.js';
+import { overlayProps } from './overlay/overlay.vue';
 import { on, off } from '../../utils/event';
 import '../icon/icon.scss';
 
@@ -161,7 +162,7 @@ export default {
       const { scrollHeight, offsetHeight, scrollTop } = el ? el : this.$el;
 
       if ((this.deltaY > 0 && scrollTop === 0) || (this.deltaY < 0 && scrollTop + offsetHeight >= scrollHeight)) {
-        //event.preventDefault();
+        event.preventDefault();
       }
     },
     getScroller(el) {

+ 9 - 3
src/packages/switch/demo.vue

@@ -58,11 +58,17 @@
     </div>
         <h4>内嵌文字标签</h4>
     <div>
-      <nut-cell>
+            <nut-cell desc="开|关">
+        <span slot="title"><nut-switch :active="true" label="开|关"></nut-switch></span>
+      </nut-cell>
+      <nut-cell desc="ON|OFF">
         <span slot="title"><nut-switch :active="true" label="ON|OFF"></nut-switch></span>
       </nut-cell>
-           <nut-cell>
-        <span slot="title"><nut-switch :active="false" label="通过|拒绝"></nut-switch></span>
+           <nut-cell desc="通 过 | 拒 绝">
+        <span slot="title"><nut-switch :active="false" label="通 过|拒 绝"></nut-switch></span>
+      </nut-cell>
+       <nut-cell desc="large尺寸, 打开的状态|禁用的状态">
+        <span slot="title"><nut-switch size="large" :active="false" label="打开的状态|禁用的状态"></nut-switch></span>
       </nut-cell>
     </div>
   </div>

+ 16 - 4
src/packages/switch/switch.scss

@@ -12,13 +12,15 @@
     position:relative;
     width:auto;
     left:0;
-    margin-left:22px;
+    margin-left:20px;
+    margin-right:3px;
     display: inline-block !important;
     padding:0 2px 0 2px  !important;
     text-align:center !important;
     color:#999 !important;
     font-style:normal !important;
     font-size:12px;
+    bottom: 2px;
   }
   .nut-switch-btn {
     position: absolute;
@@ -33,7 +35,7 @@
   &.nut-switch-active {
     border-color: $border-color-active;
    .nut-switch-label {
-    left:0;
+    left:4px;
     margin-left:0px;
     color:red !important;
     margin-right:22px;
@@ -49,7 +51,7 @@
 
 .nut-switch-small {
   height: 14px;
-  min-width: 36px;
+  min-width: 30px;
   .nut-switch-label {
     font-size: 10px;
     top:-4px;
@@ -69,7 +71,7 @@
 
 .nut-switch-base {
   height: 20px;
-  min-width: 46px;
+  min-width:38px;
   .nut-switch-btn {
     height: 20px;
     width: 22px;
@@ -83,14 +85,24 @@
 }
 
 .nut-switch-large {
+  
   height: 28px;
   min-width: 58px;
   font-size: 14px;
+  .nut-switch-label {
+    margin-right: 8px;
+    left: 6px;
+    top: 2px;
+   }
   .nut-switch-btn {
     height: 28px;
     min-width: 28px;
   }
   &.nut-switch-active {
+    .nut-switch-label {
+      top:2px;
+      margin-right:30px;
+     }
     .nut-switch-btn {
       left: 100%;
       margin-left:-28px;

+ 21 - 12
src/packages/uploader/demo.vue

@@ -69,7 +69,7 @@
       </nut-cell>
     </div>
 
-    <p>自定义headers&formData</p>
+    <p>自定义 headers&formData </p>
     <div>
       <nut-cell>
         <span slot="title">
@@ -93,6 +93,13 @@
 
     <p>预览上传图片</p>
     <div>
+      <transition name="fade">
+        <div class="preview-box">
+          <div class="img-outbox" v-for="(item,index) in previewImg" :key="index">
+            <img class="img-box" v-if="item" :src="item" alt />
+          </div>
+        </div>
+    </transition>
       <nut-cell>
         <span slot="title">
           <nut-uploader
@@ -104,6 +111,7 @@
             @fail="demoFail"
             @preview="preview"
             @showMsg="showMsg1"
+            :multiple="true"
           >
             <nut-button small>上传</nut-button>
           </nut-uploader>
@@ -111,7 +119,7 @@
         <div slot="desc"></div>
       </nut-cell>
     </div>
-    <p>上传图片前处理图片内容</p>
+    <p>开始上传钩子 @start</p>
     <nut-cell>
       <span slot="title">
         <nut-uploader
@@ -129,11 +137,11 @@
       </span>
       <div slot="desc"></div>
     </nut-cell>
-    <p>自定义增加上传图片数据</p>
+    <p>自定义增加上传数据 self-data</p>
     <nut-cell>
       <span slot="title">
         <nut-uploader
-          :selfData="selfData"
+          :self-data="selfData"
           :name="name"
           :url="url"
           :xhrState="stateNum"
@@ -147,11 +155,7 @@
       </span>
       <div slot="desc"></div>
     </nut-cell>
-    <transition name="fade">
-      <div class="img-outbox">
-        <img class="img-box" v-if="previewImg" :src="previewImg" alt />
-      </div>
-    </transition>
+    
   </div>
 </template>
 
@@ -177,7 +181,7 @@ export default {
         f2: 'test1'
       },
       progressNum: 0,
-      previewImg: null,
+      previewImg: [],
       previewImg2: null,
       progressNum2: null,
       upOver: false,
@@ -222,8 +226,9 @@ export default {
     progress(file, loaded, total) {
       this.progressNum = parseInt((100 * loaded) / total);
     },
-    preview(file) {
-      this.previewImg = file;
+    preview(file) {      
+      let previewImg = this.previewImg;
+      this.previewImg = [...previewImg,...file];      
     },
     showMsg1(msg) {
       this.$toast.text(msg);
@@ -279,4 +284,8 @@ export default {
   max-height: 100%;
   border-radius: 6px;
 }
+.preview-box{
+  display: flex;
+  padding-bottom: 10px;
+}
 </style>

+ 184 - 180
src/packages/uploader/uploader.vue

@@ -1,192 +1,196 @@
 <template>
-  <div class="nut-uploader">
-    <slot></slot>
-    <input type="file" :name="name" @change="upload($event)" class="uploader" :multiple="multiple" />
-  </div>
+    <div class="nut-uploader">
+        <slot></slot>
+        <input
+            type="file"
+            :name="name"
+            @change="upload($event)"
+            class="uploader"
+            :multiple="multiple"
+			:disabled="disabled"
+			:accept="acceptType"
+        />
+    </div>
 </template>
 <script>
-import Uploader from '../../utils/uploader.js';
-import locale from '../../mixins/locale';
+import Uploader from "../../utils/uploader.js";
+import locale from "../../mixins/locale";
 export default {
-  name: 'nut-uploader',
-  mixins: [locale],
-  props: {
-    name: {
-      type: String,
-      default: ''
-    },
-    url: {
-      type: String,
-      default: ''
-    },
-    multiple: {
-      type: Boolean,
-      default: false
-    },
-    isPreview: {
-      type: Boolean,
-      default: false
-    },
-    maxSize: {
-      type: Number,
-      default: 5242880
-    },
-    acceptType: {
-      type: Array,
-      default() {
-        return ['image/jpeg', 'image/png', 'image/gif', 'image/bmp'];
-      }
-    },
-    selfData: {
-      type: Object,
-      default() {
-        return {};
-      }
-    },
-    attach: {
-      type: Object,
-      default() {
-        return {};
-      }
+    name: "nut-uploader",
+    mixins: [locale],
+    props: {
+        name: {
+            type: String,
+            default: "file"
+        },
+        url: {
+            type: String,
+            default: ""
+        },
+        multiple: {
+            type: Boolean,
+            default: false
+		},
+		disabled: {
+			type: Boolean,
+            default: false
+		},
+        isPreview: {
+            type: Boolean,
+            default: false
+        },
+        maxSize: {
+            type: Number,
+            default: 5242880
+        },
+        acceptType: {
+            type: Array,
+            default() {
+                return ["image/jpeg", "image/png", "image/gif", "image/bmp"];
+            }
+        },
+        selfData: {
+            type: Object,
+            default() {
+                return {};
+            }
+        },
+        attach: {
+            type: Object,
+            default() {
+                return {};
+            }
+        },
+        headers: {
+            type: Object,
+            default() {
+                return {};
+            }
+        },
+        beforeUpload: {
+            type: Function
+        },
+        xhrState: {
+            type: Number,
+            default: 200
+        },
+        clearInput: {
+            type: Boolean,
+            default: true
+        },
+        xmlError: {
+            type: String,
+            default: ""
+        },
+        typeError: {
+            type: String,
+            default: "不支持上传该类型文件"
+        },
+        limitError: {
+            type: String,
+            default: "对不起,您的浏览器不支持本组件"
+        },
+        withCredentials: {
+            type: Boolean,
+            default: false
+        }
     },
-    headers: {
-      type: Object,
-      default() {
+    data() {
         return {};
-      }
-    },
-    changeEvtCallback: {
-      type: Function
-    },
-    beforeUpload: {
-      type: Function
     },
-    xhrState: {
-      type: Number,
-      default: 200
-    },
-    clearInput: {
-      type: Boolean,
-      default: false
-    },
-    xmlError: {
-      type: String,
-      default: ''
-    },
-    typeError: {
-      type: String,
-      default: ''
-    },
-    limitError: {
-      type: String,
-      default: ''
-    },
-    withCredentials: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return {};
-  },
-  methods: {
-    createUploaderOpts() {
-      const _this = this;
-      return {
-        $el: {},
-        url: this.url, //图片上传地址
-        formData: null,
-        headers: {}, //自定义headers
-        isPreview: this.isPreview, //是否开启本地预览
-        previewData: null,
-        maxSize: this.maxSize, //允许上传的文件最大字节
-        acceptType: this.acceptType, //允许上传的文件类型
-        xhrState: this.xhrState,
-        clearInput: this.clearInput,
-        withCredentials: this.withCredentials, //支持发送 cookie 凭证信息
-        xmlError: this.xmlError || this.nutTranslate('lang.uploader.xmlError'),
-        typeError: this.typeError || this.nutTranslate('lang.uploader.typeError'),
-        limitError: this.limitError || this.nutTranslate('lang.uploader.limitError'),
-        onStart() {
-          _this.$emit('start');
-        },
-        onProgress(file, loaded, total) {
-          _this.$emit('progress', file, loaded, total);
-        },
-        onPreview(previewFile) {
-          _this.$emit('preview', previewFile);
-        },
-        onSuccess(file, responseTxt) {
-          _this.$emit('success', file, responseTxt);
-        },
-        onFailure(file, responseTxt) {
-          _this.$emit('failure', file, responseTxt);
-        }
-      };
-    },
-    uploadData($event, selfData = {}) {
-      const tar = $event.target;
-      if (!this.url) {
-        this.$emit('showMsg', '请先配置上传url');
-        this.$emit('afterChange', tar, $event);
-        return;
-      }
-      const formData = new FormData();
-      for (let key of Object.keys(this.attach)) {
-        formData.append(key, this.attach[key]);
-      }
-      let finialyOutData = Object.assign(this.selfData, selfData);
-      if (finialyOutData) {
-        for (let key in finialyOutData) {
-          formData.append(key, finialyOutData[key]);
-        }
-      }
-      const opt = this.createUploaderOpts();
-      opt.$el = tar;
-      if (this.isPreview) {
-        opt.previewData = tar.files[0];
-      }
-      if (this.multiple) {
-        for (let i = 0; i < tar.files.length; i++) {
-          if (tar.files[i]) {
-            if (this.acceptType.indexOf(tar.files[i].type) == -1) {
-              this.$emit('showMsg', opt.typeError);
-              return;
+    methods: {
+        createUploaderOpts() {
+            const _this = this;
+            return {
+                $el: {},
+                url: this.url, //图片上传地址
+                formData: null,
+                headers: {}, //自定义headers
+                isPreview: this.isPreview, //是否开启本地预览
+                previewData: null,
+                maxSize: this.maxSize, //允许上传的文件最大字节
+                acceptType: this.acceptType, //允许上传的文件类型
+                xhrState: this.xhrState,
+                clearInput: this.clearInput,
+                withCredentials: this.withCredentials, //支持发送 cookie 凭证信息
+                xmlError:
+                    this.xmlError ||
+                    this.nutTranslate("lang.uploader.xmlError"),
+                typeError:
+                    this.typeError ||
+                    this.nutTranslate("lang.uploader.typeError"),
+                limitError:
+                    this.limitError ||
+                    this.nutTranslate("lang.uploader.limitError"),
+                onStart() {
+                    _this.$emit("start");
+                },
+                onProgress(file, loaded, total) {
+                    _this.$emit("progress", file, loaded, total);
+                },
+                onPreview(previewFile) {				
+                    _this.$emit("preview", previewFile);
+                },
+                onSuccess(file, responseTxt) {
+                    _this.$emit("success", file, responseTxt);
+                },
+                onFailure(file, responseTxt) {
+                    _this.$emit("failure", file, responseTxt);
+                }
+            };
+        },
+        uploadData($event, selfData = {}) {
+            const tar = $event.target;
+            if (!this.url) {
+                this.$emit("showMsg", "请先配置上传url");
+                this.$emit("afterChange", tar, $event);
+                return;
+            }
+            const formData = new FormData();
+            const opt = this.createUploaderOpts();
+            opt.$el = tar;
+            if (this.isPreview) {				
+                opt.previewData = tar.files;
+			}
+			let len = this.multiple ? tar.files.length : 1;     
+            formData.append(tar.name, tar.files[0]);
+            for (const key of Object.keys(this.attach)) {
+                formData.append(key, this.attach[key]);
+            }
+            const finialyOutData = Object.assign(this.selfData, selfData);
+            if (finialyOutData) {
+                for (const key in finialyOutData) {
+                    formData.append(key, finialyOutData[key]);
+                }
+            }
+            opt.formData = formData;
+            opt.headers = this.headers || {};
+            opt.showMsgFn = (msg) => {
+                this.$emit("showMsg", msg);
+            };
+            new Uploader(opt);
+            this.$emit("afterChange", tar, $event);
+        },
+        async upload($event) {
+            if (typeof this.beforeUpload === "function") {
+                const promise = new Promise((reslove, reject) => {
+                    reslove(this.beforeUpload($event));
+                });
+                const resData = await promise;
+                if (
+                    typeof resData === "object" &&
+                    typeof resData.event === "object"
+                ) {
+                    this.uploadData(resData.event, resData.data);
+                } else {
+                    console.warn(
+                        "resData: 必须包含 event字段且为input $event 的事件对象"
+                    );
+                }
+            } else {
+                this.uploadData($event);
             }
-          }
-        }
-      } else {
-        if (tar.files[0]) {
-          if (this.acceptType.indexOf(tar.files[0].type) == -1) {
-            this.$emit('showMsg', opt.typeError);
-            return;
-          }
-        }
-      }
-      formData.append(tar.name, tar.files[0]);
-      opt.formData = formData;
-      opt.headers = this.headers || {};
-      opt.showMsgFn = msg => {
-        this.$emit('showMsg', msg);
-      };
-      new Uploader(opt);
-      this.$emit('afterChange', tar, $event);
-    },
-    async upload($event) {
-      if (typeof this.beforeUpload === 'function') {
-        let promise = new Promise((reslove, reject) => {
-          reslove(this.beforeUpload($event));
-        });
-        let resData = await promise;
-        if (typeof resData === 'object' && typeof resData.event === 'object') {
-          this.uploadData(resData.event, resData.data);
-        } else {
-          console.warn('resData: 必须包含 event字段且为input $event 的事件对象');
         }
-      } else {
-        this.uploadData($event);
-      }
     }
-  }
 };
 </script>

+ 123 - 109
src/utils/uploader.js

@@ -1,114 +1,128 @@
-class IdaUploader {
-  constructor(settings) {
-    this.options = {
-      url: '',
-      formData: null,
-      headers: {}, //自定义headers
-      withCredentials: false, //支持发送 cookie 凭证信息
-      isPreview: true, //是否开启本地预览
-      previewData: null,
-      maxSize: 0, //允许上传的文件最大字节,0为不限制
-      acceptType: [], //允许上传的文件类型,如'image/jpeg'
-      showMsgFn: null,
-      onStart: null,
-      onProgress: null,
-      onPreview: null,
-      onSuccess: null,
-      onFailure: null,
-      xhrStatus: 200, //默认上传成功是200
-      readyState: 4,
-      xmlError: null,
-      typeError: null,
-      limitError: null
-    };
-    Object.assign(this.options, settings);
-    this[this.options.isPreview ? 'preview' : 'uploader']();
-  }
-  triggerFunc(func) {
-    if (typeof func === 'function') {
-      return func.bind(this);
-    } else {
-      console.warn(func + 'is not a function!');
-      return function() {};
-    }
-  }
-  showMsg(msg) {
-    if (typeof this.options.showMsgFn == 'function') {
-      this.options.showMsgFn(msg);
-    } else {
-      console.log(msg);
-    }
-  }
-  check(file) {
-    if (Array.isArray(file)) {
-      for (let key in file) {
-        if (this.options.maxSize && file[key].size > this.options.maxSize) {
-          this.showMsg(this.options.limitError);
-          return false;
-        }
-        if (this.options.acceptType.length && this.options.acceptType.indexOf(file[key].type) === -1) {
-          this.showMsg(this.options.typeError);
-          return false;
-        }
-      }
-    } else {
-      if (this.options.maxSize && file.size > this.options.maxSize) {
-        this.showMsg(this.options.limitError);
-        return false;
-      }
-      if (this.options.acceptType.length && this.options.acceptType.indexOf(file.type) === -1) {
-        this.showMsg(this.options.typeError);
-        return false;
-      }
-    }
-    return true;
-  }
-  preview() {
-    const file = this.options.previewData;
-    if (!this.check(file)) return;
-    const reader = new FileReader();
-    reader.onload = e => {
-      this.uploader();
-      this.triggerFunc.call(this.options, this.options.onPreview)(e.target.result);
-    };
-    reader.readAsDataURL(file);
-  }
-  uploader() {
-    const xhr = new XMLHttpRequest();
-    let options = this.options;
-    let formData = options.formData;
 
-    if (xhr.upload) {
-      xhr.upload.addEventListener(
-        'progress',
-        e => {
-          this.triggerFunc.call(options, options.onProgress)(formData, e.loaded, e.total);
-        },
-        false
-      );
-      xhr.onreadystatechange = e => {
-        if (xhr.readyState === 4) {
-          if (xhr.status === options.xhrState) {
-            this.triggerFunc.call(options, options.onSuccess)(formData, xhr.responseText);
-          } else {
-            this.triggerFunc.call(options, options.onFailure)(formData, xhr.responseText);
-          }
+class IdaUploader {
+   constructor (settings) {
+       this.options = {
+           url: '',
+           formData: null,
+           headers: {}, //自定义headers
+           withCredentials:false,//支持发送 cookie 凭证信息
+           isPreview: true, //是否开启本地预览
+           previewData: null,
+           maxSize: 0, //允许上传的文件最大字节,0为不限制
+           acceptType: [], //允许上传的文件类型,如'image/jpeg'
+           showMsgFn: null,
+           onStart: null,
+           onProgress: null,
+           onPreview: null,
+           onSuccess: null,
+           onFailure: null,
+           xhrStatus:200, //默认上传成功是200
+           readyState:4,
+           xmlError:null, 
+           typeError:null, 
+           limitError:null 
+       };
+       Object.assign(this.options, settings);
+       this[this.options.isPreview ? 'preview' : 'uploader']()
+   }
+   triggerFunc(func) {
+        if (typeof(func)==='function') {
+            return func.bind(this);
+        } else {
+            console.warn(func + 'is not a function!');
+            return function() {};
         }
-      };
-      xhr.withCredentials = options.withCredentials;
-      xhr.open('POST', options.url, true);
-      // headers
-      for (let key in options.headers) {
-        xhr.setRequestHeader(key, options.headers[key]);
-      }
-      this.triggerFunc.call(options, options.onStart)();
-      xhr.send(formData);
-      if (options.clearInput) {
-        options.$el.value = '';
-      }
-    } else {
-      this.showMsg(this.xmlError);
     }
-  }
+   showMsg (msg) {       
+       if (typeof(this.options.showMsgFn)=='function') {
+           this.options.showMsgFn(msg);
+       } else {
+           console.log(msg);
+       }
+   }
+   check (file) {
+       if(Array.isArray(file)){           
+           for(let key in file){
+                if (this.options.maxSize && (file[key].size > this.options.maxSize)) {
+                    this.showMsg(this.options.limitError);
+                    return false;
+                }
+                if (this.options.acceptType.length && this.options.acceptType.indexOf(file[key].type) === -1) {           
+                    this.showMsg(this.options.typeError);
+                    return false;
+                }
+           }
+       }else{
+            if (this.options.maxSize && (file.size > this.options.maxSize)) {
+                this.showMsg(this.options.limitError);
+                return false;
+            }
+            if (this.options.acceptType.length && this.options.acceptType.indexOf(file.type) === -1) {           
+                this.showMsg(this.options.typeError);
+                return false;
+            }
+       }       
+       return true;
+   }
+   preview () {  
+       const file = Array.from(this.options.previewData);   
+        if (!this.check(file)) return;
+        let promArray = []
+        file.map(item=>{             
+            let temp  = new Promise((resolve,reject)=>{
+                const reader = new FileReader(); 
+                reader.readAsDataURL(item);
+                reader.onload = (e) => {                
+                    this.uploader();
+                    resolve(e)
+                }  
+            })
+            promArray.push(temp)   
+        });
+        Promise.all(promArray).then(res=>{
+            console.log(res)
+            let out = [];
+            if(res){
+                res.map(item=>{
+                    out.push(item.target.result) 
+                })
+            }
+            this.triggerFunc.call(this.options, this.options.onPreview)(out); 
+        }) 
+   }
+   uploader () {
+       const xhr = new XMLHttpRequest();
+       let options = this.options;
+       let formData = options.formData;      
+       
+       
+       if (xhr.upload) {    
+           xhr.upload.addEventListener('progress', (e) => {
+               this.triggerFunc.call(options, options.onProgress)(formData, e.loaded, e.total);
+           }, false);
+           xhr.onreadystatechange = (e) => {              
+               if (xhr.readyState === 4) {                  
+                   if (xhr.status === options.xhrState) {
+                        this.triggerFunc.call(options, options.onSuccess)(formData, xhr.responseText);
+                   } else {
+                        this.triggerFunc.call(options, options.onFailure)(formData, xhr.responseText);
+                   }
+               }
+           };
+           xhr.withCredentials = options.withCredentials;
+           xhr.open('POST', options.url, true);
+           // headers
+           for (let key in options.headers) {
+                xhr.setRequestHeader(key, options.headers[key])
+           }
+           this.triggerFunc.call(options, options.onStart)();          
+           xhr.send(formData);
+           if(options.clearInput){
+                options.$el.value = ''  ;
+           }           
+       } else {
+           this.showMsg(this.xmlError)
+       }
+   }
 }
 export default IdaUploader;

+ 67 - 66
types/nutui.d.ts

@@ -15,70 +15,71 @@ export const locale: (l: any) => void;
 
 export function install(vue: typeof Vue, options: InstallationOptions): void;
 
-export declare class ActionSheet extends UIComponent {}
-export declare class Badge extends UIComponent {}
-export declare class Button extends UIComponent {}
-export declare class ButtonGroup extends UIComponent {}
-export declare class Calendar extends UIComponent {}
-export declare class Cell extends UIComponent {}
-export declare class CheckBox extends UIComponent {}
-export declare class CheckBoxGroup extends UIComponent {}
-export declare class Col extends UIComponent {}
-export declare class DatePicker extends UIComponent {}
-export declare class Dialog extends UIComponent {}
-export declare class Flex extends UIComponent {}
-export declare class Grid extends UIComponent {}
-export declare class GridCol extends UIComponent {}
-export declare class GridRow extends UIComponent {}
-export declare class Icon extends UIComponent {}
-export declare class ImagePicker extends UIComponent {}
-export declare class Menu extends UIComponent {}
-export declare class NavBar extends UIComponent {}
-export declare class NoticeBar extends UIComponent {}
-export declare class Picker extends UIComponent {}
-export declare class Price extends UIComponent {}
-export declare class Progress extends UIComponent {}
-export declare class Radio extends UIComponent {}
-export declare class RadioGroup extends UIComponent {}
-export declare class Range extends UIComponent {}
-export declare class Rate extends UIComponent {}
-export declare class Row extends UIComponent {}
-export declare class SearchBar extends UIComponent {}
-export declare class ShortPassword extends UIComponent {}
-export declare class Skeleton extends UIComponent {}
-export declare class Slider extends UIComponent {}
-export declare class Stepper extends UIComponent {}
-export declare class Steps extends UIComponent {}
-export declare class Swiper extends UIComponent {}
-export declare class Switch extends UIComponent {}
-export declare class Tab extends UIComponent {}
-export declare class Tabbar extends UIComponent {}
-export declare class TabPanel extends UIComponent {}
-export declare class Toast extends UIComponent {}
-export declare class BackTop extends UIComponent {}
-export declare class Scroller extends UIComponent {}
-export declare class CountDown extends UIComponent {}
-export declare class Uploader extends UIComponent {}
-export declare class TextInput extends UIComponent {}
-export declare class TextBox extends UIComponent {}
-export declare class Avatar extends UIComponent {}
-export declare class Infiniteloading extends UIComponent {}
-export declare class Lazyload extends UIComponent {}
-export declare class Elevator extends UIComponent {}
-export declare class LeftSlip extends UIComponent {}
-export declare class TabSelect extends UIComponent {}
-export declare class Popup extends UIComponent {}
+export declare class ActionSheet extends UIComponent { }
+export declare class Badge extends UIComponent { }
+export declare class Button extends UIComponent { }
+export declare class ButtonGroup extends UIComponent { }
+export declare class Calendar extends UIComponent { }
+export declare class Cell extends UIComponent { }
+export declare class CheckBox extends UIComponent { }
+export declare class CheckBoxGroup extends UIComponent { }
+export declare class Col extends UIComponent { }
+export declare class DatePicker extends UIComponent { }
+export declare class Dialog extends UIComponent { }
+export declare class Flex extends UIComponent { }
+export declare class Grid extends UIComponent { }
+export declare class GridCol extends UIComponent { }
+export declare class GridRow extends UIComponent { }
+export declare class Icon extends UIComponent { }
+export declare class ImagePicker extends UIComponent { }
+export declare class Menu extends UIComponent { }
+export declare class NavBar extends UIComponent { }
+export declare class NoticeBar extends UIComponent { }
+export declare class Picker extends UIComponent { }
+export declare class Price extends UIComponent { }
+export declare class Progress extends UIComponent { }
+export declare class Radio extends UIComponent { }
+export declare class RadioGroup extends UIComponent { }
+export declare class Range extends UIComponent { }
+export declare class Rate extends UIComponent { }
+export declare class Row extends UIComponent { }
+export declare class SearchBar extends UIComponent { }
+export declare class ShortPassword extends UIComponent { }
+export declare class Skeleton extends UIComponent { }
+export declare class Slider extends UIComponent { }
+export declare class Stepper extends UIComponent { }
+export declare class Steps extends UIComponent { }
+export declare class Swiper extends UIComponent { }
+export declare class Switch extends UIComponent { }
+export declare class Tab extends UIComponent { }
+export declare class Tabbar extends UIComponent { }
+export declare class TabPanel extends UIComponent { }
+export declare class Toast extends UIComponent { }
+export declare class BackTop extends UIComponent { }
+export declare class Scroller extends UIComponent { }
+export declare class CountDown extends UIComponent { }
+export declare class Uploader extends UIComponent { }
+export declare class TextInput extends UIComponent { }
+export declare class TextBox extends UIComponent { }
+export declare class Avatar extends UIComponent { }
+export declare class Infiniteloading extends UIComponent { }
+export declare class Lazyload extends UIComponent { }
+export declare class Elevator extends UIComponent { }
+export declare class LeftSlip extends UIComponent { }
+export declare class TabSelect extends UIComponent { }
+export declare class Popup extends UIComponent { }
 
-export declare class LuckDraw extends UIComponent {}
-export declare class Video extends UIComponent {}
-export declare class Signature extends UIComponent {}
-export declare class CircleProgress extends UIComponent {}
-export declare class TimeLine extends UIComponent {}
-export declare class TimeLineItem extends UIComponent {}
-export declare class SideNavBar extends UIComponent {}
-export declare class SubSideNavBar extends UIComponent {}
-export declare class SideNavBarItem extends UIComponent {}
-export declare class Qart extends UIComponent {}
-export declare class Drag extends UIComponent {}
-export declare class Address extends UIComponent {}
-export declare class Notify extends UIComponent {}
+export declare class LuckDraw extends UIComponent { }
+export declare class Video extends UIComponent { }
+export declare class Signature extends UIComponent { }
+export declare class CircleProgress extends UIComponent { }
+export declare class TimeLine extends UIComponent { }
+export declare class TimeLineItem extends UIComponent { }
+export declare class SideNavBar extends UIComponent { }
+export declare class SubSideNavBar extends UIComponent { }
+export declare class SideNavBarItem extends UIComponent { }
+export declare class Qart extends UIComponent { }
+export declare class Drag extends UIComponent { }
+export declare class Address extends UIComponent { }
+export declare class Notify extends UIComponent { }
+export declare class CountUp extends UIComponent { }