quanyawei
2023-11-10 0a24ffeb3eff074c8f11e9f94fdf2c2cb4929799
uni_modules/uview-ui/components/u-toast/u-toast.vue
New file
@@ -0,0 +1,291 @@
<template>
   <view class="u-toast">
      <u-overlay
         :show="isShow"
         :custom-style="overlayStyle"
      >
         <view
            class="u-toast__content"
            :style="[contentStyle]"
            :class="['u-type-' + tmpConfig.type, (tmpConfig.type === 'loading' || tmpConfig.loading) ?  'u-toast__content--loading' : '']"
         >
            <u-loading-icon
               v-if="tmpConfig.type === 'loading'"
               mode="circle"
               color="rgb(255, 255, 255)"
               inactiveColor="rgb(120, 120, 120)"
               size="25"
            ></u-loading-icon>
            <u-icon
               v-else-if="tmpConfig.type !== 'defalut' && iconName"
               :name="iconName"
               size="17"
               :color="tmpConfig.type"
               :customStyle="iconStyle"
            ></u-icon>
            <u-gap
               v-if="tmpConfig.type === 'loading' || tmpConfig.loading"
               height="12"
               bgColor="transparent"
            ></u-gap>
            <text
               class="u-toast__content__text"
               :class="['u-toast__content__text--' + tmpConfig.type]"
               style="max-width: 400rpx;"
            >{{ tmpConfig.message }}</text>
         </view>
      </u-overlay>
   </view>
</template>
<script>
   /**
    * toast 消息提示
    * @description 此组件表现形式类似uni的uni.showToastAPI,但也有不同的地方。
    * @tutorial https://www.uviewui.com/components/toast.html
    * @property {String | Number}   zIndex      toast展示时的zIndex值 (默认 10090 )
    * @property {Boolean}         loading      是否加载中 (默认 false )
    * @property {String | Number}   message      显示的文字内容
    * @property {String}         icon      图标,或者绝对路径的图片
    * @property {String}         type      主题类型 (默认 default)
    * @property {Boolean}         show      是否显示该组件 (默认 false)
    * @property {Boolean}         overlay      是否显示透明遮罩,防止点击穿透 (默认 false )
    * @property {String}         position   位置 (默认 'center' )
    * @property {Object}         params      跳转的参数
    * @property {String | Number}  duration   展示时间,单位ms (默认 2000 )
    * @property {Boolean}         isTab      是否返回的为tab页面 (默认 false )
    * @property {String}         url         toast消失后是否跳转页面,有则跳转,优先级高于back参数
    * @property {Function}         complete   执行完后的回调函数
    * @property {Boolean}         back      结束toast是否自动返回上一页 (默认 false )
    * @property {Object}         customStyle   组件的样式,对象形式
    * @event {Function} show 显示toast,如需一进入页面就显示toast,请在onReady生命周期调用
    * @example <u-toast ref="uToast" />
    */
   export default {
      name: 'u-toast',
      mixins: [uni.$u.mpMixin, uni.$u.mixin],
      data() {
         return {
            isShow: false,
            timer: null, // 定时器
            config: {
               message: '', // 显示文本
               type: '', // 主题类型,primary,success,error,warning,black
               duration: 2000, // 显示的时间,毫秒
               icon: true, // 显示的图标
               position: 'center', // toast出现的位置
               complete: null, // 执行完后的回调函数
               overlay: false, // 是否防止触摸穿透
               loading: false, // 是否加载中状态
            },
            tmpConfig: {}, // 将用户配置和内置配置合并后的临时配置变量
         }
      },
      computed: {
         iconName() {
            // 只有不为none,并且type为error|warning|succes|info时候,才显示图标
            if(!this.tmpConfig.icon || this.tmpConfig.icon == 'none') {
               return '';
            }
            if (['error', 'warning', 'success', 'primary'].includes(this.tmpConfig.type)) {
               return uni.$u.type2icon(this.tmpConfig.type)
            } else {
               return ''
            }
         },
         overlayStyle() {
            const style = {
               justifyContent: 'center',
               alignItems: 'center',
               display: 'flex'
            }
            // 将遮罩设置为100%透明度,避免出现灰色背景
            style.backgroundColor = 'rgba(0, 0, 0, 0)'
            return style
         },
         iconStyle() {
            const style = {}
            // 图标需要一个右边距,以跟右边的文字有隔开的距离
            style.marginRight = '4px'
            // #ifdef APP-NVUE
            // iOSAPP下,图标有1px的向下偏移,这里进行修正
            if (uni.$u.os() === 'ios') {
               style.marginTop = '-1px'
            }
            // #endif
            return style
         },
         loadingIconColor() {
            let color = 'rgb(255, 255, 255)'
            if (['error', 'warning', 'success', 'primary'].includes(this.tmpConfig.type)) {
               // loading-icon组件内部会对color参数进行一个透明度处理,该方法要求传入的颜色值
               // 必须为rgb格式的,所以这里做一个处理
               color = uni.$u.hexToRgb(uni.$u.color[this.tmpConfig.type])
            }
            return color
         },
         // 内容盒子的样式
         contentStyle() {
            const windowHeight = uni.$u.sys().windowHeight, style = {}
            let value = 0
            // 根据top和bottom,对Y轴进行窗体高度的百分比偏移
            if(this.tmpConfig.position === 'top') {
               value = - windowHeight * 0.25
            } else if(this.tmpConfig.position === 'bottom') {
               value = windowHeight * 0.25
            }
            style.transform = `translateY(${value}px)`
            return style
         }
      },
      created() {
         // 通过主题的形式调用toast,批量生成方法函数
         ['primary', 'success', 'error', 'warning', 'default', 'loading'].map(item => {
            this[item] = message => this.show({
               type: item,
               message
            })
         })
      },
      methods: {
         // 显示toast组件,由父组件通过this.$refs.xxx.show(options)形式调用
         show(options) {
            // 不将结果合并到this.config变量,避免多次调用u-toast,前后的配置造成混乱
            this.tmpConfig = uni.$u.deepMerge(this.config, options)
            // 清除定时器
            this.clearTimer()
            this.isShow = true
            this.timer = setTimeout(() => {
               // 倒计时结束,清除定时器,隐藏toast组件
               this.clearTimer()
               // 判断是否存在callback方法,如果存在就执行
               typeof(this.tmpConfig.complete) === 'function' && this.tmpConfig.complete()
            }, this.tmpConfig.duration)
         },
         // 隐藏toast组件,由父组件通过this.$refs.xxx.hide()形式调用
         hide() {
            this.clearTimer()
         },
         clearTimer() {
            this.isShow = false
            // 清除定时器
            clearTimeout(this.timer)
            this.timer = null
         }
      },
      beforeDestroy() {
         this.clearTimer()
      }
   }
