feat(Rate): 当使用 readonly 和 allow-half 时支持 modelValue 设置小数 (#8528)

* feat(Rate): 当使用readonly和allow-half时支持modelValue设置小数

* docs(Rate): 完善文档

* docs(Rate): update snapshot

* docs(Rate): add description of decimal value

Co-authored-by: neverland <chenjiahan@buaa.edu.cn>
This commit is contained in:
nemo-shen 2021-04-18 20:36:48 +08:00 committed by GitHub
parent 8e4e6d54a6
commit 8fb3560f39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 190 additions and 18 deletions

View File

@ -88,6 +88,23 @@ export default {
<van-rate v-model="value" readonly /> <van-rate v-model="value" readonly />
``` ```
### Readonly Half Star
```html
<van-rate v-model="value" readonly />
```
```js
import { ref } from 'vue';
export default {
setup() {
const value = ref(3.3);
return { value };
},
};
```
### Change Event ### Change Event
```html ```html

View File

@ -88,6 +88,25 @@ export default {
<van-rate v-model="value" readonly /> <van-rate v-model="value" readonly />
``` ```
### 只读状态显示小数
设置 `readonly``allow-half` 属性后Rate 组件可以展示任意小数结果。
```html
<van-rate v-model="value" readonly allow-half />
```
```js
import { ref } from 'vue';
export default {
setup() {
const value = ref(3.3);
return { value };
},
};
```
### 监听 change 事件 ### 监听 change 事件
```html ```html

View File

@ -15,18 +15,34 @@ const [name, bem] = createNamespace('rate');
type RateStatus = 'full' | 'half' | 'void'; type RateStatus = 'full' | 'half' | 'void';
type RateListItem = {
status: RateStatus;
value: number;
};
function getRateStatus( function getRateStatus(
value: number, value: number,
index: number, index: number,
allowHalf: boolean allowHalf: boolean,
): RateStatus { readonly: boolean
): RateListItem {
if (value >= index) { if (value >= index) {
return 'full'; return { status: 'full', value: 1 };
} }
if (value + 0.5 >= index && allowHalf) {
return 'half'; if (value + 0.5 >= index && allowHalf && !readonly) {
return { status: 'half', value: 0.5 };
} }
return 'void';
if (value + 1 >= index && allowHalf && readonly) {
const cardinal = 10 ** 10;
return {
status: 'half',
value: Math.round((value - index + 1) * cardinal) / cardinal,
};
}
return { status: 'void', value: 0 };
} }
export default defineComponent({ export default defineComponent({
@ -72,10 +88,17 @@ export default defineComponent({
const untouchable = () => const untouchable = () =>
props.readonly || props.disabled || !props.touchable; props.readonly || props.disabled || !props.touchable;
const list = computed<RateStatus[]>(() => const list = computed<RateListItem[]>(() =>
Array(props.count) Array(props.count)
.fill('') .fill('')
.map((_, i) => getRateStatus(props.modelValue, i + 1, props.allowHalf)) .map((_, i) =>
getRateStatus(
props.modelValue,
i + 1,
props.allowHalf,
props.readonly
)
)
); );
const select = (index: number) => { const select = (index: number) => {
@ -130,7 +153,7 @@ export default defineComponent({
} }
}; };
const renderStar = (status: RateStatus, index: number) => { const renderStar = (item: RateListItem, index: number) => {
const { const {
icon, icon,
size, size,
@ -145,8 +168,8 @@ export default defineComponent({
disabledColor, disabledColor,
} = props; } = props;
const score = index + 1; const score = index + 1;
const isFull = status === 'full'; const isFull = item.status === 'full';
const isVoid = status === 'void'; const isVoid = item.status === 'void';
let style; let style;
if (gutter && score !== +count) { if (gutter && score !== +count) {
@ -181,6 +204,7 @@ export default defineComponent({
{allowHalf && ( {allowHalf && (
<Icon <Icon
size={size} size={size}
style={{ width: item.value + 'em' }}
name={isVoid ? voidIcon : icon} name={isVoid ? voidIcon : icon}
class={bem('icon', ['half', { disabled, full: !isVoid }])} class={bem('icon', ['half', { disabled, full: !isVoid }])}
color={disabled ? disabledColor : isVoid ? voidColor : color} color={disabled ? disabledColor : isVoid ? voidColor : color}

View File

@ -39,8 +39,12 @@
<van-rate v-model="value6" readonly /> <van-rate v-model="value6" readonly />
</demo-block> </demo-block>
<demo-block :title="t('readonlyHalfStar')">
<van-rate v-model="value7" readonly allow-half />
</demo-block>
<demo-block v-if="!isWeapp" :title="t('changeEvent')"> <demo-block v-if="!isWeapp" :title="t('changeEvent')">
<van-rate v-model="value7" @change="onChange" /> <van-rate v-model="value8" @change="onChange" />
</demo-block> </demo-block>
</template> </template>
@ -57,6 +61,7 @@ const i18n = {
customStyle: '自定义样式', customStyle: '自定义样式',
customCount: '自定义数量', customCount: '自定义数量',
readonly: '只读状态', readonly: '只读状态',
readonlyHalfStar: '只读状态小数显示',
changeEvent: '监听 change 事件', changeEvent: '监听 change 事件',
toastContent: (value: number) => `当前值:${value}`, toastContent: (value: number) => `当前值:${value}`,
}, },
@ -67,6 +72,7 @@ const i18n = {
customStyle: 'Custom Style', customStyle: 'Custom Style',
customCount: 'Custom Count', customCount: 'Custom Count',
readonly: 'Readonly', readonly: 'Readonly',
readonlyHalfStar: 'Readonly Half Star',
changeEvent: 'Change Event', changeEvent: 'Change Event',
toastContent: (value: number) => `current value${value}`, toastContent: (value: number) => `current value${value}`,
}, },
@ -82,7 +88,8 @@ export default {
value4: 2.5, value4: 2.5,
value5: 4, value5: 4,
value6: 3, value6: 3,
value7: 2, value7: 3.3,
value8: 2,
}); });
const onChange = (value: number) => Toast(t('toastContent', value)); const onChange = (value: number) => Toast(t('toastContent', value));

View File

@ -225,7 +225,7 @@ exports[`should render demo and match snapshot 1`] = `
> >
</i> </i>
<i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half van-rate__icon--full" <i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half van-rate__icon--full"
style="font-size: 25px;" style="font-size: 25px; width: 1em;"
data-score="0.5" data-score="0.5"
> >
</i> </i>
@ -243,7 +243,7 @@ exports[`should render demo and match snapshot 1`] = `
> >
</i> </i>
<i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half van-rate__icon--full" <i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half van-rate__icon--full"
style="font-size: 25px;" style="font-size: 25px; width: 1em;"
data-score="1.5" data-score="1.5"
> >
</i> </i>
@ -261,7 +261,7 @@ exports[`should render demo and match snapshot 1`] = `
> >
</i> </i>
<i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half van-rate__icon--full" <i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half van-rate__icon--full"
style="font-size: 25px;" style="font-size: 25px; width: 0.5em;"
data-score="2.5" data-score="2.5"
> >
</i> </i>
@ -279,7 +279,7 @@ exports[`should render demo and match snapshot 1`] = `
> >
</i> </i>
<i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half" <i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half"
style="color: rgb(238, 238, 238); font-size: 25px;" style="color: rgb(238, 238, 238); font-size: 25px; width: 0em;"
data-score="3.5" data-score="3.5"
> >
</i> </i>
@ -297,7 +297,7 @@ exports[`should render demo and match snapshot 1`] = `
> >
</i> </i>
<i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half" <i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half"
style="color: rgb(238, 238, 238); font-size: 25px;" style="color: rgb(238, 238, 238); font-size: 25px; width: 0em;"
data-score="4.5" data-score="4.5"
> >
</i> </i>
@ -517,6 +517,98 @@ exports[`should render demo and match snapshot 1`] = `
</div> </div>
</div> </div>
</div> </div>
<div>
<div role="radiogroup"
class="van-rate van-rate--readonly"
tabindex="0"
>
<div role="radio"
class="van-rate__item"
tabindex="0"
aria-setsize="5"
aria-posinset="1"
aria-checked="true"
>
<i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--full"
data-score="1"
>
</i>
<i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half van-rate__icon--full"
style="width: 1em;"
data-score="0.5"
>
</i>
</div>
<div role="radio"
class="van-rate__item"
tabindex="0"
aria-setsize="5"
aria-posinset="2"
aria-checked="true"
>
<i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--full"
data-score="2"
>
</i>
<i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half van-rate__icon--full"
style="width: 1em;"
data-score="1.5"
>
</i>
</div>
<div role="radio"
class="van-rate__item"
tabindex="0"
aria-setsize="5"
aria-posinset="3"
aria-checked="true"
>
<i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--full"
data-score="3"
>
</i>
<i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half van-rate__icon--full"
style="width: 1em;"
data-score="2.5"
>
</i>
</div>
<div role="radio"
class="van-rate__item"
tabindex="0"
aria-setsize="5"
aria-posinset="4"
aria-checked="true"
>
<i class="van-badge__wrapper van-icon van-icon-star-o van-rate__icon"
data-score="4"
>
</i>
<i class="van-badge__wrapper van-icon van-icon-star van-rate__icon van-rate__icon--half van-rate__icon--full"
style="width: 0.3em;"
data-score="3.5"
>
</i>
</div>
<div role="radio"
class="van-rate__item"
tabindex="0"
aria-setsize="5"
aria-posinset="5"
aria-checked="false"
>
<i class="van-badge__wrapper van-icon van-icon-star-o van-rate__icon"
data-score="5"
>
</i>
<i class="van-badge__wrapper van-icon van-icon-star-o van-rate__icon van-rate__icon--half"
style="width: 0em;"
data-score="4.5"
>
</i>
</div>
</div>
</div>
<div> <div>
<div role="radiogroup" <div role="radiogroup"
class="van-rate" class="van-rate"

View File

@ -127,3 +127,16 @@ test('should not emit change event when untouchable rate is touchmoved', () => {
triggerDrag(wrapper, 100, 0); triggerDrag(wrapper, 100, 0);
expect(wrapper.emitted('change')).toBeFalsy(); expect(wrapper.emitted('change')).toBeFalsy();
}); });
test('should get decimal when using allow-half and readonly prop', () => {
const wrapper = mount(Rate, {
props: {
allowHalf: true,
readonly: true,
modelValue: 3.3,
},
});
const item4 = wrapper.findAll('.van-rate__icon--half')[3];
expect(item4.style.width).toEqual('0.3em');
});