feat: Rate 评分新增支持半选和触摸滑动选中 (#896)

 Closes: #669
This commit is contained in:
不如摸鱼去 2025-02-14 13:00:47 +08:00 committed by GitHub
parent 0e3195059e
commit 9d34f2e5d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 84 additions and 32 deletions

View File

@ -11,7 +11,6 @@
```
```typescript
const value = ref<number>(1)
function changeValue({ value }) {
@ -60,6 +59,14 @@ function changeValue({ value }) {
<wd-rate v-model="value" size="30px" space="10px"/>
```
## 允许半选
设置 `allowHalf` 属性。
```html
<wd-rate v-model="value" allow-half />
```
## Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
@ -75,6 +82,7 @@ function changeValue({ value }) {
| active-icon | 选中的图标类名 | string | - | wd-icon-star-on | - |
| disabled | 是否禁用 | boolean | - | false | - |
| disabled-color | 禁用的图标颜色 | string | - | linear-gradient(315deg, rgba(177,177,177,1) 0%,rgba(199,199,199,1) 100%) | - |
| allow-half | 是否允许半选 | boolean | - | false | $LOWEST_VERSION$ |
## Events

View File

@ -1,7 +1,7 @@
<!--
* @Author: weisheng
* @Date: 2023-07-29 17:03:39
* @LastEditTime: 2024-03-17 20:19:52
* @LastEditTime: 2025-02-13 23:36:41
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-design-uni/src/pages/rate/Index.vue
@ -41,6 +41,10 @@
<demo-block title="修改size、space">
<wd-rate v-model="value7" space="10px" size="30px" />
</demo-block>
<demo-block title="允许半选">
<wd-rate v-model="value8" allow-half />
</demo-block>
</page-wraper>
</template>
<script lang="ts" setup>
@ -53,6 +57,7 @@ const value4 = ref<number>(3)
const value5 = ref<number>(4)
const value6 = ref<number>(3)
const value7 = ref<number>(5)
const value8 = ref<number>(2.5)
function changeValue1({ value }: any) {
console.log(value)

View File

@ -9,16 +9,17 @@
@include e(item) {
position: relative;
display: inline-block;
touch-action: none; // 禁用默认触摸行为
}
@include e(item-star) {
display: inline-block;
vertical-align: top;
@include m(active) {
position: absolute;
left: 0;
top: 0;
overflow: hidden;
}
@include edeep(item-star) {
-webkit-background-clip: text !important;
color: transparent;
}
@include e(item-half) {
position: absolute;
left: 0;
top: 0;
overflow: hidden;
width: 50%;
}
}

View File

@ -85,5 +85,12 @@ export const rateProps = {
* 类型: string
* : 'linear-gradient(315deg, rgba(177,177,177,1) 0%,rgba(199,199,199,1) 100%)'
*/
disabledColor: makeStringProp('linear-gradient(315deg, rgba(177,177,177,1) 0%,rgba(199,199,199,1) 100%)')
disabledColor: makeStringProp('linear-gradient(315deg, rgba(177,177,177,1) 0%,rgba(199,199,199,1) 100%)'),
/**
*
* 类型: boolean
* 默认值: false
*/
allowHalf: makeBooleanProp(false)
}

View File

@ -1,18 +1,26 @@
<template>
<view :class="`wd-rate ${customClass}`" :style="customStyle">
<view :class="`wd-rate ${customClass}`" :style="customStyle" @touchmove="onTouchMove">
<view
v-for="(rate, index) in rateList"
:key="index"
:data-index="index"
:style="{ 'margin-right': index == rateList.length - 1 ? 0 : space }"
class="wd-rate__item"
@click="changeRate(index)"
>
<view class="wd-rate__item-star" :style="{ width: size, height: size }">
<wd-icon :name="icon" :size="size" :custom-style="iconStyle" />
</view>
<view class="wd-rate__item-star wd-rate__item-star--active" :style="{ width: rate, height: size }">
<wd-icon :name="activeIcon" :size="size" :custom-style="iconActiveStyle" />
<wd-icon
custom-class="wd-rate__item-star"
:name="isActive(rate) ? activeIcon : icon"
:size="size"
:custom-style="rate === '100%' ? iconActiveStyle : iconStyle"
@click="changeRate(index, false)"
/>
<view v-if="props.allowHalf" class="wd-rate__item-half" @click.stop="changeRate(index, true)">
<wd-icon
custom-class="wd-rate__item-star"
:name="isActive(rate) ? activeIcon : icon"
:size="size"
:custom-style="rate !== '0' ? iconActiveStyle : iconStyle"
/>
</view>
</view>
</view>
@ -30,8 +38,10 @@ export default {
</script>
<script lang="ts" setup>
import wdIcon from '../wd-icon/wd-icon.vue'
import { computed, ref, watch } from 'vue'
import { computed, getCurrentInstance, ref, watch } from 'vue'
import { rateProps } from './types'
import { getRect } from '../common/util'
const { proxy } = getCurrentInstance() as any
const props = defineProps(rateProps)
const emit = defineEmits(['update:modelValue', 'change'])
@ -40,11 +50,11 @@ const rateList = ref<Array<string>>([])
const activeValue = ref<string>('')
const iconStyle = computed(() => {
return `background:${props.color}; -webkit-background-clip: text; color: transparent`
return `background:${props.color};`
})
const iconActiveStyle = computed(() => {
return `background:${props.disabled ? props.disabledColor : activeValue.value}; -webkit-background-clip: text; color: transparent`
return `background:${props.disabled ? props.disabledColor : activeValue.value};`
})
watch(
@ -72,11 +82,16 @@ watch(
}
)
//
const isActive = (rate: string) => {
return rate !== '0'
}
/**
* @description 计算当前应当展示的rate数量
*/
function computeRateList() {
const { modelValue, num } = props
const { modelValue, num, allowHalf } = props
// valuenum
if (modelValue === null || !num) return
if (typeof modelValue !== 'number') {
@ -84,13 +99,12 @@ function computeRateList() {
return
}
const tempRateList: string[] = []
const fullLength = Math.ceil(modelValue) - 1
const fullLength = Math.floor(modelValue)
for (let i = 0; i < num; i++) {
if (i < fullLength) {
tempRateList.push('100%')
} else if (i === fullLength) {
const rate = modelValue - fullLength > 0.5 ? 1 : 0.5
tempRateList.push(rate * 100 + '%')
} else if (i === fullLength && allowHalf && modelValue % 1 !== 0) {
tempRateList.push('50%')
} else {
tempRateList.push('0')
}
@ -113,15 +127,32 @@ function computeActiveValue() {
}
/**
* @description 点击icon触发组件的change事件
* @param Event
*/
function changeRate(index: number) {
function changeRate(index: number, isHalf: boolean) {
if (props.readonly || props.disabled) return
emit('update:modelValue', index + 1)
const value = isHalf ? index + 0.5 : index + 1
emit('update:modelValue', value)
emit('change', {
value: index + 1
value
})
}
async function onTouchMove(event: TouchEvent) {
const { clientX } = event.touches[0]
const rateItems = await getRect('.wd-rate__item', true, proxy)
const targetIndex = Array.from(rateItems).findIndex((rect) => {
return clientX >= rect.left! && clientX <= rect.right!
})
if (targetIndex !== -1) {
const target = rateItems[targetIndex]
const itemWidth = target.width!
const isHalf = props.allowHalf && clientX - target.left! < itemWidth / 2
const value = isHalf ? targetIndex + 0.5 : targetIndex + 1
if (value >= 0.5) {
changeRate(targetIndex, isHalf)
}
}
}
</script>
<style lang="scss" scoped>
@import './index.scss';