parent
5a9e8516c8
commit
4dacb5cb65
19 changed files with 1808 additions and 191 deletions
@ -0,0 +1,130 @@ |
||||
<template> |
||||
<view |
||||
class="u-mask" |
||||
hover-stop-propagation |
||||
:style="[maskStyle, zoomStyle]" |
||||
@tap="click" |
||||
@touchmove.stop.prevent="() => {}" |
||||
:class="{ |
||||
'u-mask-zoom': zoom, |
||||
'u-mask-show': show, |
||||
}" |
||||
> |
||||
<slot /> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* mask 遮罩 |
||||
* @description 创建一个遮罩层,用于强调特定的页面元素,并阻止用户对遮罩下层的内容进行操作,一般用于弹窗场景 |
||||
* @tutorial https://www.uviewui.com/components/mask.html |
||||
* @property {Boolean} show 是否显示遮罩(默认false) |
||||
* @property {String Number} z-index z-index 层级(默认1070) |
||||
* @property {Object} custom-style 自定义样式对象,见上方说明 |
||||
* @property {String Number} duration 动画时长,单位毫秒(默认300) |
||||
* @property {Boolean} zoom 是否使用scale对这招进行缩放(默认true) |
||||
* @property {Boolean} mask-click-able 遮罩是否可点击,为false时点击不会发送click事件(默认true) |
||||
* @event {Function} click mask-click-able为true时,点击遮罩发送此事件 |
||||
* @example <u-mask :show="show" @click="show = false"></u-mask> |
||||
*/ |
||||
export default { |
||||
name: "u-mask", |
||||
props: { |
||||
// 是否显示遮罩 |
||||
show: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
// 层级z-index |
||||
zIndex: { |
||||
type: [Number, String], |
||||
default: "", |
||||
}, |
||||
// 用户自定义样式 |
||||
customStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
}, |
||||
}, |
||||
// 遮罩的动画样式, 是否使用使用zoom进行scale进行缩放 |
||||
zoom: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
// 遮罩的过渡时间,单位为ms |
||||
duration: { |
||||
type: [Number, String], |
||||
default: 300, |
||||
}, |
||||
// 是否可以通过点击遮罩进行关闭 |
||||
maskClickAble: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
zoomStyle: { |
||||
transform: "", |
||||
}, |
||||
scale: "scale(1.2, 1.2)", |
||||
}; |
||||
}, |
||||
watch: { |
||||
show(n) { |
||||
if (n && this.zoom) { |
||||
// 当展示遮罩的时候,设置scale为1,达到缩小(原来为1.2)的效果 |
||||
this.zoomStyle.transform = "scale(1, 1)"; |
||||
} else if (!n && this.zoom) { |
||||
// 当隐藏遮罩的时候,设置scale为1.2,达到放大(因为显示遮罩时已重置为1)的效果 |
||||
this.zoomStyle.transform = this.scale; |
||||
} |
||||
}, |
||||
}, |
||||
computed: { |
||||
maskStyle() { |
||||
let style = {}; |
||||
style.backgroundColor = "rgba(0, 0, 0, 0.6)"; |
||||
if (this.show) |
||||
style.zIndex = this.zIndex ? this.zIndex : this.$u.zIndex.mask; |
||||
else style.zIndex = -1; |
||||
style.transition = `all ${this.duration / 1000}s ease-in-out`; |
||||
// 判断用户传递的对象是否为空,不为空就进行合并 |
||||
if (Object.keys(this.customStyle).length) |
||||
style = { |
||||
...style, |
||||
...this.customStyle, |
||||
}; |
||||
return style; |
||||
}, |
||||
}, |
||||
methods: { |
||||
click() { |
||||
if (!this.maskClickAble) return; |
||||
this.$emit("click"); |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.u-mask { |
||||
position: fixed; |
||||
top: 0; |
||||
left: 0; |
||||
right: 0; |
||||
bottom: 0; |
||||
opacity: 0; |
||||
transition: transform 0.3s; |
||||
} |
||||
|
||||
.u-mask-show { |
||||
opacity: 1; |
||||
} |
||||
|
||||
.u-mask-zoom { |
||||
transform: scale(1.2, 1.2); |
||||
} |
||||
</style> |
@ -0,0 +1,510 @@ |
||||
<template> |
||||
<view |
||||
v-if="visibleSync" |
||||
:style="[ |
||||
customStyle, |
||||
{ |
||||
zIndex: uZindex - 1, |
||||
}, |
||||
]" |
||||
class="u-drawer" |
||||
hover-stop-propagation |
||||
> |
||||
<z-mask |
||||
:duration="duration" |
||||
:custom-style="maskCustomStyle" |
||||
:maskClickAble="maskCloseAble" |
||||
:z-index="uZindex - 2" |
||||
:show="showDrawer && mask" |
||||
@click="maskClick" |
||||
></z-mask> |
||||
<view |
||||
class="u-drawer-content" |
||||
@tap="modeCenterClose(mode)" |
||||
:class="[ |
||||
safeAreaInsetBottom ? 'safe-area-inset-bottom' : '', |
||||
'u-drawer-' + mode, |
||||
showDrawer ? 'u-drawer-content-visible' : '', |
||||
zoom && mode == 'center' ? 'u-animation-zoom' : '', |
||||
]" |
||||
@transitionend="transitionend" |
||||
@touchmove.stop.prevent |
||||
@tap.stop.prevent |
||||
:style="[style]" |
||||
> |
||||
<view |
||||
class="u-mode-center-box" |
||||
@tap.stop.prevent |
||||
@touchmove.stop.prevent |
||||
v-if="mode == 'center'" |
||||
:style="[centerStyle]" |
||||
> |
||||
<u-icon |
||||
@click="close" |
||||
v-if="closeable" |
||||
class="u-close" |
||||
:class="['u-close--' + closeIconPos]" |
||||
:name="closeIcon" |
||||
:color="closeIconColor" |
||||
:size="closeIconSize" |
||||
></u-icon> |
||||
<scroll-view class="u-drawer__scroll-view" scroll-y="true"> |
||||
<slot /> |
||||
</scroll-view> |
||||
<slot name="fixedContent" /> |
||||
</view> |
||||
<scroll-view class="u-drawer__scroll-view" scroll-y="true" v-else> |
||||
<slot /> |
||||
</scroll-view> |
||||
<view @tap="close" class="u-close" :class="['u-close--' + closeIconPos]"> |
||||
<u-icon |
||||
v-if="mode != 'center' && closeable" |
||||
:name="closeIcon" |
||||
:color="closeIconColor" |
||||
:size="closeIconSize" |
||||
></u-icon> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
/** |
||||
* popup 弹窗 |
||||
* @description 弹出层容器,用于展示弹窗、信息提示等内容,支持上、下、左、右和中部弹出。组件只提供容器,内部内容由用户自定义 |
||||
* @tutorial https://www.uviewui.com/components/popup.html |
||||
* @property {String} mode 弹出方向(默认left) |
||||
* @property {Boolean} mask 是否显示遮罩(默认true) |
||||
* @property {Stringr | Number} length mode=left | 见官网说明(默认auto) |
||||
* @property {Boolean} zoom 是否开启缩放动画,只在mode为center时有效(默认true) |
||||
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false) |
||||
* @property {Boolean} mask-close-able 点击遮罩是否可以关闭弹出层(默认true) |
||||
* @property {Object} custom-style 用户自定义样式 |
||||
* @property {Stringr | Number} negative-top 中部弹出时,往上偏移的值 |
||||
* @property {Numberr | String} border-radius 弹窗圆角值(默认0) |
||||
* @property {Numberr | String} z-index 弹出内容的z-index值(默认1075) |
||||
* @property {Boolean} closeable 是否显示关闭图标(默认false) |
||||
* @property {String} close-icon 关闭图标的名称,只能uView的内置图标 |
||||
* @property {String} close-icon-pos 自定义关闭图标位置(默认top-right) |
||||
* @property {String} close-icon-color 关闭图标的颜色(默认#909399) |
||||
* @property {Number | String} close-icon-size 关闭图标的大小,单位rpx(默认30) |
||||
* @event {Function} open 弹出层打开 |
||||
* @event {Function} close 弹出层收起 |
||||
* @example <u-popup v-model="show"><view>出淤泥而不染,濯清涟而不妖</view></u-popup> |
||||
*/ |
||||
import ZMask from "./mask.vue"; |
||||
export default { |
||||
name: "popup", |
||||
components: { |
||||
ZMask, |
||||
}, |
||||
props: { |
||||
/** |
||||
* 显示状态 |
||||
*/ |
||||
show: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
/** |
||||
* 弹出方向,left|right|top|bottom|center |
||||
*/ |
||||
mode: { |
||||
type: String, |
||||
default: "left", |
||||
}, |
||||
/** |
||||
* 是否显示遮罩 |
||||
*/ |
||||
mask: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
// 抽屉的宽度(mode=left|right),或者高度(mode=top|bottom),单位rpx,或者"auto" |
||||
// 或者百分比"50%",表示由内容撑开高度或者宽度 |
||||
length: { |
||||
type: [Number, String], |
||||
default: "auto", |
||||
}, |
||||
// 是否开启缩放动画,只在mode=center时有效 |
||||
zoom: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
// 是否开启底部安全区适配,开启的话,会在iPhoneX机型底部添加一定的内边距 |
||||
safeAreaInsetBottom: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
// 是否可以通过点击遮罩进行关闭 |
||||
maskCloseAble: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
// 用户自定义样式 |
||||
customStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
}, |
||||
}, |
||||
value: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
// 此为内部参数,不在文档对外使用,为了解决Picker和keyboard等融合了弹窗的组件 |
||||
// 对v-model双向绑定多层调用造成报错不能修改props值的问题 |
||||
popup: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
// 显示显示弹窗的圆角,单位rpx |
||||
borderRadius: { |
||||
type: [Number, String], |
||||
default: 0, |
||||
}, |
||||
zIndex: { |
||||
type: [Number, String], |
||||
default: "10075", |
||||
}, |
||||
// 是否显示关闭图标 |
||||
closeable: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
// 关闭图标的名称,只能uView的内置图标 |
||||
closeIcon: { |
||||
type: String, |
||||
default: "close", |
||||
}, |
||||
// 自定义关闭图标位置,top-left为左上角,top-right为右上角,bottom-left为左下角,bottom-right为右下角 |
||||
closeIconPos: { |
||||
type: String, |
||||
default: "top-right", |
||||
}, |
||||
// 关闭图标的颜色 |
||||
closeIconColor: { |
||||
type: String, |
||||
default: "#909399", |
||||
}, |
||||
// 关闭图标的大小,单位rpx |
||||
closeIconSize: { |
||||
type: [String, Number], |
||||
default: "30", |
||||
}, |
||||
// 宽度,只对左,右,中部弹出时起作用,单位rpx,或者"auto" |
||||
// 或者百分比"50%",表示由内容撑开高度或者宽度,优先级高于length参数 |
||||
width: { |
||||
type: String, |
||||
default: "", |
||||
}, |
||||
// 高度,只对上,下,中部弹出时起作用,单位rpx,或者"auto" |
||||
// 或者百分比"50%",表示由内容撑开高度或者宽度,优先级高于length参数 |
||||
height: { |
||||
type: String, |
||||
default: "", |
||||
}, |
||||
// 给一个负的margin-top,往上偏移,避免和键盘重合的情况,仅在mode=center时有效 |
||||
negativeTop: { |
||||
type: [String, Number], |
||||
default: 0, |
||||
}, |
||||
// 遮罩的样式,一般用于修改遮罩的透明度 |
||||
maskCustomStyle: { |
||||
type: Object, |
||||
default() { |
||||
return {}; |
||||
}, |
||||
}, |
||||
// 遮罩打开或收起的动画过渡时间,单位ms |
||||
duration: { |
||||
type: [String, Number], |
||||
default: 250, |
||||
}, |
||||
// 中间弹窗的背景颜色值 |
||||
centerPupBg: { |
||||
type: String, |
||||
default: "#fff", |
||||
}, |
||||
bgColor: { |
||||
type: String, |
||||
default: "#fff", |
||||
}, |
||||
}, |
||||
|
||||
data() { |
||||
return { |
||||
visibleSync: false, |
||||
showDrawer: false, |
||||
timer: null, |
||||
closeFromInner: false, // value的值改变,是发生在内部还是外部 |
||||
}; |
||||
}, |
||||
computed: { |
||||
// 根据mode的位置,设定其弹窗的宽度(mode = left|right),或者高度(mode = top|bottom) |
||||
style() { |
||||
let style = {}; |
||||
// 如果是左边或者上边弹出时,需要给translate设置为负值,用于隐藏 |
||||
if (this.mode == "left" || this.mode == "right") { |
||||
style = { |
||||
width: this.width |
||||
? this.getUnitValue(this.width) |
||||
: this.getUnitValue(this.length), |
||||
height: "100%", |
||||
transform: `translate3D(${ |
||||
this.mode == "left" ? "-100%" : "100%" |
||||
},0px,0px)`, |
||||
background: this.bgColor, |
||||
}; |
||||
} else if (this.mode == "top" || this.mode == "bottom") { |
||||
style = { |
||||
width: "100%", |
||||
height: this.height |
||||
? this.getUnitValue(this.height) |
||||
: this.getUnitValue(this.length), |
||||
transform: `translate3D(0px,${ |
||||
this.mode == "top" ? "-100%" : "100%" |
||||
},0px)`, |
||||
background: this.bgColor, |
||||
}; |
||||
} |
||||
style.zIndex = this.uZindex; |
||||
// 如果用户设置了borderRadius值,添加弹窗的圆角 |
||||
if (this.borderRadius) { |
||||
switch (this.mode) { |
||||
case "left": |
||||
style.borderRadius = `0 ${this.borderRadius}rpx ${this.borderRadius}rpx 0`; |
||||
break; |
||||
case "top": |
||||
style.borderRadius = `0 0 ${this.borderRadius}rpx ${this.borderRadius}rpx`; |
||||
break; |
||||
case "right": |
||||
style.borderRadius = `${this.borderRadius}rpx 0 0 ${this.borderRadius}rpx`; |
||||
break; |
||||
case "bottom": |
||||
style.borderRadius = `${this.borderRadius}rpx ${this.borderRadius}rpx 0 0`; |
||||
break; |
||||
default: |
||||
} |
||||
// 不加可能圆角无效 |
||||
style.overflow = "hidden"; |
||||
} |
||||
if (this.duration) |
||||
style.transition = `all ${this.duration / 1000}s linear`; |
||||
return style; |
||||
}, |
||||
// 中部弹窗的特有样式 |
||||
centerStyle() { |
||||
let style = {}; |
||||
style.width = this.width |
||||
? this.getUnitValue(this.width) |
||||
: this.getUnitValue(this.length); |
||||
// 中部弹出的模式,如果没有设置高度,就用auto值,由内容撑开高度 |
||||
style.height = this.height ? this.getUnitValue(this.height) : "auto"; |
||||
style.zIndex = this.uZindex; |
||||
// style.marginTop = `-${this.$u.addUnit(this.negativeTop)}`; |
||||
style.background = this.centerPupBg; |
||||
if (this.borderRadius) { |
||||
style.borderRadius = `${this.borderRadius}rpx`; |
||||
// 不加可能圆角无效 |
||||
style.overflow = "hidden"; |
||||
} |
||||
return style; |
||||
}, |
||||
// 计算整理后的z-index值 |
||||
uZindex() { |
||||
return this.zIndex ? this.zIndex : this.$u.zIndex.popup; |
||||
}, |
||||
}, |
||||
watch: { |
||||
value(val) { |
||||
if (val) { |
||||
this.open(); |
||||
} else if (!this.closeFromInner) { |
||||
this.close(); |
||||
} |
||||
this.closeFromInner = false; |
||||
}, |
||||
}, |
||||
mounted() { |
||||
// 组件渲染完成时,检查value是否为true,如果是,弹出popup |
||||
this.value && this.open(); |
||||
}, |
||||
methods: { |
||||
// 动画结束回调 |
||||
transitionend(e) { |
||||
// console.log(e, "动画结束popup"); |
||||
}, |
||||
// 判断传入的值,是否带有单位,如果没有,就默认用rpx单位 |
||||
getUnitValue(val) { |
||||
if (/(%|px|rpx|auto)$/.test(val)) return val; |
||||
else return val + "rpx"; |
||||
}, |
||||
// 遮罩被点击 |
||||
maskClick() { |
||||
this.close(); |
||||
}, |
||||
close() { |
||||
// 标记关闭是内部发生的,否则修改了value值,导致watch中对value检测,导致再执行一遍close |
||||
// 造成@close事件触发两次 |
||||
this.closeFromInner = true; |
||||
this.change("showDrawer", "visibleSync", false); |
||||
}, |
||||
// 中部弹出时,需要.u-drawer-content将居中内容,此元素会铺满屏幕,点击需要关闭弹窗 |
||||
// 让其只在mode=center时起作用 |
||||
modeCenterClose(mode) { |
||||
if (mode != "center" || !this.maskCloseAble) return; |
||||
this.close(); |
||||
}, |
||||
open() { |
||||
this.change("visibleSync", "showDrawer", true); |
||||
}, |
||||
// 此处的原理是,关闭时先通过动画隐藏弹窗和遮罩,再移除整个组件 |
||||
// 打开时,先渲染组件,延时一定时间再让遮罩和弹窗的动画起作用 |
||||
change(param1, param2, status) { |
||||
// 如果this.popup为false,意味着为picker,actionsheet等组件调用了popup组件 |
||||
if (this.popup == true) { |
||||
this.$emit("input", status); |
||||
} |
||||
this[param1] = status; |
||||
if (status) { |
||||
// #ifdef H5 || MP |
||||
this.timer = setTimeout(() => { |
||||
this[param2] = status; |
||||
this.$emit(status ? "open" : "close"); |
||||
}, 50); |
||||
// #endif |
||||
// #ifndef H5 || MP |
||||
this.$nextTick(() => { |
||||
this[param2] = status; |
||||
this.$emit(status ? "open" : "close"); |
||||
}); |
||||
// #endif |
||||
} else { |
||||
this.timer = setTimeout(() => { |
||||
this[param2] = status; |
||||
this.$emit(status ? "open" : "close"); |
||||
}, this.duration); |
||||
} |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
@mixin vue-flex($direction: row) { |
||||
/* #ifndef APP-NVUE */ |
||||
display: flex; |
||||
flex-direction: $direction; |
||||
/* #endif */ |
||||
} |
||||
.u-drawer { |
||||
/* #ifndef APP-NVUE */ |
||||
display: block; |
||||
/* #endif */ |
||||
position: fixed; |
||||
top: 0; |
||||
left: 0; |
||||
right: 0; |
||||
bottom: 0; |
||||
overflow: hidden; |
||||
} |
||||
|
||||
.u-drawer-content { |
||||
/* #ifndef APP-NVUE */ |
||||
display: block; |
||||
/* #endif */ |
||||
position: absolute; |
||||
z-index: 1003; |
||||
transition: all 0.25s linear; |
||||
} |
||||
|
||||
.u-drawer__scroll-view { |
||||
width: 100%; |
||||
height: 100%; |
||||
} |
||||
|
||||
.u-drawer-left { |
||||
top: 0; |
||||
bottom: 0; |
||||
left: 0; |
||||
} |
||||
|
||||
.u-drawer-right { |
||||
right: 0; |
||||
top: 0; |
||||
bottom: 0; |
||||
} |
||||
|
||||
.u-drawer-top { |
||||
top: 0; |
||||
left: 0; |
||||
right: 0; |
||||
} |
||||
|
||||
.u-drawer-bottom { |
||||
bottom: 0; |
||||
left: 0; |
||||
right: 0; |
||||
} |
||||
|
||||
.u-drawer-center { |
||||
@include vue-flex; |
||||
flex-direction: column; |
||||
bottom: 0; |
||||
left: 0; |
||||
right: 0; |
||||
top: 0; |
||||
justify-content: center; |
||||
align-items: center; |
||||
opacity: 0; |
||||
z-index: 99999; |
||||
} |
||||
|
||||
.u-mode-center-box { |
||||
min-width: 100rpx; |
||||
min-height: 100rpx; |
||||
/* #ifndef APP-NVUE */ |
||||
display: block; |
||||
/* #endif */ |
||||
position: relative; |
||||
} |
||||
|
||||
.u-drawer-content-visible.u-drawer-center { |
||||
transform: scale(1); |
||||
opacity: 1; |
||||
} |
||||
|
||||
.u-animation-zoom { |
||||
transform: scale(1.15); |
||||
} |
||||
|
||||
.u-drawer-content-visible { |
||||
transform: translate3D(0px, 0px, 0px) !important; |
||||
} |
||||
|
||||
.u-close { |
||||
position: absolute; |
||||
z-index: 3; |
||||
} |
||||
|
||||
.u-close--top-left { |
||||
top: 30rpx; |
||||
left: 30rpx; |
||||
} |
||||
|
||||
.u-close--top-right { |
||||
top: 30rpx; |
||||
right: 30rpx; |
||||
} |
||||
|
||||
.u-close--bottom-left { |
||||
bottom: 30rpx; |
||||
left: 30rpx; |
||||
} |
||||
|
||||
.u-close--bottom-right { |
||||
right: 30rpx; |
||||
bottom: 30rpx; |
||||
} |
||||
</style> |
@ -0,0 +1,155 @@ |
||||
## 导入即用 全端支持 |
||||
|
||||
### 有问题 + wx : zy597172583 标注来意 可评论 看到及时回复 |
||||
1.使用方式 |
||||
|
||||
```javascript |
||||
<template> |
||||
<filter-popup :data="filterData" :form.sync="filterForm" v-model="popup.filter" title="全部筛选" height="1104rpx" @finsh="subFinsh"></filter-popup> |
||||
</template> |
||||
<script> |
||||
import FilterPopup from "@/components/filter-popup/filter-popup"; |
||||
export default { |
||||
components: { |
||||
FilterPopup, |
||||
}, |
||||
data() { |
||||
return { |
||||
//筛选表单数据 |
||||
filterData: [ |
||||
{ |
||||
children: false,//是否有子项 |
||||
title: "意向度", |
||||
key: "intention_type", //键名 接收对象名字 |
||||
keyValue: "value", //获取的值是哪个 |
||||
isRadio: true, //是否单选 否则多选 |
||||
data: [ |
||||
{ |
||||
title: "一般意向", |
||||
id: 1, |
||||
value: 1, |
||||
}, |
||||
{ |
||||
title: "中意向度", |
||||
id: 2, |
||||
value: 2, |
||||
}, |
||||
{ |
||||
title: "高意向度", |
||||
id: 3, |
||||
value: 3, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
children: false,//是否有子项 |
||||
title: "手机号码", |
||||
key: "is_bind_phone", //键名 接收对象名字 |
||||
keyValue: "value", //获取的值是哪个 |
||||
isRadio: true, //是否单选 否则多选 |
||||
data: [ |
||||
{ |
||||
title: "未绑定", |
||||
value: 0, |
||||
}, |
||||
{ |
||||
title: "已绑定", |
||||
value: 1, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
children: false,//是否有子项 |
||||
title: "企微好友", |
||||
key: "is_work_customer", //键名 接收对象名字 |
||||
keyValue: "value", //获取的值是哪个 |
||||
isRadio: true, //是否单选 否则多选 |
||||
data: [ |
||||
{ |
||||
title: "未添加", |
||||
value: 0, |
||||
}, |
||||
{ |
||||
title: "已添加", |
||||
value: 1, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
children: true,//是否有子项 |
||||
isRadio: false, //是否单选 |
||||
title: "标签内容", |
||||
key: "label", //键名 接收对象名字 |
||||
keyValue: "id", //获取的值是哪个 |
||||
data: [ |
||||
{ |
||||
title: "客户重要程度", |
||||
id: 22, |
||||
children: [ |
||||
{ |
||||
title: "一般意向2", |
||||
id: 32, |
||||
value: 1, |
||||
}, |
||||
{ |
||||
title: "一般意向3", |
||||
id: 12, |
||||
value: 1, |
||||
}, |
||||
], |
||||
}, |
||||
{ |
||||
title: "客户重要程度2", |
||||
id: 122, |
||||
children: [ |
||||
{ |
||||
title: "一般意向2", |
||||
id: 43, |
||||
value: 1, |
||||
}, |
||||
{ |
||||
title: "一般意向3", |
||||
id: 23, |
||||
value: 1, |
||||
}, |
||||
], |
||||
}, |
||||
], |
||||
}, |
||||
], //筛选数据 |
||||
filterForm: {}, //选中的表单 |
||||
}; |
||||
}, |
||||
} |
||||
``` |
||||
|
||||
2.组件props |
||||
|
||||
| 参数名 | 类型 | 介绍 | |
||||
| ---------- | ------- | ------------------------------------------------- | |
||||
| form | Object | .sync双向绑定的表单值 , 可传入显示初始哪些被选中 | |
||||
| data | Array | 动态渲染选项的数据数组 | |
||||
| title | String | 标题 | |
||||
| height | String | 弹出层高度 单位 rpx px upx 百分比 vw等 | |
||||
| themeColor | String | 组件主体颜色 默认:\#0066ff | |
||||
| mask | Boolean | 是否显示弹出遮盖层 | |
||||
|
||||
3.data 参数 |
||||
|
||||
| 参数名 | 类型 | 是否必填 | 介绍 | |
||||
| -------- | ------- | -------- | ------------------------------------------------------------ | |
||||
| children | Boolean | 是 | 是否有子项 | |
||||
| data | Array | 是 | 渲染出来的选项数据 | |
||||
| isRadio | Boolean | 是 | 是否单选 单个选项指定,单选还是多选 | |
||||
| title | String | 是 | 标签内容标题 | |
||||
| key | String | 是 | 接收对象名字 会作为@finsh返回对象的键名 | |
||||
| keyValue | String | 是 | 获取的值是哪个 自定义指定获取哪个键值 value还是id或者自己定义的 | |
||||
|
||||
4.事件 |
||||
|
||||
| 事件名 | 返回参数 | 简介 | |
||||
| ------ | -------- | ----------------------------------------- | |
||||
| finsh | Object | 点击确定时触发 返回参数为选中值的对象数组 | |
||||
| close | 无 | 组件点击关闭时触发 | |
||||
|
||||
![image-20210730095456900](https://yzhsaas-cdn.qietongvip.com/asd.png) |
@ -0,0 +1,466 @@ |
||||
<template> |
||||
<popup |
||||
:mask="mask" |
||||
border-radius="50" |
||||
v-model="acceptValue" |
||||
mode="bottom" |
||||
class="filter-popup" |
||||
:height="height" |
||||
@close="close" |
||||
:style="{ '--color': themeColor }" |
||||
:mask-custom-style="{ background: 'rgba(0, 0, 0, 0.7)' }" |
||||
> |
||||
<view class="top-title flex-row-sb" v-if="showTop"> |
||||
<view></view> <view class="popup-title flex-row-c-c">{{ title }}</view |
||||
><text class="saasIcon flex-row-c-c" @click="close"></text> |
||||
</view> |
||||
|
||||
<scroll-view class="select-scroll" scroll-y :style="{ height: `calc( ${height} - 120rpx - 152rpx )` }"> |
||||
<view class="select-main"> |
||||
<view class="select-item" v-for="(item, index) in data" :key="index"> |
||||
<view class="title"> {{ item.title }} </view> |
||||
<view class="tag-list" v-if="!item.children"> |
||||
<view |
||||
class="tag-item" |
||||
:class="[acceptForm[item.key].includes(item2.value) ? 'select-on' : '']" |
||||
v-for="(item2, index2) in item.data" |
||||
:key="index2" |
||||
@click="selectTagBuyValueOrId(item2, item.key, item.keyValue, item.isRadio)" |
||||
> |
||||
{{ item2.title }} |
||||
</view> |
||||
</view> |
||||
<!-- 有childer --> |
||||
<view class="select-childer" v-else v-for="item2 in item.data" :key="item2.id"> |
||||
<view class="childer-title flex-row--c">{{ item2.title }}</view> |
||||
<view class="tag-list"> |
||||
<view |
||||
class="tag-item" |
||||
:class="[acceptForm[item.key].includes(item3.id) ? 'select-on' : '']" |
||||
v-for="item3 in item2.children" |
||||
:key="item3.id" |
||||
@click="selectTagBuyValueOrId(item3, item.key, item.keyValue, item.isRadio)" |
||||
> |
||||
{{ item3.title }} |
||||
</view> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
</view> |
||||
</scroll-view> |
||||
|
||||
<view class="filter-button flex-row-c"> |
||||
<view class="btn flex-row"> |
||||
<view class="btn-1 flex-row-c-c" @click="reset">重置</view> |
||||
<view class="btn-2 flex-row-c-c" @click="finsh">完成</view> |
||||
</view> |
||||
</view> |
||||
</popup> |
||||
</template> |
||||
|
||||
<script> |
||||
import Popup from './components/popup.vue'; |
||||
export default { |
||||
components:{ |
||||
Popup |
||||
}, |
||||
name: "filter-popup", |
||||
props: { |
||||
//选中的表单 |
||||
form: { |
||||
type: Object, |
||||
default: () => {}, |
||||
}, |
||||
//主题颜色 |
||||
themeColor: { |
||||
type: String, |
||||
default: "#0066ff", |
||||
}, |
||||
//渲染数据 |
||||
data: { |
||||
type: Array, |
||||
default: () => [], |
||||
}, |
||||
//标题 |
||||
title: { |
||||
type: String, |
||||
default: "请选择", |
||||
}, |
||||
value: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
mask: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
height: { |
||||
type: String, |
||||
default: "600rpx", |
||||
}, |
||||
//是否显示退出按钮 |
||||
showTop: { |
||||
type: Boolean, |
||||
default: true, |
||||
}, |
||||
}, |
||||
computed: { |
||||
// 接收表单 |
||||
acceptForm: { |
||||
get() { |
||||
this.originForm = JSON.parse(JSON.stringify(this.form)) |
||||
if (Object.keys(this.form).length) { |
||||
return this.form; |
||||
} else { |
||||
let obj = {}; |
||||
this.data.forEach((item) => { |
||||
obj[item.key] = []; |
||||
}); |
||||
return obj; |
||||
} |
||||
}, |
||||
set(nval) { |
||||
// console.log("set Form :>> ", nval); |
||||
this.$emit("update:form", nval); |
||||
}, |
||||
}, |
||||
acceptValue: { |
||||
get() { |
||||
return this.value; |
||||
}, |
||||
set(nval) { |
||||
this.$emit("input", nval); |
||||
}, |
||||
}, |
||||
}, |
||||
data() { |
||||
return { |
||||
originForm: {} |
||||
} |
||||
}, |
||||
methods: { |
||||
//选择存value 还是id |
||||
selectTagBuyValueOrId(item, key, keyValue, isRadio) { |
||||
//如果是单选 |
||||
if (isRadio) { |
||||
if (keyValue == "value") { |
||||
if (this.acceptForm[key].some((value) => value === item.value)) { |
||||
this.acceptForm[key] = this.acceptForm[key].filter((value) => value !== item.value); |
||||
return; |
||||
} |
||||
this.acceptForm[key] = []; |
||||
this.acceptForm[key].push(item.value); |
||||
} else { |
||||
if (this.acceptForm[key].some((id) => id === item.id)) { |
||||
this.acceptForm[key] = this.acceptForm[key].filter((id) => id !== item.id); |
||||
return; |
||||
} |
||||
this.acceptForm[key] = []; |
||||
this.acceptForm[key].push(item.id); |
||||
} |
||||
} else { |
||||
if (keyValue == "value") { |
||||
this.acceptForm[key].some((value) => value === item.value) |
||||
? (this.acceptForm[key] = this.acceptForm[key].filter((value) => value !== item.value)) |
||||
: this.acceptForm[key].push(item.value); |
||||
} else { |
||||
this.acceptForm[key].some((id) => id === item.id) |
||||
? (this.acceptForm[key] = this.acceptForm[key].filter((id) => id !== item.id)) |
||||
: this.acceptForm[key].push(item.id); |
||||
} |
||||
} |
||||
|
||||
this.acceptForm = this.acceptForm; |
||||
}, |
||||
// 点击完成 |
||||
finsh() { |
||||
this.$emit("finsh", this.acceptForm); |
||||
this.$emit("input", false); |
||||
}, |
||||
close() { |
||||
this.$emit("input", false); |
||||
this.$emit("close"); |
||||
}, |
||||
//重置 |
||||
reset() { |
||||
this.acceptForm = {} |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
@font-face { |
||||
font-family: 'iconfont'; /* Project id 2729410 */ |
||||
src: url('https://at.alicdn.com/t/font_2729410_3nhq3ibbcqg.woff2?t=1628330097695') format('woff2'), |
||||
url('https://at.alicdn.com/t/font_2729410_3nhq3ibbcqg.woff?t=1628330097695') format('woff'), |
||||
url('https://at.alicdn.com/t/font_2729410_3nhq3ibbcqg.ttf?t=1628330097695') format('truetype'); |
||||
} |
||||
.saasIcon { |
||||
font-family: "iconfont" !important; |
||||
font-style: normal; |
||||
-webkit-font-smoothing: antialiased; |
||||
-webkit-text-stroke-width: 0.2px; |
||||
-moz-osx-font-smoothing: grayscale; |
||||
} |
||||
.filter-popup { |
||||
color: #000000; |
||||
.top-title { |
||||
font-weight: bold; |
||||
height: 90rpx; |
||||
margin-left: 70rpx; |
||||
font-size: 30rpx; |
||||
margin-top: 20rpx; |
||||
.popup-title { |
||||
height: 100%; |
||||
} |
||||
.saasIcon { |
||||
// width: 150rpx; |
||||
height: 100%; |
||||
padding-right: 70rpx; |
||||
} |
||||
} |
||||
.select-scroll { |
||||
} |
||||
.select-main { |
||||
padding: 0 32rpx; |
||||
.select-item { |
||||
.title { |
||||
font-weight: bold; |
||||
font-size: 28rpx; |
||||
color: #000000; |
||||
padding-top: 30rpx; |
||||
} |
||||
.tag-list { |
||||
display: flex; |
||||
flex-wrap: wrap; |
||||
margin-left: -12rpx; |
||||
.tag-item { |
||||
margin-top: 20rpx; |
||||
padding: 10rpx 40rpx; |
||||
font-size: 26rpx; |
||||
background: #f5f5f5; |
||||
color: #484848; |
||||
border-radius: 36rpx; |
||||
margin-left: 12rpx; |
||||
&.select-on { |
||||
background: var(--color); |
||||
color: #fff; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
.select-childer { |
||||
.childer-title { |
||||
color: #484848; |
||||
font-size: 28rpx; |
||||
border-bottom: 1px solid #f5f5f5; |
||||
height: 80rpx; |
||||
} |
||||
} |
||||
} |
||||
.filter-button { |
||||
width: 100%; |
||||
height: 152rpx; |
||||
background: #ffffff; |
||||
box-shadow: 0px -3px 12px rgba(0, 0, 0, 0.06); |
||||
position: fixed; |
||||
bottom: 0; |
||||
.btn { |
||||
border-radius: 100rpx; |
||||
margin-top: 26rpx; |
||||
width: 680rpx; |
||||
height: 80rpx; |
||||
color: #ffffff; |
||||
font-size: 28rpx; |
||||
overflow: hidden; |
||||
.btn-1 { |
||||
flex: 1; |
||||
background: linear-gradient(271deg, #2698fb 0%, #84c6ff 100%); |
||||
} |
||||
.btn-2 { |
||||
flex: 1; |
||||
background: linear-gradient(90deg, #0066ff 0%, #1371ff 100%); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 自定义css |
||||
.flex-row { |
||||
display: flex; |
||||
} |
||||
|
||||
.flex-row-c { |
||||
display: flex; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.flex-row--c { |
||||
display: flex; |
||||
align-items: center; |
||||
} |
||||
|
||||
.flex-row-c-c { |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: center; |
||||
} |
||||
|
||||
.flex-row-sb-c { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
} |
||||
|
||||
.flex-row-sb-t { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: flex-start; |
||||
} |
||||
|
||||
.flex-row-sb-b { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
align-items: flex-end; |
||||
} |
||||
|
||||
.flex-row-c-sb { |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: space-between; |
||||
} |
||||
|
||||
.flex-row-sb { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
} |
||||
|
||||
.flex-row-l { |
||||
display: flex; |
||||
justify-content: flex-start; |
||||
} |
||||
|
||||
.flex-row-l-c { |
||||
display: flex; |
||||
justify-content: flex-start; |
||||
align-items: center; |
||||
} |
||||
|
||||
.flex-row-c-t { |
||||
display: flex; |
||||
justify-content: center; |
||||
align-items: flex-start; |
||||
} |
||||
|
||||
.flex-row-r-c { |
||||
display: flex; |
||||
justify-content: flex-end; |
||||
align-items: center; |
||||
} |
||||
|
||||
.flex-row-r { |
||||
display: flex; |
||||
justify-content: flex-end; |
||||
} |
||||
|
||||
.flex-row--b { |
||||
display: flex; |
||||
align-items: flex-end; |
||||
} |
||||
|
||||
.flex-col { |
||||
display: flex; |
||||
flex-direction: column; |
||||
} |
||||
|
||||
.flex-col-c { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
} |
||||
|
||||
.flex-col--c { |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
} |
||||
|
||||
.flex-col-c-c { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: center; |
||||
} |
||||
|
||||
.flex-col-sb-c { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: space-between; |
||||
align-items: center; |
||||
} |
||||
|
||||
.flex-col-c-sb { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: space-between; |
||||
} |
||||
|
||||
.flex-col-sb { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: space-between; |
||||
} |
||||
|
||||
.flex-col-t-c { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: flex-start; |
||||
align-items: center; |
||||
} |
||||
|
||||
.flex-col-c-l { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: flex-start; |
||||
} |
||||
|
||||
.flex-col-t { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: flex-start; |
||||
} |
||||
|
||||
.flex-col-b { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: flex-end; |
||||
} |
||||
|
||||
.flex-col-b-c { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: flex-end; |
||||
align-items: center; |
||||
} |
||||
|
||||
.flex-col-c-l { |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: center; |
||||
align-items: flex-end; |
||||
} |
||||
|
||||
.flex-col--l { |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: flex-start; |
||||
} |
||||
|
||||
.flex-col--r { |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: flex-end; |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,12 @@ |
||||
{ |
||||
"id": "filter-popup", |
||||
"name": "筛选 菜单 筛选菜单 上拉筛选 ", |
||||
"version": "1.0.4", |
||||
"description": "筛选菜单,支持单选和多选 , 选择后的数据通过.sync双向绑定,全端支持 导入即用", |
||||
"keywords": [ |
||||
"筛选", |
||||
"菜单", |
||||
"筛选菜单", |
||||
"上拉筛选" |
||||
] |
||||
} |
@ -0,0 +1,64 @@ |
||||
<template> |
||||
<view class="page"> |
||||
<view class="input"> |
||||
<uni-easyinput v-model.trim="account" placeholder="请输入账号"></uni-easyinput> |
||||
</view> |
||||
<button type="primary" @click="submit">确认</button> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import { queryUserInfoDetails, updatePersonCenter } from '@/apis/modules/user.js' |
||||
export default { |
||||
data() { |
||||
return { |
||||
account: '', |
||||
hrUserInfo: {}, |
||||
personalFileList: [], |
||||
userAccountList: [], |
||||
} |
||||
}, |
||||
onShow() { |
||||
this.getInfo() |
||||
}, |
||||
methods: { |
||||
// 获取个人信息 |
||||
getInfo() { |
||||
queryUserInfoDetails().then(({ result }) => { |
||||
this.hrUserInfo = result.hrUserInfo |
||||
this.userAccountList = result.userAccountList |
||||
}).catch(e => {}) |
||||
}, |
||||
submit() { |
||||
const { account, hrUserInfo, userAccountList } = this |
||||
if(!account) return this.$util.errMsg('请输入账号') |
||||
userAccountList[0].userId = hrUserInfo.userId |
||||
userAccountList[0].account = account |
||||
updatePersonCenter({ |
||||
hrUserInfo, |
||||
userAccountList |
||||
}).then(res => { |
||||
this.$util.sucMsg('修改成功!') |
||||
setTimeout(() => { |
||||
uni.reLaunch({ |
||||
url: '../person/person' |
||||
}) |
||||
}, 1000) |
||||
}).catch(e => {}) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.page { |
||||
padding: 20px; |
||||
background-color: #fff; |
||||
} |
||||
/deep/.input { |
||||
margin-bottom: 15px; |
||||
.is-input-border { |
||||
border-color: #dedede !important; |
||||
} |
||||
} |
||||
</style> |
@ -0,0 +1,130 @@ |
||||
<template> |
||||
<view class="page"> |
||||
<view class="input"> |
||||
<uni-easyinput v-model="form.email" placeholder="请填写你的邮箱" :clearable="false" /> |
||||
</view> |
||||
<view class="input code-wrap"> |
||||
<uni-easyinput class="code" v-model="form.code" placeholder="验证码" :clearable="false" /> |
||||
<view class="send-code" @click="sendCode" :disabled="codeDisabled">{{ btnText }}</view> |
||||
</view> |
||||
<button type="primary" @click="submit">确认</button> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import { queryUserInfoDetails, bindPhoneOrEmail, sendPhoneOrEmailCode } from '@/apis/modules/user.js' |
||||
export default { |
||||
data() { |
||||
return { |
||||
userId: '', |
||||
form: { |
||||
email: '', |
||||
code: '' |
||||
}, |
||||
codeDisabled: false, |
||||
phoneTimer: null, |
||||
phoneOpener: '', |
||||
btnText: '发送验证码', |
||||
} |
||||
}, |
||||
onShow() { |
||||
this.getInfo() |
||||
}, |
||||
methods: { |
||||
// 获取个人信息 |
||||
getInfo() { |
||||
queryUserInfoDetails().then(({ result }) => { |
||||
this.userId = result.hrUserInfo.userId |
||||
}).catch(e => {}) |
||||
}, |
||||
// 发送验证码 |
||||
sendCode() { |
||||
const { email } = this.form |
||||
if (!email) return this.$util.errMsg('请输入邮箱') |
||||
if (!/^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/.test(email) && !/^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/.test(email)) return this.$util.errMsg('请输入正确的请输入邮箱') |
||||
sendPhoneOrEmailCode({ |
||||
userId: this.userId, |
||||
email, |
||||
types: 1 |
||||
}).then(({ message }) => { |
||||
if (message.opener) { |
||||
this.phoneCountdown() |
||||
this.phoneOpener = message.opener |
||||
} else { |
||||
this.$util.errMsg(message) |
||||
} |
||||
}).catch(res => {}) |
||||
}, |
||||
// 验证码倒计时 |
||||
phoneCountdown() { |
||||
let count = 60 |
||||
if (!this.phoneTimer) { |
||||
this.codeDisabled = true |
||||
this.phoneTimer = setInterval(() => { |
||||
if (count > 0) { |
||||
count-- |
||||
this.btnText = `${count}秒后重试` |
||||
} else { |
||||
this.codeDisabled = false |
||||
clearInterval(this.phoneTimer) |
||||
this.phoneTimer = null |
||||
this.btnText = `发送验证码` |
||||
} |
||||
}, 1000) |
||||
} |
||||
}, |
||||
// 提交 |
||||
submit() { |
||||
const { email, code } = this.form |
||||
if (!email) return this.$util.errMsg('请输入邮箱') |
||||
if (!code) return this.$util.errMsg('请输入验证码') |
||||
bindPhoneOrEmail({ |
||||
userId: this.userId, |
||||
email, |
||||
types: 1, |
||||
code, |
||||
opener: this.phoneOpener |
||||
}).then(res => { |
||||
this.$util.sucMsg('修改成功!') |
||||
setTimeout(() => { |
||||
uni.reLaunch({ |
||||
url: '../person/person' |
||||
}) |
||||
}, 1500) |
||||
}).catch(res => {}) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.page { |
||||
padding: 20px; |
||||
background-color: #fff; |
||||
} |
||||
/deep/.input { |
||||
margin-bottom: 15px; |
||||
.is-input-border { |
||||
border-color: #dedede !important; |
||||
} |
||||
} |
||||
.input { |
||||
margin-bottom: 20px; |
||||
} |
||||
.code-wrap { |
||||
display: flex; |
||||
} |
||||
.code { |
||||
flex: 1; |
||||
} |
||||
.send-code { |
||||
width: 100px; |
||||
margin-left: 20px; |
||||
text-align: center; |
||||
color: #4386ff; |
||||
font-size: 12px; |
||||
line-height: 36px; |
||||
border: 1px solid #4386ff; |
||||
border-radius: 5px; |
||||
} |
||||
</style> |
@ -0,0 +1,130 @@ |
||||
<template> |
||||
<view class="page"> |
||||
<view class="input"> |
||||
<uni-easyinput v-model="form.phone" placeholder="请填写你的手机号" :clearable="false" /> |
||||
</view> |
||||
<view class="input code-wrap"> |
||||
<uni-easyinput class="code" v-model="form.code" placeholder="验证码" :clearable="false" /> |
||||
<view class="send-code" @click="sendCode" :disabled="codeDisabled">{{ btnText }}</view> |
||||
</view> |
||||
<button type="primary" @click="submit">确认</button> |
||||
</view> |
||||
</template> |
||||
|
||||
<script> |
||||
import { queryUserInfoDetails, bindPhoneOrEmail, sendPhoneOrEmailCode } from '@/apis/modules/user.js' |
||||
export default { |
||||
data() { |
||||
return { |
||||
userId: '', |
||||
form: { |
||||
phone: '', |
||||
code: '' |
||||
}, |
||||
codeDisabled: false, |
||||
phoneTimer: null, |
||||
phoneOpener: '', |
||||
btnText: '发送验证码', |
||||
} |
||||
}, |
||||
onShow() { |
||||
this.getInfo() |
||||
}, |
||||
methods: { |
||||
// 获取个人信息 |
||||
getInfo() { |
||||
queryUserInfoDetails().then(({ result }) => { |
||||
this.userId = result.hrUserInfo.userId |
||||
}).catch(e => {}) |
||||
}, |
||||
// 发送验证码 |
||||
sendCode() { |
||||
const { phone } = this.form |
||||
if (!phone) return this.$util.errMsg('请输入手机号') |
||||
if (!/^1[3456789]\d{9}$/.test(phone) && !/^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/.test(phone)) return this.$util.errMsg('请输入正确的手机号') |
||||
sendPhoneOrEmailCode({ |
||||
userId: this.userId, |
||||
phone, |
||||
types: 2 |
||||
}).then(({ message }) => { |
||||
if (message.opener) { |
||||
this.phoneCountdown() |
||||
this.phoneOpener = message.opener |
||||
} else { |
||||
this.$util.errMsg(message) |
||||
} |
||||
}).catch(res => {}) |
||||
}, |
||||
// 验证码倒计时 |
||||
phoneCountdown() { |
||||
let count = 60 |
||||
if (!this.phoneTimer) { |
||||
this.codeDisabled = true |
||||
this.phoneTimer = setInterval(() => { |
||||
if (count > 0) { |
||||
count-- |
||||
this.btnText = `${count}秒后重试` |
||||
} else { |
||||
this.codeDisabled = false |
||||
clearInterval(this.phoneTimer) |
||||
this.phoneTimer = null |
||||
this.btnText = `发送验证码` |
||||
} |
||||
}, 1000) |
||||
} |
||||
}, |
||||
// 提交 |
||||
submit() { |
||||
const { phone, code } = this.form |
||||
if (!phone) return this.$util.errMsg('请输入手机号') |
||||
if (!code) return this.$util.errMsg('请输入验证码') |
||||
bindPhoneOrEmail({ |
||||
userId: this.userId, |
||||
phone, |
||||
types: 2, |
||||
code, |
||||
opener: this.phoneOpener |
||||
}).then(res => { |
||||
this.$util.sucMsg('修改成功!') |
||||
setTimeout(() => { |
||||
uni.reLaunch({ |
||||
url: '../person/person' |
||||
}) |
||||
}, 1500) |
||||
}).catch(res => {}) |
||||
} |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<style scoped lang="scss"> |
||||
.page { |
||||
padding: 20px; |
||||
background-color: #fff; |
||||
} |
||||
/deep/.input { |
||||
margin-bottom: 15px; |
||||
.is-input-border { |
||||
border-color: #dedede !important; |
||||
} |
||||
} |
||||
.input { |
||||
margin-bottom: 20px; |
||||
} |
||||
.code-wrap { |
||||
display: flex; |
||||
} |
||||
.code { |
||||
flex: 1; |
||||
} |
||||
.send-code { |
||||
width: 100px; |
||||
margin-left: 20px; |
||||
text-align: center; |
||||
color: #4386ff; |
||||
font-size: 12px; |
||||
line-height: 36px; |
||||
border: 1px solid #4386ff; |
||||
border-radius: 5px; |
||||
} |
||||
</style> |
Loading…
Reference in new issue