vue-pro/yudao-ui-admin-uniapp/uni_modules/uni-rate/components/uni-rate/uni-rate.vue
2022-08-03 11:52:35 +08:00

362 lines
8.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>