PC-questions-answersnuxt/app.vue
2023-12-29 18:38:37 +08:00

2951 lines
134 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>
<div id="append_parent"></div>
<div id="ajaxwaitid"></div>
<div>
<Head>
<Title>{{ `${seo["title"] || "问答"} - 寄托天下出国留学网` }}</Title>
<Meta name="keyword" :content="seo['keyword']" />
<Meta name="description" :content="seo['description']" />
</Head>
<div class id="answer-app">
<header class="flexacenter" id="pageHeader">
<div class="flexacenter top">
<a href="./index.html">
<img class="logo" @click="handleLogo" alt="寄托问答" src="./img/logo.png" />
</a>
<div class="right flexacenter flex1">
<div class="searchInput flexacenter">
<input class="input flex1" placeholder="输入搜索关键词" v-model="keyword" @keydown.enter="searchClick()" @focus="searchFocus" @blur="searchBlur" />
<div class="clear-search flexacenter" v-if="isSearchMode">
<img class="cross-circle-black" @click="handleClickClear()" src="./img/cross-circle-black.png" />
<div class="halving-line"></div>
</div>
<img class="input-icon" src="./img/input-icon.png" @click="searchClick()" />
<div class="history-box" v-if="historicalSearchState" @click.stop>
<div class="history-title">历史搜索</div>
<div class="history-list">
<div class="history-item ellipsis" v-for="(item, index) in historicalSearchList" :key="index" @click.stop="handleClickHistoricalItem(item)">{{ item }}</div>
</div>
</div>
</div>
<div class="btn-list flexacenter">
<div class="item flex1" @click="handleMy('collect')">我的收藏</div>
<div class="item flex1" @click="handleMy('questions')">我的提问</div>
<div class="item flex1" @click="handleMy('answers')">我的回答</div>
</div>
<div class="add-btn flexcenter" @click="questionsInit()">
<img class="add-icon" src="./img/add-icon.svg" />
我要提问
</div>
</div>
</div>
<div class="tab-box flexcenter" :class="{ 'tab-list-fixed': tabListFixeState }">
<div class="tab-list flexacenter">
<div class="item flexcenter" :class="{ pitch: typePitch == null }" @click="cutType(null)">All</div>
<template v-for="(item, index) in typeList" :key="index">
<div class="halving-line">|</div>
<div class="item flexcenter" :class="{ pitch: typePitch == it['id'] }" v-for="it in item" :key="it" @click="cutType(it.id)">{{ it["name"] }}</div>
</template>
</div>
</div>
</header>
<div class="main flexflex" :class="{ 'mode-list': type == 'list' }">
<div class="list-box" @scroll="handleListScroll">
<div class="main-header" :style="{ paddingLeft: type == 'list' ? 0 : 'calc((100vw - 1200px) / 2)' }">
<div class="flexacenter">
<template v-if="myModelState">
<div class="search-keyword flexacenter" @click="closeMyModel()">
<div class="ellipsis">我的收藏/提问/回答</div>
<img class="search-keyword-cross" src="./img/cross-circle-icon.png" />
</div>
</template>
<template v-else-if="isSearchMode">
<div class="search-keyword flexacenter" @click="handleClickClear()">
<div class="ellipsis">{{ keywordText }}</div>
<img class="search-keyword-cross" src="./img/cross-circle-icon.png" />
</div>
<div class="total grid-item" @click="cut">共 {{ total }} 条搜索结果</div>
</template>
<div v-else class="total grid-item" @click="cut">共 {{ total }} 条问答</div>
</div>
<div class="look-only flexcenter" v-if="type != 'details'" @click="handleLookOnly">
<img class="look-icon" v-if="zeroreply == 0" src="@/img/tick-no.svg" />
<img class="look-icon" v-else src="@/img/tick-option.svg" />
只看0回答
</div>
</div>
<div class="list" id="list" :style="[listStyle(), { height: listHeight + 'px' }]">
<template v-for="(item, index) in list" :key="item.uniqid">
<a v-if="item['isrecom']" class="item grid-item flexflex" :style="itemStyle(index, item['content'], item['type'])" :class="{ pitch: index === pitchIndex, upLevel: index === pitchIndex - 1 }" target="_blank" :href="item['url']">
<img class="dot" src="./img/dot.svg" />
<div class="content" :style="{ width: type == 'list' ? '531px' : '430px' }">
<div class="issue-title flexcenter">
<div class="recommend flexcenter">推荐阅读</div>
<div class="issue ellipsis flex1">{{ item["title"] }}</div>
</div>
<template v-if="item['type'] == 'thread'">
<div class="answer ellipsis" v-if="item['message']">{{ item["message"] }}</div>
</template>
<div class="answer" style="height: auto;" v-else-if="item['type'] == 'vote'">
<div v-for="(ite, i) in item['option'].slice(0, 2)" :key="i">{{ replaceNumberObj[i] + ite }}</div>
<div>{{ replaceNumberObj[2] }} …</div>
</div>
<template v-else>
<div class="answer ellipsis flexacenter">
<div class="value-value" v-if="item['profession'] || item['professional']">{{ item["profession"] || item["professional"] }}</div>
<div class="value-value" v-if="item['project'] || item['degree']">{{ item["project"] || item["degree"] }}</div>
<div class="value-value" v-if="item['interviewtime'] || item['semester']">{{ item["interviewtime"] || item["semester"] }}</div>
<div class="value-value" v-if="item['apply_results']">{{ item["apply_results"] }}</div>
</div>
</template>
<div class="bottom flexacenter">
<div class="typename flexcenter">{{ item["typename"] }}</div>
</div>
</div>
</a>
<a v-else class="item grid-item flexflex" :style="itemStyle(index, item['content'])" :class="[{ pitch: index === pitchIndex, upLevel: index === pitchIndex - 1 }, `item${index}`]" :href="setItemUrl(item['uniqid'])" @click.stop.prevent="getDetails(item['uniqid'], index)">
<img class="dot" src="./img/dot.svg" />
<div class="content" :style="{ width: type == 'list' ? '531px' : '430px' }">
<div class="issue-title flexcenter">
<img class="hot-icon" v-if="item['ishot'] == 1" src="./img/hot-icon.png" />
<div class="issue ellipsis flex1">{{ item["title"] }}</div>
</div>
<div class="answer ellipsis" v-if="item['content']">{{ item["content"] }}</div>
<div class="bottom flexacenter">
<div class="typename flexcenter" v-if="item['typename']">{{ item["typename"] }}</div>
<div v-else></div>
<div class="flexacenter">
<div class="quantity">{{ item["answers"] == 0 ? "暂无回答" : "共" + item["answers"] + "个回答" }}</div>
<template v-if="type == 'list'">
<div class="longString"></div>
<div class="answer-btn" @click.stop.prevent="openListIAnswer(index)">我来回答</div>
</template>
</div>
</div>
</div>
</a>
</template>
</div>
<div class="bottom-tps" :style="bottomTpsStyle()" v-if="inTheEndState">- 到底了 -</div>
<div class="bottom-tps" :style="bottomTpsStyle()" v-if="myModelState"></div>
<div class="empty-box flexcenter" v-if="isListEmptyState && list.length == 0">
<div class="dot-list flexacenter">
<img class="item" src="./img/dot-yellow.svg" />
<img class="item" src="./img/dot-yellow.svg" />
<img class="item" src="./img/dot-yellow.svg" />
<img class="item" src="./img/dot-gray.svg" />
<img class="item" src="./img/dot-gray.svg" />
<img class="item" src="./img/dot-gray.svg" />
</div>
<img class="empty-icon" src="./img/empty-icon.svg" />
<div class="empty-hint" v-if="isSearchMode">没有找到相关结果,请更换搜索关键词</div>
<div class="empty-hint" v-else>暂无数据</div>
</div>
</div>
<div class="details-area-box flexflex" v-if="type == 'details'" @scroll="handleDetailsScroll">
<!-- 加载 -->
<div class="loading-bj flexcenter" v-if="detailLoading">
<svg t="1642133548066" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2597" width="200" height="200">
<path d="M512 249.5c-22.5 0-37.5-15-37.5-37.5V99.5c0-22.5 15-37.5 37.5-37.5s37.5 15 37.5 37.5V212c0 22.5-15 37.5-37.5 37.5z m0 712.5c-22.5 0-37.5-15-37.5-37.5V812c0-22.5 15-37.5 37.5-37.5s37.5 15 37.5 37.5v112.5c0 22.5-15 37.5-37.5 37.5zM212 549.5H99.5C77 549.5 62 534.5 62 512s15-37.5 37.5-37.5H212c22.5 0 37.5 15 37.5 37.5s-15 37.5-37.5 37.5z m712.5 0H812c-22.5 0-37.5-15-37.5-37.5s15-37.5 37.5-37.5h112.5c22.5 0 37.5 15 37.5 37.5s-15 37.5-37.5 37.5z m-153.75-150c-11.25 0-26.25-7.5-33.75-18.75-11.25-18.75-3.75-41.25 15-52.5L849.5 272c18.75-11.25 41.25-3.75 52.5 15s3.75 41.25-15 52.5l-97.5 56.25c-3.75 3.75-11.25 3.75-18.75 3.75z m-615 356.25c-11.25 0-26.25-7.5-33.75-18.75-11.25-18.75-3.75-41.25 15-52.5l97.5-56.25c18.75-11.25 41.25-3.75 52.5 15s3.75 41.25-15 52.5L174.5 752c-7.5 3.75-15 3.75-18.75 3.75z m506.25-465c-7.5 0-11.25 0-18.75-3.75-18.75-11.25-22.5-33.75-15-52.5L684.5 137c11.25-18.75 33.75-22.5 52.5-15 18.75 11.25 22.5 33.75 15 52.5L695.75 272c-7.5 11.25-22.5 18.75-33.75 18.75z m-356.25 615c-7.5 0-11.25 0-18.75-3.75-18.75-11.25-22.5-33.75-15-52.5l56.25-97.5c11.25-15 33.75-22.5 52.5-11.25s22.5 33.75 15 52.5L339.5 887c-7.5 11.25-22.5 18.75-33.75 18.75z m-52.5-506.25c-7.5 0-15 0-18.75-3.75L137 339.5c-18.75-11.25-26.25-33.75-15-52.5s33.75-22.5 52.5-15l97.5 56.25c18.75 11.25 22.5 33.75 11.25 52.5-3.75 11.25-18.75 18.75-30 18.75z m615 356.25c-7.5 0-11.25 0-18.75-3.75L752 695.75c-18.75-11.25-22.5-33.75-15-52.5 11.25-18.75 33.75-22.5 52.5-15L887 684.5c18.75 11.25 22.5 33.75 15 52.5-7.5 11.25-18.75 18.75-33.75 18.75zM362 290.75c-11.25 0-26.25-7.5-33.75-18.75L272 174.5c-7.5-18.75-3.75-41.25 15-52.5s41.25-3.75 52.5 15l56.25 97.5c7.5 18.75 3.75 41.25-15 48.75-7.5 3.75-11.25 7.5-18.75 7.5z m356.25 615c-11.25 0-26.25-7.5-33.75-18.75l-56.25-97.5c-11.25-18.75-3.75-41.25 15-52.5s41.25-3.75 52.5 15L752 849.5c11.25 18.75 3.75 41.25-15 52.5-7.5 3.75-11.25 3.75-18.75 3.75z" p-id="2598" fill="#26d79f" />
</svg>
</div>
<div class="details-box flexflex">
<!-- 转发的蒙版 -->
<div class="close-box">
<div class="close-circle flexcenter" @click="closeDetailMode()">
<img class="close-icon" src="./img/close-icon.svg" />
<img class="details-cross-icon" src="./img/cross-icon.png" />
</div>
</div>
<!-- 提问信息 -->
<div class="details-issue">
<!-- <div class="icon q flexcenter">Q</div> -->
<img class="qq" src="@/img/Q.png" />
<div class="titletitle">{{ detailsInfo["title"] }}</div>
<div class="hint" @click="handleAnswerText" v-if="detailsInfo['content']" v-html="detailsInfo['content']"></div>
<div class="info-box flexacenter">
<div class="user-info flexacenter" @click="openUserInfo()">
<img class="avatar" v-if="detailsInfo['avatar']" :src="detailsInfo['avatar']" />
<div class="user-name">{{ detailsInfo["nickname"] }}</div>
<div class="avatar-box flexflex" v-if="avatarState">
<a class="avatar-item flexcenter" target="_blank" @click.prevent="sendMessage(detailsInfo['uin'])">
<img class="avatar-icon" src="@/img/send-messages-icon.png" />
发送信息
</a>
<a class="avatar-item flexcenter" target="_blank" @click.prevent="TAHomePage(detailsInfo['uin'])">
<img class="avatar-icon" src="@/img/homepage-icon.png" />
TA的主页
</a>
<div class="avatar-mask"></div>
</div>
</div>
<div class="time">{{ handleDate(detailsInfo["publicationdate"]) }}</div>
</div>
<div class="operate-box flexacenter">
<div class="answer-btn flexcenter" :class="{ white: detailsInfo['answers'] == 0 }" @click="openIAnswer()">
<img class="answer-btn-icon answer-icon-edit" src="./img/edit-icon.png" />
<img class="answer-btn-icon answer-icon-white" src="./img/edit-icon-white.svg" />
我来回答
</div>
<div class="operate-list flexacenter">
<div class="operate-item flexacenter" style="cursor: auto;">
<img class="operate-icon operate-collect-icon" style="width: 13px; height: 8px;" src="./img/view-icon.svg" />
{{ detailsInfo["viewnum"] || 0 }}
</div>
<div class="operate-item flexacenter" @click="operateCollect()">
<img class="operate-icon operate-collect-icon" v-if="detailsIscollection == 0" src="./img/collect-icon.png" />
<img class="operate-icon operate-collect-icon" v-else src="./img/collect-icon-colours.svg" />
{{ detailsInfo["collectionnum"] > 0 ? detailsInfo["collectionnum"] : "收藏" }}
</div>
<div class="operate-item flexacenter operate-transmit" @mouseenter.stop="closeTransmitState()" @mouseleave.stop="closeAllTransmitState()">
<img class="operate-icon operate-transmit-icon" src="./img/transmit-icon.png" />
转发
<div class="transmit-box flexflex" v-if="questionsTransmitState" @click.stop style="z-index: 10;">
<img class="cross-icon" @click.stop="closeAllTransmitState()" src="./img/cross-icon.png" />
<div class="transmit-left transmit-web">
<div class="transmit-title">转发网页版</div>
<div class="transmit-content">
<div class="transmit-headline">{{ detailsInfo["title"] }}</div>
<div class="transmit-url">{{ getCurrentUrl() }}</div>
</div>
<div class="transmit-web-btn flexcenter" @click="copyText(detailsInfo['title'] + getCurrentUrl())">复制链接</div>
</div>
<div class="transmit-right transmit-mini">
<div class="transmit-title">转发小程序版</div>
<div class="transmit-content flexcenter">
<img class="transmit-mini-img" :src="detailShare['qrcode']" />
<div class="flexcenter">
<img class="give-sweep" src="./img/give-sweep.png" />
扫码转发该问答
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 一共多少 -->
<div class="answer-total-amount">共 {{ detailsInfo["answers"] }} 个回答</div>
<!-- 回答-数据 -->
<div class="answer-box-item" v-for="(item, index) in answerList" :key="index">
<!-- <div class="icon-circle-box">
<div class="icon-box flexcenter">A</div>
</div>-->
<img class="aa" src="@/img/A.png" />
<div class="answer-text" v-html="item['content']" @click="handleAnswerText"></div>
<div class="info-box flexacenter">
<div class="user-info flexacenter" @click="openUserInfo(index)">
<img class="avatar" :src="item['avatar']" />
<div class="user-name">{{ item["nickname"] }}</div>
<img class="homeShare" src="./img/title.png" v-if="item['groupid'] == 14" />
<div class="avatar-box flexflex" v-if="item['avatarState']">
<a class="avatar-item flexcenter" target="_blank" @click.prevent="sendMessage(item['uin'])">
<img class="avatar-icon" src="@/img/send-messages-icon.png" />
发送信息
</a>
<a class="avatar-item flexcenter" target="_blank" @click.prevent="TAHomePage(item['uin'])">
<img class="avatar-icon" src="@/img/homepage-icon.png" />
TA的主页
</a>
<div class="avatar-mask"></div>
</div>
</div>
<div class="time">{{ handleDate(item["publicationdate"]) }}</div>
</div>
<div class="operate-box flexacenter">
<div class="edit-box">
<div class="edit-btn flexcenter" v-if="item['ismyself'] == 1" @click="openIAnswer(index)">
<img class="edit-icon" src="./img/edit-icon.png" />
</div>
</div>
<div class="operate-list flexacenter">
<div class="operate-item flexacenter" @click="operateLike(item['token'], index)">
<img class="operate-icon operate-like-icon" v-if="item['islike'] == 1" src="./img/like-icon-colours.png" />
<img class="operate-icon operate-like-icon" v-else src="./img/like-icon.png" />
{{ item["likenum"] }}
</div>
<div class="operate-item flexacenter" :class="{ commentnum: item['commentState'] }" @click="openCommentState(index)">
<img class="operate-icon operate-comment-icon" src="./img/comment-icon.png" />
<!-- {{ item["commentnum"] }} -->
{{ item["commentnum"] }}
</div>
<div class="operate-item flexacenter" @click="operateCollect(item['token'], index)">
<img class="operate-icon operate-collect-icon" v-if="item['iscollection'] == 1" src="./img/collect-icon-colours.svg" />
<img class="operate-icon operate-collect-icon" v-else src="./img/collect-icon.png" />
{{ item["collectionnum"] == 0 ? "收藏" : item["collectionnum"] }}
</div>
<div class="operate-item operate-transmit flexacenter" @click="handleAnswerTransmitList(index)" @mouseenter.stop="handleAnswerTransmitList(index)" @mouseleave.stop="closeAllTransmitState()">
<img class="operate-icon operate-transmit-icon" src="./img/transmit-icon.png" />
转发
<div class="transmit-box flexflex" v-if="item['transmitState']" @click.stop>
<img class="cross-icon" @click.stop="closeAllTransmitState()" src="./img/cross-icon.png" />
<div class="transmit-left transmit-web">
<div class="transmit-title">转发网页版</div>
<div class="transmit-content">
<div class="transmit-headline">{{ detailsInfo["title"] }}</div>
<div class="transmit-url">{{ getCurrentUrl() }}</div>
</div>
<div class="transmit-web-btn flexcenter" @click="copyText(detailsInfo['title'] + getCurrentUrl())">复制链接</div>
</div>
<div class="transmit-right transmit-mini">
<div class="transmit-title">转发小程序版</div>
<div class="transmit-content flexcenter">
<img class="transmit-mini-img" :src="item['share']['qrcode']" />
<div class="flexcenter">
<img class="give-sweep" src="./img/give-sweep.png" />
扫码转发该问答
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<template v-if="item['commentState']">
<div class="post-comment flexacenter">
<input class="post-input flex1" placeholder="说点什么…" v-model="item['commentInput']" />
<div class="post-ok flexcenter" @click="submitAnswerComments(index)">OK</div>
</div>
<!-- 评论 -->
<div class="comments-box" v-if="item['commentList'] && item['commentList'].length != 0">
<div class="comments-item" v-for="(it, ind) in item['commentList']" :key="ind">
<div class="comments-header flexacenter">
<div class="comments-header-left flexacenter">
<img class="comments-avatar" :src="it['avatar']" @click="openUserInfo(index, ind)" />
<div class="comments-username" @click="openUserInfo(index, ind)">{{ it["nickname"] }}</div>
<div class="comments-time">{{ handleDate(it["timestamp"]) }}</div>
<div class="comments-identity" v-if="it['questioner'] == 1">提问者</div>
<div class="comments-identity" v-else-if="it['isauthor'] == 1">回答者</div>
<div class="avatar-box flexflex" v-if="it['avatarState']">
<a class="avatar-item flexcenter" target="_blank" @click.prevent="sendMessage(it['uin'])">
<img class="avatar-icon" src="@/img/send-messages-icon.png" />
发送信息
</a>
<a class="avatar-item flexcenter" target="_blank" @click.prevent="TAHomePage(it['uin'])">
<img class="avatar-icon" src="@/img/homepage-icon.png" />
TA的主页
</a>
<div class="avatar-mask" @click="openUserInfo(index, ind)"></div>
</div>
</div>
<div class="comments-header-right flexacenter">
<div class="menu-box flexacenter" @click="handleMenuState(index, ind)">
<img class="menu-icon" src="./img/menu-icon-gray.svg" />
<div class="report-box flexcenter">举报</div>
</div>
<img class="comment-icon" @click="openAnswerCommentsChild(index, ind)" src="./img/comment-icon-gray.svg" />
<div class="flexacenter like-box" @click="operateAnswerCommentsLike(it['token'], index, ind)">
<img class="like-icon" v-if="it['islike'] == 0" src="./img/like-icon-gray.png" />
<img class="like-icon" v-else src="./img/like-icon-colours.png" />
<div class="like-quantity">{{ it["likenum"] || 0 }}</div>
</div>
</div>
</div>
<div class="comments-content">
<div class="comments-text">{{ it["content"] }}</div>
<div class="comments-input-box flexacenter" v-if="it['childState']">
<div class="comments-input flexflex">
<textarea class="flex1" placeholder="回复" v-model="it['commentInput']"></textarea>
<div class="comments-btn flexcenter" @click="submitAnswerComments(index, ind)">发送</div>
</div>
<img class="forkfork" src="./img/cross-icon.png" @click="closeAnswerCommentsChild(index, ind)" />
</div>
</div>
<div class="child-comments" v-if="it['child'].length != 0">
<div class="comments-item" v-for="(ite, i) in it['child']" :key="i">
<div class="comments-header flexacenter">
<div class="comments-header-left flexacenter">
<img class="comments-avatar" @click="openUserInfo(index, ind, i)" :src="ite['avatar']" />
<div class="comments-username" @click="openUserInfo(index, ind, i)">{{ ite["nickname"] }}</div>
<div class="comments-time">{{ handleDate(ite["timestamp"]) }}</div>
<div class="comments-identity" v-if="ite['questioner'] == 1">提问者</div>
<div class="comments-identity" v-else-if="ite['isauthor'] == 1">回答者</div>
<div class="avatar-box flexflex" v-if="ite['avatarState']">
<a class="avatar-item flexcenter" target="_blank" @click.prevent="sendMessage(ite['uin'])">
<img class="avatar-icon" src="@/img/send-messages-icon.png" />
发送信息
</a>
<a class="avatar-item flexcenter" target="_blank" @click.prevent="TAHomePage(ite['uin'])">
<img class="avatar-icon" src="@/img/homepage-icon.png" />
TA的主页
</a>
<div class="avatar-mask" @click="openUserInfo(index, ind, i)"></div>
</div>
</div>
<div class="comments-header-right flexacenter">
<div class="menu-box flexacenter" @click="handleMenuState(index, ind, i)">
<img class="menu-icon" src="./img/menu-icon-gray.svg" />
<div class="report-box flexcenter">举报</div>
</div>
<img class="comment-icon" @click="openAnswerCommentsChild(index, ind, i)" src="./img/comment-icon-gray.svg" />
<div class="flexacenter like-box" @click="operateAnswerCommentsLike(ite['token'], index, ind, i)">
<img class="like-icon" v-if="ite['islike'] == 0" src="./img/like-icon-gray.png" />
<img class="like-icon" v-else src="./img/like-icon-colours.png" />
<div class="like-quantity">{{ ite["likenum"] || 0 }}</div>
</div>
</div>
</div>
<div class="comments-content">
<div class="comments-text">
<div class="comments-reply" v-if="JSON.stringify(ite['reply']) != '[]'">@{{ ite["reply"]["nickname"] }}</div>
{{ ite["content"] }}
</div>
<div class="comments-input-box flexacenter" v-if="ite['childState']">
<div class="comments-input flexflex">
<textarea class="flex1" placeholder="回复" v-model="ite['commentInput']"></textarea>
<div class="comments-btn flexcenter" @click="submitAnswerComments(index, ind, i)">发送</div>
</div>
<img class="forkfork" src="./img/cross-icon.png" @click="closeAnswerCommentsChild(index, ind, i)" />
</div>
</div>
</div>
<div class="comments-also flexacenter" v-if="it['childnum'] > it['child'].length" @click="alsoCommentsData(index, ind)">
<div class>还有{{ it["childnum"] - 1 }}条回复</div>
<img class="also-icon" src="./img/arrow-circular-gray.png" />
</div>
</div>
</div>
<!-- {{ item["commentnum"] + ' ' + item["commentList"].length }} -->
<!-- <div class="reverl-all flexcenter" @click="handleAllComment(index)" v-if="item['commentnum'] != item['commentList'].length"> -->
<div class="reverl-all flexcenter" @click="handleAllComment(index)" v-if="item['commentCount'] != item['commentList'].length">
显示全部
<img class="arrow-circular" src="./img/arrow-circular-gray.png" />
</div>
</div>
<!-- 评论为空时 -->
<!-- <div class="comments-empty-box flexflex" v-if="item['commentnum'] == 0"> -->
<div class="comments-empty-box flexflex" v-else>
<div class="empty-box flexcenter">
<div class="dot-list flexacenter">
<img class="dot-item" src="./img/dot-yellow.svg" v-for="item in 3" :key="item" />
<img class="dot-item" src="./img/dot-gray.svg" v-for="item in 3" :key="item" />
</div>
<img class="empty-icon" src="./img/empty-icon.svg" />
</div>
<div class="empty-hint">和我说说你的想法或疑问吧</div>
</div>
</template>
</div>
<!-- · 著作权归作者所有 · -->
<div class="copyright flexcenter" v-if="answerList.length > 0">· 著作权归作者所有 ·</div>
<!-- 你的答案 -->
<div class="your-answer-box" v-if="!isNeedLogin">
<div class="your-answer-header flexacenter">您的答案</div>
<div class="your-answer-textarea" :class="{ placeholder: yourAnswerPlaceholderState }" contenteditable="true" @paste="handlePaste($event, 'you')" v-html="yourAnswer['text']" @input="handleInputYou"></div>
<div class="flexacenter your-answer-bottom">
<div class="option-box flexacenter" @click="cutYourAnswerAnonymous()">
<img class="option-icon" v-if="yourAnswer['anonymous'] == 0" src="./img/tick-no.svg" />
<img class="option-icon" v-else src="./img/tick-option.svg" />
匿名发表
</div>
<div class="your-answer-submit flexcenter" @click="handleYourAnswer">提交回答</div>
</div>
</div>
<!-- 回答-没有数据 -->
<div class="answer-empty-box flexcenter" v-if="isNeedLogin && answerList.length == 0">
<div class="empty-box flexcenter">
<div class="dot-list flexacenter">
<img class="dot-item" src="./img/dot-yellow.svg" v-for="item in 3" :key="item" />
<img class="dot-item" src="./img/dot-gray.svg" v-for="item in 3" :key="item" />
</div>
<img class="empty-icon" src="./img/empty-icon.svg" />
</div>
<div class="empty-hint">我在等待你的回答</div>
</div>
<div class="mobile-phone-check flexcenter">
<img class="QRCode-icon" src="./img/QRCode-icon.svg" alt />
手机查看该问答
<div class="QRCode-pop flexcenter">
<img class="offer-mini-QRcode" :src="detailShare['qrcode']" />
<div class="QRCode-hint flexacenter">
<img class="QRCode-img" src="./img/give-sweep.png" />
微信扫一扫
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 我的弹窗 -->
<div class="popover-mask my-popover flexcenter" v-if="myType">
<div class="popover-box flexflex">
<img class="cross-icon" src="./img/cross-icon.png" @click="myType = ''" />
<div class="tab-list flexcenter">
<div class="tab-item flexacenter" :class="{ pitch: myType == 'collect' }" @click="handleMy('collect')">
我的收藏
<div class="quantity">{{ myCollectionCount || myCount["collect"] || 0 }}</div>
</div>
<div class="long-string"></div>
<div class="tab-item flexacenter" :class="{ pitch: myType == 'questions' }" @click="handleMy('questions')">
我的提问
<div class="quantity">{{ myQuestionsCount || myCount["questions"] || 0 }}</div>
</div>
<div class="long-string"></div>
<div class="tab-item flexacenter" :class="{ pitch: myType == 'answers' }" @click="handleMy('answers')">
我的回答
<div class="quantity">{{ myAnswerCount || myCount["answer"] || 0 }}</div>
</div>
</div>
<div class="empty-box flexcenter" v-if="(myType == 'collect' && myCollectionList.length == 0) || (myType == 'answers' && myAnswerList.length == 0) || (myType == 'questions' && myQuestionsList.length == 0)">
<div class="dot-list flexacenter">
<img class="item" src="./img/dot-yellow.svg" />
<img class="item" src="./img/dot-yellow.svg" />
<img class="item" src="./img/dot-yellow.svg" />
<img class="item" src="./img/dot-gray.svg" />
<img class="item" src="./img/dot-gray.svg" />
<img class="item" src="./img/dot-gray.svg" />
</div>
<img class="empty-icon" src="./img/empty-icon.svg" />
<div class="empty-hint">暂无内容</div>
</div>
<div class="content-box collect-list" v-if="myType == 'collect' && myCollectionList.length != 0" @scroll="handleCollectionScroll">
<div class="item flexflex" v-for="(item, index) in myCollectionList" :key="item" @click.stop="myOpenDetails(item.data['uniqid'])">
<template v-if="item.type == 'askanswercollection'">
<div class="icon a flexcenter">A</div>
<div class="centre flexflex flex1">
<div class="titletitle ellipsis" v-html="item['data']['content']"></div>
<div class="text ellipsis">提问:{{ item["data"]["title"] }}</div>
</div>
<div class="delete-box flexacenter">
<img class="delete-icon" @click.stop="cancelCollection(item['token'], index)" src="./img/delete-icon.svg" />
</div>
</template>
<template v-else>
<div class="icon q flexcenter">Q</div>
<div class="centre flexflex flex1">
<div class="titletitle ellipsis">{{ item["data"]["title"] }}</div>
<div class="text ellipsis">
{{ item["data"]["answers"] > 0 ? "共" + item["data"]["answers"] + "个回答" : "暂无回答" }}
</div>
</div>
<div class="delete-box flexacenter">
<img class="delete-icon" @click.stop="cancelCollection(item['token'], index)" src="./img/delete-icon.svg" />
</div>
</template>
</div>
</div>
<div class="content-box answers-list" v-if="myType == 'answers' && myAnswerList.length != 0" @scroll="handleAnswersScroll">
<div class="item flexflex" v-for="(item, index) in myAnswerList" :key="item" @click.stop="myOpenDetails(item['uniqid'])">
<div class="icon a flexcenter">A</div>
<div class="centre flexflex flex1">
<div class="info flexacenter">
<div class="name">{{ item["nickname"] }}</div>
<div class="time">{{ handleDate(item["publicationdate"]) }}</div>
</div>
<div class="titletitle ellipsis" v-html="item['content']"></div>
<div class="text ellipsis">提问:{{ item["title"] }}</div>
</div>
<div class="operate-box flexacenter">
<div class="state-box flexacenter" @click.stop="cutAnswerPopupState(index)">
<div class="text">{{ item["anonymous"] == 0 ? "公开" : "匿名" }}</div>
<img class="arrows" src="./img/arrows-icon.svg" />
<div class="state-popup flexflex" v-if="item['popupState']">
<!-- <div class="state-popup-mask" @click.stop="cutAnswerPopupState(index)"></div> -->
<div class="state-popup-item flexacenter flex1" :class="{ pitch: item['anonymous'] == 0 }" @click.stop="changeAnonymous(item['token'], 0, index)">
<div class>公开发表</div>
<img class="state-popup-icon" src="./img/tick-orange.svg" />
</div>
<div class="state-popup-item flexacenter flex1" :class="{ pitch: item['anonymous'] == 1 }" @click.stop="changeAnonymous(item['token'], 1, index)">
<div class>匿名发表</div>
<img class="state-popup-icon" src="./img/tick-orange.svg" />
</div>
</div>
</div>
<img class="edit-icon" @click.stop="openIAnswer(index, 'my')" src="./img/edit-icon.png" />
</div>
</div>
</div>
<div class="content-box questions-list" v-if="myType == 'questions' && myQuestionsList.length != 0" @scroll="handleQuestionsScroll">
<div class="item flexflex" v-for="(item, index) in myQuestionsList" :key="item" @click.stop="myOpenDetails(item['uniqid'])">
<div class="icon q flexcenter">Q</div>
<div class="centre flexflex flex1">
<div class="info flexacenter">
<div class="name">{{ item["nickname"] }}</div>
<div class="time">{{ handleDate(item["publicationdate"]) }}</div>
</div>
<div class="titletitle ellipsis">{{ item["title"] }}</div>
<div class="text flexacenter">
<div class="new-answer flexacenter" v-if="item['authornewnum'] > 0">
有{{ item["authornewnum"] }}个新回答
<div class="long-string"></div>
</div>
{{ item["answers"] == 0 ? "暂无回答" : "共" + item["answers"] + "个回答" }}
</div>
</div>
<div class="operate-box flexacenter">
<div class="state-box flexacenter" @click.stop="cutQuestionsPopupState(index)">
<div class="text">{{ item["anonymous"] == 0 ? "公开" : "匿名" }}</div>
<img class="arrows" src="./img/arrows-icon.svg" />
<div class="state-popup flexflex" v-if="item['popupState']">
<div class="state-popup-item flexacenter flex1" :class="{ pitch: item['anonymous'] == 0 }" @click.stop="changeAnonymousQuestions(item['token'], 0, index)">
<div class>公开发表</div>
<img class="state-popup-icon" src="./img/tick-orange.svg" />
</div>
<div class="state-popup-item flexacenter flex1" :class="{ pitch: item['anonymous'] == 1 }" @click.stop="changeAnonymousQuestions(item['token'], 1, index)">
<div class>匿名发表</div>
<img class="state-popup-icon" src="./img/tick-orange.svg" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 我要提问 -->
<div class="popover-mask flexcenter issue-box" v-if="questionsSetp" @click="cutQuestionsSetp(0)">
<!-- 第一步 -->
<div class="choosing-theme" v-if="questionsSetp == 1" @click.stop>
<div class="titletitle">选择提问所属主题</div>
<div class="theme-list flexflex">
<div class="theme-stair-box flexflex" v-for="(item, index) in questionsTypeList" :key="index">
<div class="theme-item flexcenter" v-for="item in item" :key="item.id" @click="choosingTheme(item.id)">{{ item.name }}</div>
</div>
</div>
</div>
<!-- 第二步 -->
<div class="issue-form" v-else @click.stop>
<img class="issue-bj" src="./img/issue-bj.svg" />
<div class="flexcenter q">Q</div>
<img class="cross-icon" @click="cutQuestionsSetp(0)" src="./img/cross-icon.png" />
<div class="issue-input">
<textarea v-model="questionsObj.title" placeholder="一句话描述问题,以问号结尾"></textarea>
</div>
<textarea class="issue-replenish" v-model="questionsObj.content" placeholder="欢迎补充,清晰表达问题的关键点,可获得更有效的解答(非必填)"></textarea>
<div class="issue-bottom flexacenter">
<div class="option-box flexacenter" @click="cutAnonymous" style="color: #333;">
<img class="option-icon" v-if="questionsObj.anonymous == 0" src="./img/tick-no.svg" />
<img class="option-icon" v-else src="./img/tick-option.svg" />
匿名发表
<div class style="color: #aaa;">(发布后只能修改是否匿名)</div>
</div>
<div class="issue-btn flexcenter" @click="postingIssue">发布问题</div>
</div>
</div>
</div>
<!-- 编辑回答 -->
<div class="popover-mask flexcenter" v-if="IAnswerEditState">
<div class="edit-answers">
<img class="close-icon" src="./img/cross-icon.png" @click="closeIAnswer" />
<div class="titletitle">编辑回答</div>
<div class="question-textarea" :class="{ placeholder: questionPlaceholderState }" contenteditable="true" @paste="handlePaste($event)" v-html="IAnswerInfo['text']" @input="handleInput"></div>
<div class="issue-bottom flexacenter">
<div class="option-box flexacenter" @click="amendIAnswer">
<img class="option-icon" v-if="IAnswerInfo['anonymous'] == 0" src="./img/tick-no.svg" />
<img class="option-icon" v-else src="./img/tick-option.svg" />
匿名发表
</div>
<div class="issue-btn flexcenter" @click="submitAnswer">提交回答</div>
</div>
</div>
</div>
<!-- 我来回答 -->
<div class="popover-mask flexcenter" v-if="IAnswerState">
<div class="i-answer-box flexflex">
<img class="close-icon" src="./img/cross-icon.png" @click="closeIAnswer" />
<div class="question-header">
<div class="question-title flexflex">
<div class="question-icon flexcenter">Q</div>
<div class="flex1" v-html="IAnswerInfo['title']"></div>
</div>
<div class="question-replenish" v-if="IAnswerInfo['content']" v-html="IAnswerInfo['content']"></div>
</div>
<div class="question-middle flexflex">
<div class="question-icon flexcenter">A</div>
<div class="question-textarea" :class="{ placeholder: questionPlaceholderState }" contenteditable="true" @paste="handlePaste($event)" v-html="IAnswerInfo['text']" @input="handleInput"></div>
</div>
<div class="issue-bottom flexacenter">
<div class="option-box flexacenter" @click="amendIAnswer">
<img class="option-icon" v-if="IAnswerInfo['anonymous'] == 0" src="./img/tick-no.svg" />
<img class="option-icon" v-else src="./img/tick-option.svg" />
匿名发表
</div>
<div class="issue-btn flexcenter" @click="submitAnswer">提交回答</div>
</div>
</div>
</div>
<!-- 提示框 -->
<transition name="msg" appear v-show="msg['state']">
<div class="box-item" :class="boxClass()" :style="{ top: 20 + 'px' }">
<div class="msg-container">{{ msg["text"] }}</div>
</div>
</transition>
<!-- 大图 -->
<div class="detail-image-mask flexcenter" v-if="dialogSrc" @click="dialogSrc = ''">
<div class="detail-image flexcenter">
<img class="detail-img" :src="dialogSrc" />
</div>
</div>
<!-- 举报 -->
<div class="alert-form" v-show="alertShow">
<div class="comments reports">
<div class="head">
<span style="display: flex; align-items: center;"> <img style="width: 25px; margin-right: 7px;" src="//app.gter.net/image/gter/offer/img/exclamationpoint.png?v=4.2.08_331040000" />举报投诉 </span>
<div class="close icon-close iconfont" @click="alertShow = false"></div>
</div>
<div class="form">
<div class="radio-area flexacenter">
<div class="radio-area-item flexacenter" :class="{ pitch: checkList.includes(s) }" v-for="(s, i) in reasonList" :key="i" @click="selectRadio(s)">
<div class="radio-area-frame"></div>
{{ s }}
</div>
</div>
<div class="text-box">
<textarea placeholder="请输入举报原因" v-model="alertText" maxlength="200"></textarea>
<div class="text-num">{{ 200 - alertText.length }}</div>
</div>
<div class="footer">
<button type="button" @click="alertShow = false">取消</button>
<button type="submit" :disabled="checkList.length == 0" @click="alertSubmit">提交</button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
// let userInfoWin = {
// "username": "",
// "uid": 256624,
// "uin": 4171117,
// "avatar": "https://oss.gter.net/avatar/97KwEWQHYuMQGDnFqkimXF9SHKuGwVV5zW-tYWFjYQ~~?istype=1&random=nDW5aKmb89WC",
// "messagenum": 0,
// "newprompt": 0,
// "todaysigned": 0
// }
import { onMounted, onUnmounted, ref, nextTick, watchEffect, watch } from "vue"
import axios from "axios"
export default {
name: "#answer-app",
async setup() {
const $ajax = (url, data) => {
url = url.indexOf("//") > -1 ? url : baseURL + url
return new Promise(function (resolve, reject) {
axios
.post(url, data, {
emulateJSON: true,
withCredentials: true,
headers: {
authorization: process.env.NODE_ENV !== "production" && "pa5o1v493ed5mahrcio66267e2", // 头部标记 ada
// "7a89997c2ccd8cb5ed8cb20d843dafdd", // 头部标记 ada
},
})
.then(function (res) {
var data = null
try {
data = typeof res.data == "string" ? JSON.parse(res.data) : res.data
if (data["code"] == 401) {
goLogin()
// isNeedLogin.value = true;
}
if (data["code"] != 200) handleMsg("error", data["message"] || "报错了,请重试!!!")
} catch (error) {}
resolve(data)
})
.catch(err => {
if (err.response.status == 401) {
goLogin()
// isNeedLogin.value = true;
}
resolve(err.response.data)
})
})
}
const $ajaxGET = (url, data) => {
url = url.indexOf("//") > -1 ? url : baseURL + url
return new Promise(function (resolve, reject) {
axios
.get(url, data, {
emulateJSON: true,
withCredentials: true,
headers: {
authorization: process.env.NODE_ENV !== "production" && "pa5o1v493ed5mahrcio66267e2", // 头部标记
// "7a89997c2ccd8cb5ed8cb20d843dafdd", // 头部标记
},
})
.then(function (res) {
var data = null
try {
data = typeof res.data == "string" ? JSON.parse(res.data) : res.data
if (data["code"] == 401) {
goLogin()
// isNeedLogin.value = true;
}
if (data["code"] != 200) handleMsg("error", data["message"] || "报错了,请重试!!!")
} catch (error) {}
resolve(data)
})
.catch(err => {
if (err.response.status == 401) {
goLogin()
// isNeedLogin.value = true;
}
resolve(err.response.data)
})
})
}
let isNeedLogin = ref(true) // 是否需要有登录
// 跳转登录
const goLogin = () => {
if (typeof window === "undefined") return
if (Object.keys(window["userInfoWin"]).length !== 0) {
if (window["userInfoWin"]["uid"]) isNeedLogin.value = false
else ajax_login()
} else ajax_login()
}
const route = useRoute()
let detailsInfoDataVuex = useState("detailsInfoData", () => {})
const baseURL = "https://ask.gter.net"
let type = ref("list") // list details
onMounted(() => {
// setTimeout(() => {
// window["userInfoWin"] = {
// "username": "",
// "uid": 256624,
// "uin": 4171117,
// "avatar": "https://oss.gter.net/avatar/97KwEWQHYuMQGDnFqkimXF9SHKuGwVV5zW-tYWFjYQ~~?istype=1&random=nDW5aKmb89WC",
// "messagenum": 0,
// "newprompt": 0,
// "todaysigned": 0
// }
// }, 400);
// 判断一下是否登录
// if (process.env.NODE_ENV !== "production") isNeedLogin.value = false;
// else
determineIsLogin()
getListClass()
window.addEventListener("scroll", handleScroll)
getHistoricalSearchList()
if (isNeedScrollMiddle) scrollLeftInMiddle()
if (process.client) originUrl.value = window.location.origin
listenPageBack()
if (detailsInfoDataVuex.value) hanleServerRequestData()
initState.value++
const params = route.query
// 是否直接打开提问
setTimeout(() => {
if (params["ispublish"]) questionsInit()
}, 500)
})
// 处理在服务端已经请求了详情接口 数据
const hanleServerRequestData = () => {
const params = route.query
type.value = "details"
let data = detailsInfoDataVuex.value
detailsInfo.value = data["info"] || {}
detailsIsanswered.value = data["isanswered"] || 0
detailsIscollection.value = data["iscollection"] || 0
detailsIsmyself.value = data["ismyself"] || 0
detailsToken = data["token"] || ""
detailShare.value = data["share"] || {}
type.value = "details"
answerList.value = []
answerPage.value = 1
getAnswerList()
closeAllTransmitState()
replaceState({ uniqid: params["uniqid"] })
seo.value = data.seo
closeBottom()
}
// 是否是后退 状态
let isBackState = false
// 监听页面后退效果 重新获取 url 请求
const listenPageBack = () => {
window.addEventListener("popstate", function (event) {
const params = route.query
if (params["keyword"]) keyword.value = params["keyword"]
if (params["tid"]) typePitch.value = params["tid"]
if (params["uniqid"]) {
isBackState = true
getDetails(params["uniqid"])
} else {
type.value = "list"
openBottom()
pitchIndex.value = null
seo.value = {}
}
})
}
// 判断是否登录状态
const determineIsLogin = () => {
let count = 0
let timer = setInterval(() => {
// console.log(Object.keys(window["userInfoWin"]).length);
if (Object.keys(window["userInfoWin"]).length !== 0) {
clearInterval(timer)
if (window["userInfoWin"]["uid"]) isNeedLogin.value = false
}
count++
if (count >= 10) clearInterval(timer)
}, 100)
}
let originUrl = ref("")
onUnmounted(() => {
window.removeEventListener("keydown", handleKeydown)
window.removeEventListener("scroll", handleScroll)
})
// 获取当前url 专门 分享的url
const getCurrentUrl = () => {
return `${window.location["origin"]}?uniqid=${detailsInfo.value["uniqid"] || ""}`
}
// 我的 数据 数量
let myCount = ref({}) //
// 获取用户数据
const getUserData = key => {
$ajax("/api/user").then(res => {
if (res.code != 200) return
let data = res.data
myCount.value = data.count
handleMy(key)
})
}
// 是否是搜索模式
let isSearchMode = ref(false)
// 获取历史记录方法
const getHistoricalSearchList = () => {
const list = localStorage.getItem("historical-Search")
if (list) historicalSearchList.value = JSON.parse(list) || []
else historicalSearchList.value = []
}
// 存入历史记录 随便去重 和 限制长度 方法
const setHistoricalSearchList = () => {
if (!keyword.value) return
historicalSearchList.value.unshift(keyword.value)
historicalSearchList.value = [...new Set(historicalSearchList.value)]
historicalSearchList.value = historicalSearchList.value.slice(0, 10)
localStorage.setItem("historical-Search", JSON.stringify(historicalSearchList.value))
}
// 搜索点击事件
const searchClick = () => {
setHistoricalSearchList()
page = 1
list.value = []
backupsList = []
listHeight.value = 0
type.value = "list"
pitchIndex.value = null
openBottom()
myModelState.value = false
deleteState(["uniqid"])
replaceState({
keyword: keyword.value,
})
searchBlur()
getList()
}
// 搜索获取焦点
const searchFocus = () => {
if (historicalSearchList.value.length == 0) return
historicalSearchState.value = true
}
// 搜索失去焦点
const searchBlur = () => {
setTimeout(() => (historicalSearchState.value = false), 300)
}
// 点击历史记录 item
const handleClickHistoricalItem = value => {
keyword.value = value
searchClick()
}
// 点击清除搜索
const handleClickClear = () => {
keyword.value = ""
page = 1
list.value = []
backupsList = []
listHeight.value = 0
type.value = "list"
openBottom()
pitchIndex.value = null
getList()
deleteState(["keyword"])
}
let historicalSearchState = ref(false) // 历史记录弹窗状态
let historicalSearchList = ref([]) // 历史记录数据
let tabListFixeState = ref(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 && type.value == "list") getList()
// 列表下 滚动到顶部 触发类型的固定状态
if (scrollTop > 115) tabListFixeState.value = true
else tabListFixeState.value = false
}
let keyword = ref("") // 搜索的值
let keywordText = ref("") // 搜索的文本
let list = ref([]) // 列表数据
let backupsList = [] // 备份列表数据
let page = 1
let total = ref(0) // 回答总数
let loading = ref(false)
let inTheEndState = ref(false) // 到底了状态
let isListEmptyState = ref() // 列表是否为空的状态
let zeroreply = ref(0) // 只看0条回答状态
// 获取列表数据
const getList = () => {
if (page == 0 || loading.value) return
loading.value = true
$ajax("/api/lists", {
page,
limit: 20,
keyword: keyword.value,
type: typePitch.value,
zeroreply: zeroreply.value,
})
.then(res => {
if (res.code == 401) goLogin()
if (res.code != 200) return
let data = res.data
data.data.forEach(element => {
element["content"] = element["content"].replace(/<[^>]*>/g, "")
element["content"] = element["content"].replace(/&nbsp;/g, "")
})
list.value = list.value.concat(data.data || [])
backupsList = backupsList.concat(data.data || [])
total.value = data.count || 0
keywordText.value = keyword.value || ""
// page++
if (list.value.length >= data["count"]) page = 0
else page++
if (page == 0 && list.value.length != 0) inTheEndState.value = true
else inTheEndState.value = false
if (list.value.length == 0) isListEmptyState.value = true
else isListEmptyState.value = false
if (keyword.value) isSearchMode.value = true
else isSearchMode.value = false
if (type.value == "details") handleInsertRelatedlist()
if (list.value.length == 0) type.value = "list"
})
.finally(() => (loading.value = false))
}
let typeList = ref([])
let typePitch = ref(null)
// 获取分类数据 列表分类
const getListClass = () => {
$ajaxGET("/api/common/typeList").then(res => {
if (res.code != 200) return
let data = res.data
typeList.value = data
getPageHeight()
})
}
let pageHeaderHeight = ref(0) // 头部的高度
let pageListHeight = ref(0) // 底部列表的高度
const getPageHeight = () => {
let pageHeader = document.querySelector("#pageHeader")
pageHeaderHeight.value = pageHeader.clientHeight + 10
pageListHeight.value = window.innerHeight - pageHeaderHeight.value
}
let detailsInfo = ref({}) // 详情信息
let detailsIsanswered = ref(0) // 详情信息
let detailsIscollection = ref(0) // 详情信息
let detailsIsmyself = ref(0) // 详情信息
let detailsToken = "" // 详情信息
let detailShare = ref({}) // 详情信息
let detailLoading = ref(false) // 详情加载
// 获取详情
const getDetails = (uniqid, index, isOpenAnswer) => {
if (detailLoading.value) return
detailLoading.value = true
// process.env.NODE_ENV !== "production" && (uniqid = "fCSyLDDa0r1q") // 标记1一下 8yr1m1fOH5CS fubm5CnD05qj fCSyLDDa0r1q
detailsInfo.value = {}
answerList.value = []
answerPage.value = 0
$ajax("/api/details", { uniqid })
.then(res => {
if (res.code != 200) {
type.value = "list"
openBottom()
pitchIndex.value = null
return
}
let data = res.data
data["info"]["uniqid"] = uniqid
detailsInfo.value = data["info"] || {}
detailsIsanswered.value = data["isanswered"] || 0
detailsIscollection.value = data["iscollection"] || 0
detailsIsmyself.value = data["ismyself"] || 0
detailsToken = data["token"] || ""
detailShare.value = data["share"] || {}
type.value = "details"
closeBottom()
if (index !== null && index !== undefined) cut(index)
else calculateListIndex(data.info, uniqid)
answerList.value = []
answerPage.value = 1
getAnswerList()
closeAllTransmitState()
if (isOpenAnswer) openIAnswer()
// 非后退状态才可以 修改 url
if (!isBackState) replaceState({ uniqid })
isBackState = false
seo.value = data.seo
// 初始化 我来回答的框
yourAnswer.value = {
text: "",
anonymous: 0,
}
// 默认 详情 div 滚动到顶部
nextTick(() => detailsAreaScrollTop())
handleInsertRelatedlist(uniqid)
})
.finally(() => (detailLoading.value = false))
}
// 默认 详情 div 滚动到顶部 预防用户在 列表重复点击
const detailsAreaScrollTop = () => {
let detailsArea = document.querySelector(".details-area-box")
detailsArea.scrollTo({
top: 0,
behavior: "smooth",
})
}
const calculateListIndex = (info, uniqid) => {
let targetList = [...list.value]
if (targetList.length == 0 && isSearchMode.value == false && myModelState.value == false) {
setTimeout(() => calculateListIndex(info, uniqid), 200)
return
}
let valve = false
list.value.forEach((element, index) => {
if (element["uniqid"] == uniqid) {
cut(index)
pitchIndex.value = index
valve = true
}
})
if (!valve) {
let content = ""
if (info["content"].indexOf("<img") == -1) content = info["content"]
content = content.replace(/<[^>]*>/g, "")
content = content.replace(/&nbsp;/g, "")
const obj = {
answers: info["answers"],
content,
publicationdate: info["publicationdate"],
title: info["title"],
typename: info["typename"],
uniqid,
}
list.value.unshift(obj)
if (!myModelState.value) backupsList.unshift(obj)
cut(0)
}
}
let answerList = ref([]) // 回答列表数据
let answerPage = ref(1) // 回答列表页数
let answerLoading = false // 回答列表加载
// 获取详情的回答数据
const getAnswerList = () => {
if (answerLoading || answerPage.value == 0) return
answerLoading = true
$ajax("/api/details/answerList", {
token: detailsToken,
limit: 20,
page: answerPage.value,
})
.then(res => {
if (res.code != 200) return
let data = res.data
data.data.forEach(element => {
element["commentList"] = []
})
answerList.value = answerList.value.concat(data.data)
if (answerList.value.length == data["count"]) answerPage.value = 0
else answerPage.value++
detailsInfo.value["answers"] = data["count"]
if (pitchIndex.value !== null) list.value[pitchIndex.value]["answers"] = data["count"]
})
.finally(() => (answerLoading = false))
}
// 操作 - 点赞
const operateLike = (token, index) => {
if (isNeedLogin.value) {
goLogin()
return
}
$ajax("/api/operate/like", { token }).then(res => {
if (res.code != 200) return
let data = res.data
answerList.value[index]["islike"] = data["status"]
answerList.value[index]["likenum"] = data["count"]
handleMsg("success", res["message"] || "操作成功")
})
}
let isNeedNewColletData = false // 是否需要获取新的收藏数据 ,顶部的我的弹窗需要
// 操作 - 收藏
const operateCollect = (token = detailsToken, index) => {
if (isNeedLogin.value) {
goLogin()
return
}
$ajax("/api/operate/collect", {
token,
}).then(res => {
if (res.code != 200) return
let data = res.data
isNeedNewColletData = true
myCollectionPage = 1
myCollectionList.value = []
if (data["type"] == "askquestioncollection") {
detailsIscollection.value = data["status"]
detailsInfo.value["collectionnum"] = data["count"]
} else {
answerList.value[index]["iscollection"] = data["status"]
answerList.value[index]["collectionnum"] = data["count"]
}
handleMsg("success", res["message"] || "操作成功")
if (data["status"]) myCount.value["collect"]++
else myCount.value["collect"]--
})
}
let IAnswerState = ref(false) // 我来回答-弹窗的状态
let IAnswerEditState = ref(false) // 编辑回答-弹窗的状态
let IAnswerInfo = ref({}) // 我来回答-弹窗的信息
// 开启我来回答 type: my 表示
const openIAnswer = (index, type) => {
if (isNeedLogin.value) {
goLogin()
return
}
if (index == null) {
IAnswerInfo.value = {
title: detailsInfo.value["title"],
content: detailsInfo.value["content"],
anonymous: 0,
}
IAnswerState.value = true
nextTick(() => handleInput())
} else {
if (type == "my") {
IAnswerInfo.value = {
title: detailsInfo.value["title"],
...myAnswerList.value[index],
text: myAnswerList.value[index]["content"],
content: detailsInfo.value["content"],
}
myType.value = ""
} else {
IAnswerInfo.value = {
title: detailsInfo.value["title"],
...answerList.value[index],
text: answerList.value[index]["content"],
content: detailsInfo.value["content"],
}
}
IAnswerEditState.value = true
nextTick(() => handleInput())
}
}
// 关闭我来回答
const closeIAnswer = () => {
IAnswerState.value = false
IAnswerEditState.value = false
isDirectlyListIAnswer = false
}
// 修改我来回答的匿名状态
const amendIAnswer = () => {
IAnswerInfo.value["anonymous"] = IAnswerInfo.value["anonymous"] == 0 ? 1 : 0
}
// 提交回答
const submitAnswer = type => {
if (isNeedLogin.value) {
goLogin()
return
}
let questionTextarea = null
if (type == "you") questionTextarea = document.querySelector(".your-answer-textarea")
else questionTextarea = document.querySelector(".question-textarea")
if (questionTextarea) IAnswerInfo.value["text"] = questionTextarea.innerHTML
$ajax("/api/publish/answerSubmit", {
token: IAnswerInfo.value["token"] || detailsToken,
anonymous: IAnswerInfo.value["anonymous"] || 0,
content: IAnswerInfo.value["text"],
}).then(res => {
if (res.code != 200) return
if (isDirectlyListIAnswer) {
getDetails(IAnswerInfo.value["uniqid"], IAnswerInfo.value["index"])
IAnswerState.value = false
} else {
answerList.value = []
answerPage.value = 1
getAnswerList()
closeIAnswer()
if (!IAnswerInfo.value["token"]) myCount.value["answer"]++
if (type == "you") questionTextarea.innerHTML = ""
}
handleMsg("success", res["message"] || "操作成功")
})
}
// 打开回答的评论
const openCommentState = index => {
if (answerList.value[index]["commentState"]) answerList.value[index]["commentState"] = false
else answerList.value[index]["commentState"] = true
if (answerList.value[index]["commentList"].length == 0 && answerList.value[index]["commentnum"] != 0) getAnswerCommentList(index)
}
let answerCommentLimit = 3
// 获取回答评论的数据
const getAnswerCommentList = index => {
getAnswerCommentPublic(index, 3).then(res => {
let data = res.data
answerList.value[index]["commentList"] = answerList.value[index]["commentList"].concat(data.data)
answerList.value[index]["commentCount"] = data["count"]
})
}
// 获取全部评论
const handleAllComment = index => {
answerCommentLimit = 1000
getAnswerCommentPublic(index, 1000).then(res => {
if (res.code != 200) return
let data = res.data
let slice3 = data.data.slice(3)
let merged1 = [...answerList.value[index]["commentList"], ...slice3.filter(item2 => !answerList.value[index]["commentList"].find(item1 => item1.id == item2.id))]
answerList.value[index]["commentList"] = merged1
})
}
// 获取评论数据的公共接口
const getAnswerCommentPublic = (index, limit) => {
return new Promise((resolve, reject) => {
$ajax("/api/comment/lists", {
token: answerList.value[index]["token"],
limit,
childlimit: 1,
}).then(res => {
if (res.code != 200) return
resolve(res)
})
})
}
// 提交回答-评论
const submitAnswerComments = (index, ind, i) => {
if (isNeedLogin.value) {
goLogin()
return
}
const targetAnswerList = [...answerList.value]
let content = ""
let parentid = null
let token = targetAnswerList[index]["token"]
if (i != null) {
content = targetAnswerList[index]["commentList"][ind]["child"][i]["commentInput"]
parentid = targetAnswerList[index]["commentList"][ind]["child"][i]["id"]
} else if (ind != null) {
content = targetAnswerList[index]["commentList"][ind]["commentInput"]
parentid = targetAnswerList[index]["commentList"][ind]["id"]
} else content = targetAnswerList[index]["commentInput"]
$ajax("/api/comment/submit", {
content,
token,
parentid,
}).then(res => {
if (res.code != 200) return
let data = res.data
if (i != null) {
let targetData = {
id: data["commentid"],
content,
isauthor: 1,
islike: 0,
likenum: 0,
reply: {
nickname: targetAnswerList[index]["commentList"][ind]["child"][i]["nickname"],
},
...data,
}
targetAnswerList[index]["commentList"][ind]["child"][i]["commentInput"] = ""
targetAnswerList[index]["commentList"][ind]["child"].unshift(targetData)
targetAnswerList[index]["commentList"][ind]["childnum"]++
} else if (ind != null) {
let targetData = {
id: data["commentid"],
content,
isauthor: 1,
islike: 0,
likenum: 0,
reply: [],
...data,
}
targetAnswerList[index]["commentList"][ind]["child"].unshift(targetData)
targetAnswerList[index]["commentList"][ind]["childnum"]++
targetAnswerList[index]["commentList"][ind]["commentInput"] = ""
} else {
let targetData = {
id: data["commentid"],
content,
isauthor: 1,
islike: 0,
likenum: 0,
...data,
child: [],
}
targetAnswerList[index]["commentList"].unshift(targetData)
targetAnswerList[index]["commentCount"]++
targetAnswerList[index]["commentInput"] = ""
}
targetAnswerList[index]["commentnum"] = data["count"]
closeAnswerCommentsChild()
handleMsg("success", res["message"] || "操作成功")
})
}
// 回答-评论 点赞
const operateAnswerCommentsLike = (token, index, ind, i) => {
if (isNeedLogin.value) {
goLogin()
return
}
$ajax("/api/comment/like", {
token,
}).then(res => {
if (res.code != 200) return
let data = res.data
const targetAnswerList = [...answerList.value]
if (i == null) {
targetAnswerList[index]["commentList"][ind]["islike"] = data["status"]
targetAnswerList[index]["commentList"][ind]["likenum"] = data["likenum"]
} else {
targetAnswerList[index]["commentList"][ind]["child"][i]["islike"] = data["status"]
targetAnswerList[index]["commentList"][ind]["child"][i]["likenum"] = data["likenum"]
}
answerList.value = targetAnswerList
handleMsg("success", res["message"] || "操作成功")
})
}
// 打开 回答-评论 的子评论
const openAnswerCommentsChild = (index, ind, i) => {
if (isNeedLogin.value) {
goLogin()
return
}
closeAnswerCommentsChild()
if (i == null) answerList.value[index].commentList[ind]["childState"] = true
else answerList.value[index].commentList[ind]["child"][i]["childState"] = true
}
// 关闭 回答-评论 的子评论
const closeAnswerCommentsChild = (index, ind, i) => {
const targetAnswerList = [...answerList.value]
targetAnswerList.forEach(element => {
if (element["commentList"] && element["commentList"].length != 0) {
element["commentList"].forEach(ele => {
ele["childState"] = false
if (ele["child"] && ele["child"].length != 0) {
ele["child"].forEach(el => {
el["childState"] = false
})
}
})
}
})
answerList.value = targetAnswerList
}
// 获取剩下的子评论
const alsoCommentsData = (index, ind) => {
const targetAnswerList = [...answerList.value]
const parentid = targetAnswerList[index]["commentList"][ind]["id"]
const token = targetAnswerList[index]["token"]
$ajax("/api/comment/childrenList", {
token,
parentid,
limit: 20,
page: 1,
childlimit: 1,
}).then(res => {
if (res.code != 200) return
let data = res.data
let merged1 = [...targetAnswerList[index]["commentList"][ind]["child"], ...data.data.filter(item2 => !targetAnswerList[index]["commentList"][ind]["child"].find(item1 => item1.id == item2.id))]
targetAnswerList[index]["commentList"][ind]["child"] = merged1
answerList.value = targetAnswerList
})
}
let myType = ref("") // collect answers questions
// 专门处理 我的 弹窗数据
const handleMy = key => {
if (isNeedLogin.value) {
goLogin()
return
}
if (Object.keys(myCount.value).length === 0) {
getUserData(key)
return
}
if (key == "collect") {
myCollectionList.value = []
myCollectionPage = 1
getMyCollection()
} else if (key == "answers") {
myAnswerList.value = []
myAnswerPage = 1
getMyAnswer()
} else if (key == "questions") {
myQuestionsList.value = []
myQuestionsPage = 1
getMyQuestions()
}
}
let myCollectionList = ref([]) // 我的收藏列表
let myCollectionCount = ref(0) // 我的收藏数量
let myCollectionPage = 1 // 我的收藏页数
let myCollectionLading = false // 我的收藏加载中
// 获取我的收藏
const getMyCollection = () => {
if (myCollectionPage == 0 || myCollectionLading) return
myCollectionLading = true
$ajax("/api/user/collect", {
limit: 20,
page: myCollectionPage,
})
.then(res => {
if (res.code != 200) return
let data = res.data
myType.value = "collect"
isNeedNewColletData = false
data.data.forEach(element => {
if (element["type"] == "askanswercollection") {
let content = element["data"]["content"]
element["data"]["content"] = processHtml(content)
}
})
myCollectionList.value = myCollectionList.value.concat(data.data)
myCollectionCount.value = data.count
if (myCollectionList.value.length != data["count"]) myCollectionPage++
else myCollectionPage = 0
})
.finally(() => (myCollectionLading = false))
}
const processHtml = html => {
var parser = new DOMParser()
var doc = parser.parseFromString(html, "text/html")
var img = doc.querySelector("img")
if (img) return `<img src="${img.src}">`
else return doc.body.textContent
}
// 取消收藏
const cancelCollection = (token, index) => {
$ajax("/api/user/deleteCollect", { token }).then(res => {
if (res.code == 200) {
myCollectionList.value.splice(index, 1)
myCollectionCount.value--
myCount.value["collect"]--
handleMsg("success", res["message"] || "操作成功")
}
})
}
// 监听 我的收藏滚动到底部
const handleCollectionScroll = e => {
const el = e.target
if (el.scrollHeight - el.scrollTop >= el.clientHeight + 10) return
getMyCollection()
}
let myAnswerList = ref([]) // 我的回答数据
let myAnswerCount = ref(0) // 我的回答数量
let myAnswerPage = 1
let myAnswerloadimg = false
// 获取我的回答
const getMyAnswer = () => {
if (myAnswerPage == 0 || myAnswerloadimg) return
myAnswerloadimg = true
$ajax("/api/user/answer", {
limit: 20,
page: myAnswerPage,
})
.then(res => {
if (res.code != 200) return
let data = res.data
data.data.forEach(element => {
element["popupState"] = false
})
data.data.forEach(element => {
let content = element["content"]
element["content"] = processHtml(content)
})
myAnswerList.value = myAnswerList.value.concat(data.data)
myAnswerCount.value = data.count
if (myAnswerList.value.length != data["count"]) myAnswerPage++
else myAnswerPage = 0
myType.value = "answers"
})
.finally(() => (myAnswerloadimg = false))
}
// 我的回答 的滚动到底部事件
const handleAnswersScroll = e => {
const el = e.target
// 判断滚动到底部
if (el.scrollHeight - el.scrollTop >= el.clientHeight + 10) return
getMyAnswer()
}
let answerIndexOld = null
// 切换 我的提问的公开匿名 弹窗状态
const cutAnswerPopupState = index => {
myAnswerList.value[index]["popupState"] = true
if (answerIndexOld == index) {
myAnswerList.value[index]["popupState"] = false
answerIndexOld = null
} else {
myAnswerList.value[index]["popupState"] = true
if (answerIndexOld != null) myAnswerList.value[answerIndexOld]["popupState"] = false
answerIndexOld = index
}
}
// 更改匿名状态
const changeAnonymous = (token, anonymous, index) => {
$ajax("/api/publish/changeAnonymous", {
token,
anonymous,
}).then(res => {
if (res.code != 200) return
let data = res.data
myAnswerList.value[index]["anonymous"] = anonymous
cutAnswerPopupState(index)
handleMsg("success", res["message"] || "操作成功")
})
}
let myQuestionsList = ref([]) // 我的提问数据
let myQuestionsCount = ref(0) // 我的提问数量
let myQuestionsPage = 0 // 我的提问页数
let myQuestionsloading = false // 我的提问页数
// 获取我的提问
const getMyQuestions = () => {
if (myQuestionsPage == 0 || myQuestionsloading) return
myQuestionsloading = true
$ajax("/api/user/questions", {
limit: 20,
page: myQuestionsPage,
})
.then(res => {
if (res.code != 200) return
let data = res.data
myQuestionsList.value = myQuestionsList.value.concat(data.data)
myQuestionsCount.value = data.count
myType.value = "questions"
if (myQuestionsList.value.length != data["count"]) myQuestionsPage++
else myQuestionsPage = 0
})
.finally(() => (myQuestionsloading = false))
}
// 我的提问 的滚动到底部 事件
const handleQuestionsScroll = e => {
const el = e.target
// 判断滚动到底部
if (el.scrollHeight - el.scrollTop >= el.clientHeight + 10) return
getMyQuestions()
}
let questionsIndexOld = null
// 切换 我的提问的公开匿名 弹窗状态
const cutQuestionsPopupState = index => {
myQuestionsList.value[index]["popupState"] = true
if (questionsIndexOld == index) {
myQuestionsList.value[index]["popupState"] = false
questionsIndexOld = null
} else {
myQuestionsList.value[index]["popupState"] = true
if (questionsIndexOld != null) myQuestionsList.value[questionsIndexOld]["popupState"] = false
questionsIndexOld = index
}
}
// 更改匿名状态
const changeAnonymousQuestions = (token, anonymous, index) => {
$ajax("/api/publish/changeAnonymous", {
token,
anonymous,
}).then(res => {
if (res.code != 200) return
myQuestionsList.value[index]["anonymous"] = anonymous
cutQuestionsPopupState(index)
handleMsg("success", res["message"] || "操作成功")
})
}
let questionsSetp = ref(0) // 提问的步骤
// 切换提问步骤
const cutQuestionsSetp = value => (questionsSetp.value = value)
let questionsTypeList = ref([]) // 提问的类型主题
let questionsObj = ref({
// 提问的内容
token: "",
title: "",
content: "",
tags: "",
tid: "",
anonymous: 0,
})
// 报 提问的初始化
const questionsInit = () => {
if (isNeedLogin.value) {
goLogin()
return
}
deleteState(["ispublish"])
$ajax("/api/publish/questions").then(res => {
if (res.code != 200) return
let data = res.data
questionsObj.value["token"] = data["token"]
questionsTypeList.value = data["typeList"] || []
cutQuestionsSetp(1)
})
}
// 选择提问主题
const choosingTheme = id => {
questionsObj.value.tid = id
cutQuestionsSetp(2)
}
// 切换匿名状态
const cutAnonymous = () => {
questionsObj.value.anonymous = questionsObj.value.anonymous == 0 ? 1 : 0
}
// 发布问题
const postingIssue = () => {
if (isNeedLogin.value) {
goLogin()
return
}
$ajax("/api/publish/questionsSubmit", questionsObj.value).then(res => {
if (res.code == 200) {
myCount.value["questions"]++
questionsSetp.value = 0
questionsObj.value = {
token: "",
title: "",
content: "",
tags: "",
tid: "",
anonymous: 0,
}
handleMsg("success", res["message"] || "操作成功")
let data = res.data
getDetails(data["uniqid"])
return
}
handleMsg("error", res["message"] || "刷新重试!!!")
})
}
let pitchIndex = ref(null) // 选中的下标
// 切换 滚动列表
const cut = index => {
pitchIndex.value = index
setTimeout(() => scrollLeftInMiddle(), 350)
}
// 是否需要在 onMounted 滚动左边在中间
let isNeedScrollMiddle = false
// 滚动左边在中间
const scrollLeftInMiddle = () => {
if (typeof document === "undefined") {
isNeedScrollMiddle = true
return
}
let list = document.querySelector(".list-box")
let item = list.querySelector(`.item${pitchIndex.value}`)
// 136 分为 两个68一个是item高度的一半另一个是列表顶部的 数量
let top = item.offsetTop - pageListHeight.value / 2 + 136
list.scrollTo({
top,
behavior: "smooth",
})
isNeedScrollMiddle = false
}
const listStyle = () => {
const newtype = type.value
let width = ""
let margin = ""
let height = ""
if (newtype == "list") {
width = "1200px"
margin = "0 auto"
} else {
width = "calc((100vw - 1200px) / 2 + 512px)"
margin = "initial"
}
return {
width,
margin,
height,
}
}
let listHeight = ref(0)
let itemHeightLeft = 0
let itemHeightRight = 0
const itemStyle = (index, content, typetype) => {
if (index == 0) {
itemHeightLeft = 0
itemHeightRight = 0
}
const newtype = type.value
// let itemHeight = content == "" ? 107 : 128
let obj = {}
if (newtype == "list") {
// let top = Math.floor(index / 2) * 128 + "px"
// obj["top"] = Math.floor(index / 2) * 128 + "px"
// obj["top"] = Math.floor(index / 2) * 128 + "px"
if (itemHeightLeft <= itemHeightRight) {
obj["top"] = itemHeightLeft + "px"
itemHeightLeft += content == "" ? 107 : 137
obj["left"] = 0
} else {
obj["top"] = itemHeightRight + "px"
itemHeightRight += content == "" ? 107 : 137
obj["left"] = 649 + "px"
}
} else {
// obj["top"] = itemHeightLeft + "px";
obj["position"] = "relative"
// position: static;
if (!typetype) {
itemHeightLeft += content == "" ? 107 : 137
obj["height"] = content == "" ? 107 + "px" : 137 + "px"
} else {
itemHeightLeft += typetype == "vote" ? 170 : 137
obj["height"] = typetype == "vote" ? 170 + "px" : 137 + "px"
}
// obj["left"] = 0;
obj["width"] = "100%"
obj["paddingLeft"] = "calc((100vw - 1200px) / 2)"
}
// 通过最后一个 算 列表的高度
if (index + 1 == list.value.length) listHeight.value = Math.max(itemHeightLeft, itemHeightRight)
return obj
}
const bottomTpsStyle = (index, content) => {
const newtype = type.value
let obj = {}
if (newtype == "list") {
} else {
obj["width"] = "calc(50vw - 88px)"
obj["height"] = `calc(100vh - ${list.value.length * 128}px - 268px)`
obj["paddingLeft"] = "calc((100vw - 1200px) / 2)"
obj["borderRight"] = "1px solid #ebebeb"
obj["borderRight"] = "1px solid #ebebeb"
obj["background"] = "#fff"
}
return obj
}
// list-box
const listBoxStyle = () => {
const newtype = type.value
let obj = {}
if (newtype == "list") obj["overflow"] = "visible"
else obj["height"] = pageListHeight.value + "px"
return obj
}
// 处理时间
const handleDate = (dateTimeStamp = new Date()) => {
dateTimeStamp = dateTimeStamp ? dateTimeStamp : null
var timestamp = new Date(dateTimeStamp)
timestamp = timestamp.getTime()
var minute = 1000 * 60
var hour = minute * 60
var day = hour * 24
var now = new Date().getTime()
var diffValue = now - timestamp
var result
if (diffValue < 0) return
var dayC = diffValue / day
var hourC = diffValue / (hour + 1)
var minC = diffValue / minute
if (dayC >= 7) {
let date = new Date(timestamp)
let Y = date.getFullYear() + "-"
let M = (date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1) + "-"
let D = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " "
let h = (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":"
let m = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()
result = "" + Y + M + D + h + m
} else if (dayC >= 1) result = "" + Math.round(dayC) + "天前"
else if (hourC >= 1) result = "" + Math.round(hourC) + "小时前"
else if (minC >= 1) result = "" + Math.round(minC) + "分钟前"
else result = "刚刚"
return result
}
let questionsTransmitState = ref(false) // 问题的转发 弹窗状态
let questionsTransmitMaskState = ref(false) // 问题的转发 弹窗蒙版状态状态
// 关闭转发状态
const closeTransmitState = () => {
questionsTransmitState.value = true
questionsTransmitMaskState.value = true
countForwardingTimes(detailsToken)
}
// 关闭全部转发状态
const closeAllTransmitState = () => {
answerList.value.forEach(element => {
element["transmitState"] = false
})
questionsTransmitState.value = false
questionsTransmitMaskState.value = false
}
// 开启和关闭回答的转发状态 type open close
const handleAnswerTransmitList = (Iindex, type) => {
answerList.value[Iindex]["transmitState"] = true
questionsTransmitMaskState.value = true
countForwardingTimes(answerList.value[Iindex]["token"])
}
// 统计转发次数
const countForwardingTimes = token => {
process.env.NODE_ENV === "production" && $ajaxGET("/api/operate/share?token=" + token)
}
// 关闭详情模式
const closeDetailMode = () => {
deleteState(["uniqid"])
if (myModelState.value) {
closeMyModel()
return
}
type.value = "list"
openBottom()
pitchIndex.value = null
seo.value = {}
list.value = JSON.parse(JSON.stringify(backupsList))
}
// 全部的启动到底部
const handleListScroll = e => {
const el = e.target
if (el.scrollHeight - el.scrollTop >= el.clientHeight + 40 || myModelState.value) return
getList()
}
// 处理 回答弹窗的复制图片 type:you 为 您的答案
const handlePaste = (event, type) => {
const items = (event.clipboardData || event.originalEvent.clipboardData).items
for (const item of items) {
if (item.type.indexOf("image") === 0) {
// 如果包含图片,阻止默认行为
event.preventDefault()
handleMsg("warning", "上传图片中")
const file = item.getAsFile()
const reader = new FileReader()
reader.onload = e => {
const base64 = e.target.result
uploadImg(base64).then(res => {
let questionTextarea = null
if (type == "you") questionTextarea = document.querySelector(".your-answer-textarea")
else questionTextarea = document.querySelector(".question-textarea")
let imgNode = document.createElement("img")
imgNode.setAttribute("src", res.url)
imgNode.setAttribute("data-aid", res.aid)
questionTextarea.appendChild(imgNode)
if (type == "you") handleInputYou()
else handleInput()
handleMsg("success", "上传成功")
})
}
reader.readAsDataURL(file)
}
}
}
// 上传图片 获取图片url
const uploadImg = base64 => {
return new Promise((resolve, reject) => {
$ajax("/api/common/upload", {
data: base64,
}).then(res => {
if (res.code != 200) return
let data = res.data
resolve(data)
})
})
}
// 回答 的 placeholder 状态
let questionPlaceholderState = ref(false)
let yourAnswerPlaceholderState = ref(true) // 您的答案的 placeholder 状态
// 回答的输入事件 判断是否显示 placeholder 用
const handleInput = () => {
const questionTextarea = document.querySelector(".question-textarea")
const html = questionTextarea.innerHTML
if (html) questionPlaceholderState.value = false
else questionPlaceholderState.value = true
}
const handleInputYou = () => {
const questionTextarea = document.querySelector(".your-answer-textarea")
const html = questionTextarea.innerHTML
if (html) yourAnswerPlaceholderState.value = false
else yourAnswerPlaceholderState.value = true
}
let msg = ref({}) // 提示文本的对象 state 类型 type success warning error 显示状态 text 内容
// 调用 提示框方法
const handleMsg = (type, text) => {
msg.value["state"] = true
msg.value["type"] = type
msg.value["text"] = text
}
// 修改提示框类型
const boxClass = () => {
msgShowTimer()
return type ? `box-item-${msg.value["type"]}` : ""
}
let msgTimer = null
const msgShowTimer = () => {
clearTimeout(msgTimer)
msgTimer = setTimeout(() => {
msg.value["state"] = false
}, 1000)
}
// 复制
let copyText = text => {
if (navigator.clipboard) {
copyText = () => {
navigator.clipboard.writeText(text)
handleMsg("success", "复制成功")
}
} else {
copyText = () => {
var tempInput = document.createElement("input")
tempInput.value = text
document.body.appendChild(tempInput)
tempInput.select()
document.execCommand("copy")
document.body.removeChild(tempInput)
handleMsg("success", "复制成功")
}
}
copyText()
}
// 修改 url
const replaceState = (obj = {}) => {
if (typeof window === "undefined") return
// 获取当前URL参数
let params = new URLSearchParams(window.location.search)
for (const key in obj) {
params.set(key, obj[key])
}
// 替换当前URL但不刷新页面
if (window.location.pathname.indexOf("index.html") == -1) window.history.pushState({}, "", `${window.location.pathname}index.html?${params}`)
else window.history.pushState({}, "", `${window.location.pathname}?${params}`)
let location = window.location
let contentUrl = location.pathname + location.search
if (window._hmt) window._hmt.push(["_trackPageview", contentUrl])
// 用于发送某个URL的PV统计请求
if (window._czc) window._czc.push(["_trackPageview", contentUrl])
}
// 删除 url 参数的key
const deleteState = (keys = []) => {
if (typeof window === "undefined") return
let params = new URLSearchParams(window.location.search)
keys.forEach(key => {
params.delete(key)
})
window.history.pushState({}, "", `${window.location.pathname}?${params}`)
}
let myModelState = ref(false) // 我的模式状态
let myModelList = ref([]) // 我的模式列表
let temporaryData = {} // 临时存放
// 我的打开 详情
const myOpenDetails = uniqid => {
if (!uniqid) {
handleMsg("error", "没有找到相关提问")
return
}
deleteState(["keyword"])
if (!myModelState.value) {
temporaryData = {
total: total.value,
keywordText: keywordText.value,
isSearchMode: isSearchMode.value,
inTheEndState: inTheEndState.value,
type: type.value,
pitchIndex: pitchIndex.value,
listlist: JSON.parse(JSON.stringify(list.value)),
}
isSearchMode.value = false
inTheEndState.value = false
keyword.value = ""
list.value = JSON.parse(JSON.stringify(myModelList.value))
myModelState.value = true
pitchIndex.value = null
}
getDetails(uniqid)
myType.value = ""
}
// 关闭我的模式
const closeMyModel = () => {
myModelList.value = JSON.parse(JSON.stringify(list.value))
isSearchMode.value = temporaryData["isSearchMode"]
keywordText.value = temporaryData["keywordText"]
keyword.value = temporaryData["keywordText"]
total.value = temporaryData["total"]
type.value = temporaryData["type"]
pitchIndex.value = temporaryData["pitchIndex"]
inTheEndState.value = temporaryData["inTheEndState"]
list.value = JSON.parse(JSON.stringify(temporaryData.listlist))
myModelState.value = false
if (pitchIndex.value !== null && pitchIndex.value !== undefined) {
let uniqid = list.value[pitchIndex.value]["uniqid"]
getDetails(uniqid)
}
}
// 详情页滚动事件
const handleDetailsScroll = e => {
const el = e.target
// 判断滚动到底部
if (el.scrollHeight - el.scrollTop !== el.clientHeight) return
getAnswerList()
}
let dialogSrc = ref("") // 大图的src
// 处理点击答案图片 展开大图
const handleAnswerText = e => {
if (e.target.tagName === "IMG") {
var src = e.target.getAttribute("src")
dialogSrc.value = src
window.addEventListener("keydown", handleKeydown)
}
}
// 大图的监听 esc 键盘按钮
const handleKeydown = event => {
if (event.key !== "Escape") return
dialogSrc.value = ""
window.removeEventListener("keydown", handleKeydown) // 取消监听
}
// 切换顶部的 type
const cutType = id => {
typePitch.value = id
page = 1
list.value = []
backupsList = []
listHeight.value = 0
myModelState.value = false
type.value = "list"
openBottom()
pitchIndex.value = null
if (id != null) {
replaceState({
tid: id,
})
deleteState(["uniqid"])
} else deleteState(["tid", "uniqid"])
getList()
// 创新请求 设置列表的高度 为0
listHeight.value = 0
}
// 打开举报
const handleMenuState = (index, ind, i) => {
if (isNeedLogin.value) {
goLogin()
return
}
if (i === undefined) reportToken = answerList.value[index].commentList[ind]["token"]
else reportToken = answerList.value[index].commentList[ind]["child"][i]["token"]
alertShow.value = true
}
// 举报 token
let reportToken = ""
const reasonList = ["广告", "辱骂", "重复发送", "不良信息", "其他"]
let checkList = ref([])
let alertShow = ref(false)
let alertText = ref("")
const selectRadio = value => {
const index = checkList.value.indexOf(value)
if (index === -1) checkList.value.push(value)
else checkList.value.splice(index, 1)
}
// 举报提交
const alertSubmit = () => {
checkList.value.push(alertText.value)
$ajax("/api/operate/report", {
message: checkList.value,
token: reportToken,
}).then(res => {
checkList.value = []
reportToken = ""
alertShow.value = false
handleMsg("success", "举报成功")
})
}
onMounted(() => {
watchEffect(() => {
if (questionsSetp.value || myType.value || IAnswerEditState.value || IAnswerState.value || dialogSrc.value) document.body.style.overflow = "hidden"
else document.body.style.overflow = "auto"
})
})
let recommendList = [] // 推荐相关的数据
let recommendPage = 1
// 获取推荐数据
const getRecommend = uniqid => {
$ajaxGET("/api/details/relatedlist", {
page: recommendPage,
limit: 20,
}).then(res => {
if (res.code != 200) return
let obj = {
offer: "Offer",
mj: "面经",
vote: "投票",
thread: "帖子",
}
let data = res.data
// 替换 类名
data.forEach(element => {
element["typename"] = obj[element["type"]]
element["isrecom"] = true
})
recommendList = recommendList.concat(data)
recommendPage++
handleInsertRelatedlist(uniqid)
})
}
// 处理插入推荐数据
const handleInsertRelatedlist = uniqid => {
if (myModelState.value) return
// 计算需要插入元素的数量
let insertCount = Math.ceil(backupsList.length / 5)
// 检查数组B的长度是否足够
if (recommendList.length < insertCount) {
getRecommend(uniqid)
return
}
let target = JSON.parse(JSON.stringify(backupsList))
let result = []
let j = 0
for (let i = 0; i < target.length; i++) {
result.push(target[i])
if ((i + 1) % 4 === 0 && j < recommendList.length) {
result.push(recommendList[j])
j++
}
}
list.value = JSON.parse(JSON.stringify(result))
// 加入 推荐阅读后 重新计算下标 和 滚动
nextTick(() => {
if (uniqid) {
result.forEach((element, index) => {
if (element["uniqid"] == uniqid) {
pitchIndex.value = index
scrollLeftInMiddle()
}
})
}
})
}
const replaceNumberObj = {
0: "①",
1: "②",
2: "③",
}
let seo = ref({})
let initState = ref(0)
let arrr = []
try {
// 这是一个 等待的方法 如果复制后 会 多次等待
// await new Promise(resolve => setTimeout(resolve, 2000))
const params = route.query
if (params["keyword"]) keyword.value = params["keyword"]
if (params["tid"]) typePitch.value = params["tid"]
if (process.server && params["uniqid"]) {
await axios
.post(baseURL + "/api/details", { uniqid: params["uniqid"] })
.then(response => {
let res = response.data
let data = res.data
data["info"]["uniqid"] = params["uniqid"]
detailsInfoDataVuex.value = data
detailsInfo.value = data["info"] || {}
detailsIsanswered.value = data["isanswered"] || 0
detailsIscollection.value = data["iscollection"] || 0
detailsIsmyself.value = data["ismyself"] || 0
detailsToken = data["token"] || ""
detailShare.value = data["share"] || {}
type.value = "details"
// calculateListIndex(data.info, params["uniqid"]);
answerList.value = []
answerPage.value = 1
getAnswerList()
closeAllTransmitState()
replaceState({ uniqid: params["uniqid"] })
seo.value = data.seo
// handleInsertRelatedlist(params["uniqid"]);
})
.catch(error => console.error(error))
// if (process.server) {
// await $ajax("/api/details", {uniqid: params["uniqid"]})
// .then(res => {
// if (res.code != 200) return
// let data = res.data
// detailsInfo.value = data["info"] || {}
// detailsIsanswered.value = data["isanswered"] || 0
// detailsIscollection.value = data["iscollection"] || 0
// detailsIsmyself.value = data["ismyself"] || 0
// detailsToken = data["token"] || ""
// detailShare.value = data["share"] || {}
// type.value = "details"
// calculateListIndex(data.info, params["uniqid"])
// answerList.value = []
// answerPage.value = 1
// getAnswerList()
// closeAllTransmitState()
// replaceState({uniqid: params["uniqid"]})
// seo.value = data.seo
// })
// .finally(() => {
// detailLoading.value = false
// })
// }
}
await $ajax("/api/lists", {
page,
limit: 20,
keyword: keyword.value,
type: typePitch.value,
}).then(res => {
if (res.code != 200) return
let data = res.data
data.data.forEach(element => {
element["content"] = element["content"].replace(/<[^>]*>/g, "")
element["content"] = element["content"].replace(/&nbsp;/g, "")
})
list.value = data.data
backupsList = data.data
total.value = data.count || 0
keywordText.value = keyword.value || ""
if (list.value.length != data["count"]) page++
else page = 0
if (page == 0 && list.value.length != 0) inTheEndState.value = true
else inTheEndState.value = false
if (list.value.length == 0) isListEmptyState.value = true
else isListEmptyState.value = false
if (keyword.value) isSearchMode.value = true
else isSearchMode.value = false
nextTick(() => {
if (list.value.length == 0) type.value = "list"
})
// if (!process.server && params["uniqid"]) handleInsertRelatedlist()
})
await $ajaxGET("/api/details/relatedlist", { page: 1, limit: 20 }).then(res => {
if (res.code != 200) return
let obj = {
offer: "Offer",
mj: "面经",
vote: "投票",
thread: "帖子",
}
let data = res.data
// // 替换 类名
data.forEach(element => {
element["typename"] = obj[element["type"]]
element["isrecom"] = true
})
recommendList = recommendList.concat(data)
recommendPage++
if (params["uniqid"]) {
let target = JSON.parse(JSON.stringify(backupsList))
let result = []
let j = 0
for (let i = 0; i < target.length; i++) {
result.push(target[i])
if ((i + 1) % 4 === 0 && j < recommendList.length) {
result.push(recommendList[j])
j++
}
}
list.value = JSON.parse(JSON.stringify(result))
initState.value++
}
})
} catch (error) {
console.error(error)
}
// 这个是监听 初始化的 推荐接口 和 onmount 都运行完成后 滚动 item ,不让冲突
watch(initState, (newValue, oldValue) => {
if (newValue === 2) {
const params = route.query
calculateListIndex(detailsInfo.value, params["uniqid"])
}
})
const setItemUrl = uniqid => {
let url = `./index.html?uniqid=${uniqid}`
let query = route.query
for (const key in query) {
if (key != "uniqid") url += `&${key}=${query[key]}`
}
return url
}
// 处理点击 logo
const handleLogo = () => {
window.location.href = window.location.origin + window.location.pathname
}
// 您的答案
let yourAnswer = ref({
text: "",
anonymous: 0,
})
// 切换您的答案 匿名状态
const cutYourAnswerAnonymous = () => {
yourAnswer.value["anonymous"] = yourAnswer.value["anonymous"] ? 0 : 1
}
// 处理 您的答案的点击提交
const handleYourAnswer = () => {
if (isNeedLogin.value) {
goLogin()
return
}
IAnswerInfo.value = { ...yourAnswer.value }
submitAnswer("you")
yourAnswer.value = {
text: "",
anonymous: 0,
}
}
let isDirectlyListIAnswer = false // 是否是直接打开列表的我来回答
// 专门处理列表状态的 我来回答
const openListIAnswer = index => {
if (isNeedLogin.value) {
goLogin()
return
}
let targetData = list.value[index]
IAnswerInfo.value = {
title: targetData["title"],
content: targetData["content"],
token: targetData["token"],
uniqid: targetData["uniqid"],
anonymous: 0,
index,
}
isDirectlyListIAnswer = true
IAnswerState.value = true
nextTick(() => handleInput())
}
// 头像框的状态
let avatarState = ref(false)
// 开启头像框
const openUserInfo = (index, ind, i) => {
if (i != null) {
if (answerList.value[index].commentList[ind]["child"][i]["uin"] > 0) answerList.value[index].commentList[ind]["child"][i]["avatarState"] = !answerList.value[index].commentList[ind]["child"][i]["avatarState"]
} else if (ind != null) {
if (answerList.value[index].commentList[ind]["uin"] > 0) answerList.value[index].commentList[ind]["avatarState"] = !answerList.value[index].commentList[ind]["avatarState"]
} else if (index != null) {
if (answerList.value[index]["uin"] > 0) answerList.value[index]["avatarState"] = !answerList.value[index]["avatarState"]
} else {
if (detailsInfo.value["uin"] > 0) avatarState.value = !avatarState.value
}
}
// 点击发送信息
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()
}
// 详情模式下关闭底部
const closeBottom = () => {
if (process.server) return
const footer = document.querySelector("body .index-footer")
if (footer) footer.style.display = "none"
else setTimeout(() => closeBottom(), 1000)
}
const openBottom = () => {
const footer = document.querySelector("body .index-footer")
if (footer) footer.style.display = "block"
else setTimeout(() => openBottom(), 1000)
}
// 处理点击 只看
const handleLookOnly = () => {
zeroreply.value = zeroreply.value == 0 ? 1 : 0
page = 1
list.value = []
backupsList = []
listHeight.value = 0
getList()
}
return {
handleLookOnly,
zeroreply,
replaceNumberObj,
closeMyModel,
myModelList,
myModelState,
listHeight,
bottomTpsStyle,
TAHomePage,
sendMessage,
avatarState,
openUserInfo,
isNeedLogin,
handleInputYou,
openListIAnswer,
isListEmptyState,
cutYourAnswerAnonymous,
handleYourAnswer,
yourAnswer,
handleLogo,
inTheEndState,
setItemUrl,
seo,
originUrl,
handleMenuState,
reasonList,
checkList,
alertShow,
alertText,
selectRadio,
alertSubmit,
cutType,
dialogSrc,
answerPage,
handleDetailsScroll,
replaceState,
copyText,
boxClass,
questionPlaceholderState,
yourAnswerPlaceholderState,
handleInput,
handlePaste,
itemStyle,
listStyle,
listBoxStyle,
myType,
type,
pitchIndex,
cut,
list,
keyword,
keywordText,
getList,
total,
typeList,
typePitch,
getDetails,
detailsInfo,
detailsIsanswered,
detailsIscollection,
detailsIsmyself,
detailShare,
detailLoading,
answerList,
operateLike,
operateCollect,
IAnswerState,
IAnswerEditState,
IAnswerInfo,
amendIAnswer,
openIAnswer,
closeIAnswer,
submitAnswer,
openCommentState,
submitAnswerComments,
operateAnswerCommentsLike,
openAnswerCommentsChild,
closeAnswerCommentsChild,
alsoCommentsData,
handleAllComment,
myCollectionList,
myCollectionCount,
myQuestionsList,
myQuestionsCount,
myAnswerList,
myAnswerCount,
cutAnswerPopupState,
handleDate,
handleCollectionScroll,
handleAnswersScroll,
handleQuestionsScroll,
cancelCollection,
getMyCollection,
questionsSetp,
questionsObj,
cutAnonymous,
cutQuestionsSetp,
cutQuestionsPopupState,
questionsTypeList,
postingIssue,
choosingTheme,
handleMy,
changeAnonymous,
changeAnonymousQuestions,
pageHeaderHeight,
pageListHeight,
questionsTransmitState,
questionsTransmitMaskState,
closeAllTransmitState,
closeTransmitState,
handleAnswerTransmitList,
closeDetailMode,
tabListFixeState,
handleListScroll,
historicalSearchState,
historicalSearchList,
searchFocus,
searchBlur,
searchClick,
handleClickHistoricalItem,
handleClickClear,
isSearchMode,
questionsInit,
myCount,
msg,
myOpenDetails,
handleAnswerText,
getCurrentUrl,
loading,
}
},
}
</script>
<style>
@import url(./index.css);
</style>