| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 | <template>	<view>		<view ref="uni-rate" class="uni-rate">			<view class="uni-rate__icon" :class="{'uni-cursor-not-allowed': disabled}"				:style="{ 'margin-right': marginNumber + 'px' }" v-for="(star, index) in stars" :key="index"				@touchstart.stop="touchstart" @touchmove.stop="touchmove" @mousedown.stop="mousedown"				@mousemove.stop="mousemove" @mouseleave="mouseleave">				<uni-icons :color="color" :size="size" :type="isFill ? 'star-filled' : 'star'" />				<!-- #ifdef APP-NVUE -->				<view :style="{ width: star.activeWitch.replace('%','')*size/100+'px'}" class="uni-rate__icon-on">					<uni-icons style="text-align: left;" :color="disabled?'#ccc':activeColor" :size="size"						type="star-filled" />				</view>				<!-- #endif -->				<!-- #ifndef APP-NVUE -->				<view :style="{ width: star.activeWitch}" class="uni-rate__icon-on">					<uni-icons :color="disabled?disabledColor:activeColor" :size="size" type="star-filled" />				</view>				<!-- #endif -->			</view>		</view>	</view></template><script>	// #ifdef APP-NVUE	const dom = uni.requireNativePlugin('dom');	// #endif	/**	 * Rate 评分	 * @description 评分组件	 * @tutorial https://ext.dcloud.net.cn/plugin?id=33	 * @property {Boolean} 	isFill = [true|false] 		星星的类型,是否为实心类型, 默认为实心	 * @property {String} 	color 						未选中状态的星星颜色,默认为 "#ececec"	 * @property {String} 	activeColor 				选中状态的星星颜色,默认为 "#ffca3e"	 * @property {String} 	disabledColor 				禁用状态的星星颜色,默认为 "#c0c0c0"	 * @property {Number} 	size 						星星的大小	 * @property {Number} 	value/v-model 				当前评分	 * @property {Number} 	max 						最大评分评分数量,目前一分一颗星	 * @property {Number} 	margin 						星星的间距,单位 px	 * @property {Boolean} 	disabled = [true|false] 	是否为禁用状态,默认为 false	 * @property {Boolean} 	readonly = [true|false] 	是否为只读状态,默认为 false	 * @property {Boolean} 	allowHalf = [true|false] 	是否实现半星,默认为 false	 * @property {Boolean} 	touchable = [true|false] 	是否支持滑动手势,默认为 true	 * @event {Function} change 						uniRate 的 value 改变时触发事件,e={value:Number}	 */	export default {		name: "UniRate",		props: {			isFill: {				// 星星的类型,是否镂空				type: [Boolean, String],				default: true			},			color: {				// 星星未选中的颜色				type: String,				default: "#ececec"			},			activeColor: {				// 星星选中状态颜色				type: String,				default: "#ffca3e"			},			disabledColor: {				// 星星禁用状态颜色				type: String,				default: "#c0c0c0"			},			size: {				// 星星的大小				type: [Number, String],				default: 24			},			value: {				// 当前评分				type: [Number, String],				default: 0			},			modelValue: {				// 当前评分				type: [Number, String],				default: 0			},			max: {				// 最大评分				type: [Number, String],				default: 5			},			margin: {				// 星星的间距				type: [Number, String],				default: 0			},			disabled: {				// 是否可点击				type: [Boolean, String],				default: false			},			readonly: {				// 是否只读				type: [Boolean, String],				default: false			},			allowHalf: {				// 是否显示半星				type: [Boolean, String],				default: false			},			touchable: {				// 是否支持滑动手势				type: [Boolean, String],				default: true			}		},		data() {			return {				valueSync: "",				userMouseFristMove: true,				userRated: false,				userLastRate: 1			};		},		watch: {			value(newVal) {				this.valueSync = Number(newVal);			},			modelValue(newVal) {				this.valueSync = Number(newVal);			},		},		computed: {			stars() {				const value = this.valueSync ? this.valueSync : 0;				const starList = [];				const floorValue = Math.floor(value);				const ceilValue = Math.ceil(value);				for (let i = 0; i < this.max; i++) {					if (floorValue > i) {						starList.push({							activeWitch: "100%"						});					} else if (ceilValue - 1 === i) {						starList.push({							activeWitch: (value - floorValue) * 100 + "%"						});					} else {						starList.push({							activeWitch: "0"						});					}				}				return starList;			},			marginNumber() {				return Number(this.margin)			}		},		created() {			this.valueSync = Number(this.value || this.modelValue);			this._rateBoxLeft = 0			this._oldValue = null		},		mounted() {			setTimeout(() => {				this._getSize()			}, 100)			// #ifdef H5			this.PC = this.IsPC()			// #endif		},		methods: {			touchstart(e) {				// #ifdef H5				if (this.IsPC()) return				// #endif				if (this.readonly || this.disabled) return				const {					clientX,					screenX				} = e.changedTouches[0]				// TODO 做一下兼容,只有 Nvue 下才有 screenX,其他平台式 clientX				this._getRateCount(clientX || screenX)			},			touchmove(e) {				// #ifdef H5				if (this.IsPC()) return				// #endif				if (this.readonly || this.disabled || !this.touchable) return				const {					clientX,					screenX				} = e.changedTouches[0]				this._getRateCount(clientX || screenX)			},			/**			 * 兼容 PC @tian			 */			mousedown(e) {				// #ifdef H5				if (!this.IsPC()) return				if (this.readonly || this.disabled) return				const {					clientX,				} = e				this.userLastRate = this.valueSync				this._getRateCount(clientX)				this.userRated = true				// #endif			},			mousemove(e) {				// #ifdef H5				if (!this.IsPC()) return				if (this.userRated) return				if (this.userMouseFristMove) {					console.log('---mousemove----', this.valueSync);					this.userLastRate = this.valueSync					this.userMouseFristMove = false				}				if (this.readonly || this.disabled || !this.touchable) return				const {					clientX,				} = e				this._getRateCount(clientX)				// #endif			},			mouseleave(e) {				// #ifdef H5				if (!this.IsPC()) return				if (this.readonly || this.disabled || !this.touchable) return				if (this.userRated) {					this.userRated = false					return				}				this.valueSync = this.userLastRate				// #endif			},			// #ifdef H5			IsPC() {				var userAgentInfo = navigator.userAgent;				var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];				var flag = true;				for (let v = 0; v < Agents.length - 1; v++) {					if (userAgentInfo.indexOf(Agents[v]) > 0) {						flag = false;						break;					}				}				return flag;			},			// #endif			/**			 * 获取星星个数			 */			_getRateCount(clientX) {				this._getSize()				const size = Number(this.size)				if (isNaN(size)) {					return new Error('size 属性只能设置为数字')				}				const rateMoveRange = clientX - this._rateBoxLeft				let index = parseInt(rateMoveRange / (size + this.marginNumber))				index = index < 0 ? 0 : index;				index = index > this.max ? this.max : index;				const range = parseInt(rateMoveRange - (size + this.marginNumber) * index);				let value = 0;				if (this._oldValue === index && !this.PC) return;				this._oldValue = index;				if (this.allowHalf) {					if (range > (size / 2)) {						value = index + 1					} else {						value = index + 0.5					}				} else {					value = index + 1				}				value = Math.max(0.5, Math.min(value, this.max))				this.valueSync = value				this._onChange()			},			/**			 * 触发动态修改			 */			_onChange() {				this.$emit("input", this.valueSync);				this.$emit("update:modelValue", this.valueSync);				this.$emit("change", {					value: this.valueSync				});			},			/**			 * 获取星星距离屏幕左侧距离			 */			_getSize() {				// #ifndef APP-NVUE				uni.createSelectorQuery()					.in(this)					.select('.uni-rate')					.boundingClientRect()					.exec(ret => {						if (ret) {							this._rateBoxLeft = ret[0].left						}					})				// #endif				// #ifdef APP-NVUE				dom.getComponentRect(this.$refs['uni-rate'], (ret) => {					const size = ret.size					if (size) {						this._rateBoxLeft = size.left					}				})				// #endif			}		}	};</script><style lang="scss">	.uni-rate {		/* #ifndef APP-NVUE */		display: flex;		/* #endif */		line-height: 1;		font-size: 0;		flex-direction: row;		/* #ifdef H5 */		cursor: pointer;		/* #endif */	}	.uni-rate__icon {		position: relative;		line-height: 1;		font-size: 0;	}	.uni-rate__icon-on {		overflow: hidden;		position: absolute;		top: 0;		left: 0;		line-height: 1;		text-align: left;	}	.uni-cursor-not-allowed {		/* #ifdef H5 */		cursor: not-allowed !important;		/* #endif */	}</style>
 |