Browse Source

Merge remote-tracking branch 'origin/v2-dev'

richard1015 5 years ago
parent
commit
0fdecb2201
80 changed files with 1815 additions and 305 deletions
  1. 3 1
      .postcssrc.js
  2. 18 0
      CHANGELOG.md
  3. 2 2
      docs/international.md
  4. 1 1
      docs/start.md
  5. 1 1
      docs/theme.md
  6. 7 4
      package.json
  7. 13 0
      src/assets/svg/back.svg
  8. 15 0
      src/assets/svg/close.svg
  9. 1 0
      src/assets/svg/notice.svg
  10. 20 10
      src/config.json
  11. 28 18
      src/nutui.js
  12. 10 1
      src/packages/actionsheet/actionsheet.vue
  13. 0 1
      src/packages/actionsheet/demo.vue
  14. 26 12
      src/packages/address/address.vue
  15. 11 1
      src/packages/address/demo.vue
  16. 13 4
      src/packages/address/doc.md
  17. 14 18
      src/packages/backtop/demo.vue
  18. 1 7
      src/packages/button/demo.vue
  19. 1 1
      src/packages/buttongroup/demo.vue
  20. 3 5
      src/packages/cell/demo.vue
  21. 4 4
      src/packages/circleprogress/demo.vue
  22. 2 2
      src/packages/datepicker/datepicker.vue
  23. 40 36
      src/packages/dialog/_dialog.js
  24. 2 1
      src/packages/dialog/demo.vue
  25. 53 22
      src/packages/dialog/dialog.scss
  26. 12 0
      src/packages/dialog/dialog.vue
  27. 19 6
      src/packages/dialog/doc.md
  28. 3 0
      src/packages/drag/demo.vue
  29. 0 2
      src/packages/drag/drag.scss
  30. 2 2
      src/packages/drag/drag.vue
  31. 1 1
      src/packages/elevator/demo.vue
  32. 1 1
      src/packages/elevator/elevator.scss
  33. 13 9
      src/packages/elevator/elevator.vue
  34. 91 0
      src/packages/fixednav/demo.vue
  35. 124 0
      src/packages/fixednav/doc.md
  36. 135 0
      src/packages/fixednav/fixednav.scss
  37. 78 0
      src/packages/fixednav/fixednav.vue
  38. 8 0
      src/packages/fixednav/index.js
  39. 1 1
      src/packages/flex/demo.vue
  40. 79 0
      src/packages/gesture/demo.vue
  41. 54 0
      src/packages/gesture/doc.md
  42. 3 0
      src/packages/gesture/gesture.scss
  43. 147 0
      src/packages/gesture/index.js
  44. 1 1
      src/packages/icon/demo.vue
  45. 2 2
      src/packages/icon/icon.vue
  46. 1 1
      src/packages/imagepicker/demo.vue
  47. 1 1
      src/packages/lazyload/demo.vue
  48. 4 3
      src/packages/leftslip/demo.vue
  49. 1 1
      src/packages/luckdraw/demo.vue
  50. 1 1
      src/packages/menu/demo.vue
  51. 2 2
      src/packages/navbar/demo.vue
  52. 5 4
      src/packages/noticebar/doc.md
  53. 32 23
      src/packages/noticebar/noticebar.scss
  54. 47 33
      src/packages/noticebar/noticebar.vue
  55. 32 0
      src/packages/notify/__test__/notify.spec.js
  56. 110 0
      src/packages/notify/_notify.js
  57. 144 0
      src/packages/notify/demo.vue
  58. 120 0
      src/packages/notify/doc.md
  59. 11 0
      src/packages/notify/index.js
  60. 28 0
      src/packages/notify/notify.scss
  61. 103 0
      src/packages/notify/notify.vue
  62. 7 22
      src/packages/popup/demo.vue
  63. 1 1
      src/packages/rate/demo.vue
  64. 5 0
      src/packages/rate/rate.vue
  65. 1 1
      src/packages/signature/demo.vue
  66. 19 5
      src/packages/stepper/stepper.vue
  67. 11 11
      src/packages/swiper/demo.vue
  68. 9 3
      src/packages/switch/demo.vue
  69. 16 4
      src/packages/switch/switch.scss
  70. 1 1
      src/packages/tab/demo.vue
  71. 4 4
      src/packages/tabbar/demo.vue
  72. 1 0
      src/packages/tabbar/doc.md
  73. 12 2
      src/packages/tabbar/tabbar.vue
  74. 1 1
      src/packages/tabselect/demo.vue
  75. 1 1
      src/packages/textbox/demo.vue
  76. 1 0
      src/packages/textbox/textbox.vue
  77. 1 1
      src/packages/timeline/demo.vue
  78. 1 1
      src/packages/video/demo.vue
  79. 15 0
      src/styles/variable.scss
  80. 3 1
      types/nutui.d.ts

+ 3 - 1
.postcssrc.js

@@ -2,6 +2,8 @@ module.exports = {
   plugins: {
     'postcss-import': {},
     'postcss-url': {},
-    autoprefixer: {}
+    autoprefixer: {
+      overrideBrowserslist: ['last 20 versions'] 
+    }
   }
 };

+ 18 - 0
CHANGELOG.md

@@ -1,3 +1,21 @@
+## 2.2.7
+
+`2020-07-09`
+
+* :sparkles: feat(FixedNav):新增悬浮导航组件 @richard1015
+* :sparkles: feat(Notify):新增通知组件 @shenqistart
+* :sparkles: feat(CountUp):新增数字滚动组件 @Ymm0008
+* :sparkles: upd(Elevator):优化电梯组件 @zhenyulei
+* :sparkles: upd(NoticeBar):重构优化内部代码 @shenqistart
+* :bug: fix(Rate):修复props 数据监听问题 @yushuang
+* :bug: fix(TextBox): 修复文本域初始化字数展示问题 @guoxiao158
+* :bug: fix(Dialog):滚动模式bug修复、新增close关闭事件 @guoxiao158
+* :bug: fix(Stepper):判断max小于min的情况 @yangxiaolu1993
+* :bug: fix(Address): 地址组件点击左上角返回问题,支持自定义图标 @yangxiaolu1993
+* :bug: fix(Drag):优化拖拽组件demo显示问题 @zy19940510 @richard1015
+* :bug: fix(Switch):修复内嵌文字居中 @zjyau
+* :bug: fix(Datepicker)修复设置起止范围,如月份是个位数(1-9月),天数的开始条件无效问题 @zjyau
+
 ## 2.2.6
 
 `2020-06-19`

+ 2 - 2
docs/international.md

