<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' > /* 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>