my-flex/src/FlexItem.vue

<template>
  <div :class="classes" :style="styles" @mouseenter="handleMouseEnter" @mouseleave="handleMouseLeave">
    <div :class="innerClasses">
      <div v-if="middle"
           :class="helperClasses">
        <slot></slot>
      </div>
      <slot v-else></slot>
    </div>

  </div>
</template>

<script>

  /**
   * FlexItem Flex布局子组件
   * @module $ui/components/my-flex-item
   */

  /**
   * 插槽
   * @member slots
   * @property {string} default 默认插槽,定义内容
   */
  export default {
    name: 'MyFlexItem',
    inject: {
      flex: {
        default: null
      }
    },
    /**
     * 属性参数
     * @member props
     * @property {number} [order] 属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。
     * @property {number} [grow]  属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
     * @property {number} [shrink] 属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
     * @property {string} [basis] 属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
     * @property {string} [align=auto] 允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch, 可选值:'auto', 'flex-start', 'flex-end', 'center', 'baseline', 'stretch'
     * @property {boolean} [center] 文本水平居中
     * @property {boolean} [middle] 内容垂直居中,
     * @property {boolean|string} [shadow] 显示阴影, 可选值:true, false, 'hover'
     */
    props: {
      // 属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。
      order: {
        type: Number
      },
      // 属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
      // 如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。
      grow: Number,

      // 属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
      // 如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。
      //
      // 负值对该属性无效。
      shrink: Number,
      // 属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
      // 它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。
      basis: String,
      // align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。
      align: {
        type: String,
        default: 'auto',
        validator(val) {
          return ['auto', 'flex-start', 'flex-end', 'center', 'baseline', 'stretch'].includes(val)
        }
      },
      // 文本水平居中
      center: Boolean,
      // 内容垂直居中
      middle: Boolean,
      // 显示阴影
      shadow: {
        type: [Boolean, String],
        default: false,
        validator(val) {
          return [true, false, 'hover'].includes(val)
        }
      }
    },
    data() {
      return {
        hover: false
      }
    },
    computed: {
      classes() {
        return [
          'my-flex-item',
          `is-align-${this.align}`,
          {
            'is-shadow': this.shadow === true || (this.hover && this.shadow === 'hover')
          }
        ]
      },
      styles() {
        return {
          order: this.order,
          flexGrow: this.grow,
          flexShrink: this.shrink,
          flexBasis: this.basis,
          width: this.getWidth()
        }
      },
      helperClasses() {
        return {
          'my-flex-item__helper': true,
          'is-middle': this.middle
        }
      },
      innerClasses() {
        return {
          'my-flex-item__inner': true,
          'is-center': this.center
        }
      }
    },
    methods: {
      getWidth() {
        if (!this.flex) return
        const column = this.flex.getColumn()
        if (!column) return
        if (this.flex.direction.includes('row')) {
          return `${100 / column}%`
        }
        return null

      },
      handleMouseEnter() {
        if (this.shadow === 'hover') {
          this.hover = true
        }
      },
      handleMouseLeave() {
        this.hover = false
      }
    }
  }
</script>