《Vue.js中的评分系统:从静态展示到动态交互》

280 阅读3分钟

前言

在数字化产品和服务日益普及的今天,用户体验成为决定应用成败的关键因素之一。而评分系统作为衡量用户满意度和反馈的重要工具,其设计与实现也变得尤为重要。本篇文章将带领你一步步构建一个既美观又实用的评分组件,该组件不仅能够在页面上优雅地展示评分,还能根据用户的操作实时响应,提供个性化的视觉反馈。

正文

    对于评分这一个组件,首先让我们思考一下,实现这个组件我们需要实现什么功能。

image.png

  1. 页面可以显示我们要的效果
  2. 对于评分主题颜色定制
  3. 实现打分机制
  4. mouserover鼠标移入事件,mouserout鼠标移出事件,click点击事件

现在我们我们就对这四个功能一一实现

1.显示数据

    使用npm快速创建一个Vue项目,父组件App.vue,子组件Rate.vue,把子组件引入父组件中,作为一个标签使用,在父组件中负责将数据传给子组件,子组件拿到数据后,在页面上展示出父组件的要求效果。 App.vue

<template>
  <div>
    <!-- 不写冒号,则会表示字符3 -->
    <Rate 
      :score="3"
    />
  </div>
</template>

<script setup>
// 显示打分
import Rate from './components/Rate.vue'
</script>

<style lang="css" scoped>

</style>

Rate.vue

<template>
    <div>
        {{rate}}
    </div>
</template>

<script setup>
import {defineProps,computed,defineEmits,ref} from 'vue'
let props = defineProps({
    score:{
        type:Number,
        default:0
    }
})
let rate = computed(()=>"★★★★★☆☆☆☆☆".slice(5-props.score,10-props.score))
</script>

<style lang="css" scoped>
</style>

这里我们星星展示,是依靠JS中的slice()对字符串进行剪切会获取到我们需要的的长度。 这样就实现了基础的数据显示。

2.定制主题

    给星星添加主题颜色,也就是给它添加一个css样式就可以实现了,到那时主题的颜色是我们再父组件中传递的,所以我们再<Rate/>标签中添加主题theme就好了,在defineProps中添加

theme:{
        type:String,
        default:'blue'
    }

默认颜色设置为蓝色。 我们给div通过v-bind动态绑定变量style里面的数据是css属性样式

image.png

// 主题可定制性  配置项
const themeObj = {
    'black':'#000',
    'yellow':'#fadb14',
    'blue':'#40a9ff',
    'green':'73d13d'
}
const fontStyle = computed(()=>{
    return `color:${themeObj[props.theme]};`
})

然后再写一个颜色的配置型,这个配置项大家可以自定义颜色。

3.实现打分以及鼠标事件

    我们在页面上打分时,可以发现星星是可以点击的,但是我们这里的星星是字符串形式,无法点击。这里我们将两种不同的星星放在两个的span中让他们两个重叠在一起,这样我们就只显示5个星星了。 在父组件中我们需要实现数据的更新机制,这里我们可以采用事件订阅,将函数updata传给子组件 App.vue

<template>
  <div>
    <!-- 不写冒号,则会表示字符3 -->
    <Rate 
      @update-score="updata"
      :score="score" theme="yellow"
    />
  </div>
</template>

<script setup>
// 显示打分
import Rate from './components/Rate.vue'
import {ref} from 'vue'

let score = ref(3)

function updata(num){
  score.value = num
}
</script>

<style lang="css" scoped>

</style>

在子组件中,我们定义一个私有化数据width用于保存更新后的数据。在父组件中我们订阅事件update,在子组件中我们声明事件update-score,并创建函数onRate,用于更新数据

//事件
const emits = defineEmits(['update-score'])
// 更新数据
const onRate = (num)=>{
    emits('update-score',num)
}

这里的参数num会通过事件订阅机制将其传给父组件中的函数

function updata(num){
  score.value = num
}

这里的星星亮几颗,我们用一个相对单位em完成,我们给实心的星星动态绑定style,我们这里用width宽度来实现有几颗星,是因为我们空心的星星和实心的星星重叠了,只需要保证实心的星星按照打分实现就好了,不需要处理空心。

const fontWidth = computed(()=> `width:${width.value}em;`)

对于鼠标事件,我们需要能够点击星星,我们使用v-for将其遍历在span标签中,而不是使用字符串,这样我们就可以点击了。然后我们给span标签绑定事件mouseOver,mouseOut,以及点击事件,点击事件我们就绑定onRate,mouseOver当鼠标移入触时,星星要亮起来,就需要将数据传入更新,鼠标移出时,就恢复原来的状态。 Rate.vue

<template>
    <div :style="fontStyle">
        <div class="rate" @mouseout="mouseOut">
            <span class="rate star" @mouseover = "mouseOver(num)" v-for="num in 5" :key="num"></span>
            <span class="hollow" :style="fontWidth">
                <span v-for="num in 5" :key="num" class="star" @mouseover = "mouseOver(num)" @click="onRate(num)"></span>
            </span>
        </div>
    </div>
</template>

<script setup>
import {defineProps,computed,defineEmits,ref} from 'vue'
let props = defineProps({
    score:{
        type:Number,
        default:0
    },
    theme:{
        type:String,
        default:'blue'
    }
})
// 主题可定制性  配置项
const themeObj = {
    'black':'#000',
    'yellow':'#fadb14',
    'blue':'#40a9ff',
    'green':'73d13d'
}
let rate = computed(()=>"★★★★★☆☆☆☆☆".slice(5-props.score,10-props.score))
const fontStyle = computed(()=>{
    return `color:${themeObj[props.theme]};`
})
// 私有状态  mouseover mouseout 改变状态
let width = ref(props.score)

//事件
const emits = defineEmits(['update-score'])
// 更新数据
const onRate = (num)=>{
    emits('update-score',num)
}

const fontWidth = computed(()=> `width:${width.value}em;`)

const mouseOver = (num)=>{
    width.value = num
}
const mouseOut = ()=>{
    width.value = props.score
}
</script>

<style lang="css" scoped>
.star{
    letter-spacing: 2px;
}
.rate{
    position: relative;
    display: inline-block;
}
.rate > span.hollow{
    position: absolute;
    display: inline-block;
    top: 0;
    left: 0;
    overflow: hidden;
}
</style>

感谢大家阅读,若有不足,恳请各位指出!!!

image.png