Browse Source

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

zhangyufei1 5 years ago
parent
commit
b90f66b377

+ 2 - 0
.npmrc

@@ -0,0 +1,2 @@
+registry=https://registry.npmjs.org
+engine-strict=true

+ 20 - 0
src/config.js

@@ -155,6 +155,16 @@ module.exports = {
           show: true,
           desc: '轻提示',
           author: 'undo'
+        },
+        {
+          version: '3.0.0',
+          name: 'Notify',
+          type: 'component',
+          cName: '消息提示',
+          desc: '在页面顶部展示消息提示,支持函数调用和组件调用两种方式',
+          sort: 4,
+          show: true,
+          author: 'wangyue217'
         }
       ]
     },
@@ -334,6 +344,16 @@ module.exports = {
           show: true,
           desc: '标签栏组件',
           author: 'Drjingfubo'
+        },
+        {
+          version: '3.0.0',
+          name: 'NoticeBar',
+          type: 'component',
+          cName: '公告栏',
+          desc: '用于循环播放展示一组消息通知',
+          sort: 5,
+          show: false,
+          author: 'wangyue92'
         }
       ]
     },

+ 39 - 12
src/packages/calendar/demo.vue

@@ -39,13 +39,34 @@
       >
       </nut-calendar>
     </div>
-    <h2>自定义日历</h2>
+
+    <h2>自定义日历-自动回填</h2>
+    <div>
+      <nut-cell
+        :showIcon="true"
+        title="选择日期"
+        :desc="date3 ? date3 : '请选择'"
+        @click="openSwitch('isVisible3')"
+      >
+      </nut-cell>
+      <nut-calendar
+        :is-visible="isVisible3"
+        @close="closeSwitch('isVisible3')"
+        @choose="setChooseValue3"
+        :default-value="date3"
+        :start-date="null"
+        :end-date="null"
+        :is-auto-back-fill="true"
+      >
+      </nut-calendar>
+    </div>
+
     <h2>平铺展示</h2>
     <div class="test-calendar-wrapper">
       <nut-calendar
         :poppable="false"
-        :is-visible="isVisible2"
         :default-value="date2"
+        :is-auto-back-fill="true"
         @choose="setChooseValue2"
       >
       </nut-calendar>
