效果图
Vue.js 音乐榜单投票系统:详细实现与代码解析
目录
- 引言
- 项目概述
- 步骤一:项目环境准备
- 步骤二:页面布局与样式
- 步骤三:实现艺人数据动态显示
- 步骤四:实现投票功能与次数限制
- 步骤五:显示投票占比与限制剩余投票次数
- 附加功能:投票按钮动画与提示
- 完整代码
- 总结
引言
在本教程中,我们将使用 Vue.js 构建一个音乐榜单投票页面。用户可以为艺人投票,且每个用户有 5 次投票的总次数限制。我们将逐步解析代码的每一部分,并最终构建一个带有动态投票排名、投票次数限制的页面。
项目概述
这个投票系统展示了多个艺人的排名。每个艺人都显示有票数、投票按钮和投票百分比。每个用户最多只能为每个艺人投一次票,且总共只能投票 5 次。
步骤一:项目环境准备
- 引入 Vue.js:
通过在线 CDN 的方式加载 Vue.js,无需复杂的安装过程。
<script src="https://cdn.staticfile.net/vue/2.7.0/vue.min.js"></script>
- 基础 HTML 结构:
在 HTML 中,创建一个div
,用来作为 Vue.js 管理的挂载点。
<div id="app"></div>
步骤二:页面布局与样式
我们先定义页面的基本布局和样式。布局将包括一个标题、艺人列表、投票按钮等。
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #000;
color: #fff;
text-align: center;
}
#app {
max-width: 900px;
margin: 0 auto;
padding: 20px;
}
h1 {
font-size: 24px;
margin-bottom: 30px;
}
.artist-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.artist {
background-color: #222;
padding: 20px;
border-radius: 10px;
text-align: center;
}
.avatar {
width: 100px;
height: 100px;
background-color: #666;
border-radius: 50%;
margin-bottom: 15px;
line-height: 100px;
color: white;
font-size: 20px;
}
.artist button {
background-color: #ffcc00;
color: black;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
transition: transform 0.2s ease;
}
.artist button:hover {
background-color: #ffaa00;
}
</style>
代码解释:
artist-list
:艺人列表的布局使用了 CSS 的grid
布局,方便控制每行艺人卡片的显示。.avatar
:为艺人的头像设置圆形背景色块,使用 CSS 的border-radius: 50%
实现。.artist button
:设置投票按钮的样式,并添加了一些 hover 效果和点击动画。
步骤三:实现艺人数据动态显示
接下来,我们在 Vue.js 的 data
中定义艺人的数据,并通过 v-for
指令动态渲染这些艺人。
new Vue({
el: '#app',
data: {
artists: [
{
name: '周深', votes: 4312456 },
{
name: '华晨宇', votes: 3303502 },
{
name: '薛之谦', votes: 457070 },
{
name: '毛不易', votes: 166685 },
{
name: '汪苏泷', votes: 85262 },
{
name: '许嵩', votes: 56455 }
]
}
});
在 HTML 中,使用 v-for
循环生成每个艺人的卡片。
<div class="artist-list">
<div v-for="(artist, index) in artists" :key="index" class="artist">
<div class="avatar">{
{ artist.name.charAt(0) }}</div>
<h2>{
{ artist.name }}</h2>
<p>{
{ artist.votes }} 票</p>
<button>投票</button>
</div>
</div>
代码解释:
v-for
:用于循环渲染艺人列表,artist.name.charAt(0)
获取艺人名字的首字母来代替头像。
步骤四:实现投票功能与次数限制
我们将为投票按钮添加点击事件,每个用户最多只能投 5 次票,并且每个艺人只能投一次票。
data: {
totalVotesLeft: 5,
artists: [
{
name: '周深', votes: 4312456, voted: false },
{
name: '华晨宇', votes: 3303502, voted: false },
{
name: '薛之谦', votes: 457070, voted: false },
// 更多艺人
]
},
methods: {
vote(index) {
if (this.artists[index].voted) {
alert(`你已经为 ${
this.artists[index].name} 投过票了!`);
} else if (this.totalVotesLeft > 0) {
this.artists[index].votes += 1;
this.artists[index].voted = true;
this.totalVotesLeft -= 1;
alert(`你为 ${
this.artists[index].name} 投了一票!`);
} else {
alert('你已没有剩余投票次数。');
}
}
}
步骤五:显示投票占比与限制剩余投票次数
为每个艺人的票数计算占比,并展示剩余的投票次数。
<p class="percentage">占比: {
{ ((artist.votes / totalVotes) * 100).toFixed(2) }}%</p>
<div class="limit-info">
剩余投票次数:{
{ totalVotesLeft }}/5
</div>
代码解释:
((artist.votes / totalVotes) * 100).toFixed(2)
:通过公式计算艺人票数占比,保留两位小数。totalVotesLeft
:显示剩余的总投票次数。
附加功能:投票按钮动画与提示
为按钮添加点击时的动画效果,提升用户体验。通过 transform
来实现按下按钮时的缩放效果。
.artist button:active {
transform: scale(0.95);
}
完整代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>音乐榜单投票 - Vue.js 实现</title>
<script src="https://cdn.staticfile.net/vue/2.7.0/vue.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #000;
color: #fff;
text-align: center;
}
#app {
max-width: 900px;
margin: 0 auto;
padding: 20px;
}
h1 {
font-size: 24px;
margin-bottom: 30px;
}
.artist-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.artist {
background-color: #222;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(255, 255, 255, 0.1);
text-align: center;
position: relative;
}
.artist .avatar {
width: 100px;
height: 100px;
background-color: #666;
border-radius: 50%;
margin-bottom: 15px;
display: inline-block;
line-height: 100px;
color: #fff;
font-size: 20px;
}
.artist h2 {
font-size: 20px;
margin: 10px 0;
}
.artist p {
font-size: 16px;
margin: 5px 0;
}
.artist button {
background-color: #ffcc00;
border: none;
color: black;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
transition: transform 0.2s ease;
}
.artist button:hover {
background-color: #ffaa00;
}
.artist button:active {
transform: scale(0.95);
}
.percentage {
margin-top: 10px;
font-size: 14px;
color: #ddd;
}
.limit-info {
margin-top: 20px;
font-size: 16px;
color: #ff6600;
}
</style>
</head>
<body>
<div id="app">
<h1>华语艺人 / 团体 投票榜单</h1>
<!-- 总票数展示 -->
<p>总票数:{
{ totalVotes }}</p>
<div class="artist-list">
<div v-for="(artist, index) in sortedArtists" :key="index" class="artist">
<div class="avatar">{
{ artist.name.charAt(0) }}</div>
<h2>{
{ index + 1 }}. {
{ artist.name }}</h2>
<p>{
{ artist.votes }} 票</p>
<p class="percentage">占比: {
{ ((artist.votes / totalVotes) * 100).toFixed(2) }}%</p>
<button @click="vote(index)">
投票
</button>
</div>
</div>
<!-- 投票限制信息 -->
<div class="limit-info">
剩余投票次数:{
{ totalVotesLeft }}/5
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
totalVotes: 0, // 总票数
totalVotesLeft: 5, // 剩余投票次数限制
artists: [
{
name: '周深', votes: 4312456, voted: false },
{
name: '华晨宇', votes: 3303502, voted: false },
{
name: '薛之谦', votes: 457070, voted: false },
{
name: '毛不易', votes: 166685, voted: false },
{
name: '汪苏泷', votes: 85262, voted: false },
{
name: '许嵩', votes: 56455, voted: false },
{
name: '张杰', votes: 32887, voted: false },
{
name: '陈奕迅', votes: 31789, voted: false },
{
name: '林俊杰', votes: 30176, voted: false },
{
name: '田馥甄', votes: 30176, voted: false },
{
name: 'NINEONE#', votes: 30176, voted: false },
{
name: 'G.E.M.邓紫棋', votes: 30176, voted: false }
]
},
computed: {
// 计算已排序的艺人列表
sortedArtists() {
return this.artists.slice().sort((a, b) => b.votes - a.votes);
}
},
methods: {
vote(index) {
if (this.artists[index].voted) {
alert(`你已经为 ${
this.artists[index].name} 投过票了!`);
} else if (this.totalVotesLeft > 0) {
this.artists[index].votes += 1;
this.artists[index].voted = true;
this.totalVotes += 1;
this.totalVotesLeft -= 1;
alert(`你为 ${
this.artists[index].name} 投了一票!`);
} else {
alert('你已没有剩余投票次数。');
}
}
},
mounted() {
this.totalVotes = this.artists.reduce((sum, artist) => sum + artist.votes, 0);
}
});
</script>
</body>
</html>
总结
在本教程中,我们使用 Vue.js 构建了一个音乐榜单投票系统,涵盖了页面布局、投票逻辑、投票次数限制等功能,并为投票按钮添加了简单的交互动画。这个项目是前端开发的一个实用练习,适合初学者熟悉 Vue.js 的基本操作。
希望这篇详细的教程能够帮助你顺利完成投票系统的开发。如果你对前端开发感兴趣,请记得订阅我的博客,获取更多实用项目的源码与教程!