Browse Source

feat: toast开发

songqibin 5 years ago
parent
commit
73742f15ef

+ 9 - 0
src/config.ts

@@ -130,6 +130,15 @@ export const nav = [
         show: true,
         desc: '较长页面快捷回到顶部',
         author: 'liqiong43'
+      },
+      {
+        name: 'Toast',
+        sort: '2',
+        cName: '吐司',
+        type: 'component',
+        show: true,
+        desc: '轻提示。',
+        author: 'undo'
       }
     ]
   },

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

@@ -5,7 +5,7 @@
       <nut-cell>
         <nut-rate v-model:value="state.val"></nut-rate>
       </nut-cell>
-      <nut-cell> 当前分数:{{ state.val }}分 </nut-cell>
+      <nut-cell>当前分数:{{ state.val }}分</nut-cell>
     </div>
 
     <h2>只读</h2>
@@ -13,7 +13,7 @@
       <nut-cell>
         <nut-rate v-model:value="state.val2" :readOnly="true"></nut-rate>
       </nut-cell>
-      <nut-cell> 结果:{{ state.val2 }} </nut-cell>
+      <nut-cell>结果:{{ state.val2 }}</nut-cell>
     </div>
 
     <h2>自定义尺寸</h2>
@@ -28,7 +28,7 @@
       <nut-cell>
         <nut-rate @click="onClick"></nut-rate>
       </nut-cell>
-      <nut-cell> 结果:{{ state.result }} </nut-cell>
+      <nut-cell>结果:{{ state.result }}</nut-cell>
     </div>
 
     <h2>自定义ICON</h2>

+ 4 - 4
src/packages/rate/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <div class="nut-rate">
-    <span
+  <view class="nut-rate">
+    <view
       class="nut-rate-item"
       :class="[{ 'nut-rate-active': n <= state.current }]"
       v-for="n in total"
@@ -12,8 +12,8 @@
         marginRight: spacing + 'px',
         backgroundImage: n <= state.current ? checkedIcon : uncheckedIcon
       }"
-    ></span>
-  </div>
+    ></view>
+  </view>
 </template>
 <script lang="ts">
 import { toRefs, watch, reactive, inject } from 'vue';

+ 80 - 0
src/packages/toast/_toast.ts

@@ -0,0 +1,80 @@
+import { createVNode, render } from 'vue';
+import VueToast from './toast.vue';
+
+const defaultOptions = {
+  msg: '',
+  visible: false,
+  duration: 2000, //显示时间(毫秒)
+  timer: null,
+  center: true,
+  type: '',
+  customClass: '',
+  bottom: 30,
+  size: 'base',
+  icon: null,
+  textAlignCenter: true,
+  loadingRotate: true,
+  bgColor: 'rgba(36, 36, 36, 0.8)',
+  onClose: null,
+  textTimer: null,
+  cover: false, //透明遮罩层
+  coverColor: 'rgba(0, 0, 0, 0)',
+  timeStamp: null,
+  closeOnClickOverlay: false
+};
+
+// let instance;
+// let instanceArr = [];
+// let defaultOptionsMap = {};
+// const currentOptions = {
+//   ...defaultOptions
+// };
+
+const mountToast = opts => {
+  opts = { ...defaultOptions, ...opts };
+  opts.visible = true;
+  opts.id = new Date().getTime();
+  console.log(opts);
+
+  const container = document.createElement('div');
+  // container.className = `nut-toast`;
+  const vm = createVNode(VueToast, opts);
+  render(vm, container);
+  document.body.appendChild(container);
+  console.log(vm);
+  return vm;
+};
+
+const errorMsg = msg => {
+  if (!msg) {
+    console.warn('[NutUI Toast]: msg不能为空');
+    return;
+  }
+};
+
+const Toast = {
+  text(msg, opts = {}) {
+    errorMsg(msg);
+    return mountToast({ ...opts, type: 'text', msg });
+  },
+  success(msg, opts = {}) {
+    errorMsg(msg);
+    return mountToast({ ...opts, msg, type: 'success' });
+  },
+  fail(msg, opts = {}) {
+    errorMsg(msg);
+    return mountToast({ ...opts, msg, type: 'fail' });
+  },
+  warn(msg, opts = {}) {
+    errorMsg(msg);
+    return mountToast({ ...opts, msg, type: 'warn' });
+  }
+};
+
+export default Toast;
+
+// export default {
+//   install(app: App): void {
+//     app.config.globalProperties.$toast = Toast;
+//   }
+// };

+ 66 - 0
src/packages/toast/demo.vue

@@ -0,0 +1,66 @@
+<template>
+  <div class="demo">
+    <h2>基本用法</h2>
+    <div>
+      <nut-cell
+        :showIcon="true"
+        :isLink="true"
+        @click="textToast('我只传了文案一个参数')"
+      >
+        <label>Text 文字提示</label>
+      </nut-cell>
+      <nut-cell
+        :showIcon="true"
+        :isLink="true"
+        @click="successToast('我只传了文案一个参数')"
+      >
+        <label>Success 成功提示</label>
+      </nut-cell>
+      <nut-cell
+        :showIcon="true"
+        :isLink="true"
+        @click="errorToast('我只传了文案一个参数')"
+      >
+        <label>Error 失败提示</label>
+      </nut-cell>
+      <nut-cell
+        :showIcon="true"
+        :isLink="true"
+        @click="warningToast('我只传了文案一个参数')"
+      >
+        <label>Warning 警告提示提示</label>
+      </nut-cell>
+    </div>
+  </div>
+</template>
+
+<script>
+import { createComponent } from '@/utils/create';
+import Toast from './_toast';
+const { createDemo } = createComponent('toast');
+
+export default createDemo({
+  setup() {
+    const textToast = msg => {
+      Toast.text(msg);
+    };
+    const successToast = msg => {
+      Toast.success(msg);
+    };
+    const errorToast = msg => {
+      Toast.fail(msg);
+    };
+    const warningToast = msg => {
+      Toast.warn(msg);
+    };
+    return {
+      textToast,
+      successToast,
+      errorToast,
+      warningToast
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped></style>

+ 175 - 0
src/packages/toast/doc.md

@@ -0,0 +1,175 @@
+# Toast 吐司
+
+轻提示。
+
+## 基本用法
+文字提示
+```javascript
+export default {
+  mounted() {
+    this.$toast.text('提示信息');
+  }
+}
+```
+
+成功提示
+
+```javascript
+export default {
+  mounted() {
+    this.$toast.success('操作成功!');
+  }
+}
+```
+
+失败提示
+
+```javascript
+export default {
+  mounted() {
+    this.$toast.fail('操作失败!');
+  }
+}
+```
+
+警告提示
+
+```javascript
+export default {
+  mounted() {
+    this.$toast.warn('确定删除?');
+  }
+}
+```
+
+## 加载提示
+
+```javascript
+// 带文案,显示透明遮罩层(默认),自动消失
+this.$toast.loading('加载中...',{ 
+    duration:3000
+});
+
+//带文案,显示半透明遮罩层,自动消失,点击遮罩层后消失
+this.$toast.loading('加载中...',{ 
+    duration:3000,
+    coverColor: "rgba(0,0,0,0.5)",
+    closeOnClickOverlay: true
+});
+
+//不会自动消失(默认),不带遮罩层
+this.loading = this.$toast.loading({
+    cover: false
+});
+
+//手动关闭上面的Loading
+this.loading.hide();
+```
+
+## 自定义样式
+
+```javascript
+//自定义背景颜色/透明度
+this.$toast.text('自定义背景颜色/透明度',{
+    bgColor:'rgba(50, 50, 50, 0.9)'
+});
+
+//自定义class
+this.$toast.text('自定义class',{
+    customClass:'my-class'
+});
+
+//自定义Icon
+this.$toast.text('自定义Icon',{
+    icon:"https://img13.360buyimg.com/imagetools/jfs/t1/98294/28/14470/22072/5e65ba08E865683aa/ded7441bdd098511.png"
+});
+```
+## 共享实例
+
+如果不设置id,多个Toast将默认拥有相同的id,**它们将共享一个实例**(loading类型与其他类型实例不共享),新的内容和设置将取代旧的,多个Toast不能同时出现。如果不需要共享实例,可以给其设置id。
+
+```javascript
+//二者id不同,不会共享一个实例
+this.$toast.success(msg,{
+    id:123
+});
+
+this.$toast.text(msg,{
+    id:321,
+    duration:4000
+});
+```
+
+## 支持在JS模块中导入使用
+```javascript
+import { Toast } from "@nutui/nutui";
+
+Toast.text('在js模块中使用');
+// 返回实例,可以手动调用实例中的hide方法关闭提示
+const toast = Toast.loading('在js模块中使用');
+toast.hide();
+```
+
+## 修改默认配置
+
+通过**Toast.setDefaultOptions**函数可以全局修改 Toast 的默认配置,**值得注意的是,loading无法支持默认配置的修改和重置**。
+
+```js
+// 更改所有Toast展示时长设置为5000毫秒
+Toast.setDefaultOptions({
+    duration: 5000,
+    coverColor: "rgba(0, 0, 0, 0.2)",
+    closeOnClickOverlay: true,
+    cover: true
+});
+
+// 重置所有 Toast 的默认配置
+Toast.resetDefaultOptions();
+
+// 更改所有文字提示默认设置
+Toast.setDefaultOptions("text", {
+    size: "large",
+    cover: true,
+    coverColor: "rgba(0, 0, 0, 0.2)",
+    duration: 3000,
+    closeOnClickOverlay: true
+});
+
+// 重置 text Toast 的默认配置
+Toast.resetDefaultOptions("text");
+```
+
+### API
+| 方法名                    | 说明                                                                    | 参数            | 返回值     |
+| ------------------------- | ----------------------------------------------------------------------- | --------------- | ---------- |
+| Toast.text                | 展示文字提示                                                            | options/message | toast 实例 |
+| Toast.loading             | 展示加载提示                                                            | options/message | toast 实例 |
+| Toast.success             | 展示成功提示                                                            | options/message | toast 实例 |
+| Toast.fail                | 展示失败提示                                                            | options/message | toast 实例 |
+| Toast.warn                | 展示警告提示                                                            | options/message | toast 实例 |
+| Toast.hide                | 关闭提示                                                                | force:boolean   | void       |
+| Toast.setDefaultOptions   | 修改默认配置,对所有 Toast 生效<br>传入 type 可以修改指定类型的默认配置 | type/options    | void       |
+| Toast.resetDefaultOptions | 重置默认配置,对所有 Toast 生效<br>传入 type 可以重置指定类型的默认配置 | type            | void       |
+
+## Options
+
+| 字段                | 说明                                                                          | 类型          | 默认值                        |
+| ------------------- | ----------------------------------------------------------------------------- | ------------- | ----------------------------- |
+| msg                 | 消息文本内容,支持传入HTML                                                     | String/VNode  | ""                            |
+| id                  | 标识符,相同者共用一个实例<br>loading类型默认使用一个实例,其他类型默认不共用 | String/Number | -                             |
+| duration            | 展示时长(毫秒)<br>值为 0 时,toast 不会自动消失(loading类型默认为0)       | Number        | 2000                          |
+| center              | 是否展示在页面中部(为false时展示在底部)                                     | Boolean       | true                          |
+| bottom              | 距页面底部的距离(像素),option.center为false时生效                          | Number       | 30                          |
+| textAlignCenter     | 多行文案是否居中                                                              | Boolean       | true                          |
+| bgColor             | 背景颜色(透明度)                                                            | String        | "rgba(46, 46, 46, 0.7)"       |
+| customClass         | 自定义类名                                                                    | String        | ""                            |
+| icon                | 自定义图标,**支持图片链接或base64格式**                                        | String        | ""                            |
+| size                | 文案尺寸,**small**/**base**/**large**三选一                                                  | String        | "base"                        |
+| cover               | 是否显示遮罩层,loading类型默认显示                                           | Boolean       | loading类型true/其他类型false |
+| coverColor          | 遮罩层颜色,默认透明                                                          | String        | "rgba(0,0,0,0)"               |
+| loadingRotate       | loading图标是否旋转,仅对loading类型生效                                      | Boolean       | true                          |
+| onClose             | 关闭时触发的事件                                                              | function      | null                          |
+| closeOnClickOverlay | 是否在点击遮罩层后关闭提示                                                    | Boolean       | false                         |
+| toastStyle          | 提示框style                                                        | object       | {}                         |
+| toastClass          | 提示框class                                                        | String       | ""                         |
+

File diff suppressed because it is too large
+ 126 - 0
src/packages/toast/index.scss


+ 19 - 0
src/packages/toast/index.vue

@@ -0,0 +1,19 @@
+<template>
+  <div class="nut-rate"></div>
+</template>
+<script lang="ts">
+import { toRefs, watch, reactive, inject } from 'vue';
+import { createComponent } from '@/utils/create';
+const { componentName, create } = createComponent('rate');
+
+export default create({
+  props: {},
+  setup(props, { emit, slots }) {
+    return {};
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 176 - 0
src/packages/toast/toast.vue

@@ -0,0 +1,176 @@
+<template>
+  <transition name="toastfade">
+    <div
+      :id="id"
+      :class="toastBodyClass"
+      v-if="state.visible"
+      :style="{
+        bottom: center ? 'auto' : state.bottom + 'px',
+        'background-color': state.coverColor
+      }"
+      @click="clickCover"
+    >
+      <div
+        class="nut-toast-inner"
+        :class="state.toastClass"
+        :style="{
+          'text-align': state.textAlignCenter ? 'center' : 'left',
+          'background-color': state.bgColor,
+          ...state.toastStyle
+        }"
+      >
+        <span v-if="hasIcon" class="nut-toast-icon-wrapper">
+          <i
+            :class="[
+              'nut-toast-icon',
+              type,
+              {
+                'nut-toast-icon-rotate':
+                  type === 'loading' && state.loadingRotate
+              }
+            ]"
+            :style="{ 'background-image': cusIcon }"
+          ></i>
+        </span>
+        <span class="nut-toast-text" v-html="msg"></span>
+      </div>
+    </div>
+  </transition>
+</template>
+<script>
+import { toRefs, reactive, computed, watch } from 'vue';
+import { createComponent } from '@/utils/create';
+const { create } = createComponent('toast');
+export default create({
+  props: {
+    id: {
+      type: String
+    },
+    msg: {
+      type: String,
+      default: ''
+    },
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    duration: {
+      type: Number,
+      default: 2000
+    },
+    center: {
+      type: Boolean,
+      default: true
+    },
+    type: {
+      type: String,
+      default: ''
+    }
+  },
+  setup(props) {
+    console.log('props', props);
+    const { msg, visible, center, type } = toRefs(props);
+    const state = reactive({
+      id: '',
+      msg: 'sadsadsa',
+      visible: visible.value,
+      duration: 2000, //显示时间(毫秒)
+      timer: null,
+      center: true,
+      type: '',
+      customClass: '',
+      bottom: 30,
+      size: 'base',
+      icon: null,
+      textAlignCenter: true,
+      loadingRotate: true,
+      bgColor: 'rgba(0, 0, 0, 1)',
+      onClose: null,
+      textTimer: null,
+      cover: false,
+      coverColor: 'rgba(0, 0, 0, 0)',
+      toastStyle: {},
+      toastClass: '',
+      timeStamp: null,
+      closeOnClickOverlay: false
+    });
+    const clearTimer = () => {
+      if (state.timer) {
+        clearTimeout(state.timer);
+        state.timer = null;
+      }
+    };
+    const hide = force => {
+      clearTimer();
+      state.visible = false;
+      if (force) {
+        clearTimeout(state.textTimer);
+      } else {
+        state.textTimer = setTimeout(() => {
+          clearTimeout(state.textTimer);
+          state.msg = '';
+        });
+      }
+      state.onClose && state.onClose();
+    };
+    const show = force => {
+      clearTimer();
+      clearTimeout(state.textTimer);
+      if (state.duration) {
+        state.timer = setTimeout(() => {
+          hide(force);
+        }, state.duration);
+      }
+    };
+    const clickCover = () => {
+      if (state.closeOnClickOverlay) {
+        hide();
+      }
+    };
+    // show();
+    watch(
+      () => state.visible,
+      val => {
+        if (val) {
+          console.log(val);
+          show();
+        }
+      }
+    );
+
+    const cusIcon = computed(() => {
+      return state.icon ? `url("${state.icon}")` : '';
+    });
+    const hasIcon = computed(() => {
+      console.log(type.value);
+      if (type.value !== 'text') {
+        return true;
+      } else {
+        return !!cusIcon.value;
+      }
+    });
+    console.log('hasIcon', hasIcon.value);
+    const toastBodyClass = computed(() => {
+      return [
+        'nut-toast',
+        { 'nut-toast-center': center.value },
+        { 'nut-toast-has-icon': hasIcon.value },
+        { 'nut-toast-cover': state.cover },
+        { 'nut-loading': type.value == 'loading' },
+        state.customClass,
+        'nut-toast-' + state.size
+      ];
+    });
+    return {
+      msg,
+      center,
+      state,
+      type,
+      clickCover,
+      cusIcon,
+      hasIcon,
+      toastBodyClass
+    };
+  }
+});
+</script>

+ 6 - 0
src/styles/variables.scss

@@ -19,6 +19,12 @@ $black: #000;
 // padding
 $padding-xs: 8px 12px;
 
+$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;
+$line-height-base: 1.5 !default;
 // Font
 $font-size-1: 12px;
 $font-size-2: 14px;