Browse Source

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

成林 宋 5 years ago
parent
commit
24c10c69b9

+ 2 - 0
.npmrc

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

+ 1 - 0
cache/docs.cache

@@ -0,0 +1 @@
+{"design":"vcgLzZ0ctHqnqRhSMmxbHFPKHOk=","international":"48VFbFFji5XeQPSYVbEkhUXs9jw=","intro":"NzilpMOPGLKCi3NAacAUHKA5PM8=","joinus":"xe5bD6hZaxe0Xg0m4zCFnTb5JKY=","start":"hWB+R788ekxKa3Y8TItd8OEYnyE=","theme":"ul//vB9gvub+r0tmKQ0utENYGGY="}

+ 1 - 0
cache/mdToVue.cache

@@ -0,0 +1 @@
+{"mdToVue":"YyHHl228Lm/wkXcldAK2nCQ7hw8="}

File diff suppressed because it is too large
+ 1 - 0
cache/src.cache


+ 29 - 0
src/config.js

@@ -139,6 +139,15 @@ module.exports = {
           author: 'liqiong43'
         },
         {
+          name: 'ActionSheet',
+          sort: '1',
+          cName: '动作面板',
+          type: 'component',
+          show: true,
+          desc: '底部弹出动作菜单面板',
+          author: 'zhangyufei'
+        },
+        {
           name: 'Toast',
           sort: '1',
           cName: '吐司',
@@ -146,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'
         }
       ]
     },
@@ -325,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'
         }
       ]
     },

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

