@minix-iot/ui
Version:
基于Vue的移动端轻量级UI组件库
196 lines (194 loc) • 5.05 kB
JSX
import { Namespace } from '../../utils/namespace'
import Icon from '../icon/icon.jsx'
const namespace = Namespace.Create('rate')
export default {
name: namespace.name,
components: {
Icon
},
props: {
value: {
type: Number,
default: 0
},
count: {
type: Number,
default: 5
},
size: {
type: String | Number,
default: 'middle'
},
gutter: {
type: Number,
default: 0
},
color: {
type: String,
default: '#c7c7cc'
},
activeColor: {
type: String,
default: '#FF9500'
},
icon: {
type: String,
default: 'star-line'
},
activeIcon: {
type: String,
default: 'star'
},
disabled: {
type: Boolean,
default: false
},
readonly: {
type: Boolean,
default: false
},
touchable: {
type: Boolean,
default: false
},
allowHalf: {
type: Boolean,
default: false
}
},
model: {
prop: 'value',
event: 'change'
},
data() {
return {
rectScoreMap: []
};
},
computed: {
cls() {
return namespace.derive({
disabled: this.disabled
});
},
itemCls() {
return namespace.derive('item', [this.size]);
},
halfItemCls(){
return namespace.derive('item-half')
},
iconSize() {
if (Number.isNaN(parseInt(this.size))) {
let res = 16;
switch (this.size) {
case 'small':
res = 16;
break;
case 'middle':
res = 28;
break;
case 'large':
res = 32;
break;
}
return res;
} else {
return this.size;
}
},
itemMetaList() {
const res = [];
const value = this.value > this.count ? this.value % this.count : this.value;
for (let i = 0; i < this.count; i++) {
res.push(this.getMetaByIndex(i + 1, value));
}
return res;
}
},
methods: {
getMetaByIndex(index, value) {
const meta = { type: this.icon, color: this.color, isHalf: false };
if (value >= index) {
meta.type = this.activeIcon;
meta.color = this.activeColor;
return meta;
}
if (value + 0.5 >= index && this.allowHalf) {
meta.isHalf = true;
}
return meta;
},
mapScoreWithRange() {
const res = [];
for (let i = 0; i < this.itemMetaList.length; i++) {
const rect = this.$refs[`item_${i}`].getBoundingClientRect();
if (this.allowHalf) {
res.push({ left: rect.left, score: i + 0.5 }, { left: rect.left + rect.width / 2, score: i + 1 });
} else {
res.push({ left: rect.left, score: i + 1 });
}
}
this.rectScoreMap = res;
},
getScoreByPosition(left) {
for (let index = this.rectScoreMap.length - 1; index > 0; index--) {
const item = this.rectScoreMap[index];
if (item.left <= left) {
return item.score;
}
}
return this.allowHalf ? 0.5 : 1;
},
onTouchStart(e) {
if (this.readonly || !this.touchable || this.disabled) {
return;
}
this.mapScoreWithRange();
},
onTouchMove(e) {
if (this.readonly || !this.touchable || this.disabled) {
return;
}
const { clientX } = e.touches[0];
const score = this.getScoreByPosition(clientX);
this.emitChange(score);
},
onClick(e, i) {
if (this.readonly || this.disabled) {
return;
}
let res = i;
if (this.allowHalf) {
const target = e.currentTarget;
const rect = target.getBoundingClientRect();
const left = rect.left + rect.width / 2;
res += e.clientX <= left ? 0.5 : 1;
} else {
res += 1;
}
this.emitChange(res);
},
emitChange(value) {
this.$emit('change', value);
}
},
render(){
const renderItems = ()=> this.itemMetaList.map((v,i)=>(
<div ref={`item_${i}`} class={this.itemCls} onclick={e => this.onClick(e, i)}>
<Icon name={v.type} color={v.color} size={this.iconSize} />
{
v.isHalf &&(
<div class={this.halfItemCls}>
<Icon name={this.activeIcon} color={this.activeColor} size={this.iconSize} />
</div>
)
}
</div>
))
return (
<div class={this.cls} onTouchstart={this.onTouchStart} onTouchmove={this.onTouchMove}>
{renderItems()}
</div>
)
}
};