You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

419 lines
11 KiB

<template>
<div class="articleandvote-section">
<!-- 文章组件 -->
<div class="article-section">
<div>
<h2 class="article-title"> {{ ActiveAndVote.articleTitle }} </h2>
<div class="article-content">
<p>{{ ActiveAndVote.articleContent }}</p>
</div>
</div>
</div>
<!-- 投票组件 -->
<div class="voting-title">{{ ActiveAndVote.voteTitle }}</div>
<div v-for="(option,index) in optionLists" :key="option.id" class="progressContainer">
<el-progress
v-if="!isResultShown"
class="centered-progress"
:percentage="100"
:stroke-width="40"
:format="() => option.optionContent"
:text-inside="true"
:class="{ active: selectedOptions.includes(option.id) }"
@click="selectOption(option.id,index)"
>
</el-progress>
<div v-if="isResultShown" class="progressWrapper">
<el-progress
class="left-progress"
:percentage="getPercentage(option.count + 25)"
:stroke-width="40"
:format="() => option.optionContent"
:text-inside="true"
/>
<span class="count-right">{{ option.count + 25 }}</span>
</div>
</div>
<button
class="confirm-button"
:class="{ 'disabled-style': ActiveAndVote.votePollStatus === 2 }"
:disabled="ActiveAndVote.votePollStatus === 2 || selectedOptions.length === 0"
@click="showConfirmModal">
确认投票
</button>
<!-- 提示框 -->
<div v-if="showModal" class="modal-overlay">
<div class="modal-content">
<p>今日投票次数剩余:{{ userVoteIndexNow }} 是否继续投票?</p>
<div class="modal-buttons">
<button class="modal-button yes" @click="submitVote">是</button>
<button class="modal-button no" @click="closeModal">否</button>
</div>
</div>
</div>
<!-- 底部组件 -->
<div class="voting-info">
<div style="display: flex; align-items: center;">
<p style="margin-right: 350px;">当前共有{{ totalVotes }}人参与</p>
<a href="#" class="homily-link">{{ ActiveAndVote.username }}</a><a>&nbsp;创建</a>
</div>
<p>此投票于{{ ActiveAndVote.deadlineTime }}结束</p>
</div>
</div>
</template>
<script setup>
import { ref, reactive, computed } from 'vue'
import {apiGetVote,apiAddVoteRecord,apiGetVoteIndex} from '@/apis/vote.js'
import { ElMessage } from 'element-plus';
// 进度条是否显示
const isResultShown = ref(false)
// 添加投票记录
const record = reactive({
userId: 0,
articleId: 0,
voteId: 0,
OptionId: [],
voteIndex: 1
})
// 初始投票数
const totalVotes = ref(0);
const selectNum = ref(0);
// 文章与投票活动信息
const ActiveAndVote = reactive({
voteId: 0,
optionList:[],
participationNumber:0
});
// 投票选项信息
const optionLists = reactive([{
id:0,
optionContent:"",
count:0
}])
// 获取文章及其投票活动
record.articleId = 12 // 模拟数据,此处为文章id
const getVote = async () => {
try {
const res = await apiGetVote(record.articleId)
if (res.data.code === 0) {
Object.assign(ActiveAndVote, res.data.data) // 赋值
optionLists.splice(0) // 清空旧数据
optionLists.push(...ActiveAndVote.optionList) // 批量添加新数据
record.voteId = ActiveAndVote.voteId // 赋值
selectNum.value = ActiveAndVote.optionList.length
totalVotes.value = selectNum.value * 25
totalVotes.value += ActiveAndVote.participationNumber; // 赋值
console.log('投票数据加载成功:', ActiveAndVote)
await GetIndex() // 调用函数
} else {
console.error('请求失败:', res.message)
}
} catch (err) {
console.error('网络错误:', err)
}
}
getVote()
// 获取当前用户今天参与本活动的次数
const userVoteIndexNow = ref(3) // 用户今日本活动剩余投票数
record.userId = 1 // 模拟数据,此处为用户id
const GetIndex = async() => {
const res = await apiGetVoteIndex(record.userId,record.voteId)
userVoteIndexNow.value = res.data.data.voteIndex
console.log('用户今日本活动剩余投票数:', userVoteIndexNow.value)
}
// 选择按钮点击事件,将选项的id存到数组
const selectedOptions = ref([]);// 数组,用于存储多个选中的选项ID
const selectOption = (optionId,index) => {
const idx = selectedOptions.value.indexOf(optionId);
// 单选逻辑:只允许选一个
if (ActiveAndVote.multiOption == 0) {
// 先清除之前所有已选中项的 √
selectedOptions.value.forEach(id => {
const prevIndex = optionLists.findIndex(opt => opt.id === id);
if (prevIndex > -1) {
optionLists[prevIndex].optionContent = optionLists[prevIndex].optionContent.replace(/ √$/, '');
}
});
// 将选项id加到存储数组
selectedOptions.value = [optionId];
console.log(selectedOptions.value)
// 给新选中的项添加 √
optionLists[index].optionContent += ' √';
return;
}
// 多选逻辑:最多选十个
if (idx > -1) {
// 已存在则移除
selectedOptions.value.splice(idx, 1);
console.log(selectedOptions.value)
// 移除 √
optionLists[index].optionContent = optionLists[index].optionContent.replace(/ √$/, '');
} else {
// 不存在则添加
if (selectedOptions.value.length >= 10) {
alert('最多只能选择 10 个选项');
return;
}
selectedOptions.value.push(optionId);
console.log(selectedOptions.value)
// 添加 √
optionLists[index].optionContent += ' √';
}
};
// 提示框
// 控制模态框显示
const showModal = ref(false)
// 显示确认模态框
const showConfirmModal = () => {
showModal.value = true;
};
// 关闭模态框
const closeModal = () => {
showModal.value = false;
isResultShown.value = true; // 进度条显示
};
// 进度条
const totalcounts = computed(() => {
const baseCount = ActiveAndVote.optionList.reduce((sum, option) => sum + option.count, 0); //基础票数
const extra = selectNum.value * 25; // 假数据
return baseCount + extra;
});
const getPercentage = (count) => {
if (totalVotes.value === 0) return 0;
const percentage = Math.min(100, Number(((count / totalcounts.value) * 100).toFixed(2)));
return percentage
};
// 提交投票
const submitVote = async() => {
console.log('剩余投票次数',userVoteIndexNow.value,'选择个数',selectedOptions.value.length)
if (userVoteIndexNow.value > 0 && selectedOptions.value.length > 0) {
record.OptionId = computed(() => selectedOptions.value);
record.voteIndex = 4 - userVoteIndexNow.value
// 这里可以发送请求提交投票
console.log('添加投票信息',record.userId,record.articleId,record.voteId,record.OptionId,record.voteIndex)
await apiAddVoteRecord(record)
ElMessage.success('添加成功')
selectedOptions.value = []; // 清空已选选项
showModal.value = false // 关闭模态框
if(userVoteIndexNow.value == 1){
isResultShown.value = true; // 进度条显示
}
await getVote() // 调用函数
} else {
ElMessage.error('没有剩余投票次数或未选择任何选项')
}
};
</script>
<style scoped>
.articleandvote-section {
width: 40%; /* 占屏幕的 */
max-width: 800px; /* 最大宽度限制 */
margin-left: 18%; /* 距离左边屏幕四分之一 */
background-color: #fff;
border: 1px solid #eee;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
/* 文章区域样式 */
.article-section {
margin-bottom: 20px;
}
.article-title {
font-size: 20px;
font-weight: bold;
margin-bottom: 10px;
color: #333;
}
.article-content {
font-size: 14px;
color: #555;
line-height: 1.5;
}
/* 投票区域样式 */
.voting-title {
font-size: 30px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
text-align: center;
}
.progressContainer {
width: 400px;
margin: 0 auto 20px; /* 让每个进度条容器居中,并有底部间距 */
}
.centered-progress {
cursor: pointer;
font-size: 18px !important; /* 控制字体大小 */
}
/* 修改 Element Plus 进度条内层样式 */
.centered-progress :deep(.el-progress-bar__inner) {
display: flex !important;
align-items: center !important;
justify-content: center !important;
font-size: 18px !important;
color: #000 !important;
}
.centered-progress :deep(.el-progress-bar__innerText) {
margin: 0 !important;
width: 100% !important;
text-align: center !important;
font-size: 18px !important;
color: #000 !important;
}
/* 点击激活后的颜色 */
.centered-progress.active :deep(.el-progress-bar__inner) {
background-color: #FF9900 !important;
}
/* 包含进度条的相对定位容器 */
.progressWrapper {
width: 400px; /* 同进度条宽度 */
position: relative;
}
/* count 数字,绝对定位到右端,中间垂直对齐 */
.count-right {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
margin-right: 10px; /* 根据需要微调,让数字在灰条内稍留空隙 */
font-weight: normal;
z-index: 1;
}
/* 左对齐文本状态 */
.left-progress :deep(.el-progress-bar__inner) {
display: flex !important;
align-items: center !important;
justify-content: flex-start !important;
padding-left: 8px !important;
}
.left-progress :deep(.el-progress-bar__innerText) {
font-size: 15px !important;
color: #000 !important;
margin: 0 !important;
}
.confirm-button {
background-color: #FF0000; /* 确认按钮背景为红色 */
color: white;
border: none;
padding: 10px 30px;
border-radius: 25px;
cursor: pointer;
font-size: 15px;
width: 25%; /* 确认按钮宽度为选择按钮的四分之一 */
margin: 0 auto; /* 确认按钮居中 */
display: block;
transition: background-color 0.3s;
text-align: center; /* 文字居中 */
}
.confirm-button:hover {
background-color: #CC0000;
transform: translateY(-2px); /* 微微上移 */
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.confirm-button.disabled-style {
background-color: gray; /* 灰色背景 */
color: #ccc; /* 灰色文字 */
cursor: not-allowed; /* 鼠标变成禁止符号 */
}
/* 提示框样式 */
/* 提示框背景遮罩 */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
/* 提示框主体 */
.modal-content {
background-color: #87CEEB; /* 更明显的浅蓝色背景 */
padding: 30px 40px; /* 增加内边距 */
border-radius: 12px; /* 边角更圆润一些 */
width: 350px;
min-height: 180px;
text-align: center;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); /* 添加阴影效果 */
}
/* 提示文字 */
.modal-content p {
font-size: 25px; /* 文字更大 */
color: #333;
margin-bottom: 20px;
}
.modal-buttons {
display: flex;
justify-content: space-around;
margin-top: 10px;
}
.modal-button {
padding: 10px 20px;
border: none;
border-radius: 25px;
font-size: 14px;
cursor: pointer;
}
.modal-button.yes {
background-color: orange;
color: white;
}
.modal-button.no {
background-color: lightgray;
color: black;
}
/* 底部样式 */
.voting-info {
font-size: 13px;
color: #888;
line-height: 1.6;
}
.homily-link {
color: #4267B2;
text-decoration: none;
font-size: 13px;
}
</style>