skyouc
2024-12-21 c9c263dc43ad90f95f24a036cee9e6b47afb596c
uni_modules/uni-badge/components/uni-badge/uni-badge.vue
@@ -1,268 +1,268 @@
<template>
   <view class="uni-badge--x">
      <slot />
      <text v-if="text" :class="classNames" :style="[badgeWidth, positionStyle, customStyle, dotStyle]"
         class="uni-badge" @click="onClick()">{{displayValue}}</text>
   </view>
</template>
<script>
   /**
    * Badge 数字角标
    * @description 数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景
    * @tutorial https://ext.dcloud.net.cn/plugin?id=21
    * @property {String} text 角标内容
    * @property {String} size = [normal|small] 角标内容
    * @property {String} type = [info|primary|success|warning|error] 颜色类型
    *    @value info 灰色
    *    @value primary 蓝色
    *    @value success 绿色
    *    @value warning 黄色
    *    @value error 红色
    * @property {String} inverted = [true|false] 是否无需背景颜色
    * @property {Number} maxNum 展示封顶的数字值,超过 99 显示 99+
    * @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上
    *    @value rightTop 右上
    *    @value rightBottom 右下
    *    @value leftTop 左上
    *    @value leftBottom 左下
    * @property {Array[number]} offset   距定位角中心点的偏移量,只有存在 absolute 属性时有效,例如:[-10, -10] 表示向外偏移 10px,[10, 10] 表示向 absolute 指定的内偏移 10px
    * @property {String} isDot = [true|false] 是否显示为一个小点
    * @event {Function} click 点击 Badge 触发事件
    * @example <uni-badge text="1"></uni-badge>
    */
   export default {
      name: 'UniBadge',
      emits: ['click'],
      props: {
         type: {
            type: String,
            default: 'error'
         },
         inverted: {
            type: Boolean,
            default: false
         },
         isDot: {
            type: Boolean,
            default: false
         },
         maxNum: {
            type: Number,
            default: 99
         },
         absolute: {
            type: String,
            default: ''
         },
         offset: {
            type: Array,
            default () {
               return [0, 0]
            }
         },
         text: {
            type: [String, Number],
            default: ''
         },
         size: {
            type: String,
            default: 'small'
         },
         customStyle: {
            type: Object,
            default () {
               return {}
            }
         }
      },
      data() {
         return {};
      },
      computed: {
         width() {
            return String(this.text).length * 8 + 12
         },
         classNames() {
            const {
               inverted,
               type,
               size,
               absolute
            } = this
            return [
               inverted ? 'uni-badge--' + type + '-inverted' : '',
               'uni-badge--' + type,
               'uni-badge--' + size,
               absolute ? 'uni-badge--absolute' : ''
            ].join(' ')
         },
         positionStyle() {
            if (!this.absolute) return {}
            let w = this.width / 2,
               h = 10
            if (this.isDot) {
               w = 5
               h = 5
            }
            const x = `${- w  + this.offset[0]}px`
            const y = `${- h + this.offset[1]}px`
            const whiteList = {
               rightTop: {
                  right: x,
                  top: y
               },
               rightBottom: {
                  right: x,
                  bottom: y
               },
               leftBottom: {
                  left: x,
                  bottom: y
               },
               leftTop: {
                  left: x,
                  top: y
               }
            }
            const match = whiteList[this.absolute]
            return match ? match : whiteList['rightTop']
         },
         badgeWidth() {
            return {
               width: `${this.width}px`
            }
         },
         dotStyle() {
            if (!this.isDot) return {}
            return {
               width: '10px',
               height: '10px',
               borderRadius: '10px'
            }
         },
         displayValue() {
            const {
               isDot,
               text,
               maxNum
            } = this
            return isDot ? '' : (Number(text) > maxNum ? `${maxNum}+` : text)
         }
      },
      methods: {
         onClick() {
            this.$emit('click');
         }
      }
   };
