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
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> 创建</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>
|