This commit is contained in:
wangxueliang 2017-11-01 17:03:42 +08:00
parent f4fb4fedbb
commit 6fc3c2c9f1
9 changed files with 358 additions and 0 deletions

View File

@ -3,6 +3,7 @@ import './checkbox/style'
import './icon/style'
import './radio/style'
import './grid/style'
import './rate/style'
export { default as Button } from './button'
@ -13,3 +14,5 @@ export { default as Icon } from './icon'
export { default as Radio } from './radio'
export { default as Grid } from './grid'
export { default as Rate } from './rate'

119
components/rate/Rate.vue Normal file
View File

@ -0,0 +1,119 @@
<template>
<ul
:class="[prefixCls, disabled ? `${prefixCls}-disabled` : '', className]"
@mouseleave="onMouseLeave">
<template v-for="i in count">
<Star
ref="stars"
:index="i"
:disabled="disabled"
:prefix-cls="`${prefixCls}-star`"
:allowHalf="allowHalf"
:value="currentValue"
@onClick="onClick"
@onHover="onHover"
:key="i">
<template slot-scope="props">
<slot>
<Icon type="star"/>
</slot>
</template>
</Star>
</template>
</ul>
</template>
<script>
import Star from './Star.vue';
import Icon from '../icon/index';
import { getOffsetLeft } from '../util/util';
export default {
name: 'Rate',
props: {
count: {
type: Number,
default: 5,
},
value: {
type: Number,
default: 0,
},
defaultValue: {
type: Number,
default: 0,
},
onChange: {
type: Function,
default: () => {},
},
onHoverChange: {
type: Function,
default: () => {},
},
allowHalf: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
className: String,
},
data() {
return {
prefixCls: 'ant-rate',
hoverValue: undefined,
currentValue: undefined,
markValue: undefined,
}
},
created () {
this.currentValue = this.markValue = this.value || this.defaultValue
},
watch: {
hoverValue(val) {
if(val === undefined) {
this.currentValue = this.markValue;
return;
}
this.currentValue = val;
}
},
methods: {
onClick(event, index) {
let clValue = this.getStarValue(index, event.pageX);
this.markValue = clValue;
this.onMouseLeave();
this.onChange(clValue);
},
onHover(event, index) {
this.hoverValue = this.getStarValue(index, event.pageX);
this.onHoverChange(this.hoverValue);
},
getStarDOM (index) {
return this.$refs.stars[index].$el
},
getStarValue (index, x) {
let value = index;
if (this.allowHalf) {
const leftEdge = getOffsetLeft(this.getStarDOM(0))
const width = getOffsetLeft(this.getStarDOM(1)) - leftEdge
if ((x - leftEdge - width * (index-1)) < width / 2) {
value -= 0.5
}
}
return value
},
onMouseLeave() {
this.hoverValue = undefined
this.onHoverChange(undefined);
},
},
components: {
Star,
Icon,
}
}
</script>

52
components/rate/Star.vue Normal file
View File

@ -0,0 +1,52 @@
<template>
<li
:class="getClassName()"
@click="onClick"
@mousemove="onHover">
<div :class="`${this.prefixCls}-first`"><slot></slot></div>
<div :class="`${this.prefixCls}-second`"><slot></slot></div>
</li>
</template>
<script>
export default {
name: 'Star',
props: {
index: Number,
disabled: Boolean,
prefixCls: String,
allowHalf: Boolean,
value: Number,
},
data() {
return {
}
},
mounted() {
},
computed: {
},
methods: {
getClassName() {
const { prefixCls, index, value, allowHalf } = this;
const starValue = index;
if (allowHalf && value + 0.5 === starValue) {
return `${prefixCls} ${prefixCls}-half ${prefixCls}-active`;
}
return starValue <= value ? `${prefixCls} ${prefixCls}-full` : `${prefixCls} ${prefixCls}-zero`;
},
onClick(e) {
if(this.disabled) return;
this.$emit("onClick", e, this.index);
},
onHover(e) {
if(this.disabled) return;
this.$emit("onHover", e, this.index);
},
},
watch: {
},
commponents: {
}
}
</script>

3
components/rate/index.js Normal file
View File

@ -0,0 +1,3 @@
import Rate from './Rate';
export default Rate;

View File

@ -0,0 +1,2 @@
import '../../style/index.less';
import './index.less';