</script>
<style lang="scss" scoped>
   $uni-primary: #2979ff !default;
   $uni-success: #4cd964 !default;
   $uni-warning: #f0ad4e !default;
   $uni-error: #dd524d !default;
   $uni-info: #909399 !default;
   $bage-size: 12px;
   $bage-small: scale(0.8);
   .uni-badge--x {
      /* #ifdef APP-NVUE */
      // align-self: flex-start;
      /* #endif */
      /* #ifndef APP-NVUE */
      display: inline-block;
      /* #endif */
      position: relative;
   }
   .uni-badge--absolute {
      position: absolute;
   }
   .uni-badge--small {
      transform: $bage-small;
      transform-origin: center center;
   }
   .uni-badge {
      /* #ifndef APP-NVUE */
      display: flex;
      overflow: hidden;
      box-sizing: border-box;
      /* #endif */
      justify-content: center;
      flex-direction: row;
      height: 20px;
      line-height: 18px;
      color: #fff;
      border-radius: 100px;
      background-color: $uni-info;
      background-color: transparent;
      border: 1px solid #fff;
      text-align: center;
      font-family: 'Helvetica Neue', Helvetica, sans-serif;
      font-size: $bage-size;
      /* #ifdef H5 */
      z-index: 999;
      cursor: pointer;
      /* #endif */
      &--info {
         color: #fff;
         background-color: $uni-info;
      }
      &--primary {
         background-color: $uni-primary;
      }
      &--success {
         background-color: $uni-success;
      }
      &--warning {
         background-color: $uni-warning;
      }
      &--error {
         background-color: $uni-error;
      }
      &--inverted {
         padding: 0 5px 0 0;
         color: $uni-info;
      }
      &--info-inverted {
         color: $uni-info;
         background-color: transparent;
      }
      &--primary-inverted {
         color: $uni-primary;
         background-color: transparent;
      }
      &--success-inverted {
         color: $uni-success;
         background-color: transparent;
      }
      &--warning-inverted {
         color: $uni-warning;
         background-color: transparent;
      }
      &--error-inverted {
         color: $uni-error;
         background-color: transparent;
      }
   }
</style>
<template>
   <view class="uni-badge--x">
      <slot />
      <text v-if="text" :class="classNames" :style="[badgeWidth, positionStyle, customStyle, dotStyle]"
         class="uni-badge" @click="onClick()">{{displayValue}}</text>
   </view>
</template>
<script>
   /**
    * Badge 数字角标
    * @description 数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景
    * @tutorial https://ext.dcloud.net.cn/plugin?id=21
    * @property {String} text 角标内容
    * @property {String} size = [normal|small] 角标内容
    * @property {String} type = [info|primary|success|warning|error] 颜色类型
    *    @value info 灰色
    *    @value primary 蓝色
    *    @value success 绿色
    *    @value warning 黄色
    *    @value error 红色
    * @property {String} inverted = [true|false] 是否无需背景颜色
    * @property {Number} maxNum 展示封顶的数字值,超过 99 显示 99+
    * @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上
    *    @value rightTop 右上
    *    @value rightBottom 右下
    *    @value leftTop 左上
    *    @value leftBottom 左下
    * @property {Array[number]} offset   距定位角中心点的偏移量,只有存在 absolute 属性时有效,例如:[-10, -10] 表示向外偏移 10px,[10, 10] 表示向 absolute 指定的内偏移 10px
    * @property {String} isDot = [true|false] 是否显示为一个小点
    * @event {Function} click 点击 Badge 触发事件
    * @example <uni-badge text="1"></uni-badge>
    */
   export default {
      name: 'UniBadge',
      emits: ['click'],
      props: {
         type: {
            type: String,
            default: 'error'
         },
         inverted: {
            type: Boolean,
            default: false
         },
         isDot: {
            type: Boolean,
            default: false
         },
         maxNum: {
            type: Number,
            default: 99
         },
         absolute: {
            type: String,
            default: ''
         },
         offset: {
            type: Array,
            default () {
               return [0, 0]
            }
         },
         text: {
            type: [String, Number],
            default: ''
         },
         size: {
            type: String,
            default: 'small'
         },
         customStyle: {
            type: Object,
            default () {
               return {}
            }
         }
      },
      data() {
         return {};
      },
      computed: {
         width() {
            return String(this.text).length * 8 + 12
         },
         classNames() {
            const {
               inverted,
               type,
               size,
               absolute
            } = this
            return [
               inverted ? 'uni-badge--' + type + '-inverted' : '',
               'uni-badge--' + type,
               'uni-badge--' + size,
               absolute ? 'uni-badge--absolute' : ''
            ].join(' ')
         },
         positionStyle() {
            if (!this.absolute) return {}
            let w = this.width / 2,
               h = 10
            if (this.isDot) {
               w = 5
               h = 5
            }
            const x = `${- w  + this.offset[0]}px`
            const y = `${- h + this.offset[1]}px`
            const whiteList = {
               rightTop: {
                  right: x,
                  top: y
               },
               rightBottom: {
                  right: x,
                  bottom: y
               },
               leftBottom: {
                  left: x,
                  bottom: y
               },
               leftTop: {
                  left: x,
                  top: y
               }
            }
            const match = whiteList[this.absolute]
            return match ? match : whiteList['rightTop']
         },
         badgeWidth() {
            return {
               width: `${this.width}px`
            }
         },
         dotStyle() {
            if (!this.isDot) return {}
            return {
               width: '10px',
               height: '10px',
               borderRadius: '10px'
            }
         },
         displayValue() {
            const {
               isDot,
               text,
               maxNum
            } = this
            return isDot ? '' : (Number(text) > maxNum ? `${maxNum}+` : text)
         }
      },
      methods: {
         onClick() {
            this.$emit('click');
         }
      }
   };
</script>
<style lang="scss" scoped>
   $uni-primary: #2979ff !default;
   $uni-success: #4cd964 !default;
   $uni-warning: #f0ad4e !default;
   $uni-error: #dd524d !default;
   $uni-info: #909399 !default;
   $bage-size: 12px;
   $bage-small: scale(0.8);
   .uni-badge--x {
      /* #ifdef APP-NVUE */
      // align-self: flex-start;
      /* #endif */
      /* #ifndef APP-NVUE */
      display: inline-block;
      /* #endif */
      position: relative;
   }
   .uni-badge--absolute {
      position: absolute;
   }
   .uni-badge--small {
      transform: $bage-small;
      transform-origin: center center;
   }
   .uni-badge {
      /* #ifndef APP-NVUE */
      display: flex;
      overflow: hidden;
      box-sizing: border-box;
      /* #endif */
      justify-content: center;
      flex-direction: row;
      height: 20px;
      line-height: 18px;
      color: #fff;
      border-radius: 100px;
      background-color: $uni-info;
      background-color: transparent;
      border: 1px solid #fff;
      text-align: center;
      font-family: 'Helvetica Neue', Helvetica, sans-serif;
      font-size: $bage-size;
      /* #ifdef H5 */
      z-index: 999;
      cursor: pointer;
      /* #endif */
      &--info {
         color: #fff;
         background-color: $uni-info;
      }
      &--primary {
         background-color: $uni-primary;
      }
      &--success {
         background-color: $uni-success;
      }
      &--warning {
         background-color: $uni-warning;
      }
      &--error {
         background-color: $uni-error;
      }
      &--inverted {
         padding: 0 5px 0 0;
         color: $uni-info;
      }
      &--info-inverted {
         color: $uni-info;
         background-color: transparent;
      }
      &--primary-inverted {
         color: $uni-primary;
         background-color: transparent;
      }
      &--success-inverted {
         color: $uni-success;
         background-color: transparent;
      }
      &--warning-inverted {
         color: $uni-warning;
         background-color: transparent;
      }
      &--error-inverted {
         color: $uni-error;
         background-color: transparent;
      }
   }
</style>