Commit 4a3a32b8 authored by beiqiao's avatar beiqiao

语法提示,文档说明,进一步更新。

parent da09bd81
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
* @property {String Number} speed 水平滚动时的滚动速度,即每秒移动多少距离,只对水平衔接方式有效,单位rpx(默认160) * @property {String Number} speed 水平滚动时的滚动速度,即每秒移动多少距离,只对水平衔接方式有效,单位rpx(默认160)
* @property {String Number} font-size 字体大小,单位rpx(默认28) * @property {String Number} font-size 字体大小,单位rpx(默认28)
* @property {Boolean} is-circular mode为horizontal时,指明是否水平衔接滚动(默认true) * @property {Boolean} is-circular mode为horizontal时,指明是否水平衔接滚动(默认true)
* @property {String} play-state 播放状态,paly - 播放,paused - 暂停(默认paly) * @property {String} play-state 播放状态,play - 播放,paused - 暂停(默认play)
* @property {Boolean} disable-touch 是否禁止通过手动滑动切换通知,只有mode = vertical,或者mode = horizontal且is-circular = false时有效(默认true) * @property {Boolean} disable-touch 是否禁止通过手动滑动切换通知,只有mode = vertical,或者mode = horizontal且is-circular = false时有效(默认true)
* @event {Function} click 点击通告文字触发,只有mode = vertical,或者mode = horizontal且is-circular = false时有效 * @event {Function} click 点击通告文字触发,只有mode = vertical,或者mode = horizontal且is-circular = false时有效
* @event {Function} close 点击右侧关闭图标触发 * @event {Function} close 点击右侧关闭图标触发
...@@ -116,7 +116,7 @@ ...@@ -116,7 +116,7 @@
type: Boolean, type: Boolean,
default: true default: true
}, },
// 播放状态,paly-播放,paused-暂停 // 播放状态,play-播放,paused-暂停
playState: { playState: {
type: String, type: String,
default: 'play' default: 'play'
......
...@@ -40,16 +40,16 @@ ...@@ -40,16 +40,16 @@
</template> </template>
<script> <script>
/** /**
* alertTips 提示 * alertTips 提示
* @description 骨架屏一般用于页面在请求远程数据尚未完成时,页面用灰色块预显示本来的页面结构,给用户更好的体验。 * @description 骨架屏一般用于页面在请求远程数据尚未完成时,页面用灰色块预显示本来的页面结构,给用户更好的体验。
* @tutorial https://www.uviewui.com/components/skeleton.html * @tutorial https://www.uviewui.com/components/skeleton.html
* @property {String} el-color 骨架块状元素的背景颜色(默认#e5e5e5) * @property {String} el-color 骨架块状元素的背景颜色(默认#e5e5e5)
* @property {String} bg-color 骨架组件背景颜色(默认#ffffff) * @property {String} bg-color 骨架组件背景颜色(默认#ffffff)
* @property {Boolean} animation 骨架块是否显示动画效果(默认false) * @property {Boolean} animation 骨架块是否显示动画效果(默认false)
* @property {String Number} border-radius u-skeleton-fillet类名元素,对应的骨架块的圆角大小,单位rpx(默认10) * @property {String Number} border-radius u-skeleton-fillet类名元素,对应的骨架块的圆角大小,单位rpx(默认10)
* @property {Boolean} loading 是否显示骨架组件,请求完成后,将此值设置为false(默认true) * @property {Boolean} loading 是否显示骨架组件,请求完成后,将此值设置为false(默认true)
* @example <u-skeleton :loading="true" :animation="true"></u-skeleton> * @example <u-skeleton :loading="true" :animation="true"></u-skeleton>
*/ */
export default { export default {
name: "u-skeleton", name: "u-skeleton",
......
<template> <template>
<view class=""> <view class="">
<view class="u-steps"> <view class="u-steps">
<view class="u-steps-item" v-for="(item,index) in list" :key="index"> <view class="u-steps-item" v-for="(item,index) in list" :key="index">
<view class="u-steps-item-num" v-if="mode == 'number' && current < index">{{index+1}}</view> <view class="u-steps-item-num" v-if="mode == 'number' && current < index">{{index+1}}</view>
<view class="u-steps-item-dot" v-if="mode == 'dot'" :style="{backgroundColor: index <= current ? innerActiveColor : unActiveColor}"></view> <view class="u-steps-item-dot" v-if="mode == 'dot'" :style="{backgroundColor: index <= current ? innerActiveColor : unActiveColor}"></view>
<u-icon size="22" class="u-steps-item-checked" :style="{backgroundColor: index <= current ? innerActiveColor : unActiveColor}" <u-icon size="22" class="u-steps-item-checked" :style="{backgroundColor: index <= current ? innerActiveColor : unActiveColor}"
v-if="mode == 'number' && current >= index" name="checkmark"></u-icon> v-if="mode == 'number' && current >= index" name="checkmark"></u-icon>
<text :style="{color: index <= current ? innerActiveColor : unActiveColor}">{{item.name}}</text> <text :style="{color: index <= current ? innerActiveColor : unActiveColor}">{{item.name}}</text>
<view class="u-steps-item-line" :style="{backgroundColor: index <= current ? innerActiveColor : unActiveColor, top: mode == 'dot' ? '24rpx' : '36rpx'}"> <view class="u-steps-item-line" :style="{backgroundColor: index <= current ? innerActiveColor : unActiveColor, top: mode == 'dot' ? '24rpx' : '36rpx'}">
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
export default { /**
props: { * alertTips 提示
// 步骤条的类型,dot|number * @description 该组件一般用于完成一个任务要分几个步骤,标识目前处于第几步的场景。
mode: { * @tutorial https://www.uviewui.com/components/steps.html
type: String, * @property {String} mode 设置模式(默认dot)
default: 'dot' * @property {Array} list 数轴条数据,数组。具体见上方示例
}, * @property {String} type type主题(默认primary)
// 步骤条的数据 * @property {Number String} current 设置当前处于第几步
list: { * @property {String} active-color 已完成步骤的激活颜色,如设置,type值会失效
type: Array, * @property {String} un-active-color 未激活的颜色,用于表示未完成步骤的颜色(默认#606266)
default () { * @example <u-steps :list="numList" active-color="#fa3534"></u-steps>
return [] */
} export default {
}, name: "u-steps",
// 主题类型, primary|success|info|warning|error props: {
type: { // 步骤条的类型,dot|number
type: String, mode: {
default: 'primary' type: String,
}, default: 'dot'
// 当前哪一步是激活的 },
current: { // 步骤条的数据
type: [Number, String], list: {
default: 0 type: Array,
}, default () {
// 激活步骤的颜色 return []
activeColor: { }
type: String, },
default: '' // 主题类型, primary|success|info|warning|error
}, type: {
// 未激活的颜色 type: String,
unActiveColor: { default: 'primary'
type: String, },
default: '#606266' // 当前哪一步是激活的
} current: {
}, type: [Number, String],
data() { default: 0
return { },
// 激活步骤的颜色
} activeColor: {
}, type: String,
computed: { default: ''
innerActiveColor() { },
if (this.activeColor) return this.activeColor; // 未激活的颜色
else if (this.type) return this.$u.color[this.type]; unActiveColor: {
else return "#2979ff"; type: String,
}, default: '#606266'
} }
} },
</script> data() {
return {
<style lang="scss" scoped>
.u-steps { }
display: flex; },
} computed: {
innerActiveColor() {
.u-steps-item { if (this.activeColor) return this.activeColor;
flex: 1; else if (this.type) return this.$u.color[this.type];
text-align: center; else return "#2979ff";
position: relative; },
min-width: 100rpx; }
font-size: 26rpx; }
color: #8799a3; </script>
}
<style lang="scss" scoped>
.u-steps-item .u-steps-item-line { .u-steps {
content: ""; display: flex;
position: absolute; }
height: 2rpx;
width: calc(100% - 80rpx); .u-steps-item {
left: calc(0rpx - (100% - 80rpx) / 2); flex: 1;
top: 36rpx; text-align: center;
z-index: 0; position: relative;
} min-width: 100rpx;
font-size: 26rpx;
.u-steps-item:first-child .u-steps-item-line { color: #8799a3;
display: none; }
}
.u-steps-item .u-steps-item-line {
.u-steps-item-num { content: "";
display: flex; position: absolute;
align-items: center; height: 2rpx;
justify-content: center; width: calc(100% - 80rpx);
width: 44rpx; left: calc(0rpx - (100% - 80rpx) / 2);
height: 44rpx; top: 36rpx;
border: 1px solid #8799a3; z-index: 0;
border-radius: 50%; }
margin: 14rpx auto;
overflow: hidden; .u-steps-item:first-child .u-steps-item-line {
} display: none;
}
.u-steps-item-dot {
width: 20rpx; .u-steps-item-num {
height: 20rpx; display: flex;
display: flex; align-items: center;
border-radius: 50%; justify-content: center;
margin: 14rpx auto; width: 44rpx;
} height: 44rpx;
border: 1px solid #8799a3;
.u-steps-item-checked { border-radius: 50%;
display: flex; margin: 14rpx auto;
align-items: center; overflow: hidden;
justify-content: center; }
width: 44rpx;
color: #fff !important; .u-steps-item-dot {
height: 44rpx; width: 20rpx;
border-radius: 50%; height: 20rpx;
margin: 14rpx auto; display: flex;
overflow: hidden; border-radius: 50%;
} margin: 14rpx auto;
}
.u-steps-item-checked {
display: flex;
align-items: center;
justify-content: center;
width: 44rpx;
color: #fff !important;
height: 44rpx;
border-radius: 50%;
margin: 14rpx auto;
overflow: hidden;
}
</style> </style>
<template> <template>
<view class=""> <view class="">
<view class="u-sticky-wrap" :class="[elClass]" :style="{ <view class="u-sticky-wrap" :class="[elClass]" :style="{
height: fixed ? height + 'px' : 'auto', height: fixed ? height + 'px' : 'auto',
backgroundColor: bgColor backgroundColor: bgColor
}"> }">
<view class="u-sticky" :style="{ <view class="u-sticky" :style="{
position: fixed ? 'fixed' : 'static', position: fixed ? 'fixed' : 'static',
top: stickyTop + 'px', top: stickyTop + 'px',
left: left + 'px', left: left + 'px',
width: width == 'auto' ? 'auto' : width + 'px', width: width == 'auto' ? 'auto' : width + 'px',
zIndex: uZIndex zIndex: uZIndex
}"> }">
<slot></slot> <slot></slot>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
export default { /**
props: { * alertTips 提示
// 吸顶容器到顶部某个距离的时候,进行吸顶,在H5平台,NavigationBar为44px * @description 该组件与CSS中position: sticky属性实现的效果一致,当组件达到预设的到顶部距离时, 就会固定在指定位置,组件位置大于预设的顶部距离时,会重新按照正常的布局排列。
offsetTop: { * @tutorial https://www.uviewui.com/components/sticky.html
type: [Number, String], * @property {String Number} offset-top 吸顶时与顶部的距离,单位rpx(默认0)
default: 0 * @property {String Number} index 自定义标识,用于区分是哪一个组件
}, * @property {Boolean} enable 是否开启吸顶功能(默认true)
//列表中的索引值 * @property {String} bg-color 组件背景颜色(默认#ffffff)
index: { * @property {String Number} z-index 吸顶时的z-index值(默认970)
type: [Number, String], * @property {String Number} h5-nav-height 导航栏高度,自定义导航栏时(无导航栏时需设置为0),需要传入此值,单位px(默认44)
default: '' * @event {Function} fixed 组件吸顶时触发
}, * @example <u-sticky offset-top="200"><view>塞下秋来风景异,衡阳雁去无留意</view></u-sticky>
// 是否开启吸顶功能 */
enable: { export default {
type: Boolean, name: "u-sticky",
default: true props: {
}, // 吸顶容器到顶部某个距离的时候,进行吸顶,在H5平台,NavigationBar为44px
// h5顶部导航栏的高度 offsetTop: {
h5NavHeight: { type: [Number, String],
type: [Number, String], default: 0
default: 44 },
}, //列表中的索引值
// 吸顶区域的背景颜色 index: {
bgColor: { type: [Number, String],
type: String, default: ''
default: '#ffffff' },
}, // 是否开启吸顶功能
// z-index值 enable: {
zIndex: { type: Boolean,
type: [Number, String], default: true
default: '' },
} // h5顶部导航栏的高度
}, h5NavHeight: {
data() { type: [Number, String],
return { default: 44
fixed: false, },
height: 'auto', // 吸顶区域的背景颜色
stickyTop: 0, bgColor: {
elClass: this.$u.guid(), type: String,
left: 0, default: '#ffffff'
width: 'auto', },
}; // z-index值
}, zIndex: {
watch: { type: [Number, String],
offsetTop(val) { default: ''
this.initObserver(); }
}, },
enable(val) { data() {
if(val == false) { return {
this.fixed = false; fixed: false,
this.disconnectObserver('contentObserver'); height: 'auto',
} else { stickyTop: 0,
this.initObserver(); elClass: this.$u.guid(),
} left: 0,
} width: 'auto',
}, };
computed: { },
uZIndex() { watch: {
return this.zIndex ? this.zIndex : this.$u.zIndex.sticky; offsetTop(val) {
} this.initObserver();
}, },
mounted() { enable(val) {
this.initObserver(); if (val == false) {
}, this.fixed = false;
methods: { this.disconnectObserver('contentObserver');
initObserver() { } else {
if(!this.enable) return ; this.initObserver();
// #ifdef H5 }
this.stickyTop = this.offsetTop != 0 ? uni.upx2px(this.offsetTop) + this.h5NavHeight : this.h5NavHeight; }
// #endif },
// #ifndef H5 computed: {
this.stickyTop = this.offsetTop != 0 ? uni.upx2px(this.offsetTop) : 0; uZIndex() {
// #endif return this.zIndex ? this.zIndex : this.$u.zIndex.sticky;
}
this.disconnectObserver('contentObserver'); },
this.$uGetRect('.' + this.elClass).then((res) => { mounted() {
this.height = res.height; this.initObserver();
this.left = res.left; },
this.width = res.width; methods: {
this.$nextTick(() => { initObserver() {
this.observeContent(); if (!this.enable) return;
}); // #ifdef H5
}); this.stickyTop = this.offsetTop != 0 ? uni.upx2px(this.offsetTop) + this.h5NavHeight : this.h5NavHeight;
}, // #endif
observeContent() { // #ifndef H5
this.disconnectObserver('contentObserver'); this.stickyTop = this.offsetTop != 0 ? uni.upx2px(this.offsetTop) : 0;
const contentObserver = this.createIntersectionObserver({ // #endif
thresholds: [0.95, 0.98, 1]
}); this.disconnectObserver('contentObserver');
contentObserver.relativeToViewport({ this.$uGetRect('.' + this.elClass).then((res) => {
top: -this.stickyTop this.height = res.height;
}); this.left = res.left;
contentObserver.observe('.' + this.elClass, res => { this.width = res.width;
if (!this.enable) return ; this.$nextTick(() => {
this.setFixed(res.boundingClientRect.top); this.observeContent();
}); });
this.contentObserver = contentObserver; });
}, },
setFixed(top) { observeContent() {
const fixed = top < this.stickyTop; this.disconnectObserver('contentObserver');
this.fixed = fixed; const contentObserver = this.createIntersectionObserver({
if(fixed) this.$emit('fixed', this.index); thresholds: [0.95, 0.98, 1]
}, });
disconnectObserver(observerName) { contentObserver.relativeToViewport({
const observer = this[observerName]; top: -this.stickyTop
observer && observer.disconnect(); });
}, contentObserver.observe('.' + this.elClass, res => {
} if (!this.enable) return;
}; this.setFixed(res.boundingClientRect.top);
</script> });
this.contentObserver = contentObserver;
<style scoped lang="scss"> },
.u-sticky { setFixed(top) {
z-index: 9999999999; const fixed = top < this.stickyTop;
} this.fixed = fixed;
</style> if (fixed) this.$emit('fixed', this.index);
\ No newline at end of file },
disconnectObserver(observerName) {
const observer = this[observerName];
observer && observer.disconnect();
},
}
};
</script>
<style scoped lang="scss">
.u-sticky {
z-index: 9999999999;
}
</style>
<template> <template>
<view class="u-subsection" :style="[subsectionStyle]"> <view class="u-subsection" :style="[subsectionStyle]">
<view <view class="u-item u-line-1" :style="[itemStyle(index)]" @tap="click(index)" :class="[noBorderRight(index), 'u-item-' + index]"
class="u-item u-line-1" v-for="(item, index) in listInfo" :key="index">
:style="[itemStyle(index)]" <view :style="[textStyle(index)]" class="u-item-text u-line-1">{{ item.name }}</view>
@tap="click(index)" </view>
:class="[noBorderRight(index), 'u-item-' + index]" <view class="u-item-bg" :style="[itemBarStyle]"></view>
v-for="(item, index) in listInfo" </view>
:key="index" </template>
>
<view :style="[textStyle(index)]" class="u-item-text u-line-1">{{ item.name }}</view> <script>
</view> /**
<view class="u-item-bg" :style="[itemBarStyle]"></view> * alertTips 提示
</view> * @description 该分段器一般用于用户从几个选项中选择某一个的场景
</template> * @tutorial https://www.uviewui.com/components/subsection.html
* @property {Array} list 选项的数组,形式见上方"基本使用"
<script> * @property {String Number} current 初始化时默认选中的选项索引值(默认0)
export default { * @property {String} active-color 激活时的颜色,mode为subsection时固定为白色(默认#ff9900)
props: { * @property {String} inactive-color 未激活时字体的颜色,mode为subsection时无效(默认#303133)
// tab的数据 * @property {String} mode 模式选择,见官网"模式选择"说明(默认button)
list: { * @property {String Number} font-size 字体大小,单位rpx(默认28)
type: Array, * @property {Boolean} animation 是否开启动画效果,见上方说明(默认true)
default() { * @property {Boolean} bold 激活选项的字体是否加粗(默认true)
return []; * @property {String} bg-color 组件背景颜色,mode为button时有效(默认#eeeeef)
} * @property {String} button-color 按钮背景颜色,mode为button时有效(默认#ffffff)
}, * @event {Function} change 分段器选项发生改变时触发
// 当前活动的tab的index * @example <u-subsection active-color="#ff9900"></u-subsection>
current: { */
type: [Number, String], export default {
default: 0 name: "u-subsection",
}, props: {
// 激活的颜色 // tab的数据
activeColor: { list: {
type: String, type: Array,
default: '#303133' default () {
}, return [];
// 模式选择,mode=button为按钮形式,mode=subsection时为分段模式 }
mode: { },
type: String, // 当前活动的tab的index
default: 'button' current: {
}, type: [Number, String],
// 字体大小,单位rpx default: 0
fontSize: { },
type: [Number, String], // 激活的颜色
default: 28 activeColor: {
}, type: String,
// 是否开启动画效果 default: '#303133'
animation: { },
type: Boolean, // 模式选择,mode=button为按钮形式,mode=subsection时为分段模式
default: true mode: {
}, type: String,
// 组件的高度,单位rpx default: 'button'
height: { },
type: [Number, String], // 字体大小,单位rpx
default: 70 fontSize: {
}, type: [Number, String],
// 激活tab的字体是否加粗 default: 28
bold: { },
type: Boolean, // 是否开启动画效果
default: true animation: {
}, type: Boolean,
// mode=button时,组件背景颜色 default: true
bgColor: { },
type: String, // 组件的高度,单位rpx
default: '#eeeeef' height: {
}, type: [Number, String],
// mode = button时,滑块背景颜色 default: 70
buttonColor: { },
type: String, // 激活tab的字体是否加粗
default: '#ffffff' bold: {
}, type: Boolean,
// 在切换分段器的时候,是否让设备震一下 default: true
vibrateShort: { },
type: Boolean, // mode=button时,组件背景颜色
default: false bgColor: {
} type: String,
}, default: '#eeeeef'
data() { },
return { // mode = button时,滑块背景颜色
listInfo: [], buttonColor: {
itemBgStyle: { type: String,
width: 0, default: '#ffffff'
left: 0, },
backgroundColor: '#ffffff', // 在切换分段器的时候,是否让设备震一下
height: '100%', vibrateShort: {
transition: '' type: Boolean,
}, default: false
currentIndex: this.current, }
buttonPadding: 3, // mode = button 时,组件的内边距 },
borderRadius: 5, // 圆角值 data() {
firstTimeVibrateShort: true // 组件初始化时,会触发current变化,此时不应震动 return {
}; listInfo: [],
}, itemBgStyle: {
watch: { width: 0,
current: { left: 0,
immediate: true, backgroundColor: '#ffffff',
handler(nVal) { height: '100%',
this.currentIndex = nVal; transition: ''
this.changeSectionStatus(nVal); },
} currentIndex: this.current,
} buttonPadding: 3, // mode = button 时,组件的内边距
}, borderRadius: 5, // 圆角值
created() { firstTimeVibrateShort: true // 组件初始化时,会触发current变化,此时不应震动
// 将list的数据,传入listInfo数组,因为不能修改props传递的list值 };
// 可以接受直接数组形式,或者数组元素为对象的形式,如:['简介', '评论'],或者[{name: '简介'}, {name: '评论'}] },
this.listInfo = this.list.map((val, index) => { watch: {
if (typeof val != 'object') { current: {
let obj = { immediate: true,
width: 0, handler(nVal) {
name: val this.currentIndex = nVal;
}; this.changeSectionStatus(nVal);
return obj; }
} else { }
val.width = 0; },
return val; created() {
} // 将list的数据,传入listInfo数组,因为不能修改props传递的list值
}); // 可以接受直接数组形式,或者数组元素为对象的形式,如:['简介', '评论'],或者[{name: '简介'}, {name: '评论'}]
}, this.listInfo = this.list.map((val, index) => {
computed: { if (typeof val != 'object') {
// 设置mode=subsection时,滑块特有的样式 let obj = {
noBorderRight() { width: 0,
return index => { name: val
if (this.mode != 'subsection') return; };
let classs = ''; return obj;
// 不显示右边的边框 } else {
if (index < this.list.length - 1) classs += ' u-none-border-right'; val.width = 0;
// 显示整个组件的左右边圆角 return val;
if (index == 0) classs += ' u-item-first'; }
if (index == this.list.length - 1) classs += ' u-item-last'; });
return classs; },
}; computed: {
}, // 设置mode=subsection时,滑块特有的样式
// 文字的样式 noBorderRight() {
textStyle() { return index => {
return index => { if (this.mode != 'subsection') return;
let style = {}; let classs = '';
// 设置字体颜色 // 不显示右边的边框
if (this.mode == 'subsection') { if (index < this.list.length - 1) classs += ' u-none-border-right';
if (index == this.currentIndex) { // 显示整个组件的左右边圆角
style.color = '#ffffff'; if (index == 0) classs += ' u-item-first';
} else { if (index == this.list.length - 1) classs += ' u-item-last';
style.color = this.activeColor; return classs;
} };
} else { },
if (index == this.currentIndex) { // 文字的样式
style.color = this.activeColor; textStyle() {
} else { return index => {
style.color = this.inactiveColor; let style = {};
} // 设置字体颜色
} if (this.mode == 'subsection') {
// 字体加粗 if (index == this.currentIndex) {
if (index == this.currentIndex && this.bold) style.fontWeight = 'bold'; style.color = '#ffffff';
// 文字大小 } else {
style.fontSize = this.fontSize + 'rpx'; style.color = this.activeColor;
return style; }
}; } else {
}, if (index == this.currentIndex) {
// 每个分段器item的样式 style.color = this.activeColor;
itemStyle() { } else {
return index => { style.color = this.inactiveColor;
let style = {}; }
if (this.mode == 'subsection') { }
// 设置border的样式 // 字体加粗
style.borderColor = this.activeColor; if (index == this.currentIndex && this.bold) style.fontWeight = 'bold';
style.borderWidth = '1px'; // 文字大小
style.borderStyle = 'solid'; style.fontSize = this.fontSize + 'rpx';
} return style;
return style; };
}; },
}, // 每个分段器item的样式
// mode=button时,外层view的样式 itemStyle() {
subsectionStyle() { return index => {
let style = {}; let style = {};
style.height = uni.upx2px(this.height) + 'px'; if (this.mode == 'subsection') {
if (this.mode == 'button') { // 设置border的样式
style.backgroundColor = this.bgColor; style.borderColor = this.activeColor;
style.padding = `${this.buttonPadding}px`; style.borderWidth = '1px';
style.borderRadius = `${this.borderRadius}px`; style.borderStyle = 'solid';
} }
return style; return style;
}, };
// 滑块的样式 },
itemBarStyle() { // mode=button时,外层view的样式
let style = {}; subsectionStyle() {
style.backgroundColor = this.activeColor; let style = {};
style.zIndex = 1; style.height = uni.upx2px(this.height) + 'px';
if (this.mode == 'button') { if (this.mode == 'button') {
style.backgroundColor = this.buttonColor; style.backgroundColor = this.bgColor;
style.borderRadius = `${this.borderRadius}px`; style.padding = `${this.buttonPadding}px`;
style.bottom = `${this.buttonPadding}px`; style.borderRadius = `${this.borderRadius}px`;
style.height = uni.upx2px(this.height) - this.buttonPadding * 2 + 'px'; }
style.zIndex = 0; return style;
} },
return Object.assign(this.itemBgStyle, style); // 滑块的样式
} itemBarStyle() {
}, let style = {};
mounted() { style.backgroundColor = this.activeColor;
setTimeout(() => { style.zIndex = 1;
this.getTabsInfo(); if (this.mode == 'button') {
}, 10); style.backgroundColor = this.buttonColor;
}, style.borderRadius = `${this.borderRadius}px`;
methods: { style.bottom = `${this.buttonPadding}px`;
// 改变滑块的样式 style.height = uni.upx2px(this.height) - this.buttonPadding * 2 + 'px';
changeSectionStatus(nVal) { style.zIndex = 0;
if (this.mode == 'subsection') { }
// 根据滑块在最左边和最右边时,显示左边和右边的圆角 return Object.assign(this.itemBgStyle, style);
if (nVal == this.list.length - 1) { }
this.itemBgStyle.borderRadius = `0 ${this.buttonPadding}px ${this.buttonPadding}px 0`; },
} mounted() {
if (nVal == 0) { setTimeout(() => {
this.itemBgStyle.borderRadius = `${this.buttonPadding}px 0 0 ${this.buttonPadding}px`; this.getTabsInfo();
} }, 10);
if (nVal > 0 && nVal < this.list.length - 1) { },
this.itemBgStyle.borderRadius = '0'; methods: {
} // 改变滑块的样式
} changeSectionStatus(nVal) {
// 更新滑块的位置 if (this.mode == 'subsection') {
setTimeout(() => { // 根据滑块在最左边和最右边时,显示左边和右边的圆角
this.itemBgLeft(); if (nVal == this.list.length - 1) {
}, 10); this.itemBgStyle.borderRadius = `0 ${this.buttonPadding}px ${this.buttonPadding}px 0`;
if (this.vibrateShort && !this.firstTimeVibrateShort) { }
// 使手机产生短促震动,微信小程序有效,APP(HX 2.6.8)和H5无效 if (nVal == 0) {
// #ifndef H5 this.itemBgStyle.borderRadius = `${this.buttonPadding}px 0 0 ${this.buttonPadding}px`;
uni.vibrateShort(); }
// #endif if (nVal > 0 && nVal < this.list.length - 1) {
} this.itemBgStyle.borderRadius = '0';
// 第一次过后,设置firstTimeVibrateShort为false,让其下一次可以震动(如果允许震动的话) }
this.firstTimeVibrateShort = false; }
}, // 更新滑块的位置
click(index) { setTimeout(() => {
// 不允许点击当前激活选项 this.itemBgLeft();
if (index == this.currentIndex) return; }, 10);
this.currentIndex = index; if (this.vibrateShort && !this.firstTimeVibrateShort) {
this.changeSectionStatus(index); // 使手机产生短促震动,微信小程序有效,APP(HX 2.6.8)和H5无效
this.$emit('change', Number(index)); // #ifndef H5
}, uni.vibrateShort();
// 获取各个tab的节点信息 // #endif
getTabsInfo() { }
let view = uni.createSelectorQuery().in(this); // 第一次过后,设置firstTimeVibrateShort为false,让其下一次可以震动(如果允许震动的话)
for (let i = 0; i < this.list.length; i++) { this.firstTimeVibrateShort = false;
view.select('.u-item-' + i).boundingClientRect(); },
} click(index) {
view.exec(res => { // 不允许点击当前激活选项
if (!res.length) { if (index == this.currentIndex) return;
setTimeout(() => { this.currentIndex = index;
this.getTabsInfo(); this.changeSectionStatus(index);
return; this.$emit('change', Number(index));
}, 10); },
} // 获取各个tab的节点信息
// 将分段器每个item的宽度,放入listInfo数组 getTabsInfo() {
res.map((val, index) => { let view = uni.createSelectorQuery().in(this);
this.listInfo[index].width = val.width; for (let i = 0; i < this.list.length; i++) {
}); view.select('.u-item-' + i).boundingClientRect();
// 初始化滑块的宽度 }
if (this.mode == 'subsection') { view.exec(res => {
this.itemBgStyle.width = this.listInfo[0].width + 'px'; if (!res.length) {
} else if (this.mode == 'button') { setTimeout(() => {
this.itemBgStyle.width = this.listInfo[0].width + 'px'; this.getTabsInfo();
} return;
// 初始化滑块的位置 }, 10);
this.itemBgLeft(); }
}); // 将分段器每个item的宽度,放入listInfo数组
}, res.map((val, index) => {
itemBgLeft() { this.listInfo[index].width = val.width;
// 根据是否开启动画效果, });
if (this.animation) { // 初始化滑块的宽度
this.itemBgStyle.transition = 'all 0.35s'; if (this.mode == 'subsection') {
} else { this.itemBgStyle.width = this.listInfo[0].width + 'px';
this.itemBgStyle.transition = 'all 0s'; } else if (this.mode == 'button') {
} this.itemBgStyle.width = this.listInfo[0].width + 'px';
let left = 0; }
// 计算当前活跃item到组件左边的距离 // 初始化滑块的位置
this.listInfo.map((val, index) => { this.itemBgLeft();
if (index < this.currentIndex) left += val.width; });
}); },
// 根据mode不同模式,计算滑块需要移动的距离 itemBgLeft() {
if (this.mode == 'subsection') { // 根据是否开启动画效果,
this.itemBgStyle.left = left + 'px'; if (this.animation) {
} else if (this.mode == 'button') { this.itemBgStyle.transition = 'all 0.35s';
this.itemBgStyle.left = left + this.buttonPadding + 'px'; } else {
} this.itemBgStyle.transition = 'all 0s';
} }
} let left = 0;
}; // 计算当前活跃item到组件左边的距离
</script> this.listInfo.map((val, index) => {
if (index < this.currentIndex) left += val.width;
<style lang="scss" scoped> });
.u-subsection { // 根据mode不同模式,计算滑块需要移动的距离
display: flex; if (this.mode == 'subsection') {
align-items: center; this.itemBgStyle.left = left + 'px';
overflow: hidden; } else if (this.mode == 'button') {
position: relative; this.itemBgStyle.left = left + this.buttonPadding + 'px';
} }
}
.u-item { }
flex: 1; };
text-align: center; </script>
font-size: 26rpx;
height: 100%; <style lang="scss" scoped>
display: flex; .u-subsection {
align-items: center; display: flex;
justify-content: center; align-items: center;
color: $u-main-color; overflow: hidden;
display: inline-flex; position: relative;
padding: 0 6rpx; }
}
.u-item {
.u-item-bg { flex: 1;
background-color: $u-type-primary; text-align: center;
position: absolute; font-size: 26rpx;
z-index: -1; height: 100%;
} display: flex;
align-items: center;
.u-none-border-right { justify-content: center;
border-right: none !important; color: $u-main-color;
} display: inline-flex;
padding: 0 6rpx;
.u-item-first { }
border-top-left-radius: 8rpx;
border-bottom-left-radius: 8rpx; .u-item-bg {
} background-color: $u-type-primary;
position: absolute;
.u-item-last { z-index: -1;
border-top-right-radius: 8rpx; }
border-bottom-right-radius: 8rpx;
} .u-none-border-right {
border-right: none !important;
.u-item-text { }
transition: all 0.35s;
color: $u-main-color; .u-item-first {
display: flex; border-top-left-radius: 8rpx;
align-items: center; border-bottom-left-radius: 8rpx;
position: relative; }
z-index: 99;
} .u-item-last {
border-top-right-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.u-item-text {
transition: all 0.35s;
color: $u-main-color;
display: flex;
align-items: center;
position: relative;
z-index: 99;
}
</style> </style>
<template> <template>
<movable-area class="u-swipe-action" :style="{backgroundColor: bgColor}"> <movable-area class="u-swipe-action" :style="{backgroundColor: bgColor}">
<movable-view <movable-view class="u-swipe-view" @change="change" @touchend="touchend" @touchstart="touchstart" direction="horizontal"
class="u-swipe-view" :disabled="disabled" :x="moveX" :style="{
@change="change" width: movableViewWidth
@touchend="touchend" }">
@touchstart="touchstart" <view class="u-swipe-content">
direction="horizontal" <slot></slot>
:disabled="disabled" </view>
:x="moveX" <view class="u-swipe-del" @tap.stop="del" :style="{
:style="{ width: innerBtnWidth + 'px',
width: movableViewWidth backgroundColor: btnBgColor
}" }">
> <view class="u-btn-text">{{ btnText }}</view>
<view class="u-swipe-content"><slot></slot></view> </view>
<view </movable-view>
class="u-swipe-del" </movable-area>
@tap.stop="del" </template>
:style="{
width: innerBtnWidth + 'px', <script>
backgroundColor: btnBgColor /**
}" * alertTips 提示
> * @description 该组件一般用于左滑唤出操作菜单的场景,用的最多的是左滑删除操作。
<view class="u-btn-text">{{ btnText }}</view> * @tutorial https://www.uviewui.com/components/swipeAction.html
</view> * @property {String} btn-text 按钮文字(默认“删除”)
</movable-view> * @property {String} btn-bg-color 按钮背景颜色(默认#ff0033)
</movable-area> * @property {String} bg-color 整个组件背景颜色(默认#ffffff)
</template> * @property {String Number} index 标识符,点击时候用于区分点击了哪一个,用v-for循环时的index即可
* @property {String Number} btn-width 按钮宽度,单位rpx(默认180)
<script> * @property {Boolean} disabled 是否禁止某个swipeAction滑动(默认false)
export default { * @event {Function} click 点击组件时触发
props: { * @event {Function} close 组件触发关闭状态时
// 左边滑动出来按钮的文字 * @event {Function} open 组件触发打开状态时
btnText: { * @example <u-swipe-action btn-text="收藏">...</u-swipe-action>
type: String, */
default: '删除' export default {
}, name: "u-swipe-action",
// 滑动出来的按钮的背景颜色 props: {
btnBgColor: { // 左边滑动出来按钮的文字
type: String, btnText: {
default: '#ff0033' type: String,
}, default: '删除'
// index值,用于得知点击删除的是哪个按钮 },
index: { // 滑动出来的按钮的背景颜色
type: [Number, String], btnBgColor: {
default: '' type: String,
}, default: '#ff0033'
// 滑动按钮的宽度,单位为rpx },
btnWidth: { // index值,用于得知点击删除的是哪个按钮
type: [String, Number], index: {
default: 180 type: [Number, String],
}, default: ''
// 是否禁止某个action滑动 },
disabled: { // 滑动按钮的宽度,单位为rpx
type: Boolean, btnWidth: {
default: false type: [String, Number],
}, default: 180
// 打开或着关闭组件 },
show: { // 是否禁止某个action滑动
type: Boolean, disabled: {
default: false type: Boolean,
}, default: false
// 组件背景颜色 },
bgColor: { // 打开或着关闭组件
type: String, show: {
default: '#ffffff' type: Boolean,
} default: false
}, },
watch: { // 组件背景颜色
show: { bgColor: {
immediate: true, type: String,
handler(nVal, oVal) { default: '#ffffff'
if(nVal) { }
this.open(); },
} else { watch: {
this.close(); show: {
} immediate: true,
} handler(nVal, oVal) {
} if (nVal) {
}, this.open();
data() { } else {
return { this.close();
moveX: 0, // movable-view元素在x轴上需要移动的目标移动距离,用于展开或收起滑动的按钮 }
scrollX: 0, // movable-view移动过程中产生的change事件中的x轴移动值 }
status: false, // 滑动的状态,表示当前是展开还是关闭按钮的状态 }
movableAreaWidth: 0, // 滑动区域 },
elId: this.$u.guid(), // id,用于通知另外组件关闭时的识别 data() {
} return {
}, moveX: 0, // movable-view元素在x轴上需要移动的目标移动距离,用于展开或收起滑动的按钮
computed: { scrollX: 0, // movable-view移动过程中产生的change事件中的x轴移动值
movableViewWidth() { status: false, // 滑动的状态,表示当前是展开还是关闭按钮的状态
return this.movableAreaWidth + this.innerBtnWidth + 'px'; movableAreaWidth: 0, // 滑动区域
}, elId: this.$u.guid(), // id,用于通知另外组件关闭时的识别
innerBtnWidth() { }
return uni.upx2px(this.btnWidth); },
}, computed: {
movableViewWidth() {
}, return this.movableAreaWidth + this.innerBtnWidth + 'px';
mounted() { },
this.getActionRect(); innerBtnWidth() {
}, return uni.upx2px(this.btnWidth);
methods: { },
// 点击删除按钮
del() { },
this.status = false mounted() {
this.moveX = 0 this.getActionRect();
this.$emit('click', this.index); },
}, methods: {
// movable-view元素移动事件 // 点击删除按钮
change(e) { del() {
this.scrollX = e.detail.x; this.status = false
}, this.moveX = 0
// 关闭按钮状态 this.$emit('click', this.index);
close() { },
this.moveX = 0; // movable-view元素移动事件
this.status = false; change(e) {
}, this.scrollX = e.detail.x;
// 打开按钮的状态 },
open() { // 关闭按钮状态
if(this.disabled) return ; close() {
this.moveX = - this.btnWidth; this.moveX = 0;
this.status = true; this.status = false;
}, },
// 用户手指离开movable-view元素,停止触摸 // 打开按钮的状态
touchend() { open() {
this.moveX = this.scrollX; if (this.disabled) return;
// 停止触摸时候,判断当前是展开还是关闭状态 this.moveX = -this.btnWidth;
// 关闭状态 this.status = true;
// 这一步很重要,需要先给this.moveX一个变化的随机值,否则因为前后设置的为同一个值 },
// props单向数据流的原因,导致movable-view元素不会发生变化,切记,详见文档: // 用户手指离开movable-view元素,停止触摸
// https://uniapp.dcloud.io/use?id=%e5%b8%b8%e8%a7%81%e9%97%ae%e9%a2%98 touchend() {
this.$nextTick(function(){ this.moveX = this.scrollX;
if(this.status == false) { // 停止触摸时候,判断当前是展开还是关闭状态
// 关闭状态左滑,产生的x轴位移为负值,也就是说滑动的距离大于按钮的三分之一宽度,自动展开按钮 // 关闭状态
if(this.scrollX <= -this.innerBtnWidth / 3) { // 这一步很重要,需要先给this.moveX一个变化的随机值,否则因为前后设置的为同一个值
this.moveX = -this.innerBtnWidth; // 按钮宽度的负值,即为展开状态movable-view元素左滑的距离 // props单向数据流的原因,导致movable-view元素不会发生变化,切记,详见文档:
this.status = true; // 标志当前为展开状态 // https://uniapp.dcloud.io/use?id=%e5%b8%b8%e8%a7%81%e9%97%ae%e9%a2%98
this.emitOpenEvent(); this.$nextTick(function() {
// 产生震动效果 if (this.status == false) {
uni.vibrateShort(); // 关闭状态左滑,产生的x轴位移为负值,也就是说滑动的距离大于按钮的三分之一宽度,自动展开按钮
} else { if (this.scrollX <= -this.innerBtnWidth / 3) {
this.moveX = 0; // 如果距离没有按钮宽度的三分之一,自动收起 this.moveX = -this.innerBtnWidth; // 按钮宽度的负值,即为展开状态movable-view元素左滑的距离
this.status = false; this.status = true; // 标志当前为展开状态
this.emitCloseEvent(); this.emitOpenEvent();
} // 产生震动效果
} else { uni.vibrateShort();
// 如果在打开的状态下,右滑动的距离X轴偏移超过按钮的三分之一(负值反过来的三分之二),自动收起按钮 } else {
if(this.scrollX > -this.innerBtnWidth * 2 / 3) { this.moveX = 0; // 如果距离没有按钮宽度的三分之一,自动收起
this.moveX = 0; this.status = false;
this.$nextTick(() => { this.emitCloseEvent();
this.moveX = 101; }
}) } else {
this.status = false; // 如果在打开的状态下,右滑动的距离X轴偏移超过按钮的三分之一(负值反过来的三分之二),自动收起按钮
this.emitCloseEvent(); if (this.scrollX > -this.innerBtnWidth * 2 / 3) {
} else { this.moveX = 0;
this.moveX = -this.innerBtnWidth; this.$nextTick(() => {
this.status = true; this.moveX = 101;
this.emitOpenEvent(); })
} this.status = false;
} this.emitCloseEvent();
}) } else {
}, this.moveX = -this.innerBtnWidth;
emitOpenEvent() { this.status = true;
this.$emit('open', this.index); this.emitOpenEvent();
}, }
emitCloseEvent() { }
this.$emit('close', this.index); })
}, },
// 开始触摸 emitOpenEvent() {
touchstart() { this.$emit('open', this.index);
},
}, emitCloseEvent() {
getActionRect() { this.$emit('close', this.index);
this.$uGetRect('.u-swipe-action').then(res => { },
this.movableAreaWidth = res.width; // 开始触摸
}) touchstart() {
}
} },
} getActionRect() {
</script> this.$uGetRect('.u-swipe-action').then(res => {
this.movableAreaWidth = res.width;
<style scoped lang="scss"> })
.u-swipe-action { }
width: auto; }
height: initial; }
position: relative; </script>
overflow: hidden;
} <style scoped lang="scss">
.u-swipe-action {
.u-swipe-view { width: auto;
display: flex; height: initial;
height: initial; position: relative;
position: relative; /* 这一句很关键,覆盖默认的绝对定位 */ overflow: hidden;
} }
.u-swipe-content { .u-swipe-view {
flex: 1; display: flex;
} height: initial;
position: relative;
.u-swipe-del { /* 这一句很关键,覆盖默认的绝对定位 */
position: relative; }
font-size: 30rpx;
color: #ffffff; .u-swipe-content {
} flex: 1;
}
.u-btn-text {
position: absolute; .u-swipe-del {
top: 50%; position: relative;
left: 50%; font-size: 30rpx;
transform: translate(-50%, -50%); color: #ffffff;
} }
.u-btn-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style> </style>
<template> <template>
<view class="u-swiper-wrap" <view class="u-swiper-wrap" :style="{
:style="{ borderRadius: `${borderRadius}rpx`,
borderRadius: `${borderRadius}rpx`, }">
}"> <swiper @change="change" :current="current" :interval="interval" :circular="circular" :duration="duration" :autoplay="autoplay"
<swiper :previous-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'" :next-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'"
@change="change" :style="{
:current="current" height: height + 'rpx'
:interval="interval" }">
:circular="circular" <swiper-item class="u-swiper-item" v-for="(item, index) in list" :key="index" @tap="listClick(index)">
:duration="duration" <view class="u-list-image-wrap" :class="[current != index ? 'u-list-scale' : '']" :style="{
:autoplay="autoplay" borderRadius: `${borderRadius}rpx`,
:previous-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'" transform: effect3d && current != index ? 'scaleY(0.9)' : 'scaleY(1)',
:next-margin="effect3d ? effect3dPreviousMargin + 'rpx' : '0'" margin: effect3d && current != index ? '0 20rpx' : 0
:style="{ }">
height: height + 'rpx' <image class="u-swiper-image" :src="item[name]" :mode="imgMode"></image>
}" <view v-if="title" class="u-swiper-title u-line-1" :style="{
> 'padding-bottom': titlePaddingBottom
<swiper-item }">
class="u-swiper-item" {{ item.title }}
v-for="(item, index) in list" </view>
:key="index" </view>
@tap="listClick(index)" </swiper-item>
> </swiper>
<view <view class="u-swiper-indicator" :style="{
class="u-list-image-wrap" top: indicatorPos == 'topLeft' || indicatorPos == 'topCenter' || indicatorPos == 'topRight' ? '12rpx' : 'auto',
:class="[current != index ? 'u-list-scale' : '']" bottom: indicatorPos == 'bottomLeft' || indicatorPos == 'bottomCenter' || indicatorPos == 'bottomRight' ? '12rpx' : 'auto',
:style="{ justifyContent: justifyContent,
borderRadius: `${borderRadius}rpx`, padding: `0 ${effect3d ? '74rpx' : '24rpx'}`
transform: effect3d && current != index ? 'scaleY(0.9)' : 'scaleY(1)', }">
margin: effect3d && current != index ? '0 20rpx' : 0 <block v-if="mode == 'rect'">
}" <view class="u-indicator-item-rect" :class="{ 'u-indicator-item-rect-active': index == current }" v-for="(item, index) in list"
> :key="index"></view>
<image class="u-swiper-image" :src="item[name]" :mode="imgMode"></image> </block>
<view v-if="title" <block v-if="mode == 'dot'">
class="u-swiper-title u-line-1" <view class="u-indicator-item-dot" :class="{ 'u-indicator-item-dot-active': index == current }" v-for="(item, index) in list"
:style="{ :key="index"></view>
'padding-bottom': titlePaddingBottom </block>
}" <block v-if="mode == 'round'">
> <view class="u-indicator-item-round" :class="{ 'u-indicator-item-round-active': index == current }" v-for="(item, index) in list"
{{ item.title }} :key="index"></view>
</view> </block>
</view> <block v-if="mode == 'number'">
</swiper-item> <view class="u-indicator-item-number">{{ current + 1 }}/{{ list.length }}</view>
</swiper> </block>
<view </view>
class="u-swiper-indicator" </view>
:style="{ </template>
top: indicatorPos == 'topLeft' || indicatorPos == 'topCenter' || indicatorPos == 'topRight' ? '12rpx' : 'auto',
bottom: indicatorPos == 'bottomLeft' || indicatorPos == 'bottomCenter' || indicatorPos == 'bottomRight' ? '12rpx' : 'auto', <script>
justifyContent: justifyContent, /**
padding: `0 ${effect3d ? '74rpx' : '24rpx'}` * alertTips 提示
}" * @description 该组件一般用于导航轮播,广告展示等场景,可开箱即用
> * @tutorial https://www.uviewui.com/components/swiper.html
<block v-if="mode == 'rect'"> * @property {Array} list 轮播图数据,见官网"基本使用"说明
<view class="u-indicator-item-rect" :class="{ 'u-indicator-item-rect-active': index == current }" v-for="(item, index) in list" :key="index"></view> * @property {Boolean} title 是否显示标题文字,需要配合list参数,见官网说明(默认false)
</block> * @property {String} mode 指示器模式,见官网说明(默认round)
<block v-if="mode == 'dot'"> * @property {String Number} height 轮播图组件高度,单位rpx(默认250)
<view class="u-indicator-item-dot" :class="{ 'u-indicator-item-dot-active': index == current }" v-for="(item, index) in list" :key="index"></view> * @property {String} indicator-pos 指示器的位置(默认bottomCenter)
</block> * @property {Boolean} effect3d 是否开启3D效果(默认false)
<block v-if="mode == 'round'"> * @property {Boolean} autoplay 是否自动播放(默认true)
<view class="u-indicator-item-round" :class="{ 'u-indicator-item-round-active': index == current }" v-for="(item, index) in list" :key="index"></view> * @property {String Number} interval 自动轮播时间间隔,单位ms(默认2500)
</block> * @property {Boolean} circular 是否衔接播放,见官网说明(默认true)
<block v-if="mode == 'number'"> * @property {String Number} border-radius 轮播图圆角值,单位rpx(默认8)
<view class="u-indicator-item-number">{{ current + 1 }}/{{ list.length }}</view> * @property {Object} title-style 自定义标题样式
</block> * @property {String Number} effect3d-previous-margin mode = true模式的情况下,激活项与前后项之间的距离,单位rpx(默认50)
</view> * @property {String} img-mode 图片的裁剪模式,详见image组件裁剪模式(默认aspectFill)
</view> * @event {Function} click 点击轮播图时触发
</template> * @example <u-swiper :list="list" mode="dot" indicator-pos="bottomRight"></u-swiper>
*/
<script> export default {
export default { name: "u-swiper",
props: { props: {
// 轮播图的数据,格式如:[{image: 'xxxx', title: 'xxxx'},{image: 'yyyy', title: 'yyyy'}],其中title字段可选 // 轮播图的数据,格式如:[{image: 'xxxx', title: 'xxxx'},{image: 'yyyy', title: 'yyyy'}],其中title字段可选
list: { list: {
type: Array, type: Array,
default() { default () {
return []; return [];
} }
}, },
// 是否显示title标题 // 是否显示title标题
title: { title: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// 用户自定义的指示器的样式 // 用户自定义的指示器的样式
indicator: { indicator: {
type: Object, type: Object,
default() { default () {
return {}; return {};
} }
}, },
// 圆角值 // 圆角值
borderRadius: { borderRadius: {
type: [Number, String], type: [Number, String],
default: 8 default: 8
}, },
// 隔多久自动切换 // 隔多久自动切换
interval: { interval: {
type: [String, Number], type: [String, Number],
default: 3000 default: 3000
}, },
// 指示器的模式,rect|dot|number|round // 指示器的模式,rect|dot|number|round
mode: { mode: {
type: String, type: String,
default: 'round' default: 'round'
}, },
// list的高度,单位rpx // list的高度,单位rpx
height: { height: {
type: [Number, String], type: [Number, String],
default: 250 default: 250
}, },
// 指示器的位置,topLeft|topCenter|topRight|bottomLeft|bottomCenter|bottomRight // 指示器的位置,topLeft|topCenter|topRight|bottomLeft|bottomCenter|bottomRight
indicatorPos: { indicatorPos: {
type: String, type: String,
default: 'bottomCenter' default: 'bottomCenter'
}, },
// 是否开启缩放效果 // 是否开启缩放效果
effect3d: { effect3d: {
type: Boolean, type: Boolean,
default: false default: false
}, },
// 3D模式的情况下,激活item与前后item之间的距离,单位rpx // 3D模式的情况下,激活item与前后item之间的距离,单位rpx
effect3dPreviousMargin: { effect3dPreviousMargin: {
type: [Number, String], type: [Number, String],
default: 50 default: 50
}, },
// 是否自动播放 // 是否自动播放
autoplay: { autoplay: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// 自动轮播时间间隔,单位ms // 自动轮播时间间隔,单位ms
duration: { duration: {
type: [Number, String], type: [Number, String],
default: 500 default: 500
}, },
// 是否衔接滑动,即到最后一张时接着滑动,是佛自动切换到第一张 // 是否衔接滑动,即到最后一张时接着滑动,是佛自动切换到第一张
circular: { circular: {
type: Boolean, type: Boolean,
default: true default: true
}, },
// 图片的形式模式 // 图片的形式模式
imgMode: { imgMode: {
type: String, type: String,
default: 'aspectFill' default: 'aspectFill'
}, },
// 从list数组中读取的图片的属性名 // 从list数组中读取的图片的属性名
name: { name: {
type: String, type: String,
default: 'image' default: 'image'
} }
}, },
data() { data() {
return { return {
current: 0 // 当前活跃的swiper-item的index current: 0 // 当前活跃的swiper-item的index
}; };
}, },
computed: { computed: {
justifyContent() { justifyContent() {
if(this.indicatorPos == 'topLeft' || this.indicatorPos == 'bottomLeft') return 'flex-start'; if (this.indicatorPos == 'topLeft' || this.indicatorPos == 'bottomLeft') return 'flex-start';
if(this.indicatorPos == 'topCenter' || this.indicatorPos == 'bottomCenter') return 'center'; if (this.indicatorPos == 'topCenter' || this.indicatorPos == 'bottomCenter') return 'center';
if(this.indicatorPos == 'topRight' || this.indicatorPos == 'bottomRight') return 'flex-end'; if (this.indicatorPos == 'topRight' || this.indicatorPos == 'bottomRight') return 'flex-end';
}, },
titlePaddingBottom() { titlePaddingBottom() {
let tmp = 0; let tmp = 0;
if(this.mode == 'none') return '12rpx'; if (this.mode == 'none') return '12rpx';
if(['bottomLeft', 'bottomCenter', 'bottomRight'].indexOf(this.indicatorPos) >= 0 && this.mode == 'number') { if (['bottomLeft', 'bottomCenter', 'bottomRight'].indexOf(this.indicatorPos) >= 0 && this.mode == 'number') {
tmp = '60rpx'; tmp = '60rpx';
} else if(['bottomLeft', 'bottomCenter', 'bottomRight'].indexOf(this.indicatorPos) >= 0 && this.mode != 'number') { } else if (['bottomLeft', 'bottomCenter', 'bottomRight'].indexOf(this.indicatorPos) >= 0 && this.mode != 'number') {
tmp = '40rpx'; tmp = '40rpx';
} else { } else {
tmp = '12rpx'; tmp = '12rpx';
} }
return tmp; return tmp;
} }
}, },
methods: { methods: {
listClick(index) { listClick(index) {
this.$emit('click', index); this.$emit('click', index);
}, },
change(e) { change(e) {
this.current = e.detail.current; this.current = e.detail.current;
} }
} }
}; };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.u-swiper-wrap { .u-swiper-wrap {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
} }
.u-swiper-image { .u-swiper-image {
width: 100%; width: 100%;
will-change: transform; will-change: transform;
height: 100%; height: 100%;
display: block; display: block;
/* #ifdef H5 */ /* #ifdef H5 */
pointer-events: none; pointer-events: none;
/* #endif */ /* #endif */
} }
.u-swiper-indicator { .u-swiper-indicator {
padding: 0 24rpx; padding: 0 24rpx;
position: absolute; position: absolute;
display: flex; display: flex;
width: 100%; width: 100%;
z-index: 1; z-index: 1;
} }
.u-indicator-item-rect { .u-indicator-item-rect {
width: 26rpx; width: 26rpx;
height: 8rpx; height: 8rpx;
margin: 0 6rpx; margin: 0 6rpx;
transition: all 0.5s; transition: all 0.5s;
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
} }
.u-indicator-item-rect-active { .u-indicator-item-rect-active {
background-color: rgba(255, 255, 255, 0.8); background-color: rgba(255, 255, 255, 0.8);
} }
.u-indicator-item-dot { .u-indicator-item-dot {
width: 14rpx; width: 14rpx;
height: 14rpx; height: 14rpx;
margin: 0 6rpx; margin: 0 6rpx;
border-radius: 20rpx; border-radius: 20rpx;
transition: all 0.5s; transition: all 0.5s;
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
} }
.u-indicator-item-dot-active { .u-indicator-item-dot-active {
background-color: rgba(255, 255, 255, 0.8); background-color: rgba(255, 255, 255, 0.8);
} }
.u-indicator-item-round { .u-indicator-item-round {
width: 14rpx; width: 14rpx;
height: 14rpx; height: 14rpx;
margin: 0 6rpx; margin: 0 6rpx;
border-radius: 20rpx; border-radius: 20rpx;
transition: all 0.5s; transition: all 0.5s;
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
} }
.u-indicator-item-round-active { .u-indicator-item-round-active {
width: 34rpx; width: 34rpx;
background-color: rgba(255, 255, 255, 0.8); background-color: rgba(255, 255, 255, 0.8);
} }
.u-indicator-item-number { .u-indicator-item-number {
padding: 6rpx 16rpx; padding: 6rpx 16rpx;
line-height: 1; line-height: 1;
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
border-radius: 100rpx; border-radius: 100rpx;
font-size: 26rpx; font-size: 26rpx;
color: rgba(255, 255, 255, 0.8); color: rgba(255, 255, 255, 0.8);
} }
.u-list-scale { .u-list-scale {
transform-origin: center center; transform-origin: center center;
} }
.u-list-image-wrap { .u-list-image-wrap {
width: 100%; width: 100%;
height: 100%; height: 100%;
flex: 1; flex: 1;
transition: all 0.5s; transition: all 0.5s;
overflow: hidden; overflow: hidden;
box-sizing: content-box; box-sizing: content-box;
position: relative; position: relative;
} }
.u-swiper-title { .u-swiper-title {
position: absolute; position: absolute;
background-color: rgba(0, 0, 0, 0.3); background-color: rgba(0, 0, 0, 0.3);
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 100%; width: 100%;
font-size: 28rpx; font-size: 28rpx;
padding: 12rpx 24rpx; padding: 12rpx 24rpx;
color: rgba(255, 255, 255, 0.9); color: rgba(255, 255, 255, 0.9);
} }
.u-swiper-item { .u-swiper-item {
display: flex; display: flex;
overflow: hidden; overflow: hidden;
align-items: center; align-items: center;
} }
</style> </style>
<template> <template>
<view <view class="u-switch" :class="[value == true ? 'u-switch--on' : '', disabled ? 'u-switch--disabled' : '']" @tap="onClick"
class="u-switch" :style="[switchStyle]">
:class="[value == true ? 'u-switch--on' : '', disabled ? 'u-switch--disabled' : '']" <view class="u-switch__node node-class">
@tap="onClick" <u-loading :show="loading" class="u-switch__loading" :size="size * 0.6" :color="loadingColor" />
:style="[switchStyle]" </view>
> </view>
<view class="u-switch__node node-class"> </template>
<u-loading :show="loading" class="u-switch__loading" :size="size * 0.6" :color="loadingColor"/>
</view> <script>
</view> /**
</template> * alertTips 提示
* @description 选择开关一般用于只有两个选择,且只能选其一的场景。
<script> * @tutorial https://www.uviewui.com/components/switch.html
export default { * @property {Boolean} loading 是否处于加载中(默认false)
props: { * @property {Boolean} disabled 是否禁用(默认false)
// 是否为加载中状态 * @property {String Number} size 开关尺寸,单位rpx(默认50)
loading: { * @property {String} active-color 打开时的背景色(默认#2979ff)
type: Boolean, * @property {Boolean} inactive-color 关闭时的背景色(默认#ffffff)
default: false * @event {Function} change 在switch打开或关闭时触发
}, * @example <u-switch v-model="checked" active-color="red" inactive-color="#eee"></u-switch>
// 是否为禁用装填 */
disabled: { export default {
type: Boolean, name: "u-switch",
default: false props: {
}, // 是否为加载中状态
// 开关尺寸,单位rpx loading: {
size: { type: Boolean,
type: [Number, String], default: false
default: 50 },
}, // 是否为禁用装填
// 打开时的背景颜色 disabled: {
activeColor: { type: Boolean,
type: String, default: false
default: '#2979ff' },
}, // 开关尺寸,单位rpx
// 关闭时的背景颜色 size: {
unactiveColor: { type: [Number, String],
type: String, default: 50
default: '#ffffff' },
}, // 打开时的背景颜色
// 通过v-model双向绑定的值 activeColor: {
value: { type: String,
type: Boolean, default: '#2979ff'
default: false },
} // 关闭时的背景颜色
}, unactiveColor: {
data() { type: String,
return { default: '#ffffff'
},
} // 通过v-model双向绑定的值
}, value: {
computed: { type: Boolean,
switchStyle() { default: false
let style = {}; }
style.fontSize = this.size + 'rpx'; },
style.backgroundColor = this.value ? this.activeColor : this.unactiveColor; data() {
return style; return {
},
loadingColor() { }
return this.value ? this.activeColor : null; },
} computed: {
}, switchStyle() {
methods: { let style = {};
onClick() { style.fontSize = this.size + 'rpx';
if (!this.disabled && !this.loading) { style.backgroundColor = this.value ? this.activeColor : this.unactiveColor;
// 使手机产生短促震动,微信小程序有效,APP(HX 2.6.8)和H5无效 return style;
uni.vibrateShort(); },
this.$emit('input', !this.value); loadingColor() {
// 放到下一个生命周期,因为双向绑定的value修改父组件状态需要时间,且是异步的 return this.value ? this.activeColor : null;
this.$nextTick(function(){ }
this.$emit('change', this.value); },
}) methods: {
} onClick() {
} if (!this.disabled && !this.loading) {
} // 使手机产生短促震动,微信小程序有效,APP(HX 2.6.8)和H5无效
}; uni.vibrateShort();
</script> this.$emit('input', !this.value);
// 放到下一个生命周期,因为双向绑定的value修改父组件状态需要时间,且是异步的
<style lang="scss" scoped> this.$nextTick(function() {
.u-switch { this.$emit('change', this.value);
position: relative; })
display: inline-block; }
box-sizing: initial; }
width: 2em; }
width: var(--switch-width, 2em); };
height: 1em; </script>
height: var(--switch-height, 1em);
background-color: #fff; <style lang="scss" scoped>
background-color: var(--switch-background-color, #fff); .u-switch {
border: 1px solid rgba(0, 0, 0, 0.1); position: relative;
border: var(--switch-border, 1px solid rgba(0, 0, 0, 0.1)); display: inline-block;
border-radius: 1em; box-sizing: initial;
border-radius: var(--switch-node-size, 1em); width: 2em;
transition: background-color 0.3s; width: var(--switch-width, 2em);
transition: background-color var(--switch-transition-duration, 0.3s); height: 1em;
} height: var(--switch-height, 1em);
background-color: #fff;
.u-switch__node { background-color: var(--switch-background-color, #fff);
display: flex; border: 1px solid rgba(0, 0, 0, 0.1);
align-items: center; border: var(--switch-border, 1px solid rgba(0, 0, 0, 0.1));
justify-content: center; border-radius: 1em;
position: absolute; border-radius: var(--switch-node-size, 1em);
top: 0; transition: background-color 0.3s;
left: 0; transition: background-color var(--switch-transition-duration, 0.3s);
border-radius: 100%; }
z-index: 1;
z-index: var(--switch-node-z-index, 1); .u-switch__node {
width: 1em; display: flex;
width: var(--switch-node-size, 1em); align-items: center;
height: 1em; justify-content: center;
height: var(--switch-node-size, 1em); position: absolute;
background-color: #fff; top: 0;
background-color: var(--switch-node-background-color, #fff); left: 0;
box-shadow: 0 3px 1px 0 rgba(0, 0, 0, 0.05), 0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 3px 3px 0 rgba(0, 0, 0, 0.05); border-radius: 100%;
box-shadow: var(--switch-node-box-shadow, 0 3px 1px 0 rgba(0, 0, 0, 0.05), 0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 3px 3px 0 rgba(0, 0, 0, 0.05)); z-index: 1;
transition: -webkit-transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05); z-index: var(--switch-node-z-index, 1);
transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05); width: 1em;
transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05), -webkit-transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05); width: var(--switch-node-size, 1em);
transition: -webkit-transform var(--switch-transition-duration, 0.3s) cubic-bezier(0.3, 1.05, 0.4, 1.05); height: 1em;
transition: transform var(--switch-transition-duration, 0.3s) cubic-bezier(0.3, 1.05, 0.4, 1.05); height: var(--switch-node-size, 1em);
transition: transform var(--switch-transition-duration, 0.3s) cubic-bezier(0.3, 1.05, 0.4, 1.05), background-color: #fff;
-webkit-transform var(--switch-transition-duration, 0.3s) cubic-bezier(0.3, 1.05, 0.4, 1.05); background-color: var(--switch-node-background-color, #fff);
} box-shadow: 0 3px 1px 0 rgba(0, 0, 0, 0.05), 0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 3px 3px 0 rgba(0, 0, 0, 0.05);
box-shadow: var(--switch-node-box-shadow, 0 3px 1px 0 rgba(0, 0, 0, 0.05), 0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 3px 3px 0 rgba(0, 0, 0, 0.05));
.u-switch__loading { transition: -webkit-transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
display: flex; transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
align-items: center; transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05), -webkit-transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
justify-content: center; transition: -webkit-transform var(--switch-transition-duration, 0.3s) cubic-bezier(0.3, 1.05, 0.4, 1.05);
} transition: transform var(--switch-transition-duration, 0.3s) cubic-bezier(0.3, 1.05, 0.4, 1.05);
transition: transform var(--switch-transition-duration, 0.3s) cubic-bezier(0.3, 1.05, 0.4, 1.05),
.u-switch--on { -webkit-transform var(--switch-transition-duration, 0.3s) cubic-bezier(0.3, 1.05, 0.4, 1.05);
background-color: #1989fa; }
background-color: var(--switch-on-background-color, #1989fa);
} .u-switch__loading {
display: flex;
.u-switch--on .u-switch__node { align-items: center;
-webkit-transform: translateX(1em); justify-content: center;
transform: translateX(1em); }
-webkit-transform: translateX(calc(var(--switch-width, 2em) - var(--switch-node-size, 1em)));
transform: translateX(calc(var(--switch-width, 2em) - var(--switch-node-size, 1em))); .u-switch--on {
} background-color: #1989fa;
background-color: var(--switch-on-background-color, #1989fa);
.u-switch--disabled { }
opacity: 0.4;
opacity: var(--switch-disabled-opacity, 0.4); .u-switch--on .u-switch__node {
} -webkit-transform: translateX(1em);
transform: translateX(1em);
-webkit-transform: translateX(calc(var(--switch-width, 2em) - var(--switch-node-size, 1em)));
transform: translateX(calc(var(--switch-width, 2em) - var(--switch-node-size, 1em)));
}
.u-switch--disabled {
opacity: 0.4;
opacity: var(--switch-disabled-opacity, 0.4);
}
</style> </style>
<template> <template>
<view class="u-table" :style="[tableStyle]"> <view class="u-table" :style="[tableStyle]">
<slot /> <slot />
</view> </view>
</template> </template>
<script> <script>
export default { /**
props: { * alertTips 提示
borderColor: { * @description 表格组件一般用于展示大量结构化数据的场景
type: String, * @tutorial https://www.uviewui.com/components/table.html
default: '#e4e7ed' * @property {String} border-color 表格边框的颜色(默认#e4e7ed)
}, * @property {String} bg-color 表格的背景颜色(默认#ffffff)
align: { * @property {String} align 单元格的内容对齐方式,作用类似css的text-align(默认center)
type: String, * @property {String} padding 单元格的内边距,同css的padding写法(默认10rpx 0)
default: 'center' * @property {String Number} font-size 单元格字体大小,单位rpx(默认28)
}, * @property {String} color 单元格字体颜色(默认#606266)
// td的内边距 * @property {Object} th-style th单元格的样式,对象形式(将th所需参数放在table组件,是为了避免每一个th组件要写一遍)
padding: { * @event {Function} click 点击组件时触发
type: String, * @event {Function} close 点击关闭按钮时触发
default: '10rpx 6rpx' * @example <u-table></u-table>
}, */
// 字体大小 export default {
fontSize: { name: "u-table",
type: [String, Number], props: {
default: 28 borderColor: {
}, type: String,
// 字体颜色 default: '#e4e7ed'
color: { },
type: String, align: {
default: '#606266' type: String,
}, default: 'center'
// th的自定义样式 },
thStyle: { // td的内边距
type: Object, padding: {
default() { type: String,
return {} default: '10rpx 6rpx'
} },
}, // 字体大小
// table的背景颜色 fontSize: {
bgColor: { type: [String, Number],
type: String, default: 28
default: '#ffffff' },
} // 字体颜色
}, color: {
provide() { type: String,
return { default: '#606266'
uTable: this, },
uTd: this // th的自定义样式
}; thStyle: {
}, type: Object,
data() { default () {
return { return {}
} }
}, },
computed: { // table的背景颜色
tableStyle() { bgColor: {
let style = {}; type: String,
style.borderLeft = `solid 1px ${this.borderColor}`; default: '#ffffff'
style.borderTop = `solid 1px ${this.borderColor}`; }
style.backgroundColor = this.bgColor;; },
return style; provide() {
} return {
} uTable: this,
} uTd: this
</script> };
},
<style lang="scss" scoped> data() {
.u-table { return {}
width: 100%; },
box-sizing: border-box; computed: {
} tableStyle() {
let style = {};
.u-table /deep/ t-tr { style.borderLeft = `solid 1px ${this.borderColor}`;
display: flex; style.borderTop = `solid 1px ${this.borderColor}`;
} style.backgroundColor = this.bgColor;;
return style;
}
}
}
</script>
<style lang="scss" scoped>
.u-table {
width: 100%;
box-sizing: border-box;
}
.u-table /deep/ t-tr {
display: flex;
}
</style> </style>
<template> <template>
<view <view class="u-tabs" :style="{
class="u-tabs" zIndex: zIndex,
:style="{ background: bgColor
zIndex: zIndex, }">
background: bgColor <scroll-view scroll-x class="u-scroll-view" :scroll-left="scrollLeft" scroll-with-animation :style="{ zIndex: zIndex + 1 }">
}" <view class="u-tabs-scroll-box" :class="{'u-tabs-scorll-flex': !isScroll}">
> <view class="u-tabs-item" :style="{
<scroll-view scroll-x class="u-scroll-view" :scroll-left="scrollLeft" scroll-with-animation :style="{ zIndex: zIndex + 1 }"> height: height + 'rpx',
<view class="u-tabs-scroll-box" :class="{'u-tabs-scorll-flex': !isScroll}"> lineHeight: height + 'rpx',
<view padding: `0 ${gutter / 2}rpx`,
class="u-tabs-item" color: tabsInfo.length > 0 ? (tabsInfo[index] ? tabsInfo[index].color : activeColor) : inactiveColor,
:style="{ fontSize: fontSize + 'rpx',
height: height + 'rpx', zIndex: zIndex + 2,
lineHeight: height + 'rpx', fontWeight: (index == getCurrent && bold) ? 'bold' : 'normal'
padding: `0 ${gutter / 2}rpx`, }"
color: tabsInfo.length > 0 ? (tabsInfo[index] ? tabsInfo[index].color : activeColor) : inactiveColor, v-for="(item, index) in getTabs" :key="index" :class="[preId + index]" @tap="emit(index)">
fontSize: fontSize + 'rpx', {{ item[name] || item['name']}}
zIndex: zIndex + 2, </view>
fontWeight: (index == getCurrent && bold) ? 'bold' : 'normal' <view class="u-scroll-bar" :style="{
}" width: barWidthPx + 'px',
v-for="(item, index) in getTabs" height: barHeight + 'rpx',
:key="index" borderRadius: '100px',
:class="[preId + index]" backgroundColor: activeColor,
@tap="emit(index)" left: scrollBarLeft + 'px'
> }"></view>
{{ item[name] || item['name']}} </view>
</view> </scroll-view>
<view </view>
class="u-scroll-bar" </template>
:style="{
width: barWidthPx + 'px', <script>
height: barHeight + 'rpx', /**
borderRadius: '100px', * alertTips 提示
backgroundColor: activeColor, * @description 该组件内部实现主要依托于uniapp的scroll-view和swiper组件,主要特色是切换过程中,tabsSwiper文字的颜色可以渐变,底部滑块可以 跟随式滑动,活动tab滚动居中等。应用场景可以用于需要左右切换页面,比如商城的订单中心(待收货-待付款-待评价-已退货)等应用场景。
left: scrollBarLeft + 'px' * @tutorial https://www.uviewui.com/components/tabsSwiper.html
}" * @property {Boolean} is-scroll tabs是否可以左右拖动(默认true)
></view> * @property {Array} list 标签数组,元素为对象,如[{name: '推荐'}]
</view> * @property {String Number} current 指定哪个tab为激活状态(默认0)
</scroll-view> * @property {String Number} height 导航栏的高度,单位rpx(默认80)
</view> * @property {String Number} font-size tab文字大小,单位rpx(默认30)
</template> * @property {String Number} swiper-width tabs组件外部swiper的宽度,默认为屏幕宽度,单位rpx(默认750)
* @property {String} active-color 滑块和激活tab文字的颜色(默认#2979ff)
<script> * @property {String} inactive-color tabs文字颜色(默认#303133)
import colorGradient from '@/uview/libs/function/colorGradient'; * @property {String Number} bar-width 滑块宽度,单位rpx(默认40)
let color = colorGradient; * @property {String Number} bar-height 滑块高度,单位rpx(默认6)
const { windowWidth } = uni.getSystemInfoSync(); * @property {String Number} gutter 单个tab标签的左右内边距之和,单位rpx(默认40)
const preId = 'UEl_'; * @property {String} bg-color tabs导航栏的背景颜色(默认#ffffff)
export default { * @property {String} name 组件内部读取的list参数中的属性名,见官网说明(默认name)
props: { * @property {Boolean} bold 激活选项的字体是否加粗(默认true)
// 导航菜单是否需要滚动,如只有2或者3个的时候,就不需要滚动了,此时使用flex平分tab的宽度 * @event {Function} change 点击标签时触发
isScroll: { * @example <u-tabs-swiper ref="tabs" :list="list" :is-scroll="false"></u-tabs-swiper>
type: Boolean, */
default: true
},
//需循环的标签列表 import colorGradient from '@/uview/libs/function/colorGradient';
list: { let color = colorGradient;
type: Array, const {
default() { windowWidth
return []; } = uni.getSystemInfoSync();
} const preId = 'UEl_';
}, export default {
// 当前活动tab的索引 name: "u-tabs-swiper",
current: { props: {
type: [Number, String], // 导航菜单是否需要滚动,如只有2或者3个的时候,就不需要滚动了,此时使用flex平分tab的宽度
default: 0 isScroll: {
}, type: Boolean,
// 导航栏的高度和行高,单位rpx default: true
height: { },
type: [Number, String], //需循环的标签列表
default: 80 list: {
}, type: Array,
// 字体大小,单位rpx default () {
fontSize: { return [];
type: [Number, String], }
default: 30 },
}, // 当前活动tab的索引
// 过渡动画时长, 单位s current: {
// duration: { type: [Number, String],
// type: [Number, String], default: 0
// default: 0.5 },
// }, // 导航栏的高度和行高,单位rpx
swiperWidth: { height: {
//line3生效, 外部swiper的宽度, 单位rpx type: [Number, String],
type: [String, Number], default: 80
default: 750 },
}, // 字体大小,单位rpx
// 选中项的主题颜色 fontSize: {
activeColor: { type: [Number, String],
type: String, default: 30
default: '#2979ff' },
}, // 过渡动画时长, 单位s
// 未选中项的颜色 // duration: {
inactiveColor: { // type: [Number, String],
type: String, // default: 0.5
default: '#303133' // },
}, swiperWidth: {
// 菜单底部移动的bar的宽度,单位rpx //line3生效, 外部swiper的宽度, 单位rpx
barWidth: { type: [String, Number],
type: [Number, String], default: 750
default: 40 },
}, // 选中项的主题颜色
// 移动bar的高度 activeColor: {
barHeight: { type: String,
type: [Number, String], default: '#2979ff'
default: 6 },
}, // 未选中项的颜色
// 单个tab的左或右内边距(各占一半),单位rpx inactiveColor: {
gutter: { type: String,
type: [Number, String], default: '#303133'
default: 40 },
}, // 菜单底部移动的bar的宽度,单位rpx
// 如果是绝对定位,添加z-index值 barWidth: {
zIndex: { type: [Number, String],
type: [Number, String], default: 40
default: 1 },
}, // 移动bar的高度
// 导航栏的背景颜色 barHeight: {
bgColor: { type: [Number, String],
type: String, default: 6
default: '#ffffff' },
}, // 单个tab的左或右内边距(各占一半),单位rpx
//滚动至中心目标类型 gutter: {
autoCenterMode: { type: [Number, String],
type: String, default: 40
default: 'window' },
}, // 如果是绝对定位,添加z-index值
// 读取传入的数组对象的属性 zIndex: {
name: { type: [Number, String],
type: String, default: 1
default: 'name' },
}, // 导航栏的背景颜色
// 活动tab字体是否加粗 bgColor: {
bold: { type: String,
type: Boolean, default: '#ffffff'
default: true },
} //滚动至中心目标类型
}, autoCenterMode: {
data() { type: String,
return { default: 'window'
scrollLeft: 0, // 滚动scroll-view的左边滚动距离 },
tabQueryInfo: [], // 存放对tab菜单查询后的节点信息 // 读取传入的数组对象的属性
windowWidth: 0, // 屏幕宽度,单位为px name: {
//scrollBarLeft: 0, // 移动bar需要通过translateX()移动的距离 type: String,
animationFinishCurrent: this.current, default: 'name'
componentsWidth: 0, },
line3AddDx: 0, // 活动tab字体是否加粗
line3Dx: 0, bold: {
preId, type: Boolean,
sW: 0, default: true
tabsInfo: [], }
colorGradientArr: [], },
colorStep: 100 // 两个颜色之间的渐变等分 data() {
}; return {
}, scrollLeft: 0, // 滚动scroll-view的左边滚动距离
computed: { tabQueryInfo: [], // 存放对tab菜单查询后的节点信息
// 获取当前活跃的current值 windowWidth: 0, // 屏幕宽度,单位为px
getCurrent() { //scrollBarLeft: 0, // 移动bar需要通过translateX()移动的距离
const current = Number(this.current); animationFinishCurrent: this.current,
// 判断是否超出边界 componentsWidth: 0,
if (current > this.getTabs.length - 1) { line3AddDx: 0,
return this.getTabs.length - 1; line3Dx: 0,
} preId,
if (current < 0) return 0; sW: 0,
return current; tabsInfo: [],
}, colorGradientArr: [],
getTabs() { colorStep: 100 // 两个颜色之间的渐变等分
return this.list; };
}, },
// 滑块需要移动的距离 computed: {
scrollBarLeft() { // 获取当前活跃的current值
return Number(this.line3Dx) + Number(this.line3AddDx); getCurrent() {
}, const current = Number(this.current);
// 滑块的宽度转为px单位 // 判断是否超出边界
barWidthPx() { if (current > this.getTabs.length - 1) {
return uni.upx2px(this.barWidth); return this.getTabs.length - 1;
} }
}, if (current < 0) return 0;
watch: { return current;
current(n, o) { },
this.change(n); getTabs() {
this.setFinishCurrent(n); return this.list;
}, },
list() { // 滑块需要移动的距离
this.$nextTick(() => { scrollBarLeft() {
this.init(); return Number(this.line3Dx) + Number(this.line3AddDx);
}) },
} // 滑块的宽度转为px单位
}, barWidthPx() {
mounted() { return uni.upx2px(this.barWidth);
this.init(); }
}, },
methods: { watch: {
async init() { current(n, o) {
this.countPx(); this.change(n);
await this.getTabsInfo(); this.setFinishCurrent(n);
this.countLine3Dx(); },
this.getQuery(() => { list() {
this.setScrollViewToCenter(); this.$nextTick(() => {
}); this.init();
// 颜色渐变过程数组 })
this.colorGradientArr = color.colorGradient(this.inactiveColor, this.activeColor, this.colorStep); }
}, },
// 获取各个tab的节点信息 mounted() {
getTabsInfo() { this.init();
return new Promise((resolve, reject) => { },
let view = uni.createSelectorQuery().in(this); methods: {
for (let i = 0; i < this.list.length; i++) { async init() {
view.select('.' + preId + i).boundingClientRect(); this.countPx();
} await this.getTabsInfo();
view.exec(res => { this.countLine3Dx();
const arr = []; this.getQuery(() => {
for (let i = 0; i < res.length; i++) { this.setScrollViewToCenter();
// 给每个tab添加其文字颜色属性 });
res[i].color = this.inactiveColor; // 颜色渐变过程数组
// 当前tab直接赋予activeColor this.colorGradientArr = color.colorGradient(this.inactiveColor, this.activeColor, this.colorStep);
if (i == this.getCurrent) res[i].color = this.activeColor; },
arr.push(res[i]); // 获取各个tab的节点信息
} getTabsInfo() {
this.tabsInfo = arr; return new Promise((resolve, reject) => {
resolve(); let view = uni.createSelectorQuery().in(this);
}); for (let i = 0; i < this.list.length; i++) {
}) view.select('.' + preId + i).boundingClientRect();
}, }
// 当swiper滑动结束,计算滑块最终要停留的位置 view.exec(res => {
countLine3Dx() { const arr = [];
const tab = this.tabsInfo[this.animationFinishCurrent]; for (let i = 0; i < res.length; i++) {
// 让滑块中心点和当前tab中心重合 // 给每个tab添加其文字颜色属性
if (tab) this.line3Dx = tab.left + tab.width / 2 - this.barWidthPx / 2; res[i].color = this.inactiveColor;
}, // 当前tab直接赋予activeColor
countPx() { if (i == this.getCurrent) res[i].color = this.activeColor;
// swiper宽度由rpx转为px单位,因为dx等,都是px单位 arr.push(res[i]);
this.sW = uni.upx2px(Number(this.swiperWidth)); }
}, this.tabsInfo = arr;
emit(index) { resolve();
this.$emit('change', index); });
}, })
change() { },
this.setScrollViewToCenter(); // 当swiper滑动结束,计算滑块最终要停留的位置
}, countLine3Dx() {
getQuery(cb) { const tab = this.tabsInfo[this.animationFinishCurrent];
try { // 让滑块中心点和当前tab中心重合
let view = uni.createSelectorQuery().in(this).select('.u-tabs'); if (tab) this.line3Dx = tab.left + tab.width / 2 - this.barWidthPx / 2;
view.fields( },
{ countPx() {
size: true // swiper宽度由rpx转为px单位,因为dx等,都是px单位
}, this.sW = uni.upx2px(Number(this.swiperWidth));
data => { },
if (data) { emit(index) {
this.componentsWidth = data.width; this.$emit('change', index);
if (cb && typeof cb === 'function') cb(data); },
} else { change() {
this.getQuery(cb); this.setScrollViewToCenter();
} },
} getQuery(cb) {
).exec(); try {
} catch (e) { let view = uni.createSelectorQuery().in(this).select('.u-tabs');
this.componentsWidth = windowWidth; view.fields({
} size: true
}, },
// 把活动tab移动到屏幕中心点 data => {
setScrollViewToCenter() { if (data) {
let tab; this.componentsWidth = data.width;
tab = this.tabsInfo[this.animationFinishCurrent]; if (cb && typeof cb === 'function') cb(data);
if (tab) { } else {
let tabCenter = tab.left + tab.width / 2; this.getQuery(cb);
let fatherWidth; }
// 活动tab移动到中心时,以屏幕还是tab组件为宽度为基准 }
if (this.autoCenterMode === 'window') { ).exec();
fatherWidth = windowWidth; } catch (e) {
} else { this.componentsWidth = windowWidth;
fatherWidth = this.componentsWidth; }
} },
this.scrollLeft = tabCenter - fatherWidth / 2; // 把活动tab移动到屏幕中心点
} setScrollViewToCenter() {
}, let tab;
setDx(dx) { tab = this.tabsInfo[this.animationFinishCurrent];
let nextTabIndex = dx > 0 ? this.animationFinishCurrent + 1 : this.animationFinishCurrent - 1; if (tab) {
// 判断索引是否超出边界 let tabCenter = tab.left + tab.width / 2;
nextTabIndex = nextTabIndex <= 0 ? 0 : nextTabIndex; let fatherWidth;
nextTabIndex = nextTabIndex >= this.list.length ? this.list.length - 1 : nextTabIndex; // 活动tab移动到中心时,以屏幕还是tab组件为宽度为基准
const tab = this.tabsInfo[nextTabIndex]; if (this.autoCenterMode === 'window') {
// 当前tab中心点x轴坐标 fatherWidth = windowWidth;
let nowTab = this.tabsInfo[this.animationFinishCurrent]; } else {
let nowTabX = nowTab.left + nowTab.width / 2; fatherWidth = this.componentsWidth;
// 下一个tab }
let nextTab = this.tabsInfo[nextTabIndex]; this.scrollLeft = tabCenter - fatherWidth / 2;
let nextTabX = nextTab.left + nextTab.width / 2; }
// 两个tab之间的距离,因为下一个tab可能在当前tab的左边或者右边,取绝对值即可 },
let distanceX = Math.abs(nextTabX - nowTabX); setDx(dx) {
this.line3AddDx = (dx / this.sW) * distanceX; let nextTabIndex = dx > 0 ? this.animationFinishCurrent + 1 : this.animationFinishCurrent - 1;
this.setTabColor(this.animationFinishCurrent, nextTabIndex, dx); // 判断索引是否超出边界
}, nextTabIndex = nextTabIndex <= 0 ? 0 : nextTabIndex;
// 设置tab的颜色 nextTabIndex = nextTabIndex >= this.list.length ? this.list.length - 1 : nextTabIndex;
setTabColor(nowTabIndex, nextTabIndex, dx) { const tab = this.tabsInfo[nextTabIndex];
let colorIndex = Math.abs(Math.ceil((dx / this.sW) * 100)); // 当前tab中心点x轴坐标
let colorLength = this.colorGradientArr.length; let nowTab = this.tabsInfo[this.animationFinishCurrent];
// 处理超出索引边界的情况 let nowTabX = nowTab.left + nowTab.width / 2;
colorIndex = colorIndex >= colorLength ? colorLength - 1 : colorIndex <= 0 ? 0 : colorIndex; // 下一个tab
// 设置下一个tab的颜色 let nextTab = this.tabsInfo[nextTabIndex];
this.tabsInfo[nextTabIndex].color = this.colorGradientArr[colorIndex]; let nextTabX = nextTab.left + nextTab.width / 2;
// 设置当前tab的颜色 // 两个tab之间的距离,因为下一个tab可能在当前tab的左边或者右边,取绝对值即可
this.tabsInfo[nowTabIndex].color = this.colorGradientArr[colorLength - 1 - colorIndex]; let distanceX = Math.abs(nextTabX - nowTabX);
}, this.line3AddDx = (dx / this.sW) * distanceX;
// swiper结束滑动 this.setTabColor(this.animationFinishCurrent, nextTabIndex, dx);
setFinishCurrent(current) { },
// 如果滑动的索引不一致,修改tab颜色变化,因为可能会有直接点击tab的情况 // 设置tab的颜色
if (current != this.animationFinishCurrent) { setTabColor(nowTabIndex, nextTabIndex, dx) {
this.tabsInfo.map((val, index) => { let colorIndex = Math.abs(Math.ceil((dx / this.sW) * 100));
if (current == index) val.color = this.activeColor; let colorLength = this.colorGradientArr.length;
else val.color = this.inactiveColor; // 处理超出索引边界的情况
return val; colorIndex = colorIndex >= colorLength ? colorLength - 1 : colorIndex <= 0 ? 0 : colorIndex;
}); // 设置下一个tab的颜色
} this.tabsInfo[nextTabIndex].color = this.colorGradientArr[colorIndex];
this.line3AddDx = 0; // 设置当前tab的颜色
this.animationFinishCurrent = current; this.tabsInfo[nowTabIndex].color = this.colorGradientArr[colorLength - 1 - colorIndex];
this.countLine3Dx(); },
} // swiper结束滑动
} setFinishCurrent(current) {
}; // 如果滑动的索引不一致,修改tab颜色变化,因为可能会有直接点击tab的情况
</script> if (current != this.animationFinishCurrent) {
this.tabsInfo.map((val, index) => {
<style scoped lang="scss"> if (current == index) val.color = this.activeColor;
view, else val.color = this.inactiveColor;
scroll-view { return val;
box-sizing: border-box; });
} }
this.line3AddDx = 0;
.u-tabs { this.animationFinishCurrent = current;
width: 100%; this.countLine3Dx();
transition-property: background-color, color; }
} }
};
::-webkit-scrollbar, </script>
::-webkit-scrollbar,
::-webkit-scrollbar { <style scoped lang="scss">
display: none; view,
width: 0 !important; scroll-view {
height: 0 !important; box-sizing: border-box;
-webkit-appearance: none; }
background: transparent;
} .u-tabs {
width: 100%;
/* #ifdef H5 */ transition-property: background-color, color;
// 通过样式穿透,隐藏H5下,scroll-view下的滚动条 }
scroll-view /deep/ ::-webkit-scrollbar {
display: none; ::-webkit-scrollbar,
width: 0 !important; ::-webkit-scrollbar,
height: 0 !important; ::-webkit-scrollbar {
-webkit-appearance: none; display: none;
background: transparent; width: 0 !important;
} height: 0 !important;
/* #endif */ -webkit-appearance: none;
background: transparent;
.u-scroll-view { }
width: 100%;
white-space: nowrap; /* #ifdef H5 */
position: relative; // 通过样式穿透,隐藏H5下,scroll-view下的滚动条
} scroll-view /deep/ ::-webkit-scrollbar {
display: none;
.u-tabs-scroll-box { width: 0 !important;
position: relative; height: 0 !important;
} -webkit-appearance: none;
background: transparent;
.u-tabs-scorll-flex { }
display: flex;
justify-content: space-between; /* #endif */
}
.u-scroll-view {
.u-tabs-scorll-flex .u-tabs-item { width: 100%;
flex: 1; white-space: nowrap;
} position: relative;
}
.u-tabs-item {
position: relative; .u-tabs-scroll-box {
display: inline-block; position: relative;
text-align: center; }
transition-property: background-color, color, font-weight;
} .u-tabs-scorll-flex {
display: flex;
.content { justify-content: space-between;
overflow: hidden; }
white-space: nowrap;
text-overflow: ellipsis; .u-tabs-scorll-flex .u-tabs-item {
} flex: 1;
}
.boxStyle {
pointer-events: none; .u-tabs-item {
position: absolute; position: relative;
transition-property: all; display: inline-block;
} text-align: center;
transition-property: background-color, color, font-weight;
.boxStyle2 { }
pointer-events: none;
position: absolute; .content {
bottom: 0; overflow: hidden;
transition-property: all; white-space: nowrap;
transform: translateY(-100%); text-overflow: ellipsis;
} }
.itemBackgroundBox { .boxStyle {
pointer-events: none; pointer-events: none;
position: absolute; position: absolute;
top: 0; transition-property: all;
transition-property: left, background-color; }
display: flex;
flex-direction: row; .boxStyle2 {
justify-content: center; pointer-events: none;
align-items: center; position: absolute;
} bottom: 0;
transition-property: all;
.itemBackground { transform: translateY(-100%);
height: 100%; }
width: 100%;
transition-property: all; .itemBackgroundBox {
} pointer-events: none;
position: absolute;
.u-scroll-bar { top: 0;
position: absolute; transition-property: left, background-color;
bottom: 4rpx; display: flex;
} flex-direction: row;
justify-content: center;
align-items: center;
}
.itemBackground {
height: 100%;
width: 100%;
transition-property: all;
}
.u-scroll-bar {
position: absolute;
bottom: 4rpx;
}
</style> </style>
<template> <template>
<view class="u-tabs" :id="id" :style="{ <view class="u-tabs" :id="id" :style="{
background: bgColor background: bgColor
}"> }">
<scroll-view scroll-x class="u-scroll-view" :scroll-left="scrollLeft" scroll-with-animation> <scroll-view scroll-x class="u-scroll-view" :scroll-left="scrollLeft" scroll-with-animation>
<view class="u-scroll-box" :class="{'u-tabs-scorll-flex': !isScroll}"> <view class="u-scroll-box" :class="{'u-tabs-scorll-flex': !isScroll}">
<view <view class="u-tab-item" :id="'u-tab-item-' + index" v-for="(item, index) in list" :key="index" @tap="clickTab(index)"
class="u-tab-item" :style="[tabItemStyle(index)]">
:id="'u-tab-item-' + index" {{ item[name] || item['name']}}
v-for="(item, index) in list" </view>
:key="index" <view class="u-tab-bar" :style="[tabBarStyle]"></view>
@tap="clickTab(index)" </view>
:style="[tabItemStyle(index)]" </scroll-view>
> </view>
{{ item[name] || item['name']}} </template>
</view>
<view class="u-tab-bar" :style="[tabBarStyle]"></view> <script>
</view> /**
</scroll-view> * alertTips 提示
</view> * @description 该组件,是一个tabs标签组件,在标签多的时候,可以配置为左右滑动,标签少的时候,可以禁止滑动。 该组件的一个特点是配置为滚动模式时,激活的tab会自动移动到组件的中间位置。
</template> * @tutorial https://www.uviewui.com/components/tabs.html
* @property {Boolean} is-scroll tabs是否可以左右拖动(默认true)
<script> * @property {Array} list 标签数组,元素为对象,如[{name: '推荐'}]
export default { * @property {String Number} current 指定哪个tab为激活状态(默认0)
props: { * @property {String Number} height 导航栏的高度,单位rpx(默认80)
// 导航菜单是否需要滚动,如只有2或者3个的时候,就不需要滚动了,此时使用flex平分tab的宽度 * @property {String Number} font-size tab文字大小,单位rpx(默认30)
isScroll: { * @property {String Number} duration 滑块移动一次所需的时间,单位秒(默认0.5)
type: Boolean, * @property {String} active-color 滑块和激活tab文字的颜色(默认#2979ff)
default: true * @property {String} inactive-color tabs文字颜色(默认#303133)
}, * @property {String Number} bar-width 滑块宽度,单位rpx(默认40)
//需循环的标签列表 * @property {String Number} bar-height 滑块高度,单位rpx(默认6)
list: { * @property {String Number} gutter 单个tab标签的左右内边距之和,单位rpx(默认40)
type: Array, * @property {String} bg-color tabs导航栏的背景颜色(默认#ffffff)
default() { * @property {String} name 组件内部读取的list参数中的属性名,见官网说明(默认name)
return []; * @property {Boolean} bold 激活选项的字体是否加粗(默认true)
} * @event {Function} change 点击标签时触发
}, * @example <u-tabs ref="tabs" :list="list" :is-scroll="false"></u-tabs>
// 当前活动tab的索引 */
current: { export default {
type: Number, name: "u-tabs",
default: 0 props: {
}, // 导航菜单是否需要滚动,如只有2或者3个的时候,就不需要滚动了,此时使用flex平分tab的宽度
// 导航栏的高度和行高 isScroll: {
height: { type: Boolean,
type: [String, Number], default: true
default: 80 },
}, //需循环的标签列表
// 字体大小 list: {
fontSize: { type: Array,
type: [String, Number], default () {
default: 30 return [];
}, }
// 过渡动画时长, 单位ms },
duration: { // 当前活动tab的索引
type: [String, Number], current: {
default: 0.5 type: Number,
}, default: 0
// 选中项的主题颜色 },
activeColor: { // 导航栏的高度和行高
type: String, height: {
default: '#2979ff' type: [String, Number],
}, default: 80
// 未选中项的颜色 },
inactiveColor: { // 字体大小
type: String, fontSize: {
default: '#303133' type: [String, Number],
}, default: 30
// 菜单底部移动的bar的宽度,单位rpx },
barWidth: { // 过渡动画时长, 单位ms
type: [String, Number], duration: {
default: 40 type: [String, Number],
}, default: 0.5
// 移动bar的高度 },
barHeight: { // 选中项的主题颜色
type: [String, Number], activeColor: {
default: 6 type: String,
}, default: '#2979ff'
// 单个tab的左或有内边距(左右相同) },
gutter: { // 未选中项的颜色
type: [String, Number], inactiveColor: {
default: 30 type: String,
}, default: '#303133'
// 导航栏的背景颜色 },
bgColor: { // 菜单底部移动的bar的宽度,单位rpx
type: String, barWidth: {
default: '#ffffff' type: [String, Number],
}, default: 40
// 读取传入的数组对象的属性 },
name: { // 移动bar的高度
type: String, barHeight: {
default: 'name' type: [String, Number],
}, default: 6
// 活动tab字体是否加粗 },
bold: { // 单个tab的左或有内边距(左右相同)
type: Boolean, gutter: {
default: true type: [String, Number],
} default: 30
}, },
data() { // 导航栏的背景颜色
return { bgColor: {
scrollLeft: 0, // 滚动scroll-view的左边滚动距离 type: String,
tabQueryInfo: [], // 存放对tab菜单查询后的节点信息 default: '#ffffff'
componentWidth: 0, // 屏幕宽度,单位为px },
scrollBarLeft: 0 ,// 移动bar需要通过translateX()移动的距离 // 读取传入的数组对象的属性
parentLeft: 0, // 父元素(tabs组件)到屏幕左边的距离 name: {
id: this.$u.guid(), // id值 type: String,
currentIndex: this.current, default: 'name'
}; },
}, // 活动tab字体是否加粗
watch: { bold: {
// 监听tab的变化,重新计算tab菜单的布局信息,因为实际使用中菜单可能是通过 type: Boolean,
// 后台获取的(如新闻app顶部的菜单),获取返回需要一定时间,所以list变化时,重新获取布局信息 default: true
list(n, o) { }
// 用$nextTick等待视图更新完毕后再计算tab的局部信息,否则可能因为tab还没生成就获取,就会有问题 },
this.$nextTick(() => { data() {
this.init(); return {
}); scrollLeft: 0, // 滚动scroll-view的左边滚动距离
}, tabQueryInfo: [], // 存放对tab菜单查询后的节点信息
current: { componentWidth: 0, // 屏幕宽度,单位为px
immediate: true, scrollBarLeft: 0, // 移动bar需要通过translateX()移动的距离
handler(nVal) { parentLeft: 0, // 父元素(tabs组件)到屏幕左边的距离
// 视图更新后再执行移动操作 id: this.$u.guid(), // id值
this.$nextTick(() => { currentIndex: this.current,
this.currentIndex = nVal; };
this.scrollByIndex(); },
}); watch: {
} // 监听tab的变化,重新计算tab菜单的布局信息,因为实际使用中菜单可能是通过
}, // 后台获取的(如新闻app顶部的菜单),获取返回需要一定时间,所以list变化时,重新获取布局信息
}, list(n, o) {
computed: { // 用$nextTick等待视图更新完毕后再计算tab的局部信息,否则可能因为tab还没生成就获取,就会有问题
// 移动bar的样式 this.$nextTick(() => {
tabBarStyle() { this.init();
return { });
width: this.barWidth + 'rpx', },
transform: `translate(${this.scrollBarLeft}px, -100%)`, current: {
'transition-duration': `${this.duration}s`, immediate: true,
'background-color': this.activeColor, handler(nVal) {
height: this.barHeight + 'rpx', // 视图更新后再执行移动操作
// 设置一个很大的值,它会自动取能用的最大值,不用高度的一半,是因为高度可能是单数,会有小数出现 this.$nextTick(() => {
'border-radius': `${this.barHeight / 2}px` this.currentIndex = nVal;
}; this.scrollByIndex();
}, });
// tab的样式 }
tabItemStyle() { },
return (index) => { },
let style = { computed: {
height: this.height + 'rpx', // 移动bar的样式
'line-height': this.height + 'rpx', tabBarStyle() {
'font-size': this.fontSize + 'rpx', return {
'transition-duration': `${this.duration}s`, width: this.barWidth + 'rpx',
padding: this.isScroll ? `0 ${this.gutter}rpx` : '', transform: `translate(${this.scrollBarLeft}px, -100%)`,
}; 'transition-duration': `${this.duration}s`,
// 字体加粗 'background-color': this.activeColor,
if (index == this.currentIndex && this.bold) style.fontWeight = 'bold'; height: this.barHeight + 'rpx',
if(index == this.currentIndex) { // 设置一个很大的值,它会自动取能用的最大值,不用高度的一半,是因为高度可能是单数,会有小数出现
style.color = this.activeColor; 'border-radius': `${this.barHeight / 2}px`
} else { };
style.color = this.inactiveColor; },
} // tab的样式
return style; tabItemStyle() {
} return (index) => {
} let style = {
}, height: this.height + 'rpx',
methods: { 'line-height': this.height + 'rpx',
// 设置一个init方法,方便多处调用 'font-size': this.fontSize + 'rpx',
async init() { 'transition-duration': `${this.duration}s`,
// 获取tabs组件的尺寸信息 padding: this.isScroll ? `0 ${this.gutter}rpx` : '',
let tabRect = await this.$uGetRect('#' + this.id); };
// tabs组件距离屏幕左边的宽度 // 字体加粗
this.parentLeft = tabRect.left; if (index == this.currentIndex && this.bold) style.fontWeight = 'bold';
// tabs组件的宽度 if (index == this.currentIndex) {
this.componentWidth = tabRect.width; style.color = this.activeColor;
this.getTabRect(); } else {
}, style.color = this.inactiveColor;
// 点击某一个tab菜单 }
clickTab(index) { return style;
// 发送事件给父组件 }
this.$emit('change', index); }
}, },
// 查询tab的布局信息 methods: {
getTabRect() { // 设置一个init方法,方便多处调用
// 创建节点查询 async init() {
let query = uni.createSelectorQuery().in(this); // 获取tabs组件的尺寸信息
// 历遍所有tab,这里是执行了查询,最终使用exec()会一次性返回查询的数组结果 let tabRect = await this.$uGetRect('#' + this.id);
for (let i = 0; i < this.list.length; i++) { // tabs组件距离屏幕左边的宽度
// 只要size和rect两个参数 this.parentLeft = tabRect.left;
query.select(`#u-tab-item-${i}`).fields({ // tabs组件的宽度
size: true, this.componentWidth = tabRect.width;
rect: true this.getTabRect();
}); },
} // 点击某一个tab菜单
// 执行查询,一次性获取多个结果 clickTab(index) {
query.exec( // 发送事件给父组件
function(res) { this.$emit('change', index);
this.tabQueryInfo = res; },
// 初始化滚动条和移动bar的位置 // 查询tab的布局信息
this.scrollByIndex(); getTabRect() {
}.bind(this) // 创建节点查询
); let query = uni.createSelectorQuery().in(this);
}, // 历遍所有tab,这里是执行了查询,最终使用exec()会一次性返回查询的数组结果
// 滚动scroll-view,让活动的tab处于屏幕的中间位置 for (let i = 0; i < this.list.length; i++) {
scrollByIndex() { // 只要size和rect两个参数
// 当前活动tab的布局信息,有tab菜单的width和left(为元素左边界到父元素左边界的距离)等信息 query.select(`#u-tab-item-${i}`).fields({
let tabInfo = this.tabQueryInfo[this.currentIndex]; size: true,
if (!tabInfo) return; rect: true
// 活动tab的宽度 });
let tabWidth = tabInfo.width; }
// 活动item的左边到tabs组件左边的距离,用item的left减去tabs的left // 执行查询,一次性获取多个结果
let offsetLeft = tabInfo.left - this.parentLeft; query.exec(
// 将活动的tabs-item移动到屏幕正中间,实际上是对scroll-view的移动 function(res) {
let scrollLeft = offsetLeft - (this.componentWidth - tabWidth) / 2; this.tabQueryInfo = res;
this.scrollLeft = scrollLeft < 0 ? 0 : scrollLeft; // 初始化滚动条和移动bar的位置
// 当前活动item的中点点到左边的距离减去滑块宽度的一半,即可得到滑块所需的移动距离 this.scrollByIndex();
let left = tabInfo.left + tabInfo.width / 2 - this.parentLeft; }.bind(this)
// 计算当前活跃item到组件左边的距离 );
this.scrollBarLeft = left - uni.upx2px(this.barWidth) / 2; },
} // 滚动scroll-view,让活动的tab处于屏幕的中间位置
}, scrollByIndex() {
mounted() { // 当前活动tab的布局信息,有tab菜单的width和left(为元素左边界到父元素左边界的距离)等信息
this.init(); let tabInfo = this.tabQueryInfo[this.currentIndex];
} if (!tabInfo) return;
}; // 活动tab的宽度
</script> let tabWidth = tabInfo.width;
// 活动item的左边到tabs组件左边的距离,用item的left减去tabs的left
<style lang="scss"> let offsetLeft = tabInfo.left - this.parentLeft;
view, // 将活动的tabs-item移动到屏幕正中间,实际上是对scroll-view的移动
scroll-view { let scrollLeft = offsetLeft - (this.componentWidth - tabWidth) / 2;
box-sizing: border-box; this.scrollLeft = scrollLeft < 0 ? 0 : scrollLeft;
} // 当前活动item的中点点到左边的距离减去滑块宽度的一半,即可得到滑块所需的移动距离
let left = tabInfo.left + tabInfo.width / 2 - this.parentLeft;
::-webkit-scrollbar, // 计算当前活跃item到组件左边的距离
::-webkit-scrollbar, this.scrollBarLeft = left - uni.upx2px(this.barWidth) / 2;
::-webkit-scrollbar { }
display: none; },
width: 0 !important; mounted() {
height: 0 !important; this.init();
-webkit-appearance: none; }
background: transparent; };
} </script>
.u-scroll-box { <style lang="scss">
position: relative; view,
} scroll-view {
box-sizing: border-box;
/* #ifdef H5 */ }
// 通过样式穿透,隐藏H5下,scroll-view下的滚动条
scroll-view /deep/ ::-webkit-scrollbar { ::-webkit-scrollbar,
display: none; ::-webkit-scrollbar,
width: 0 !important; ::-webkit-scrollbar {
height: 0 !important; display: none;
-webkit-appearance: none; width: 0 !important;
background: transparent; height: 0 !important;
} -webkit-appearance: none;
/* #endif */ background: transparent;
}
.u-scroll-view {
width: 100%; .u-scroll-box {
white-space: nowrap; position: relative;
position: relative; }
}
/* #ifdef H5 */
.u-tab-item { // 通过样式穿透,隐藏H5下,scroll-view下的滚动条
position: relative; scroll-view /deep/ ::-webkit-scrollbar {
display: inline-block; display: none;
text-align: center; width: 0 !important;
transition-property: background-color, color; height: 0 !important;
} -webkit-appearance: none;
background: transparent;
.u-tab-bar { }
position: absolute;
bottom: 0; /* #endif */
}
.u-scroll-view {
.u-tabs-scorll-flex { width: 100%;
display: flex; white-space: nowrap;
justify-content: space-between; position: relative;
} }
.u-tab-item {
position: relative;
display: inline-block;
text-align: center;
transition-property: background-color, color;
}
.u-tab-bar {
position: absolute;
bottom: 0;
}
.u-tabs-scorll-flex {
display: flex;
justify-content: space-between;
}
</style> </style>
<template> <template>
<view v-if="show" :class="[ <view v-if="show" :class="[
disabled ? 'u-disabled' : '', disabled ? 'u-disabled' : '',
'u-size-' + size, 'u-size-' + size,
'u-shape-' + shape, 'u-shape-' + shape,
'u-mode-' + mode + '-' + type 'u-mode-' + mode + '-' + type
]" ]"
class="u-tag" :style="[customStyle]" @tap="clickTag"> class="u-tag" :style="[customStyle]" @tap="clickTag">
{{text}} {{text}}
<view class="u-icon-wrap" @tap.stop> <view class="u-icon-wrap" @tap.stop>
<u-icon @click="close" size="22" v-if="closeable" name="close" class="u-close-icon" :style="[iconStyle]"></u-icon> <u-icon @click="close" size="22" v-if="closeable" name="close" class="u-close-icon" :style="[iconStyle]"></u-icon>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
export default { /**
// 是否禁用这个标签,禁用的话,会屏蔽点击事件 * alertTips 提示
props: { * @description 该组件一般用于标记和选择
// 标签类型info、primary、success、warning、error * @tutorial https://www.uviewui.com/components/tag.html
type: { * @property {String} type 主题类型(默认primary)
type: String, * @property {String} size 标签大小(默认default)
default: 'primary' * @property {String} shape 标签形状(默认square)
}, * @property {String} text 标签的文字内容
disabled: { * @property {String} bg-color 自定义标签的背景颜色
type: [Boolean, String], * @property {String} border-color 标签的边框颜色
default: false * @property {String} close-color 关闭按钮的颜色
}, * @property {String Number} index 点击标签时,会通过click事件返回该值
// 标签的大小,分为default(默认),mini(较小) * @property {String} mode 模式选择,见官网说明(默认light)
size: { * @property {Boolean} closeable 是否可关闭,设置为true,文字右边会出现一个关闭图标(默认false)
type: String, * @property {Boolean} show 标签显示与否(默认true)
default: 'default' * @event {Function} click 点击标签触发
}, * @event {Function} close closeable为true时,点击标签关闭按钮触发
// tag的形状,circle(两边半圆形), square(方形,带圆角),circleLeft(左边是半圆),circleRight(右边是半圆) * @example <u-tag text="雪月夜" type="success" />
shape: { */
type: String, export default {
default: 'square' name: "u-tag",
}, // 是否禁用这个标签,禁用的话,会屏蔽点击事件
// 标签文字 props: {
text: { // 标签类型info、primary、success、warning、error
type: String, type: {
default: '' type: String,
}, default: 'primary'
// 背景颜色,默认为空字符串,即不处理 },
bgColor: { disabled: {
type: String, type: [Boolean, String],
default: '' default: false
}, },
// 标签字体颜色,默认为空字符串,即不处理 // 标签的大小,分为default(默认),mini(较小)
color: { size: {
type: String, type: String,
default: '' default: 'default'
}, },
// 镂空形式标签的边框颜色 // tag的形状,circle(两边半圆形), square(方形,带圆角),circleLeft(左边是半圆),circleRight(右边是半圆)
borderColor: { shape: {
type: String, type: String,
default: '' default: 'square'
}, },
// 关闭按钮图标的颜色 // 标签文字
closeColor: { text: {
type: String, type: String,
default: '' default: ''
}, },
// 点击时返回的索引值,用于区分例遍的数组哪个元素被点击了 // 背景颜色,默认为空字符串,即不处理
index: { bgColor: {
type: [Number, String], type: String,
default: '' default: ''
}, },
// 模式选择,dark|light|plain // 标签字体颜色,默认为空字符串,即不处理
mode: { color: {
type: String, type: String,
default: 'light' default: ''
}, },
// 是否可关闭 // 镂空形式标签的边框颜色
closeable: { borderColor: {
type: Boolean, type: String,
default: false default: ''
}, },
// 是否显示 // 关闭按钮图标的颜色
show: { closeColor: {
type: Boolean, type: String,
default: true default: ''
} },
}, // 点击时返回的索引值,用于区分例遍的数组哪个元素被点击了
data() { index: {
return { type: [Number, String],
default: ''
} },
}, // 模式选择,dark|light|plain
computed: { mode: {
customStyle() { type: String,
let style = {}; default: 'light'
// 文字颜色(如果有此值,会覆盖type值的颜色) },
if(this.color) style.color = this.color+"!important"; // 是否可关闭
// tag的背景颜色(如果有此值,会覆盖type值的颜色) closeable: {
if(this.bgColor) style.backgroundColor = this.bgColor+"!important"; type: Boolean,
// 如果是镂空型tag,没有传递边框颜色(borderColor)的话,使用文字的颜色(color属性) default: false
if(this.plain && this.color && !this.borderColor) style.borderColor = this.color; },
else style.borderColor = this.borderColor; // 是否显示
return style; show: {
}, type: Boolean,
iconStyle() { default: true
if(!this.closeable) return ; }
let style = {}; },
if(this.size == 'mini') style.fontSize = '20rpx'; data() {
else style.fontSize = '22rpx'; return {
if(this.mode == 'plain' || this.mode == 'light') style.color = this.$u.color[this.type];
else if(this.mode == 'dark') style.color = "#ffffff"; }
if(this.closeColor) style.color = this.closeColor; },
return style; computed: {
} customStyle() {
}, let style = {};
methods: { // 文字颜色(如果有此值,会覆盖type值的颜色)
// 标签被点击 if (this.color) style.color = this.color + "!important";
clickTag() { // tag的背景颜色(如果有此值,会覆盖type值的颜色)
// 如果是disabled状态,不发送点击事件 if (this.bgColor) style.backgroundColor = this.bgColor + "!important";
if(this.disabled) return ; // 如果是镂空型tag,没有传递边框颜色(borderColor)的话,使用文字的颜色(color属性)
this.$emit('click', this.index); if (this.plain && this.color && !this.borderColor) style.borderColor = this.color;
}, else style.borderColor = this.borderColor;
// 点击标签关闭按钮 return style;
close() { },
this.$emit('close', this.index); iconStyle() {
} if (!this.closeable) return;
} let style = {};
} if (this.size == 'mini') style.fontSize = '20rpx';
</script> else style.fontSize = '22rpx';
if (this.mode == 'plain' || this.mode == 'light') style.color = this.$u.color[this.type];
<style lang="scss" scoped> else if (this.mode == 'dark') style.color = "#ffffff";
.u-tag { if (this.closeColor) style.color = this.closeColor;
box-sizing: border-box; return style;
align-items: center; }
border-radius: 6rpx; },
display: inline-block; methods: {
line-height: 1; // 标签被点击
} clickTag() {
// 如果是disabled状态,不发送点击事件
.u-size-default { if (this.disabled) return;
font-size: 22rpx; this.$emit('click', this.index);
padding: 12rpx 22rpx; },
} // 点击标签关闭按钮
close() {
.u-size-mini { this.$emit('close', this.index);
font-size: 20rpx; }
padding: 6rpx 12rpx; }
} }
</script>
.u-mode-light-primary {
background-color: $u-type-primary-light; <style lang="scss" scoped>
color: $u-type-primary; .u-tag {
border: 1px solid rgb(215, 234, 254); box-sizing: border-box;
} align-items: center;
border-radius: 6rpx;
.u-mode-light-success { display: inline-block;
background-color: $u-type-success-light; line-height: 1;
color: $u-type-success; }
border: 1px solid #BEF5C8;
} .u-size-default {
font-size: 22rpx;
.u-mode-light-error { padding: 12rpx 22rpx;
background-color: $u-type-error-light; }
color: $u-type-error;
border: 1px solid #fde2e2; .u-size-mini {
} font-size: 20rpx;
padding: 6rpx 12rpx;
.u-mode-light-warning { }
background-color: $u-type-warning-light;
color: $u-type-warning; .u-mode-light-primary {
border: 1px solid #faecd8; background-color: $u-type-primary-light;
} color: $u-type-primary;
border: 1px solid rgb(215, 234, 254);
.u-mode-light-info { }
background-color: $u-type-info-light;
color: $u-type-info; .u-mode-light-success {
border: 1px solid #ebeef5; background-color: $u-type-success-light;
} color: $u-type-success;
border: 1px solid #BEF5C8;
.u-mode-dark-primary { }
background-color: $u-type-primary;
color: #FFFFFF; .u-mode-light-error {
} background-color: $u-type-error-light;
color: $u-type-error;
.u-mode-dark-success { border: 1px solid #fde2e2;
background-color: $u-type-success; }
color: #FFFFFF;
} .u-mode-light-warning {
background-color: $u-type-warning-light;
.u-mode-dark-error { color: $u-type-warning;
background-color: $u-type-error; border: 1px solid #faecd8;
color: #FFFFFF; }
}
.u-mode-light-info {
.u-mode-dark-warning { background-color: $u-type-info-light;
background-color: $u-type-warning; color: $u-type-info;
color: #FFFFFF; border: 1px solid #ebeef5;
} }
.u-mode-dark-info { .u-mode-dark-primary {
background-color: $u-type-info; background-color: $u-type-primary;
color: #FFFFFF; color: #FFFFFF;
} }
.u-mode-plain-primary { .u-mode-dark-success {
background-color: #FFFFFF; background-color: $u-type-success;
color: $u-type-primary; color: #FFFFFF;
border: 1px solid $u-type-primary; }
}
.u-mode-dark-error {
.u-mode-plain-success { background-color: $u-type-error;
background-color: #FFFFFF; color: #FFFFFF;
color: $u-type-success; }
border: 1px solid $u-type-success;
} .u-mode-dark-warning {
background-color: $u-type-warning;
.u-mode-plain-error { color: #FFFFFF;
background-color: #FFFFFF; }
color: $u-type-error;
border: 1px solid $u-type-error; .u-mode-dark-info {
} background-color: $u-type-info;
color: #FFFFFF;
.u-mode-plain-warning { }
background-color: #FFFFFF;
color: $u-type-warning; .u-mode-plain-primary {
border: 1px solid $u-type-warning; background-color: #FFFFFF;
} color: $u-type-primary;
border: 1px solid $u-type-primary;
.u-mode-plain-info { }
background-color: #FFFFFF;
color: $u-type-info; .u-mode-plain-success {
border: 1px solid $u-type-info; background-color: #FFFFFF;
} color: $u-type-success;
border: 1px solid $u-type-success;
.u-disabled { }
opacity: 0.55;
} .u-mode-plain-error {
background-color: #FFFFFF;
.u-shape-circle { color: $u-type-error;
border-radius: 100rpx; border: 1px solid $u-type-error;
} }
.u-shape-circleRight { .u-mode-plain-warning {
border-radius: 0 100rpx 100rpx 0; background-color: #FFFFFF;
} color: $u-type-warning;
border: 1px solid $u-type-warning;
.u-shape-circleLeft { }
border-radius: 100rpx 0 0 100rpx;
} .u-mode-plain-info {
background-color: #FFFFFF;
.u-close-icon { color: $u-type-info;
margin-left: 14rpx; border: 1px solid $u-type-info;
font-size: 22rpx; }
color: $u-type-success;
} .u-disabled {
opacity: 0.55;
.u-icon-wrap { }
display: inline-flex;
transform: scale(0.86); .u-shape-circle {
} border-radius: 100rpx;
}
.u-shape-circleRight {
border-radius: 0 100rpx 100rpx 0;
}
.u-shape-circleLeft {
border-radius: 100rpx 0 0 100rpx;
}
.u-close-icon {
margin-left: 14rpx;
font-size: 22rpx;
color: $u-type-success;
}
.u-icon-wrap {
display: inline-flex;
transform: scale(0.86);
}
</style> </style>
<template> <template>
<view class="u-td" :style="[tdStyle]"> <view class="u-td" :style="[tdStyle]">
<slot></slot> <slot></slot>
</view> </view>
</template> </template>
<script> <script>
export default { /**
props: { * alertTips 提示
// 宽度,百分比或者具体带单位的值,如30%, 200rpx等,一般使用百分比 * @description 表格组件一般用于展示大量结构化数据的场景(搭配<u-table>使用)
width: { * @tutorial https://www.uviewui.com/components/table.html#td-props
type: [Number, String], * @property {String Number} width 单元格宽度百分比或者具体带单位的值,如30%, 200rpx等,一般使用百分比,单元格宽度默认为均分tr的长度(默认auto)
default: 'auto' * @example <u-td>二年级</u-td>
} */
}, export default {
data() { name: "u-td",
return { props: {
tr: [] // 宽度,百分比或者具体带单位的值,如30%, 200rpx等,一般使用百分比
}; width: {
}, type: [Number, String],
inject: ['uTable', 'uTr'], default: 'auto'
provide() { }
return { },
uTd: this data() {
} return {
}, tr: []
created() { };
},
}, inject: ['uTable', 'uTr'],
computed: { provide() {
tdStyle() { return {
let style = {}; uTd: this
if(this.width != "auto") style.flex = `0 0 ${this.width}`; }
style.textAlign = this.uTable.align; },
style.padding = this.tr.length == 0 ? this.uTable.padding : 0; created() {
style.borderBottom = this.tr.length == 0 ? `solid 1px ${this.uTable.borderColor}` : 0;
style.borderRight = this.tr.length == 0 ? `solid 1px ${this.uTable.borderColor}` : 0; },
style.fontSize = this.uTable.fontSize + 'rpx'; computed: {
style.color = this.uTable.color; tdStyle() {
return style; let style = {};
} if (this.width != "auto") style.flex = `0 0 ${this.width}`;
} style.textAlign = this.uTable.align;
}; style.padding = this.tr.length == 0 ? this.uTable.padding : 0;
</script> style.borderBottom = this.tr.length == 0 ? `solid 1px ${this.uTable.borderColor}` : 0;
style.borderRight = this.tr.length == 0 ? `solid 1px ${this.uTable.borderColor}` : 0;
<style lang="scss" scoped> style.fontSize = this.uTable.fontSize + 'rpx';
.u-td { style.color = this.uTable.color;
display: flex; return style;
flex-direction: column; }
flex: 1; }
justify-content: center; };
font-size: 28rpx; </script>
color: $u-content-color;
align-self: stretch; <style lang="scss" scoped>
box-sizing: border-box; .u-td {
} display: flex;
flex-direction: column;
.u-col-1 { flex: 1;
flex: 0 0 calc(100%/12); justify-content: center;
} font-size: 28rpx;
color: $u-content-color;
.u-col-2 { align-self: stretch;
flex: 0 0 calc(100%/12 * 2); box-sizing: border-box;
} }
.u-col-3 { .u-col-1 {
flex: 0 0 calc(100%/12 * 3); flex: 0 0 calc(100%/12);
} }
.u-col-4 { .u-col-2 {
flex: 0 0 calc(100%/12 * 4); flex: 0 0 calc(100%/12 * 2);
} }
.u-col-5 { .u-col-3 {
flex: 0 0 calc(100%/12 * 5); flex: 0 0 calc(100%/12 * 3);
} }
.u-col-6 { .u-col-4 {
flex: 0 0 calc(100%/12 * 6); flex: 0 0 calc(100%/12 * 4);
} }
.u-col-7 { .u-col-5 {
flex: 0 0 calc(100%/12 * 7); flex: 0 0 calc(100%/12 * 5);
} }
.u-col-8 { .u-col-6 {
flex: 0 0 calc(100%/12 * 8); flex: 0 0 calc(100%/12 * 6);
} }
.u-col-9 { .u-col-7 {
flex: 0 0 calc(100%/12 * 9); flex: 0 0 calc(100%/12 * 7);
} }
.u-col-10 { .u-col-8 {
flex: 0 0 calc(100%/12 * 10); flex: 0 0 calc(100%/12 * 8);
} }
.u-col-11 { .u-col-9 {
flex: 0 0 calc(100%/12 * 11); flex: 0 0 calc(100%/12 * 9);
} }
.u-col-12 { .u-col-10 {
flex: 0 0 calc(100%/12 * 12); flex: 0 0 calc(100%/12 * 10);
} }
.u-col-11 {
flex: 0 0 calc(100%/12 * 11);
}
.u-col-12 {
flex: 0 0 calc(100%/12 * 12);
}
</style> </style>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment