Browse Source

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

dsj 5 years ago
parent
commit
d21670e11b

+ 3 - 1
.postcssrc.js

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

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

+ 2 - 1
package.json

@@ -48,7 +48,8 @@
     "@babel/plugin-transform-runtime": "^7.9.6",
     "@commitlint/cli": "^8.0.0",
     "@commitlint/config-conventional": "^8.0.0",
-    "@nutui/cli": "^0.2.2",
+    "@nutui/cli": "^0.2.3",
+    "autoprefixer": "^9.8.4",
     "babel-plugin-istanbul": "^6.0.0",
     "gsap": "^3.2.6",
     "hammerjs": "^2.0.8",

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": "数字滚动",
@@ -644,6 +645,15 @@
       "author": "yumingming"
     },
     {
+      "name": "FixedNav",
+      "type": "component",
+      "chnName": "悬浮导航",
+      "desc": "拖拽导航",
+      "sort": "3",
+      "showDemo": true,
+      "author": "richard1015"
+    },
+    {
       "version": "1.0.0",
       "name": "Gesture",
       "type": "directive",
@@ -654,4 +664,4 @@
       "author": "dsj"
     }
   ]
-}
+}

+ 19 - 13
src/nutui.js

@@ -124,8 +124,12 @@ import './packages/drag/drag.scss'; // import VueQr from "./packages/qart/index.
 
 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';
+import FixedNav from './packages/fixednav/index.js';
+import './packages/fixednav/fixednav.scss';
 import Gesture from './packages/gesture/index.js';
 import './packages/gesture/gesture.scss';
 
@@ -178,19 +182,21 @@ 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
 };
 

+ 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]}`;
         }

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

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

@@ -0,0 +1,90 @@
+<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>

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

@@ -0,0 +1,116 @@
+# FixedNav 悬浮导航
+
+可拖拽的悬浮导航
+
+## 基本用法
+
+```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;
+            }
+        }
+    }
+}

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

@@ -0,0 +1,79 @@
+<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

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

@@ -1,8 +1,12 @@
 <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',

+ 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;
+    }
+  
+    &--success {
+      background-color: $notify-success-background-color;
+    }
+  
+    &--danger {
+      background-color: $notify-danger-background-color;
+    }
+  
+    &--warning {
+      background-color: $notify-warning-background-color;
+    }
+  }

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

@@ -0,0 +1,104 @@
+<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>

+ 23 - 3
src/styles/variable.scss

@@ -10,7 +10,8 @@ $dark-color: #dadada !default;
 // ---- base ----
 $body-background: #f6f6f6 !default;
 $mask-bg: rgba(0, 0, 0, 0.5) !default;
-$font-family: PingHei, 'Lucida Grande', 'Lucida Sans Unicode', STHeiti, Helvetica, Arial, Verdana, 'sans-serif', 'PingHei-light', SimHei, 'Droid Sans' !default;
+$font-family: PingHei, "Lucida Grande", "Lucida Sans Unicode", STHeiti, Helvetica, Arial, Verdana, "sans-serif",
+    "PingHei-light", SimHei, "Droid Sans" !default;
 $font-size-base: 14px !default;
 $font-size-small: 12px !default;
 $font-size-large: 16px !default;
@@ -60,7 +61,11 @@ $btn-gradient-color: #fff !default;
 $btn-gradient-start-color: #ff4f18;
 $btn-gradient-end-color: #f20000;
 $btn-gradient-bg: linear-gradient(315deg, $btn-gradient-start-color 0%, $btn-gradient-end-color 100%) !default;
-$btn-gradient-active-bg: linear-gradient(315deg, darken($btn-gradient-start-color, 10%) 0%, darken($btn-gradient-end-color, 10%) 100%) !default;
+$btn-gradient-active-bg: linear-gradient(
+    315deg,
+    darken($btn-gradient-start-color, 10%) 0%,
+    darken($btn-gradient-end-color, 10%) 100%
+) !default;
 $btn-default-color: $normal-color !default;
 $btn-default-bg: #fff !default;
 $btn-default-border: $border-color-dark !default;
@@ -78,8 +83,23 @@ $zindex-actionsheet: 10001 !default;
 $zindex-dialog: 10000 !default;
 $zindex-picker: 10050 !default;
 // ---- Assets path ----
-$assetsPath: '../../assets' !default;
+$assetsPath: "../../assets" !default;
 // ---- Stepper ----
 $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;

+ 2 - 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,5 +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 {}