@@ -64,12 +85,11 @@ interface TestCalendarState {
   isVisible: boolean;
   date: string;
   dateWeek: string;
-
-  date2: string;
-  isVisible2: boolean;
-
   isVisible1: boolean;
   date1: string[];
+  date2: string;
+  isVisible3: boolean;
+  date3: string;
 }
 export default createDemo({
   props: {},
@@ -79,11 +99,13 @@ export default createDemo({
       date: '',
       dateWeek: '',
 
+      isVisible1: false,
+      date1: ['2019-12-23', '2019-12-26'],
+
       date2: '2020-07-08',
-      isVisible2: true,
 
-      isVisible1: false,
-      date1: ['2019-12-23', '2019-12-26']
+      isVisible3: false,
+      date3: ''
     });
     const openSwitch = param => {
       state[`${param}`] = true;
@@ -98,13 +120,17 @@ export default createDemo({
       state.dateWeek = param[4];
     };
 
+    const setChooseValue1 = param => {
+      state.date1 = [...[param[0][3], param[1][3]]];
+    };
+
     const setChooseValue2 = param => {
       state.date2 = param[3];
       console.log(state.date2);
     };
 
-    const setChooseValue1 = param => {
-      state.date1 = [...[param[0][3], param[1][3]]];
+    const setChooseValue3 = param => {
+      state.date3 = param[3];
     };
 
     return {
@@ -112,8 +138,9 @@ export default createDemo({
       openSwitch,
       closeSwitch,
       setChooseValue,
+      setChooseValue1,
       setChooseValue2,
-      setChooseValue1
+      setChooseValue3
     };
   }
 });

+ 212 - 34
src/packages/calendar/doc.md

@@ -1,34 +1,212 @@
-#  calendar组件
-
-    ### 介绍
-    
-    基于 xxxxxxx
-    
-    ### 安装
-    
-    
-    
-    ## 代码演示
-    
-    ### 基础用法1
-    
-
-    
-    ## API
-    
-    ### Props
-    
-    | 参数         | 说明                             | 类型   | 默认值           |
-    |--------------|----------------------------------|--------|------------------|
-    | name         | 图标名称或图片链接               | String | -                |
-    | color        | 图标颜色                         | String | -                |
-    | size         | 图标大小,如 '20px' '2em' '2rem' | String | -                |
-    | class-prefix | 类名前缀,用于使用自定义图标     | String | 'nutui-iconfont' |
-    | tag          | HTML 标签                        | String | 'i'              |
-    
-    ### Events
-    
-    | 事件名 | 说明           | 回调参数     |
-    |--------|----------------|--------------|
-    | click  | 点击图标时触发 | event: Event |
-    
+# calendar 组件
+
+### 介绍
+
+日历,可平铺/弹窗展示
+
+### 安装
+
+```javascript
+import { createApp } from 'vue';
+import { Calendar } from '@nutui/nutui';
+
+const app = createApp();
+app.use(Calendar);
+```
+
+## 代码演示
+
+### 基础用法
+
+```html
+<nut-cell
+  :showIcon="true"
+  title="选择单个日期"
+  :desc="date ? `${date} ${dateWeek}` : '请选择'"
+  @click="openSwitch('isVisible')"
+>
+</nut-cell>
+<nut-calendar
+  :is-visible="isVisible"
+  :default-value="date"
+  @close="closeSwitch('isVisible')"
+  @choose="setChooseValue"
+  :start-date="`2019-10-11`"
+  :end-date="`2022-11-11`"
+>
+</nut-calendar>
+```
+
+```javascript
+setup() {
+    const state: TestCalendarState = reactive({
+      isVisible: false,
+      date: '',
+      dateWeek: ''
+    });
+    const openSwitch = param => {
+      state[`${param}`] = true;
+    };
+    const closeSwitch = param => {
+      state[`${param}`] = false;
+    };
+    const setChooseValue = param => {
+      state.date = param[3];
+      state.dateWeek = param[4];
+    };
+    return {
+      ...toRefs(state),
+      openSwitch,
+      closeSwitch,
+      setChooseValue
+    };
+  }
+```
+
+### 区间选择
+
+```html
+<nut-cell
+  :showIcon="true"
+  title="选择日期区间"
+  :desc="date ? `${date[0]}至${date[1]}` : '请选择'"
+  @click="openSwitch('isVisible')"
+>
+</nut-cell>
+<nut-calendar
+  :is-visible="isVisible"
+  :default-value="date"
+  type="range"
+  :start-date="`2019-12-22`"
+  :end-date="`2021-01-08`"
+  @close="closeSwitch('isVisible')"
+  @choose="setChooseValue"
+>
+</nut-calendar>
+```
+
+```javascript
+setup() {
+    const state: TestCalendarState = reactive({
+      date: ['2019-12-23', '2019-12-26'],
+      isVisible2: true
+    });
+    const openSwitch = param => {
+      state[`${param}`] = true;
+    };
+    const closeSwitch = param => {
+      state[`${param}`] = false;
+    };
+    const setChooseValue= param => {
+      state.date = [...[param[0][3], param[1][3]]];
+    };
+    return {
+      ...toRefs(state),
+      openSwitch,
+      closeSwitch,
+      setChooseValue,
+    };
+  }
+```
+
+### 自定义日历-自动回填
+
+```html
+<nut-cell
+  :showIcon="true"
+  title="选择日期"
+  :desc="date ? date : '请选择'"
+  @click="openSwitch('isVisible')"
+>
+</nut-cell>
+<nut-calendar
+  :is-visible="isVisible"
+  @close="closeSwitch('isVisible')"
+  @choose="setChooseValue"
+  :start-date="null"
+  :end-date="null"
+  :is-auto-back-fill="true"
+>
+</nut-calendar>
+```
+
+```javascript
+setup() {
+    const state: TestCalendarState = reactive({
+      date: '',
+      isVisible: false
+    });
+    const openSwitch = param => {
+      state[`${param}`] = true;
+    };
+    const closeSwitch = param => {
+      state[`${param}`] = false;
+    };
+     const setChooseValue = param => {
+      state.date= param[3];
+    };
+    return {
+      ...toRefs(state),
+      setChooseValue
+    };
+  }
+```
+
+### 平铺展示
+
+```html
+<div class="test-calendar-wrapper">
+  <nut-calendar
+    :poppable="false"
+    :is-auto-back-fill="true"
+    :default-value="date"
+    @choose="setChooseValue"
+  >
+  </nut-calendar
+></div>
+```
+
+```javascript
+setup() {
+    const state: TestCalendarState = reactive({
+      date: '2020-07-08'
+    });
+    const setChooseValue = param => {
+      state.date = param[3];
+    };
+    return {
+      ...toRefs(state),
+      setChooseValue
+    };
+  }
+```
+
+### 基础用法
+
+```html
+
+```
+
+```javascript
+```
+
+## API
+
+### Props
+
+| 字段              | 说明                                              | 类型           | 默认值          |
+| ----------------- | ------------------------------------------------- | -------------- | --------------- |
+| type              | 类型,日期选择'one',区间选择'range'              | String         | 'one'           |
+| is-visible        | 是否可见                                          | Boolean        | false           |
+| poppable          | 是否弹窗状态展示                                  | Boolean        | true            |
+| is-auto-back-fill | 自动回填                                          | Boolean        | false           |
+| title             | 显示标题                                          | String         | ‘日期选择’      |
+| default-value     | 默认值,日期选择 String 格式,区间选择 Array 格式 | String / Array | null            |
+| start-date        | 开始日期, 如果不限制开始日期传 null              | String         | 今天            |
+| end-date          | 结束日期,如果不限制结束日期传 null               | String         | 距离今天 365 天 |
+
+### Events
+
+| 事件名 | 说明 | 回调参数 |
+| choose | 选择之后或是点击确认按钮触发 | 日期数组(包含年月日和星期)
+| close | 关闭时触发 | -

+ 12 - 10
src/packages/calendar/index.vue

@@ -1,14 +1,16 @@
 <template>
   <nut-popup
+    v-if="poppable"
     v-model:show="state.childIsVisible"
     position="bottom"
     round
-    @click="closeActionSheet"
-    v-if="poppable"
+    :closeable="true"
+    @click-overlay="closePopup"
+    @click-close-icon="closePopup"
   >
     <nut-calendar-item
       props
-      ref="mychild"
+      ref="calendarRef"
       :type="type"
       :is-auto-back-fill="isAutoBackFill"
       :poppable="poppable"
@@ -83,7 +85,7 @@ export default create({
 
   setup(props, { emit }) {
     // element refs
-    const mychild = ref<null | HTMLElement>(null);
+    const calendarRef = ref<null | HTMLElement>(null);
 
     // state
     const state = reactive({
@@ -91,10 +93,6 @@ export default create({
     });
 
     // methods
-    const closeActionSheet = () => {
-      //mychild.value && mychild.value.closeActionSheet();
-    };
-
     const update = () => {
       state.childIsVisible = false;
     };
@@ -108,6 +106,10 @@ export default create({
       emit('choose', param);
     };
 
+    const closePopup = () => {
+      close();
+    };
+
     watch(
       () => props.isVisible,
       val => {
@@ -118,11 +120,11 @@ export default create({
     );
 
     return {
-      closeActionSheet,
+      closePopup,
       update,
       close,
       choose,
-      mychild,
+      calendarRef,
       state,
       ...toRefs(props)
     };

+ 8 - 4
src/packages/calendaritem/index.scss

@@ -2,7 +2,7 @@
   position: relative;
   display: flex;
   flex: 1;
-  height: 100%;
+  height: 518px;
   padding-top: 132px;
   padding-bottom: 78px;
   color: $calendar-base-color;
@@ -11,6 +11,7 @@
   overflow: hidden;
 
   &.nut-calendar-tile {
+    height: 100%;
     padding-top: 46px;
     padding-bottom: 0;
 
@@ -21,6 +22,10 @@
     }
   }
 
+  &.nut-calendar-nofooter {
+    padding-bottom: 0;
+  }
+
   // 头部导航
   .nut-calendar-header {
     position: absolute;
@@ -51,7 +56,6 @@
       align-items: center;
       justify-content: space-around;
       height: 46px;
-      border-radius: 0px 0px 12px 12px;
       box-shadow: 0px 4px 10px 0px rgba($color: #000000, $alpha: 0.06);
 
       .calendar-week-item {
@@ -123,7 +127,7 @@
           flex-direction: column;
           position: relative;
 
-          .curr-tips,
+          .calendar-curr-tips,
           .calendar-day-tip {
             position: absolute;
             top: 10px;
@@ -137,7 +141,7 @@
             background-color: $calendar-primary-color;
             color: $white !important;
 
-            .curr-tips {
+            .calendar-curr-tips {
               visibility: hidden;
             }
 

+ 77 - 49
src/packages/calendaritem/index.vue

@@ -1,5 +1,11 @@
 <template>
-  <view class="nut-calendar" :class="{ 'nut-calendar-tile': !poppable }">
+  <view
+    class="nut-calendar"
+    :class="{
+      'nut-calendar-tile': !poppable,
+      'nut-calendar-nofooter': isAutoBackFill
+    }"
+  >
     <!-- header -->
     <view
       class="nut-calendar-header"
@@ -7,9 +13,9 @@
     >
       <template v-if="poppable">
         <view class="calendar-title">{{ title }}</view>
-        <view class="calendar-curr-month">2020年11月</view>
+        <view class="calendar-curr-month">{{ yearMonthTitle }}</view>
       </template>
-      <view class="calendar-weeks">
+      <view class="calendar-weeks" ref="weeksPanel">
         <view
           class="calendar-week-item"
           v-for="(item, index) of weeks"
@@ -50,7 +56,9 @@
                   <view class="calendar-day">{{
                     day.type == 'curr' ? day.day : ''
                   }}</view>
-                  <view class="curr-tips" v-if="isCurrDay(month, day.day)"
+                  <view
+                    class="calendar-curr-tips"
+                    v-if="isCurrDay(month, day.day)"
                     >今天</view
                   >
                   <view
@@ -71,28 +79,20 @@
       </view>
     </view>
     <!-- footer-->
-    <view class="nut-calendar-footer" v-if="poppable">
+    <view class="nut-calendar-footer" v-if="poppable && !isAutoBackFill">
       <view class="calendar-confirm-btn" @click="confirm">确定</view>
     </view>
   </view>
 </template>
 <script lang="ts">
-// import {
-//   PropType,
-//   computed,
-//   watch,
-//   reactive,
-//   ref,
-//   toRefs,
-//   readonly
-// } from 'vue';
 import { PropType, reactive, ref, watch, toRefs } from 'vue';
-import { createComponent } from '@/utils/create';
+import { createComponent } from '../../utils/create';
 const { create } = createComponent('calendar-item');
-import Utils from '@/utils/date';
+import Utils from '../../utils/date';
+import requestAniFrame from '../../utils/raf';
 type InputDate = string | string[];
 interface CalendarState {
-  childIsVisible: boolean;
+  yearMonthTitle: string;
   currDate: InputDate;
   unLoadPrev: boolean;
   touchParams: any;
@@ -106,6 +106,7 @@ interface CalendarState {
   startData: InputDate;
   endData: InputDate;
   isRange: boolean;
+  timer: number;
 }
 
 export default create({
@@ -147,10 +148,11 @@ export default create({
     // element refs
     const months = ref<null | HTMLElement>(null);
     const monthsPanel = ref<null | HTMLElement>(null);
+    const weeksPanel = ref<null | HTMLElement>(null);
 
     // state
     const state: CalendarState = reactive({
-      childIsVisible: false,
+      yearMonthTitle: '',
       currDate: '',
       unLoadPrev: false,
       touchParams: {
@@ -170,7 +172,8 @@ export default create({
       dayPrefix: 'calendar-month-day',
       startData: '',
       endData: '',
-      isRange: props.type === 'range'
+      isRange: props.type === 'range',
+      timer: 0
     });
 
     // 日期转化成数组
@@ -206,7 +209,8 @@ export default create({
       const currDate = getCurrDate(day, month, isRange);
       if (day.type == 'curr') {
         if (
-          (!state.isRange && Utils.isEqual(state.currDate, currDate)) ||
+          (!state.isRange &&
+            Utils.isEqual(state.currDate as string, currDate)) ||
           (state.isRange && (isStart(currDate) || isEnd(currDate)))
         ) {
           return `${state.dayPrefix}-active`;
@@ -235,7 +239,6 @@ export default create({
       if ((state.isRange && state.chooseData.length == 2) || !state.isRange) {
         emit('choose', state.chooseData);
         if (props.poppable) {
-          state.childIsVisible = false;
           emit('update');
         }
       }
@@ -397,12 +400,12 @@ export default create({
       } else {
         if (
           props.startDate &&
-          Utils.compareDate(state.currDate, props.startDate)
+          Utils.compareDate(state.currDate as string, props.startDate)
         ) {
           state.currDate = props.startDate;
         } else if (
           props.endDate &&
-          !Utils.compareDate(state.currDate, props.endDate)
+          !Utils.compareDate(state.currDate as string, props.endDate)
         ) {
           state.currDate = props.endDate;
         }
@@ -411,6 +414,7 @@ export default create({
       }
 
       getMonth(state.defaultData, 'next');
+      state.yearMonthTitle = state.monthsData[0].title;
 
       let i = 1;
       do {
@@ -436,7 +440,6 @@ export default create({
           true
         );
       }
-      console.log(state.currDate, 'state.currDate');
     };
 
     // 区间选择&&当前月&&选中态
@@ -448,6 +451,7 @@ export default create({
       );
     };
 
+    // 是否有开始提示
     const isStartTip = (day, month) => {
       if (isActive(day, month)) {
         return isStart(getCurrDate(day, month));
@@ -456,34 +460,65 @@ export default create({
       }
     };
 
+    // 是否有结束提示
     const isEndTip = (day, month) => {
       return isActive(day, month);
     };
 
+    // 是否有是当前日期
     const isCurrDay = (month, day) => {
       const date = `${month.curData[0]}-${month.curData[1]}-${day}`;
       return Utils.isEqual(date, Utils.date2Str(new Date()));
     };
 
-    const setTransform = (translateY = 0, type?, time = 1000) => {
-      if (type === 'end') {
-        monthsPanel?.value &&
-          (monthsPanel.value.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`);
-      } else {
-        monthsPanel?.value && (monthsPanel.value.style.webkitTransition = '');
+    // 监听月份滚动,改变月份标题
+    const loadScroll = () => {
+      if (!props.poppable) {
+        return false;
       }
+      requestAniFrame(() => {
+        if (weeksPanel?.value && monthsPanel?.value) {
+          const top = weeksPanel?.value.getBoundingClientRect().bottom;
+          const monthsDoms = monthsPanel.value.querySelectorAll(
+            '.calendar-month'
+          );
+          for (let i = 0; i < monthsDoms.length; i++) {
+            if (
+              monthsDoms[i].getBoundingClientRect().top <= top &&
+              monthsDoms[i].getBoundingClientRect().bottom >= top
+            ) {
+              state.yearMonthTitle = state.monthsData[i].title;
+            } else if (state.scrollDistance === 0) {
+              state.yearMonthTitle = state.monthsData[0].title;
+            }
+          }
+        }
+      });
+    };
 
-      monthsPanel?.value &&
-        (monthsPanel.value.style.webkitTransform = `translateY(${translateY}px)`);
-      state.scrollDistance = translateY;
+    // 设置月份滚动距离和速度
+    const setTransform = (translateY = 0, type?, time = 1000) => {
+      if (monthsPanel?.value) {
+        if (type === 'end') {
+          monthsPanel.value.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`;
+          clearTimeout(state.timer);
+          state.timer = setTimeout(() => {
+            loadScroll();
+          }, time);
+        } else {
+          monthsPanel.value.style.webkitTransition = '';
+          loadScroll();
+        }
+        monthsPanel.value.style.webkitTransform = `translateY(${translateY}px)`;
+        state.scrollDistance = translateY;
+      }
     };
 
+    // 计算滚动方向和距离
     const setMove = (move, type?, time?) => {
       let updateMove = move + state.transformY;
       const h = months.value?.offsetHeight || 0;
-
       const offsetHeight = monthsPanel.value?.offsetHeight || 0;
-
       if (type === 'end') {
         // 限定滚动距离
         if (updateMove > 0) {
@@ -495,8 +530,7 @@ export default create({
         if (offsetHeight <= h && state.monthsData.length == 1) {
           updateMove = 0;
         }
-        const endMove = updateMove;
-        setTransform(endMove, type, time);
+        setTransform(updateMove, type, time);
       } else {
         if (updateMove > 0 && updateMove > 100) {
           updateMove = 100;
@@ -518,6 +552,7 @@ export default create({
       }
     };
 
+    // 监听touch开始
     const touchStart = event => {
       const changedTouches = event.changedTouches[0];
       state.touchParams.startY = changedTouches.pageY;
@@ -525,6 +560,7 @@ export default create({
       state.transformY = state.scrollDistance;
     };
 
+    // 监听touchmove
     const touchMove = event => {
       //event.preventDefault();
       const changedTouches = event.changedTouches[0];
@@ -537,6 +573,7 @@ export default create({
       setMove(move);
     };
 
+    // 监听touchend
     const touchEnd = event => {
       const changedTouches = event.changedTouches[0];
       state.touchParams.lastY = changedTouches.pageY;
@@ -568,6 +605,7 @@ export default create({
       }
     };
 
+    // 重新渲染
     const resetRender = () => {
       state.chooseData.splice(0);
       state.monthsData.splice(0);
@@ -577,16 +615,7 @@ export default create({
       initData();
     };
 
-    const closeActionSheet = () => {
-      if (props.poppable) {
-        state.childIsVisible = false;
-        emit('update');
-        emit('close');
-      }
-      resetRender();
-    };
-
-    // 初始化
+    // 初始化数据
     initData();
 
     //监听 默认值更改
@@ -594,7 +623,6 @@ export default create({
       () => props.defaultValue,
       val => {
         if (val) {
-          console.log(val, 'init');
           resetRender();
         }
       }
@@ -613,7 +641,7 @@ export default create({
       confirm,
       monthsPanel,
       months,
-      closeActionSheet,
+      weeksPanel,
       ...toRefs(state),
       ...toRefs(props)
     };

+ 12 - 3
src/packages/menu/demo.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="demo full">
+  <div class="demo full menu-demo">
     <h2>基础用法</h2>
     <nut-menu>
       <nut-menu-item :menuList="menuList" title="最新商品"></nut-menu-item>
@@ -46,6 +46,13 @@
       ></nut-menu-item>
     </nut-menu>
 
+    <h2>禁止蒙层展示</h2>
+    <p class="tips">属性`hasMask`控制是否有蒙层,默认为 `true`展示蒙层 </p>
+    <nut-menu :hasMask="false">
+      <nut-menu-item :menuList="menuList" title="最新商品"></nut-menu-item>
+      <nut-menu-item :menuList="menuList" :title="title"></nut-menu-item>
+    </nut-menu>
+
     <h2>点击事件</h2>
     <p class="tips"
       >标题点击事件`menu-click`,菜单列表选择点击事件`on-change`</p
@@ -136,8 +143,7 @@ export default createDemo({
   color: #909ca4;
   margin-top: -10px;
   margin-bottom: 10px;
-}
-.nut-menu {
+  padding: 0 22px;
 }
 .base-style.nut-menu-item {
   .nut-menu-panel {
@@ -148,4 +154,7 @@ export default createDemo({
 .user-style {
   padding: 20px;
 }
+#app .demo.menu-demo {
+  padding-bottom: 200px;
+}
 </style>

+ 14 - 0
src/packages/menu/doc.md

@@ -67,6 +67,19 @@ app.use(Menu);
     <nut-menu-item :menuList="menuList2" title="筛选" disabled ></nut-menu-item>
 </nut-menu>
 ```
+
+### 禁止蒙层展示
+属性`hasMask`控制是否有蒙层,默认为 `true`展示蒙层 
+
+```html
+<nut-menu :hasMask="false">
+    <nut-menu-item :menuList="menuList" title="最新商品">
+    </nut-menu-item>
+    <nut-menu-item :menuList="menuList" :title="title">
+    </nut-menu-item>
+</nut-menu>
+```
+
 ### 点击事件
 
 `Menu` 的 `@menu-click` 事件返回点击的菜单标题,`@on-change`事件返回菜单列表选中的数据。
@@ -133,6 +146,7 @@ const alertText = (info, type) => {
 | disabled | 是否开启禁用设置,默认不开启    | Boolean | false |
 | maxHeight | 菜单列表最大高度,单位px    | String, Number | - |
 | autoClose | 选择后下拉菜单列表是否自动收起,默认自动收起   | Boolean | true |
+|hasMask| 是否有蒙层 | Boolean | true|
 
 ### Events
 

+ 18 - 7
src/packages/menu/index.vue

@@ -1,10 +1,10 @@
 <template>
-  <view class="nut-menu">
+  <view class="nut-menu" :style="showMask && `z-index:9999`">
     <slot></slot>
   </view>
 </template>
 <script lang="ts">
-import { toRefs } from 'vue';
+import { toRefs, reactive } from 'vue';
 import { createComponent } from '@/utils/create';
 import { useChildren } from '@/utils/useRelation/useChildren';
 export const MENU_KEY = 'nutMenu';
@@ -16,16 +16,27 @@ export default create({
       //单选 simple  多选  multiple,暂留
       type: String,
       default: 'simple'
+    },
+    hasMask: {
+      type: Boolean,
+      default: true
     }
   },
   components: {},
 
   setup(props, { emit }) {
-    // const { autoClose } = toRefs(props);
-    // const handleClick = (event: Event) => {
-    //   emit('click', event);
-    // };
-    // return { autoClose };
+    const state = reactive({
+      showMask: false
+    });
+    const handleMaskShow = status => {
+      state.showMask = status;
+    };
+    const { linkChildren } = useChildren(MENU_KEY);
+    linkChildren({
+      handleMaskShow,
+      hasMask: props.hasMask
+    });
+    return { ...toRefs(state) };
   }
 });
 </script>

+ 16 - 1
src/packages/menuitem/index.vue

@@ -1,12 +1,15 @@
 <template>
   <view
+    id="menuId"
     class="nut-menu-item"
     :class="[{ disabled: disabled }, { 'nut-menu-active': showPanel }]"
   >
+    <nut-popup v-model:show="showMask"></nut-popup>
     <view class="nut-menu-title" @click="handleMenuPanel">
       <view class="title-name" v-html="menuTitle"></view>
       <i class="icon"></i>
     </view>
+
     <view
       class="nut-menu-panel"
       ref="menuPanel"
@@ -90,9 +93,11 @@ export default create({
     const { menuList, multiStyle } = toRefs(props);
     const menuTitle = ref(props.title);
     const menu = useParent(MENU_KEY);
+    const parent: any = reactive(menu.parent as any);
     const state = reactive({
       showPanel: false,
-      currMenu: 0
+      currMenu: 0,
+      showMask: false
     });
 
     const handleMenuPanel = () => {
@@ -101,7 +106,12 @@ export default create({
       if (props.disabled) {
         return;
       }
+
       state.showPanel = !state.showPanel;
+      if (parent.hasMask) {
+        state.showMask = !state.showMask;
+        parent.handleMaskShow(state.showPanel);
+      }
     };
     //menu列表浮层展示和隐藏
     const handleShowAndHide = (event: any) => {
@@ -109,6 +119,8 @@ export default create({
       if (menuBox && state.showPanel) {
         if (!menuBox.contains(event.target)) {
           state.showPanel = false;
+          state.showMask = false;
+          parent.handleMaskShow(false);
         }
       }
     };
@@ -117,6 +129,8 @@ export default create({
       state.currMenu = index;
       if (props.autoClose) {
         state.showPanel = false;
+        state.showMask = false;
+        parent.handleMaskShow(false);
       }
       emit('on-change', item, menuTitle.value);
     };
@@ -138,6 +152,7 @@ export default create({
     });
     return {
       ...toRefs(state),
+      ...toRefs(parent),
       handleMenuPanel,
       checkMenus,
       menuTitle,

+ 23 - 0
src/packages/noticebar/demo.vue

@@ -0,0 +1,23 @@
+<template>
+  <div class="demo">
+    <h2>基础用法</h2>
+    <nut-cell>
+      <nut-noticebar
+        text="华为畅享9新品即将上市,活动期间0元预约可参与抽奖,赢HUAWEI WATCH等好礼,更多产品信息请持续关注!"
+      ></nut-noticebar>
+    </nut-cell>
+  </div>
+</template>
+
+<script lang="ts">
+import { createComponent } from '@/utils/create';
+const { createDemo } = createComponent('noticebar');
+export default createDemo({
+  props: {},
+  setup() {
+    return {};
+  }
+});
+</script>
+
+<style lang="scss" scoped></style>

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

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

+ 2 - 0
src/packages/noticebar/index.scss

@@ -0,0 +1,2 @@
+.nut-noticebar {
+}

+ 42 - 0
src/packages/noticebar/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <view :class="classes">
+    <view>{{ name }}</view>
+    <view>{{ txt }}</view>
+  </view>
+</template>
+<script lang="ts">
+import { toRefs } from 'vue';
+import { createComponent } from '@/utils/create';
+const { componentName, create } = createComponent('noticebar');
+
+export default create({
+  props: {
+    name: {
+      type: String,
+      default: ''
+    },
+    txt: {
+      type: String,
+      default: ''
+    }
+  },
+  components: {},
+  emits: ['click'],
+
+  setup(props, { emit }) {
+    console.log('componentName', componentName);
+
+    const { name, txt } = toRefs(props);
+
+    const handleClick = (event: Event) => {
+      emit('click', event);
+    };
+
+    return { name, txt, handleClick };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

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

@@ -0,0 +1,35 @@
+<template>
+  <div class="demo">
+    <h2>基础用法</h2>
+    <nut-cell :showIcon="true" :isLink="true" @click="notify1('通知内容')">
+      <span>
+        <label>基础用法</label>
+      </span>
+    </nut-cell>
+  </div>
+</template>
+
+<script lang="ts">
+import { createApp } from 'vue';
+import { createComponent } from '@/utils/create';
+import notify from './index';
+const { createDemo } = createComponent('notify');
+const app = createApp({});
+app.use(notify);
+export default createDemo({
+  props: {},
+  setup() {
+    const notify1 = () => {
+      notify('content');
+    };
+    return {
+      notify1
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.nut-temp {
+}
+</style>

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

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

+ 2 - 0
src/packages/notify/index.scss

@@ -0,0 +1,2 @@
+.nut-notify {
+}

+ 143 - 0
src/packages/notify/index.ts

@@ -0,0 +1,143 @@
+import VanNotify from './index.vue';
+import {
+  createApp,
+  reactive,
+  Component,
+  nextTick,
+  getCurrentInstance,
+  h
+} from 'vue';
+
+let timer;
+let instance;
+const inBrowser = typeof window !== 'undefined';
+
+function isObject(val: unknown): val is Record<any, any> {
+  return val !== null && typeof val === 'object';
+}
+
+function parseOptions(message) {
+  return isObject(message) ? message : { message };
+}
+
+function useExpose(apis: Record<string, any>) {
+  const instance = getCurrentInstance();
+  if (instance) {
+    Object.assign(instance.proxy, apis);
+  }
+}
+function usePopupState() {
+  const state = reactive({
+    show: false
+  });
+
+  const toggle = (show: boolean) => {
+    state.show = show;
+  };
+
+  const open = (props: Record<string, any>) => {
+    Object.assign(state, props);
+
+    nextTick(() => {
+      toggle(true);
+    });
+  };
+
+  const close = () => {
+    toggle(false);
+  };
+
+  useExpose({ open, close, toggle });
+
+  return {
+    open,
+    close,
+    state,
+    toggle
+  };
+}
+function mountComponent(RootComponent: Component) {
+  const app = createApp(RootComponent);
+  const root = document.createElement('div');
+
+  document.body.appendChild(root);
+
+  return {
+    instance: app.mount(root),
+    unmount() {
+      app.unmount(root);
+      document.body.removeChild(root);
+    }
+  };
+}
+function initInstance() {
+  ({ instance } = mountComponent({
+    setup() {
+      const { state, toggle } = usePopupState();
+      return h('img', {});
+    }
+  }));
+}
+
+function Notify(options) {
+  if (!inBrowser) {
+    return;
+  }
+
+  if (!instance) {
+    initInstance();
+  }
+
+  options = {
+    ...Notify.currentOptions,
+    ...parseOptions(options)
+  };
+
+  instance.open(options);
+  clearTimeout(timer);
+
+  if (options.duration > 0) {
+    timer = setTimeout(Notify.clear, options.duration);
+  }
+
+  return instance;
+}
+
+function defaultOptions() {
+  return {
+    type: 'danger',
+    color: undefined,
+    message: '',
+    onClose: null,
+    onClick: null,
+    onOpened: null,
+    duration: 3000,
+    className: '',
+    background: undefined
+  };
+}
+
+Notify.clear = () => {
+  if (instance) {
+    instance.toggle(false);
+  }
+};
+
+Notify.currentOptions = defaultOptions();
+
+Notify.setDefaultOptions = options => {
+  Object.assign(Notify.currentOptions, options);
+};
+
+Notify.resetDefaultOptions = () => {
+  Notify.currentOptions = defaultOptions();
+};
+
+Notify.install = app => {
+  app.use(VanNotify);
+  app.config.globalProperties.$notify = Notify;
+};
+
+Notify.Component = VanNotify;
+
+export default Notify;

+ 47 - 0
src/packages/notify/index.vue

@@ -0,0 +1,47 @@
+<template>
+  <view class="nut-notify">
+    <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>
+  </view>
+</template>
+<script lang="ts">
+import { toRefs } from 'vue';
+import { createComponent } from '@/utils/create';
+import Popup from '@/packages/popup/index.vue';
+const { componentName, create } = createComponent('notify');
+
+export default create({
+  props: {
+    color: String,
+    message: [Number, String],
+    className: null,
+    background: String,
+    type: {
+      type: String,
+      default: 'danger'
+    }
+  },
+
+  setup(props, { slots }) {
+    return {};
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 1 - 1
src/sites/service/ArticleApiService.ts

@@ -18,6 +18,6 @@ export class ArticleApiService {
    * @returns
    */
   saveUserInfo(parmas) {
-    return this.httpClient.request('/user/saveVisitInfo', 'post', parmas);
+    return this.httpClient.request('/visit/saveVisitInfo', 'post', parmas);
   }
 }

+ 12 - 43
src/utils/date.ts

@@ -3,15 +3,15 @@ const Utils = {
    * 是否为闫年
    * @return {Boolse} true|false
    */
-  isLeapYear: function(y: number) {
+  isLeapYear: function(y: number): boolean {
     return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
   },
 
   /**
    * 返回星期数
-   * @return {Number}
+   * @return {String}
    */
-  getWhatDay: function(year, month, day) {
+  getWhatDay: function(year: number, month: number, day: number): string {
     const date = new Date(year + '/' + month + '/' + day);
     const index = date.getDay();
     const dayNames = [
@@ -30,7 +30,7 @@ const Utils = {
    * 返回星期数
    * @return {Number}
    */
-  getMonthPreDay: function(year, month) {
+  getMonthPreDay: function(year: number, month: number): number {
     const date = new Date(year + '/' + month + '/01');
     let day = date.getDay();
     if (day == 0) {
@@ -43,14 +43,14 @@ const Utils = {
    * 返回月份天数
    * @return {Number}
    */
-  getMonthDays: function(year, month) {
+  getMonthDays: function(year: string, month: string): number {
     if (/^0/.test(month)) {
       month = month.split('')[1];
     }
     return [
       0,
       31,
-      this.isLeapYear(year) ? 29 : 28,
+      this.isLeapYear(Number(year)) ? 29 : 28,
       31,
       30,
       31,
@@ -68,7 +68,7 @@ const Utils = {
    * 补齐数字位数
    * @return {string}
    */
-  getNumTwoBit: function(n) {
+  getNumTwoBit: function(n: number): string {
     n = Number(n);
     return (n > 9 ? '' : '0') + n;
   },
@@ -77,8 +77,7 @@ const Utils = {
    * 日期对象转成字符串
    * @return {string}
    */
-  date2Str: function(date, split?: string) {
-    if (typeof date == 'string') return date;
+  date2Str: function(date: Date, split?: string): string {
     split = split || '-';
     const y = date.getFullYear();
     const m = this.getNumTwoBit(date.getMonth() + 1);
@@ -91,7 +90,7 @@ const Utils = {
    * @param {Number} 0返回今天的日期、1返回明天的日期,2返回后天得日期,依次类推
    * @return {string} '2014-12-31'
    */
-  getDay: function(i) {
+  getDay: function(i: number): string {
     i = i || 0;
     let date = new Date();
     const diff = i * (1000 * 60 * 60 * 24);
@@ -100,25 +99,10 @@ const Utils = {
   },
 
   /**
-   * 时间戳转换为日期格式
-   * @return {String}
-   */
-  timestampToDate: function(timestamp) {
-    const date = new Date(timestamp);
-    return (
-      date.getFullYear() +
-      '-' +
-      this.getNumTwoBit(date.getMonth() + 1) +
-      '-' +
-      this.getNumTwoBit(date.getDate())
-    );
-  },
-
-  /**
    * 时间比较
    * @return {Boolean}
    */
-  compareDate: function(date1, date2) {
+  compareDate: function(date1: string, date2: string): boolean {
     const startTime = new Date(date1.replace('-', '/').replace('-', '/'));
     const endTime = new Date(date2.replace('-', '/').replace('-', '/'));
     if (startTime >= endTime) {
@@ -126,27 +110,12 @@ const Utils = {
     }
     return true;
   },
-  /**
-   * 时间比较
-   * @return {Boolean}
-   */
-  compareDateArr: function(date1, date2) {
-    const startTime = new Date();
-    startTime.setFullYear(date1[0], date1[1], date1[2]);
-    startTime.setHours(date1[3], date1[4]);
-    const endTime = new Date();
-    endTime.setFullYear(date2[0], date2[1], date2[2]);
-    endTime.setHours(date2[3], date2[4]);
-    if (startTime >= endTime) {
-      return false;
-    }
-    return true;
-  },
+
   /**
    * 时间是否相等
    * @return {Boolean}
    */
-  isEqual: function(date1, date2) {
+  isEqual: function(date1: string, date2: string): boolean {
     const startTime = new Date(date1).getTime();
     const endTime = new Date(date2).getTime();
     if (startTime == endTime) {

+ 17 - 0
src/utils/raf.ts

@@ -0,0 +1,17 @@
+function requestAniFrame() {
+  if (typeof window !== 'undefined') {
+    return (
+      window.requestAnimationFrame ||
+      window.webkitRequestAnimationFrame ||
+      function(callback) {
+        window.setTimeout(callback, 1000 / 60);
+      }
+    );
+  } else {
+    return function(callback) {
+      setTimeout(callback, 1000 / 60);
+    };
+  }
+}
+
+export default requestAniFrame();