</script>
<style lang="scss" scoped>
   @import "../../libs/css/components.scss";
   $u-toast-color:#fff !default;
   $u-toast-border-radius:4px !default;
   $u-toast-border-background-color:#585858 !default;
   $u-toast-border-font-size:14px !default;
   $u-toast-border-padding:12px 20px !default;
   $u-toast-loading-border-padding: 20px 20px !default;
   $u-toast-content-text-color:#fff !default;
   $u-toast-content-text-font-size:15px !default;
   $u-toast-u-icon:10rpx !default;
   $u-toast-u-type-primary-color:$u-primary !default;
   $u-toast-u-type-primary-background-color:#ecf5ff !default;
   $u-toast-u-type-primary-border-color:rgb(215, 234, 254) !default;
   $u-toast-u-type-primary-border-width:1px !default;
   $u-toast-u-type-success-color: $u-success !default;
   $u-toast-u-type-success-background-color: #dbf1e1 !default;
   $u-toast-u-type-success-border-color: #BEF5C8 !default;
   $u-toast-u-type-success-border-width: 1px !default;
   $u-toast-u-type-error-color:$u-error !default;
   $u-toast-u-type-error-background-color:#fef0f0 !default;
   $u-toast-u-type-error-border-color:#fde2e2 !default;
   $u-toast-u-type-error-border-width: 1px !default;
   $u-toast-u-type-warning-color:$u-warning !default;
   $u-toast-u-type-warning-background-color:#fdf6ec !default;
   $u-toast-u-type-warning-border-color:#faecd8 !default;
   $u-toast-u-type-warning-border-width: 1px !default;
   $u-toast-u-type-default-color:#fff !default;
   $u-toast-u-type-default-background-color:#585858 !default;
   .u-toast {
      &__content {
         @include flex;
         padding: $u-toast-border-padding;
         border-radius: $u-toast-border-radius;
         background-color: $u-toast-border-background-color;
         color: $u-toast-color;
         align-items: center;
         /* #ifndef APP-NVUE */
         max-width: 600rpx;
         /* #endif */
         position: relative;
         &--loading {
            flex-direction: column;
            padding: $u-toast-loading-border-padding;
         }
         &__text {
            color: $u-toast-content-text-color;
            font-size: $u-toast-content-text-font-size;
            line-height: $u-toast-content-text-font-size;
            &--default {
               color: $u-toast-content-text-color;
            }
            &--error {
               color: $u-error;
            }
            &--primary {
               color: $u-primary;
            }
            &--success {
               color: $u-success;
            }
            &--warning {
               color: $u-warning;
            }
         }
      }
   }
   .u-type-primary {
      color: $u-toast-u-type-primary-color;
      background-color: $u-toast-u-type-primary-background-color;
      border-color: $u-toast-u-type-primary-border-color;
      border-width: $u-toast-u-type-primary-border-width;
   }
   .u-type-success {
      color: $u-toast-u-type-success-color;
      background-color: $u-toast-u-type-success-background-color;
      border-color: $u-toast-u-type-success-border-color;
      border-width: 1px;
   }
   .u-type-error {
      color: $u-toast-u-type-error-color;
      background-color: $u-toast-u-type-error-background-color;
      border-color: $u-toast-u-type-error-border-color;
      border-width: $u-toast-u-type-error-border-width;
   }
   .u-type-warning {
      color: $u-toast-u-type-warning-color;
      background-color: $u-toast-u-type-warning-background-color;
      border-color: $u-toast-u-type-warning-border-color;
      border-width: 1px;
   }
   .u-type-default {
      color: $u-toast-u-type-default-color;
      background-color: $u-toast-u-type-default-background-color;
   }
</style>