2024-08-12 11:03:32 +08:00

399 lines
13 KiB
Vue

<template>
<Head>
<Title>投票 - 寄托天下出国留学网</Title>
</Head>
<TopHead></TopHead>
<div class="search-info flexacenter" v-if="keyword">
<div class="flexacenter" @click="closeKeyword">
{{ keyword }}
<img class="round-fork-fork" src="@/assets/img/round-fork-fork.png" />
</div>
<div class="halving-line"></div>
<div class="search-result">共 {{ count }} 条搜索数据</div>
</div>
<div class="vote-list-box" :class="{ 'firstdata': firstdataState }" ref="gridContainer" v-loading="loading">
<a class="vote-item" target="_blank" :href="`/details/${item['uniqid']}?colorI=${index % 6}`" v-for="(item, index) in list" :key="index" :class="{ 'isvote': item['isvote'] == 1 || item['status'] == 0 }" :style="{ '--main-color': colourValue[index % 6]['main'], '--bg-color': colourValue[index % 6]['bg'], '--bc-color': colourValue[index % 6]['bc'] }">
<div class="vote-title">
<div class="vote-state" v-if="item['status'] == 1">进行中</div>
<div class="vote-state finish" v-else>已结束</div>
{{ item["title"] }}
</div>
<div class="vote-explain">{{ item["message"] }}</div>
<div class="vote-option-list flexflex">
<div class="vote-option-item flexflex" :class="{ 'pitch': item.selected == 1 }" v-for="(item, index) in item?.option" :key="index">
<div class="flexflex" style="padding: 2px 0;">
<div class="vote-option-number flexcenter">{{ index + 1 }}</div>
<img class="tick-icon" src="@/assets/img/tick-black.svg" />
<div class="vote-option-content flex1">{{ item["value"] }}</div>
</div>
<div class="vote-option-progress flexacenter">
<div class="vote-option-progress-step" :style="{ width: item['percentage'] + '%' }"></div>
<div class="vote-option-progress-value">{{ item["count"] }}</div>
</div>
</div>
</div>
<div class="vote-data flexacenter">
<div class="vote-data-left flexacenter">
{{ item.votes }}人参与 <template v-if="item['deadline']">| {{ handleDeadline(item["deadline"]) }}结束</template>
</div>
<div class="vote-data-right flexacenter">
<div class="vote-data-item flexacenter"><img class="vote-data-icon" src="@/assets/img/eye-icon.svg" />&nbsp; {{ item.views }}</div>
<div class="vote-data-item flexacenter" @click.stop.prevent="handleLike(item['token'], index)">
<!-- <img v-if="item['islike'] == 0" class="vote-data-icon" src="@/assets/img/expression-icon.svg" />
<img v-else class="vote-data-icon" src="@/assets/img/like-yes.png" />&nbsp; {{ item["likes"] }} -->
<img class="vote-data-icon" src="@/assets/img/expression-icon.png" />&nbsp; {{ item["ripostes"] }}
</div>
<div class="vote-data-item flexacenter"><img class="vote-data-icon" src="@/assets/img/comment-icon.svg" />&nbsp; {{ item["comments"] }}</div>
</div>
</div>
</a>
<div class="empty-box flexcenter" v-if="keyword && list.length == 0 && !loading"><Empty :isNeedIssue="true"></Empty></div>
</div>
</template>
<script setup>
useHead({ script: [{ src: "https://app.gter.net/bottom?tpl=header&menukey=vote" }, { src: "https://app.gter.net/bottom?tpl=footer,popupnotification", body: true }] })
let isNeedLogin = inject("isNeedLogin")
const goLogin = inject("goLogin")
import { useRoute } from "vue-router"
const route = useRoute()
const router = useRouter()
let keyword = ref("")
let page = ref(1) // 投票页数从 1 开始
let count = ref(0)
let list = ref([])
let loading = ref(false) // 加载中
const firstdataState = ref(true)
keyword.value = route.query["keyword"]
const gridContainer = ref(null)
let masonryInstance = null
onMounted(async () => {
let Masonry = await import("masonry-layout")
masonryInstance = new Masonry.default(gridContainer.value, {
itemSelector: ".vote-item",
gutter: 22.5,
})
getList()
window.addEventListener("scroll", handleScroll)
})
const getList = () => {
if (page.value == 0 || loading.value) return
loading.value = true
getListHttp({ page: page.value, keyword: keyword.value, limit: 20 })
.then(res => {
if (res.code != 200) {
page.value = 0
ElMessage.error(res.message)
return
}
let data = res.data
list.value = list.value.concat(data.data)
count.value = data.count
if (data.count > list.value.length) page.value++
else page.value = 0
firstdataState.value = false
// data.data.forEach((element, index) => {
// // let uniqidEnd = element["uniqid"].charAt(element["uniqid"].length - 1)
// // element["uniqidIndex"] = base62ToDecimal(uniqidEnd) % 6
// element["uniqidIndex"] = index % 6
// })
nextTick(() => {
masonryInstance.reloadItems()
masonryInstance.layout()
})
})
.finally(() => (loading.value = false))
}
const handleScroll = () => {
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
const scrollHeight = document.documentElement.scrollHeight
const clientHeight = document.documentElement.clientHeight
// 列表下 滑动到底部 获取新数据
if (scrollTop + clientHeight >= scrollHeight - 40) getList()
}
// 处理点赞
const handleLike = (token, index) => {
if (isNeedLogin.value) {
goLogin()
return
}
operateLikeHttp({ token }).then(res => {
if (res.code != 200) {
ElMessage.error(res.message)
return
}
let data = res.data
list.value[index].likes = data["count"]
list.value[index].islike = data["status"]
ElMessage.success(res.message)
})
}
// 使用 process.client 判断是否在浏览器环境下
const isServer = computed(() => {
return process.server // 使用 process.client 判断是否在浏览器环境下
})
// 点击关闭搜索
const closeKeyword = () => router.push("./index.html")
watch(
() => route.query,
() => {
keyword.value = route.query["keyword"]
page.value = 1
list.value = []
count.value = 0
getList()
}
)
try {
if (process.server) {
await getListHttp({ page: 1, keyword: keyword.value }).then(res => {
let data = res.data
// data.data.forEach((element, index) => {
// let uniqidEnd = element["uniqid"].charAt(element["uniqid"].length - 1)
// element["uniqidIndex"] = base62ToDecimal(uniqidEnd) % 6
// element["uniqidIndex"] = index % 6
// })
list.value = list.value.concat(data.data)
count.value = data.count
})
}
} catch (error) {}
</script>
<style lang="less" scoped>
.vote-item {
--main-color: rgba(44, 186, 230, 1);
--bg-color: rgba(234, 245, 248, 1);
--bc-color: rgba(213, 235, 242, 1);
}
.search-info {
font-size: 14px;
color: #72db86;
width: 1200px;
margin: 0 auto 31px;
.round-fork-fork {
width: 14px;
height: 14px;
margin-left: 8px;
cursor: pointer;
}
.halving-line {
width: 1px;
height: 13px;
background-color: #d7d7d7;
margin: 0 20px;
}
.search-result {
font-size: 13px;
color: #7f7f7f;
}
}
.vote-list-box {
width: 1200px;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
min-height: 100vh;
&.firstdata {
.vote-item {
margin-right: 22.5px;
&:nth-of-type(3n) {
margin-right: 0;
}
}
}
.vote-item {
width: 385px;
background-color: rgba(255, 255, 255, 1);
border-radius: 16px;
padding: 25px 22px 24px;
margin-bottom: 20px;
cursor: pointer;
&:hover {
-moz-box-shadow: 0px 0px 5px 2px rgba(216, 216, 216, 0.48);
-webkit-box-shadow: 0px 0px 5px 2px rgba(216, 216, 216, 0.48);
box-shadow: 0px 0px 5px 2px rgba(216, 216, 216, 0.48);
}
&.isvote {
.vote-option-list {
.vote-option-item {
.vote-option-progress {
display: flex;
}
}
}
}
.vote-title {
.vote-state {
background-color: var(--main-color);
border-radius: 25px;
font-size: 12px;
margin-right: 6px;
height: 20px;
color: #ffffff;
padding: 0 6px;
display: inline-flex;
justify-content: center;
align-items: center;
&.finish {
color: #ffffff;
background: #000;
}
}
font-weight: 650;
font-style: normal;
font-size: 16px;
color: #000000;
line-height: 26px;
margin-bottom: 10px;
word-break: break-all;
}
.vote-explain {
color: #555555;
line-height: 22px;
font-size: 13px;
word-break: break-word;
margin-bottom: 14px;
}
.vote-option-list {
width: 340px;
background-color: var(--bg-color);
border: 1px solid var(--bc-color);
border-radius: 13px;
flex-direction: column;
padding: 8px 0;
margin-bottom: 16px;
.vote-option-item {
padding: 10px 15px;
flex-direction: column;
&:not(:last-of-type) {
border-bottom: 1px solid var(--bc-color);
}
&.pitch {
.vote-option-number {
display: none;
}
.tick-icon {
display: block;
}
.vote-option-content {
color: #000;
font-weight: 650;
}
}
.vote-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 {
width: 14px;
height: 14px;
margin-top: 3px;
margin-right: 6px;
display: none;
}
.vote-option-content {
font-size: 14px;
color: #333;
line-height: 20px;
word-break: break-word;
}
.vote-option-progress {
height: 5px;
width: 100%;
justify-content: flex-end;
display: none;
margin-top: 5px;
.vote-option-progress-step {
// background-color: rgba(49, 215, 46, 0.498039215686275);
background-color: var(--main-color);
opacity: 0.49039;
// width: 150px;
height: 4px;
border-radius: 66px;
margin-right: 14px;
}
.vote-option-progress-value {
font-size: 12px;
color: var(--main-color);
line-height: 20px;
}
}
}
}
.vote-data {
justify-content: space-between;
font-size: 12px;
color: #aaaaaa;
line-height: 22px;
.vote-data-item {
margin-left: 16px;
.vote-data-icon {
width: 14px;
cursor: pointer;
}
}
}
}
}
.empty-box {
width: 1200px;
height: 540px;
background-color: rgba(255, 255, 255, 1);
border-radius: 16px;
margin: 0 auto;
}
</style>