Commit 9d200c0c authored by wlxuqu's avatar wlxuqu

1. 优化懒加载的方法

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