#
whycq
2023-02-13 1bea37073f305929215bf2c3f28e2263acb6fc20
uni_modules/uni-row/components/uni-col/uni-col.vue
New file
@@ -0,0 +1,317 @@
<template>
   <!-- #ifndef APP-NVUE -->
   <view :class="['uni-col', sizeClass, pointClassList]" :style="{
      paddingLeft:`${Number(gutter)}rpx`,
      paddingRight:`${Number(gutter)}rpx`,
   }">
      <slot></slot>
   </view>
   <!-- #endif -->
   <!-- #ifdef APP-NVUE -->
   <!-- 在nvue上,类名样式不生效,换为style -->
   <!-- 设置right正值失效,设置 left 负值 -->
   <view :class="['uni-col']" :style="{
      paddingLeft:`${Number(gutter)}rpx`,
      paddingRight:`${Number(gutter)}rpx`,
      width:`${nvueWidth}rpx`,
      position:'relative',
      marginLeft:`${marginLeft}rpx`,
      left:`${right === 0 ? left : -right}rpx`
   }">
      <slot></slot>
   </view>
   <!-- #endif -->
</template>
<script>
   /**
    * Col   布局-列
    * @description   搭配uni-row使用,构建布局。
    * @tutorial   https://ext.dcloud.net.cn/plugin?id=3958
    *
    * @property   {span} type = Number 栅格占据的列数
    *                   默认 24
    * @property   {offset} type = Number 栅格左侧的间隔格数
    * @property   {push} type = Number 栅格向右移动格数
    * @property   {pull} type = Number 栅格向左移动格数
    * @property   {xs} type = [Number, Object] <768px 响应式栅格数或者栅格属性对象
    *                   @description   Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
    * @property   {sm} type = [Number, Object] ≥768px 响应式栅格数或者栅格属性对象
    *                   @description   Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
    * @property   {md} type = [Number, Object] ≥992px 响应式栅格数或者栅格属性对象
    *                   @description   Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
    * @property   {lg} type = [Number, Object] ≥1200px 响应式栅格数或者栅格属性对象
    *                   @description   Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
    * @property   {xl} type = [Number, Object] ≥1920px 响应式栅格数或者栅格属性对象
    *                   @description   Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
    */
   const ComponentClass = 'uni-col';
   // -1 默认值,因为在微信小程序端只给Number会有默认值0
   export default {
      name: 'uniCol',
      // #ifdef MP-WEIXIN
      options: {
         virtualHost: true // 在微信小程序中将组件节点渲染为虚拟节点,更加接近Vue组件的表现
      },
      // #endif
      props: {
         span: {
            type: Number,
            default: 24
         },
         offset: {
            type: Number,
            default: -1
         },
         pull: {
            type: Number,
            default: -1
         },
         push: {
            type: Number,
            default: -1
         },
         xs: [Number, Object],
         sm: [Number, Object],
         md: [Number, Object],
         lg: [Number, Object],
         xl: [Number, Object]
      },
      data() {
         return {
            gutter: 0,
            sizeClass: '',
            parentWidth: 0,
            nvueWidth: 0,
            marginLeft: 0,
            right: 0,
            left: 0
         }
      },
      created() {
         // 字节小程序中,在computed中读取$parent为undefined
         let parent = this.$parent;
         while (parent && parent.$options.componentName !== 'uniRow') {
            parent = parent.$parent;
         }
         this.updateGutter(parent.gutter)
         parent.$watch('gutter', (gutter) => {
            this.updateGutter(gutter)
         })
         // #ifdef APP-NVUE
         this.updateNvueWidth(parent.width)
         parent.$watch('width', (width) => {
            this.updateNvueWidth(width)
         })
         // #endif
      },
      computed: {
         sizeList() {
            let {
               span,
               offset,
               pull,
               push
            } = this;
            return {
               span,
               offset,
               pull,
               push
            }
         },
         // #ifndef APP-NVUE
         pointClassList() {
            let classList = [];
            ['xs', 'sm', 'md', 'lg', 'xl'].forEach(point => {
               const props = this[point];
               if (typeof props === 'number') {
                  classList.push(`${ComponentClass}-${point}-${props}`)
               } else if (typeof props === 'object' && props) {
                  Object.keys(props).forEach(pointProp => {
                     classList.push(
                        pointProp === 'span' ?
                        `${ComponentClass}-${point}-${props[pointProp]}` :
                        `${ComponentClass}-${point}-${pointProp}-${props[pointProp]}`
                     )
                  })
               }
            });
            // 支付宝小程序使用 :class=[ ['a','b'] ],渲染错误
            return classList.join(' ');
         }
         // #endif
      },
      methods: {
         updateGutter(parentGutter) {
            parentGutter = Number(parentGutter);
            if (!isNaN(parentGutter)) {
               this.gutter = parentGutter / 2
            }
         },
         // #ifdef APP-NVUE
         updateNvueWidth(width) {
            // 用于在nvue端,span,offset,pull,push的计算
            this.parentWidth = width;
            ['span', 'offset', 'pull', 'push'].forEach(size => {
               const curSize = this[size];
               if ((curSize || curSize === 0) && curSize !== -1) {
                  let RPX = 1 / 24 * curSize * width
                  RPX = Number(RPX);
                  switch (size) {
                     case 'span':
                        this.nvueWidth = RPX
                        break;
                     case 'offset':
                        this.marginLeft = RPX
                        break;
                     case 'pull':
                        this.right = RPX
                        break;
                     case 'push':
                        this.left = RPX
                        break;
                  }
               }
            });
         }
         // #endif
      },
      watch: {
         sizeList: {
            immediate: true,
            handler(newVal) {
               // #ifndef APP-NVUE
               let classList = [];
               for (let size in newVal) {
                  const curSize = newVal[size];
                  if ((curSize || curSize === 0) && curSize !== -1) {
                     classList.push(
                        size === 'span' ?
                        `${ComponentClass}-${curSize}` :
                        `${ComponentClass}-${size}-${curSize}`
                     )
                  }
               }
               // 支付宝小程序使用 :class=[ ['a','b'] ],渲染错误
               this.sizeClass = classList.join(' ');
               // #endif
               // #ifdef APP-NVUE
               this.updateNvueWidth(this.parentWidth);
               // #endif
            }
         }
      }
   }
</script>
<style lang='scss' scoped>
   /* breakpoints */
   $--sm: 768px !default;
   $--md: 992px !default;
   $--lg: 1200px !default;
   $--xl: 1920px !default;
   $breakpoints: ('xs' : (max-width: $--sm - 1),
   'sm' : (min-width: $--sm),
   'md' : (min-width: $--md),
   'lg' : (min-width: $--lg),
   'xl' : (min-width: $--xl));
   $layout-namespace: ".uni-";
   $col: $layout-namespace+"col";
   @function getSize($size) {
      /* TODO 1/24 * $size * 100 * 1%; 使用计算后的值,为了解决 vue3 控制台报错 */
      @return 0.04166666666 * $size * 100 * 1%;
   }
   @mixin res($key, $map:$breakpoints) {
      @if map-has-key($map, $key) {
         @media screen and #{inspect(map-get($map,$key))} {
            @content;
         }
      }
      @else {
         @warn "Undeinfed point: `#{$key}`";
      }
   }
   /* #ifndef APP-NVUE */
   #{$col} {
      float: left;
      box-sizing: border-box;
   }
   #{$col}-0 {
      /* #ifdef APP-NVUE */
      width: 0;
      height: 0;
      margin-top: 0;
      margin-right: 0;
      margin-bottom: 0;
      margin-left: 0;
      /* #endif */
      /* #ifndef APP-NVUE */
      display: none;
      /* #endif */
   }
   @for $i from 0 through 24 {
      #{$col}-#{$i} {
         width: getSize($i);
      }
      #{$col}-offset-#{$i} {
         margin-left: getSize($i);
      }
      #{$col}-pull-#{$i} {
         position: relative;
         right: getSize($i);
      }
      #{$col}-push-#{$i} {
         position: relative;
         left: getSize($i);
      }
   }
   @each $point in map-keys($breakpoints) {
      @include res($point) {
         #{$col}-#{$point}-0 {
            display: none;
         }
         @for $i from 0 through 24 {
            #{$col}-#{$point}-#{$i} {
               width: getSize($i);
            }
            #{$col}-#{$point}-offset-#{$i} {
               margin-left: getSize($i);
            }
            #{$col}-#{$point}-pull-#{$i} {
               position: relative;
               right: getSize($i);
            }
            #{$col}-#{$point}-push-#{$i} {
               position: relative;
               left: getSize($i);
            }
         }
      }
   }
   /* #endif */
</style>