@@ -0,0 +1,190 @@
+<template>
+  <div class="demo-list demo">
+    <h4>基本用法(选择类)</h4>
+    <div>
+      <nut-cell
+        :showIcon="true"
+        :isLink="true"
+        @click="switchActionSheet('isVisible1')"
+      >
+        <span><label>基础用法</label></span>
+        <div class="selected-option">{{ state.val1 }}</div>
+      </nut-cell>
+      <nut-cell
+        :showIcon="true"
+        :isLink="true"
+        @click="switchActionSheet('isVisible2')"
+      >
+        <span><label>展示取消按钮</label></span>
+        <div class="selected-option">{{ state.val2 }}</div>
+      </nut-cell>
+      <div>
+        <nut-cell :isLink="true" @click="switchActionSheet('isVisible3')">
+          <span><label>展示描述信息</label></span>
+        </nut-cell>
+      </div>
+      <h4>选项状态</h4>
+
+      <nut-cell :isLink="true" @click="switchActionSheet('isVisible4')">
+        <span><label>选项状态</label></span>
+        <!-- <div class="selected-option">打开</div> -->
+      </nut-cell>
+      <h4>自定义面板</h4>
+
+      <nut-cell :isLink="true" @click="switchActionSheet('isVisible5')">
+        <span><label>自定义内容</label></span>
+        <!-- <div class="selected-option">打开</div> -->
+      </nut-cell>
+    </div>
+    <!-- demo 基础用法 -->
+    <nut-actionsheet
+      :is-visible="state.isVisible1"
+      @close="switchActionSheet('isVisible1')"
+      :menu-items="menuItemsOne"
+      @choose="chooseItem"
+    ></nut-actionsheet>
+    <!-- demo(带取消按钮) -->
+    <nut-actionsheet
+      :is-visible="state.isVisible2"
+      @close="switchActionSheet('isVisible2')"
+      cancelTxt="取消"
+      :menu-items="menuItemsOne"
+      @choose="chooseItemTwo"
+    ></nut-actionsheet>
+
+    <nut-actionsheet
+      :is-visible="state.isVisible3"
+      :description="state.desc"
+      :menu-items="menuItemsTwo"
+      cancelTxt="取消"
+      @close="switchActionSheet('isVisible3')"
+    >
+    </nut-actionsheet>
+    <!-- demo 选项状态-->
+    <nut-actionsheet
+      :is-visible="state.isVisible4"
+      @close="switchActionSheet('isVisible4')"
+      cancelTxt="取消"
+      :menu-items="menuItemsThree"
+    ></nut-actionsheet>
+    <!-- demo 自定义 -->
+    <nut-actionsheet
+      :is-visible="state.isVisible5"
+      cancelTxt="取消"
+      @close="switchActionSheet('isVisible5')"
+    >
+      <div class="custom-wrap"><span>自定义</span></div>
+    </nut-actionsheet>
+  </div>
+</template>
+
+<script>
+import { reactive } from 'vue';
+import { createComponent } from '@/utils/create';
+const { createDemo } = createComponent('actionsheet');
+export default createDemo({
+  props: {},
+  setup() {
+    const state = reactive({
+      isVisible1: false,
+      isVisible2: false,
+      isVisible3: false,
+      isVisible4: false,
+      isVisible5: false,
+      val1: '',
+      val2: '',
+      val3: '',
+      desc: '这是一段描述信息'
+    });
+    const menuItemsOne = [
+      {
+        name: '选项一',
+        value: 0
+      },
+      {
+        name: '选项二',
+        value: 1
+      },
+      {
+        name: '选项三',
+        value: 2
+      }
+    ];
+    const menuItemsTwo = [
+      {
+        name: '选项一',
+        value: 0
+      },
+      {
+        name: '选项二',
+        value: 1
+      },
+      {
+        name: '选项三',
+        subname: '描述信息',
+        value: 2
+      }
+    ];
+    const menuItemsThree = [
+      {
+        name: '着色选项',
+        color: '#ee0a24',
+        value: 0
+      },
+      {
+        name: '禁用选项',
+        disable: true,
+        value: 1
+      }
+    ];
+    const switchActionSheet = param => {
+      console.log(param);
+      state[`${param}`] = !state[`${param}`];
+      console.log(state[`${param}`], '2');
+    };
+
+    const chooseItem = itemParams => {
+      console.log(itemParams, 'itemParams');
+      state.val1 = itemParams.name;
+    };
+
+    function chooseItemTwo(itemParams) {
+      state.val2 = itemParams.name;
+    }
+
+    return {
+      state,
+      menuItemsOne,
+      menuItemsTwo,
+      menuItemsThree,
+      chooseItem,
+      chooseItemTwo,
+      switchActionSheet
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.demo {
+  height: 100%;
+  background: #f7f8fa;
+  overflow-x: hidden;
+  overflow-y: auto;
+  padding: 0 25px;
+  padding-top: 57px;
+  h4 {
+    height: 56px;
+    line-height: 56px;
+    font-size: 14px;
+    color: #909ca4;
+  }
+}
+.custom-wrap {
+  padding: 110px 0;
+  text-align: center;
+}
+.nut-cell {
+  justify-content: space-between;
+}
+</style>

+ 280 - 0
src/packages/actionsheet/doc.md

@@ -0,0 +1,280 @@
+# ActionSheet 动作面板
+
+从底部弹出的动作菜单面板。
+
+## 基本用法(选择类)
+
+默认
+```html
+<div @click.native="switchActionSheet">
+    <span class="title"><label>性别</label></span>
+    <span class="selected-option">{{sex}}</span>
+</div>
+<nut-actionsheet :is-visible="isVisible" 
+    @close="switchActionSheet"
+    :menu-items="menuItems"
+    @choose="chooseItem"
+></nut-actionsheet>
+```
+```javascript
+export default {
+    data() {
+        return {
+            sex: '请选择',
+            isVisible: false,
+            menuItems: [
+                {
+                    'name': '男',
+                    'value': 0
+                },
+                {
+                    'name': '女',
+                    'value': 1
+                }
+            ]
+        };
+    },
+    methods: {
+        switchActionSheet() {
+            this.isVisible = !this.isVisible;
+        },
+
+        chooseItem(itemParams) {
+            this.sex = itemParams.name;
+        }
+    }
+};
+```
+
+带取消按钮
+```html
+<div @click.native="switchActionSheet">
+    <span class="title"><label>性别</label></span>
+    <span class="selected-option">{{sex}}</span>
+</div>
+ <nut-actionsheet :is-visible="isVisible" 
+    @close="switchActionSheet"
+    :menu-items="menuItems"
+    @choose="chooseItem"
+    cancelTxt="取消"
+></nut-actionsheet>
+```
+```javascript
+export default {
+    data() {
+        return {
+            sex: '请选择',
+            isVisible: false,
+            menuItems: [
+                {
+                    'name': '男',
+                    'value': 0
+                },
+                {
+                    'name': '女',
+                    'value': 1
+                }
+            ]
+        };
+    },
+    methods: {
+        switchActionSheet() {
+            this.isVisible = !this.isVisible;
+        },
+
+        chooseItem(itemParams) {
+            this.sex = itemParams.name;
+        }
+    }
+};
+```
+
+高亮已选项
+```html
+<div @click.native="switchActionSheet">
+    <span class="title"><label>性别</label></span>
+    <span class="selected-option">{{sex}}</span>
+</div>
+<nut-actionsheet :is-visible="isVisible" 
+    @close="switchActionSheet"
+    :menu-items="menuItems"
+    @choose="chooseItem"
+    :chooseTagValue="sex"
+></nut-actionsheet>
+```
+```javascript
+export default {
+    data() {
+        return {
+            sex: '请选择',
+            isVisible: false,
+            menuItems: [
+                {
+                    'name': '男',
+                    'value': 0
+                },
+                {
+                    'name': '女',
+                    'value': 1
+                }
+            ]
+        };
+    },
+    methods: {
+        switchActionSheet() {
+            this.isVisible = !this.isVisible;
+        },
+
+        chooseItem(itemParams) {
+            this.sex = itemParams.name;
+        }
+    }
+};
+```
+
+设置禁用状态
+```html
+<div @click.native="switchActionSheet">
+    <span class="title"><label>性别</label></span>
+    <span class="selected-option">{{sex}}</span>
+</div>
+<nut-actionsheet :is-visible="isVisible" 
+    @close="switchActionSheet"
+    :menu-items="menuItems"
+    @choose="chooseItem"
+    :chooseTagValue="sex"
+></nut-actionsheet>
+```
+```javascript
+export default {
+    data() {
+        return {
+            sex: '请选择',
+            isVisible: false,
+            menuItems: [
+                {
+                    'name': '男',
+                    'value': 0,
+                    'disable': false
+                },
+                {
+                    'name': '女',
+                    'value': 1,
+                    'disable': true
+                }
+            ]
+        };
+    },
+    methods: {
+        switchActionSheet() {
+            this.isVisible = !this.isVisible;
+        },
+
+        chooseItem(itemParams) {
+            this.sex = itemParams.name;
+        }
+    }
+};
+```
+
+## 提示
+```html
+<div @click.native="switchActionSheet">
+    <span class="title"><label>我就列表测试数据</label></span>
+    <span class="sub-title">我是描述~~~~</span>
+    <div class="selected-option">删除本条</div>
+</div>
+<nut-actionsheet :is-visible="isVisible" 
+    :menu-items="menuItems4" 
+    chooseTagValue="确定"
+    cancelTxt="取消"
+    @close="switchActionSheet"
+    @choose="choose"
+>
+    <span slot="title"><label>确定删除吗?</label></span>
+    <span slot="sub-title">删除之后不能,描述信息,删除之后不能,描述信息</span>
+</nut-actionsheet>
+```
+```javascript
+export default {
+    data() {
+        return {
+            sex: '请选择',
+            isVisible: false,
+            menuItems: [
+                {
+                     'name': '确定'
+                }
+            ]
+        };
+    },
+    methods: {
+        switchActionSheet() {
+            this.isVisible = !this.isVisible;
+        },
+
+        choose(itemParams) {
+            
+        }
+    }
+};
+```
+
+## 自定义内容
+```html
+<div @click.native="switchActionSheet">
+    <span class="title"><label>内容自定义</label></span>
+    <div class="selected-option">打开</div>
+</div>
+<nut-actionsheet :is-visible="isVisible" 
+    @close="switchActionSheet"
+>
+    <div slot="custom" class="custom-wrap"><span>自定义</span></div>
+</nut-actionsheet>
+```
+```javascript
+
+export default {
+    data() {
+        return {
+            isVisible: false
+        }   
+    },
+    methods: {
+        switchActionSheet() {
+            this.isVisible = !this.isVisible;
+        }
+    }
+};
+```
+
+## Prop
+
+| 字段 | 说明 | 类型 | 默认值
+|----- | ----- | ----- | ----- 
+| is-animation | 是否开启动画 | Boolean | true
+| is-lock-bg-scroll | 是否锁定背景滚动 | Boolean | false
+| is-visible | 是否可见 | Boolean | false
+| is-show-mask | 是否显示背景遮罩 | Boolean | true
+| is-click-choose-close | 是否点击列表项后立即关闭 | Boolean | true
+| is-click-close-mask | 是否点击mask蒙层关闭 | Boolean | true
+| cancel-txt | 取消文案 | String | '取消'
+| choose-tag-value | 已选值,如果填写,高亮显示 | String | -
+| menu-items | 列表项 | Array | [ ]
+| option-tag | 设置列表项展示使用参数 | String | 'name'
+
+## Slot
+
+| 名称 | 说明 
+|----- | ----- 
+| custom | 自定义内容
+| title | 自定义标题
+| subTitle | 自定义副标题  
+
+## Event
+
+| 字段 | 说明 | 回调参数 
+|----- | ----- | ----- 
+| choose | 选择之后触发 | 选中列表项 
+| close | 关闭时触发 | 无 
+| cancel | 点击取消文案时触发 | 无 

+ 82 - 0
src/packages/actionsheet/index.scss

@@ -0,0 +1,82 @@
+.nut-actionsheet-panel {
+  position: fixed;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  width: 100%;
+  max-height: 80%;
+  overflow: auto;
+  z-index: $zindex-actionsheet;
+  background-color: $body-background;
+}
+
+.nut-actionsheet-modal {
+  padding: 10px;
+  margin: 0;
+  text-align: center;
+  background-color: #fff;
+  border-bottom: 1px solid $light-color;
+
+  .nut-actionsheet-title,
+  .nut-actionsheet-sub-title {
+    padding: 5px 0;
+  }
+
+  .nut-actionsheet-title {
+    font-size: $font-size-base;
+    color: $title-color;
+  }
+
+  .nut-actionsheet-sub-title {
+    font-size: $font-size-small;
+    color: $title-color;
+    margin-inline-start: 0px;
+  }
+}
+
+.nut-actionsheet-menu {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.nut-actionsheet-cancel,
+.nut-actionsheet-item {
+  //   height: 24px;
+  padding: 10px;
+  line-height: 24px;
+  font-size: $font-size-base;
+  color: $title-color;
+  text-align: center;
+  background-color: #fff;
+}
+
+.desc {
+  font-size: 14px;
+  color: #999;
+}
+
+.subdesc {
+  display: block;
+  font-size: 12px;
+  color: #999;
+}
+
+.nut-actionsheet-item {
+  border-bottom: 1px solid $light-color;
+  cursor: pointer;
+}
+
+.nut-actionsheet-item-active {
+  color: $primary-color;
+}
+
+.nut-actionsheet-item-disabled {
+  color: #e1e1e1;
+  cursor: not-allowed;
+}
+
+.nut-actionsheet-cancel {
+  margin-top: 5px;
+  border-top: 1px solid $light-color;
+}

+ 139 - 0
src/packages/actionsheet/index.vue

@@ -0,0 +1,139 @@
+<template>
+  <view class="nut-actionsheet">
+    <nut-popup v-model:show="state.maskIsVisible" position="bottom" round>
+      <view class="nut-actionsheet-panel">
+        <view class="nut-actionsheet-custom">
+          <slot name="custom"></slot>
+        </view>
+        <dl
+          class="nut-actionsheet-modal"
+          v-if="$slots.title || $slots.subTitle"
+        >
+          <dt class="nut-actionsheet-title"><slot name="title"></slot></dt>
+          <dd class="nut-actionsheet-sub-title"
+            ><slot name="sub-title"></slot
+          ></dd>
+        </dl>
+        <ul class="nut-actionsheet-menu">
+          <li class="nut-actionsheet-item desc" v-if="description">{{
+            description
+          }}</li>
+          <li
+            class="nut-actionsheet-item"
+            :class="{
+              'nut-actionsheet-item-disabled': item.disable
+            }"
+            :style="{ color: isHighlight(item) }"
+            v-for="(item, index) of menuItems"
+            :key="index"
+            @click="chooseItem(item, index)"
+            >{{ item[optionTag]
+            }}<view class="subdesc">{{ item.subname }}</view></li
+          >
+        </ul>
+        <div
+          class="nut-actionsheet-cancel"
+          v-if="cancelTxt"
+          @click="cancelActionSheet"
+          >{{ cancelTxt }}</div
+        >
+      </view>
+    </nut-popup>
+  </view>
+</template>
+<script>
+import Popup from '@/packages/popup/index.vue';
+import { createComponent } from '@/utils/create';
+import { watch, reactive } from 'vue';
+const { create } = createComponent('actionsheet');
+export default create({
+  props: {
+    isVisible: {
+      type: Boolean,
+      default: false
+    },
+    isClickChooseClose: {
+      type: Boolean,
+      default: true
+    },
+    cancelTxt: {
+      type: String,
+      default: ''
+    },
+    optionTag: {
+      type: String,
+      default: 'name'
+    },
+    chooseTagValue: {
+      type: String,
+      default: ''
+    },
+    description: {
+      type: String,
+      default: ''
+    },
+    subname: {
+      type: String,
+      default: ''
+    },
+    menuItems: {
+      type: Array,
+      default: () => []
+    }
+  },
+  emits: ['click', 'close', 'cancel', 'choose'],
+
+  setup(props, { emit }) {
+    // state
+    const state = reactive({
+      maskIsVisible: false
+    });
+
+    // methods
+    const isHighlight = item => {
+      console.log(item.color, 'color');
+      return item.color;
+    };
+
+    const closeActionSheet = () => {
+      state.maskIsVisible = false;
+      console.log(state.maskIsVisible, 'mask');
+      emit('close');
+    };
+
+    const cancelActionSheet = () => {
+      closeActionSheet();
+      emit('cancel');
+    };
+
+    const chooseItem = (item, index) => {
+      if (!item.disable) {
+        if (props.isClickChooseClose) {
+          closeActionSheet();
+        }
+        emit('choose', item, index);
+      }
+    };
+
+    watch(
+      () => props.isVisible,
+      val => {
+        if (val) {
+          state.maskIsVisible = true;
+        }
+      }
+    );
+
+    return {
+      isHighlight,
+      closeActionSheet,
+      cancelActionSheet,
+      chooseItem,
+      state
+    };
+  }
+});
+</script>
+<style lang="scss">
+@import 'index.scss';
+</style>

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

+ 6 - 0
src/styles/variables.scss

@@ -116,6 +116,12 @@ $inputnumber-input-border-radius: 8px;
 $inputnumber-input-width: 40px;
 $inputnumber-input-height: 20px;
 
+// actionsheet
+$zindex-actionsheet: 10001 !default;
+$body-background: #f6f6f6 !default;
+$light-color: #f6f6f6 !default;
+$font-size-base: 14px !default;
+$font-size-small: 12px !default;
 // calendar
 $calendar-primary-color: $primary-color;
 $calendar-choose-color: #fef6f6;