feat(推荐/精华): 为推荐和精华标签添加点击跳转功能
- 修改item-head组件,将推荐和精华图标改为可点击的链接 - 新增best.js文件实现精华内容列表展示功能 - 推荐图标点击跳转至/recommend页面 - 精华图标点击跳转至/best页面
This commit is contained in:
@@ -167,5 +167,5 @@ export const itemHead = defineComponent({
|
|||||||
report,
|
report,
|
||||||
},
|
},
|
||||||
|
|
||||||
template: `<div class="item-head flexacenter" ref="itemHead"> <div class="user-box flexacenter" @click="goPersonalHomepage(item?.user?.uniqid)"> <img class="avatar" :src="item?.user?.avatar || item.avatar" /> <div class="name">{{ item?.user?.nickname || item.nickname || "匿名用户" }}</div> <img class="group" v-if="item.user?.groupimage" :src="item.user?.groupimage" /> </div> <div class="time">{{ timestamp }}</div> <div class="flex1"></div> <div class="circlePen flexcenter" @click="openedit(item.type)" v-if="page == 'edit' && (item.type == 'offer' || item.type == 'offer_summary')"> <img class="icon" :src="valueUrl + '/img/pen-icon.png'" /> </div> <div class="flexacenter" style="position: relative;"> <div class="anonymous-box flexcenter" @click.stop="cutAnonymous" v-if="page == 'edit' && (item.type == 'vote' || item.type == 'interviewexperience')"> <span v-if="item.anonymous == 0">公开</span> <span v-else>匿名</span> </div> <!-- 是否 公开发表 --> <template v-if="anonymousState"> <div class="mask" @click.stop="cutAnonymous"></div> <div class="isPublicityBox"> <div class="isPublicity-item" :class="{'green': item.anonymous == 0}" @click.stop="cutAnonymousState(0)">公开发表 <img v-if="item.anonymous == 0" class="isPublicityIcon" :src="valueUrl + '/img/u1829.svg'"></image> </div> <div class="isPublicity-item" :class="{'green': item.anonymous != 0}" @click.stop="cutAnonymousState(1)">匿名发表 <img v-if="item.anonymous != 0" class="isPublicityIcon" :src="valueUrl + '/img/u1829.svg'"></image> </div> </div> </template> </div> <div class="view flexacenter"> <img class="icon" :src="valueUrl + '/img/eye-icon.svg'" /> <div class="text">{{ item.views }}</div> </div> <div v-if="item.type != 'tenement'" class="btn flexcenter" @click.stop="cutShow"> <img class="icon" :src="valueUrl + '/img/dot-dot-dot-gray.png'" /> </div> <div v-if="show"> <div class="mask" @click.stop="cutShow"></div> <div class="operate"> <div class="item" @click.stop="report">举报</div> <template v-if="ismanager"> <div class="item" @click.stop="hide">{{ item.hidden == 0 ? "隐藏" : "显示" }}</div> <div class="item" @click.stop="recommend">{{ item.recommend == 1 ? "取消" : "" }}推荐</div> <div class="item" @click.stop="essence">{{ item.best == 1 ? "取消" : "" }}精华</div> </template> <template v-if="item.type == 'thread' && item.ismyself"> <div class="item" @click.stop="edit">编辑</div> <div class="item" @click.stop="deleteItem">删除</div> </template> <div class="item" v-if="page == 'edit' && item.type == 'vote'" @click.stop="deleteItem">删除</div> </div> </div></div><div class="label flexflex" v-if="sectionn?.length || tags?.length || item.recommend == 1 || item.best == 1"> <img class="item icon" v-if="item.recommend == 1" :src="valueUrl + '/img/recommend-icon.png'" /> <img class="item icon" v-if="item.best == 1" :src="valueUrl + '/img/essence-icon.png'" /> <a class="item blue" v-for="(item, index) in sectionn" :key="item" :href="'/section/' + item.uniqid" target="_blank">{{ item.name }}</a> <a class="item" v-for="(item, index) in tags" :key="item" :href="'/tag/' + item" target="_blank">{{ item }}</a></div><report v-if="reportState" :itemdata="item"></report>`,
|
template: `<div class="item-head flexacenter" ref="itemHead"> <div class="user-box flexacenter" @click="goPersonalHomepage(item?.user?.uniqid)"> <img class="avatar" :src="item?.user?.avatar || item.avatar" /> <div class="name">{{ item?.user?.nickname || item.nickname || "匿名用户" }}</div> <img class="group" v-if="item.user?.groupimage" :src="item.user?.groupimage" /> </div> <div class="time">{{ timestamp }}</div> <div class="flex1"></div> <div class="circlePen flexcenter" @click="openedit(item.type)" v-if="page == 'edit' && (item.type == 'offer' || item.type == 'offer_summary')"> <img class="icon" :src="valueUrl + '/img/pen-icon.png'" /> </div> <div class="flexacenter" style="position: relative;"> <div class="anonymous-box flexcenter" @click.stop="cutAnonymous" v-if="page == 'edit' && (item.type == 'vote' || item.type == 'interviewexperience')"> <span v-if="item.anonymous == 0">公开</span> <span v-else>匿名</span> </div> <!-- 是否 公开发表 --> <template v-if="anonymousState"> <div class="mask" @click.stop="cutAnonymous"></div> <div class="isPublicityBox"> <div class="isPublicity-item" :class="{'green': item.anonymous == 0}" @click.stop="cutAnonymousState(0)">公开发表 <img v-if="item.anonymous == 0" class="isPublicityIcon" :src="valueUrl + '/img/u1829.svg'"></image> </div> <div class="isPublicity-item" :class="{'green': item.anonymous != 0}" @click.stop="cutAnonymousState(1)">匿名发表 <img v-if="item.anonymous != 0" class="isPublicityIcon" :src="valueUrl + '/img/u1829.svg'"></image> </div> </div> </template> </div> <div class="view flexacenter"> <img class="icon" :src="valueUrl + '/img/eye-icon.svg'" /> <div class="text">{{ item.views }}</div> </div> <div v-if="item.type != 'tenement'" class="btn flexcenter" @click.stop="cutShow"> <img class="icon" :src="valueUrl + '/img/dot-dot-dot-gray.png'" /> </div> <div v-if="show"> <div class="mask" @click.stop="cutShow"></div> <div class="operate"> <div class="item" @click.stop="report">举报</div> <template v-if="ismanager"> <div class="item" @click.stop="hide">{{ item.hidden == 0 ? "隐藏" : "显示" }}</div> <div class="item" @click.stop="recommend">{{ item.recommend == 1 ? "取消" : "" }}推荐</div> <div class="item" @click.stop="essence">{{ item.best == 1 ? "取消" : "" }}精华</div> </template> <template v-if="item.type == 'thread' && item.ismyself"> <div class="item" @click.stop="edit">编辑</div> <div class="item" @click.stop="deleteItem">删除</div> </template> <div class="item" v-if="page == 'edit' && item.type == 'vote'" @click.stop="deleteItem">删除</div> </div> </div></div><div class="label flexflex" v-if="sectionn?.length || tags?.length || item.recommend == 1 || item.best == 1"> <a class="item icon" v-if="item.recommend == 1 && item.best != 1" target="_blank" href="/recommend"> <img style="height: 24px;" :src="valueUrl + '/img/recommend-icon.png'" /> </a> <a class="item icon" v-if="item.best == 1" target="_blank" href="/best"> <img style="height: 24px;" v-if="item.best == 1" :src="valueUrl + '/img/essence-icon.png'" /> </a> <a class="item blue" v-for="(item, index) in sectionn" :key="item" :href="'/section/' + item.uniqid" target="_blank">{{ item.name }}</a> <a class="item" v-for="(item, index) in tags" :key="item" :href="'/tag/' + item" target="_blank">{{ item }}</a></div><report v-if="reportState" :itemdata="item"></report>`,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -62,8 +62,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="label flexflex" v-if="sectionn?.length || tags?.length || item.recommend == 1 || item.best == 1">
|
<div class="label flexflex" v-if="sectionn?.length || tags?.length || item.recommend == 1 || item.best == 1">
|
||||||
<img class="item icon" v-if="item.recommend == 1" :src="valueUrl + '/img/recommend-icon.png'" />
|
<a class="item icon" v-if="item.recommend == 1 && item.best != 1" target="_blank" href="/recommend">
|
||||||
<img class="item icon" v-if="item.best == 1" :src="valueUrl + '/img/essence-icon.png'" />
|
<img style="height: 24px;" :src="valueUrl + '/img/recommend-icon.png'" />
|
||||||
|
</a>
|
||||||
|
<a class="item icon" v-if="item.best == 1" target="_blank" href="/best">
|
||||||
|
<img style="height: 24px;" v-if="item.best == 1" :src="valueUrl + '/img/essence-icon.png'" />
|
||||||
|
</a>
|
||||||
<a class="item blue" v-for="(item, index) in sectionn" :key="item" :href="'/section/' + item.uniqid" target="_blank">{{ item.name }}</a>
|
<a class="item blue" v-for="(item, index) in sectionn" :key="item" :href="'/section/' + item.uniqid" target="_blank">{{ item.name }}</a>
|
||||||
<a class="item" v-for="(item, index) in tags" :key="item" :href="'/tag/' + item" target="_blank">{{ item }}</a>
|
<a class="item" v-for="(item, index) in tags" :key="item" :href="'/tag/' + item" target="_blank">{{ item }}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
266
js/best.js
Normal file
266
js/best.js
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
const { createApp, ref, onMounted, nextTick, onUnmounted, computed, watch, provide } = Vue;
|
||||||
|
|
||||||
|
const { itemForum } = await import(withVer("../component/item-forum/item-forum.js"));
|
||||||
|
const { itemOffer } = await import(withVer("../component/item-offer/item-offer.js"));
|
||||||
|
const { itemSummary } = await import(withVer("../component/item-summary/item-summary.js"));
|
||||||
|
const { itemVote } = await import(withVer("../component/item-vote/item-vote.js"));
|
||||||
|
const { itemMj } = await import(withVer("../component/item-mj/item-mj.js"));
|
||||||
|
const { itemTenement } = await import(withVer("../component/item-tenement/item-tenement.js"));
|
||||||
|
const { headTop } = await import(withVer("../component/head-top/head-top.js"));
|
||||||
|
const { hotTag } = await import(withVer("../component/hot-tag/hot-tag.js"));
|
||||||
|
const { hotSearch } = await import(withVer("../component/hot-search/hot-search.js"));
|
||||||
|
const { slideshowBox } = await import(withVer("../component/slideshow-box/slideshow-box.js"));
|
||||||
|
const { latestList } = await import(withVer("../component/latest-list/latest-list.js"));
|
||||||
|
const { loadBox } = await import(withVer("../component/load-box/load-box.js"));
|
||||||
|
|
||||||
|
const appSearch = createApp({
|
||||||
|
setup() {
|
||||||
|
let tag = ref("");
|
||||||
|
let tagValue = ref(null);
|
||||||
|
let typeValue = ref(null);
|
||||||
|
onMounted(() => {
|
||||||
|
// const params = getUrlParams();
|
||||||
|
// tag.value = params.tag || "";
|
||||||
|
|
||||||
|
// const urlObj = new URL(location.href);
|
||||||
|
// const pathParts = urlObj.pathname.split("/").filter((part) => part);
|
||||||
|
// tag.value = pathParts.pop();
|
||||||
|
|
||||||
|
tag.value = tagValue.value.innerText;
|
||||||
|
const type = typeValue.value.innerText;
|
||||||
|
if (type) tabValue.value = type;
|
||||||
|
|
||||||
|
// init();
|
||||||
|
getList();
|
||||||
|
getUserInfoWin();
|
||||||
|
|
||||||
|
window.addEventListener("scroll", handleScroll);
|
||||||
|
|
||||||
|
const preLoader = document.getElementById("pre-loader");
|
||||||
|
if (preLoader) preLoader.style.display = "none";
|
||||||
|
});
|
||||||
|
|
||||||
|
let isLogin = ref(false);
|
||||||
|
let realname = ref(0); // 是否已经实名
|
||||||
|
let userInfoWin = ref({});
|
||||||
|
let permissions = ref([]);
|
||||||
|
|
||||||
|
const getUserInfoWin = () => {
|
||||||
|
const checkUser = () => {
|
||||||
|
const user = window.userInfoWin;
|
||||||
|
if (!user) return;
|
||||||
|
document.removeEventListener("getUser", checkUser);
|
||||||
|
realname.value = user.realname;
|
||||||
|
userInfoWin.value = user;
|
||||||
|
if (user?.uin > 0 || user?.uid > 0) isLogin.value = true;
|
||||||
|
permissions.value = user?.authority || [];
|
||||||
|
};
|
||||||
|
document.addEventListener("getUser", checkUser);
|
||||||
|
};
|
||||||
|
|
||||||
|
const openAttest = () => {
|
||||||
|
const handleAttestClose = () => {
|
||||||
|
document.removeEventListener("closeAttest", handleAttestClose);
|
||||||
|
realname.value = window.userInfoWin?.realname || 0;
|
||||||
|
};
|
||||||
|
// 启动认证流程时添加监听
|
||||||
|
document.addEventListener("closeAttest", handleAttestClose);
|
||||||
|
loadAttest(2);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转登录
|
||||||
|
const goLogin = () => {
|
||||||
|
if (typeof window === "undefined") return;
|
||||||
|
if (window["userInfoWin"] && Object.keys(window["userInfoWin"]).length !== 0) {
|
||||||
|
if (window["userInfoWin"]["uid"]) isLogin.value = true;
|
||||||
|
else ajax_login();
|
||||||
|
} else ajax_login();
|
||||||
|
};
|
||||||
|
|
||||||
|
provide("isLogin", isLogin);
|
||||||
|
provide("userInfoWin", userInfoWin);
|
||||||
|
provide("realname", realname);
|
||||||
|
provide("openAttest", openAttest);
|
||||||
|
provide("goLogin", goLogin);
|
||||||
|
|
||||||
|
const cutTab = (type) => {
|
||||||
|
if (!uniqid) return;
|
||||||
|
if (tabValue.value == type) return;
|
||||||
|
page.value = 1;
|
||||||
|
list.value = [];
|
||||||
|
count.value = 0;
|
||||||
|
tabValue.value = type;
|
||||||
|
pagination.value = [];
|
||||||
|
|
||||||
|
updateUrlParams({ type: type == "all" ? null : type });
|
||||||
|
|
||||||
|
getList();
|
||||||
|
};
|
||||||
|
|
||||||
|
let tabList = ref({
|
||||||
|
all: "全部",
|
||||||
|
thread: "论坛",
|
||||||
|
offer: "Offer",
|
||||||
|
offer_summary: "总结",
|
||||||
|
interviewexperience: "面经",
|
||||||
|
vote: "投票",
|
||||||
|
});
|
||||||
|
|
||||||
|
let tabValue = ref("all");
|
||||||
|
|
||||||
|
let uniqid = "";
|
||||||
|
const init = () => {
|
||||||
|
ajaxGet(`/v2/api/forum/tagDetails?name=${tag.value}`).then((res) => {
|
||||||
|
if (res.code != 200) {
|
||||||
|
creationAlertBox("error", res.message);
|
||||||
|
page.value = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = res.data;
|
||||||
|
uniqid = data.uniqid;
|
||||||
|
page.value = 1;
|
||||||
|
|
||||||
|
console.log("data", data);
|
||||||
|
|
||||||
|
getList();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let loading = ref(false);
|
||||||
|
let page = ref(1);
|
||||||
|
let maxPage = ref(0);
|
||||||
|
let count = ref(0);
|
||||||
|
let list = ref([]);
|
||||||
|
let pagination = ref([]);
|
||||||
|
const getList = () => {
|
||||||
|
console.log("page.value", page.value);
|
||||||
|
if (loading.value || page.value == 0) return;
|
||||||
|
loading.value = true;
|
||||||
|
// https://api.gter.net/v2/api/forum/topicLists?type=thread&page=1&limit=20&best=1
|
||||||
|
const limit = 20;
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
console.log("getList");
|
||||||
|
|
||||||
|
ajaxGet(`/v2/api/forum/topicLists?type=${tabValue.value == "all" ? "" : tabValue.value}&page=${page.value}&limit=${limit}&best=1`)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.code != 200) {
|
||||||
|
creationAlertBox("error", res.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = res.data;
|
||||||
|
list.value = data.data;
|
||||||
|
if (list.value.length == 0) page.value = null;
|
||||||
|
count.value = data.count;
|
||||||
|
loading.value = false;
|
||||||
|
maxPage.value = Math.ceil(count.value / limit);
|
||||||
|
pagination.value = calculatePagination(page.value, maxPage.value);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
err = err.data;
|
||||||
|
if (err.code == 401) goLogin();
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const calculatePagination = (currentPage, totalPages, visibleCount = 3) => {
|
||||||
|
// 处理特殊情况:总页数小于等于1时,无需显示分页
|
||||||
|
if (totalPages <= 1) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const pages = [];
|
||||||
|
// 始终显示第一页
|
||||||
|
pages.push(1);
|
||||||
|
|
||||||
|
// 计算中间需要显示的页码范围
|
||||||
|
let startPage = Math.max(2, currentPage - Math.floor(visibleCount / 2));
|
||||||
|
let endPage = Math.min(totalPages - 1, startPage + visibleCount - 1);
|
||||||
|
|
||||||
|
// 调整起始页码,确保显示足够数量的页码
|
||||||
|
startPage = Math.max(2, endPage - visibleCount + 1);
|
||||||
|
|
||||||
|
// 前面的省略号:如果第一页和起始页之间有间隔
|
||||||
|
if (startPage > 2) {
|
||||||
|
pages.push("...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加中间的页码
|
||||||
|
for (let i = startPage; i <= endPage; i++) {
|
||||||
|
pages.push(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 后面的省略号:如果最后一页和结束页之间有间隔
|
||||||
|
if (endPage < totalPages - 1) {
|
||||||
|
pages.push("...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 始终显示最后一页(如果总页数大于1)
|
||||||
|
if (totalPages > 1) {
|
||||||
|
pages.push(totalPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pages;
|
||||||
|
};
|
||||||
|
|
||||||
|
const cutPage = (value) => {
|
||||||
|
if (value == "...") return;
|
||||||
|
if (value == page.value) return;
|
||||||
|
page.value = value;
|
||||||
|
list.value = [];
|
||||||
|
getList();
|
||||||
|
};
|
||||||
|
|
||||||
|
const prevPage = () => {
|
||||||
|
page.value -= 1;
|
||||||
|
pagination.value = [];
|
||||||
|
list.value = [];
|
||||||
|
getList();
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextPage = () => {
|
||||||
|
page.value += 1;
|
||||||
|
pagination.value = [];
|
||||||
|
list.value = [];
|
||||||
|
getList();
|
||||||
|
};
|
||||||
|
|
||||||
|
const sidebarFixed = ref(false);
|
||||||
|
|
||||||
|
const handleScroll = () => {
|
||||||
|
// const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
|
||||||
|
// const clientHeight = window.innerHeight;
|
||||||
|
// // 侧边栏滚动固定
|
||||||
|
// if (scrollTop >= matterRef.value.offsetTop + sidebarRef.value.offsetHeight - clientHeight) sidebarFixed.value = true;
|
||||||
|
// else sidebarFixed.value = false;
|
||||||
|
|
||||||
|
matterHeight.value = -(contentRef.value.offsetHeight - window.innerHeight);
|
||||||
|
sidebarHeight.value = -(sidebarRef.value.offsetHeight - window.innerHeight);
|
||||||
|
|
||||||
|
if (matterHeight.value > 0) matterHeight.value = 12;
|
||||||
|
if (sidebarHeight.value > 0) sidebarHeight.value = 12;
|
||||||
|
};
|
||||||
|
|
||||||
|
const matterRef = ref(null);
|
||||||
|
const contentRef = ref(null);
|
||||||
|
const sidebarRef = ref(null);
|
||||||
|
|
||||||
|
let sidebarHeight = ref(0);
|
||||||
|
let matterHeight = ref(0);
|
||||||
|
|
||||||
|
return { sidebarHeight, matterHeight, sidebarFixed, contentRef, matterRef, sidebarRef, loading, tagValue, typeValue, maxPage, prevPage, nextPage, tag, tabValue, cutTab, tabList, count, list, page, pagination, cutPage };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
appSearch.component("item-forum", itemForum);
|
||||||
|
appSearch.component("itemOffer", itemOffer);
|
||||||
|
appSearch.component("itemSummary", itemSummary);
|
||||||
|
appSearch.component("itemVote", itemVote);
|
||||||
|
appSearch.component("itemMj", itemMj);
|
||||||
|
appSearch.component("itemTenement", itemTenement);
|
||||||
|
appSearch.component("head-top", headTop);
|
||||||
|
appSearch.component("hot-tag", hotTag);
|
||||||
|
appSearch.component("hot-search", hotSearch);
|
||||||
|
appSearch.component("slideshow-box", slideshowBox);
|
||||||
|
appSearch.component("latest-list", latestList);
|
||||||
|
appSearch.component("load-box", loadBox);
|
||||||
|
appSearch.mount("#search-tag");
|
||||||
Reference in New Issue
Block a user