¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |