Commit 9d200c0c authored by wlxuqu's avatar wlxuqu

1. 优化懒加载的方法

2. 优化步进器的代码逻辑
3. 修改评分组件对支付宝小程序的支持
4. 添加搜索组件的maxlength参数
5. 优化$u.getRect()方法,使其支持支付宝小程序
parent 95fd1c8e
<template>
<view class="content">
<view class="cropper-wrapper" :style="{height: cropperOpt.height + 'px'}">
<canvas class="cropper" :disable-scroll="true" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd"
:style="{ width: cropperOpt.width, height: cropperOpt.height, backgroundColor: 'rgba(0, 0, 0, 0.8)' }" canvas-id="cropper"></canvas>
<canvas class="cropper" :disable-scroll="true" :style="{
position: 'fixed',
top: `-${cropperOpt.width * cropperOpt.pixelRatio}px`,
left: `-${cropperOpt.height * cropperOpt.pixelRatio}px`,
width: `${cropperOpt.width * cropperOpt.pixelRatio}px`,
height: `${cropperOpt.height * cropperOpt.pixelRatio}`
}"
canvas-id="targetId">
</canvas>
<view class="cropper-wrapper" :style="{ height: cropperOpt.height + 'px' }">
<canvas
class="cropper"
:disable-scroll="true"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
:style="{ width: cropperOpt.width, height: cropperOpt.height, backgroundColor: 'rgba(0, 0, 0, 0.8)' }"
canvas-id="cropper"
></canvas>
<canvas
class="cropper"
:disable-scroll="true"
:style="{
position: 'fixed',
top: `-${cropperOpt.width * cropperOpt.pixelRatio}px`,
left: `-${cropperOpt.height * cropperOpt.pixelRatio}px`,
width: `${cropperOpt.width * cropperOpt.pixelRatio}px`,
height: `${cropperOpt.height * cropperOpt.pixelRatio}`
}"
canvas-id="targetId"
></canvas>
</view>
<view class="cropper-buttons safe-area-padding" :style="{height: bottomNavHeight + 'px'}">
<view class="cropper-buttons safe-area-padding" :style="{ height: bottomNavHeight + 'px' }">
<view class="upload" @tap="uploadTap">重新选择</view>
<view class="getCropperImage" @tap="getCropperImage(false)">确定</view>
</view>
......@@ -21,247 +31,248 @@
</template>
<script>
import WeCropper from './weCropper.js';
export default {
props: {
// 裁剪矩形框的样式,其中可包含的属性为lineWidth-边框宽度(单位rpx),color: 边框颜色,
// mask-遮罩颜色,一般设置为一个rgba的透明度,如"rgba(0, 0, 0, 0.35)"
boundStyle: {
type: Object,
default() {
return {
lineWidth: 4,
borderColor: 'rgb(245, 245, 245)',
mask: 'rgba(0, 0, 0, 0.35)'
}
}
},
// // 裁剪框宽度,单位rpx
// rectWidth: {
// type: [String, Number],
// default: 400
// },
// // 裁剪框高度,单位rpx
// rectHeight: {
// type: [String, Number],
// default: 400
// },
// // 输出图片宽度,单位rpx
// destWidth: {
// type: [String, Number],
// default: 400
// },
// // 输出图片高度,单位rpx
// destHeight: {
// type: [String, Number],
// default: 400
// },
// // 输出的图片类型,如果发现裁剪的图片很大,可能是因为设置为了"png",改成"jpg"即可
// fileType: {
// type: String,
// default: 'jpg',
// },
// // 生成的图片质量
// // H5上无效,目前不考虑使用此参数
// quality: {
// type: [Number, String],
// default: 1
// }
},
data() {
return {
// 底部导航的高度
bottomNavHeight: 50,
originWidth: 200,
import WeCropper from './weCropper.js';
export default {
props: {
// 裁剪矩形框的样式,其中可包含的属性为lineWidth-边框宽度(单位rpx),color: 边框颜色,
// mask-遮罩颜色,一般设置为一个rgba的透明度,如"rgba(0, 0, 0, 0.35)"
boundStyle: {
type: Object,
default() {
return {
lineWidth: 4,
borderColor: 'rgb(245, 245, 245)',
mask: 'rgba(0, 0, 0, 0.35)'
};
}
}
// // 裁剪框宽度,单位rpx
// rectWidth: {
// type: [String, Number],
// default: 400
// },
// // 裁剪框高度,单位rpx
// rectHeight: {
// type: [String, Number],
// default: 400
// },
// // 输出图片宽度,单位rpx
// destWidth: {
// type: [String, Number],
// default: 400
// },
// // 输出图片高度,单位rpx
// destHeight: {
// type: [String, Number],
// default: 400
// },
// // 输出的图片类型,如果发现裁剪的图片很大,可能是因为设置为了"png",改成"jpg"即可
// fileType: {
// type: String,
// default: 'jpg',
// },
// // 生成的图片质量
// // H5上无效,目前不考虑使用此参数
// quality: {
// type: [Number, String],
// default: 1
// }
},
data() {
return {
// 底部导航的高度
bottomNavHeight: 50,
originWidth: 200,
width: 0,
height: 0,
cropperOpt: {
id: 'cropper',
targetId: 'targetCropper',
pixelRatio: 1,
width: 0,
height: 0,
cropperOpt: {
id: 'cropper',
targetId: 'targetCropper',
pixelRatio: 1,
width: 0,
height: 0,
scale: 2.5,
zoom: 8,
cut: {
x: (this.width - this.originWidth) / 2,
y: (this.height - this.originWidth) / 2,
width: this.originWidth,
height: this.originWidth,
},
boundStyle: {
lineWidth: uni.upx2px(this.boundStyle.lineWidth),
mask: this.boundStyle.mask,
color: this.boundStyle.borderColor
},
scale: 2.5,
zoom: 8,
cut: {
x: (this.width - this.originWidth) / 2,
y: (this.height - this.originWidth) / 2,
width: this.originWidth,
height: this.originWidth
},
// 裁剪框和输出图片的尺寸,高度默认等于宽度
// 输出图片宽度,单位px
destWidth: 200,
// 裁剪框宽度,单位px
rectWidth: 200,
// 输出的图片类型,如果'png'类型发现裁剪的图片太大,改成"jpg"即可
fileType: 'jpg',
}
},
onLoad(option) {
let rectInfo = uni.getSystemInfoSync();
this.width = rectInfo.windowWidth;
this.height = rectInfo.windowHeight - this.bottomNavHeight;
this.cropperOpt.width = this.width;
this.cropperOpt.height = this.height;
this.cropperOpt.pixelRatio = rectInfo.pixelRatio;
if(option.destWidth) this.destWidth = option.destWidth;
if(option.rectWidth){
let rectWidth = Number(option.rectWidth);
this.cropperOpt.cut = {
x: (this.width - rectWidth) / 2,
y: (this.height - rectWidth) / 2,
width: rectWidth,
height: rectWidth,
boundStyle: {
lineWidth: uni.upx2px(this.boundStyle.lineWidth),
mask: this.boundStyle.mask,
color: this.boundStyle.borderColor
}
} this.rectWidth = option.rectWidth;
if(option.fileType) this.fileType = option.fileType;
// 初始化
this.cropper = new WeCropper(this.cropperOpt)
.on('ready', (ctx) => {
},
// 裁剪框和输出图片的尺寸,高度默认等于宽度
// 输出图片宽度,单位px
destWidth: 200,
// 裁剪框宽度,单位px
rectWidth: 200,
// 输出的图片类型,如果'png'类型发现裁剪的图片太大,改成"jpg"即可
fileType: 'jpg'
};
},
onLoad(option) {
let rectInfo = uni.getSystemInfoSync();
this.width = rectInfo.windowWidth;
this.height = rectInfo.windowHeight - this.bottomNavHeight;
this.cropperOpt.width = this.width;
this.cropperOpt.height = this.height;
this.cropperOpt.pixelRatio = rectInfo.pixelRatio;
if (option.destWidth) this.destWidth = option.destWidth;
if (option.rectWidth) {
let rectWidth = Number(option.rectWidth);
this.cropperOpt.cut = {
x: (this.width - rectWidth) / 2,
y: (this.height - rectWidth) / 2,
width: rectWidth,
height: rectWidth
};
}
this.rectWidth = option.rectWidth;
if (option.fileType) this.fileType = option.fileType;
// 初始化
this.cropper = new WeCropper(this.cropperOpt)
.on('ready', ctx => {
// console.log(`wecropper is ready for work!`)
})
.on('beforeImageLoad', (ctx) => {
.on('beforeImageLoad', ctx => {
// console.log(`before picture loaded, i can do something`)
})
.on('imageLoad', (ctx) => {
.on('imageLoad', ctx => {
// console.log(`picture loaded`)
})
.on('beforeDraw', (ctx, instance) => {
// console.log(`before canvas draw,i can do something`)
})
// 设置导航栏样式,以免用户在page.json中没有设置为黑色背景
uni.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: '#000000',
})
});
// 设置导航栏样式,以免用户在page.json中没有设置为黑色背景
uni.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: '#000000'
});
uni.chooseImage({
count: 1, // 默认9
sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: res => {
let src = res.tempFilePaths[0];
// 获取裁剪图片资源后,给data添加src属性及其值
this.cropper.pushOrign(src);
}
});
},
methods: {
touchStart(e) {
this.cropper.touchStart(e);
},
touchMove(e) {
this.cropper.touchMove(e);
},
touchEnd(e) {
this.cropper.touchEnd(e);
},
getCropperImage(isPre = false) {
let cropper_opt = {
destHeight: Number(this.destWidth), // uni.canvasToTempFilePath要求这些参数为数值
destWidth: Number(this.destWidth),
fileType: this.fileType
};
this.cropper.getCropperImage(cropper_opt, (path, err) => {
if (err) {
uni.showModal({
title: '温馨提示',
content: err.message
});
} else {
if (isPre) {
uni.previewImage({
current: '', // 当前显示图片的 http 链接
urls: [path] // 需要预览的图片 http 链接列表
});
} else {
uni.$emit('uAvatarCropper', path);
this.$u.route({
type: 'back'
});
}
}
});
},
uploadTap() {
const self = this;
uni.chooseImage({
count: 1, // 默认9
sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: (res) => {
let src = res.tempFilePaths[0];
success(res) {
const src = res.tempFilePaths[0];
// 获取裁剪图片资源后,给data添加src属性及其值
this.cropper.pushOrign(src);
self.cropper.pushOrign(src);
}
});
},
methods: {
touchStart(e) {
this.cropper.touchStart(e)
},
touchMove(e) {
this.cropper.touchMove(e)
},
touchEnd(e) {
this.cropper.touchEnd(e)
},
getCropperImage(isPre = false) {
let cropper_opt = {
destHeight: Number(this.destWidth), // uni.canvasToTempFilePath要求这些参数为数值
destWidth: Number(this.destWidth),
fileType: this.fileType,
}
this.cropper.getCropperImage(cropper_opt, (path, err) => {
if (err) {
uni.showModal({
title: '温馨提示',
content: err.message
})
} else {
if(isPre) {
uni.previewImage({
current: '', // 当前显示图片的 http 链接
urls: [path] // 需要预览的图片 http 链接列表
})
} else {
uni.$emit('uAvatarCropper', path);
this.$u.route({
type: 'back'
})
}
}
})
},
uploadTap() {
const self = this
uni.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success(res) {
const src = res.tempFilePaths[0]
// 获取裁剪图片资源后,给data添加src属性及其值
self.cropper.pushOrign(src)
}
})
},
}
}
};
</script>
<style scoped>
.content {
background: rgba(255, 255, 255, 1);
}
.content {
background: rgba(255, 255, 255, 1);
}
.cropper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 99999999999999;
}
.cropper {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 99999999999999;
}
.cropper-buttons {
background-color: #000000;
color: #eee;
}
.cropper-buttons {
background-color: #000000;
color: #eee;
}
.cropper-wrapper {
position: relative;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
background-color: #000;
}
.cropper-wrapper {
position: relative;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
background-color: #000;
}
.cropper-buttons {
width: 100vw;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
position: fixed;
bottom: 0;
left: 0;
font-size: 28rpx;
}
.cropper-buttons {
width: 100vw;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
position: fixed;
bottom: 0;
left: 0;
font-size: 28rpx;
}
.cropper-buttons .upload,
.cropper-buttons .getCropperImage {
width: 50%;
text-align: center;
}
.cropper-buttons .upload {
text-align: left;
padding-left: 50rpx;
}
.cropper-buttons .getCropperImage {
text-align: right;
padding-right: 50rpx;
}
</style>
\ No newline at end of file
.cropper-buttons .upload,
.cropper-buttons .getCropperImage {
width: 50%;
text-align: center;
}
.cropper-buttons .upload {
text-align: left;
padding-left: 50rpx;
}
.cropper-buttons .getCropperImage {
text-align: right;
padding-right: 50rpx;
}
</style>
<template>
<button id="u-wave-btn" class="u-btn u-line-1 u-fix-ios-appearance" :class="[
'u-size-' + size,
plain ? 'u-' + type + '-plain' : '',
loading ? 'u-loading' : '',
shape == 'circle' ? 'u-round-circle' : '',
hairLine ? showHairLineBorder : 'u-bold-border',
]"
:disabled="disabled" :form-type="formType" :open-type="openType" :app-parameter="appParameter"
:hover-stop-propagation="hoverStopPropagation" :send-message-title="sendMessageTitle" send-message-path="sendMessagePath"
:lang="lang" :session-from="sessionFrom" :send-message-img="sendMessageImg" :show-message-card="showMessageCard"
@getphonenumber="getphonenumber" @getuserinfo="getuserinfo" @error="error" @opensetting="opensetting" @launchapp="launchapp"
:style="[buttonStyle]" @tap="click($event)" :hover-class="getHoverClass" :loading="loading">
<button
id="u-wave-btn"
class="u-btn u-line-1 u-fix-ios-appearance"
:class="[
'u-size-' + size,
plain ? 'u-' + type + '-plain' : '',
loading ? 'u-loading' : '',
shape == 'circle' ? 'u-round-circle' : '',
hairLine ? showHairLineBorder : 'u-bold-border'
]"
:disabled="disabled"
:form-type="formType"
:open-type="openType"
:app-parameter="appParameter"
:hover-stop-propagation="hoverStopPropagation"
:send-message-title="sendMessageTitle"
send-message-path="sendMessagePath"
:lang="lang"
:session-from="sessionFrom"
:send-message-img="sendMessageImg"
:show-message-card="showMessageCard"
@getphonenumber="getphonenumber"
@getuserinfo="getuserinfo"
@error="error"
@opensetting="opensetting"
@launchapp="launchapp"
:style="[buttonStyle]"
@tap="click($event)"
:hover-class="getHoverClass"
:loading="loading"
>
<slot></slot>
<view v-if="ripple" class="u-wave-ripple" :class="[waveActive ? 'u-wave-active' : '']" :style="{
'top': rippleTop + 'px',
'left': rippleLeft + 'px',
'width': fields.targetWidth + 'px',
'height': fields.targetWidth + 'px',
'background-color': (rippleBgColor || 'rgba(0, 0, 0, 0.15)')
}">
</view>
<view
v-if="ripple"
class="u-wave-ripple"
:class="[waveActive ? 'u-wave-active' : '']"
:style="{
top: rippleTop + 'px',
left: rippleLeft + 'px',
width: fields.targetWidth + 'px',
height: fields.targetWidth + 'px',
'background-color': rippleBgColor || 'rgba(0, 0, 0, 0.15)'
}"
></view>
</button>
</template>
<script>
/**
* button 按钮
* @description Button 按钮
* @tutorial https://www.uviewui.com/components/button.html
* @property {String} size 按钮的大小
* @property {Boolean} ripple 是否开启点击水波纹效果
* @property {String} ripple-bg-color 水波纹的背景色,ripple为true时有效
* @property {String} type 按钮的样式类型
* @property {Boolean} plain 按钮是否镂空,背景色透明
* @property {Boolean} disabled 是否禁用
* @property {Boolean} hair-line 是否显示按钮的细边框(默认true)
* @property {Boolean} shape 按钮外观形状,见文档说明
* @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈)
* @property {String} form-type 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
* @property {String} open-type 开放能力
* @property {String} hover-class 指定按钮按下去的样式类。当 hover-class="none" 时,没有点击态效果(App-nvue 平台暂不支持)
* @property {Number} hover-start-time 按住后多久出现点击态,单位毫秒
* @property {Number} hover-stay-time 手指松开后点击态保留时间,单位毫秒
* @property {Object} custom-style 对按钮的自定义样式,对象形式,见文档说明
* @event {Function} click 按钮点击
* @event {Function} getphonenumber open-type="getPhoneNumber"时有效
* @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo
* @event {Function} error 当使用开放能力时,发生错误的回调
* @event {Function} opensetting 在打开授权设置页并关闭后回调
* @event {Function} launchapp 打开 APP 成功的回调
* @example <u-button>月落</u-button>
*/
export default {
name:"u-button",
props: {
// 是否细边框
hairLine: {
type: Boolean,
default: true
},
// 按钮的预置样式,default,primary,error,warning,success
type: {
type: String,
default: 'default'
},
// 按钮尺寸,default,medium,mini
size: {
type: String,
default: 'default'
},
// 按钮形状,circle(两边为半圆),square(带圆角)
shape: {
type: String,
default: 'square'
},
// 按钮是否镂空
plain: {
type: Boolean,
default: false
},
// 是否禁止状态
disabled: {
type: Boolean,
default: false
},
// 是否加载中
loading: {
type: Boolean,
default: false
},
// 开放能力,具体请看uniapp稳定关于button组件部分说明
// https://uniapp.dcloud.io/component/button
openType: {
type: String,
default: ''
},
// 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
// 取值为submit(提交表单),reset(重置表单)
formType: {
type: String,
default: ''
},
// 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
// 只微信小程序、QQ小程序有效
appParameter: {
type: String,
default: ''
},
// 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
hoverStopPropagation: {
type: Boolean,
default: false
},
// 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
lang: {
type: String,
default: 'en'
},
// 会话来源,open-type="contact"时有效。只微信小程序有效
sessionFrom: {
type: String,
default: ''
},
// 会话内消息卡片标题,open-type="contact"时有效
// 默认当前标题,只微信小程序有效
sendMessageTitle: {
type: String,
default: ''
},
// 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
// 默认当前分享路径,只微信小程序有效
sendMessagePath: {
type: String,
default: ''
},
// 会话内消息卡片图片,open-type="contact"时有效
// 默认当前页面截图,只微信小程序有效
sendMessageImg: {
type: String,
default: ''
},
// 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
// 用户点击后可以快速发送小程序消息,open-type="contact"时有效
showMessageCard: {
type: Boolean,
default: false
},
// 手指按(触摸)按钮时按钮时的背景颜色
hoverBgColor: {
type: String,
default: ''
},
// 水波纹的背景颜色
rippleBgColor: {
type: String,
default: ''
},
// 是否开启水波纹效果
ripple: {
type: Boolean,
default: false
},
// 按下的类名
hoverClass: {
type: String,
default: ''
},
// 自定义样式,对象形式
customStyle: {
type: Object,
default () {
return {}
}
/**
* button 按钮
* @description Button 按钮
* @tutorial https://www.uviewui.com/components/button.html
* @property {String} size 按钮的大小
* @property {Boolean} ripple 是否开启点击水波纹效果
* @property {String} ripple-bg-color 水波纹的背景色,ripple为true时有效
* @property {String} type 按钮的样式类型
* @property {Boolean} plain 按钮是否镂空,背景色透明
* @property {Boolean} disabled 是否禁用
* @property {Boolean} hair-line 是否显示按钮的细边框(默认true)
* @property {Boolean} shape 按钮外观形状,见文档说明
* @property {Boolean} loading 按钮名称前是否带 loading 图标(App-nvue 平台,在 ios 上为雪花,Android上为圆圈)
* @property {String} form-type 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
* @property {String} open-type 开放能力
* @property {String} hover-class 指定按钮按下去的样式类。当 hover-class="none" 时,没有点击态效果(App-nvue 平台暂不支持)
* @property {Number} hover-start-time 按住后多久出现点击态,单位毫秒
* @property {Number} hover-stay-time 手指松开后点击态保留时间,单位毫秒
* @property {Object} custom-style 对按钮的自定义样式,对象形式,见文档说明
* @event {Function} click 按钮点击
* @event {Function} getphonenumber open-type="getPhoneNumber"时有效
* @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,从返回参数的detail中获取到的值同uni.getUserInfo
* @event {Function} error 当使用开放能力时,发生错误的回调
* @event {Function} opensetting 在打开授权设置页并关闭后回调
* @event {Function} launchapp 打开 APP 成功的回调
* @example <u-button>月落</u-button>
*/
export default {
name: 'u-button',
props: {
// 是否细边框
hairLine: {
type: Boolean,
default: true
},
// 按钮的预置样式,default,primary,error,warning,success
type: {
type: String,
default: 'default'
},
// 按钮尺寸,default,medium,mini
size: {
type: String,
default: 'default'
},
// 按钮形状,circle(两边为半圆),square(带圆角)
shape: {
type: String,
default: 'square'
},
// 按钮是否镂空
plain: {
type: Boolean,
default: false
},
// 是否禁止状态
disabled: {
type: Boolean,
default: false
},
// 是否加载中
loading: {
type: Boolean,
default: false
},
// 开放能力,具体请看uniapp稳定关于button组件部分说明
// https://uniapp.dcloud.io/component/button
openType: {
type: String,
default: ''
},
// 用于 <form> 组件,点击分别会触发 <form> 组件的 submit/reset 事件
// 取值为submit(提交表单),reset(重置表单)
formType: {
type: String,
default: ''
},
// 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效
// 只微信小程序、QQ小程序有效
appParameter: {
type: String,
default: ''
},
// 指定是否阻止本节点的祖先节点出现点击态,微信小程序有效
hoverStopPropagation: {
type: Boolean,
default: false
},
// 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。只微信小程序有效
lang: {
type: String,
default: 'en'
},
// 会话来源,open-type="contact"时有效。只微信小程序有效
sessionFrom: {
type: String,
default: ''
},
// 会话内消息卡片标题,open-type="contact"时有效
// 默认当前标题,只微信小程序有效
sendMessageTitle: {
type: String,
default: ''
},
// 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效
// 默认当前分享路径,只微信小程序有效
sendMessagePath: {
type: String,
default: ''
},
// 会话内消息卡片图片,open-type="contact"时有效
// 默认当前页面截图,只微信小程序有效
sendMessageImg: {
type: String,
default: ''
},
// 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,
// 用户点击后可以快速发送小程序消息,open-type="contact"时有效
showMessageCard: {
type: Boolean,
default: false
},
// 手指按(触摸)按钮时按钮时的背景颜色
hoverBgColor: {
type: String,
default: ''
},
// 水波纹的背景颜色
rippleBgColor: {
type: String,
default: ''
},
// 是否开启水波纹效果
ripple: {
type: Boolean,
default: false
},
// 按下的类名
hoverClass: {
type: String,
default: ''
},
// 自定义样式,对象形式
customStyle: {
type: Object,
default() {
return {};
}
}
},
computed: {
// 当没有传bgColor变量时,按钮按下去的颜色类名
getHoverClass() {
// 如果开启水波纹效果,则不启用hover-class效果
if (this.loading || this.disabled || this.ripple || this.hoverClass) return '';
let hoverClass = '';
hoverClass = this.plain ? 'u-' + this.type + '-plain-hover' : 'u-' + this.type + '-hover';
return hoverClass;
},
computed: {
// 当没有传bgColor变量时,按钮按下去的颜色类名
getHoverClass() {
// 如果开启水波纹效果,则不启用hover-class效果
if (this.loading || this.disabled || this.ripple || this.hoverClass) return '';
let hoverClass = '';
hoverClass = this.plain ? 'u-' + this.type + '-plain-hover' : 'u-' + this.type + '-hover';
return hoverClass;
},
// 按钮主题
buttonStyle() {
let style = {};
if (this.type == 'default') {
if (this.disabled) {
style.color = "#c0c4cc";
style.backgroundColor = "#ffffff";
style.borderColor = "#e4e7ed";
// 按钮主题
buttonStyle() {
let style = {};
if (this.type == 'default') {
if (this.disabled) {
style.color = '#c0c4cc';
style.backgroundColor = '#ffffff';
style.borderColor = '#e4e7ed';
} else {
style.color = this.$u.color['contentColor'];
style.backgroundColor = '#ffffff';
style.borderColor = '#c0c4cc';
}
} else {
if (this.disabled) {
if (this.plain) {
style.color = this.$u.color[this.type + 'Disabled'];
style.backgroundColor = this.$u.color[this.type + 'Light'];
style.borderColor = this.$u.color[this.type + 'Disabled'];
} else {
style.color = this.$u.color['contentColor'];
style.backgroundColor = "#ffffff";
style.borderColor = "#c0c4cc";
style.color = '#ffffff';
style.backgroundColor = this.$u.color[this.type + 'Disabled'];
style.borderColor = this.$u.color[this.type + 'Disabled'];
}
} else {
if (this.disabled) {
if (this.plain) {
style.color = this.$u.color[this.type + 'Disabled'];
style.backgroundColor = this.$u.color[this.type + 'Light'];
style.borderColor = this.$u.color[this.type + 'Disabled'];
} else {
style.color = "#ffffff";
style.backgroundColor = this.$u.color[this.type + 'Disabled'];
style.borderColor = this.$u.color[this.type + 'Disabled'];
}
if (this.plain) {
style.color = this.$u.color[this.type];
style.backgroundColor = this.$u.color[this.type + 'Light'];
style.borderColor = this.$u.color[this.type + 'Disabled'];
} else {
if (this.plain) {
style.color = this.$u.color[this.type]
style.backgroundColor = this.$u.color[this.type + 'Light'];
style.borderColor = this.$u.color[this.type + 'Disabled'];
} else {
style.color = "#ffffff";
style.backgroundColor = this.$u.color[this.type];
style.borderColor = this.$u.color[this.type];
}
style.color = '#ffffff';
style.backgroundColor = this.$u.color[this.type];
style.borderColor = this.$u.color[this.type];
}
}
return Object.assign(style, this.customStyle);
},
// 在'primary', 'success', 'error', 'warning'类型下,不显示边框,否则会造成四角有毛刺现象
showHairLineBorder() {
if (['primary', 'success', 'error', 'warning'].indexOf(this.type) >= 0 && !this.plain) {
return '';
} else {
return 'u-hairline-border';
}
}
return Object.assign(style, this.customStyle);
},
data() {
return {
rippleTop: 0, // 水波纹的起点Y坐标到按钮上边界的距离
rippleLeft: 0, // 水波纹起点X坐标到按钮左边界的距离
fields: {}, // 波纹按钮节点信息
waveActive: false, // 激活水波纹
// 在'primary', 'success', 'error', 'warning'类型下,不显示边框,否则会造成四角有毛刺现象
showHairLineBorder() {
if (['primary', 'success', 'error', 'warning'].indexOf(this.type) >= 0 && !this.plain) {
return '';
} else {
return 'u-hairline-border';
}
}
},
data() {
return {
rippleTop: 0, // 水波纹的起点Y坐标到按钮上边界的距离
rippleLeft: 0, // 水波纹起点X坐标到按钮左边界的距离
fields: {}, // 波纹按钮节点信息
waveActive: false // 激活水波纹
};
},
methods: {
// 按钮点击
click(e) {
// 如果按钮时disabled和loading状态,不触发水波纹效果
if (this.loading === true || this.disabled === true) return;
// 是否开启水波纹效果
if (this.ripple) {
// 每次点击时,移除上一次的类,再次添加,才能触发动画效果
this.waveActive = false;
this.$nextTick(function() {
this.getWaveQuery(e);
});
}
this.$emit('click');
},
methods: {
// 按钮点击
click(e) {
// 如果按钮时disabled和loading状态,不触发水波纹效果
if (this.loading === true || this.disabled === true) return;
// 是否开启水波纹效果
if (this.ripple) {
// 每次点击时,移除上一次的类,再次添加,才能触发动画效果
this.waveActive = false;
this.$nextTick(function() {
this.getWaveQuery(e);
});
}
this.$emit('click');
},
// 查询按钮的节点信息
getWaveQuery(e) {
this.getElQuery().then(res => {
// 查询返回的是一个数组节点
let data = res[0];
// 查询不到节点信息,不操作
if (!data.width || !data.width) return;
// 水波纹的最终形态是一个正方形(通过border-radius让其变为一个圆形),这里要保证正方形的边长等于按钮的最长边
// 最终的方形(变换后的圆形)才能覆盖整个按钮
data.targetWidth = (data.height > data.width ? data.height : data.width);
if (!data.targetWidth) return;
this.fields = data;
let touchesX = '',
touchesY = '';
// #ifdef MP-BAIDU
touchesX = e.changedTouches[0].clientX;
touchesY = e.changedTouches[0].clientY;
// #endif
// #ifdef MP-ALIPAY
touchesX = e.detail.clientX;
touchesY = e.detail.clientY;
// #endif
// #ifndef MP-BAIDU || MP-ALIPAY
touchesX = e.touches[0].clientX;
touchesY = e.touches[0].clientY;
// #endif
// 获取触摸点相对于按钮上边和左边的x和y坐标,原理是通过屏幕的触摸点(touchesY),减去按钮的上边界data.top
// 但是由于`transform-origin`默认是center,所以这里再减去半径才是水波纹view应该的位置
// 总的来说,就是把水波纹的矩形(变换后的圆形)的中心点,移动到我们的触摸点位置
this.rippleTop = (touchesY - data.top - (data.targetWidth / 2));
this.rippleLeft = (touchesX - data.left - (data.targetWidth / 2));
this.$nextTick(() => {
this.waveActive = true;
})
})
},
// 获取节点信息
getElQuery() {
return new Promise(resolve => {
let queryInfo = '';
// 获取元素节点信息,请查看uniapp相关文档
// https://uniapp.dcloud.io/api/ui/nodes-info?id=nodesrefboundingclientrect
queryInfo = uni.createSelectorQuery().in(this);
//#ifdef MP-ALIPAY
queryInfo = uni.createSelectorQuery();
//#endif
queryInfo.select('.u-btn').boundingClientRect();
queryInfo.exec((data) => {
resolve(data)
});
})
},
// 下面为对接uniapp官方按钮开放能力事件回调的对接
getphonenumber(res) {
this.$emit('getphonenumber', res);
},
getuserinfo(res) {
this.$emit('getuserinfo', res);
},
error(res) {
this.$emit('error', res);
},
opensetting(res) {
this.$emit('opensetting', res);
},
launchapp(res) {
this.$emit('launchapp', res);
},
// 查询按钮的节点信息
getWaveQuery(e) {
this.getElQuery().then(res => {
// 查询返回的是一个数组节点
let data = res[0];
// 查询不到节点信息,不操作
if (!data.width || !data.width) return;
// 水波纹的最终形态是一个正方形(通过border-radius让其变为一个圆形),这里要保证正方形的边长等于按钮的最长边
// 最终的方形(变换后的圆形)才能覆盖整个按钮
data.targetWidth = data.height > data.width ? data.height : data.width;
if (!data.targetWidth) return;
this.fields = data;
let touchesX = '',
touchesY = '';
// #ifdef MP-BAIDU
touchesX = e.changedTouches[0].clientX;
touchesY = e.changedTouches[0].clientY;
// #endif
// #ifdef MP-ALIPAY
touchesX = e.detail.clientX;
touchesY = e.detail.clientY;
// #endif
// #ifndef MP-BAIDU || MP-ALIPAY
touchesX = e.touches[0].clientX;
touchesY = e.touches[0].clientY;
// #endif
// 获取触摸点相对于按钮上边和左边的x和y坐标,原理是通过屏幕的触摸点(touchesY),减去按钮的上边界data.top
// 但是由于`transform-origin`默认是center,所以这里再减去半径才是水波纹view应该的位置
// 总的来说,就是把水波纹的矩形(变换后的圆形)的中心点,移动到我们的触摸点位置
this.rippleTop = touchesY - data.top - data.targetWidth / 2;
this.rippleLeft = touchesX - data.left - data.targetWidth / 2;
this.$nextTick(() => {
this.waveActive = true;
});
});
},
// 获取节点信息
getElQuery() {
return new Promise(resolve => {
let queryInfo = '';
// 获取元素节点信息,请查看uniapp相关文档
// https://uniapp.dcloud.io/api/ui/nodes-info?id=nodesrefboundingclientrect
queryInfo = uni.createSelectorQuery().in(this);
//#ifdef MP-ALIPAY
queryInfo = uni.createSelectorQuery();
//#endif
queryInfo.select('.u-btn').boundingClientRect();
queryInfo.exec(data => {
resolve(data);
});
});
},
// 下面为对接uniapp官方按钮开放能力事件回调的对接
getphonenumber(res) {
this.$emit('getphonenumber', res);
},
getuserinfo(res) {
this.$emit('getuserinfo', res);
},
error(res) {
this.$emit('error', res);
},
opensetting(res) {
this.$emit('opensetting', res);
},
launchapp(res) {
this.$emit('launchapp', res);
}
}
};
</script>
<style scoped lang="scss">
.u-btn::after {
border: none;
}
.u-btn::after {
border: none;
}
.u-btn {
position: relative;
border: 0;
//border-radius: 10rpx;
display: inline-block;
overflow: hidden;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
padding: 0 40rpx;
z-index: 1;
box-sizing: border-box;
transition: all 0.15s;
}
.u-btn {
position: relative;
border: 0;
//border-radius: 10rpx;
display: inline-block;
overflow: hidden;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
padding: 0 40rpx;
z-index: 1;
box-sizing: border-box;
transition: all 0.15s;
}
.u-hairline-border:after {
content: ' ';
position: absolute;
pointer-events: none;
// 设置为border-box,意味着下面的scale缩小为0.5,实际上缩小的是伪元素的内容(border-box意味着内容不含border)
box-sizing: border-box;
// 中心点作为变形(scale())的原点
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
left: 0;
top: 0;
width: 200%;
height: 200%;
-webkit-transform: scale(0.5, 0.5);
transform: scale(0.5, 0.5);
border: 1px solid currentColor;
z-index: 0;
}
.u-hairline-border:after {
content: ' ';
position: absolute;
pointer-events: none;
// 设置为border-box,意味着下面的scale缩小为0.5,实际上缩小的是伪元素的内容(border-box意味着内容不含border)
box-sizing: border-box;
// 中心点作为变形(scale())的原点
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
left: 0;
top: 0;
width: 200%;
height: 200%;
-webkit-transform: scale(0.5, 0.5);
transform: scale(0.5, 0.5);
border: 1px solid currentColor;
z-index: 0;
}
.u-bold-border {
border: 1px solid #FFFFFF;
}
.u-bold-border {
border: 1px solid #ffffff;
}
.u-wave-ripple {
z-index: 0;
position: absolute;
border-radius: 100%;
background-clip: padding-box;
pointer-events: none;
user-select: none;
transform: scale(0);
opacity: 1;
transform-origin: center;
}
.u-wave-ripple {
z-index: 0;
position: absolute;
border-radius: 100%;
background-clip: padding-box;
pointer-events: none;
user-select: none;
transform: scale(0);
opacity: 1;
transform-origin: center;
}
.u-wave-ripple.u-wave-active {
opacity: 0;
transform: scale(2);
transition: opacity 1s linear, transform 0.4s linear;
}
.u-wave-ripple.u-wave-active {
opacity: 0;
transform: scale(2);
transition: opacity 1s linear, transform 0.4s linear;
}
.u-round-circle {
border-radius: 100rpx;
}
.u-round-circle {
border-radius: 100rpx;
}
.u-round-circle::after {
border-radius: 100rpx;
}
.u-round-circle::after {
border-radius: 100rpx;
}
.u-loading::after {
background-color: hsla(0, 0%, 100%, .35);
}
.u-loading::after {
background-color: hsla(0, 0%, 100%, 0.35);
}
.u-size-default {
font-size: 30rpx;
height: 80rpx;
line-height: 80rpx;
}
.u-size-default {
font-size: 30rpx;
height: 80rpx;
line-height: 80rpx;
}
.u-size-medium {
display: inline-flex;
width: auto;
font-size: 26rpx;
height: 70rpx;
line-height: 70rpx;
padding: 0 80rpx;
}
.u-size-medium {
display: inline-flex;
width: auto;
font-size: 26rpx;
height: 70rpx;
line-height: 70rpx;
padding: 0 80rpx;
}
.u-size-mini {
display: inline-flex;
width: auto;
font-size: 22rpx;
padding-top: 1px;
height: 50rpx;
line-height: 50rpx;
padding: 0 20rpx;
}
.u-size-mini {
display: inline-flex;
width: auto;
font-size: 22rpx;
padding-top: 1px;
height: 50rpx;
line-height: 50rpx;
padding: 0 20rpx;
}
.u-primary-plain-hover {
color: #FFFFFF !important;
background: $u-type-primary-dark !important;
}
.u-primary-plain-hover {
color: #ffffff !important;
background: $u-type-primary-dark !important;
}
.u-default-plain-hover {
color: $u-type-primary-dark !important;
background: $u-type-primary-light !important;
}
.u-default-plain-hover {
color: $u-type-primary-dark !important;
background: $u-type-primary-light !important;
}
.u-success-plain-hover {
color: #FFFFFF !important;
background: $u-type-success-dark !important;
}
.u-success-plain-hover {
color: #ffffff !important;
background: $u-type-success-dark !important;
}
.u-warning-plain-hover {
color: #FFFFFF !important;
background: $u-type-warning-dark !important;
}
.u-warning-plain-hover {
color: #ffffff !important;
background: $u-type-warning-dark !important;
}
.u-error-plain-hover {
color: #FFFFFF !important;
background: $u-type-error-dark !important;
}
.u-error-plain-hover {
color: #ffffff !important;
background: $u-type-error-dark !important;
}
.u-info-plain-hover {
color: #FFFFFF !important;
background: $u-type-info-dark !important;
}
.u-info-plain-hover {
color: #ffffff !important;
background: $u-type-info-dark !important;
}
.u-default-hover {
color: $u-type-primary-dark !important;
border-color: $u-type-primary-dark !important;
background-color: $u-type-primary-light !important;
;
}
.u-default-hover {
color: $u-type-primary-dark !important;
border-color: $u-type-primary-dark !important;
background-color: $u-type-primary-light !important;
}
.u-primary-hover {
background: $u-type-primary-dark !important;
color: #fff;
}
.u-primary-hover {
background: $u-type-primary-dark !important;
color: #fff;
}
.u-success-hover {
background: $u-type-success-dark !important;
color: #fff;
}
.u-success-hover {
background: $u-type-success-dark !important;
color: #fff;
}
.u-info-hover {
background: $u-type-info-dark !important;
color: #fff;
}
.u-info-hover {
background: $u-type-info-dark !important;
color: #fff;
}
.u-warning-hover {
background: $u-type-warning-dark !important;
color: #fff;
}
.u-warning-hover {
background: $u-type-warning-dark !important;
color: #fff;
}
.u-error-hover {
background: $u-type-error-dark !important;
color: #fff;
}
.u-error-hover {
background: $u-type-error-dark !important;
color: #fff;
}
</style>
......@@ -76,17 +76,17 @@ export default {
},
// 左侧标题
title: {
type: String,
type: [String, Number],
default: ''
},
// 右侧内容
value: {
type: String,
type: [String, Number],
default: ''
},
// 标题下方的描述信息
label: {
type: String,
type: [String, Number],
default: ''
},
// 是否显示内边框
......
......@@ -16,8 +16,6 @@
</template>
<script>
// observer最终赋值的是个对象,不能放到data中,vue会报错(浏览器),也不能在用到的地方let定义,某些安卓也报错……
let observer = {};
/**
* lazyLoad 懒加载
* @description 懒加载使用的场景为:页面有很多图片时,APP会同时加载所有的图片,导致页面卡顿,各个位置的图片出现前后不一致等.
......@@ -116,9 +114,13 @@
},
// 计算图片的高度,可能为auto,带%,或者直接数值
imgHeight() {
return this.height == 'auto' ? 'auto' : this.height.indexOf('%') != -1 ? this.height : this.height + 'rpx';
return this.height == 'auto' ? 'auto' : String(this.height).indexOf('%') != -1 ? this.height : this.height + 'rpx';
}
},
created() {
// 由于一些特殊原因,不能将此变量放到data中定义
this.observer = {};
},
watch: {
isShow(nVal) {
// 如果是不开启过渡效果,直接返回
......@@ -166,7 +168,11 @@
// 图片加载失败
loadError() {
this.isError = true;
}
},
disconnectObserver(observerName) {
const observer = this[observerName];
observer && observer.disconnect();
},
},
beforeDestroy() {
// 销毁页面时,可能还没触发某张很底部的懒加载图片,所以把这个事件给去掉
......@@ -180,25 +186,23 @@
});
})
// mounted的时候,不一定挂载了这个元素,延时30ms,否则会报错或者不报错,但是也没有效果
let that = this;
//let observer = null;
setTimeout(() => {
// 这里是组件内获取布局状态,不能用uni.createIntersectionObserver,而必须用this.createIntersectionObserver
// 因为这样可以把选择器的选取范围定义在自定义组件内
// 否则某些安卓机型报错,也不能加this(如createIntersectionObserver(this)),否则某些机型也报错
observer = uni.createIntersectionObserver(this);
this.disconnectObserver('contentObserver');
const contentObserver = uni.createIntersectionObserver(this);
// 要理解这里怎么计算的,请看这个:
// https://blog.csdn.net/qq_25324335/article/details/83687695
observer.relativeToViewport({
bottom: that.getThreshold,
}).observe('.u-lazy-item-' + that.elIndex, (res) => {
contentObserver.relativeToViewport({
bottom: this.getThreshold,
}).observe('.u-lazy-item-' + this.elIndex, (res) => {
if (res.intersectionRatio > 0) {
// 懒加载状态改变
that.isShow = true;
this.isShow = true;
// 如果图片已经加载,去掉监听,减少性能的消耗
observer.disconnect();
this.disconnectObserver('contentObserver');
}
})
this.contentObserver = contentObserver;
}, 30)
}
}
......
......@@ -114,8 +114,8 @@
// 为了让用户能够删除所有输入值,重新输入内容,删除所有值后,内容为空字符串
if (v1 == '') return;
let value = 0;
// 首先判断是否正整数,并且在min和max之间,如果不是,使用原来值
let tmp = /(^\d+$)/.test(v1) && value[0] != 0;
// 首先判断是否正整数,并且第一位数字不为0,并且在min和max之间,如果不是,使用原来值
let tmp = /(^\d+$)/.test(v1) && String(v1)[0] != 0;
if (tmp && v1 >= this.min && v1 <= this.max) value = v1;
else value = v2;
this.handleChange(value, 'change');
......
<template>
<view class="u-rate" :id="elId" @touchmove.stop.prevent="touchMove">
<view class="u-star-wrap" v-for="(item, index) in count" :key="index" :class="[elClass]">
<u-icon :name="activeIndex > index ? activeIcon : inactiveIcon" @click="click(index + 1, $event)" :style="{
color: activeIndex > index ? activeColor : inactiveColor,
fontSize: size + 'rpx',
padding: `0 ${gutter / 2 + 'rpx'}`
}"></u-icon>
<u-icon
:name="activeIndex > index ? activeIcon : inactiveIcon"
@click="click(index + 1, $event)"
:color="activeIndex > index ? activeColor : inactiveColor"
:style="{
fontSize: size + 'rpx',
padding: `0 ${gutter / 2 + 'rpx'}`
}"
></u-icon>
</view>
</view>
</template>
<script>
/**
* rate 评分
* @description 该组件一般用于满意度调查,星型评分的场景
* @tutorial https://www.uviewui.com/components/rate.html
* @property {String Number} count 最多可选的星星数量(默认5)
* @property {String Number} current 默认选中的星星数量(默认0)
* @property {Boolean} disabled 是否禁止用户操作(默认false)
* @property {String Number} size 星星的大小,单位rpx(默认32)
* @property {String} inactive-color 未选中星星的颜色(默认#b2b2b2)
* @property {String} active-color 选中的星星颜色(默认#FA3534)
* @property {String} gutter 星星之间的距离(默认10)
* @property {String Number} min-count 最少选中星星的个数(默认0)
* @property {Boolean} allow-half 是否允许半星选择(默认false)
* @event {Function} change 选中的星星发生变化时触发
* @example <u-rate :count="count" :current="2"></u-rate>
*/
export default {
name: "u-rate",
props: {
// 要显示的星星数量
count: {
type: [Number, String],
default: 5
},
// 当前需要默认选中的星星(选中的个数)
current: {
type: [Number, String],
default: 0
},
// 是否不可选中
disabled: {
type: Boolean,
default: false
},
// 星星的大小,单位rpx
size: {
type: [Number, String],
default: 32
},
// 未选中时的颜色
inactiveColor: {
type: String,
default: "#b2b2b2"
},
// 选中的颜色
activeColor: {
type: String,
default: "#FA3534"
},
// 星星之间的间距,单位rpx
gutter: {
type: [Number, String],
default: 10
},
// 最少能选择的星星个数
minCount: {
type: [Number, String],
default: 0
},
// 是否允许半星(功能尚未实现)
allowHalf: {
type: Boolean,
default: false
},
// 选中时的图标(星星)
activeIcon: {
type: String,
default: 'star-fill'
},
// 未选中时的图标(星星)
inactiveIcon: {
type: String,
default: 'star'
},
/**
* rate 评分
* @description 该组件一般用于满意度调查,星型评分的场景
* @tutorial https://www.uviewui.com/components/rate.html
* @property {String Number} count 最多可选的星星数量(默认5)
* @property {String Number} current 默认选中的星星数量(默认0)
* @property {Boolean} disabled 是否禁止用户操作(默认false)
* @property {String Number} size 星星的大小,单位rpx(默认32)
* @property {String} inactive-color 未选中星星的颜色(默认#b2b2b2)
* @property {String} active-color 选中的星星颜色(默认#FA3534)
* @property {String} gutter 星星之间的距离(默认10)
* @property {String Number} min-count 最少选中星星的个数(默认0)
* @property {Boolean} allow-half 是否允许半星选择(默认false)
* @event {Function} change 选中的星星发生变化时触发
* @example <u-rate :count="count" :current="2"></u-rate>
*/
export default {
name: 'u-rate',
props: {
// 要显示的星星数量
count: {
type: [Number, String],
default: 5
},
data() {
return {
// 生成一个唯一id,否则一个页面多个评分组件,会造成冲突
elId: this.$u.guid(),
elClass: this.$u.guid(),
starBoxLeft: 0, // 评分盒子左边到屏幕左边的距离,用于滑动选择时计算距离
activeIndex: this.current, // 当前激活的星星的index
starWidth: 0, // 每个星星的宽度
starWidthArr: [], //每个星星最右边到组件盒子最左边的距离
}
// 当前需要默认选中的星星(选中的个数)
current: {
type: [Number, String],
default: 0
},
watch: {
current(val) {
this.activeIndex = val;
}
// 是否不可选中
disabled: {
type: Boolean,
default: false
},
methods: {
// 获取评分组件盒子的布局信息
getElRectById() {
let query = uni.createSelectorQuery().in(this)
query.select("#" + this.elId).boundingClientRect((res) => {
// 如果获取不到,延时到本轮代码的末期再尝试
if (!res.left) {
setTimeout(this.getElRectByClass);
return;
}
this.starBoxLeft = res.left;
}).exec()
},
// 获取单个星星的尺寸
getElRectByClass() {
let query = uni.createSelectorQuery().in(this)
query.select("." + this.elClass).boundingClientRect((res) => {
if (!res.width) {
setTimeout(this.getElRectByClass);
return;
}
this.starWidth = res.width;
// 把每个星星右边到组件盒子左边的距离放入数组中
for (let i = 0; i < this.count; i++) {
this.starWidthArr[i] = (i + 1) * this.starWidth;
}
}).exec()
},
// 手指滑动
touchMove(e) {
if (this.disabled) {
return;
}
if (!e.changedTouches[0]) {
return;
}
const movePageX = e.changedTouches[0].pageX;
// 滑动点相对于评分盒子左边的距离
const distance = movePageX - this.starBoxLeft;
// 如果滑动到了评分盒子的左边界,就设置为0星
if (distance <= 0) {
this.activeIndex = 0;
}
// 滑动的距离,相当于多少颗星星
let index = Math.ceil(distance / this.starWidth);
this.activeIndex = index > this.count ? this.count : index;
// 对最少颗星星的限制
if (this.activeIndex < this.minCount) this.activeIndex = this.minCount;
this.$emit('change', this.activeIndex)
},
// 通过点击,直接选中
click(index, e) {
if (this.disabled) {
return;
}
// 半星选择,尚未实现
if (this.allowHalf) {
}
// 对第一个星星特殊处理,只有一个的时候,点击可以取消,否则无法作0星评价
if (index == 1) {
if (this.activeIndex == 1) this.activeIndex = 0;
else this.activeIndex = 1;
} else {
this.activeIndex = index;
// 星星的大小,单位rpx
size: {
type: [Number, String],
default: 32
},
// 未选中时的颜色
inactiveColor: {
type: String,
default: '#b2b2b2'
},
// 选中的颜色
activeColor: {
type: String,
default: '#FA3534'
},
// 星星之间的间距,单位rpx
gutter: {
type: [Number, String],
default: 10
},
// 最少能选择的星星个数
minCount: {
type: [Number, String],
default: 0
},
// 是否允许半星(功能尚未实现)
allowHalf: {
type: Boolean,
default: false
},
// 选中时的图标(星星)
activeIcon: {
type: String,
default: 'star-fill'
},
// 未选中时的图标(星星)
inactiveIcon: {
type: String,
default: 'star'
}
},
data() {
return {
// 生成一个唯一id,否则一个页面多个评分组件,会造成冲突
elId: this.$u.guid(),
elClass: this.$u.guid(),
starBoxLeft: 0, // 评分盒子左边到屏幕左边的距离,用于滑动选择时计算距离
activeIndex: this.current, // 当前激活的星星的index
starWidth: 0, // 每个星星的宽度
starWidthArr: [] //每个星星最右边到组件盒子最左边的距离
};
},
watch: {
current(val) {
this.activeIndex = val;
}
},
methods: {
// 获取评分组件盒子的布局信息
getElRectById() {
// uView封装的获取节点的方法,详见文档
this.$u.getRect('#' + this.elId).then(res => {
this.starBoxLeft = res.left;
})
},
// 获取单个星星的尺寸
getElRectByClass() {
// uView封装的获取节点的方法,详见文档
this.$u.getRect('.' + this.elClass).then(res => {
this.starWidth = res.width;
// 把每个星星右边到组件盒子左边的距离放入数组中
for (let i = 0; i < this.count; i++) {
this.starWidthArr[i] = (i + 1) * this.starWidth;
}
// 对最少颗星星的限制
if (this.activeIndex < this.minCount) this.activeIndex = this.minCount;
this.$emit('change', this.activeIndex)
},
})
},
mounted() {
this.getElRectById();
this.getElRectByClass();
// 手指滑动
touchMove(e) {
if (this.disabled) {
return;
}
if (!e.changedTouches[0]) {
return;
}
const movePageX = e.changedTouches[0].pageX;
// 滑动点相对于评分盒子左边的距离
const distance = movePageX - this.starBoxLeft;
// 如果滑动到了评分盒子的左边界,就设置为0星
if (distance <= 0) {
this.activeIndex = 0;
}
// 滑动的距离,相当于多少颗星星
let index = Math.ceil(distance / this.starWidth);
this.activeIndex = index > this.count ? this.count : index;
// 对最少颗星星的限制
if (this.activeIndex < this.minCount) this.activeIndex = this.minCount;
this.$emit('change', this.activeIndex);
},
// 通过点击,直接选中
click(index, e) {
if (this.disabled) {
return;
}
// 半星选择,尚未实现
if (this.allowHalf) {
}
// 对第一个星星特殊处理,只有一个的时候,点击可以取消,否则无法作0星评价
if (index == 1) {
if (this.activeIndex == 1) this.activeIndex = 0;
else this.activeIndex = 1;
} else {
this.activeIndex = index;
}
// 对最少颗星星的限制
if (this.activeIndex < this.minCount) this.activeIndex = this.minCount;
this.$emit('change', this.activeIndex);
}
},
mounted() {
this.getElRectById();
this.getElRectByClass();
}
};
</script>
<style scoped lang="scss">
.u-rate {
display: -webkit-inline-flex;
display: inline-flex;
align-items: center;
margin: 0;
padding: 0;
}
.u-rate {
display: -webkit-inline-flex;
display: inline-flex;
align-items: center;
margin: 0;
padding: 0;
}
.u-icon {
box-sizing: border-box;
}
.u-icon {
box-sizing: border-box;
}
</style>
......@@ -18,6 +18,7 @@
@input="inputChange"
:disabled="disabled"
@focus="getFocus"
:maxlength="getMaxlength"
:focus="focus"
placeholder-class="u-placeholder-class"
:placeholder="placeholder"
......@@ -144,6 +145,11 @@ export default {
default() {
return {}
}
},
// 输入框最大能输入的长度,-1为不限制长度(来自uniapp文档)
maxlength: {
type: [Number, String],
default: -1
}
},
data() {
......@@ -180,6 +186,10 @@ export default {
borderStyle() {
if (this.borderColor) return `1px solid ${this.borderColor}`;
else return 'none';
},
// 将maxlength转为数值
getMaxlength() {
return Number(this.maxlength);
}
},
methods: {
......
This diff is collapsed.
......@@ -10,8 +10,15 @@ module.exports = {
// 查询节点信息
$uGetRect(selector, all) {
return new Promise(resolve => {
uni.createSelectorQuery()
.in(this)[all ? 'selectAll' : 'select'](selector)
let query = null;
// 支付宝小程序不能加后面的.in(this),是它自身的限制
// #ifndef MP-ALIPAY
query = uni.createSelectorQuery().in(this)
// #endif
// #ifdef MP-ALIPAY
query = uni.createSelectorQuery()
// #endif
query[all ? 'selectAll' : 'select'](selector)
.boundingClientRect(rect => {
if (all && Array.isArray(rect) && rect.length) {
resolve(rect)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment