From e01ac5326b3c2aea9f4ea752ae604442bc93a7e3 Mon Sep 17 00:00:00 2001 From: A1300399510 <A1300399510> Date: Mon, 15 Jan 2024 19:02:10 +0800 Subject: [PATCH] no message --- app.vue | 5 +- components/DetailsArea.vue | 14 ++- components/DetailsComments.vue | 40 +++++--- components/Empty.vue | 22 +++- components/MyPopup.vue | 171 ++++++++++++++++++++++--------- components/Report.vue | 5 +- components/top-head.vue | 19 +++- composables/api.js | 52 +++++++++- composables/utils.js | 3 +- pages/details/[id].vue | 182 ++++++++++++++++++++++++++++++--- pages/index.html/index.vue | 54 ++++++++-- pages/publish/index.vue | 31 +++--- 12 files changed, 479 insertions(+), 119 deletions(-) diff --git a/app.vue b/app.vue index 6ac4428..0f3fa14 100644 --- a/app.vue +++ b/app.vue @@ -1,8 +1,5 @@ <template> - <div> - <RouterView></RouterView> - <!-- <NuxtWelcome /> --> - </div> + <RouterView></RouterView> </template> <script setup> const route = useRoute() diff --git a/components/DetailsArea.vue b/components/DetailsArea.vue index b495751..08864ae 100644 --- a/components/DetailsArea.vue +++ b/components/DetailsArea.vue @@ -6,19 +6,17 @@ <img class="icon" src="@/assets/img/eye-icon-black.svg" /> {{ info["views"] }} </div> --> - <div class="item flexacenter" @click="handleLike"> - <img class="icon" v-if="islike == 1" src="@/assets/img/like-icon-colours.png" /> - <img class="icon" v-else src="@/assets/img/like-icon.png" /> - {{ info["likes"] || "" }} - </div> <ClientOnly> + <div class="item flexacenter" @click="handleLike"> + <img class="icon" v-if="islike == 1" src="@/assets/img/like-icon-colours.png" /> + <img class="icon" v-else src="@/assets/img/like-icon.png" /> + {{ info["likes"] || "" }} + </div> <div class="item flexacenter" @click="handleCollect()"> <img class="icon" v-if="iscollection == 1" src="@/assets/img/collect-icon-colours.svg" /> <img class="icon" v-else src="@/assets/img/collect-icon.png" /> {{ info["favs"] || "收藏" }} </div> - </ClientOnly> - <ClientOnly> <el-popover placement="bottom" width="628px" trigger="click" popper-style="padding: 0;border-radius: 10px;" v-model:visible="transmitBoxState"> <template #reference> <div class="item flexacenter" @click="handleShare"><img class="icon" src="@/assets/img/transmit-icon.png" />转发</div> @@ -32,7 +30,7 @@ <div class="transmit-headline">{{ info["title"] }}</div> <div class="transmit-url">{{ getFullUrl() }}</div> </div> - <div class="transmit-web-btn flexcenter" @click="copyText(`${info['subject']} + ${getFullUrl()}`)">复制链接</div> + <div class="transmit-web-btn flexcenter" @click="copyText(`${info['title']} + ${getFullUrl()}`)">复制链接</div> </div> <div class="transmit-right transmit-mini"> <div class="transmit-title">转发小程序版</div> diff --git a/components/DetailsComments.vue b/components/DetailsComments.vue index 874b463..15cbfae 100644 --- a/components/DetailsComments.vue +++ b/components/DetailsComments.vue @@ -52,7 +52,7 @@ </div> </div> <div class="comment-text" @click="openAnswerCommentsChild(index)">{{ item["content"] }}</div> - <!-- <div class="comments-input-box flexacenter" v-if="item['childState']"> --> + <div class="alreadyVoted" v-if="item.voteoption">已投:{{ item.voteoption }}</div> <div class="comments-input-box flexacenter" v-if="item['childState']"> <div class="comments-input flexflex"> <textarea class="flex1" placeholder="回复" v-model="commentInput"></textarea> @@ -105,6 +105,7 @@ <div class="comments-reply" v-if="ite?.reply?.nickname">@{{ ite?.reply?.nickname }}</div> {{ ite["content"] }} </div> + <div class="alreadyVoted" v-if="ite.voteoption">已投:{{ ite.voteoption }}</div> <div class="comments-input-box flexacenter" v-if="ite['childState']"> <div class="comments-input flexflex"> <textarea class="flex1" placeholder="回复" v-model="commentInput"></textarea> @@ -139,17 +140,11 @@ const props = defineProps({ watch( () => props.token, - newV => { - console.log(newV) - getCommentList() - }, - { - immediate: false, - } + () => getCommentList(), + { immediate: false } ) -onMounted(() => { - window.addEventListener("scroll", handleScroll) -}) + +onMounted(() => window.addEventListener("scroll", handleScroll)) const sendMessage = inject("sendMessage") const TAHomePage = inject("TAHomePage") @@ -159,7 +154,6 @@ let commentComments = ref(0) // 所有的评论数 let commentPage = ref(1) let commentList = ref([]) let commentLoading = false -let token = "4ZKbui89pS81jKgWpT41kLgcglLOJa8UQCmuucFl-cyzQKdjr0iEMTl4grDC04TSnq1vC90fZ2pVdeP6IUYPN2Y4Ng~~" let isEmptyState = ref(false) // 评论是否为空 // 获取详情评论数据 @@ -255,7 +249,7 @@ const submitAnswerComments = (index, i) => { detailsSubmitommentListHttp({ content, - token, + token: props.token, parentid, }).then(res => { if (res.code != 200) { @@ -297,6 +291,7 @@ const submitAnswerComments = (index, i) => { commentCount.value++ } } + commentComments.value++ commentList.value = targetCommentList @@ -323,7 +318,7 @@ const alsoCommentsData = (index, ind) => { limit: 10, page, parentid, - token, + token: props.token, }).then(res => { if (res.code != 200) return let data = res.data @@ -398,6 +393,7 @@ provide("reportAlertShow", reportAlertShow) height: 60px; background-color: rgba(255, 255, 255, 1); border: 1px solid rgba(215, 215, 215, 1); + border-right: none; border-radius: 6px; margin-bottom: 30px; margin-right: 30px; @@ -459,7 +455,7 @@ provide("reportAlertShow", reportAlertShow) .comment-header { display: flex; justify-content: space-between; - padding-right: 30px; + // padding-right: 30px; margin-bottom: 10px; .comment-header-left { @@ -576,6 +572,16 @@ provide("reportAlertShow", reportAlertShow) } } + .alreadyVoted { + font-size: 12px; + color: #aaaaaa; + background-color: rgba(246, 246, 246, 1); + line-height: 17px; + width: fit-content; + margin-bottom: 15px; + word-break: break-word; + } + .comments-input-box { margin-top: 13px; margin-bottom: 10px; @@ -585,6 +591,7 @@ provide("reportAlertShow", reportAlertShow) flex: 1; height: 60px; border: 1px solid rgba(215, 215, 215, 1); + border-right: none; border-radius: 8px; margin-right: 16px; position: relative; @@ -614,7 +621,8 @@ provide("reportAlertShow", reportAlertShow) .comments-btn { width: 58px; height: 58px; - background-color: #31d72e; + // background-color: #31d72e; + background-color: var(--main-color); border-radius: 0 7px 7px 0; font-size: 14px; color: #ffffff; diff --git a/components/Empty.vue b/components/Empty.vue index 8e3a042..5993c23 100644 --- a/components/Empty.vue +++ b/components/Empty.vue @@ -9,14 +9,28 @@ <img class="item" src="@/assets/img/dot-gray.svg" /> </div> <img class="empty-icon" src="@/assets/img/empty-icon.svg" /> - <div class="empty-hint">{{ hint || "暂无内容" }}</div> + + <template v-if="isNeedIssue"> + <div class="empty-hint" style="margin-bottom: 7px;">没有找到相关结果,请更换搜索关键词</div> + <div class="empty-hint flexacenter"> + 或者 + <div class="sponsor" @click="goIssue">发起一个新投票</div> + </div> + </template> + <div v-else class="empty-hint">{{ hint || "暂无内容" }}</div> </div> </template> <script setup> +import { useRouter } from "vue-router" +const router = useRouter() + let props = defineProps({ hint: String, + isNeedIssue: Boolean, }) + +const goIssue = () => router.push("/publish") </script> <style lang="less" scoped> @@ -48,6 +62,12 @@ let props = defineProps({ font-size: 13px; color: #7f7f7f; line-height: 22px; + .sponsor { + text-decoration: underline; + color: #72db86; + margin-left: 5px; + cursor: pointer; + } } } </style> diff --git a/components/MyPopup.vue b/components/MyPopup.vue index e3a9c31..25ff53f 100644 --- a/components/MyPopup.vue +++ b/components/MyPopup.vue @@ -9,67 +9,75 @@ </div> </div> - <div class="empty-box flexcenter" v-loading="true" v-if="(MyPopupState == 'collect' && collectLoading) || (MyPopupState == 'mj' && publisloading)"></div> - <div class="empty-box flexcenter" v-else-if="showList.length == 0"> + <!-- <div class="empty-box flexcenter" v-loading="true" v-if="(MyPopupState == 'collect' && collectLoading) || (MyPopupState == 'mj' && publisloading)"></div> --> + <div class="empty-box flexcenter" v-if="showList.length == 0"> <Empty></Empty> </div> <el-scrollbar v-else height="479px"> <div class="content" @scroll="handleListScroll"> - <div class="item flexflex" v-for="(item, index) in showList" :key="index" @click="goDetails(item['uniqid'] || item?.data?.uniqid)"> + <div class="item flexflex" v-for="(item, index) in showList" :key="item.uniqid" @click="goDetails(item['uniqid'] || item?.data?.uniqid)"> <div class="left flexflex"> - <div class="name">{{ item.title }}</div> - <div class="message">{{ item.message }}</div> + <div class="name ellipsis">{{ item.title || item.data?.title }}</div> + <div class="message ellipsis">{{ item.message || item.data?.message }}</div> <div class="data"> - 30人参与 <i>|</i> 投票已结束 - <span><i>|</i> 我已投:不懂,围观学习</span> + {{ item.votes || item.data?.votes || 0 }}人参与 <i>|</i> {{ handleDeadline(item.deadline || item?.data?.deadline) }}结束 + <span v-if="item.optionvalue || item?.data?.optionvalue"><i>|</i> 我已投:{{ item.optionvalue || item?.data?.optionvalue }}</span> </div> </div> <div class="operate-area flexacenter"> - <img class="delete-icon" v-if="MyPopupState == 'collect'" @click.stop="cancelCollection(item['token'], index)" src="@/assets/img/delete-icon.svg" /> - <div class="anonymous-box flexacenter" v-else @click.stop="openAnonymousState(index)"> - <div class="text">{{ item["anonymous"] == 1 ? "匿名" : "公开" }}</div> - <img class="arrow-icon" src="@/assets/img/arrow-gray.svg" /> - <div class="state-popup flexflex" v-if="item['anonymousState']" @click.stop=""> - <div class="state-popup-item flexacenter flex1" :class="{ 'pitch': item['anonymous'] == 0 }" @click="handleAnonymousState(item['token'], index, 0)"> - <div class>公开发表</div> - <img class="state-popup-icon" src="@/assets/img/tick-green.svg" /> - </div> - <div class="state-popup-item flexacenter flex1" :class="{ 'pitch': item['anonymous'] == 1 }" @click="handleAnonymousState(item['token'], index, 1)"> - <div class>匿名发表</div> - <img class="state-popup-icon" src="@/assets/img/tick-green.svg" /> + <!-- <div class="anonymous-box flexacenter" v-if="MyPopupState == 'publish'" @click.stop="openAnonymousState(index)"> --> + <template v-if="MyPopupState == 'publish'"> + <div class="anonymous-box flexacenter" @click.stop="openAnonymousState(index)"> + <div class="text">{{ item["anonymous"] == 1 ? "匿名" : "公开" }}</div> + <img class="arrow-icon" src="@/assets/img/arrow-gray.svg" /> + <div class="state-popup flexflex" v-if="item['anonymousState']" @click.stop=""> + <div class="state-popup-item flexacenter flex1" :class="{ 'pitch': item['anonymous'] == 0 }" @click="handleAnonymousState(item['token'], index, 0)"> + <div class>公开发表</div> + <img class="state-popup-icon" src="@/assets/img/tick-green.svg" /> + </div> + <div class="state-popup-item flexacenter flex1" :class="{ 'pitch': item['anonymous'] == 1 }" @click="handleAnonymousState(item['token'], index, 1)"> + <div class>匿名发表</div> + <img class="state-popup-icon" src="@/assets/img/tick-green.svg" /> + </div> </div> </div> - </div> + <div class="halving-line"></div> + <img class="delete-icon" @click.stop="openDeleteVote(item['token'], index, item.uniqid || item?.data?.uniqid)" src="@/assets/img/delete-icon.svg" /> + </template> + + <img class="delete-icon" v-if="MyPopupState == 'collect'" @click.stop="cancelCollection(item['token'], index, item.uniqid || item?.data?.uniqid)" src="@/assets/img/delete-icon.svg" /> </div> </div> </div> </el-scrollbar> </div> </el-dialog> + + <el-dialog class="options-popup" v-model="deleteState" 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="deleteVote">删除投票</div> + <div class="options-popup-item options-yes flexcenter" @click="deleteState = false">不删除</div> + </div> + </el-dialog> <!-- </div> --> </template> <script setup> let props = defineProps({ - // MyPopupState: String, // collect mj - count: Object, + tabList: Array, }) +let deleteState = ref(false) // 确认删除弹窗 + +let count = inject("count") + let show = ref(false) const router = useRouter() const route = useRoute() -let MyPopupState = ref("") // collect participation sponsor +let MyPopupState = ref("") // collect takevote publish -onMounted(() => { - // if (MyPopupState.value == "collect") getCollect(); - // else if (MyPopupState.value == "mj") getPublish(); -}) - -const tabList = [ - { name: "我的收藏", type: "collect" }, - { name: "我参与的投票", type: "participation" }, - { name: "我发起的投票", type: "sponsor" }, -] +onMounted(() => {}) // 展示的 列表数据 let showList = ref([]) @@ -82,20 +90,20 @@ let collectCount = ref(0) const getCollect = () => { if (collectPage == 0 || collectLoading.value) return collectLoading.value = true + // if (collectPage == 2) return + MyUserCollectHttp({ page: collectPage }) .then(res => { if (res.code != 200) return let data = res.data collectList = collectList.concat(data.data) showList.value = collectList + // showList.value = showList.value.concat(data.data) if (collectList.length < data["count"]) collectPage++ else collectPage = 0 collectCount.value = data["count"] - - // MyPopupState.value = "collect" - // show.value = true }) .finally(() => (collectLoading.value = false)) } @@ -104,11 +112,10 @@ let publishList = [] let publisPage = 1 let publisloading = ref(false) const getPublish = () => { - return if (publisPage == 0 && !publisloading.value) return publisloading.value = true - MyUserPublishHttp({ limit: 4, page: publisPage }) + MyUserPublishHttp({ page: publisPage }) .then(res => { if (res.code != 200) return let data = res.data @@ -116,13 +123,28 @@ const getPublish = () => { if (publishList.length < data["count"]) publisPage++ else publisPage = 0 showList.value = publishList - - // MyPopupState.value = "mj" - // show.value = true }) .finally(() => (publisloading.value = false)) } +let takevoteList = [] +let takevotePage = 1 +let takevoteloading = ref(false) +const getTakevote = () => { + if (takevotePage == 0 && !takevoteloading.value) return + takevoteloading.value = true + MyUserTakevoteHttp({ page: takevotePage }) + .then(res => { + if (res.code != 200) return + let data = res.data + takevoteList = takevoteList.concat(data.data) + if (takevoteList.length < data["count"]) takevotePage++ + else takevotePage = 0 + showList.value = takevoteList + }) + .finally(() => (takevoteloading.value = false)) +} + // 切换 isEmpty 是否清空收藏数据, 因为不确定用户是否有新收藏 const cutMy = (key, isEmpty) => { if (isEmpty) { @@ -132,10 +154,12 @@ const cutMy = (key, isEmpty) => { } if (key == "collect" && collectList.length == 0) getCollect() - else if (key == "mj" && publishList.length == 0) getPublish() + else if (key == "takevote" && takevoteList.length == 0) getTakevote() + else if (key == "publish" && publishList.length == 0) getPublish() if (key == "collect") showList.value = collectList - else if (key == "mj") showList.value = publishList + else if (key == "takevote") showList.value = takevoteList + else if (key == "publish") showList.value = publishList MyPopupState.value = key @@ -161,7 +185,10 @@ const closeAllAnonymousState = () => { // 修改匿名状态 const handleAnonymousState = (token, index, anonymous) => { changeAnonymousHttp({ token, anonymous }).then(res => { - if (res.code != 200) return + if (res.code != 200) { + ElMessage.error(res.message) + return + } publishList[index]["anonymous"] = anonymous showList.value = [...publishList] @@ -177,7 +204,8 @@ const handleListScroll = e => { // 判断滚动到底部 if (el.scrollHeight - el.scrollTop !== el.clientHeight) return if (MyPopupState.value == "collect") getCollect() - if (MyPopupState.value == "mj") getPublish() + if (MyPopupState.value == "takevote") getTakevote() + if (MyPopupState.value == "publish") getPublish() } let clearAllData = inject("clearAllData") || null @@ -185,7 +213,7 @@ let getDetails = inject("getDetails") || null // 打开详情页 const goDetails = uniqid => { - return + // return let path = route["path"] || "" if (path.indexOf("/details/") != -1) { clearAllData() @@ -209,9 +237,12 @@ const closeDialog = () => { } // const emit = defineEmits(["cutMy"]); +const unbookmarkSamePage = inject("unbookmarkSamePage") // 处理取消收藏 -const cancelCollection = (token, index) => { +const cancelCollection = (token, index, uniqid) => { + const id = route.params["id"] + MyUserDeleteCollectHttp({ token }).then(res => { if (res.code != 200) { ElMessage.error(res.message) @@ -219,8 +250,42 @@ const cancelCollection = (token, index) => { } collectList.splice(index, 1) + count.value.collect-- collectCount.value-- showList.value = [...collectList] + + if (id == uniqid) unbookmarkSamePage() + }) +} + +let deleteobj = {} + +const openDeleteVote = (token, index, uniqid) => { + deleteobj["token"] = token + deleteobj["index"] = index + deleteobj["uniqid"] = uniqid + deleteState.value = true +} + +// 点删除投票 +const deleteVote = () => { + const id = route.params["id"] + + deleteHttp({ token: deleteobj["token"] }).then(res => { + if (res.code != 200) { + ElMessage.error(res.message) + return + } + + count.value.publish-- + publishList.splice(deleteobj["index"], 1) + showList.value = [...publishList] + + if (id == deleteobj["uniqid"]) unbookmarkSamePage() + + ElMessage.success(res.message) + deleteobj = {} + deleteState.value = false }) } </script> @@ -302,7 +367,8 @@ const cancelCollection = (token, index) => { .content { width: 100%; - height: 100%; + height: 479px; + // height: 100%; // background: #000000; overflow: auto; // padding-right: 13px; @@ -337,12 +403,14 @@ const cancelCollection = (token, index) => { line-height: 20px; font-size: 14px; margin-bottom: 10px; + width: 550px; } .message { color: #7f7f7f; line-height: 22px; font-size: 13px; margin-bottom: 6px; + width: 550px; } .data { @@ -363,6 +431,13 @@ const cancelCollection = (token, index) => { cursor: pointer; } + .halving-line { + margin: 0 20px; + width: 1px; + height: 13px; + border-right: 1px solid #d7d7d7; + } + .anonymous-box { .text { font-size: 13px; diff --git a/components/Report.vue b/components/Report.vue index 51dcdad..2658f04 100644 --- a/components/Report.vue +++ b/components/Report.vue @@ -56,10 +56,7 @@ const alertSubmit = () => { }).then(res => { checkList.value = [] reportAlertShow.value = false - ElMessage({ - message: res.message || "举报成功", - type: "success", - }) + ElMessage.success(res.message || "举报成功") }) } diff --git a/components/top-head.vue b/components/top-head.vue index 4df5412..96c939a 100644 --- a/components/top-head.vue +++ b/components/top-head.vue @@ -17,20 +17,18 @@ <div class="my-btn-item flexcenter" @click="handleUser('collect')">我的收藏</div> </div> --> <div class="my-btn-list flexacenter"> - <div class="my-btn-item flexcenter" @click="handleUser('collect')">我的收藏</div> - <div class="my-btn-item flexcenter" @click="handleUser('participation')">我参与的投票</div> - <div class="my-btn-item flexcenter" @click="handleUser('sponsor')">我发起的投票</div> + <div class="my-btn-item flexcenter" v-for="item in tabList" :key="item.type" @click="handleUser(item.type)">{{ item.name }}</div> </div> <div class="sponsor-btn flexcenter" @click="goPublish"> <img class="add-bj" src="@/assets/img/add-bj.svg" /> <img class="add-icon" src="@/assets/img/add-icon.svg" /> - 发布面经 + 发布投票 </div> </div> </div> </section> - <MyPopup ref="MyPopupRef" :count="count"></MyPopup> + <MyPopup ref="MyPopupRef" :tabList="tabList"></MyPopup> </template> <script setup> @@ -42,14 +40,23 @@ const route = useRoute() let isNeedLogin = inject("isNeedLogin") const goLogin = inject("goLogin") +const tabList = [ + { name: "我的收藏", type: "collect" }, + { name: "我参与的投票", type: "takevote" }, + { name: "我发起的投票", type: "publish" }, +] + let keyword = ref("") onMounted(() => { getHistoricalSearchList() keyword.value = route.query["keyword"] }) + let count = ref({}) +provide("count", count) + const getUser = () => { return new Promise((resolve, reject) => { MyUserInfoHttp().then(res => { @@ -134,6 +141,8 @@ const handleUser = async key => { return } + console.log("key", key) + if (Object.keys(count.value).length === 0) { await getUser() MyPopupRef.value.cutMy(key, true) diff --git a/composables/api.js b/composables/api.js index e179c5a..b6c1a75 100644 --- a/composables/api.js +++ b/composables/api.js @@ -39,6 +39,15 @@ export const publishSubmitHttp = query => { return Http.post("/api/publish/submit", query) } +// 发布相关 - 更改匿名状态 +export const changeAnonymousHttp = query => { + return Http.post("/api/publish/changeAnonymous", query) +} + +// 发布相关 - 删除投票 +export const deleteHttp = query => { + return Http.post("/api/publish/delete", query) +} // 操作-点赞 export const operateLikeHttp = query => { @@ -48,4 +57,45 @@ export const operateLikeHttp = query => { // 数据操作 - 收藏 export const operateCollectHttp = query => { return Http.post("/api/operate/collect", query) -} \ No newline at end of file +} + +// 数据操作 - 投票操作 +export const operationCollectHttp = query => { + return Http.post("/api/operate/operation", query) +} + +// 数据操作 - 取消投票操作 +export const unvoteCollectHttp = query => { + return Http.post("/api/operate/unvote", query) +} + +// 我的 - 用户信息 +export const MyUserInfoHttp = query => { + return Http.post("/api/user", query) +} + +// 我的 - 我的发布的面经 +export const MyUserPublishHttp = query => { + return Http.post("/api/user/publish", query) +} + +// 我的 - 用户相关 - 我参与 +export const MyUserTakevoteHttp = query => { + return Http.post("/api/user/takevote", query) +} + +// 我的 - 删除收藏 +export const MyUserDeleteCollectHttp = query => { + return Http.post("/api/user/deleteCollect", query) +} + +// 数据操作 - 收藏 +export const MyUserCollectHttp = query => { + return Http.post("/api/user/collect", query) +} + + +// 评论相关 - 举报 Comment related +export const commentReportHttp = query => { + return Http.post("/api/operate/report", query) +} diff --git a/composables/utils.js b/composables/utils.js index f5cdbc3..6296f85 100644 --- a/composables/utils.js +++ b/composables/utils.js @@ -32,7 +32,8 @@ export const handleDate = (dateTimeStamp = new Date()) => { // 处理 截止时间 export const handleDeadline = (dateTimeStamp = new Date()) => { - // dateTimeStamp = dateTimeStamp ? dateTimeStamp * 1000 : null + if (typeof dateTimeStamp == "number") dateTimeStamp = dateTimeStamp ? dateTimeStamp * 1000 : null + var timestamp = new Date(dateTimeStamp) timestamp = timestamp.getTime() var minute = 1000 * 60 diff --git a/pages/details/[id].vue b/pages/details/[id].vue index 86e2ab3..937e877 100644 --- a/pages/details/[id].vue +++ b/pages/details/[id].vue @@ -1,4 +1,9 @@ <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"> @@ -43,18 +48,17 @@ </div> </div> <div class="message">{{ info.message }}</div> - <!-- 共有 58 人参与,你已投票 --> - <div class="hint">{{ info.status == 1 && isvote == 0 ? `已有 ${info.votes} 人参与,` : `共有 ${info.votes} 人参与` }} {{ `${isvote == 1 ? "你已投票" : info.status == 1 ? "参与投票即可查看实时结果" : ""}` }}</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"> - <div class="option-item flexflex" v-for="(item, index) in option" :key="item.id"> + <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"> + <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" /> @@ -70,13 +74,25 @@ <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> -import { useRoute } from "vue-router" +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() -// console.log(route, "route") +const router = useRouter() + let isNeedLogin = inject("isNeedLogin") const goLogin = inject("goLogin") @@ -84,7 +100,10 @@ let id = route.params.id const uniqidEnd = id.charAt(id.length - 1) const uniqidIndex = base62ToDecimal(uniqidEnd) % 6 -onMounted(() => getinit()) +onMounted(() => { + getDetails() + clearBottom() +}) let info = ref({}) let qrcode = ref("") // 分享二维码 @@ -95,17 +114,19 @@ 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 getinit = () => { +const getDetails = () => { detailsHttp({ uniqid: id }).then(res => { - console.log(res) if (res.code != 200) { ElMessage.error(res.message) + router.push("/index.html") return } @@ -118,11 +139,12 @@ const getinit = () => { option.value = data["option"] qrcode.value = data.share?.qrcode token.value = data["token"] - - console.log(data.share?.qrcode, "data.share?.qrcode") + seo.value = data.seo }) } +provide("getDetails", getDetails) + // 点击发送信息 const sendMessage = uin => { redirectToExternalWebsite(`https://bbs.gter.net/home.php?mod=space&showmsg=1&uid=${uin}`) @@ -143,6 +165,91 @@ const redirectToExternalWebsite = url => { 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"> @@ -153,6 +260,7 @@ provide("TAHomePage", TAHomePage) 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); @@ -234,6 +342,7 @@ provide("TAHomePage", TAHomePage) line-height: 24px; color: #333; margin-bottom: 30px; + word-wrap: break-word; } .hint { @@ -326,10 +435,12 @@ provide("TAHomePage", TAHomePage) 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; } @@ -395,3 +506,50 @@ provide("TAHomePage", TAHomePage) } } </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> diff --git a/pages/index.html/index.vue b/pages/index.html/index.vue index e4ad6e7..b9e2467 100644 --- a/pages/index.html/index.vue +++ b/pages/index.html/index.vue @@ -8,6 +8,7 @@ <div class="halving-line"></div> <div class="search-result">共 {{ count }} 条搜索数据</div> </div> + <div class="vote-list-box" ref="gridContainer"> <a class="vote-item" target="_blank" :href="`/details/${item['uniqid']}`" v-for="(item, index) in list" :key="index" :class="{ 'isvote': item['isvote'] == 1 || item['status'] == 0 }" :style="{ '--main-color': colourValue[item.uniqidIndex]['main'], '--bg-color': colourValue[item.uniqidIndex]['bg'], '--bc-color': colourValue[item.uniqidIndex]['bc'] }"> <div class="vote-title"> @@ -17,7 +18,7 @@ </div> <div class="vote-explain">{{ item["message"] }}</div> <div class="vote-option-list flexflex"> - <div class="vote-option-item flexflex" :class="{ 'pitch': index == 1 }" v-for="(item, index) in item?.option" :key="index"> + <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" /> @@ -31,11 +32,11 @@ </div> <div class="vote-data flexacenter"> <div class="vote-data-left flexacenter"> - 18人参与 <template v-if="item['deadline']">| {{ handleDeadline(item["deadline"]) }}结束</template> + {{ 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" /> {{ 96 }}</div> - <div class="vote-data-item flexacenter" @click="handleLike(item['token'])"> + <div class="vote-data-item flexacenter"><img class="vote-data-icon" src="@/assets/img/eye-icon.svg" /> {{ 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/like-no.svg" /> <img v-else class="vote-data-icon" src="@/assets/img/like-yes.png" /> {{ item["likes"] }} </div> @@ -43,9 +44,14 @@ </div> </div> </a> + <div class="empty-box flexcenter" v-if="keyword && list.length == 0"> + <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", body: true }] }) + import { useRoute } from "vue-router" const route = useRoute() const router = useRouter() @@ -109,8 +115,18 @@ const handleScroll = () => { } // 处理点赞 -const handleLike = token => { - console.log("handleLike", token) +const handleLike = (token, index) => { + 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) + }) } // 点击关闭搜索 @@ -129,6 +145,24 @@ watch( getList() } ) + +try { + if (process.server) { + await getListHttp({ page, keyword: keyword.value }).then(res => { + let data = res.data + data.data.forEach(element => { + let uniqidEnd = element["uniqid"].charAt(element["uniqid"].length - 1) + element["uniqidIndex"] = base62ToDecimal(uniqidEnd) % 6 + }) + list.value = list.value.concat(data.data) + count.value = data.count + if (data.count > list.value.length) page++ + else page = 0 + + console.log("keyword", keyword.value) + }) + } +} catch (error) {} </script> <style lang="less" scoped> .vote-item { @@ -323,4 +357,12 @@ watch( } } } + +.empty-box { + width: 1200px; + height: 540px; + background-color: rgba(255, 255, 255, 1); + border-radius: 16px; + margin: 0 auto; +} </style> diff --git a/pages/publish/index.vue b/pages/publish/index.vue index af42e95..5e84b71 100644 --- a/pages/publish/index.vue +++ b/pages/publish/index.vue @@ -21,7 +21,7 @@ 标题 <div class="asterisk">*</div> </div> - <el-input class="item-input headline-textarea" type="textarea" placeholder="请输入" maxlength="24" show-word-limit v-model="info.title" autosize></el-input> + <el-input class="item-input headline-textarea" type="textarea" placeholder="请输入" maxlength="60" show-word-limit v-model="info.title" autosize></el-input> </div> <div class="item"> @@ -55,7 +55,7 @@ <div class="option-item flexacenter" v-for="(item, index) in 2" :key="index"> <div class="option-content flexacenter"> <div class="option-text flexcenter">{{ index + 1 }}</div> - <input class="option-input flex1" placeholder="请输入" /> + <el-input class="option-input flex1" placeholder="请输入" /> </div> <div class="option-drag flexcenter"> <img class="option-icon" src="@/assets/img/option-icon.svg" /> @@ -66,7 +66,8 @@ <div class="option-item flexacenter" v-for="(item, index) in optionList" :key="item.id"> <div class="option-content flexacenter"> <div class="option-text flexcenter">{{ index + 1 }}</div> - <input class="option-input flex1" placeholder="请输入" v-model="optionList[index]['message']" /> + <!-- <input class="option-input flex1" placeholder="请输入" v-model="optionList[index]['message']" /> --> + <el-input class="option-input flex1" placeholder="请输入" maxlength="100" show-word-limit v-model="optionList[index]['message']" /> <img class="option-cross" v-if="optionList[index]['message']" @click="clearMessage(index)" src="@/assets/img/cross-undertint-icon.svg" /> </div> <div class="option-drag flexcenter"> @@ -111,6 +112,7 @@ </template> <script setup> +useHead({ script: [{ src: "https://app.gter.net/bottom?tpl=footer", body: true }] }) import { useRouter } from "vue-router" import { ElMessage } from "element-plus" @@ -184,6 +186,7 @@ const submit = (status = 1) => { if (status == 1) { if (option.length < 2) { ElMessage.error("请设置至少2个选项~") + loading = false return } @@ -326,15 +329,14 @@ const clearMessage = index => { justify-content: center; padding: 0 122px; } + @media (max-width: 920px) { .content-box { display: block; padding: 0 10px; - // padding: 0 122px; } } .contentcontent { - // width: 1200px; max-width: 1200px; min-width: 900px; min-height: calc(100vh - 120px); @@ -364,11 +366,8 @@ const clearMessage = index => { .box-left { border-right: 16px solid #f6f6f6; width: 48.176%; - - // .area-box { padding: 30px; padding-right: 50px; - border-bottom: 1px solid #ebebeb; .item { &:not(:last-of-type) { @@ -378,9 +377,7 @@ const clearMessage = index => { .item-input { border-radius: 5px; outline: none; - // width: 482px; width: 100%; - // padding: 13px 14px; font-size: 14px; /deep/ .el-textarea__inner { @@ -500,12 +497,19 @@ const clearMessage = index => { outline: none; border-radius: 5px; color: #333; + + /deep/ .el-input__wrapper { + outline: none; + border: none; + box-shadow: none; + padding-right: 10px; + } } .option-cross { width: 12px; height: 12px; cursor: pointer; - margin: 0 10px; + margin-right: 10px; } } @@ -555,7 +559,7 @@ const clearMessage = index => { .floor-box { width: 100vw; - min-width: 1200px; + min-width: 900px; height: 90px; background-color: rgba(255, 255, 255, 1); -moz-box-shadow: 0px -1px 2px rgba(0, 0, 0, 0.192156862745098); @@ -564,7 +568,8 @@ const clearMessage = index => { position: fixed; bottom: 0; .box { - width: 1200px; + max-width: 1200px; + min-width: 900px; height: 100%; margin: 0 auto; justify-content: space-between;