Files
PC-Light-Forum/component/head-top/head-top.js
DESKTOP-RQ919RC\Pc 73731fbbba feat(签到组件): 重构签到功能并优化样式
- 添加签到组件到详情页
- 修改签到初始化逻辑,使用SignInComponent代替原有方法
- 优化签到弹窗样式和交互
- 移除调试用的console.log
- 更新资源路径为绝对路径
2025-11-25 13:59:12 +08:00

180 lines
9.6 KiB
JavaScript
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.

// my-component.js
// 引入全局 Vue 对象(因在 HTML 中通过 script 引入Vue 已挂载到 window
const { defineComponent, ref, onMounted, onUnmounted, nextTick } = Vue;
// 定义组件(直接使用模板)
export const headTop = defineComponent({
name: "headTop",
props: {
page: {
type: String,
default: "",
},
},
setup(props) {
// 轮播相关状态
let currentIndex = ref(0); // 当前显示的关键词索引
let carouselTimer = ref(null); // 轮播定时器
let isPaused = ref(false); // 是否暂停轮播
onMounted(() => {
getHistorySearch();
// 写一个函数 ,判断本地缓存有没有 wConfig 并判断 是否过一天 如果过了一天 则更新 wConfig
checkWConfig();
// 启动轮播
startCarousel();
});
// 启动轮播函数
const startCarousel = () => {
// 清除已有的定时器
if (carouselTimer.value) {
clearInterval(carouselTimer.value);
}
// 设置新的定时器,每秒滚动一次
carouselTimer.value = setInterval(() => {
if (!searchInputState.value && hotSearchWords.value.length > 1) {
// 当滚动到复制的数据部分时,进行无缝切换
if (currentIndex.value >= hotSearchWords.value.length - 1) {
// 先滚动到复制的数据
currentIndex.value++;
// 在下一帧,当动画完成后,立即切换回对应的数据索引位置,但不带动画
setTimeout(() => {
if (!searchInputState.value) {
currentIndex.value = 0;
// 强制重新渲染以重置位置
nextTick(() => {
// 继续正常滚动
});
}
}, 2300);
} else {
currentIndex.value++;
}
}
}, 2300);
};
// 暂停轮播
const pauseCarousel = () => {
isPaused.value = true;
};
// 恢复轮播
const resumeCarousel = () => {
isPaused.value = false;
};
// 组件卸载时清理定时器
const onUnmounted = () => {
if (carouselTimer.value) {
clearInterval(carouselTimer.value);
}
};
let hotSearchWords = ref([]);
const checkWConfig = () => {
const wConfig = JSON.parse(localStorage.getItem("wConfig")) || {};
if (wConfig.time) {
const time = new Date(wConfig.time);
const now = new Date();
if (now - time > 24 * 60 * 60 * 1000) getWConfig();
else {
hotSearchWords.value = wConfig.hotSearchWords || [];
}
} else {
getWConfig();
}
};
const getWConfig = () => {
ajaxGet("/v2/api/config/website").then((res) => {
if (res.code == 200) {
let data = res["data"] || {};
hotSearchWords.value = data.hotSearchWords || {};
data.time = new Date().toISOString();
localStorage.setItem("wConfig", JSON.stringify(data));
}
});
};
let state = ref(0); // 是否已经签到
let userInfoWinTimerCount = 0;
const userInfoWinTimer = setInterval(() => {
if (location.host == "127.0.0.1:5501") return;
if (todaysignedState) {
state.value = todaysigned;
clearInterval(userInfoWinTimer);
}
userInfoWinTimerCount++;
if (userInfoWinTimerCount >= 3000) clearInterval(userInfoWinTimer);
}, 50);
const signIn = () => {
SignInComponent.initComponent();
// ajax("/v2/api/forum/sign").then((res) => {
// if (res.code != 200) {
// creationAlertBox("error", res.message);
// return;
// }
// let data = res.data;
// state.value = 1;
// creationAlertBox("success", res.message || "签到成功");
// });
};
let pitchState = ref(false);
let page = ref(...props.page);
// console.log("page", page.value);
let input = ref("");
let historySearchList = ref([]); // 历史搜索数据
// 获取历史搜索
const getHistorySearch = () => {
const data = JSON.parse(localStorage.getItem("history-search")) || [];
historySearchList.value = data;
};
// 跳转搜索
const searchEvent = (value) => {
const kw = value || input.value || hotSearchWords.value[currentIndex.value]?.keyword || "";
if (!kw) return;
historySearchList.value.unshift(kw);
historySearchList.value = [...new Set(historySearchList.value)];
if (historySearchList.value.length > 10) historySearchList.value = historySearchList.value.splice(0, 10);
localStorage.setItem("history-search", JSON.stringify(historySearchList.value));
redirectToExternalWebsite("/search/" + kw);
searchInputBlur();
};
let searchHistoryShowState = ref(false); // 历史记录的展开状态
let searchInputState = ref(false); // 搜索框的状态
// 切换历史记录展示状态
const searchInputFocus = () => {
searchInputState.value = true;
if (historySearchList.value.length == 0) return;
searchHistoryShowState.value = true;
};
const searchInputBlur = () => {
setTimeout(() => {
searchInputState.value = false;
searchHistoryShowState.value = false;
}, 200);
};
return { hotSearchWords, historySearchList, searchEvent, searchInputState, searchHistoryShowState, searchInputFocus, searchInputBlur, page, pitchState, state, signIn, input, currentIndex, pauseCarousel, resumeCarousel };
},
template: `<div class="head-top flexacenter"> <a href="/" class="flexacenter" target="_blank"> <img class="logo" src="https://oss.gter.net/logo" alt="" /> </a> <div class="flex1"></div> <div class="input-box flexacenter" :class="{'pitch': searchInputState}"> <div class="placeholder" v-if="!searchInputState && !input"> <div class="placeholder-box" :style="{transform: 'translateY(-' + currentIndex * 36 + 'px)', transition: 'transform .3s ease'}"> <div class="item one-line-display" v-for="(item,index) in hotSearchWords" :key="index">大家都在搜:{{ item.keyword }}</div> <div class="item one-line-display" v-for="(item,index) in hotSearchWords.slice(0, 2)" :key="'copy-' + index">大家都在搜:{{ item.keyword }}</div> </div> </div> <input class="input flex1" type="text" @keyup.enter="searchEvent()" v-model="input" @focus="searchInputFocus" @blur="searchInputBlur" maxlength="140" /> <img class="icon" src="/img/search-icon.svg" @click="searchEvent()" /> <div class="search-box-history" v-if="searchHistoryShowState"> <div class="search-box-history-title">历史搜索</div> <div class="search-box-history-list"> <div class="search-box-history-item one-line-display" v-for="(item,index) in historySearchList " :key="index" @click="searchEvent(item)">{{ item }}</div> </div> </div> </div> <div class="post-list flexacenter" v-if="page == 'details'"> <a href="/publish" target="_blank" style="margin-right: 10px"> <img class="post-item" src="/img/post-thread.png" /> </a> <a href="https://offer.gter.net/post" target="_blank" style="margin-right: 10px"> <img class="post-item" src="/img/post-offer.png" /> </a> <a href="https://offer.gter.net/post/summary" target="_blank" style="margin-right: 10px"> <img class="post-item" src="/img/post-summary.png" /> </a> <a href="https://interviewexperience.gter.net/publish" target="_blank" style="margin-right: 10px"> <img class="post-item" src="/img/post-mj.png" /> </a> <a href="https://vote.gter.net/publish" target="_blank" style="margin-right: 10px"> <img class="post-item" src="/img/post-vote.png" /> </a> </div> <template v-else> <div class="sign-in sign-in-no flexacenter" v-if="state == 0" @click="signIn()" v-cloak> <img class="sign-in-bj" src="/img/sign-in-bj.svg" /> <img class="coin-bj" src="/img/coin-bj.svg" /> <img class="coin-icon" src="/img/coin-icon.png" /> <span class="text flex1">签到领寄托币</span> <div class="sign-go flexcenter"> <img class="sign-go-bj" src="/img/sign-go.svg" /> GO </div> <img class="petal1" src="/img/petal1.png" /> <img class="petal2" src="/img/petal2.png" /> <img class="petal3" src="/img/petal3.png" /> </div> <div class="sign-in sign-in-already flexcenter" v-else @click="signIn()"> <img class="sign-icon" src="/img/sign-icon.png" /> <span>已签到,明天再来</span> </div> </template></div>`,
});