This commit is contained in:
2026-01-05 12:47:14 +08:00
commit 1fc846fae3
1614 changed files with 162035 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
.nut-rate {
display: inline-flex;
&-item {
position: relative;
display: flex;
flex-shrink: 0;
margin-right: 14px;
&:last-child {
margin-right: 0;
}
&__icon {
flex-shrink: 0;
color: $rate-icon-color;
cursor: pointer;
&--disabled {
color: $rate-icon-void-color;
}
&--full {
display: flex;
}
&--half {
position: absolute;
top: 0;
left: 0;
display: flex;
width: 50%;
overflow: hidden;
}
}
}
}

View File

@@ -0,0 +1 @@
export * from './rate'

View File

@@ -0,0 +1,56 @@
import type { ExtractPropTypes } from 'vue'
import { CHANGE_EVENT, UPDATE_MODEL_EVENT } from '../_constants'
import { commonProps, isNumber, makeNumericProp, makeStringProp, nullableBooleanProp } from '../_utils'
export const rateProps = {
...commonProps,
/**
* @description 当前 `star` 数,可使用 `v-model` 双向绑定数据
*/
modelValue: makeNumericProp(0),
/**
* @description `star` 总数
*/
count: makeNumericProp(5),
/**
* @description 图标选中颜色
*/
activeColor: String,
/**
* @description 图标未选中颜色
*/
voidColor: String,
/**
* @description 是否半星
*/
allowHalf: Boolean,
/**
* @description 是否只读
*/
readonly: Boolean,
/**
* @description 是否禁用
*/
disabled: nullableBooleanProp,
/**
* @description 间距
*/
spacing: [String, Number],
/**
* @description `Icon` 尺寸大小,如 `20px` `2em` `2rem`
*/
size: [String, Number],
/**
* @description 自定义 `Icon`
*/
customIcon: makeStringProp('star-fill-n'),
}
export type RateProps = ExtractPropTypes<typeof rateProps>
export const rateEmits = {
[UPDATE_MODEL_EVENT]: (val: number) => isNumber(val),
[CHANGE_EVENT]: (val: number) => isNumber(val),
}
export type RateEmits = typeof rateEmits

View File

@@ -0,0 +1,92 @@
<script setup lang="ts">
import { computed, defineComponent, ref, toRef } from 'vue'
import { CHANGE_EVENT, PREFIX, UPDATE_MODEL_EVENT } from '../_constants'
import { getMainClass, getRandomId, pxCheck } from '../_utils'
import { useFormDisabled } from '../form/form'
import NutIcon from '../icon/icon.vue'
import { rateEmits, rateProps } from './rate'
const props = defineProps(rateProps)
const emit = defineEmits(rateEmits)
const disabled = useFormDisabled(toRef(props, 'disabled'))
const refRandomId = getRandomId()
const rateRefs = ref<HTMLElement[]>([])
const classes = computed(() => {
return getMainClass(props, componentName)
})
function updateVal(value: number) {
emit(UPDATE_MODEL_EVENT, value)
emit(CHANGE_EVENT, value)
}
function onClick(e: number, index: number) {
if (disabled.value || props.readonly)
return
let value = 0
if (index === 1 && props.modelValue === index) {
//
}
else {
value = index
if (props.allowHalf && e === 2)
value -= 0.5
}
updateVal(value)
}
</script>
<script lang="ts">
const componentName = `${PREFIX}-rate`
export default defineComponent({
name: componentName,
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared',
},
})
</script>
<template>
<view :class="classes" :style="customStyle">
<view
v-for="n in Number(count)"
:id="`rateRefs-${refRandomId}${n}`"
:key="n"
ref="rateRefs"
class="nut-rate-item"
:style="n < Number(count) ? { marginRight: pxCheck(spacing!) } : {}"
>
<view class="nut-rate-item__icon--full" @click="onClick(1, n)">
<NutIcon
:size="props.size"
:custom-class="`nut-rate-item__icon ${disabled || n > +modelValue ? 'nut-rate-item__icon--disabled' : ''}`"
:name="customIcon"
:custom-color="n <= +modelValue ? activeColor : voidColor"
/>
</view>
<view v-if="allowHalf && Number(modelValue) + 1 > n" class="nut-rate-item__icon--half" @click="onClick(2, n)">
<NutIcon
:size="props.size"
custom-class="nut-rate-item__icon"
:name="customIcon"
:custom-color="n <= Number(modelValue) + 1 ? activeColor : voidColor"
@click="onClick(2, n)"
/>
</view>
<view v-else-if="allowHalf && Number(modelValue) + 1 < n" class="nut-rate-item__icon--half" @click="onClick(2, n)">
<NutIcon
:size="props.size"
:name="customIcon"
custom-class="nut-rate-item__icon nut-rate-item__icon--disabled"
:custom-color="voidColor"
/>
</view>
</view>
</view>
</template>
<style lang="scss">
@import './index';
</style>