View File

@ -0,0 +1,73 @@
@import "../../style/themes/default";
@import "../../style/mixins/index";
@rate-prefix-cls: ~"@{ant-prefix}-rate";
.@{rate-prefix-cls} {
margin: 0;
padding: 0;
list-style: none;
font-size: 20px;
display: inline-block;
vertical-align: middle;
&-disabled &-star {
cursor: not-allowed;
&:hover {
transform: scale(1);
}
}
&-star {
margin: 0;
padding: 0;
display: inline-block;
margin-right: 8px;
position: relative;
transition: all .3s;
color: @rate-star-bg;
cursor: pointer;
&-first,
&-second {
user-select: none;
transition: all .3s;
}
&:hover {
transform: scale(1.1);
}
&-first {
position: absolute;
left: 0;
top: 0;
width: 50%;
height: 100%;
overflow: hidden;
opacity: 0;
}
&-half &-first,
&-half &-second {
opacity: 1;
}
&-half &-first,
&-full &-second {
color: @rate-star-color;
}
&-half:hover &-first,
&-full:hover &-second {
color: tint(@rate-star-color, 20%);
}
}
&-text {
margin-left: 8px;
vertical-align: middle;
display: inline-block;
font-size: @font-size-base;
}
}

40
components/util/util.js Normal file
View File

@ -0,0 +1,40 @@
function getScroll(w, top) {
let ret = top ? w.pageYOffset : w.pageXOffset;
const method = top ? 'scrollTop' : 'scrollLeft';
if (typeof ret !== 'number') {
const d = w.document;
// ie6,7,8 standard mode
ret = d.documentElement[method];
if (typeof ret !== 'number') {
// quirks mode
ret = d.body[method];
}
}
return ret;
}
function getClientPosition(elem) {
let box;
let x;
let y;
const doc = elem.ownerDocument;
const body = doc.body;
const docElem = doc && doc.documentElement;
box = elem.getBoundingClientRect();
x = box.left;
y = box.top;
x -= docElem.clientLeft || body.clientLeft || 0;
y -= docElem.clientTop || body.clientTop || 0;
return {
left: x,
top: y,
};
}
export const getOffsetLeft = (el) => {
const pos = getClientPosition(el);
const doc = el.ownerDocument;
const w = doc.defaultView || doc.parentWindow;
pos.left += getScroll(w);
return pos.left;
}

View File

@ -4,6 +4,7 @@ import Button from './button.vue'
import Radio from './radio.vue'
import Grid from './grid.vue'
// import Dialog from './dialog.vue'
import Rate from './rate.vue'
import './index.less'
new Vue({
el: '#app',
@ -13,6 +14,7 @@ new Vue({
<Checkbox />
<AntButton />
<Radio />
<Rate />
</div>
`,
components: {
@ -21,5 +23,6 @@ new Vue({
Checkbox,
Grid,
Radio,
Rate,
},
})

63
examples/rate.vue Normal file
View File

@ -0,0 +1,63 @@
<template>
<div>
基本
<Rate className="custom"></Rate>
</br>
半星
<Rate :allowHalf="allowHalf"></Rate>
</br>
默认3颗星
<Rate :value="initValue"></Rate>
</br>
只读
<Rate :value="initValue" :disabled="disabled"></Rate>
</br>
回调函数
<Rate
:onChange="onChange"
:onHoverChange="onHoverChange"></Rate>
<span v-if="hoverValue">{{hoverValue}}stars</span>
<span v-if="rValue">{{rValue}}stars</span>
<br/>
<Rate
:allowHalf="allowHalf"
:onHoverChange="onHoverChangeAH"></Rate>
<span v-if="hoverValueAH">{{hoverValueAH}}stars</span>
</br>
自定义
<Rate :value="initValue">
<template slot-scope="props">
<span>A</span>
</template>
</Rate>
</div>
</template>
<script>
import { Rate } from '../components/index'
export default {
data () {
return {
allowHalf: true,
initValue: 3,
disabled: true,
hoverValue: undefined,
rValue: undefined,
hoverValueAH: undefined,
}
},
methods: {
onHoverChange(val) {
this.hoverValue = val;
},
onChange(val) {
this.rValue = val;
},
onHoverChangeAH(val) {
this.hoverValueAH = val;
}
},
components: {
Rate
},
}
</script>