@@ -20,7 +20,7 @@ Vue.use(NutUI, {
 
 ### 按需引用组件
 
-通过 **[@nutui/babel-plugin-seperate-import](https://www.npmjs.com/package/@nutui/babel-plugin-separate-import)** 插件,我们可以根据项目需要引用 NutUI 的组件,最终只打包引用的组件,减少引入代码的体积。国际化功能同样支持按需引用的方式。
+通过 **[@nutui/babel-plugin-separate-import](https://www.npmjs.com/package/@nutui/babel-plugin-separate-import)** 插件,我们可以根据项目需要引用 NutUI 的组件,最终只打包引用的组件,减少引入代码的体积。国际化功能同样支持按需引用的方式。
 
 ```javascript
 import Vue from 'vue';
@@ -31,7 +31,7 @@ import enUS from '@nutui/nutui/dist/locales/lang/en-US';
 locale('en-US', enUS);
 ```
 
-> 请注意:通过该插件进行按需引用组件时默认引用的是构建后的文件,此时并不支持国际化的功能。如需使用组件库的国际化功能,需要在 babel 的配置文件(如.babelrc)中将 **@nutui/babel-plugin-seperate-import** 插件的 **sourceCode** 参数值设为 **true** 。这样插件将引用未经构建的源文件,同时引用的组件也不再具有 `install` 方法,请使用 `Vue.component` 对组件进行注册。
+> 请注意:通过该插件进行按需引用组件时默认引用的是构建后的文件,此时并不支持国际化的功能。如需使用组件库的国际化功能,需要在 babel 的配置文件(如.babelrc)中将 **@nutui/babel-plugin-separate-import** 插件的 **sourceCode** 参数值设为 **true** 。这样插件将引用未经构建的源文件,同时引用的组件也不再具有 `install` 方法,请使用 `Vue.component` 对组件进行注册。
 
 ```bash
 {

+ 1 - 1
docs/start.md

@@ -143,7 +143,7 @@ NutUI.install(Vue);
 
 以下两种方式都可以实现只加载用到的组件,从而减少加载的文件体积。
 
-### 1. 使用 webpack 插件 **[@nutui/babel-plugin-seperate-import](https://www.npmjs.com/package/@nutui/babel-plugin-separate-import)** (推荐)
+### 1. 使用 webpack 插件 **[@nutui/babel-plugin-separate-import](https://www.npmjs.com/package/@nutui/babel-plugin-separate-import)** (推荐)
 
 首先安装 **@nutui/babel-plugin-separate-import** 插件
 

+ 1 - 1
docs/theme.md

@@ -87,7 +87,7 @@ import Button from '@nutui/nutui/dist/packages/button/button.js';
 import '@nutui/nutui/dist/packages/button/button.scss';
 ```
 
-3.使用了插件 **[@nutui/babel-plugin-seperate-import](https://www.npmjs.com/package/@nutui/babel-plugin-separate-import)** 进行按需引用时,需修改babel的配置文件(如.babelrc),将 **style** 的设置为 **scss**。该插件将会自动引入指定组件对应的 SCSS 文件。
+3.使用了插件 **[@nutui/babel-plugin-separate-import](https://www.npmjs.com/package/@nutui/babel-plugin-separate-import)** 进行按需引用时,需修改babel的配置文件(如.babelrc),将 **style** 的设置为 **scss**。该插件将会自动引入指定组件对应的 SCSS 文件。
 
 ```bash
 {

+ 7 - 4
package.json

@@ -1,6 +1,6 @@
 {
   "name": "@nutui/nutui",
-  "version": "2.2.6",
+  "version": "2.2.7",
   "description": "一套轻量级移动端Vue组件库",
   "typings": "dist/types/index.d.ts",
   "main": "dist/nutui.js",
@@ -20,7 +20,7 @@
     "test": "cross-env NODE_ENV=test nyc mocha-webpack --webpack-config node_modules/@nutui/cli/dist_cli/webpack/test.config.js  --require node_modules/@nutui/cli/dist_cli/test/setup.js src/packages/*/__test__/**.spec.js",
     "coveralls": "cat ./coverage/lcov.info | coveralls",
     "test:watch": "npm run test --watch",
-    "prettier:fix": "prettier --write src/**/*.{js,vue,scss}"
+    "prettier:fix": "prettier --write src/**/*.{ts,js,vue,scss}"
   },
   "husky": {
     "hooks": {
@@ -38,7 +38,8 @@
     "webpack",
     "vue component",
     "jdc",
-    "jdcfe"
+    "jdcfe",
+    "jdl"
   ],
   "author": "jdcfe",
   "license": "MIT",
@@ -48,9 +49,10 @@
     "@babel/plugin-transform-runtime": "^7.9.6",
     "@commitlint/cli": "^8.0.0",
     "@commitlint/config-conventional": "^8.0.0",
-    "@nutui/cli": "^0.2.2",
+    "autoprefixer": "^9.8.4",
     "babel-plugin-istanbul": "^6.0.0",
     "gsap": "^3.2.6",
+    "hammerjs": "^2.0.8",
     "husky": "^3.0.0",
     "vue-lazyload": "^1.3.3",
     "vue-qr": "^2.2.1"
@@ -59,6 +61,7 @@
     "vue": "^2.6.10"
   },
   "devDependencies": {
+    "@nutui/cli": "^0.3.3",
     "@babel/plugin-syntax-dynamic-import": "^7.8.3",
     "@babel/plugin-transform-object-assign": "^7.8.3",
     "@typescript-eslint/eslint-plugin": "^2.16.0",

File diff suppressed because it is too large
+ 13 - 0
src/assets/svg/back.svg


File diff suppressed because it is too large
+ 15 - 0
src/assets/svg/close.svg


File diff suppressed because it is too large
+ 1 - 0
src/assets/svg/notice.svg


+ 20 - 10
src/config.json

@@ -1,13 +1,5 @@
 {
-  "sorts": [
-    "数据展示",
-    "数据录入",
-    "操作反馈",
-    "导航组件",
-    "布局组件",
-    "基础组件",
-    "业务组件"
-  ],
+  "sorts": ["数据展示", "数据录入", "操作反馈", "导航组件", "布局组件", "基础组件", "业务组件"],
   "packages": [
     {
       "name": "Cell",
@@ -635,6 +627,15 @@
     },
     {
       "version": "1.0.0",
+      "name": "Notify",
+      "type": "method",
+      "chnName": "展示消息提示",
+      "desc": "在页面顶部展示消息提示,支持函数调用和组件调用两种方式",
+      "sort": "2",
+      "showDemo": true,
+      "author": "wangyue217"
+    },
+    {
       "name": "CountUp",
       "type": "component",
       "chnName": "数字滚动",
@@ -642,6 +643,15 @@
       "sort": "0",
       "showDemo": true,
       "author": "yumingming"
+    },
+    {
+      "name": "FixedNav",
+      "type": "component",
+      "chnName": "悬浮导航",
+      "desc": "拖拽导航",
+      "sort": "3",
+      "showDemo": true,
+      "author": "richard1015"
     }
   ]
-}
+}

+ 28 - 18
src/nutui.js

@@ -119,13 +119,20 @@ import './packages/subsidenavbar/subsidenavbar.scss';
 import SideNavBarItem from './packages/sidenavbaritem/index.js';
 import './packages/sidenavbaritem/sidenavbaritem.scss';
 import Drag from './packages/drag/index.js';
-import './packages/drag/drag.scss'; // import VueQr from "./packages/qart/index.js";
+import './packages/drag/drag.scss'; 
+// import VueQr from "./packages/qart/index.js";
 // import "./packages/qart/qart.scss";
 
 import Address from './packages/address/index.js';
 import './packages/address/address.scss';
-import CountUp from "./packages/countup/index.js";
-import "./packages/countup/countup.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';
+import FixedNav from './packages/fixednav/index.js';
+import './packages/fixednav/fixednav.scss';
+// import Gesture from './packages/gesture/index.js';
+// import './packages/gesture/gesture.scss';
 
 const packages = {
   Cell,
@@ -176,19 +183,22 @@ const packages = {
   Elevator,
   Popup,
   LeftSlip,
-  TabSelect: TabSelect,
-  LuckDraw: LuckDraw,
-  Video: Video,
-  Signature: Signature,
-  CircleProgress: CircleProgress,
-  TimeLine: TimeLine,
-  TimeLineItem: TimeLineItem,
-  SideNavBar: SideNavBar,
-  SubSideNavBar: SubSideNavBar,
-  SideNavBarItem: SideNavBarItem,
-  Drag: Drag,
-  Address: Address,
-  CountUp: CountUp
+  TabSelect,
+  LuckDraw,
+  Video,
+  Signature,
+  CircleProgress,
+  TimeLine,
+  TimeLineItem,
+  SideNavBar,
+  SubSideNavBar,
+  SideNavBarItem,
+  Drag,
+  Address,
+  Notify,
+  CountUp,
+  FixedNav,
+  // Gesture: Gesture
 };
 
 const components = {};
@@ -216,7 +226,7 @@ pkgList.map(item => {
   }
 });
 
-const install = function (Vue, opts = {}) {
+const install = function(Vue, opts = {}) {
   if (install.installed) return;
 
   if (opts.locale) {
@@ -272,4 +282,4 @@ export default {
   ...filters,
   ...directives,
   ...methods
-};
+};

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

@@ -82,12 +82,21 @@ export default {
         if (value) {
           document.body.classList.add('nut-overflow-hidden');
         } else {
-          document.body.classList.remove('nut-overflow-hidden');
+          this.remolveLockScrool();
         }
       }
     }
   },
+  deactivated() {
+    this.remolveLockScroll();
+  },
+  destroyed() {
+    this.remolveLockScroll();
+  },
   methods: {
+    remolveLockScroll() {
+      document.body.classList.remove('nut-overflow-hidden');
+    },
     isHighlight(item) {
       return (this.chooseTagValue && this.chooseTagValue == item[this.optionTag]) || this.chooseTagValue === 0;
     },

+ 0 - 1
src/packages/actionsheet/demo.vue

@@ -77,7 +77,6 @@
     <nut-actionsheet :is-visible="isVisible5" @close="switchActionSheet('isVisible5')">
       <div slot="custom" class="custom-wrap"><span>自定义</span></div>
     </nut-actionsheet>
-    <div style="height: 400px;"></div>
   </div>
 </template>
 

+ 26 - 12
src/packages/address/address.vue

@@ -10,14 +10,15 @@
       @open="closeWay = 'self'"
     >
       <div class="title">
-        <span class="arrow" @click="switchModule">
-          <nut-icon v-if="showModule == 'custom' && type == 'exist'" type="self" :url="require('../../assets/svg/arrows-back.svg')"></nut-icon>
+        <span class="arrow" @click="switchModule" v-if="showModule == 'custom' && type == 'exist' && backBtnIcon">
+          <nut-icon type="self" :url="backBtnIcon"></nut-icon>
         </span>
+        <span class="arrow" v-else></span>
 
         <span v-if="type == 'custom'">{{ customAddressTitle }}</span>
         <span v-if="type == 'exist'">{{ existAddressTitle }}</span>
 
-        <span @click="handClose('hand')"><nut-icon type="circle-cross" size="18px"></nut-icon></span>
+        <span @click="handClose('cross')"><nut-icon v-if="closeBtnIcon" size="18px" type="self" :url="closeBtnIcon"></nut-icon></span>
       </div>
 
       <!-- 请选择 -->
@@ -136,6 +137,16 @@ export default {
       // 地址选择列表前 - 选中的图标
       type: String,
       default: require('../../assets/svg/tick-red.svg')
+    },
+    closeBtnIcon: {
+      // 关闭弹框按钮 icon
+      type: String,
+      default: require('../../assets/svg/circle-cross.svg')
+    },
+    backBtnIcon: {
+      // 选择其他地址左上角返回 icon
+      type: String,
+      default: require('../../assets/svg/arrows-back.svg')
     }
   },
   data() {
@@ -296,26 +307,24 @@ export default {
       if (this.closeWay == 'self') {
         this.$emit('close', res);
       } else {
-        // this.$emit('close',{type:'hand'})
+        this.$emit('closeMask', { closeWay: this.closeWay });
       }
 
       setTimeout(() => {
         that.showModule = 'type';
       }, 500);
     },
-    // 手动关闭 点击叉号,或者蒙层
+    // 手动关闭 点击叉号(cross),或者蒙层(mask)
     handClose(type = 'self') {
-      if (type == 'hand') {
-        this.closeWay = 'hand';
-      } else {
-        this.closeWay = 'self';
-      }
+      if (!this.closeBtnIcon) return;
+
+      this.closeWay = type == 'cross' ? 'cross' : 'self';
 
       this.showPopup = false;
     },
     // 点击遮罩层关闭
     clickOverlay() {
-      this.closeWay = 'hand';
+      this.closeWay = 'mask';
     },
     // 初始化
     initAddress() {
@@ -327,7 +336,12 @@ export default {
     },
     // 选择其他地址
     switchModule() {
-      this.showModule = this.showModule == 'exist' ? 'custom' : 'exist';
+      if (this.showModule == 'exist') {
+        this.showModule = 'custom';
+      } else {
+        this.showModule = 'exist';
+      }
+
       this.initAddress();
 
       this.$emit('switchModule', { type: this.showModule });

+ 11 - 1
src/packages/address/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="address-box">
+  <div class="address-box demo-list">
     <h4>选择自定义地址</h4>
     <div class="address-list init" @click="showAddress">
       <div class="titile">选择地址</div>
@@ -55,6 +55,7 @@
       @selected="selected3"
       :defaultIcon="defaultIcon"
       :selectedIcon="selectedIcon"
+      :closeBtnIcon="closeBtnIcon"
     ></nut-address>
 
     <nut-address
@@ -65,11 +66,13 @@
       :city="city"
       :country="country"
       :town="town"
+      :backBtnIcon="backBtnIcon"
       @onChange="onChange4"
       @close="close4"
       @selected="selected4"
       customAndExistTitle="选择其他地址"
       @switchModule="switchModule"
+      @closeMask="closeMask"
     ></nut-address>
   </div>
 </template>
@@ -104,6 +107,9 @@ export default {
       selectedIcon: require('../../assets/svg/checked.svg'),
       defaultIcon: require('../../assets/svg/unchecked.svg'),
 
+      closeBtnIcon: require('../../assets/svg/close.svg'),
+      backBtnIcon: require('../../assets/svg/back.svg'),
+
       showPopupOther: false,
       existAddress: [
         { id: 1, addressDetail: 'th ', cityName: '石景山区', countyName: '城区', provinceName: '北京', selectedAddress: true, townName: '' },
@@ -226,6 +232,10 @@ export default {
       } else {
         console.log('点击了自定义地址左上角的返回按钮');
       }
+    },
+
+    closeMask(val) {
+      console.log('关闭弹层', val);
     }
   }
 };

+ 13 - 4
src/packages/address/doc.md

@@ -133,7 +133,8 @@ export default {
   :isShowCustomAddress="false" 
   @selected="selected3" 
   :defaultIcon="defaultIcon" 
-  :selectedIcon='selectedIcon'>
+  :selectedIcon='selectedIcon'
+  :closeBtnIcon="closeBtnIcon">
 </nut-address>
 
 ```
@@ -146,6 +147,7 @@ export default {
         showPopupCustomImg:false,
         selectedIcon:require('../../assets/svg/checked.svg'),
         defaultIcon:require('../../assets/svg/unchecked.svg'),
+        closeBtnIcon: require('../../assets/svg/close.svg'),
         existAddress:[
           {"id":1,"addressDetail":"th ","cityName":"石景山区","countyName":"城区","provinceName":"北京","selectedAddress":true,"townName":""},
           {"id":2,"addressDetail":"12_ ","cityName":"电饭锅","countyName":"扶绥县","provinceName":"北京","selectedAddress":false,"townName":""},
@@ -200,11 +202,13 @@ export default {
     :city="city" 
     :country="country" 
     :town="town" 
+    :backBtnIcon="backBtnIcon"
     customAndExistTitle="选择其他地址"
     @onChange="onChange4" 
     @close="close4" 
     @selected="selected4"
-    @switchModule="switchModule">
+    @switchModule="switchModule"
+    @closeMask="closeMask">
 </nut-address>
 
 
@@ -225,6 +229,7 @@ export default {
       city:[{"id":7,"name":"朝阳区"},{"id":8,"name":"崇文区"},{"id":9,"name":"昌平区"},{"id":6,"name":"石景山区"}],// 市
       country:[{"id":3,"name":"八里庄街道"},{"id":9,"name":"北苑"},{"id":4,"name":"常营乡"}],// 县
       town:[], // 镇 必传值:name
+      backBtnIcon: require('../../assets/svg/back.svg'),
     };
   },
   methods: {
@@ -249,12 +254,14 @@ export default {
       console.log(nowExistAdd)
     },
     switchModule(cal){
-        
         if(cal.type == 'custom'){
           console.log('点击了“选择其他地址”按钮')
         } else {
           console.log('点击了自定义地址左上角的返回按钮')
         }
+    },
+    closeMask(val){
+      console.log('关闭弹层',val)
     }
   }
 }
@@ -274,6 +281,8 @@ export default {
 | existAddress | 已存在地址列表,每个地址对象中,必传值provinceName、cityName、countyName、townName、addressDetail、selectedAddress(字段解释见下) | Array | []
 | defaultIcon | 已有地址列表默认图标,type=‘exist’ 时生效 | string | ''
 | selectedIcon | 已有地址列表选中图标,type=‘exist’ 时生效 | string | ''
+| closeBtnIcon | 自定义关闭弹框按钮图标 | string | -
+| backBtnIcon | 自定义地址与已有地址切换时,自定义返回的按钮图标 | string | -
 | isShowCustomAddress | 是否可以切换自定义地址选择,type=‘exist’ 时生效 | Boolean | true
 | customAddressTitle  | 自定义地址选择文案,type='custom' 时生效 | string | '请选择所在地区'
 | existAddressTitle| 已有地址文案 ,type=‘exist’ 时生效| string | '配送至'
@@ -293,6 +302,7 @@ export default {
 | onChange | 自定义选择地址时,选择地区时触发 |  参考 onChange
 | selected | 选择已有地址列表时触发 | 参考 selected
 | close | 地址选择弹框关闭时触发 | 参考 close
+| closeMask | 点击遮罩层或点击右上角叉号关闭时触发 | {closeWay:'mask'/'cross'}
 | switchModule | 点击‘选择其他地址’或自定义地址选择左上角返回按钮触发 | {type:'exist'/'custom'}
 
 
@@ -316,4 +326,3 @@ export default {
 | type | 地址选择类型 exist/custom  |  exist/custom
 | data | 选择地址的值,custom 时,addressStr 为选择的地址组合 | {} 
 
-注:点击叉号或者点击遮罩层关闭地址选择,不会触发 close 事件。

+ 14 - 18
src/packages/backtop/demo.vue

@@ -55,29 +55,25 @@
     <nut-backtop @click="handleClick"></nut-backtop>
     <nut-backtop :bottom="120" :is-animation="false" @click="handleClick">
       <div
-        style="
-          background-color: rgb(0, 0, 0);
-          color: rgb(255, 255, 255);
-          display: flex;
-          height: 44px;
-          width: 44px;
-          align-items: center;
-          justify-content: center;
-        "
+        style="background-color: rgb(0, 0, 0);
+    color: rgb(255, 255, 255);
+    display: flex;
+    height: 44px;
+    width: 44px;
+    align-items: center;
+    justify-content: center;"
         >无</div
       >
     </nut-backtop>
     <nut-backtop :bottom="70" @click="handleClick">
       <div
-        style="
-          background-color: rgb(0, 0, 0);
-          color: rgb(255, 255, 255);
-          display: flex;
-          height: 44px;
-          width: 44px;
-          align-items: center;
-          justify-content: center;
-        "
+        style="background-color: rgb(0, 0, 0);
+    color: rgb(255, 255, 255);
+    display: flex;
+    height: 44px;
+    width: 44px;
+    align-items: center;
+    justify-content: center;"
         >Top</div
       >
     </nut-backtop>

+ 1 - 7
src/packages/button/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list-pd">
     <h4>常规按钮</h4>
     <div class="bg">
       <nut-button @click="clickHandler" :disabled="disabled">去结算</nut-button>
@@ -90,12 +90,6 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.bg {
-  margin: 0 10px;
-}
-.gap {
-  height: 10px;
-}
 .white-bg {
   padding: 10px;
   background: #fff;

+ 1 - 1
src/packages/buttongroup/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list">
     <h4>常规按钮组</h4>
     <nut-buttongroup>
       <nut-button type="light">重置</nut-button>

+ 3 - 5
src/packages/cell/demo.vue

@@ -1,10 +1,8 @@
 <template>
-  <div>
+  <div class="demo-list">
     <h4>基本用法</h4>
-    <div>
-      <nut-cell title="我是标题" desc="描述文字" @click-cell="clickEvnt" to="/index"> </nut-cell>
-      <nut-cell :is-link="true" link-url="//m.jd.com" :show-icon="true" title="带链接" target="_target"> </nut-cell>
-    </div>
+    <nut-cell title="我是标题" desc="描述文字" @click-cell="clickEvnt" to="/index"> </nut-cell>
+    <nut-cell :is-link="true" link-url="//m.jd.com" :show-icon="true" title="带链接" target="_target"> </nut-cell>
     <h4>通过Slot插槽分发内容</h4>
     <div>
       <nut-cell :is-link="true" :show-icon="true">

+ 4 - 4
src/packages/circleprogress/demo.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="demo-list">
-    <p>基本用法</p>
+    <h4>基本用法</h4>
     <div>
       <nut-cell>
         <span slot="title">
@@ -8,7 +8,7 @@
         </span>
       </nut-cell>
     </div>
-    <p>环形进度条自定义样式</p>
+    <h4>环形进度条自定义样式</h4>
     <div>
       <nut-cell>
         <span slot="title">
@@ -16,7 +16,7 @@
         </span>
       </nut-cell>
     </div>
-    <p>环形进度条自定义内容</p>
+    <h4>环形进度条自定义内容</h4>
     <div>
       <nut-cell>
         <span slot="title">
@@ -26,7 +26,7 @@
         </span>
       </nut-cell>
     </div>
-    <p>动态改变环形进度条的进度</p>
+    <h4>动态改变环形进度条的进度</h4>
     <div>
       <nut-cell>
         <span slot="title">

+ 2 - 2
src/packages/datepicker/datepicker.vue

@@ -239,8 +239,8 @@ export default {
       month = this.removeChinese(month);
       let days = Array.from(Array(Utils.getMonthDays(year, month)), (v, k) => {
         if (
-          !(year == this.startDateArr[0] && month == this.startDateArr[1] && k + 1 < this.startDateArr[2]) &&
-          !(year == this.endDateArr[0] && month == this.endDateArr[1] && k + 1 > this.endDateArr[2])
+          !(year == this.startDateArr[0] && month == parseInt(this.startDateArr[1]) && k + 1 < parseInt(this.startDateArr[2])) &&
+          !(year == this.endDateArr[0] && month == parseInt(this.endDateArr[1]) && k + 1 > parseInt(this.endDateArr[2]))
         ) {
           return `${k + 1}${this.chinese[2]}`;
         }

+ 40 - 36
src/packages/dialog/_dialog.js

@@ -6,40 +6,44 @@ let DialogConstructor = Vue.extend(settings);
 let inst;
 
 let Dialog = function(options) {
-  options.id = options.id || 'nut-dialog-default-id';
-  if (options.type === 'image' && typeof options.closeBtn === 'undefined') {
-    options.closeBtn = true;
-  }
-
-  inst = new DialogConstructor({
-    propsData: options
-  });
-
-  inst.vm = inst.$mount();
-
-  let dialogDom = document.querySelector('#' + options.id);
-  if (options.id && dialogDom) {
-    dialogDom.parentNode.replaceChild(inst.$el, dialogDom);
-  } else {
-    document.body.appendChild(inst.$el);
-  }
-
-  // setTimeout(() => {
-  //     //  设置z-index保证最新的弹窗再最上面
-  //     let dialogThis = document.querySelector('#'+options.id);
-  //     var nutDialogWrapper = document.getElementsByClassName('nut-dialog-wrapper');
-  //     var zIndexNum = 100 + (10 * (nutDialogWrapper.length));
-  //     dialogThis.style.zIndex = zIndexNum;
-  //     setTimeout(function() {
-  //         for(var i = 0;i < nutDialogWrapper.length;i++) {
-  //             nutDialogWrapper[i].style.zIndex = zIndexNum - 1 - i;
-  //         }
-  //     }, 0);
-  // }, 10);
-
-  Vue.nextTick(() => {
-    inst.visible = true;
-  });
+    options.id = options.id || 'nut-dialog-default-id';
+    if (options.type === 'image' && typeof options.closeBtn === 'undefined') {
+        options.closeBtn = true;
+    }
+
+    inst = new DialogConstructor({
+        propsData: options
+    });
+
+    inst.vm = inst.$mount();
+
+    let dialogDom = document.querySelector('#' + options.id);
+    if (options.id && dialogDom) {
+        dialogDom.parentNode.replaceChild(inst.$el, dialogDom);
+    } else {
+        document.body.appendChild(inst.$el);
+    }
+
+    // setTimeout(() => {
+    //     //  设置z-index保证最新的弹窗再最上面
+    //     let dialogThis = document.querySelector('#'+options.id);
+    //     var nutDialogWrapper = document.getElementsByClassName('nut-dialog-wrapper');
+    //     var zIndexNum = 100 + (10 * (nutDialogWrapper.length));
+    //     dialogThis.style.zIndex = zIndexNum;
+    //     setTimeout(function() {
+    //         for(var i = 0;i < nutDialogWrapper.length;i++) {
+    //             nutDialogWrapper[i].style.zIndex = zIndexNum - 1 - i;
+    //         }
+    //     }, 0);
+    // }, 10);
+
+    Vue.nextTick(() => {
+        inst.visible = true;
+    });
 };
-
-export default Dialog;
+Dialog.closed = function() {
+    if (inst) {
+        inst.close()
+    }
+}
+export default Dialog;

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

@@ -103,7 +103,8 @@ export default {
     showDialog1: function() {
       const options = {
         title: '确定删除此订单?',
-        content: '删除后将从你的记录里消失,无法找回'
+        content: '删除后将从你的记录里消失,无法找回',
+        closeOnPopstate: true
       };
 
       this.$dialog(options);

+ 53 - 22
src/packages/dialog/dialog.scss

@@ -96,7 +96,36 @@ body.dialog-open {
       no-repeat center;
     background-size: 13px 13px;
     img {
-      height: 13px;
+        height: 13px;
+    }
+}
+
+.nut-dialog-image-wrapper {
+    .nut-dialog {
+        width: auto;
+        max-width: 80%;
+        max-height: 75%;
+        background: transparent;
+        border-radius: none;
+        display: inline-block;
+        overflow: visible;
+    }
+    .nut-dialog-close {
+        position: absolute;
+        right: 0;
+        top: -40px;
+        width: 25px;
+        height: 25px;
+        font-size: 20px;
+        text-align: center;
+        text-decoration: none;
+        border: 2px solid #fff;
+        border-radius: 50%;
+        background: url("data:image/svg+xml, %3Csvg width='30' height='30' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='rgb(255,255,255)' fill-rule='evenodd'%3E%3Cpath d='M.44 2.56A1.5 1.5 0 1 1 2.56.44l27 27a1.5 1.5 0 1 1-2.12 2.12L15 17.123 2.56 29.56A1.5 1.5 0 1 1 .44 27.44L12.878 15 .44 2.56zM27.44.44a1.5 1.5 0 1 1 2.12 2.12l-9 9a1.5 1.5 0 1 1-2.12-2.12l9-9z'/%3E%3C/g%3E%3C/svg%3E") no-repeat center;
+        background-size: 13px 13px;
+        img {
+            height: 13px;
+        }
     }
   }
 }
@@ -112,11 +141,12 @@ body.dialog-open {
 }
 
 .nut-dialog-body {
-  box-sizing: border-box;
-  padding: 30px 20px 20px;
-  display: flex;
-  flex-direction: column;
-  flex: 0 1 auto;
+    box-sizing: border-box;
+    padding: 30px 20px 20px;
+    display: flex;
+    flex-direction: column;
+    flex: 0 1 auto;
+    overflow: auto;
 }
 
 .nut-dialog-content {
@@ -166,23 +196,24 @@ body.dialog-open {
 }
 
 .nut-dialog-ok {
-  border-radius: 0 0 5px 0;
-  background: $btn-gradient-bg;
-  background-origin: border-box;
-  color: $btn-gradient-color;
-  margin-right: -1px;
-  &:active {
-    background: $btn-gradient-active-bg;
-  }
+    border-radius: 0 0 5px 0;
+    background: $btn-gradient-bg;
+    border-top: 1px solid $light-color;
+    background-origin: border-box;
+    color: $btn-gradient-color;
+    margin-right: -1px;
+    &:active {
+        background: $btn-gradient-active-bg;
+    }
 }
 
 .nut-dialog-cancel {
-  border-radius: 0 0 0 5px;
-  border-top: 1px solid $light-color;
-  background: #fff;
-  background-origin: border-box;
-  &:active {
-    border-top: 1px solid #ccc;
-    background: #ccc;
-  }
+    border-radius: 0 0 0 5px;
+    border-top: 1px solid $light-color;
+    background: #fff;
+    background-origin: border-box;
+    &:active {
+        border-top: 1px solid #ccc;
+        background: #ccc;
+    }
 }

+ 12 - 0
src/packages/dialog/dialog.vue

@@ -170,6 +170,10 @@ export default {
     customClass: {
       type: String,
       default: ''
+    },
+    closeOnPopstate: {
+      type: Boolean,
+      default: false
     }
   },
   data() {
@@ -181,6 +185,14 @@ export default {
   created() {
     this.destroy = true;
   },
+  mounted() {
+    var that = this;
+    if (that.closeOnPopstate) {
+      window.addEventListener('popstate', function() {
+        that.close();
+      });
+    }
+  },
   methods: {
     modalClick() {
       if (!this.closeOnClickModal) {

+ 19 - 6
src/packages/dialog/doc.md

@@ -10,6 +10,10 @@ this.$dialog({
   content: "删除后将从你的记录里消失,无法找回"
 });
 ```
+## 直接关闭当前dialog
+```javascript
+this.$dialog.closed()  //可以直接关闭当前dialog
+```
 
 ## ID
 
@@ -107,7 +111,7 @@ export default {
 }
 ```
 
-## API
+## prop
 
 | 字段 | 说明 | 类型 | 默认值
 |----- | ----- | ----- | ----- 
@@ -125,14 +129,23 @@ export default {
 | okBtnDisabled | 禁用确定按钮 | Boolean | false
 | cancelAutoClose | 取消按钮是否默认关闭弹窗 | Boolean | true
 | textAlign | 文字对齐方向,可选值同css的text-align | String | "center"
-| onOkBtn | 确定按钮回调 | Function | -
-| onCancelBtn | 取消按钮回调 | Function | -
-| onCloseBtn | 关闭按钮回调 | Function | -
-| closeCallback | 关闭回调,任何情况关闭弹窗都会触发 | Function | -
-| onClickImageLink | 图片链接点击回调,仅对图片类型弹窗有效 | Function | -
 | maskBgStyle | 遮罩层样式(颜色、透明度) | String | -
 | customClass | 增加一个自定义class | String | -
 | link | 点击图片跳转的Url,仅对图片类型弹窗有效 | String | -
 | imgSrc | 图片Url,仅对图片类型弹窗有效 | String | -
 | animation | 是否开启默认动效 | Boolean | true
+| closeOnPopstate | 是否在页面回退时自动关闭 | Boolean | false
 | lockBgScroll | 锁定遮罩层滚动,不影响弹窗内部滚动(实验性质)会给body添加posotion:fix属性,注意 | Boolean | false
+
+
+## 事件
+
+| 字段 | 说明 | 类型 | 默认值
+|----- | ----- | ----- | ----- 
+
+| onOkBtn | 确定按钮回调 | Function | -
+| onCancelBtn | 取消按钮回调 | Function | -
+| onCloseBtn | 关闭按钮回调 | Function | -
+| closeCallback | 关闭回调,任何情况关闭弹窗都会触发 | Function | -
+| onClickImageLink | 图片链接点击回调,仅对图片类型弹窗有效 | Function | -
+| closed | 关闭dialog | Function | -

+ 3 - 0
src/packages/drag/demo.vue

@@ -9,6 +9,9 @@
     <nut-drag direction="x" :style="{ top: '200px', left: '8px' }">
       <div class="touch-dom">只能在X轴拖动</div>
     </nut-drag>
+    <nut-drag direction="y" :style="{ top: '200px', right: '100px' }">
+      <div class="touch-dom">只能在Y轴拖动</div>
+    </nut-drag>
     <h4 :style="{ top: '250px' }">自动吸边</h4>
     <nut-drag direction="x" :attract="true" :style="{ top: '300px', left: '8px' }">
       <div class="touch-dom">拖动我</div>

+ 0 - 2
src/packages/drag/drag.scss

@@ -1,8 +1,6 @@
 .nut-drag {
   position: fixed;
   display: inline-block;
-  top: 0;
-  right: 0;
   z-index: 9997 !important;
   width: fit-content;
   height: fit-content;

+ 2 - 2
src/packages/drag/drag.vue

@@ -72,12 +72,12 @@ export default {
       el.style.zIndex = this.zIndex;
       if (this.boundary.left) {
         el.style.left = this.boundary.left + 'px';
-      } else {
+      } else if (this.boundary.right) {
         el.style.right = this.boundary.right + 'px';
       }
       if (this.boundary.top) {
         el.style.top = this.boundary.top + 'px';
-      } else {
+      } else if (this.boundary.bottom) {
         el.style.bottom = this.boundary.bottom + 'px';
       }
     },

+ 1 - 1
src/packages/elevator/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list">
     <nut-elevator
       :dataArray="dataList"
       :showIndicator="true"

+ 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;

+ 91 - 0
src/packages/fixednav/demo.vue

@@ -0,0 +1,91 @@
+<template>
+  <div class="demo-list">
+    <nut-noticebar :closeMode="true" v-if="!isMobile">此 Demo 仅能在移动端浏览器体验,建议在 Android 或 iOS 设备上体验。 </nut-noticebar>
+    <!-- 基本用法 -->
+    <nut-drag direction="y" :style="{ right: '0px', top: '50px' }">
+      <nut-fixednav active-text="右侧收起" un-active-text="右侧展开" :nav-list="navList" @selected="selected"> </nut-fixednav>
+    </nut-drag>
+    <nut-drag direction="y" :style="{ left: '0px', top: '100px' }">
+      <nut-fixednav active-text="左侧收起" un-active-text="左侧展开" type="left" :nav-list="navList" @selected="selected"> </nut-fixednav>
+    </nut-drag>
+    <nut-drag direction="y" :style="{ right: '0px', top: '150px' }">
+      <nut-fixednav :nav-list="navList" @selected="selected"> </nut-fixednav>
+    </nut-drag>
+    <nut-drag direction="y" :style="{ left: '0px', top: '200px' }">
+      <nut-fixednav :nav-list="navList" type="left" @selected="selected"> </nut-fixednav>
+    </nut-drag>
+    <nut-drag direction="y" :style="{ right: '0px', top: '250px' }">
+      <nut-fixednav :nav-list="navList" @selected="selected"> </nut-fixednav>
+    </nut-drag>
+    <nut-drag direction="y" :style="{ left: '0px', top: '300px' }">
+      <nut-fixednav :nav-list="navList" type="left" @selected="selected"> </nut-fixednav>
+    </nut-drag>
+    <!-- 自定义用法 -->
+    <nut-drag direction="y" :style="{ left: '0px', top: '300px' }">
+      <nut-fixednav :active.sync="myActive" type="left" @selected="selected">
+        <ul slot="list" class="fixed-list">
+          <li>1</li>
+          <li>2</li>
+          <li>3</li>
+          <li>4</li>
+          <li>5</li>
+        </ul>
+        <template slot="btn" @click="myActive = !myActive">
+          <img
+            style="width:20px;height:20px"
+            src="https://img10.360buyimg.com/imagetools/jfs/t1/143466/8/1743/6993/5ef9fb50E10f30d87/993e4e681fc50cac.png"
+          />
+          <span>{{ myActive ? '自定义开' : '自定义关' }}</span>
+        </template>
+      </nut-fixednav>
+    </nut-drag>
+
+    <!-- 无拖拽效果 -->
+    <nut-fixednav
+      active-text="收起"
+      un-active-text="无法拖拽"
+      :style="{ position: 'fixed', bottom: '10px', right: '0px' }"
+      :nav-list="navList"
+      @selected="selected"
+    >
+    </nut-fixednav>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      myActive: false,
+      navList: [
+        {
+          id: 1,
+          text: '首页',
+          icon: 'https://img11.360buyimg.com/imagetools/jfs/t1/117646/2/11112/1297/5ef83e95E81d77f05/daf8e3b1c81e3c98.png'
+        },
+        {
+          id: 2,
+          text: '分类',
+          icon: 'https://img12.360buyimg.com/imagetools/jfs/t1/119490/8/9568/1798/5ef83e95E968c69a6/dd029326f7d5042e.png'
+        },
+        {
+          id: 3,
+          text: '购物车',
+          num: 2,
+          icon: 'https://img14.360buyimg.com/imagetools/jfs/t1/130725/4/3157/1704/5ef83e95Eb976644f/b36c6cfc1cc1a99d.png'
+        },
+        {
+          id: 4,
+          text: '我的',
+          icon: 'https://img12.360buyimg.com/imagetools/jfs/t1/147573/29/1603/1721/5ef83e94E1393a678/5ddf1695ec989373.png'
+        }
+      ]
+    };
+  },
+  methods: {
+    selected($event) {
+      console.log($event);
+    }
+  }
+};
+</script>

+ 124 - 0
src/packages/fixednav/doc.md

@@ -0,0 +1,124 @@
+# FixedNav 悬浮导航
+
+可拖拽的悬浮导航
+
+## 基本用法
+
+> 按需引用组件
+``` javascript
+import {FixedNav, Drag, Popup } from '@nutui/nutui';
+FixedNav.install(Vue);
+Drag.install(Vue);
+Popup.install(Vue);
+```
+
+```html
+<!-- 基本用法 -->
+<nut-drag direction="y" :style="{right:'0px',top:'50px'}">
+    <nut-fixednav active-text="右侧收起" un-active-text="右侧展开" :nav-list="navList" @selected="selected">
+    </nut-fixednav>
+</nut-drag>
+<nut-drag direction="y" :style="{left:'0px',top:'100px'}">
+    <nut-fixednav active-text="左侧收起" un-active-text="左侧展开" type="left" :nav-list="navList" @selected="selected">
+    </nut-fixednav>
+</nut-drag>
+<nut-drag direction="y" :style="{right:'0px',top:'150px'}">
+    <nut-fixednav :nav-list="navList" @selected="selected">
+    </nut-fixednav>
+</nut-drag>
+<nut-drag direction="y" :style="{left:'0px',top:'200px'}">
+    <nut-fixednav :nav-list="navList" type="left" @selected="selected">
+    </nut-fixednav>
+</nut-drag>
+<nut-drag direction="y" :style="{right:'0px',top:'250px'}">
+    <nut-fixednav :nav-list="navList" @selected="selected">
+    </nut-fixednav>
+</nut-drag>
+<nut-drag direction="y" :style="{left:'0px',top:'300px'}">
+    <nut-fixednav :nav-list="navList" type="left" @selected="selected">
+    </nut-fixednav>
+</nut-drag>
+<!-- 自定义用法 -->
+<nut-drag direction="y" :style="{left:'0px',top:'300px'}">
+    <nut-fixednav :active.sync="myActive" type="left" @selected="selected">
+    <ul slot="list" class="fixed-list">
+        <li>1</li>
+        <li>2</li>
+        <li>3</li>
+        <li>4</li>
+        <li>5</li>
+    </ul>
+    <template slot="btn" @click="myActive=!myActive">
+        <img style="width:20px;height:20px"
+        src="https://img10.360buyimg.com/imagetools/jfs/t1/143466/8/1743/6993/5ef9fb50E10f30d87/993e4e681fc50cac.png" />
+        <span>{{myActive?'自定义开':'自定义关'}}</span>
+    </template>
+    </nut-fixednav>
+</nut-drag>
+
+<!-- 无拖拽效果 -->
+<nut-fixednav active-text="收起" un-active-text="无法拖拽" :style="{position:'fixed',bottom:'10px',right:'0px'}"
+    :nav-list="navList" @selected="selected">
+</nut-fixednav>
+```
+
+``` javascript
+export default {
+  data() {
+    return {
+      myActive: false,
+      navList: [
+        {
+          id: 1,
+          text: '首页',
+          icon: 'https://img11.360buyimg.com/imagetools/jfs/t1/117646/2/11112/1297/5ef83e95E81d77f05/daf8e3b1c81e3c98.png'
+        },
+        {
+          id: 2,
+          text: '分类',
+          icon: 'https://img12.360buyimg.com/imagetools/jfs/t1/119490/8/9568/1798/5ef83e95E968c69a6/dd029326f7d5042e.png'
+        },
+        {
+          id: 3,
+          text: '购物车',
+          num: 2,
+          icon: 'https://img14.360buyimg.com/imagetools/jfs/t1/130725/4/3157/1704/5ef83e95Eb976644f/b36c6cfc1cc1a99d.png'
+        },
+        {
+          id: 4,
+          text: '我的',
+          icon: 'https://img12.360buyimg.com/imagetools/jfs/t1/147573/29/1603/1721/5ef83e94E1393a678/5ddf1695ec989373.png'
+        }
+      ]
+    };
+  },
+  methods: {
+    selected($event) {
+      console.log($event);
+    }
+  }
+};
+```
+
+## Prop
+| 字段           | 说明                       | 类型    | 默认值     |
+|:---------------|:---------------------------|:--------|:-----------|
+| active         | 是否打开                   | Boolean | false      |
+| navList        | 悬浮列表内容数据           | Array   | []         |
+| active-text    | 收起列表按钮文案           | String  | '收起导航' |
+| un-active-text | 展开列表按钮文案           | String  | '快速导航' |
+| type           | 导航方向,可选值 left right | String  | 'right'     |
+
+
+## Slot
+
+| 名称 | 说明               |
+|------|--------------------|
+| btn  | 自定义按钮         |
+| list | 自定义展开列表内容 |
+
+## Event
+
+| 字段     | 说明         | 回调参数                 |
+|----------|--------------|--------------------------|
+| selected | 选择之后触发 | {item:item,$event:Event} |

+ 135 - 0
src/packages/fixednav/fixednav.scss

@@ -0,0 +1,135 @@
+.nut-fixednav {
+    height: 50px;
+    display: flex;
+    align-items: center;
+    position: relative;
+    z-index: 0;
+    width: 300px;
+
+    &.active {
+        &.left {
+            .fixed-btn img {
+                transform: rotate(180deg);
+            }
+            .fixed-list {
+                left: 0;
+            }
+        }
+        .fixed-btn {
+            img {
+                transform: rotate(0deg);
+            }
+        }
+        .fixed-list {
+            right: 0;
+        }
+    }
+
+    .fixed-btn {
+        box-sizing: border-box;
+        position: absolute;
+        right: 0;
+        z-index: 2;
+        width: 50px;
+        padding-left: 5px;
+        height: 50px;
+        background: linear-gradient(
+            135deg,
+            rgba(242, 20, 12, 1) 0%,
+            rgba(242, 39, 12, 1) 70%,
+            rgba(242, 77, 12, 1) 100%
+        );
+        border-radius: 25px 0px 0px 25px;
+        box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.2);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        > img {
+            margin-right: 5px;
+            width: 4px;
+            height: 10px;
+            flex-shrink: 0;
+            transition: all 0.3s;
+            transform: rotate(180deg);
+        }
+        > span {
+            width: 24px;
+            line-height: 13px;
+            font-size: 10px;
+            color: $fixed-bg-color;
+            flex-shrink: 0;
+        }
+    }
+    ul.fixed-list {
+        position: absolute;
+        right: -100%;
+        transition: all 0.5s;
+        z-index: 1;
+        flex-shrink: 0;
+        height: 44px;
+        background: $fixed-bg-color;
+        display: flex;
+        justify-content: space-between;
+        border-radius: 25px 0px 0px 25px;
+        box-shadow: 2px 2px 8px 0px rgba(0, 0, 0, 0.2);
+        padding: {
+            left: 20px;
+            right: 50px;
+        }
+        li {
+            position: relative;
+            flex: 1;
+            height: 100%;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            align-items: center;
+            min-width: 40px;
+            flex-shrink: 0;
+            > img {
+                width: 20px;
+                height: 20px;
+                margin-bottom: 2px;
+            }
+            span {
+                font-size: 10px;
+                color: $fixed-font-color;
+            }
+            b {
+                position: absolute;
+                right: 0;
+                top: 1px;
+                height: 14px;
+                line-height: 14px;
+                font-size: 10px;
+                padding: 0 3px;
+                color: $primary-color;
+                background: $fixed-bg-color;
+                border: 1px solid $primary-color;
+                border-radius: 7px;
+                text-align: center;
+                min-width: 12px;
+            }
+        }
+    }
+    &.left {
+        .fixed-btn {
+            flex-direction: row-reverse;
+            right: auto;
+            left: 0;
+            border-radius: 0 25px 25px 0;
+            > img {
+                transform: rotate(0deg);
+            }
+        }
+        ul.fixed-list {
+            left: -100%;
+            right: auto;
+            border-radius: 0px 25px 25px 0px;
+            padding: {
+                left: 50px;
+                right: 20px;
+            }
+        }
+    }
+}

+ 78 - 0
src/packages/fixednav/fixednav.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="nut-fixednav" :class="styled">
+    <div style="position:relative;z-index:1">
+      <nut-popup v-model="defaultActive"></nut-popup>
+    </div>
+    <slot name="list">
+      <ul class="fixed-list">
+        <li v-for="(item, index) in navList" @click="selected(item, $event)" :key="item.id || index">
+          <img :src="item.icon" />
+          <span>{{ item.text }}</span>
+          <b v-if="item.num">{{ item.num }}</b>
+        </li>
+      </ul>
+    </slot>
+
+    <div class="fixed-btn" @click="defaultActive = !defaultActive">
+      <slot name="btn">
+        <img src="https://img14.360buyimg.com/imagetools/jfs/t1/149975/30/1615/368/5ef86176Eb75bae46/5f70ae80a2d567b4.png" />
+        <span>{{ defaultActive ? activeText : unActiveText }}</span>
+      </slot>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  name: 'nut-fixednav',
+  props: {
+    active: {
+      type: Boolean,
+      default: false
+    },
+    navList: {
+      default: () => [],
+      type: Array
+    },
+    activeText: {
+      default: '收起导航',
+      type: String
+    },
+    unActiveText: {
+      default: '快速导航',
+      type: String
+    },
+    type: {
+      default: 'right',
+      type: String
+    }
+  },
+  computed: {
+    styled() {
+      return {
+        active: this.defaultActive,
+        [this.defaultType]: true
+      };
+    }
+  },
+  watch: {
+    defaultActive(newV, oldV) {
+      this.$emit('update:active', newV);
+    }
+  },
+  data() {
+    return {
+      defaultActive: false,
+      defaultType: ''
+    };
+  },
+  mounted() {
+    this.defaultActive = this.active;
+    this.defaultType = this.type;
+  },
+  methods: {
+    selected(item, $event) {
+      this.$emit('selected', { item, $event });
+    }
+  }
+};
+</script>

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

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

+ 1 - 1
src/packages/flex/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list">
     <h4>基础布局</h4>
     <div class="box-item">
       <nut-row>

+ 79 - 0
src/packages/gesture/demo.vue

@@ -0,0 +1,79 @@
+<template>
+  <div class="container">
+    <div class="item" v-gesture:pan="test1" v-gesture:panend="test2" :style="{ left: left, top: top }">
+      <h4>基本操作</h4>
+      <p>平移</p>
+    </div>
+
+    <div class="item" v-gesture:pan.left="test3">
+      <h4>指定方向平移</h4>
+      <p>左移</p>
+    </div>
+
+    <div class="item" v-gesture:swipe.horizontal="test3">
+      <h4>指定方向滑动</h4>
+      <p>swipe</p>
+    </div>
+
+    <div class="item" v-gesture:press="test3" v-gesture:tap="test3" v-gesture:pan="test3">
+      <h4>绑定多个事件</h4>
+      <p>press、tap、pan</p>
+    </div>
+    <div class="show">{{ text }}</div>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    return {
+      left: 0,
+      top: 70,
+      posX: 0,
+      posY: 70,
+      text: ''
+    };
+  },
+  mounted() {},
+  methods: {
+    test1(e) {
+      this.left = parseInt(this.posX) + e.deltaX + 'px';
+      this.top = parseInt(this.posY) + e.deltaY + 'px';
+    },
+    test2(e) {
+      this.posX = this.left;
+      this.posY = this.top;
+    },
+    test3(e) {
+      this.text = 'x: ' + e.deltaX + ', y: ' + e.deltaY;
+    }
+  }
+};
+</script>
+
+<style>
+.item {
+  position: absolute;
+  left: 0;
+  width: 200px;
+  height: 80px;
+  background-color: #ddd;
+}
+
+@for $i from 1 to 20 {
+  .container .item:nth-child(#{$i}) {
+    top: $i * 90 + px;
+  }
+}
+.content {
+  position: absolute;
+  left: 0;
+  top: 200px;
+  width: 100px;
+  height: 200px;
+  background-color: red;
+}
+.show {
+  position: fixed;
+  bottom: 0;
+}
+</style>

+ 54 - 0
src/packages/gesture/doc.md

@@ -0,0 +1,54 @@
+# Gesture 手势组件
+基于[hammer.js](http://hammerjs.github.io/getting-started/)封装,支持移动端各种手势操作。包括'tap', 'pan', 'pinch', 'press', 'rotate', 'swipe'
+
+## 基本用法
+```html
+<div class="item" v-gesture:pan="test1" v-gesture:panend="test2" :style="{ left: left, top: top }">
+    平移
+</div>
+```
+## 指定方向平移
+```html
+<div class="item" v-gesture:pan.left="test3">
+    左移
+</div>
+```
+## 指定方向滑动
+```html
+<div class="item" v-gesture:swipe.horizontal="test3">
+    swipe
+</div>
+```
+## 绑定多个事件
+```html
+<div class="item" v-gesture:press="test3" v-gesture:tap="test3" v-gesture:pan="test3">
+    press、tap、pan
+</div>
+<script>
+export default {
+  data() {
+    return {
+      left: 0,
+      top: 70,
+      posX: 0,
+      posY: 70
+    };
+  },
+  mounted() {},
+  methods: {
+    test1(e) {
+      this.left = parseInt(this.posX) + e.deltaX + 'px';
+      this.top = parseInt(this.posY) + e.deltaY + 'px';
+    },
+    test2(e) {
+      this.posX = this.left;
+      this.posY = this.top;
+    },
+    test3(e) {
+      console.log(e.deltaX, e.deltaY);
+    }
+  }
+};
+</script>
+
+```

+ 3 - 0
src/packages/gesture/gesture.scss

@@ -0,0 +1,3 @@
+.nut-gesture{
+
+}

+ 147 - 0
src/packages/gesture/index.js

@@ -0,0 +1,147 @@
+import Hammer from 'hammerjs';
+
+const gestures = ['tap', 'pan', 'pinch', 'press', 'rotate', 'swipe'];
+const subGestures = [
+  'panstart',
+  'panend',
+  'panmove',
+  'pancancel',
+  'pinchstart',
+  'pinchmove',
+  'pinchend',
+  'pinchcancel',
+  'pinchin',
+  'pinchout',
+  'pressup',
+  'rotatestart',
+  'rotatemove',
+  'rotateend',
+  'rotatecancel'
+];
+const directions = ['up', 'down', 'left', 'right', 'horizontal', 'vertical', 'all'];
+function verifyDirection(options) {
+  var dir = options.direction;
+  if (typeof dir === 'string') {
+    var hammerDirection = 'DIRECTION_' + dir.toUpperCase();
+    if (directions.indexOf(dir) > -1 && Hammer.hasOwnProperty(hammerDirection)) {
+      options.direction = Hammer[hammerDirection];
+    } else {
+      console.warn('[vue-hammer] invalid direction: ' + dir);
+    }
+  }
+}
+function handleDirection(event, direction) {
+  let dirArray = new Set();
+  const relations = {
+    horizontal: ['left', 'right'],
+    vertical: ['up', 'down'],
+    all: ['left', 'right', 'up', 'down']
+  };
+  direction.forEach(dir => {
+    dir = dir.toLowerCase();
+    if (relations[dir]) {
+      dirArray = new Set([...dirArray, ...relations[dir]]);
+    } else {
+      dirArray.add(dir);
+    }
+  });
+
+  if (dirArray.size === 0) {
+    return event;
+  }
+  dirArray = [...dirArray].map(dir => {
+    return event + dir;
+  });
+  return dirArray.join(' ');
+}
+
+function update(el, binding) {
+  const mc = el.hammer;
+  const event = binding.arg;
+  const dirType = subGestures.find(subGes => subGes === event) ? event : handleDirection(event, el.storage[event].direction);
+  // 删除已绑定的事件
+  if (mc.handler) {
+    mc.off(dirType, mc.handler);
+  }
+  if (typeof binding.value !== 'function') {
+    mc.handler = null;
+    console.warn(binding.arg + '请传入function类型');
+  } else {
+    mc.on(dirType, (mc.handler = binding.value));
+  }
+}
+const Gesture = {
+  name: 'gesture',
+  bind: (el, binding) => {
+    if (!el.hammer) {
+      // 是否已初始化
+      el.hammer = new Hammer.Manager(el, { inputClass: Hammer.TouchMouseInput });
+      el.hammer.domEvents = true;
+    }
+
+    const elType = binding.arg; // pan panleft ...
+    const mc = el.hammer;
+    const direction = binding.modifiers; // 方向: {right: true}
+
+    el.storage = el.storage || {}; // 记录所有事件及其方向
+    el.storage[elType] = el.storage[elType] || {};
+    el.storage[elType].direction = el.storage[elType].direction || [];
+
+    // 存储传入的方向到对应事件下
+    Object.keys(direction).forEach(keyName => {
+      if (!el.storage[elType].direction.includes(keyName)) {
+        el.storage[elType].direction.push(keyName);
+      }
+    });
+
+    let recognizerType = gestures.find(gesture => gesture === elType); // 验证传入的事件合法性
+    if (!recognizerType) {
+      return;
+    }
+    let recognizer = mc.get(recognizerType); // 获取识别器实例
+    if (!recognizer) {
+      // 创建构造器
+      recognizer = new Hammer[recognizerType.charAt(0).toUpperCase() + recognizerType.slice(1)]();
+      // 同时识别多个手势
+      recognizer.recognizeWith(mc.recognizers);
+      // 向管理器添加新的识别器实例。
+      mc.add(recognizer);
+    }
+
+    // 有方向传入时默认取第一个进行设置
+    if (el.storage[recognizerType].direction.length >= 1) {
+      let options = {
+        direction: el.storage[recognizerType].direction[0]
+      };
+      verifyDirection(options);
+      recognizer.set(options);
+    }
+  },
+  inserted: (el, binding) => {
+    update(el, binding);
+  },
+  componentUpdated: (el, binding) => {
+    update(el, binding);
+  },
+  unbind: (el, binding) => {
+    const mc = el.hammer;
+    const event = binding.arg;
+    const dirType = subGestures.find(subGes => subGes === event) ? event : handleDirection(event, el.storage[event].direction);
+    if (mc.handler) {
+      el.hammer.off(dirType, mc.handler);
+    }
+    let eventkeys = Object.keys(el.hammer.handlers);
+    let isDestroy = true;
+    eventkeys.forEach(element => {
+      if (mc.handlers[element].length > 0) {
+        isDestroy = false;
+      }
+    });
+    if (isDestroy) {
+      el.hammer.destroy();
+      el.hammer = null;
+    }
+  }
+};
+
+export default Gesture;

+ 1 - 1
src/packages/icon/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list">
     <h4>所有ICON,单击可复制</h4>
     <table>
       <tr>

+ 2 - 2
src/packages/icon/icon.vue

@@ -1,8 +1,8 @@
 <template>
-  <i :class="['nut-icon', 'nut-icon-' + type]" v-html="icon" :style="{ height: size, width: size, color: color }"> </i>
+  <i :class="['nut-icon', 'nut-icon-' + type]" v-html="icon" :style="{ height: size, width: size, color: color }"></i>
 </template>
 <script>
-const types = ['top', 'action', 'cross', 'down', 'right', 'more', 'plus', 'search', 'trolley', 'tick', 'minus', 'circle-cross'];
+const types = ['top', 'action', 'cross', 'down', 'right', 'more', 'notice', 'plus', 'search', 'trolley', 'tick', 'minus', 'circle-cross'];
 
 export default {
   name: 'nut-icon',

+ 1 - 1
src/packages/imagepicker/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list-pd">
     <h4>基本用法</h4>
     <nut-imagepicker @imgMsg="imgMsg" :imgList.sync="imgList1"></nut-imagepicker>
     <h4>指定宽度和高度都是120px,图片间距是10px</h4>

+ 1 - 1
src/packages/lazyload/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="lazyloadDemo">
+  <div class="lazyloadDemo demo-list">
     <h4>基础使用</h4>
     <img v-lazy="imageArray[0]" />
     <h4>背景图懒加载</h4>

+ 4 - 3
src/packages/leftslip/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list">
     <h4>基本用法</h4>
     <div>
       <nut-leftslip ref="demo1">
@@ -58,9 +58,10 @@
         >
       </nut-leftslip>
     </div>
-    <p>禁止滑动</p>
-    <div>
+    <div class="demo-list-pd">
       <button class="btn" @click="disabledHandle">{{ isDisable ? '开启滑动' : '禁止滑动' }}</button>
+    </div>
+    <div>
       <nut-leftslip :disabled="isDisable">
         <div slot="slip-main" class="slip-main">左滑触发删除</div>
         <div slot="slipbtns" class="slipbtns"><a href="javascript:;">删除</a></div>

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

@@ -1,5 +1,5 @@
 <template>
-  <div class="luckDrawBox">
+  <div class="luckDrawBox demo-list">
     <h4>基本用法</h4>
     <div>
       <nut-cell>

+ 1 - 1
src/packages/menu/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list">
     <h4>单选</h4>
     <div class="list list1">
       <span slot="title" @click="switchMenu('isVisible1', 1)" ref="title1">综合</span>

+ 2 - 2
src/packages/navbar/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list">
     <h4>默认用法</h4>
     <nut-navbar @on-click-back="back" @on-click-title="title" :leftShow="false" :rightShow="false" @on-click-more="more"></nut-navbar>
     <h4>增加左侧按钮</h4>
@@ -27,7 +27,7 @@
       @on-click-back="back"
       @on-click-title="title"
       @on-click-more="more"
-      style="background-color: #000; color: #fff; height: 60px; line-height: 60px;"
+      style="background-color:#000; color: #fff; height:60px; line-height: 60px;"
       >NavBar5</nut-navbar
     >
   </div>

+ 5 - 4
src/packages/noticebar/doc.md

@@ -45,10 +45,11 @@
 | background | 导航栏的背景颜色                                           | String        | 空     |
 | delay      | 延时多少秒                                                 | String/Number | 1      |
 | scrollable | 是否可以滚动                                               | Boolean       | true   |
-| speed      | 移动375px需要用的时间                                      | Number        | 6      |
+| speed      | 滚动速率 (px/s)                                            | Number        | 50     |
 
 ## Event
 
-| 字段  | 说明             | 回调参数 |
-| ----- | ---------------- | -------- |
-| click | 外层点击事件回调 | 无       |
+| 字段  | 说明             | 回调参数     |
+| ----- | ---------------- | ------------ |
+| click | 外层点击事件回调 | event: Event |
+| close | 关闭通知栏时触发 | event: Event |

File diff suppressed because it is too large
+ 32 - 23
src/packages/noticebar/noticebar.scss


File diff suppressed because it is too large
+ 47 - 33
src/packages/noticebar/noticebar.vue


+ 32 - 0
src/packages/notify/__test__/notify.spec.js

@@ -0,0 +1,32 @@
+import { shallowMount, mount } from '@vue/test-utils'
+import Notify from '../notify.vue';
+import Vue from 'vue';
+
+
+describe('notify.vue', () => {
+    const wrapper = shallowMount(Notify, {
+        propsData: {
+            type: 'warn',
+            msg: "通知内容"
+        }
+    });
+
+    it('自定义背景色和字体颜色', () => {
+        wrapper.setProps({
+            color: '#ad0000',
+            background: '#ffe1e1'
+        });
+        return Vue.nextTick().then(function () {
+            expect(wrapper.find('.nut-notify').hasStyle('color', '#ad0000')).toBe(true);
+            expect(wrapper.find('.nut-notify').hasStyle('background', '#ffe1e1')).toBe(true);
+        })
+    });
+    it('主要通知', () => {
+        wrapper.setProps({
+            type: 'primary'
+        });
+        return Vue.nextTick().then(function () {
+            expect(wrapper.classes('nut-notify--primary')).toBe(true)
+        })
+    });
+});

+ 110 - 0
src/packages/notify/_notify.js

@@ -0,0 +1,110 @@
+import Vue from 'vue';
+import settings from './notify.vue';
+// 扩展为类
+let NotifyConstructor = Vue.extend(settings);
+let timer, instance, instanceArr = [];
+let defaultOptionsMap = {};
+const id = '0';
+// 默认传入值
+const defaultOptions = {
+  type: 'danger',
+  showPopup: false,
+  msg: '',
+  color: undefined,
+  background: undefined,
+  duration: 3000,
+  className: '',
+  onClosed: null,
+  onClick: null,
+  onOpened: null,
+  textTimer: null,
+};
+// 当前传入值
+let currentOptions = {
+  ...defaultOptions
+};
+// 展示,挂载
+function _showNotify() {
+  instance.vm = instance.$mount();
+  document.body.appendChild(instance.$el);
+  Vue.nextTick(() => {
+    instance.showPopup = true;
+  });
+}
+function _getInstance(obj) {
+  // 默认传递的值
+  let opt = {
+    id
+  };
+  Object.assign(opt, currentOptions, defaultOptionsMap[obj.type], obj);
+  console.log(opt, obj, 'obj');
+  //有相同id者共用一个实例,否则新增实例
+  if (opt['id'] && instanceArr[opt['id']]) {
+    instance = instanceArr[opt['id']];
+    instance.hide(true);
+    instance = Object.assign(instance, opt);
+  } else {
+    instance = new NotifyConstructor({
+      propsData: opt
+    });
+
+    instance = Object.assign(instance, obj);
+    opt['id'] && (instanceArr[opt['id']] = instance);
+  }
+
+  _showNotify();
+  return instance;
+}
+
+function errorMsg(msg) {
+  if (!msg) {
+    console.warn('[NutUI Toast]: msg不能为空');
+    return;
+  }
+}
+
+let Notify = {
+  text(msg, obj = {}) {
+    errorMsg(msg);
+    return _getInstance({ ...obj, msg });
+  },
+  primary(msg, obj = {}) {
+    errorMsg(msg);
+    return _getInstance({ ...obj, msg, type: 'primary' });
+  },
+  success(msg, obj = {}) {
+    errorMsg(msg);
+    return _getInstance({ ...obj, msg, type: 'success' });
+  },
+  danger(msg, obj = {}) {
+    errorMsg(msg);
+    return _getInstance({ ...obj, msg, type: 'danger' });
+  },
+  warn(msg, obj = {}) {
+    errorMsg(msg);
+    return _getInstance({ ...obj, msg, type: 'warning' });
+  },
+
+  // 全局设置默认内容
+  setDefaultOptions(type, options) {
+    if (typeof type === 'string') {
+      defaultOptionsMap[type] = options;
+    } else {
+      Object.assign(currentOptions, type);
+    }
+  },
+  // 重置默认内容
+  resetDefaultOptions(type) {
+    if (typeof type === 'string') {
+      defaultOptionsMap[type] = null;
+    } else {
+      currentOptions = { ...defaultOptions };
+      defaultOptionsMap = {};
+    }
+  }
+};
+
+
+
+
+export default Notify;

+ 144 - 0
src/packages/notify/demo.vue

@@ -0,0 +1,144 @@
+<template>
+  <div class="demo-list">
+    <h4>基本用法</h4>
+    <div>
+      <nut-cell :showIcon="true" :isLink="true" @click.native="notify1('通知内容')">
+        <span slot="title">
+          <label>基础用法</label>
+        </span>
+      </nut-cell>
+    </div>
+    <h4>通知类型</h4>
+    <div>
+      <nut-cell :showIcon="true" :isLink="true" @click.native="notify2('通知内容')">
+        <span slot="title">
+          <label>主要通知</label>
+        </span>
+      </nut-cell>
+    </div>
+    <div>
+      <nut-cell :showIcon="true" :isLink="true" @click.native="notify3('成功通知')">
+        <span slot="title">
+          <label>成功通知</label>
+        </span>
+      </nut-cell>
+    </div>
+    <div>
+      <nut-cell :showIcon="true" :isLink="true" @click.native="notify4('危险通知')">
+        <span slot="title">
+          <label>危险通知</label>
+        </span>
+      </nut-cell>
+    </div>
+    <div>
+      <nut-cell :showIcon="true" :isLink="true" @click.native="notify5('警告通知')">
+        <span slot="title">
+          <label>警告通知</label>
+        </span>
+      </nut-cell>
+    </div>
+    <h4>自定义样式</h4>
+    <div>
+      <nut-cell :showIcon="true" :isLink="true" @click.native="cusBgNotify('自定义背景色和字体颜色')">
+        <span slot="title">
+          <label>自定义背景色和字体颜色</label>
+        </span>
+      </nut-cell>
+    </div>
+    <h4>自定义时长</h4>
+    <div>
+      <nut-cell :showIcon="true" :isLink="true" @click.native="timeNotify('自定义时间')">
+        <span slot="title">
+          <label>自定义时长</label>
+        </span>
+      </nut-cell>
+    </div>
+    <h4>组件调用</h4>
+    <div>
+      <nut-cell :showIcon="true" :isLink="true" @click.native="showNotify">
+        <span slot="title">
+          <label>组件调用</label>
+        </span>
+      </nut-cell>
+      <nut-notify :showPopup="show" type="success" msg="hello" duration="10000">
+        <span>hello</span>
+      </nut-notify>
+    </div>
+    <h4>修改默认配置</h4>
+    <div>
+      <nut-cell :showIcon="true" :isLink="true" @click.native="defaultNotify">
+        <span slot="title">
+          <label>更改所有Notify展示时长设置为5000毫秒</label>
+        </span>
+      </nut-cell>
+      <nut-cell :showIcon="true" :isLink="true" @click.native="resetDefaultOptions">
+        <span slot="title">
+          <label>恢复所有Toast提示默认配置</label>
+        </span>
+      </nut-cell>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  data() {
+    return {
+      show: false
+    };
+  },
+  methods: {
+    notify1(val) {
+      this.$notify.text(val, {
+        onClosed() {
+          console.log('close');
+        },
+        onClick: () => {
+          console.log('click');
+        },
+        onOpened: () => {
+          console.log('opened');
+        }
+      });
+    },
+    notify2(val) {
+      this.$notify.primary(val);
+    },
+    notify3(val) {
+      this.$notify.success(val);
+    },
+    notify4(val) {
+      this.$notify.danger(val);
+    },
+    notify5(val) {
+      this.$notify.warn(val);
+    },
+    cusBgNotify(val) {
+      this.$notify.text(val, {
+        color: '#ad0000',
+        background: '#ffe1e1'
+      });
+    },
+    timeNotify(val) {
+      this.$notify.text(val, {
+        duration: 10000
+      });
+    },
+    showNotify() {
+      this.show = true;
+      setTimeout(() => {
+        this.show = false;
+      }, 200);
+    },
+    defaultNotify() {
+      this.$notify.setDefaultOptions({
+        duration: 5000
+      });
+      this.$notify.text('将所有Notify提示展示时长设置为5000毫秒');
+    },
+    resetDefaultOptions() {
+      this.$notify.resetDefaultOptions();
+      this.$notify.text('恢复所有Notify提示默认配置');
+    }
+  }
+};
+</script>

+ 120 - 0
src/packages/notify/doc.md

@@ -0,0 +1,120 @@
+# Notify 消息提示
+
+## 基本用法
+
+文字提示
+```javascript
+export default {
+  mounted() {
+      this.$notify.text('通知内容', {
+        onClosed() {
+          console.log('close');
+        },
+        onClick: () => {
+          console.log('click');
+        },
+        onOpened: () => {
+          console.log('opened');
+        }
+      });
+  }
+}
+```
+## 通知类型
+### 主要通知
+```javascript
+    mounted(){
+      this.$notify.primary('通知内容');
+    }
+```
+### 成功通知
+```javascript
+    mounted(){
+      this.$notify.success('通知内容');
+    }
+```
+### 危险通知
+```javascript
+    mounted(){
+      this.$notify.danger('通知内容');
+    }
+```
+### 警告通知
+```javascript
+    mounted(){
+      this.$notify.warn('通知内容');
+    }
+```
+## 自定义样式
+### 自定义样式
+```javascript
+    mounted(){
+      this.$notify.text(val, {
+        color: '#ad0000',
+        background: '#ffe1e1'
+      });
+    }
+```
+### 自定义时长
+```javascript
+    mounted(){
+      this.$notify.text(val, {
+        duration: 10000
+      });
+    }
+```
+## 组件调用
+```html
+    <nut-notify :showPopup="show" type="success" msg="组件调用">
+      <span>hello</span>
+    </nut-notify>
+```
+
+```javascript
+    mounted(){
+      this.show = true;
+      setTimeout(() => {
+        this.show = false;
+      }, 200);
+    }
+```
+## 修改默认配置
+通过**Notify.setDefaultOptions**函数可以全局修改 Notify 的默认配置,**值得注意的是,标签形式的组件不支持默认样式**。
+```javascript
+// 更改所有Toast展示时长设置为5000毫秒
+      this.$notify.setDefaultOptions({
+        duration: 5000
+      });
+// 重置所有 Toast 的默认配置
+      this.$notify.resetDefaultOptions();
+```
+
+
+
+## Prop
+
+| 字段       | 说明                                     | 类型          | 默认值 |
+| ---------- | ---------------------------------------- | ------------- | ------ |
+| type       | 提示的信息                               | String        | 空     |
+| message    | 展示文案,支持通过\n换行                 | Boolean       | false  |
+| duration   | 展示时长(ms),值为 0 时,notify 不会消失 | String        | 空     |
+| color      | 字体颜色                                 | String        | 空     |
+| background | 背景颜色                                 | String        | 空     |
+| className  | 自定义类名                               | String/Number | 1      |
+
+## Event
+
+| 字段     | 说明               | 回调参数 |
+| -------- | ------------------ | -------- |
+| onClick  | 点击事件回调       | 无       |
+| onOpened | 完全展示后事件回调 | 无       |
+| onClose  | 关闭事件回调       | 无       |
+
+
+## Function
+
+| 方法名                     | 说明                             | 参数    | 返回值  |
+| -------------------------- | -------------------------------- | ------- | ------- |
+| Notify                     | 提示的信息                       | options | message | notify 实例 |
+| Notify.setDefaultOptions   | 修改默认配置,对所有 Notify 生效 | options | void    |
+| Notify.resetDefaultOptions | 重置默认配置,对所有 Notify 生效 | String  | void    |

+ 11 - 0
src/packages/notify/index.js

@@ -0,0 +1,11 @@
+import Notify from './_notify';
+import NotifyVue from './notify.vue';
+import './notify.scss';
+
+const NotifyArr = [Notify, NotifyVue];
+NotifyArr.install = function (Vue) {
+  Vue.prototype['$notify'] = Notify;
+  Vue.component(NotifyVue.name, NotifyVue);
+};
+
+export default NotifyArr

+ 28 - 0
src/packages/notify/notify.scss

@@ -0,0 +1,28 @@
+@import '../../styles/variable.scss';
+.nut-notify {
+    box-sizing: border-box;
+    padding: $notify-padding;
+    color: $notify-text-color;
+    font-size: $notify-font-size;
+    line-height: $notify-line-height;
+  
+    white-space: pre-wrap;
+    text-align: center;
+    word-wrap: break-word;
+  
+    &--primary {
+      background-color: $notify-primary-background-color !important;
+    }
+  
+    &--success {
+      background-color: $notify-success-background-color !important;
+    }
+  
+    &--danger {
+      background-color: $notify-danger-background-color !important;
+    }
+  
+    &--warning {
+      background-color: $notify-warning-background-color !important;
+    }
+  }

+ 103 - 0
src/packages/notify/notify.vue

@@ -0,0 +1,103 @@
+<template>
+  <nut-popup
+    v-model="curVisible"
+    position="top"
+    :style="{ color: color, background: background }"
+    :overlay="false"
+    :lockScroll="false"
+    :class="['nut-notify', `nut-notify--${type}`, { className }]"
+    @click="handleClick"
+    @opened="handleOpened"
+    @closed="handleClosed"
+  >
+    <template v-if="$slots.default">
+      <slot></slot>
+    </template>
+    <template v-else>{{ msg }}</template>
+  </nut-popup>
+</template>
+<script>
+import { overlayProps, getProps } from '../popup/index';
+import Popup from './../popup/popup.vue';
+export default {
+  name: 'nut-notify',
+  props: {
+    ...overlayProps,
+    color: { type: String, default: '' },
+    msg: { type: [Number, String], default: '' },
+    duration: { type: [Number, String], default: 3000 },
+    className: {
+      type: String,
+      default: ''
+    },
+    background: { type: String, default: '' },
+    type: {
+      type: String,
+      default: 'danger'
+    },
+    showPopup: {
+      type: Boolean,
+      default: false
+    }
+  },
+  watch: {
+    showPopup: {
+      handler(val) {
+        if (val) {
+          this.curVisible = val;
+          this.show();
+        }
+      }
+      // immediate: true
+    }
+  },
+  data() {
+    return { timer: null, curVisible: false };
+  },
+  components: {
+    'nut-popup': Popup
+  },
+  methods: {
+    handleClick() {
+      typeof this.onClick === 'function' && this.onClick();
+    },
+    handleOpened() {
+      typeof this.onOpened === 'function' && this.onOpened();
+    },
+    handleClosed() {
+      typeof this.onClosed === 'function' && this.onClosed();
+    },
+    show(force) {
+      this.clearTimer();
+      clearTimeout(this.textTimer);
+
+      if (this.duration) {
+        this.timer = setTimeout(() => {
+          this.hide(force);
+        }, this.duration);
+      }
+    },
+    hide(force) {
+      this.clearTimer();
+      this.curVisible = false;
+      if (force) {
+        clearTimeout(this.textTimer);
+      } else {
+        this.textTimer = setTimeout(() => {
+          clearTimeout(this.textTimer);
+        }, 300);
+      }
+    },
+    clearTimer() {
+      if (this.timer) {
+        clearTimeout(this.timer);
+        this.timer = null;
+      }
+    }
+  },
+  destroyed() {
+    this.textTimer = null;
+    this.timer = null;
+  }
+};
+</script>

+ 7 - 22
src/packages/popup/demo.vue

@@ -1,12 +1,12 @@
 <template>
-  <div>
-    <h2 class="title">基本用法</h2>
+  <div class="demo-list">
+    <h4>基本用法</h4>
     <div>
       <nut-cell isLink title="展示弹出层" :showIcon="true" @click.native="showBasic = true"> </nut-cell>
     </div>
     <nut-popup :style="{ padding: '30px 50px' }" v-model="showBasic">正文</nut-popup>
 
-    <h2 class="title">弹出位置</h2>
+    <h4>弹出位置</h4>
     <div>
       <nut-cell isLink title="顶部弹出" :showIcon="true" @click.native="showTop = true"> </nut-cell>
       <nut-popup position="top" v-model="showTop" :style="{ height: '20%' }"> </nut-popup>
@@ -17,7 +17,7 @@
       <nut-cell isLink title="右侧弹出" :showIcon="true" @click.native="showRight = true"> </nut-cell>
       <nut-popup position="right" v-model="showRight" :style="{ width: '20%', height: '100%' }"></nut-popup>
     </div>
-    <h2 class="title">关闭图标</h2>
+    <h4>关闭图标</h4>
     <div>
       <nut-cell isLink title="关闭图标" :showIcon="true" @click.native="showIcon = true"> </nut-cell>
       <nut-popup position="bottom" closeable v-model="showIcon" :style="{ height: '20%' }"></nut-popup>
@@ -29,12 +29,12 @@
       <nut-popup position="bottom" closeable close-icon="tick" v-model="showCloseIcon" :style="{ height: '20%' }"></nut-popup>
     </div>
 
-    <h2 class="title">圆角弹框</h2>
+    <h4>圆角弹框</h4>
     <div>
       <nut-cell isLink title="圆角弹框" :showIcon="true" @click.native="showRound = true"> </nut-cell>
       <nut-popup round v-model="showRound" position="bottom" :style="{ height: '20%' }"></nut-popup>
     </div>
-    <h2 class="title">指定挂载节点</h2>
+    <h4>指定挂载节点</h4>
     <div>
       <nut-cell isLink title="指定挂载节点" :showIcon="true" @click.native="getContainer = true"> </nut-cell>
     </div>
@@ -65,19 +65,4 @@ export default {
   }
 };
 </script>
-<style lang="scss" scoped>
-.title {
-  text-align: left;
-  margin: 0;
-  padding: 32px 16px 16px;
-  color: rgba(69, 90, 100, 0.6);
-  font-weight: normal;
-  font-size: 14px;
-  line-height: 16px;
-}
-.demo {
-  padding-left: 0;
-  padding-right: 0;
-  overflow: hidden;
-}
-</style>
+<style lang="scss" scoped></style>

+ 1 - 1
src/packages/rate/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list">
     <h4>基本用法</h4>
     <div>
       <nut-cell>

+ 5 - 0
src/packages/rate/rate.vue

@@ -69,6 +69,11 @@ export default {
         this.$emit('click', idx);
       }
     }
+  },
+  watch: {
+    value(newVal, oldVal) {
+      this.current = newVal;
+    }
   }
 };
 </script>

+ 1 - 1
src/packages/signature/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list">
     <h4>基本用法</h4>
     <p>默认</p>
     <p><nut-signature @confirm="confirm" @clear="clear"></nut-signature></p>

+ 19 - 5
src/packages/stepper/stepper.vue

@@ -5,7 +5,7 @@
       type="number"
       :min="minNum"
       :max="max"
-      :readonly="readonly"
+      :readonly="readonly || !isLegal"
       :value="num | maxv(minNum, max)"
       :style="{ visibility: showNum ? 'visible' : 'hidden' }"
       @input="numchange"
@@ -41,7 +41,8 @@
       <div>{{ animNum[0] }}</div>
       <div>{{ animNum[1] }}</div>
     </div>
-    <span @click="add()" :class="{ 'nut-stepper-grey': max && Number(num) > max - step }" v-html="require('../../assets/svg/plus.svg')"> </span>
+    <span @click="add()" :class="{ 'nut-stepper-grey': (max && Number(num) > max - step) || !isLegal }" v-html="require('../../assets/svg/plus.svg')">
+    </span>
   </div>
 </template>
 <script>
@@ -92,7 +93,8 @@ export default {
       showReduceAnim: false,
       animNum: [this.value, this.value],
       animTranslate_add: 0,
-      animTranslate_: -100
+      animTranslate_: -100,
+      isLegal: true //是否合法 isLegal
     };
   },
   filters: {
@@ -115,8 +117,20 @@ export default {
     },
     min: {
       handler(v, ov) {
+        this.isLegal = true;
         if (v < this.max) {
           this.minNum = v;
+        } else {
+          this.isLegal = false;
+        }
+      },
+      immediate: true
+    },
+    max: {
+      handler(v, ov) {
+        this.isLegal = true;
+        if (v <= this.min) {
+          this.isLegal = false;
         }
       },
       immediate: true
@@ -129,7 +143,7 @@ export default {
   },
   methods: {
     focus(e) {
-      if (this.readonly) return;
+      if (this.readonly || !this.isLegal) return;
       // clear val temporary when focus, e...s
       const v = this.num;
       this.tempNum = v;
@@ -139,7 +153,7 @@ export default {
       this.$emit('focus', e, this.num);
     },
     blur(e) {
-      if (this.readonly) return this.$emit('blur', e, this.num);
+      if (this.readonly || !this.isLegal) return this.$emit('blur', e, this.num);
       let v = e.target.value;
       this.minNum = this.min;
       this.focusing = false;

+ 11 - 11
src/packages/swiper/demo.vue

@@ -1,16 +1,16 @@
 <template>
-  <div id="vueSwiper">
+  <div id="vueSwiper" class="demo-list">
     <h4>横向无缝切换</h4>
     <nut-swiper :paginationVisibile="true" direction="horizontal" :swiperData="dataImgItem">
-      <div v-for="(item, index) in dataImgItem" :key="index" class="nut-swiper-slide">
-        <img :src="item.imgSrc" style="max-width: 100%;" />
+      <div v-for="(item, index) in dataImgItem" :key="index" class="nut-swiper-slide ">
+        <img :src="item.imgSrc" style="max-width:100%;" />
       </div>
     </nut-swiper>
 
     <h4>横向切换</h4>
     <nut-swiper :paginationVisibile="true" direction="horizontal" :swiperData="dataImgItem" :canDragging="false" :paginationVisible="true">
-      <div v-for="(item, index) in dataImgItem" :key="index" class="nut-swiper-slide">
-        <img :src="item.imgSrc" style="max-width: 100%;" />
+      <div v-for="(item, index) in dataImgItem" :key="index" class="nut-swiper-slide ">
+        <img :src="item.imgSrc" style="max-width:100%;" />
       </div>
     </nut-swiper>
 
@@ -23,15 +23,15 @@
       :canDragging="false"
       :paginationVisible="true"
     >
-      <div v-for="(item, index) in dataImgItem" :key="index" class="nut-swiper-slide">
-        <img :src="item.imgSrc" style="max-width: 100%;" />
+      <div v-for="(item, index) in dataImgItem" :key="index" class="nut-swiper-slide ">
+        <img :src="item.imgSrc" style="max-width:100%;" />
       </div>
     </nut-swiper>
 
     <h4>纵向自动播放</h4>
     <nut-swiper direction="vertical" :autoPlay="3000" :swiperData="dataImgItem">
-      <div v-for="(item, index) in dataImgItem" :key="index" class="nut-swiper-slide">
-        <img :src="item.imgSrc" style="max-width: 100%;" />
+      <div v-for="(item, index) in dataImgItem" :key="index" class="nut-swiper-slide ">
+        <img :src="item.imgSrc" style="max-width:100%;" />
       </div>
     </nut-swiper>
 
@@ -46,8 +46,8 @@
       @slideMove="slideMove"
       @slideChangeStart="slideChangeStart"
     >
-      <div v-for="(item, index) in dataImgItem" :key="index" class="nut-swiper-slide">
-        <img :data-src="item.imgSrc" style="max-width: 100%;" class="nut-img-lazyload" />
+      <div v-for="(item, index) in dataImgItem" :key="index" class="nut-swiper-slide ">
+        <img :data-src="item.imgSrc" style="max-width:100%;" class="nut-img-lazyload" />
       </div>
     </nut-swiper>
   </div>

+ 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;

+ 1 - 1
src/packages/tab/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list-pd">
     <!-- DEMO区域 -->
     <h4>默认用法</h4>
     <nut-tab @tab-switch="tabSwitch">

+ 4 - 4
src/packages/tabbar/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list">
     <h4>图标文本标签栏</h4>
     <nut-tabbar @tab-switch="tabSwitch1" type="card" :tabbar-list="tabList1"></nut-tabbar>
     <h4>带有tips的文本标签栏</h4>
@@ -57,7 +57,7 @@ export default {
           curr: true,
           icon: 'https://img14.360buyimg.com/imagetools/jfs/t1/115926/10/6303/6603/5eb93bdeE8951c288/d277b2a668645e73.png',
           activeIcon: 'https://img12.360buyimg.com/imagetools/jfs/t1/126024/30/1055/6952/5eb93be3E45d921c4/3060f25d49fc4ae7.png',
-          href: '###'
+          href: 'https://m.jd.com'
         },
         {
           tabTitle: '分类',
@@ -79,14 +79,14 @@ export default {
           num: 2,
           icon: 'https://img11.360buyimg.com/imagetools/jfs/t1/126465/3/1055/5848/5eb93f31E0ce4f65b/087c08eaeef97b64.png',
           activeIcon: 'https://img10.360buyimg.com/imagetools/jfs/t1/111251/23/6376/6446/5eb93f2dE659da502/41fea546d36b8aaa.png',
-          href: '###'
+          href: 'https://m.jd.com'
         },
         {
           tabTitle: '我的',
           curr: false,
           icon: 'http://img20.360buyimg.com/uba/jfs/t1/20004/20/1045/3620/5c0f3d61Eaaec1670/9e59db63983b7b9f.jpg',
           activeIcon: 'http://img14.360buyimg.com/uba/jfs/t1/23967/14/1072/6714/5c0f3d61E0ad8991e/8f741953f6e38f15.jpg',
-          href: '###'
+          href: 'https://m.jd.com'
         }
       ]
     };

+ 1 - 0
src/packages/tabbar/doc.md

@@ -160,6 +160,7 @@ export default {
 | type | 页签栏的样式 based/card:based是默认样式如吸底样式,card类型每个卡片间有边界线 | String | based
 | tabbar-list | 渲染数据 ,兼容 tabbar-list 和 tabbarList| Array | []
 | bottom | 是否固定在页面底部 |Booble|false|
+| replace | replace为true的时候,跳转url以替换的形式进行 |Boolean|false|
 
 ### tabbar-list
 

+ 12 - 2
src/packages/tabbar/tabbar.vue

@@ -6,7 +6,6 @@
       :class="[{ curr: index == currIndex }, type]"
       :key="value.tabTitle"
       v-on:click="switchTabs(value, index)"
-      :href="value.href"
     >
       <span class="icon-box">
         <b class="tips num" v-if="value.num && value.num <= 99">{{ value.num }}</b>
@@ -36,6 +35,10 @@ export default {
     type: {
       type: String,
       default: 'based'
+    },
+    replace: {
+      type: Boolean,
+      default: false
     }
   },
   data() {
@@ -66,8 +69,15 @@ export default {
     },
     switchTabs: function(value, index) {
       this.currIndex = index;
+      if (this.replace) {
+        //替换url
+        window.location.replace(value.href);
+      } else {
+        if (value.href) {
+          window.location.href = value.href;
+        }
+      }
       this.$emit('tab-switch', value, index);
-      this.$emit('tabSwitch', value, index); //兼容以前驼峰法
     }
   }
 };

+ 1 - 1
src/packages/tabselect/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list">
     <nut-cell isLink title="展示单选弹出层" :showIcon="true" @click.native="show = true"> </nut-cell>
     <nut-tabselect
       :mainTitle="mainTitle"

+ 1 - 1
src/packages/textbox/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="textbox-demo">
+  <div class="demo-list">
     <!-- <h4>示例</h4> -->
     <h4>默认用法 支持异步回显数据</h4>
     <nut-textbox v-model="val"></nut-textbox>

+ 1 - 0
src/packages/textbox/textbox.vue

@@ -56,6 +56,7 @@ export default {
   computed: {
     currentValue: {
       get() {
+        this.txtNum = this.value.length;
         return this.value;
       },
       set(val) {}

+ 1 - 1
src/packages/timeline/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list-pd">
     <h4>基本用法</h4>
     <nut-timeline>
       <nut-timelineitem>

+ 1 - 1
src/packages/video/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="demo-list">
     <h4>基本用法</h4>
     <div class="video-con">
       <nut-video :sources="sources" :options="options" @play="play" @pause="pause" @playend="playend"> </nut-video>

+ 15 - 0
src/styles/variable.scss

@@ -83,3 +83,18 @@ $assetsPath: '../../assets' !default;
 $stepper-color: #333 !default;
 $stepperbar-width: 11px !default;
 $stepperbar-color: #333 !default;
+
+// Notify
+$notify-text-color: #fff;
+$notify-padding: 8px 16px;
+$notify-font-size: $font-size-base;
+$notify-line-height: 20px;
+$notify-primary-background-color: #1989fa;
+$notify-success-background-color: #07c160;
+$notify-danger-background-color: #ee0a24;
+$notify-warning-background-color: #ff976a;
+
+// Fixed
+
+$fixed-bg-color: #fff;
+$fixed-font-color: #333;

+ 3 - 1
types/nutui.d.ts

@@ -68,7 +68,6 @@ 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 {}
@@ -81,4 +80,7 @@ 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 {}
+export declare class FixedNav extends UIComponent {}
+export declare class Gesture extends UIComponent {}