Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
U
uview-ui
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
李晖
uview-ui
Commits
4a3a32b8
Commit
4a3a32b8
authored
Apr 23, 2020
by
beiqiao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
语法提示,文档说明,进一步更新。
parent
da09bd81
Changes
22
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
3739 additions
and
3497 deletions
+3739
-3497
u-notice-bar.vue
uview/components/u-notice-bar/u-notice-bar.vue
+2
-2
u-skeleton.vue
uview/components/u-skeleton/u-skeleton.vue
+10
-10
u-steps.vue
uview/components/u-steps/u-steps.vue
+139
-126
u-sticky.vue
uview/components/u-sticky/u-sticky.vue
+150
-136
u-subsection.vue
uview/components/u-subsection/u-subsection.vue
+347
-335
u-swipe-action.vue
uview/components/u-swipe-action/u-swipe-action.vue
+218
-211
u-swiper.vue
uview/components/u-swiper/u-swiper.vue
+290
-290
u-switch.vue
uview/components/u-switch/u-switch.vue
+156
-147
u-table.vue
uview/components/u-table/u-table.vue
+91
-76
u-tabs-swiper.vue
uview/components/u-tabs-swiper/u-tabs-swiper.vue
+435
-421
u-tabs.vue
uview/components/u-tabs/u-tabs.vue
+294
-277
u-tag.vue
uview/components/u-tag/u-tag.vue
+277
-257
u-td.vue
uview/components/u-td/u-td.vue
+112
-104
u-th.vue
uview/components/u-th/u-th.vue
+55
-47
u-time-line-item.vue
uview/components/u-time-line-item/u-time-line-item.vue
+80
-71
u-time-line.vue
uview/components/u-time-line/u-time-line.vue
+40
-33
u-toast.vue
uview/components/u-toast/u-toast.vue
+210
-201
u-top-tips.vue
uview/components/u-top-tips/u-top-tips.vue
+118
-109
u-tr.vue
uview/components/u-tr/u-tr.vue
+33
-26
u-upload.vue
uview/components/u-upload/u-upload.vue
+460
-419
u-verification-code.vue
uview/components/u-verification-code/u-verification-code.vue
+99
-85
u-waterfall.vue
uview/components/u-waterfall/u-waterfall.vue
+123
-114
No files found.
uview/components/u-notice-bar/u-notice-bar.vue
View file @
4a3a32b8
...
@@ -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 播放状态,p
aly - 播放,paused - 暂停(默认pal
y)
* @property {String} play-state 播放状态,p
lay - 播放,paused - 暂停(默认pla
y)
* @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
},
},
// 播放状态,p
al
y-播放,paused-暂停
// 播放状态,p
la
y-播放,paused-暂停
playState
:
{
playState
:
{
type
:
String
,
type
:
String
,
default
:
'
play
'
default
:
'
play
'
...
...
uview/components/u-skeleton/u-skeleton.vue
View file @
4a3a32b8
...
@@ -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
"
,
...
...
uview/components/u-steps/u-steps.vue
View file @
4a3a32b8
<
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
>
uview/components/u-sticky/u-sticky.vue
View file @
4a3a32b8
<
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
>
uview/components/u-subsection/u-subsection.vue
View file @
4a3a32b8
<
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
>
uview/components/u-swipe-action/u-swipe-action.vue
View file @
4a3a32b8
<
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
>
uview/components/u-swiper/u-swiper.vue
View file @
4a3a32b8
<
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
>
uview/components/u-switch/u-switch.vue
View file @
4a3a32b8
<
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
>
uview/components/u-table/u-table.vue
View file @
4a3a32b8
<
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
>
uview/components/u-tabs-swiper/u-tabs-swiper.vue
View file @
4a3a32b8
<
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
>
uview/components/u-tabs/u-tabs.vue
View file @
4a3a32b8
<
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
>
uview/components/u-tag/u-tag.vue
View file @
4a3a32b8
<
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
>
uview/components/u-td/u-td.vue
View file @
4a3a32b8
<
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
>
uview/components/u-th/u-th.vue
View file @
4a3a32b8
<
template
>
<
template
>
<view
class=
"u-th"
:style=
"[thStyle]"
>
<view
class=
"u-th"
:style=
"[thStyle]"
>
<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的长度
default
:
''
* @example
}
*/
},
export
default
{
data
()
{
name
:
"
u-th
"
,
return
{
props
:
{
// 宽度,百分比或者具体带单位的值,如30%, 200rpx等,一般使用百分比
};
width
:
{
},
type
:
[
Number
,
String
],
inject
:
[
'
uTable
'
],
default
:
''
computed
:
{
}
thStyle
()
{
},
let
style
=
{};
data
()
{
if
(
this
.
width
)
style
.
flex
=
`0 0
${
this
.
width
}
%`
;
return
{
style
.
textAlign
=
this
.
uTable
.
align
;
style
.
padding
=
this
.
uTable
.
padding
;
};
style
.
borderBottom
=
`solid 1px
${
this
.
uTable
.
borderColor
}
`
;
},
style
.
borderRight
=
`solid 1px
${
this
.
uTable
.
borderColor
}
`
;
inject
:
[
'
uTable
'
],
Object
.
assign
(
style
,
this
.
uTable
.
thStyle
);
computed
:
{
return
style
;
thStyle
()
{
}
let
style
=
{};
}
if
(
this
.
width
)
style
.
flex
=
`0 0
${
this
.
width
}
%`
;
};
style
.
textAlign
=
this
.
uTable
.
align
;
</
script
>
style
.
padding
=
this
.
uTable
.
padding
;
style
.
borderBottom
=
`solid 1px
${
this
.
uTable
.
borderColor
}
`
;
<
style
lang=
"scss"
scoped
>
style
.
borderRight
=
`solid 1px
${
this
.
uTable
.
borderColor
}
`
;
.u-th
{
Object
.
assign
(
style
,
this
.
uTable
.
thStyle
);
display
:
flex
;
return
style
;
flex-direction
:
column
;
}
flex
:
1
;
}
justify-content
:
center
;
};
font-size
:
28rpx
;
</
script
>
color
:
$u-main-color
;
font-weight
:
bold
;
<
style
lang=
"scss"
scoped
>
background-color
:
rgb
(
245
,
246
,
248
);
.u-th
{
}
display
:
flex
;
flex-direction
:
column
;
flex
:
1
;
justify-content
:
center
;
font-size
:
28rpx
;
color
:
$u-main-color
;
font-weight
:
bold
;
background-color
:
rgb
(
245
,
246
,
248
);
}
</
style
>
</
style
>
uview/components/u-time-line-item/u-time-line-item.vue
View file @
4a3a32b8
<
template
>
<
template
>
<view
class=
"u-time-axis-item"
>
<view
class=
"u-time-axis-item"
>
<slot
name=
"content"
/>
<slot
name=
"content"
/>
<view
class=
"u-time-axis-node"
:style=
"[nodeStyle]"
>
<view
class=
"u-time-axis-node"
:style=
"[nodeStyle]"
>
<slot
name=
"node"
>
<slot
name=
"node"
>
<view
class=
"u-dot"
>
<view
class=
"u-dot"
>
</view>
</view>
</slot>
</slot>
</view>
</view>
</view>
</view>
</
template
>
</
template
>
<
script
>
<
script
>
export
default
{
/**
props
:
{
* alertTips 提示
// 节点的背景颜色
* @description 时间轴组件一般用于物流信息展示,各种跟时间相关的记录等场景。(搭配<u-time-line>使用)
bgColor
:
{
* @tutorial https://www.uviewui.com/components/timeLine.html
type
:
String
,
* @property {String} bg-color 左边节点的背景颜色,一般通过slot内容自定义背景颜色即可(默认#ffffff)
default
:
"
#ffffff
"
* @property {String Number} node-top 节点左边图标绝对定位的top值,单位rpx
},
* @example <u-time-line-item node-top="2">...</u-time-line-item>
// 节点左边图标绝对定位的top值
*/
nodeTop
:
{
export
default
{
type
:
[
String
,
Number
],
name
:
"
u-time-line-item
"
,
default
:
""
props
:
{
}
// 节点的背景颜色
},
bgColor
:
{
data
()
{
type
:
String
,
return
{
default
:
"
#ffffff
"
},
}
// 节点左边图标绝对定位的top值
},
nodeTop
:
{
computed
:
{
type
:
[
String
,
Number
],
nodeStyle
()
{
default
:
""
let
style
=
{
}
backgroundColor
:
this
.
bgColor
,
},
};
data
()
{
if
(
this
.
nodeTop
!=
""
)
style
.
top
=
this
.
nodeTop
+
'
rpx
'
;
return
{
return
style
;
}
}
}
},
}
computed
:
{
</
script
>
nodeStyle
()
{
let
style
=
{
<
style
lang=
"scss"
scoped
>
backgroundColor
:
this
.
bgColor
,
.u-time-axis-item
{
};
display
:
flex
;
if
(
this
.
nodeTop
!=
""
)
style
.
top
=
this
.
nodeTop
+
'
rpx
'
;
flex-direction
:
column
;
return
style
;
width
:
100%
;
}
position
:
relative
;
}
margin-bottom
:
32rpx
;
}
}
</
script
>
.u-time-axis-node
{
<
style
lang=
"scss"
scoped
>
position
:
absolute
;
.u-time-axis-item
{
top
:
12rpx
;
display
:
flex
;
left
:
-40rpx
;
flex-direction
:
column
;
transform-origin
:
0
;
width
:
100%
;
transform
:
translateX
(
-50%
);
position
:
relative
;
display
:
flex
;
margin-bottom
:
32rpx
;
align-items
:
center
;
}
justify-content
:
center
;
z-index
:
100
;
.u-time-axis-node
{
font-size
:
24rpx
;
position
:
absolute
;
}
top
:
12rpx
;
left
:
-40rpx
;
.u-dot
{
transform-origin
:
0
;
height
:
16rpx
;
transform
:
translateX
(
-50%
);
width
:
16rpx
;
display
:
flex
;
border-radius
:
100rpx
;
align-items
:
center
;
background
:
#ddd
;
justify-content
:
center
;
}
z-index
:
100
;
font-size
:
24rpx
;
}
.u-dot
{
height
:
16rpx
;
width
:
16rpx
;
border-radius
:
100rpx
;
background
:
#ddd
;
}
</
style
>
</
style
>
uview/components/u-time-line/u-time-line.vue
View file @
4a3a32b8
<
template
>
<
template
>
<view
class=
"u-time-axis"
>
<view
class=
"u-time-axis"
>
<slot
/>
<slot
/>
</view>
</view>
</
template
>
</
template
>
<
script
>
<
script
>
export
default
{
/**
data
()
{
* alertTips 提示
return
{
* @description 时间轴组件一般用于物流信息展示,各种跟时间相关的记录等场景。
* @tutorial https://www.uviewui.com/components/timeLine.html
}
* @example <u-time-line></u-time-line>
}
*/
}
export
default
{
</
script
>
name
:
"
u-time-line
"
,
data
()
{
<
style
lang=
"scss"
scoped
>
return
{
.u-time-axis
{
padding-left
:
40rpx
;
}
position
:
relative
;
}
}
}
</
script
>
.
u-time-axis
:
:
before
{
content
:
" "
;
<
style
lang=
"scss"
scoped
>
position
:
absolute
;
.u-time-axis
{
left
:
0
;
padding-left
:
40rpx
;
top
:
12rpx
;
position
:
relative
;
width
:
1px
;
}
bottom
:
0
;
border-left
:
1px
solid
#ddd
;
.
u-time-axis
:
:
before
{
transform-origin
:
0
0
;
content
:
" "
;
transform
:
scaleX
(
0
.5
);
position
:
absolute
;
}
left
:
0
;
top
:
12rpx
;
width
:
1px
;
bottom
:
0
;
border-left
:
1px
solid
#ddd
;
transform-origin
:
0
0
;
transform
:
scaleX
(
0
.5
);
}
</
style
>
</
style
>
uview/components/u-toast/u-toast.vue
View file @
4a3a32b8
<
template
>
<
template
>
<view
class=
"u-toast"
:class=
"[isShow ? 'u-show' : '', 'u-type-' + config.type, 'u-postion-' + config.postion]"
:style=
"
{
<view
class=
"u-toast"
:class=
"[isShow ? 'u-show' : '', 'u-type-' + config.type, 'u-postion-' + config.postion]"
:style=
"
{
padding: isShow ? '0 40rpx' : 0,
padding: isShow ? '0 40rpx' : 0,
zIndex: uZIndex
zIndex: uZIndex
}">
}">
<view
class=
"u-icon-wrap"
>
<view
class=
"u-icon-wrap"
>
<u-icon
v-if=
"config.icon"
class=
"u-icon"
:name=
"iconName"
:size=
"30"
:color=
"$u.color[config.type]"
></u-icon>
<u-icon
v-if=
"config.icon"
class=
"u-icon"
:name=
"iconName"
:size=
"30"
:color=
"$u.color[config.type]"
></u-icon>
</view>
</view>
<text
class=
"u-text"
>
{{
config
.
title
}}
</text>
<text
class=
"u-text"
>
{{
config
.
title
}}
</text>
</view>
</view>
</
template
>
</
template
>
<
script
>
<
script
>
export
default
{
/**
props
:
{
* alertTips 提示
// z-index值
* @description 此组件表现形式类似uni的uni.showToastAPI,但也有不同的地方。
zIndex
:
{
* @tutorial https://www.uviewui.com/components/toast.html
type
:
[
Number
,
String
],
* @property {String} z-index toast展示时的z-index值
default
:
''
* @event {Function} show 显示toast,如需一进入页面就显示toast,请在onReady生命周期调用
},
* @example <u-toast ref="uToast" />
},
*/
data
()
{
export
default
{
return
{
name
:
"
u-toast
"
,
isShow
:
false
,
props
:
{
timer
:
null
,
// 定时器
// z-index值
config
:
{
zIndex
:
{
params
:
{},
// URL跳转的参数,对象
type
:
[
Number
,
String
],
title
:
''
,
// 显示文本
default
:
''
type
:
''
,
// 主题类型,primary,success,error,warning,black
},
duration
:
2000
,
// 显示的时间,毫秒
},
isTab
:
false
,
// 是否跳转tab页面
data
()
{
url
:
''
,
// toast消失后是否跳转页面,有则跳转
return
{
icon
:
true
,
// 显示的图标
isShow
:
false
,
postion
:
'
center
'
,
// toast出现的位置
timer
:
null
,
// 定时器
}
config
:
{
};
params
:
{},
// URL跳转的参数,对象
},
title
:
''
,
// 显示文本
computed
:
{
type
:
''
,
// 主题类型,primary,success,error,warning,black
iconName
()
{
duration
:
2000
,
// 显示的时间,毫秒
// 只有不为none,并且type为error|warning|succes|info时候,才显示图标
isTab
:
false
,
// 是否跳转tab页面
if
([
'
error
'
,
'
warning
'
,
'
success
'
,
'
info
'
].
indexOf
(
this
.
config
.
type
)
>=
0
&&
this
.
config
.
icon
)
{
url
:
''
,
// toast消失后是否跳转页面,有则跳转
let
icon
=
this
.
$u
.
type2icon
(
this
.
config
.
type
);
icon
:
true
,
// 显示的图标
return
icon
;
postion
:
'
center
'
,
// toast出现的位置
}
}
},
};
uZIndex
()
{
},
// 显示toast时候,如果用户有传递z-index值,有限使用
computed
:
{
return
this
.
isShow
?
(
this
.
zIndex
?
this
.
zIndex
:
this
.
$u
.
zIndex
.
toast
)
:
'
-1
'
;
iconName
()
{
}
// 只有不为none,并且type为error|warning|succes|info时候,才显示图标
},
if
([
'
error
'
,
'
warning
'
,
'
success
'
,
'
info
'
].
indexOf
(
this
.
config
.
type
)
>=
0
&&
this
.
config
.
icon
)
{
methods
:
{
let
icon
=
this
.
$u
.
type2icon
(
this
.
config
.
type
);
// 显示toast组件,由父组件通过this.$refs.xxx.show(options)形式调用
return
icon
;
show
(
options
)
{
}
this
.
config
=
Object
.
assign
(
this
.
config
,
options
);
},
if
(
this
.
timer
)
{
uZIndex
()
{
// 清除定时器
// 显示toast时候,如果用户有传递z-index值,有限使用
clearTimeout
(
this
.
timer
);
return
this
.
isShow
?
(
this
.
zIndex
?
this
.
zIndex
:
this
.
$u
.
zIndex
.
toast
)
:
'
-1
'
;
this
.
timer
=
null
;
}
}
},
this
.
isShow
=
true
;
methods
:
{
this
.
timer
=
setTimeout
(()
=>
{
// 显示toast组件,由父组件通过this.$refs.xxx.show(options)形式调用
// 倒计时结束,清除定时器,隐藏toast组件
show
(
options
)
{
this
.
isShow
=
false
;
this
.
config
=
Object
.
assign
(
this
.
config
,
options
);
clearTimeout
(
this
.
timer
);
if
(
this
.
timer
)
{
this
.
timer
=
null
;
// 清除定时器
this
.
timeEnd
();
clearTimeout
(
this
.
timer
);
},
this
.
config
.
duration
);
this
.
timer
=
null
;
},
}
// 隐藏toast组件,由父组件通过this.$refs.xxx.hide()形式调用
this
.
isShow
=
true
;
hide
()
{
this
.
timer
=
setTimeout
(()
=>
{
this
.
isShow
=
false
;
// 倒计时结束,清除定时器,隐藏toast组件
if
(
this
.
timer
)
{
this
.
isShow
=
false
;
// 清除定时器
clearTimeout
(
this
.
timer
);
clearTimeout
(
this
.
timer
);
this
.
timer
=
null
;
this
.
timer
=
null
;
this
.
timeEnd
();
}
},
this
.
config
.
duration
);
},
},
// 倒计时结束之后,进行的一些操作
// 隐藏toast组件,由父组件通过this.$refs.xxx.hide()形式调用
timeEnd
()
{
hide
()
{
// 如果带有url值,根据isTab为true或者false进行跳转
this
.
isShow
=
false
;
if
(
this
.
config
.
url
)
{
if
(
this
.
timer
)
{
// 如果url没有"/"开头,添加上,因为uni的路由跳转需要"/"开头
// 清除定时器
if
(
this
.
config
.
url
[
0
]
!=
'
/
'
)
this
.
config
.
url
=
'
/
'
+
this
.
config
.
url
;
clearTimeout
(
this
.
timer
);
// 判断是否有传递显式的参数
this
.
timer
=
null
;
if
(
Object
.
keys
(
this
.
config
.
params
).
length
)
{
}
// 判断用户传递的url中,是否带有参数
},
// 使用正则匹配,主要依据是判断是否有"/","?","="等,如“/page/index/index?name=mary"
// 倒计时结束之后,进行的一些操作
// 如果有params参数,转换后无需带上"?"
timeEnd
()
{
let
query
=
''
;
// 如果带有url值,根据isTab为true或者false进行跳转
if
(
/.*
\/
.*
\?
.*=.*/
.
test
(
this
.
config
.
url
))
{
if
(
this
.
config
.
url
)
{
// object对象转为get类型的参数
// 如果url没有"/"开头,添加上,因为uni的路由跳转需要"/"开头
query
=
this
.
$u
.
queryParams
(
this
.
config
.
params
,
false
);
if
(
this
.
config
.
url
[
0
]
!=
'
/
'
)
this
.
config
.
url
=
'
/
'
+
this
.
config
.
url
;
this
.
config
.
url
=
this
.
config
.
url
+
"
&
"
+
query
;
// 判断是否有传递显式的参数
}
else
{
if
(
Object
.
keys
(
this
.
config
.
params
).
length
)
{
query
=
this
.
$u
.
queryParams
(
this
.
config
.
params
);
// 判断用户传递的url中,是否带有参数
this
.
config
.
url
+=
query
;
// 使用正则匹配,主要依据是判断是否有"/","?","="等,如“/page/index/index?name=mary"
}
// 如果有params参数,转换后无需带上"?"
}
let
query
=
''
;
// 如果是跳转tab页面,就使用uni.switchTab
if
(
/.*
\/
.*
\?
.*=.*/
.
test
(
this
.
config
.
url
))
{
if
(
this
.
config
.
isTab
)
{
// object对象转为get类型的参数
uni
.
switchTab
({
query
=
this
.
$u
.
queryParams
(
this
.
config
.
params
,
false
);
url
:
this
.
config
.
url
this
.
config
.
url
=
this
.
config
.
url
+
"
&
"
+
query
;
});
}
else
{
}
else
{
query
=
this
.
$u
.
queryParams
(
this
.
config
.
params
);
uni
.
navigateTo
({
this
.
config
.
url
+=
query
;
url
:
this
.
config
.
url
}
});
}
}
// 如果是跳转tab页面,就使用uni.switchTab
}
if
(
this
.
config
.
isTab
)
{
}
uni
.
switchTab
({
}
url
:
this
.
config
.
url
};
});
</
script
>
}
else
{
uni
.
navigateTo
({
<
style
lang=
"scss"
scoped
>
url
:
this
.
config
.
url
.u-toast
{
});
position
:
fixed
;
}
z-index
:
-1
;
}
transition
:
opacity
0
.3s
;
}
text-align
:
center
;
}
color
:
#fff
;
};
border-radius
:
8rpx
;
</
script
>
background
:
#585858
;
height
:
80rpx
;
<
style
lang=
"scss"
scoped
>
display
:
flex
;
.u-toast
{
align-items
:
center
;
position
:
fixed
;
justify-content
:
center
;
z-index
:
-1
;
font-size
:
28rpx
;
transition
:
opacity
0
.3s
;
opacity
:
0
;
text-align
:
center
;
}
color
:
#fff
;
border-radius
:
8rpx
;
.u-toast.u-show
{
background
:
#585858
;
opacity
:
1
;
height
:
80rpx
;
z-index
:
9999999
;
display
:
flex
;
}
align-items
:
center
;
justify-content
:
center
;
.u-text
{
font-size
:
28rpx
;
word-break
:
keep-all
;
opacity
:
0
;
white-space
:nowrap
;
}
line-height
:
normal
;
}
.u-toast.u-show
{
opacity
:
1
;
.u-icon
{
z-index
:
9999999
;
margin-right
:
10rpx
;
}
display
:
flex
;
align-items
:
center
;
.u-text
{
line-height
:
normal
;
word-break
:
keep-all
;
}
white-space
:
nowrap
;
line-height
:
normal
;
.u-postion-center
{
}
left
:
50%
;
top
:
50%
;
.u-icon
{
transform
:
translateX
(
-50%
)
translateY
(
-50%
);
margin-right
:
10rpx
;
}
display
:
flex
;
align-items
:
center
;
.u-postion-top
{
line-height
:
normal
;
left
:
50%
;
}
top
:
20%
;
transform
:
translateX
(
-50%
)
translateY
(
-50%
);
.u-postion-center
{
}
left
:
50%
;
top
:
50%
;
.u-postion-bottom
{
transform
:
translateX
(
-50%
)
translateY
(
-50%
);
left
:
50%
;
}
bottom
:
20%
;
transform
:
translateX
(
-50%
)
translateY
(
-50%
);
.u-postion-top
{
}
left
:
50%
;
top
:
20%
;
.u-type-primary
{
transform
:
translateX
(
-50%
)
translateY
(
-50%
);
color
:
$u-type-primary
;
}
background-color
:
$u-type-primary-light
;
border
:
1px
solid
rgb
(
215
,
234
,
254
);
.u-postion-bottom
{
}
left
:
50%
;
bottom
:
20%
;
.u-type-success
{
transform
:
translateX
(
-50%
)
translateY
(
-50%
);
color
:
$u-type-success
;
}
background-color
:
$u-type-success-light
;
border
:
1px
solid
#BEF5C8
;
.u-type-primary
{
}
color
:
$u-type-primary
;
background-color
:
$u-type-primary-light
;
.u-type-error
{
border
:
1px
solid
rgb
(
215
,
234
,
254
);
color
:
$u-type-error
;
}
background-color
:
$u-type-error-light
;
border
:
1px
solid
#fde2e2
;
.u-type-success
{
}
color
:
$u-type-success
;
background-color
:
$u-type-success-light
;
.u-type-warning
{
border
:
1px
solid
#BEF5C8
;
color
:
$u-type-warning
;
}
background-color
:
$u-type-warning-light
;
border
:
1px
solid
#faecd8
;
.u-type-error
{
}
color
:
$u-type-error
;
background-color
:
$u-type-error-light
;
.u-type-info
{
border
:
1px
solid
#fde2e2
;
color
:
$u-type-info
;
}
background-color
:
$u-type-info-light
;
border
:
1px
solid
#ebeef5
;
.u-type-warning
{
}
color
:
$u-type-warning
;
background-color
:
$u-type-warning-light
;
.u-type-default
{
border
:
1px
solid
#faecd8
;
color
:
#fff
;
}
background-color
:
#585858
;
}
.u-type-info
{
color
:
$u-type-info
;
background-color
:
$u-type-info-light
;
border
:
1px
solid
#ebeef5
;
}
.u-type-default
{
color
:
#fff
;
background-color
:
#585858
;
}
</
style
>
</
style
>
uview/components/u-top-tips/u-top-tips.vue
View file @
4a3a32b8
<
template
>
<
template
>
<view
class=
"u-tips"
:class=
"['u-' + type, isShow ? 'u-tip-show' : '']"
:style=
"
{
<view
class=
"u-tips"
:class=
"['u-' + type, isShow ? 'u-tip-show' : '']"
:style=
"
{
top: navbarHeight + 'px',
top: navbarHeight + 'px',
zIndex: uZIndex
zIndex: uZIndex
}">
{{
title
}}
</view>
}">
{{
title
}}
</view>
</
template
>
</
template
>
<
script
>
<
script
>
export
default
{
/**
props
:
{
* alertTips 提示
// 导航栏高度,用于提示的初始化
* @description 该组件一般用于页面顶部向下滑出一个提示,尔后自动收起的场景。
navbarHeight
:
{
* @tutorial https://www.uviewui.com/components/topTips.html
type
:
[
Number
,
String
],
* @property {String Number} navbar-height 导航栏高度(包含状态栏高度在内),单位PX
// #ifndef H5
* @property {String Number} z-index z-index值(默认975)
default
:
0
,
* @example <u-top-tips ref="uTips" type="success" duration="1500"></u-top-tips>
// #endif
*/
// #ifdef H5
export
default
{
default
:
44
,
name
:
"
u-top-tips
"
,
// #endif
props
:
{
},
// 导航栏高度,用于提示的初始化
// z-index值
navbarHeight
:
{
zIndex
:
{
type
:
[
Number
,
String
],
type
:
[
Number
,
String
],
// #ifndef H5
default
:
''
default
:
0
,
}
// #endif
},
// #ifdef H5
data
()
{
default
:
44
,
return
{
// #endif
timer
:
null
,
// 定时器
},
isShow
:
false
,
// 是否显示消息组件
// z-index值
title
:
''
,
// 组件中显示的消息内容
zIndex
:
{
type
:
'
primary
'
,
// 消息的类型(颜色不同),primary,success,error,warning,info
type
:
[
Number
,
String
],
duration
:
2000
,
// 组件显示的时间,单位为毫秒
default
:
''
};
}
},
},
computed
:
{
data
()
{
uZIndex
()
{
return
{
return
this
.
zIndex
?
this
.
zIndex
:
this
.
$u
.
zIndex
.
topTips
;
timer
:
null
,
// 定时器
}
isShow
:
false
,
// 是否显示消息组件
},
title
:
''
,
// 组件中显示的消息内容
methods
:
{
type
:
'
primary
'
,
// 消息的类型(颜色不同),primary,success,error,warning,info
show
(
config
=
{})
{
duration
:
2000
,
// 组件显示的时间,单位为毫秒
// 先清除定时器(可能是上一次定义的,需要清除了再开始新的)
};
clearTimeout
(
this
.
timer
);
},
// 时间,内容,类型主题(type)等参数
computed
:
{
if
(
config
.
duration
)
this
.
duration
=
config
.
duration
;
uZIndex
()
{
if
(
config
.
type
)
this
.
type
=
config
.
type
;
return
this
.
zIndex
?
this
.
zIndex
:
this
.
$u
.
zIndex
.
topTips
;
this
.
title
=
config
.
title
;
}
this
.
isShow
=
true
;
},
// 倒计时
methods
:
{
this
.
timer
=
setTimeout
(()
=>
{
show
(
config
=
{})
{
this
.
isShow
=
false
;
// 先清除定时器(可能是上一次定义的,需要清除了再开始新的)
clearTimeout
(
this
.
timer
);
clearTimeout
(
this
.
timer
);
this
.
timer
=
null
;
// 时间,内容,类型主题(type)等参数
},
this
.
duration
);
if
(
config
.
duration
)
this
.
duration
=
config
.
duration
;
}
if
(
config
.
type
)
this
.
type
=
config
.
type
;
}
this
.
title
=
config
.
title
;
};
this
.
isShow
=
true
;
</
script
>
// 倒计时
this
.
timer
=
setTimeout
(()
=>
{
<
style
lang=
"scss"
scoped
>
this
.
isShow
=
false
;
view
{
clearTimeout
(
this
.
timer
);
box-sizing
:
border-box
;
this
.
timer
=
null
;
}
},
this
.
duration
);
}
// 顶部弹出类型样式
}
.u-tips
{
};
width
:
100%
;
</
script
>
position
:
fixed
;
z-index
:
1
;
<
style
lang=
"scss"
scoped
>
padding
:
20rpx
30rpx
;
view
{
color
:
#FFFFFF
;
box-sizing
:
border-box
;
font-size
:
28rpx
;
}
left
:
0
;
right
:
0
;
// 顶部弹出类型样式
display
:
flex
;
.u-tips
{
align-items
:
center
;
width
:
100%
;
justify-content
:
center
;
position
:
fixed
;
opacity
:
0
;
z-index
:
1
;
// 此处为最核心点,translateY(-100%)意味着将其从Y轴隐藏(隐藏到顶部(h5)或者说导航栏(app)下面)
padding
:
20rpx
30rpx
;
transform
:
translateY
(
-100%
);
color
:
#FFFFFF
;
transition
:
all
0
.35s
linear
;
font-size
:
28rpx
;
}
left
:
0
;
right
:
0
;
.u-tip-show
{
display
:
flex
;
transform
:
translateY
(
0
);
align-items
:
center
;
opacity
:
1
;
justify-content
:
center
;
z-index
:
99
;
opacity
:
0
;
}
// 此处为最核心点,translateY(-100%)意味着将其从Y轴隐藏(隐藏到顶部(h5)或者说导航栏(app)下面)
transform
:
translateY
(
-100%
);
.u-primary
{
transition
:
all
0
.35s
linear
;
background
:
$u-type-primary
;
}
}
.u-tip-show
{
.u-success
{
transform
:
translateY
(
0
);
background
:
$u-type-success
;
opacity
:
1
;
}
z-index
:
99
;
}
.u-warning
{
background
:
$u-type-warning
;
.u-primary
{
}
background
:
$u-type-primary
;
}
.u-error
{
background
:
$u-type-error
;
.u-success
{
}
background
:
$u-type-success
;
}
.u-info
{
background
:
$u-type-info
;
.u-warning
{
}
background
:
$u-type-warning
;
}
.u-error
{
background
:
$u-type-error
;
}
.u-info
{
background
:
$u-type-info
;
}
</
style
>
</
style
>
uview/components/u-tr/u-tr.vue
View file @
4a3a32b8
<
template
>
<
template
>
<view
class=
"u-tr"
>
<view
class=
"u-tr"
>
<slot></slot>
<slot></slot>
</view>
</view>
</
template
>
</
template
>
<
script
>
<
script
>
export
default
{
/**
inject
:
[
'
uTable
'
,
'
uTd
'
],
* alertTips 提示
provide
()
{
* @description 表格组件一般用于展示大量结构化数据的场景(搭配<u-table>使用)
return
{
* @tutorial https://www.uviewui.com/components/table.html
uTr
:
this
,
* @example <u-tr></u-tr>
};
*/
},
export
default
{
created
()
{
name
:
"
u-tr
"
,
if
(
this
.
uTd
&&
this
.
uTd
.
tr
)
{
inject
:
[
'
uTable
'
,
'
uTd
'
],
this
.
uTd
.
tr
.
push
(
this
);
provide
()
{
}
return
{
}
uTr
:
this
,
}
};
</
script
>
},
created
()
{
<
style
lang=
"scss"
scoped
>
if
(
this
.
uTd
&&
this
.
uTd
.
tr
)
{
.u-tr
{
this
.
uTd
.
tr
.
push
(
this
);
display
:
flex
;
}
}
}
}
</
script
>
<
style
lang=
"scss"
scoped
>
.u-tr
{
display
:
flex
;
}
</
style
>
</
style
>
uview/components/u-upload/u-upload.vue
View file @
4a3a32b8
<
template
>
<
template
>
<view
class=
"u-upload"
v-if=
"!disabled"
>
<view
class=
"u-upload"
v-if=
"!disabled"
>
<view
v-if=
"showUploadList"
class=
"u-list-item u-preview-wrap"
v-for=
"(item, index) in lists"
:key=
"index"
>
<view
v-if=
"showUploadList"
class=
"u-list-item u-preview-wrap"
v-for=
"(item, index) in lists"
:key=
"index"
>
<view
v-if=
"deletable"
class=
"u-delete-icon"
@
tap.stop=
"deleteItem(index)"
>
<view
v-if=
"deletable"
class=
"u-delete-icon"
@
tap.stop=
"deleteItem(index)"
>
<u-icon
class=
"u-icon"
name=
"close"
size=
"20"
color=
"#ffffff"
></u-icon>
<u-icon
class=
"u-icon"
name=
"close"
size=
"20"
color=
"#ffffff"
></u-icon>
</view>
</view>
<u-line-progress
v-if=
"showProgress && item.progress > 0 && !item.error"
:show-percent=
"false"
height=
"16"
class=
"u-progress"
:percent=
"item.progress"
></u-line-progress>
<u-line-progress
v-if=
"showProgress && item.progress > 0 && !item.error"
:show-percent=
"false"
height=
"16"
class=
"u-progress"
<view
@
tap.stop=
"retry(index)"
v-if=
"item.error"
class=
"u-error-btn"
>
点击重试
</view>
:percent=
"item.progress"
></u-line-progress>
<image
@
tap.stop=
"doPreviewImage(item.url || item.path, index)"
class=
"u-preview-image"
v-if=
"!item.isImage"
:src=
" item.url || item.path "
<view
@
tap.stop=
"retry(index)"
v-if=
"item.error"
class=
"u-error-btn"
>
点击重试
</view>
:mode=
"imageMode"
></image>
<image
@
tap.stop=
"doPreviewImage(item.url || item.path, index)"
class=
"u-preview-image"
v-if=
"!item.isImage"
:src=
" item.url || item.path "
</view>
:mode=
"imageMode"
></image>
<slot
name=
"file"
:file=
"lists"
></slot>
</view>
<view
style=
"display: inline-block;"
@
tap=
"selectFile"
v-if=
"maxCount > lists.length"
>
<slot
name=
"file"
:file=
"lists"
></slot>
<slot
name=
"addBtn"
></slot>
<view
style=
"display: inline-block;"
@
tap=
"selectFile"
v-if=
"maxCount > lists.length"
>
<view
v-if=
"!customBtn"
class=
"u-list-item u-add-wrap"
hover-class=
"u-add-wrap__hover"
hover-stay-time=
"150"
>
<slot
name=
"addBtn"
></slot>
<u-icon
name=
"plus"
class=
"u-add-btn"
size=
"40"
></u-icon>
<view
v-if=
"!customBtn"
class=
"u-list-item u-add-wrap"
hover-class=
"u-add-wrap__hover"
hover-stay-time=
"150"
>
<view
class=
"u-add-tips"
>
{{
uploadText
}}
</view>
<u-icon
name=
"plus"
class=
"u-add-btn"
size=
"40"
></u-icon>
</view>
<view
class=
"u-add-tips"
>
{{
uploadText
}}
</view>
</view>
</view>
</view>
</view>
</
template
>
</view>
</
template
>
<
script
>
export
default
{
<
script
>
props
:
{
/**
//是否显示组件自带的图片预览功能
* alertTips 提示
showUploadList
:
{
* @description 该组件用于上传图片场景
type
:
Boolean
,
* @tutorial https://www.uviewui.com/components/upload.html
default
:
true
* @property {String} action 服务器上传地址
},
* @property {String Number} max-count 最大选择图片的数量(默认99)
// 后端地址
* @property {Boolean} custom-btn 如果需要自定义选择图片的按钮,设置为true(默认false)
action
:
{
* @property {Boolean} show-progress 是否显示进度条(默认true)
type
:
String
,
* @property {Boolean} disabled 是否启用(显示/移仓)组件(默认false)
default
:
''
* @property {String} image-mode 预览图片等显示模式,可选值为uni的image的mode属性值(默认aspectFill)
},
* @property {Object} header 上传携带的头信息,对象形式
// 最大上传数量
* @property {Object} form-data 上传额外携带的参数
maxCount
:
{
* @property {String} name 上传文件的字段名,供后端获取使用(默认file)
type
:
[
String
,
Number
],
* @property {Array<String>} size-type original 原图,compressed 压缩图,默认二者都有(默认['original', 'compressed'])
default
:
52
* @property {Array<String>} source-type 选择图片的来源,album-从相册选图,camera-使用相机,默认二者都有(默认['album', 'camera'])
},
* @property {Boolean} preview-full-image 是否可以通过uni.previewImage预览已选择的图片(默认true)
// 是否显示进度条
* @property {Boolean} multiple 是否开启图片多选,部分安卓机型不支持(默认true)
showProgress
:
{
* @property {Boolean} deletable 是否显示删除图片的按钮(默认true)
type
:
Boolean
,
* @property {String Number} max-size 选择单个文件的最大大小,单位B(byte),默认不限制(默认Number.MAX_VALUE)
default
:
true
* @property {Array<Object>} file-list 默认显示的图片列表,数组元素为对象,必须提供url属性
},
* @property {Boolean} upload-text 选择图片按钮的提示文字(默认“选择图片”)
// 是否启用
* @property {Boolean} auto-upload 选择完图片是否自动上传,见上方说明(默认true)
disabled
:
{
* @property {Boolean} show-tips 特殊情况下是否自动提示toast,见上方说明(默认true)
type
:
Boolean
,
* @property {Boolean} show-upload-list 是否显示组件内部的图片预览(默认true)
default
:
false
* @event {Function} on-oversize 图片大小超出最大允许大小
},
* @event {Function} on-preview 全屏预览图片时触发
// 预览上传的图片时的裁剪模式,和image组件mode属性一致
* @event {Function} on-remove 移除图片时触发
imageMode
:
{
* @event {Function} on-success 图片上传成功时触发
type
:
String
,
* @event {Function} on-change 图片上传后,无论成功或者失败都会触发
default
:
'
aspectFill
'
* @event {Function} on-error 图片上传失败时触发
},
* @event {Function} on-progress 图片上传过程中的进度变化过程触发
// 头部信息
* @event {Function} on-uploaded 所有图片上传完毕触发
header
:
{
* @event {Function} on-choose-complete 每次选择图片后触发,只是让外部可以得知每次选择后,内部的文件列表
type
:
Object
,
* @example <u-upload :action="action" :file-list="fileList" ></u-upload>
default
()
{
*/
return
{}
export
default
{
}
name
:
"
u-upload
"
,
},
props
:
{
// 额外携带的参数
//是否显示组件自带的图片预览功能
formData
:
{
showUploadList
:
{
type
:
Object
,
type
:
Boolean
,
default
()
{
default
:
true
return
{}
},
}
// 后端地址
},
action
:
{
// 上传的文件字段名
type
:
String
,
name
:
{
default
:
''
type
:
String
,
},
default
:
'
file
'
// 最大上传数量
},
maxCount
:
{
// 所选的图片的尺寸, 可选值为original compressed
type
:
[
String
,
Number
],
sizeType
:
{
default
:
52
type
:
Array
,
},
default
()
{
// 是否显示进度条
return
[
'
original
'
,
'
compressed
'
]
showProgress
:
{
}
type
:
Boolean
,
},
default
:
true
sourceType
:
{
},
type
:
Array
,
// 是否启用
default
()
{
disabled
:
{
return
[
'
album
'
,
'
camera
'
]
type
:
Boolean
,
}
default
:
false
},
},
// 是否在点击预览图后展示全屏图片预览
// 预览上传的图片时的裁剪模式,和image组件mode属性一致
previewFullImage
:
{
imageMode
:
{
type
:
Boolean
,
type
:
String
,
default
:
true
default
:
'
aspectFill
'
},
},
// 是否开启图片多选,部分安卓机型不支持
// 头部信息
multiple
:
{
header
:
{
type
:
Boolean
,
type
:
Object
,
default
:
true
default
()
{
},
return
{}
// 是否展示删除按钮
}
deletable
:
{
},
type
:
Boolean
,
// 额外携带的参数
default
:
true
formData
:
{
},
type
:
Object
,
// 文件大小限制,单位为byte
default
()
{
maxSize
:
{
return
{}
type
:
[
String
,
Number
],
}
default
:
Number
.
MAX_VALUE
},
},
// 上传的文件字段名
// 显示已上传的文件列表
name
:
{
fileList
:
{
type
:
String
,
type
:
Array
,
default
:
'
file
'
default
()
{
},
return
[]
// 所选的图片的尺寸, 可选值为original compressed
}
sizeType
:
{
},
type
:
Array
,
// 上传区域的提示文字
default
()
{
uploadText
:
{
return
[
'
original
'
,
'
compressed
'
]
type
:
String
,
}
default
:
'
选择图片
'
},
},
sourceType
:
{
// 是否自动上传
type
:
Array
,
autoUpload
:
{
default
()
{
type
:
Boolean
,
return
[
'
album
'
,
'
camera
'
]
default
:
true
}
},
},
// 是否显示toast消息提示
// 是否在点击预览图后展示全屏图片预览
showTips
:
{
previewFullImage
:
{
type
:
Boolean
,
type
:
Boolean
,
default
:
true
default
:
true
},
},
// 是否通过slot自定义传入选择图标的按钮
// 是否开启图片多选,部分安卓机型不支持
customBtn
:
{
multiple
:
{
type
:
Boolean
,
type
:
Boolean
,
default
:
false
default
:
true
}
},
},
// 是否展示删除按钮
mounted
()
{
deletable
:
{
type
:
Boolean
,
},
default
:
true
data
()
{
},
return
{
// 文件大小限制,单位为byte
lists
:
[],
maxSize
:
{
isInCount
:
true
,
type
:
[
String
,
Number
],
uploading
:
false
default
:
Number
.
MAX_VALUE
}
},
},
// 显示已上传的文件列表
watch
:
{
fileList
:
{
fileList
:
{
type
:
Array
,
immediate
:
true
,
default
()
{
handler
(
val
)
{
return
[]
val
.
map
(
value
=>
{
}
this
.
lists
.
push
({
url
:
value
.
url
,
error
:
false
,
progress
:
100
});
},
})
// 上传区域的提示文字
}
uploadText
:
{
}
type
:
String
,
},
default
:
'
选择图片
'
methods
:
{
},
// 选择图片
// 是否自动上传
selectFile
()
{
autoUpload
:
{
if
(
this
.
disabled
)
return
;
type
:
Boolean
,
const
{
default
:
true
name
=
''
,
maxCount
,
multiple
,
maxSize
,
sizeType
,
lists
,
camera
,
compressed
,
maxDuration
,
sourceType
},
}
=
this
;
// 是否显示toast消息提示
let
chooseFile
=
null
;
showTips
:
{
const
newMaxCount
=
maxCount
-
lists
.
length
;
type
:
Boolean
,
// 设置为只选择图片的时候使用 chooseImage 来实现
default
:
true
chooseFile
=
new
Promise
((
resolve
,
reject
)
=>
{
},
wx
.
chooseImage
({
// 是否通过slot自定义传入选择图标的按钮
count
:
multiple
?
(
newMaxCount
>
9
?
9
:
newMaxCount
)
:
1
,
customBtn
:
{
sourceType
:
sourceType
,
type
:
Boolean
,
sizeType
,
default
:
false
success
:
resolve
,
}
fail
:
reject
},
});
mounted
()
{
});
chooseFile
},
.
then
((
res
)
=>
{
data
()
{
let
file
=
null
;
return
{
let
listOldLength
=
this
.
lists
.
length
;
lists
:
[],
res
.
tempFiles
.
map
((
val
,
index
)
=>
{
isInCount
:
true
,
// 如果是非多选,index大于等于1或者超出最大限制数量时,不处理
uploading
:
false
if
(
!
multiple
&&
index
>=
1
)
return
;
}
if
(
val
.
size
>
maxSize
)
{
},
this
.
$emit
(
'
on-oversize
'
,
val
,
this
.
lists
);
watch
:
{
this
.
showToast
(
'
超出允许的文件大小
'
);
fileList
:
{
}
else
{
immediate
:
true
,
if
(
maxCount
<=
lists
.
length
)
{
handler
(
val
)
{
this
.
$emit
(
'
on-exceed
'
,
val
,
this
.
lists
);
val
.
map
(
value
=>
{
this
.
showToast
(
'
超出最大允许的文件个数
'
);
this
.
lists
.
push
({
return
;
url
:
value
.
url
,
}
error
:
false
,
lists
.
push
({
progress
:
100
url
:
val
.
path
,
});
progress
:
0
,
})
error
:
false
}
});
}
}
},
})
methods
:
{
// 每次图片选择完,抛出一个事件,并将当前内部选择的图片数组抛出去
// 选择图片
this
.
$emit
(
'
on-choose-complete
'
,
this
.
lists
);
selectFile
()
{
if
(
this
.
autoUpload
)
this
.
uploadFile
(
listOldLength
);
if
(
this
.
disabled
)
return
;
})
const
{
.
catch
(
error
=>
{
name
=
''
,
maxCount
,
multiple
,
maxSize
,
sizeType
,
lists
,
camera
,
compressed
,
maxDuration
,
sourceType
// this.$emit('on-error', error);
}
=
this
;
});
let
chooseFile
=
null
;
},
const
newMaxCount
=
maxCount
-
lists
.
length
;
// 提示用户消息
// 设置为只选择图片的时候使用 chooseImage 来实现
showToast
(
message
,
force
=
false
)
{
chooseFile
=
new
Promise
((
resolve
,
reject
)
=>
{
if
(
this
.
showTips
||
force
)
{
wx
.
chooseImage
({
uni
.
showToast
({
count
:
multiple
?
(
newMaxCount
>
9
?
9
:
newMaxCount
)
:
1
,
title
:
message
,
sourceType
:
sourceType
,
icon
:
"
none
"
sizeType
,
});
success
:
resolve
,
}
fail
:
reject
},
});
// 该方法供用户通过ref调用,手动上传
});
upload
()
{
chooseFile
this
.
uploadFile
();
.
then
((
res
)
=>
{
},
let
file
=
null
;
// 对失败的图片重新上传
let
listOldLength
=
this
.
lists
.
length
;
retry
(
index
)
{
res
.
tempFiles
.
map
((
val
,
index
)
=>
{
this
.
lists
[
index
].
progress
=
0
;
// 如果是非多选,index大于等于1或者超出最大限制数量时,不处理
this
.
lists
[
index
].
error
=
false
;
if
(
!
multiple
&&
index
>=
1
)
return
;
this
.
lists
[
index
].
response
=
null
;
if
(
val
.
size
>
maxSize
)
{
uni
.
showLoading
({
this
.
$emit
(
'
on-oversize
'
,
val
,
this
.
lists
);
title
:
'
重新上传
'
this
.
showToast
(
'
超出允许的文件大小
'
);
});
}
else
{
this
.
uploadFile
(
index
);
if
(
maxCount
<=
lists
.
length
)
{
},
this
.
$emit
(
'
on-exceed
'
,
val
,
this
.
lists
);
// 上传图片
this
.
showToast
(
'
超出最大允许的文件个数
'
);
uploadFile
(
index
=
0
)
{
return
;
if
(
this
.
disabled
)
return
;
}
if
(
this
.
uploading
)
return
;
lists
.
push
({
// 全部上传完成
url
:
val
.
path
,
if
(
index
>=
this
.
lists
.
length
)
{
progress
:
0
,
this
.
$emit
(
'
on-uploaded
'
,
this
.
lists
);
error
:
false
return
;
});
}
}
// 检查上传地址
})
if
(
!
this
.
action
)
{
// 每次图片选择完,抛出一个事件,并将当前内部选择的图片数组抛出去
this
.
showToast
(
'
请配置上传地址
'
,
true
);
this
.
$emit
(
'
on-choose-complete
'
,
this
.
lists
);
return
;
if
(
this
.
autoUpload
)
this
.
uploadFile
(
listOldLength
);
}
})
// 检查是否是已上传或者正在上传中
.
catch
(
error
=>
{
if
(
this
.
lists
[
index
].
progress
==
100
)
{
// this.$emit('on-error', error);
if
(
this
.
autoUpload
==
false
)
this
.
uploadFile
(
index
+
1
);
});
return
;
},
}
// 提示用户消息
this
.
lists
[
index
].
error
=
false
;
showToast
(
message
,
force
=
false
)
{
this
.
uploading
=
true
;
if
(
this
.
showTips
||
force
)
{
// 创建上传对象
uni
.
showToast
({
const
task
=
uni
.
uploadFile
({
title
:
message
,
url
:
this
.
action
,
icon
:
"
none
"
filePath
:
this
.
lists
[
index
].
url
,
});
name
:
this
.
name
,
}
formData
:
this
.
formData
,
},
header
:
this
.
header
,
// 该方法供用户通过ref调用,手动上传
success
:
(
res
)
=>
{
upload
()
{
if
(
res
.
statusCode
!=
200
)
{
this
.
uploadFile
();
this
.
uploadError
(
index
,
res
.
data
);
},
}
else
{
// 对失败的图片重新上传
// 上传成功
retry
(
index
)
{
this
.
lists
[
index
].
response
=
res
.
data
;
this
.
lists
[
index
].
progress
=
0
;
this
.
lists
[
index
].
progress
=
100
;
this
.
lists
[
index
].
error
=
false
;
this
.
lists
[
index
].
error
=
false
;
this
.
lists
[
index
].
response
=
null
;
this
.
$emit
(
'
on-success
'
,
res
.
data
,
index
,
this
.
lists
);
uni
.
showLoading
({
}
title
:
'
重新上传
'
},
});
fail
:
(
e
)
=>
{
this
.
uploadFile
(
index
);
this
.
uploadError
(
index
,
e
);
},
},
// 上传图片
complete
:
(
res
)
=>
{
uploadFile
(
index
=
0
)
{
uni
.
hideLoading
();
if
(
this
.
disabled
)
return
;
this
.
uploading
=
false
;
if
(
this
.
uploading
)
return
;
this
.
uploadFile
(
index
+
1
);
// 全部上传完成
this
.
$emit
(
'
on-change
'
,
res
,
index
,
this
.
lists
);
if
(
index
>=
this
.
lists
.
length
)
{
}
this
.
$emit
(
'
on-uploaded
'
,
this
.
lists
);
});
return
;
task
.
onProgressUpdate
((
res
)
=>
{
}
if
(
res
.
progress
>
0
)
{
// 检查上传地址
this
.
lists
[
index
].
progress
=
res
.
progress
;
if
(
!
this
.
action
)
{
this
.
$emit
(
'
on-progress
'
,
res
,
index
,
this
.
lists
);
this
.
showToast
(
'
请配置上传地址
'
,
true
);
}
return
;
});
}
},
// 检查是否是已上传或者正在上传中
// 上传失败
if
(
this
.
lists
[
index
].
progress
==
100
)
{
uploadError
(
index
,
err
)
{
if
(
this
.
autoUpload
==
false
)
this
.
uploadFile
(
index
+
1
);
this
.
lists
[
index
].
progress
=
0
;
return
;
this
.
lists
[
index
].
error
=
true
;
}
this
.
lists
[
index
].
response
=
null
;
this
.
lists
[
index
].
error
=
false
;
this
.
$emit
(
'
on-error
'
,
err
,
index
,
this
.
lists
);
this
.
uploading
=
true
;
this
.
showToast
(
'
上传失败,请重试
'
);
// 创建上传对象
},
const
task
=
uni
.
uploadFile
({
// 删除一个图片
url
:
this
.
action
,
deleteItem
(
index
)
{
filePath
:
this
.
lists
[
index
].
url
,
uni
.
showModal
({
name
:
this
.
name
,
title
:
'
提示
'
,
formData
:
this
.
formData
,
content
:
'
您确定要删除此项吗?
'
,
header
:
this
.
header
,
success
:
res
=>
{
success
:
(
res
)
=>
{
if
(
res
.
confirm
)
{
if
(
res
.
statusCode
!=
200
)
{
if
(
this
.
lists
[
index
].
process
<
100
&&
this
.
lists
[
index
].
process
>
0
)
{
this
.
uploadError
(
index
,
res
.
data
);
typeof
this
.
lists
[
index
].
uploadTask
!=
'
undefined
'
&&
this
.
lists
[
index
].
uploadTask
.
abort
();
}
else
{
}
// 上传成功
this
.
lists
.
splice
(
index
,
1
);
this
.
lists
[
index
].
response
=
res
.
data
;
this
.
$forceUpdate
();
this
.
lists
[
index
].
progress
=
100
;
this
.
$emit
(
'
on-remove
'
,
index
,
this
.
lists
);
this
.
lists
[
index
].
error
=
false
;
this
.
showToast
(
'
移除成功
'
);
this
.
$emit
(
'
on-success
'
,
res
.
data
,
index
,
this
.
lists
);
}
}
}
},
});
fail
:
(
e
)
=>
{
},
this
.
uploadError
(
index
,
e
);
// 预览图片
},
doPreviewImage
(
url
,
index
)
{
complete
:
(
res
)
=>
{
if
(
!
this
.
previewFullImage
)
uni
.
hideLoading
();
return
;
this
.
uploading
=
false
;
const
images
=
this
.
lists
.
map
(
item
=>
item
.
url
||
item
.
path
);
this
.
uploadFile
(
index
+
1
);
uni
.
previewImage
({
this
.
$emit
(
'
on-change
'
,
res
,
index
,
this
.
lists
);
urls
:
images
,
}
current
:
url
,
});
success
:
()
=>
{
task
.
onProgressUpdate
((
res
)
=>
{
this
.
$emit
(
'
on-preview
'
,
url
,
this
.
lists
);
if
(
res
.
progress
>
0
)
{
},
this
.
lists
[
index
].
progress
=
res
.
progress
;
fail
:
()
=>
{
this
.
$emit
(
'
on-progress
'
,
res
,
index
,
this
.
lists
);
uni
.
showToast
({
}
title
:
'
预览图片失败
'
,
});
icon
:
'
none
'
},
});
// 上传失败
}
uploadError
(
index
,
err
)
{
});
this
.
lists
[
index
].
progress
=
0
;
}
this
.
lists
[
index
].
error
=
true
;
}
this
.
lists
[
index
].
response
=
null
;
}
this
.
$emit
(
'
on-error
'
,
err
,
index
,
this
.
lists
);
</
script
>
this
.
showToast
(
'
上传失败,请重试
'
);
},
<
style
lang=
"scss"
scoped
>
// 删除一个图片
.u-upload
{
deleteItem
(
index
)
{
display
:
flex
;
uni
.
showModal
({
flex-wrap
:
wrap
;
title
:
'
提示
'
,
align-items
:
center
;
content
:
'
您确定要删除此项吗?
'
,
}
success
:
res
=>
{
if
(
res
.
confirm
)
{
.u-list-item
{
if
(
this
.
lists
[
index
].
process
<
100
&&
this
.
lists
[
index
].
process
>
0
)
{
width
:
200rpx
;
typeof
this
.
lists
[
index
].
uploadTask
!=
'
undefined
'
&&
this
.
lists
[
index
].
uploadTask
.
abort
();
height
:
200rpx
;
}
overflow
:
hidden
;
this
.
lists
.
splice
(
index
,
1
);
margin
:
10rpx
;
this
.
$forceUpdate
();
background
:
rgb
(
244
,
245
,
246
);
this
.
$emit
(
'
on-remove
'
,
index
,
this
.
lists
);
position
:
relative
;
this
.
showToast
(
'
移除成功
'
);
border-radius
:
10rpx
;
}
display
:
inline-flex
;
}
align-items
:
center
;
});
justify-content
:
center
;
},
}
// 预览图片
doPreviewImage
(
url
,
index
)
{
.u-preview-wrap
{
if
(
!
this
.
previewFullImage
)
border
:
1px
solid
rgb
(
235
,
236
,
238
);
return
;
}
const
images
=
this
.
lists
.
map
(
item
=>
item
.
url
||
item
.
path
);
uni
.
previewImage
({
.u-add-wrap
{
urls
:
images
,
flex-direction
:
column
;
current
:
url
,
color
:
$u-content-color
;
success
:
()
=>
{
font-size
:
28rpx
;
this
.
$emit
(
'
on-preview
'
,
url
,
this
.
lists
);
}
},
fail
:
()
=>
{
.u-add-tips
{
uni
.
showToast
({
margin-top
:
20rpx
;
title
:
'
预览图片失败
'
,
}
icon
:
'
none
'
});
.u-add-wrap__hover
{
}
background-color
:
rgb
(
235
,
236
,
238
);
});
}
}
}
.u-preview-image
{
}
display
:
block
;
</
script
>
width
:
100%
;
height
:
100%
;
<
style
lang=
"scss"
scoped
>
}
.u-upload
{
display
:
flex
;
.u-delete-icon
{
flex-wrap
:
wrap
;
position
:
absolute
;
align-items
:
center
;
top
:
10rpx
;
}
right
:
10rpx
;
z-index
:
10
;
.u-list-item
{
background-color
:
$u-type-error
;
width
:
200rpx
;
border-radius
:
100rpx
;
height
:
200rpx
;
width
:
44rpx
;
overflow
:
hidden
;
height
:
44rpx
;
margin
:
10rpx
;
display
:
flex
;
background
:
rgb
(
244
,
245
,
246
);
align-items
:
center
;
position
:
relative
;
justify-content
:
center
;
border-radius
:
10rpx
;
}
display
:
inline-flex
;
align-items
:
center
;
.u-icon
{
justify-content
:
center
;
display
:
flex
;
}
align-items
:
center
;
justify-content
:
center
;
.u-preview-wrap
{
}
border
:
1px
solid
rgb
(
235
,
236
,
238
);
}
.u-progress
{
position
:
absolute
;
.u-add-wrap
{
bottom
:
10rpx
;
flex-direction
:
column
;
left
:
8rpx
;
color
:
$u-content-color
;
right
:
8rpx
;
font-size
:
28rpx
;
z-index
:
9
;
}
width
:
auto
;
}
.u-add-tips
{
margin-top
:
20rpx
;
.u-error-btn
{
}
color
:
#FFFFFF
;
background-color
:
$u-type-error
;
.u-add-wrap__hover
{
font-size
:
20rpx
;
background-color
:
rgb
(
235
,
236
,
238
);
padding
:
4px
0
;
}
text-align
:
center
;
position
:
absolute
;
.u-preview-image
{
bottom
:
0
;
display
:
block
;
left
:
0
;
width
:
100%
;
right
:
0
;
height
:
100%
;
z-index
:
9
;
}
line-height
:
1
;
}
.u-delete-icon
{
position
:
absolute
;
top
:
10rpx
;
right
:
10rpx
;
z-index
:
10
;
background-color
:
$u-type-error
;
border-radius
:
100rpx
;
width
:
44rpx
;
height
:
44rpx
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
}
.u-icon
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
}
.u-progress
{
position
:
absolute
;
bottom
:
10rpx
;
left
:
8rpx
;
right
:
8rpx
;
z-index
:
9
;
width
:
auto
;
}
.u-error-btn
{
color
:
#FFFFFF
;
background-color
:
$u-type-error
;
font-size
:
20rpx
;
padding
:
4px
0
;
text-align
:
center
;
position
:
absolute
;
bottom
:
0
;
left
:
0
;
right
:
0
;
z-index
:
9
;
line-height
:
1
;
}
</
style
>
</
style
>
uview/components/u-verification-code/u-verification-code.vue
View file @
4a3a32b8
<
template
>
<
template
>
<view
class=
"u-code-wrap"
>
<view
class=
"u-code-wrap"
>
</view>
</view>
</
template
>
</
template
>
<
script
>
<
script
>
export
default
{
/**
props
:
{
* alertTips 提示
// 倒计时总秒数
* @description 考虑到用户实际发送验证码的场景,可能是一个按钮,也可能是一段文字,提示语各有不同,所以本组件 不提供界面显示,只提供提示语,由用户将提示语嵌入到具体的场景
seconds
:
{
* @tutorial https://www.uviewui.com/components/verificationCode.html
type
:
[
String
,
Number
],
* @property {Number String} seconds 倒计时所需的秒数(默认60)
default
:
60
* @property {String} start-text 开始前的提示语,见官网说明(默认获取验证码)
},
* @property {String} change-text 倒计时期间的提示语,必须带有字母"x",见官网说明(默认X秒重新获取)
// 尚未开始时提示
* @property {String} end-text 倒计结束的提示语,见官网说明(默认重新获取)
startText
:
{
* @event {Function} change 倒计时期间,每秒触发一次
type
:
String
,
* @event {Function} start 开始倒计时触发
default
:
'
获取验证码
'
* @event {Function} end 结束倒计时触发
},
* @example <u-verification-code :seconds="seconds" @end="end" @start="start" ref="uCode"
// 正在倒计时中的提示
*/
changeText
:
{
export
default
{
type
:
String
,
name
:
"
u-verification-code
"
,
default
:
'
X秒重新获取
'
props
:
{
},
// 倒计时总秒数
// 倒计时结束时的提示
seconds
:
{
endText
:
{
type
:
[
String
,
Number
],
type
:
String
,
default
:
60
default
:
'
重新获取
'
},
},
// 尚未开始时提示
},
startText
:
{
data
()
{
type
:
String
,
return
{
default
:
'
获取验证码
'
secNum
:
this
.
seconds
,
},
timer
:
null
,
// 正在倒计时中的提示
canGetCode
:
true
,
// 是否可以执行验证码操作
changeText
:
{
}
type
:
String
,
},
default
:
'
X秒重新获取
'
mounted
()
{
},
this
.
changeEvent
(
this
.
startText
);
// 倒计时结束时的提示
},
endText
:
{
methods
:
{
type
:
String
,
// 开始倒计时
default
:
'
重新获取
'
start
()
{
},
this
.
secNum
=
this
.
seconds
;
},
this
.
$emit
(
'
start
'
);
data
()
{
this
.
canGetCode
=
false
;
return
{
// 这里放这句,是为了一开始时就提示,否则要等setInterval的1秒后才会有提示
secNum
:
this
.
seconds
,
this
.
changeEvent
(
this
.
changeText
.
replace
(
/x|X/
,
this
.
secNum
));
timer
:
null
,
this
.
timer
=
setInterval
(()
=>
{
canGetCode
:
true
,
// 是否可以执行验证码操作
if
(
--
this
.
secNum
)
{
}
// 用当前倒计时的秒数替换提示字符串中的"x"字母
},
this
.
changeEvent
(
this
.
changeText
.
replace
(
/x|X/
,
this
.
secNum
));
mounted
()
{
}
else
{
this
.
changeEvent
(
this
.
startText
);
clearInterval
(
this
.
timer
);
},
this
.
changeEvent
(
this
.
endText
);
methods
:
{
this
.
secNum
=
this
.
seconds
;
// 开始倒计时
this
.
$emit
(
'
end
'
);
start
()
{
this
.
canGetCode
=
true
;
this
.
secNum
=
this
.
seconds
;
}
this
.
$emit
(
'
start
'
);
},
1000
);
this
.
canGetCode
=
false
;
},
// 这里放这句,是为了一开始时就提示,否则要等setInterval的1秒后才会有提示
// 重置,可以让用户再次获取验证码
this
.
changeEvent
(
this
.
changeText
.
replace
(
/x|X/
,
this
.
secNum
));
reset
()
{
this
.
timer
=
setInterval
(()
=>
{
this
.
canGetCode
=
true
;
if
(
--
this
.
secNum
)
{
clearInterval
(
this
.
timer
);
// 用当前倒计时的秒数替换提示字符串中的"x"字母
this
.
secNum
=
this
.
seconds
;
this
.
changeEvent
(
this
.
changeText
.
replace
(
/x|X/
,
this
.
secNum
));
this
.
changeEvent
(
this
.
endText
);
}
else
{
},
clearInterval
(
this
.
timer
);
changeEvent
(
text
)
{
this
.
changeEvent
(
this
.
endText
);
this
.
$emit
(
'
change
'
,
text
);
this
.
secNum
=
this
.
seconds
;
}
this
.
$emit
(
'
end
'
);
},
this
.
canGetCode
=
true
;
beforeDestroy
()
{
}
clearTimeout
(
this
.
timer
)
},
1000
);
},
},
}
// 重置,可以让用户再次获取验证码
</
script
>
reset
()
{
this
.
canGetCode
=
true
;
<
style
lang=
"scss"
scoped
>
clearInterval
(
this
.
timer
);
.u-code-wrap
{
this
.
secNum
=
this
.
seconds
;
width
:
0
;
this
.
changeEvent
(
this
.
endText
);
height
:
0
;
},
position
:
fixed
;
changeEvent
(
text
)
{
z-index
:
-1
;
this
.
$emit
(
'
change
'
,
text
);
}
}
},
beforeDestroy
()
{
clearTimeout
(
this
.
timer
)
},
}
</
script
>
<
style
lang=
"scss"
scoped
>
.u-code-wrap
{
width
:
0
;
height
:
0
;
position
:
fixed
;
z-index
:
-1
;
}
</
style
>
</
style
>
uview/components/u-waterfall/u-waterfall.vue
View file @
4a3a32b8
<
template
>
<
template
>
<view
class=
"u-waterfall"
>
<view
class=
"u-waterfall"
>
<view
id=
"u-left-cloumn"
class=
"u-cloumn"
>
<view
id=
"u-left-cloumn"
class=
"u-cloumn"
>
<slot
name=
"left"
:list=
"leftList"
></slot>
<slot
name=
"left"
:list=
"leftList"
></slot>
</view>
</view>
<view
id=
"u-right-cloumn"
class=
"u-cloumn"
>
<view
id=
"u-right-cloumn"
class=
"u-cloumn"
>
<slot
name=
"right"
:list=
"rightList"
></slot>
<slot
name=
"right"
:list=
"rightList"
></slot>
</view>
</view>
</view>
</view>
</
template
>
</
template
>
<
script
>
<
script
>
export
default
{
/**
props
:
{
* alertTips 提示
flowList
:
{
* @description 这是一个瀑布流形式的组件,内容分为左右两列,结合uView的懒加载组件效果更佳。相较于某些只是奇偶数左右分别,或者没有利用vue作用域插槽的做法,uView的瀑布流实现了真正的 组件化,搭配LazyLoad 懒加载和loadMore 加载更多组件,让您开箱即用,眼前一亮。
// 瀑布流数据
* @tutorial https://www.uviewui.com/components/waterfall.html
type
:
Array
,
* @property {Array} flow-list 用于渲染的数据
required
:
true
,
* @property {String Number} add-time 单条数据添加到队列的时间间隔,单位ms,见上方注意事项说明(默认200)
default
:
function
()
{
* @example <u-waterfall :flowList="flowList"></u-waterfall>
return
[];
*/
}
export
default
{
},
name
:
"
u-waterfall
"
,
// 每次向结构插入数据的时间间隔,间隔越长,越能保证两列高度相近,但是对用户体验越不好
props
:
{
// 单位ms
flowList
:
{
addTime
:
{
// 瀑布流数据
type
:
[
Number
,
String
],
type
:
Array
,
default
:
200
required
:
true
,
}
default
:
function
()
{
},
return
[];
provide
()
{
}
return
{
},
uWaterfall
:
this
// 每次向结构插入数据的时间间隔,间隔越长,越能保证两列高度相近,但是对用户体验越不好
}
// 单位ms
},
addTime
:
{
data
()
{
type
:
[
Number
,
String
],
return
{
default
:
200
leftList
:
[],
}
rightList
:
[],
},
tempList
:
[],
provide
()
{
children
:
[]
return
{
}
uWaterfall
:
this
},
}
watch
:
{
},
copyFlowList
(
nVal
,
oVal
)
{
data
()
{
// 取差值,即这一次数组变化新增的部分
return
{
let
startIndex
=
Array
.
isArray
(
oVal
)
&&
oVal
.
length
>
0
?
oVal
.
length
:
0
;
leftList
:
[],
this
.
tempList
=
this
.
cloneData
(
nVal
.
slice
(
startIndex
));
rightList
:
[],
this
.
splitData
();
tempList
:
[],
},
children
:
[]
},
}
mounted
()
{
},
this
.
tempList
=
this
.
cloneData
(
this
.
copyFlowList
);
watch
:
{
this
.
splitData
();
copyFlowList
(
nVal
,
oVal
)
{
},
// 取差值,即这一次数组变化新增的部分
computed
:
{
let
startIndex
=
Array
.
isArray
(
oVal
)
&&
oVal
.
length
>
0
?
oVal
.
length
:
0
;
// 破坏flowList变量的引用,否则watch的结果新旧值是一样的
this
.
tempList
=
this
.
cloneData
(
nVal
.
slice
(
startIndex
));
copyFlowList
()
{
this
.
splitData
();
return
this
.
cloneData
(
this
.
flowList
);
},
}
},
},
mounted
()
{
methods
:
{
this
.
tempList
=
this
.
cloneData
(
this
.
copyFlowList
);
async
splitData
()
{
this
.
splitData
();
if
(
!
this
.
tempList
.
length
)
return
;
},
let
leftRect
=
await
this
.
$uGetRect
(
'
#u-left-cloumn
'
);
computed
:
{
let
rightRect
=
await
this
.
$uGetRect
(
'
#u-right-cloumn
'
);
// 破坏flowList变量的引用,否则watch的结果新旧值是一样的
// 如果左边小于或等于右边,就添加到左边,否则添加到右边
copyFlowList
()
{
let
item
=
this
.
tempList
[
0
];
return
this
.
cloneData
(
this
.
flowList
);
if
(
leftRect
.
height
<
rightRect
.
height
)
{
}
this
.
leftList
.
push
(
item
);
},
}
else
if
(
leftRect
.
height
>
rightRect
.
height
)
{
methods
:
{
this
.
rightList
.
push
(
item
);
async
splitData
()
{
}
else
{
if
(
!
this
.
tempList
.
length
)
return
;
// 这里是为了保证第一和第二张添加时,左右都能有内容
let
leftRect
=
await
this
.
$uGetRect
(
'
#u-left-cloumn
'
);
// 因为添加第一张,实际队列的高度可能还是0,这时需要根据队列元素长度判断下一个该放哪边
let
rightRect
=
await
this
.
$uGetRect
(
'
#u-right-cloumn
'
);
if
(
this
.
leftList
.
length
<=
this
.
rightList
.
length
)
{
// 如果左边小于或等于右边,就添加到左边,否则添加到右边
this
.
leftList
.
push
(
item
);
let
item
=
this
.
tempList
[
0
];
}
else
{
if
(
leftRect
.
height
<
rightRect
.
height
)
{
this
.
rightList
.
push
(
item
);
this
.
leftList
.
push
(
item
);
}
}
else
if
(
leftRect
.
height
>
rightRect
.
height
)
{
}
this
.
rightList
.
push
(
item
);
// 移除临时列表的第一项
}
else
{
this
.
tempList
.
splice
(
0
,
1
);
// 这里是为了保证第一和第二张添加时,左右都能有内容
// 如果临时数组还有数据,继续循环
// 因为添加第一张,实际队列的高度可能还是0,这时需要根据队列元素长度判断下一个该放哪边
if
(
this
.
tempList
.
length
)
{
if
(
this
.
leftList
.
length
<=
this
.
rightList
.
length
)
{
setTimeout
(()
=>
{
this
.
leftList
.
push
(
item
);
this
.
splitData
();
}
else
{
},
this
.
addTime
)
this
.
rightList
.
push
(
item
);
}
}
},
}
// 复制而不是引用对象和数组
// 移除临时列表的第一项
cloneData
(
data
)
{
this
.
tempList
.
splice
(
0
,
1
);
return
JSON
.
parse
(
JSON
.
stringify
(
data
));
// 如果临时数组还有数据,继续循环
}
if
(
this
.
tempList
.
length
)
{
}
setTimeout
(()
=>
{
}
this
.
splitData
();
</
script
>
},
this
.
addTime
)
}
<
style
lang=
"scss"
scoped
>
},
.u-waterfall
{
// 复制而不是引用对象和数组
display
:
flex
;
cloneData
(
data
)
{
flex-direction
:
row
;
return
JSON
.
parse
(
JSON
.
stringify
(
data
));
align-items
:
flex-start
;
}
}
}
}
.u-cloumn
{
</
script
>
display
:
flex
;
flex
:
1
;
<
style
lang=
"scss"
scoped
>
flex-direction
:
column
;
.u-waterfall
{
height
:
auto
;
display
:
flex
;
}
flex-direction
:
row
;
align-items
:
flex-start
;
.u-image
{
}
width
:
100%
;
}
.u-cloumn
{
display
:
flex
;
flex
:
1
;
flex-direction
:
column
;
height
:
auto
;
}
.u-image
{
width
:
100%
;
}
</
style
>
</
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment