PC-vote/pages/details/[id].vue
2024-01-15 19:02:10 +08:00

556 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<Head>
<Title>{{ `${seo["title"] || "投票"} - 寄托天下出国留学网` }}</Title>
<Meta name="keyword" :content="seo['keyword']" />
<Meta name="description" :content="seo['description']" />
</Head>
<TopHead></TopHead>
<div class="content flexflex" :style="{ '--main-color': colourValue[uniqidIndex]['main'], '--bg-color': colourValue[uniqidIndex]['bg'], '--bc-color': colourValue[uniqidIndex]['bc'] }">
<div class="header flexacenter">
<span>{{ info.title }}</span>
<span class="views flexcenter">
<img class="eye-icon" src="@/assets/img/eye-icon.svg" />
{{ info.views }}
</span>
</div>
<div class="left">
<div class="info flexacenter">
<div class="info-left flexacenter">
<el-popover placement="bottom-start" :width="140" trigger="click" popper-class="avatar-box-popper" :show-arrow="false">
<template #reference>
<div class="flexcenter">
<img class="avatar" v-if="info.avatar" :src="info.avatar" />
<div class="username">{{ info.nickname }}</div>
</div>
</template>
<div class="avatar-box flexflex" v-if="info['uin']">
<a class="avatar-item flexcenter" target="_blank" @click.prevent="sendMessage(info['uin'])">
<img class="avatar-icon" src="@/assets/img/send-messages-icon.png" />
发送信息
</a>
<a class="avatar-item flexcenter" target="_blank" @click.prevent="TAHomePage(info['uin'])">
<img class="avatar-icon" src="@/assets/img/homepage-icon.png" />
TA的主页
</a>
</div>
</el-popover>
<div class="post-time">{{ handleDate(info.releasetime) }}发布</div>
</div>
<div class="info-right flexacenter" v-if="info['status'] == 1">
<div class="cut-off">{{ handleDeadline(info.deadline) }}结束</div>
<div class="state">进行中</div>
</div>
<div class="info-right flexacenter" v-else>
<div class="cut-off" v-if="info.deadline">已于{{ info.deadline }}结束</div>
<div class="state over">已结束</div>
</div>
</div>
<div class="message">{{ info.message }}</div>
<div class="hint">{{ info.status == 1 && isvote == 0 ? `已有 ${info.votes || ""} 人参与,` : `共有 ${info.votes || 0} 人参与` }} {{ `${isvote == 1 ? "你已投票" : info.status == 1 ? "参与投票即可查看实时结果" : ""}` }}</div>
<div class="option-list flexflex" v-if="info['status'] == 1 && isvote == 0">
<div class="option-item flexflex" v-for="(item, index) in option" :key="item.id" @click="handleVote(item.id, index)">
<div class="serial flexcenter">{{ index + 1 }}</div>
<span class="flex1">{{ item.value }} </span>
</div>
</div>
<div class="option-area" v-else>
<div class="option-item flexflex" :class="{ 'pitch': item.selected }" v-for="(item, index) in option" :key="item.id" @click="handleUnvoteVote(index, item.selected)">
<div class="flexflex" style="padding: 2px 0px;">
<div class="option-number flexcenter">{{ index + 1 }}</div>
<img class="tick-icon" src="@/assets/img/tick-black.svg" />
<div class="option-content flex1">{{ item.value }}</div>
</div>
<div class="option-progress flexacenter">
<div class="option-progress-step" :style="{ width: item.percentage + '%' }"></div>
<div class="option-progress-value">{{ item.count }}</div>
</div>
</div>
</div>
</div>
<div class="right"><DetailsComments :token="token"></DetailsComments></div>
</div>
<DetailsArea></DetailsArea>
<el-dialog class="options-popup" v-model="cancelPopoverState" width="488px" align-center>
<div class="options-popup-text">您要取消投票吗</div>
<div class="options-popup-btn flexflex">
<div class="options-popup-item options-no flexcenter" @click="unvoteVote">取消投票</div>
<div class="options-popup-item options-yes flexcenter" @click="cancelPopoverState = false">不取消</div>
</div>
</el-dialog>
</template>
<script setup>
useHead({ script: [{ src: "https://app.gter.net/bottom?tpl=header&menukey=mj" }, { src: "https://app.gter.net/bottom?tpl=footer", body: true }] })
import { useRoute, useRouter } from "vue-router"
import { ElMessage } from "element-plus"
const route = useRoute()
const router = useRouter()
let isNeedLogin = inject("isNeedLogin")
const goLogin = inject("goLogin")
let id = route.params.id
const uniqidEnd = id.charAt(id.length - 1)
const uniqidIndex = base62ToDecimal(uniqidEnd) % 6
onMounted(() => {
getDetails()
clearBottom()
})
let info = ref({})
let qrcode = ref("") // 分享二维码
let iscollection = ref(0) // 是否收藏
let islike = ref(0) // 是否点赞
let ismyself = ref(0) // 是否是作者
let detailsLoading = ref(false) // 详情加载中
let isvote = ref(0) // 是否已经投票
let option = ref([])
let token = ref("")
let cancelPopoverState = ref(false) // 取消投票弹窗
provide("info", info)
provide("islike", islike)
provide("iscollection", iscollection)
provide("token", token)
provide("qrcode", qrcode)
const getDetails = () => {
detailsHttp({ uniqid: id }).then(res => {
if (res.code != 200) {
ElMessage.error(res.message)
router.push("/index.html")
return
}
let data = res.data
info.value = data["info"]
isvote.value = data["isvote"]
iscollection.value = data["iscollection"]
islike.value = data["islike"]
ismyself.value = data["ismyself"]
option.value = data["option"]
qrcode.value = data.share?.qrcode
token.value = data["token"]
seo.value = data.seo
})
}
provide("getDetails", getDetails)
// 点击发送信息
const sendMessage = uin => {
redirectToExternalWebsite(`https://bbs.gter.net/home.php?mod=space&showmsg=1&uid=${uin}`)
}
// 点击ta的主页
const TAHomePage = uin => {
redirectToExternalWebsite(`https://bbs.gter.net/home.php?mod=space&uid=${uin}`)
}
// 跳转 url
const redirectToExternalWebsite = url => {
const link = document.createElement("a")
link.href = url
link.target = "_blank"
link.click()
}
provide("sendMessage", sendMessage)
provide("TAHomePage", TAHomePage)
// 处理点进投票
const handleVote = (token, index) => {
operationCollectHttp({ token }).then(res => {
if (res.code != 200) {
ElMessage.error(res.message)
return
}
let data = res.data
let optionList = data["optionList"] || []
optionList[index]["selected"] = 1
option.value = optionList
isvote.value = 1
info.value.votes = data["votes"]
ElMessage.success(res.message)
})
}
let unvoteVoteIndex = null // 选项下标
// 点击 取消投票
const handleUnvoteVote = (index, selected) => {
if (selected == 0) return
cancelPopoverState.value = true
unvoteVoteIndex = index
}
const unvoteVote = () => {
const token = option.value[unvoteVoteIndex].id
unvoteCollectHttp({ token }).then(res => {
if (res.code != 200) {
ElMessage.error(res.message)
return
}
let data = res.data
let optionList = data["optionList"] || []
optionList[unvoteVoteIndex]["selected"] = 0
option.value = optionList
isvote.value = 0
info.value.votes = data["votes"]
cancelPopoverState.value = false
})
}
const clearAllData = () => {
info.value = {}
qrcode.value = ""
iscollection.value = 0
islike.value = 0
ismyself.value = 0
isvote.value = 0
option.value = []
}
provide("clearAllData", clearAllData)
// 取消了同页面的收藏
const unbookmarkSamePage = () => {
iscollection.value = 0
info.value.favs--
}
provide("unbookmarkSamePage", unbookmarkSamePage)
// 删除同页面的投票需要跳转到 首页
const unbookmark = () => router.push("/index.html")
provide("unbookmark", unbookmark)
let seo = ref({})
// 清除底部的次数
let clearBottomCount = 0
// 清除 底部
const clearBottom = () => {
const indexFooter = document.querySelector("section.index-footer")
if (!indexFooter) {
clearBottomCount++
setTimeout(() => clearBottom(), 200)
return
}
if (clearBottomCount == 5) return
indexFooter.style.display = "none"
}
</script>
<style scoped lang="less">
.content {
width: 1200px;
// height: 500px;
margin: 0 auto;
border-radius: 16px;
background: #fff;
flex-wrap: wrap;
// min-height: 100vh;
--main-color: rgba(44, 186, 230, 1);
--bg-color: rgba(234, 245, 248, 1);
--bc-color: rgba(213, 235, 242, 1);
.header {
width: 100%;
height: 80px;
padding: 0 30px;
border-bottom: 1px solid #ebebeb;
font-weight: 650;
font-size: 20px;
color: #000000;
line-height: 20px;
justify-content: space-between;
.views {
font-size: 12px;
color: #aaa;
font-weight: 400;
.eye-icon {
margin-right: 5px;
}
}
}
.left {
width: 658px;
// height: 500px;
padding: 30px 42px 100px 30px;
border-right: 16px solid #f6f6f6;
.info {
font-size: 13px;
justify-content: space-between;
margin-bottom: 24px;
.info-left {
.avatar {
width: 24px;
height: 24px;
margin-right: 10px;
cursor: pointer;
border-radius: 50%;
}
.username {
color: #333;
margin-right: 10px;
cursor: pointer;
}
.post-time {
line-height: 22px;
color: #aaa;
}
}
.info-right {
.cut-off {
color: #aaa;
}
.state {
height: 20px;
line-height: 20px;
padding: 0 7px;
color: #fff;
background: var(--main-color);
border-radius: 25px;
font-size: 12px;
margin-left: 10px;
&.over {
background: rgba(51, 51, 51, 1);
}
}
}
}
.message {
font-size: 14px;
line-height: 24px;
color: #333;
margin-bottom: 30px;
word-wrap: break-word;
}
.hint {
font-size: 13px;
line-height: 22px;
color: #aaaaaa;
margin-bottom: 16px;
}
.tick-icon {
width: 14px;
height: 14px;
margin-top: 3px;
margin-right: 6px;
}
.option-list {
flex-direction: column;
.option-item {
width: 570px;
// height: 40px;
// background-color: var(--bg-color);
border: 1px solid var(--bc-color);
border-radius: 10px;
word-break: break-all;
font-size: 14px;
line-height: 20px;
color: #333333;
padding: 9px 15px;
cursor: pointer;
position: relative;
overflow: hidden;
z-index: 1;
&::after {
background-color: var(--bg-color);
content: "";
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: -1;
// border: 1px solid var(--bc-color);
}
&:hover::after {
background-color: var(--main-color);
opacity: 0.156862745098039;
}
&:not(:last-of-type) {
margin-bottom: 10px;
}
&.pitch {
.option-number {
display: none;
}
.tick-icon {
display: block;
}
.option-content {
color: #000000;
font-weight: 650;
}
}
.serial {
width: 14px;
height: 14px;
border-radius: 50%;
background: var(--main-color);
font-size: 11px;
color: #ffffff;
margin-top: 3px;
margin-right: 6px;
}
}
}
.option-area {
width: 570px;
// height: 318px;
background-color: var(--bg-color);
border: 1px solid var(--bc-color);
border-radius: 10px;
padding: 8px 0;
.option-item {
padding: 7px 15px 10px;
flex-direction: column;
word-break: break-all;
cursor: no-drop;
&:not(:last-of-type) {
border-bottom: 1px solid var(--bc-color);
}
&.pitch {
cursor: pointer;
.option-number {
display: none;
}
.tick-icon {
display: block;
}
.option-content {
font-weight: 650;
color: #000000;
}
}
.option-number {
font-size: 11px;
color: #ffffff;
width: 14px;
height: 14px;
background-color: var(--main-color);
border-radius: 50%;
margin-right: 6px;
margin-top: 3px;
}
.tick-icon {
display: none;
}
.option-content {
font-size: 14px;
color: #333;
line-height: 20px;
word-break: break-word;
}
.option-progress {
height: 5px;
width: 100%;
justify-content: flex-end;
// display: none;
margin-top: 3px;
.option-progress-step {
width: 24%;
background-color: var(--main-color);
opacity: 0.49803922;
height: 4px;
border-radius: 66px;
margin-right: 14px;
}
.option-progress-value {
font-size: 12px;
color: var(--main-color);
line-height: 20px;
}
}
}
}
}
.right {
flex: 1;
padding-top: 22px;
padding-left: 42px;
}
}
</style>
<style lang="less">
.options-popup {
border-radius: 10px;
.el-dialog__header {
padding: 0;
.el-dialog__headerbtn {
width: 36px;
height: 36px;
}
}
padding: 44px 74px;
.el-dialog__body {
padding: 0;
}
.options-popup-text {
font-size: 14px;
color: #333333;
text-align: center;
margin-bottom: 71px;
}
.options-popup-btn {
justify-content: space-between;
.options-popup-item {
font-size: 13px;
width: 160px;
height: 40px;
border-radius: 150px;
border: 1px solid;
cursor: pointer;
&.options-yes {
background-color: rgba(114, 219, 134, 1);
border-color: rgba(114, 219, 134, 1);
color: #fff;
}
&.options-no {
background-color: #fff;
border-color: rgba(170, 170, 170, 1);
color: #333;
}
}
}
}
</style>