uni-col.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <template>
  2. <!-- #ifndef APP-NVUE -->
  3. <view :class="['uni-col', sizeClass, pointClassList]" :style="{
  4. paddingLeft:`${Number(gutter)}rpx`,
  5. paddingRight:`${Number(gutter)}rpx`,
  6. }">
  7. <slot></slot>
  8. </view>
  9. <!-- #endif -->
  10. <!-- #ifdef APP-NVUE -->
  11. <!-- 在nvue上,类名样式不生效,换为style -->
  12. <!-- 设置right正值失效,设置 left 负值 -->
  13. <view :class="['uni-col']" :style="{
  14. paddingLeft:`${Number(gutter)}rpx`,
  15. paddingRight:`${Number(gutter)}rpx`,
  16. width:`${nvueWidth}rpx`,
  17. position:'relative',
  18. marginLeft:`${marginLeft}rpx`,
  19. left:`${right === 0 ? left : -right}rpx`
  20. }">
  21. <slot></slot>
  22. </view>
  23. <!-- #endif -->
  24. </template>
  25. <script>
  26. /**
  27. * Col 布局-列
  28. * @description 搭配uni-row使用,构建布局。
  29. * @tutorial https://ext.dcloud.net.cn/plugin?id=3958
  30. *
  31. * @property {span} type = Number 栅格占据的列数
  32. * 默认 24
  33. * @property {offset} type = Number 栅格左侧的间隔格数
  34. * @property {push} type = Number 栅格向右移动格数
  35. * @property {pull} type = Number 栅格向左移动格数
  36. * @property {xs} type = [Number, Object] <768px 响应式栅格数或者栅格属性对象
  37. * @description Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
  38. * @property {sm} type = [Number, Object] ≥768px 响应式栅格数或者栅格属性对象
  39. * @description Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
  40. * @property {md} type = [Number, Object] ≥992px 响应式栅格数或者栅格属性对象
  41. * @description Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
  42. * @property {lg} type = [Number, Object] ≥1200px 响应式栅格数或者栅格属性对象
  43. * @description Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
  44. * @property {xl} type = [Number, Object] ≥1920px 响应式栅格数或者栅格属性对象
  45. * @description Number时表示在此屏幕宽度下,栅格占据的列数。Object时可配置多个描述{span: 4, offset: 4}
  46. */
  47. const ComponentClass = 'uni-col';
  48. // -1 默认值,因为在微信小程序端只给Number会有默认值0
  49. export default {
  50. name: 'uniCol',
  51. // #ifdef MP-WEIXIN
  52. options: {
  53. virtualHost: true // 在微信小程序中将组件节点渲染为虚拟节点,更加接近Vue组件的表现
  54. },
  55. // #endif
  56. props: {
  57. span: {
  58. type: Number,
  59. default: 24
  60. },
  61. offset: {
  62. type: Number,
  63. default: -1
  64. },
  65. pull: {
  66. type: Number,
  67. default: -1
  68. },
  69. push: {
  70. type: Number,
  71. default: -1
  72. },
  73. xs: [Number, Object],
  74. sm: [Number, Object],
  75. md: [Number, Object],
  76. lg: [Number, Object],
  77. xl: [Number, Object]
  78. },
  79. data() {
  80. return {
  81. gutter: 0,
  82. sizeClass: '',
  83. parentWidth: 0,
  84. nvueWidth: 0,
  85. marginLeft: 0,
  86. right: 0,
  87. left: 0
  88. }
  89. },
  90. created() {
  91. // 字节小程序中,在computed中读取$parent为undefined
  92. let parent = this.$parent;
  93. while (parent && parent.$options.componentName !== 'uniRow') {
  94. parent = parent.$parent;
  95. }
  96. this.updateGutter(parent.gutter)
  97. parent.$watch('gutter', (gutter) => {
  98. this.updateGutter(gutter)
  99. })
  100. // #ifdef APP-NVUE
  101. this.updateNvueWidth(parent.width)
  102. parent.$watch('width', (width) => {
  103. this.updateNvueWidth(width)
  104. })
  105. // #endif
  106. },
  107. computed: {
  108. sizeList() {
  109. let {
  110. span,
  111. offset,
  112. pull,
  113. push
  114. } = this;
  115. return {
  116. span,
  117. offset,
  118. pull,
  119. push
  120. }
  121. },
  122. // #ifndef APP-NVUE
  123. pointClassList() {
  124. let classList = [];
  125. ['xs', 'sm', 'md', 'lg', 'xl'].forEach(point => {
  126. const props = this[point];
  127. if (typeof props === 'number') {
  128. classList.push(`${ComponentClass}-${point}-${props}`)
  129. } else if (typeof props === 'object' && props) {
  130. Object.keys(props).forEach(pointProp => {
  131. classList.push(
  132. pointProp === 'span' ?
  133. `${ComponentClass}-${point}-${props[pointProp]}` :
  134. `${ComponentClass}-${point}-${pointProp}-${props[pointProp]}`
  135. )
  136. })
  137. }
  138. });
  139. // 支付宝小程序使用 :class=[ ['a','b'] ],渲染错误
  140. return classList.join(' ');
  141. }
  142. // #endif
  143. },
  144. methods: {
  145. updateGutter(parentGutter) {
  146. parentGutter = Number(parentGutter);
  147. if (!isNaN(parentGutter)) {
  148. this.gutter = parentGutter / 2
  149. }
  150. },
  151. // #ifdef APP-NVUE
  152. updateNvueWidth(width) {
  153. // 用于在nvue端,span,offset,pull,push的计算
  154. this.parentWidth = width;
  155. ['span', 'offset', 'pull', 'push'].forEach(size => {
  156. const curSize = this[size];
  157. if ((curSize || curSize === 0) && curSize !== -1) {
  158. let RPX = 1 / 24 * curSize * width
  159. RPX = Number(RPX);
  160. switch (size) {
  161. case 'span':
  162. this.nvueWidth = RPX
  163. break;
  164. case 'offset':
  165. this.marginLeft = RPX
  166. break;
  167. case 'pull':
  168. this.right = RPX
  169. break;
  170. case 'push':
  171. this.left = RPX
  172. break;
  173. }
  174. }
  175. });
  176. }
  177. // #endif
  178. },
  179. watch: {
  180. sizeList: {
  181. immediate: true,
  182. handler(newVal) {
  183. // #ifndef APP-NVUE
  184. let classList = [];
  185. for (let size in newVal) {
  186. const curSize = newVal[size];
  187. if ((curSize || curSize === 0) && curSize !== -1) {
  188. classList.push(
  189. size === 'span' ?
  190. `${ComponentClass}-${curSize}` :
  191. `${ComponentClass}-${size}-${curSize}`
  192. )
  193. }
  194. }
  195. // 支付宝小程序使用 :class=[ ['a','b'] ],渲染错误
  196. this.sizeClass = classList.join(' ');
  197. // #endif
  198. // #ifdef APP-NVUE
  199. this.updateNvueWidth(this.parentWidth);
  200. // #endif
  201. }
  202. }
  203. }
  204. }
  205. </script>
  206. <style lang='scss' scoped>
  207. /* breakpoints */
  208. $--sm: 768px !default;
  209. $--md: 992px !default;
  210. $--lg: 1200px !default;
  211. $--xl: 1920px !default;
  212. $breakpoints: ('xs' : (max-width: $--sm - 1),
  213. 'sm' : (min-width: $--sm),
  214. 'md' : (min-width: $--md),
  215. 'lg' : (min-width: $--lg),
  216. 'xl' : (min-width: $--xl));
  217. $layout-namespace: ".uni-";
  218. $col: $layout-namespace+"col";
  219. @function getSize($size) {
  220. /* TODO 1/24 * $size * 100 * 1%; 使用计算后的值,为了解决 vue3 控制台报错 */
  221. @return 0.04166666666 * $size * 100 * 1%;
  222. }
  223. @mixin res($key, $map:$breakpoints) {
  224. @if map-has-key($map, $key) {
  225. @media screen and #{inspect(map-get($map,$key))} {
  226. @content;
  227. }
  228. }
  229. @else {
  230. @warn "Undeinfed point: `#{$key}`";
  231. }
  232. }
  233. /* #ifndef APP-NVUE */
  234. #{$col} {
  235. float: left;
  236. box-sizing: border-box;
  237. }
  238. #{$col}-0 {
  239. /* #ifdef APP-NVUE */
  240. width: 0;
  241. height: 0;
  242. margin-top: 0;
  243. margin-right: 0;
  244. margin-bottom: 0;
  245. margin-left: 0;
  246. /* #endif */
  247. /* #ifndef APP-NVUE */
  248. display: none;
  249. /* #endif */
  250. }
  251. @for $i from 0 through 24 {
  252. #{$col}-#{$i} {
  253. width: getSize($i);
  254. }
  255. #{$col}-offset-#{$i} {
  256. margin-left: getSize($i);
  257. }
  258. #{$col}-pull-#{$i} {
  259. position: relative;
  260. right: getSize($i);
  261. }
  262. #{$col}-push-#{$i} {
  263. position: relative;
  264. left: getSize($i);
  265. }
  266. }
  267. @each $point in map-keys($breakpoints) {
  268. @include res($point) {
  269. #{$col}-#{$point}-0 {
  270. display: none;
  271. }
  272. @for $i from 0 through 24 {
  273. #{$col}-#{$point}-#{$i} {
  274. width: getSize($i);
  275. }
  276. #{$col}-#{$point}-offset-#{$i} {
  277. margin-left: getSize($i);
  278. }
  279. #{$col}-#{$point}-pull-#{$i} {
  280. position: relative;
  281. right: getSize($i);
  282. }
  283. #{$col}-#{$point}-push-#{$i} {
  284. position: relative;
  285. left: getSize($i);
  286. }
  287. }
  288. }
  289. }
  290. /* #endif */
  291. </style>