优化结构,地图添加照片

This commit is contained in:
DelLevin-Home
2026-01-30 20:13:27 +08:00
parent 0c62a7982e
commit aa997dd772
25 changed files with 744 additions and 1521 deletions

View File

@@ -53,9 +53,7 @@
<div class="nav-tab" data-tab="website"><span data-i18n="nav_website"></span></div>
<div class="nav-tab" data-tab="tools"><span data-i18n="nav_tools"></span></div>
<div class="nav-tab" data-tab="games"><span data-i18n="nav_games"></span></div>
<div class="nav-tab" data-tab="footprint"><span data-i18n="nav_footprint"></span></div>
<div class="nav-tab" data-tab="memos"><span data-i18n="nav_memos"></span></div>
<div class="nav-tab" data-tab="contact"><span data-i18n="nav_contact"></span></div>
</div>
@@ -122,7 +120,9 @@
<div class="tab-content" id="website-content">
<div class="content-section">
<h2 data-i18n="my_website_h2"></h2>
<div class="website-links" id="dynamic-website-links"></div>
<div class="website-links" id="dynamic-website-links">
<div class="memo-loading" data-i18n="loading_website"></div>
</div>
</div>
</div>
@@ -130,9 +130,8 @@
<div class="tab-content" id="tools-content">
<div class="content-section">
<h2 data-i18n="my_tools_h2">我的工具</h2>
<div id="tools-container" class="tools-grid">
<div class="memo-loading" data-i18n="loading_tools">正在加载工具列表...</div>
<div class="memo-loading" data-i18n="loading_tools"></div>
</div>
</div>
</div>
@@ -142,7 +141,7 @@
<div class="content-section">
<h2 data-i18n="my_games_h2">我常玩的游戏</h2>
<div id="games-container" class="games-grid">
<div class="memo-loading" data-i18n="loading_games">正在加载游戏列表...</div>
<div class="memo-loading" data-i18n="loading_games"></div>
</div>
</div>
</div>
@@ -159,7 +158,7 @@
<div class="content-section">
<h2 data-i18n="memos_h2">闲言碎语</h2>
<div id="memos-container" class="memos-container">
<div class="memo-loading" data-i18n="loading_memos">正在加载...</div>
<div class="memo-loading" data-i18n="loading_memos"></div>
</div>
</div>
</div>
@@ -173,11 +172,11 @@
<div id="footer-wrap">
<div class="webinfo">
<div class="webinfo-item">
<div class="webinfo-site-uv-name" data-i18n="visitor_count_label">本站访客数 :</div>
<div class="webinfo-site-uv-name" data-i18n="visitor_count_label"></div>
<div class="webinfo-site-uv-count" id="site_uv">-</div>
</div>
<div class="webinfo-item">
<div class="webinfo-site-name" data-i18n="visit_count_label">本站总访问量 :</div>
<div class="webinfo-site-name" data-i18n="visit_count_label"></div>
<div class="webinfo-site-pv-count" id="site_pv">-</div>
</div>
</div>
@@ -190,8 +189,7 @@
<div id="modalOverlay"
style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 1000;cursor: pointer; ">
<div id="modalContent"
style="
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
background-color: white; padding: 20px; border-radius: 5px; box-shadow: 0 4px 8px rgba(0,0,0,0.2); text-align: center; cursor: default; ">
<p>博主是个宅男,还没去过哦~</p>
</div>
@@ -252,8 +250,8 @@
<script src="./static/js/anypage.js"></script>
<script src="./static/js/game_my.js"></script>
<script src="./static/js/tools_my.js"></script>
<script src="./static/js/websites_my.js"></script>
<script src="./static/js/memos.js"></script>
<script src="./static/js/my-websites.js"></script>
<script src="./static/js/map-footprint.js"></script>
<script src="./static/js/tabchange.js"></script>
</body>

View File

@@ -1,235 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="95后全干开发者">
<meta name="keywords" content="Java,Spring,个人博客微信小程序博客技术文章编程开发者白荼Del Levin">
<link rel="shortcut icon" href="./favicon.ico">
<script type="text/javascript">
if (!!window.ActiveXObject || "ActiveXObject" in window) { //is IE?
alert('朋友,上古浏览器不支持呢~');
}
</script>
<!-- 今日诗词 -->
<script src="https://sdk.jinrishici.com/v2/browser/jinrishici.js" charset="utf-8"></script>
<!-- 百度统计 -->
<script>
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?d82271fd27b69793bf97f088c0a87fd4";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<!-- umami统计 -->
<script defer src="https://umami.iletter.top/anyjs" data-website-id="ae6cd64c-5900-49c9-9c22-95cedc24a508"></script>
<!-- 多语言 -->
<script src="./static/js/language/zh.js"></script>
<script src="./static/js/language/en.js"></script>
<script src="./static/js/axios.min.js"></script>
<style>
@import url("./static/css/main.css");
@import url("./static/css/memos.css");
@import url("./static/css/game_dialog.css");
</style>
<title data-i18n="page_title">白荼 - BAITU</title>
</head>
<body>
<div class="letter-container">
<!-- 语言切换按钮 -->
<div class="lang-switch-container">
<img id="lang-switch-btn" src="./static/img/中文.png" alt="Switch Language" title="切换语言" />
</div>
<!-- 头部导航 -->
<div class="nav-tabs">
<div class="nav-tab active" data-tab="about"><span data-i18n="nav_about"></span></div>
<div class="nav-tab" data-tab="website"><span data-i18n="nav_website"></span></div>
<div class="nav-tab" data-tab="tools"><span data-i18n="nav_tools"></span></div>
<div class="nav-tab" data-tab="games"><span data-i18n="nav_games"></span></div>
<div class="nav-tab" data-tab="memos"><span data-i18n="nav_memos"></span></div>
<div class="nav-tab" data-tab="contact"><span data-i18n="nav_contact"></span></div>
</div>
<!-- 今日诗词 -->
<div id="jinrishici-sentence">正在加载今日诗词...</div>
<!-- 联系我内容 -->
<div class="tab-content" id="contact-content">
<div class="content-section">
<h2 data-i18n="contact_me_h2"></h2>
<p data-i18n="contact_intro"></p>
<div class="contact-info">
<div class="contact-item">
<span class="contact-label" data-i18n="email_label">邮箱:</span>
<span>dellevin99@gmail.com</span>
</div>
<div class="contact-item">
<span class="contact-label" data-i18n="wechat_label">微信:</span>
<span>E-Levin_</span>
</div>
<div class="contact-item">
<span class="contact-label" data-i18n="qq_label">扣扣:</span>
<span>1754084631</span>
</div>
<div class="contact-item">
<span class="contact-label" data-i18n="address_label">地址:</span>
<span data-i18n="address_value">山东省·济南市·市中区</span>
</div>
</div>
<h3 data-i18n="social_media_h3">社交媒体</h3>
<div class="website-links">
<a href="https://github.com/dellevin" target="_blank" data-i18n="social_link1"
data-i18n-attr="href">GitHub</a>
<a href="https://weibo.com/u/6094785109999999999" target="_blank" data-i18n="social_link2"
data-i18n-attr="href">微博</a>
<a href="https://www.zhihu.com/people/bing-xue-chen-xi-59" target="_blank" data-i18n="social_link3"
data-i18n-attr="href">知乎</a>
<a href="https://space.bilibili.com/403551212" target="_blank" data-i18n="social_link4"
data-i18n-attr="href">B站</a>
<a href="https://www.52pojie.cn/home.php?mod=space&uid=2059006" target="_blank"
data-i18n="social_link5" data-i18n-attr="href">52pojie</a>
</div>
<h3 data-i18n="leave_message_h3">留言</h3>
<p data-i18n="leave_message_p"><a href="https://blog.iletter.top/401.html" target="_blank"
data-i18n="leave_message_link" data-i18n-attr="href">点击此链接</a>去我的博客下面给我留言!</p>
</div>
</div>
<!-- 关于我内容 -->
<div class="tab-content active" id="about-content">
<div class="content-section about-me-content">
<h2 data-i18n="hello"></h2>
<p data-i18n="greeting"></p>
<p data-i18n="about_me_p1"></p>
<p data-i18n="about_me_p2"></p>
<p data-i18n="about_me_p3"></p>
<p data-i18n="about_me_p4"></p>
</div>
</div>
<!-- 我的网站内容 -->
<div class="tab-content" id="website-content">
<div class="content-section">
<h2 data-i18n="my_website_h2"></h2>
<div class="website-links" id="dynamic-website-links"></div>
</div>
</div>
<!-- 工具内容 -->
<div class="tab-content" id="tools-content">
<div class="content-section">
<h2 data-i18n="my_tools_h2">我的工具</h2>
<div id="tools-container" class="tools-grid">
<div class="memo-loading" data-i18n="loading_tools">正在加载工具列表...</div>
</div>
</div>
</div>
<!-- 游戏内容 -->
<div class="tab-content" id="games-content">
<div class="content-section">
<h2 data-i18n="my_games_h2">我常玩的游戏</h2>
<div id="games-container" class="games-grid">
<div class="memo-loading" data-i18n="loading_games">正在加载游戏列表...</div>
</div>
</div>
</div>
<!-- 闲言碎语内容 -->
<div class="tab-content" id="memos-content">
<div class="content-section">
<h2 data-i18n="memos_h2">闲言碎语</h2>
<div id="memos-container" class="memos-container">
<div class="memo-loading" data-i18n="loading_memos">正在加载...</div>
</div>
</div>
</div>
<div class="letter-footer">
<span data-i18n="footer_text"></span>
</div>
</div>
<!-- 底部版权信息和网站统计 - 在信纸外部 -->
<div id="footer-wrap">
<div class="webinfo">
<div class="webinfo-item">
<div class="webinfo-site-uv-name" data-i18n="visitor_count_label">本站访客数 :</div>
<div class="webinfo-site-uv-count" id="site_uv">-</div>
</div>
<div class="webinfo-item">
<div class="webinfo-site-name" data-i18n="visit_count_label">本站总访问量 :</div>
<div class="webinfo-site-pv-count" id="site_pv">-</div>
</div>
</div>
<div class="copyright" style="margin-top: 15px;"><a href="http://beian.miit.gov.cn"
target="_blank">冀ICP备2025122609号</a> &copy;<span>2024 - 2026</span> <span>Power BY</span> <span>Del Levin </span>
</div>
</div>
<script>
// ========== 语言切换逻辑 ==========
const translations = {
'zh': translationsZH,
'en': translationsEN
};
if (typeof translations === 'undefined') {
console.error('Translations file (translations.js) is not loaded!');
}
const switchButton = document.getElementById('lang-switch-btn');
const LANG_KEY = 'preferred_language';
function switchLanguage() {
let currentLang = localStorage.getItem(LANG_KEY) || 'zh';
let newLang = currentLang === 'zh' ? 'en' : 'zh';
localStorage.setItem(LANG_KEY, newLang);
updatePageContent(newLang);
updateLanguageIcon(newLang);
document.documentElement.lang = newLang;
location.reload();
}
function updatePageContent(lang) {
const elements = document.querySelectorAll('[data-i18n]');
elements.forEach(element => {
const key = element.getAttribute('data-i18n');
const translation = translations[lang][key];
if (translation !== undefined) {
element.innerHTML = translation;
} else {
console.warn(`Translation key '${key}' not found for language '${lang}'`);
}
});
}
function updateLanguageIcon(lang) {
if (lang === 'zh') {
switchButton.src = './static/img/中文.png';
switchButton.alt = 'Switch to English';
switchButton.title = '切换到英文';
} else {
switchButton.src = './static/img/英文.png';
switchButton.alt = 'Switch to Chinese';
switchButton.title = '切换到中文';
}
}
document.addEventListener('DOMContentLoaded', function () {
const savedLang = localStorage.getItem(LANG_KEY) || 'zh';
updatePageContent(savedLang);
updateLanguageIcon(savedLang);
document.documentElement.lang = savedLang;
switchButton.addEventListener('click', switchLanguage);
});
</script>
<script src="./static/js/anypage.js"></script>
<script src="./static/js/game_my.js"></script>
<script src="./static/js/tools_my.js"></script>
<script src="./static/js/memos.js"></script>
<script src="./static/js/my-websites.js"></script>
<script src="./static/js/tabchange.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 KiB

View File

@@ -1,67 +1,112 @@
// ========== 游戏配置数据 ==========
// ========== 游戏配置数据 (包含中英双语) ==========
const gamesData = [
{ id: 0, name: "《永劫无间》111", desc: "我身无拘,武道无穷" },
{ id: 1, name: "《我的世界》", desc: "这个小盒才是我的永远的家" },
{ id: 2, name: "《鬼谷八荒》", desc: "当互动版的修仙小说看了,很好玩." },
{
id: 0,
name_zh: "《永劫无间》",
desc_zh: "我身无拘,武道无穷",
name_en: "Naraka",
desc_en: "My body knows no bounds, martial arts are endless."
},
{
id: 1,
name_zh: "《我的世界》",
desc_zh: "这个小盒才是我的永远的家",
name_en: "Minecraft",
desc_en: "This little box is my forever home."
},
{
id: 2,
name_zh: "《鬼谷八荒》",
desc_zh: "当互动版的修仙小说看了,很好玩.",
name_en: "Gorogoa Bahuang",
desc_en: "Treat it as an interactive cultivation novel. Very fun."
},
{
id: 3,
name: "《江城创业记》",
desc: "缝合怪,但是全缝了。很喜欢的一款游戏",
name_zh: "《江城创业记》",
desc_zh: "缝合怪,但是全缝了。很喜欢的一款游戏",
name_en: "Jiangcheng Entrepreneurship Story",
desc_en: "A mashup, but it's fully integrated. A game I really like."
},
{
id: 4,
name: "《中国式家长》",
desc: "我承认我做不好一个孩子,也做不好一个家长",
name_zh: "《中国式家长》",
desc_zh: "我承认我做不好一个孩子,也做不好一个家长",
name_en: "Chinese Parents",
desc_en: "I admit I can't be a good child or a good parent."
},
{
id: 5,
name: "《王者荣耀》",
desc: "买了好多皮肤,和朋友玩才是真快乐(已退坑)",
name_zh: "《王者荣耀》",
desc_zh: "买了好多皮肤,和朋友玩才是真快乐(已退坑)",
name_en: "Honor of Kings",
desc_en: "Bought lots of skins. Real joy is playing with friends. (Quitted)"
},
{
id: 6,
name: "《QQ飞车手游》",
desc: "氪了好多,有些后悔了,太耗注意力了(已退坑)",
name_zh: "《QQ飞车手游》",
desc_zh: "氪了好多,有些后悔了,太耗注意力了(已退坑)",
name_en: "QQ Speed Mobile",
desc_en: "Spent a lot, somewhat regretful. Too attention-consuming. (Quitted)"
},
{
id: 7,
name_zh: "《龙族幻想》",
desc_zh: "因为龙族入坑的!(已退坑)",
name_en: "Dragon Raja Mobile",
desc_en: "Got into it because of Dragon Raja novels! (Quitted)"
},
{ id: 7, name: "《龙族幻想》", desc: "因为龙族入坑的,江南老贼!(已退坑)" },
{
id: 8,
name: "《洛克王国》",
desc: "还记得小时候拿压岁钱偷偷买点卡(已退坑)",
name_zh: "《洛克王国》",
desc_zh: "还记得小时候拿压岁钱偷偷买点卡(已退坑)",
name_en: "Roco Kingdom",
desc_en: "Still remember secretly buying cards with New Year money as a kid. (Quitted)"
},
{
id: 9,
name: "《造梦西游》",
desc: "造梦3从小学玩到大学现在还时不时的回味一下已退坑",
name_zh: "《造梦西游》",
desc_zh: "造梦3从小学玩到大学现在还时不时的回味一下已退坑",
name_en: "Zao Meng Xi You",
desc_en: "Zao Meng 3 from elementary to university. Still reminisce sometimes. (Quitted)"
},
{
id:10,
name:'《Street Fighter 6》',
desc: "街霸6超级炎炎舞outfit3是真好看。",
id: 10,
name_zh: '《街头霸王6》',
desc_zh: "街霸6超级炎炎舞outfit3是真好看。",
name_en: 'Street Fighter 6',
desc_en: "SF6, Super En'en Bu! Outfit 3 is truly nice-looking."
},
{
id:11,
name:'《WorldBox》',
desc: "世界盒子,无意间发现的一款游戏,可惜价格太贵了。",
id: 11,
name_zh: '《世界盒子》',
desc_zh: "世界盒子,无意间发现的一款游戏,可惜价格太贵了。",
name_en: 'WorldBox',
desc_en: "WorldBox, a game discovered by accident. Unfortunately, the price is too high."
},
{
id:12,
name:'《无人深空》',
desc: "难以想象的开放遨游!这次,我的目标是星辰大海!",
id: 12,
name_zh: '《无人深空》',
desc_zh: "难以想象的开放遨游!这次,我的目标是星辰大海!",
name_en: 'No Man\'s Sky',
desc_en: "Inconceivably open exploration! This time, my goal is the starry sea!"
},
{
id:13,
name:'《戴森球计划》',
desc: "没有蓝图玩的,把自己恶心坏了。主打一个自动化",
id: 13,
name_zh: '《戴森球计划》',
desc_zh: "没有蓝图玩的,把自己恶心坏了。主打一个自动化",
name_en: 'Dyson Sphere Program',
desc_en: "Played without blueprints, made myself sick. All about automation."
},
];
// ========== 游戏详情数据 ==========
// ========== 游戏详情数据 (包含中英双语) ==========
const gameDetailData = {
0: {
id: "游戏ID: 42956400140163",
rank: "<strong>历史最高段位:</strong>无相龙王",
detailDesc: "我希望能玩一辈子永劫无间!",
detailDesc_zh: "我希望能玩一辈子永劫无间!",
detailDesc_en: "I hope to play Naraka forever!",
images: [
"./static/img/game/yjwj/yjwj_chuanyun.png",
"./static/img/game/yjwj/yjwj_xiafeng.png",
@@ -69,9 +114,10 @@ const gameDetailData = {
],
},
1: {
id: "levin9992@qq.com",
id: "游戏idlevin9992@qq.com",
rank: "",
detailDesc: "我自己精神的自留地!我幻想中的乌托邦",
detailDesc_zh: "我自己精神的自留地!我幻想中的乌托邦",
detailDesc_en: "My spiritual private land! My imagined utopia",
images: [
"./static/img/game/wodeshijie/1.jpg",
"./static/img/game/wodeshijie/2.jpg",
@@ -81,7 +127,8 @@ const gameDetailData = {
2: {
id: "",
rank: "<strong>历史最高段位:</strong>登仙",
detailDesc: "好玩!爱玩!立绘非常好看!",
detailDesc_zh: "好玩!爱玩!立绘非常好看!",
detailDesc_en: "Fun! Love playing! The illustrations are very nice!",
images: [
"./static/img/game/guigubahuang/1.jpg",
"./static/img/game/guigubahuang/2.jpg",
@@ -91,7 +138,8 @@ const gameDetailData = {
6: {
id: "",
rank: "<strong>历史最高段位:</strong>传奇车神",
detailDesc: "巅峰竞速!可惜我的天行者被割韭菜了。。。",
detailDesc_zh: "巅峰竞速!可惜我的天行者被割韭菜了。。。",
detailDesc_en: "Peak racing! Pity my Sky Walker was scalped...",
images: [
"./static/img/game/qqspead/qqspead1.jpg",
"./static/img/game/qqspead/qqspead2.jpg",
@@ -99,8 +147,7 @@ const gameDetailData = {
},
};
let currentlyOpenTooltip = null; // 用于跟踪当前打开的tooltip
let currentlyOpenTooltip = null;
// ========== 显示/隐藏游戏详情的函数 (Tooltip 版本) ==========
function toggleGameDetailTooltip(gameElement, gameIndex) {
// 如果点击的是同一个游戏且tooltip是打开的则关闭它
@@ -128,15 +175,15 @@ function toggleGameDetailTooltip(gameElement, gameIndex) {
const detail = gameDetailData[gameIndex];
const currentLang = localStorage.getItem("preferred_language") || "zh";
const nameKey = `game_name_${gameIndex}`;
const descKey = `game_desc_${gameIndex}`;
const gameName =
typeof translations !== "undefined" && translations[currentLang]
? translations[currentLang][nameKey] || gamesData[gameIndex].name
: gamesData[gameIndex].name;
// 根据当前语言选择游戏名称和描述
const gameEntry = gamesData.find(game => game.id === gameIndex);
const gameName = currentLang === 'en' ? gameEntry?.name_en : gameEntry?.name_zh;
const gameDesc = currentLang === 'en' ? gameEntry?.desc_en : gameEntry?.desc_zh;
let tooltipContentHtml = "";
if (detail) {
// 根据当前语言选择详情描述
const detailDesc = currentLang === 'en' ? detail.detailDesc_en : detail.detailDesc_zh;
tooltipContentHtml = `
<div class="tooltip-content">
<div class="tooltip-header">
@@ -146,7 +193,7 @@ function toggleGameDetailTooltip(gameElement, gameIndex) {
<div class="tooltip-body">
<p><strong>${detail.id}</strong></p>
<p> ${detail.rank}</p>
<p><em>${detail.detailDesc}</em></p>
<p><em>${detailDesc}</em></p>
${
detail.images && detail.images.length > 0
? `<div class="tooltip-images">
@@ -158,6 +205,8 @@ function toggleGameDetailTooltip(gameElement, gameIndex) {
</div>
`;
} else {
// 使用根据语言变量生成的消息
const noDetailMsg = currentLang === 'en' ? 'The author has not written information yet~ Please wait patiently!' : '博主暂未书写信息~请耐心等待哦!';
tooltipContentHtml = `
<div class="tooltip-content">
<div class="tooltip-header">
@@ -165,7 +214,7 @@ function toggleGameDetailTooltip(gameElement, gameIndex) {
<span class="tooltip-close">&times;</span>
</div>
<div class="tooltip-body">
<p style="color: #999; font-style: italic;">博主暂未书写信息~请耐心等待哦!</p>
<p style="color: #999; font-style: italic;">${noDetailMsg}</p>
</div>
</div>
`;
@@ -201,9 +250,9 @@ function toggleGameDetailTooltip(gameElement, gameIndex) {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
// 计算tooltip的位置 (例如,出现在游戏项下方)
const top = rect.bottom + scrollTop + 10; // 10px 间隙
const left = rect.left + scrollLeft; // 与游戏项左侧对齐
// 计算tooltip的位置
const top = rect.bottom + scrollTop + 10;
const left = rect.left + scrollLeft;
tooltipDiv.style.top = `${top}px`;
tooltipDiv.style.left = `${left}px`;
@@ -248,32 +297,28 @@ function renderGames(gamesArray) {
return;
}
if (!gamesArray || gamesArray.length === 0) {
container.innerHTML =
'<div class="memo-error" data-i18n="no_games_data">暂无游戏数据</div>';
return;
// 检查数组是否为空或未定义/未初始化
const arrayToRender = gamesArray || gamesData;
if (!arrayToRender || !Array.isArray(arrayToRender) || arrayToRender.length === 0) {
const currentLang = localStorage.getItem("preferred_language") || "zh";
let noDataMessage = currentLang === 'en' ? 'No games data available.' : '暂无游戏数据';
container.innerHTML = `<div class="memo-error">${noDataMessage}</div>`;
return;
}
const currentLang = localStorage.getItem("preferred_language") || "zh";
let html = "";
gamesArray.forEach((game, index) => {
const nameKey = `game_name_${index}`;
const descKey = `game_desc_${index}`;
const nameTranslation =
typeof translations !== "undefined" && translations[currentLang]
? translations[currentLang][nameKey] || game.name
: game.name;
const descTranslation =
typeof translations !== "undefined" && translations[currentLang]
? translations[currentLang][descKey] || game.desc
: game.desc;
arrayToRender.forEach((game, index) => {
// 根据当前语言选择对应的名称和描述
const name = currentLang === 'en' ? game.name_en : game.name_zh;
const desc = currentLang === 'en' ? game.desc_en : game.desc_zh;
// 为每个游戏项添加点击事件,并传递其索引
html += `
<div class="game-item" data-game-index="${index}">
<div class="game-name">${nameTranslation}</div>
<div class="game-desc">${descTranslation}</div>
<div class="game-item" data-game-index="${game.id}"> <!-- 使用 game.id 而不是 index -->
<div class="game-name">${name}</div>
<div class="game-desc">${desc}</div>
</div>
`;
});
@@ -285,13 +330,13 @@ function renderGames(gamesArray) {
gameItems.forEach((item) => {
item.addEventListener("click", function (event) {
event.stopPropagation(); // 阻止点击事件冒泡到document避免立即关闭
const gameIndex = parseInt(this.getAttribute("data-game-index"));
const gameIndex = parseInt(this.getAttribute("data-game-index")); // 获取 game.id
toggleGameDetailTooltip(this, gameIndex);
});
});
}
// ========== 初始化 ==========
document.addEventListener("DOMContentLoaded", function () {
renderGames(gamesData);
});
// document.addEventListener("DOMContentLoaded", function () {
// renderGames();
// });

View File

@@ -1,144 +1,60 @@
const translationsEN = {
'footprint_map_h2': '足迹',// 添加这个
'page_title': 'BaiTu - BAITU',
'nav_about': 'About Me',
'nav_website': 'My Websites',
'nav_tools': 'Tools',
'nav_games': 'Games',
'nav_footprint':'Footprints',
'nav_memos': 'Memos',
'nav_contact': 'Contact Me',
'hello': 'Hello',
'greeting': '',
'my_name': 'BaiTu',
'about_me_p1': 'My name is <strong>BaiTu</strong>, from Hengshui, Hebei, a native northerner. I graduated from an obscure second-tier university with a major in Information Management and Information Systems. I self-studied development technologies like Java, Vue, Linux, MySQL, Python, and some software reverse engineering techniques. I also have basic skills in PS, PR, and AE. I have experience with systems, networking, PLCs, microcontrollers, server maintenance, and managing public accounts and websites. In short, I know a little bit about many things. Others might think I\'m an expert, but actually, I\'m just a rookie. However, I won\'t give up and will strive to become an expert. As Liu Lian from NARAKA said, "The most important skill in the world is perseverance."',
'about_me_p2': 'My hobbies usually involve playing games, reading books, watching anime, and I prefer staying indoors. I hate repetitive and simple work, so I often write small tools to make life easier because laziness drives my productivity. I frequently lurk on various forums and blogs, shouting "666" or "Amazing" under tech bloggers. My own blog updates sporadically, mainly recording daily life notes and technical challenges encountered.',
'about_me_p3': 'This site follows a minimalist style, using pure HTML. Umami tracks UV/PV at the bottom. The Memos section uses the Memos API. It also employs Baota Cloud WAF protection and nginx caching for static resources. If you are interested, you can directly download the source code from <a href="https://gitea.iletter.top/dellevin/BaiTu-homepage" target="_blank">https://gitea.iletter.top/dellevin/BaiTu-homepage </a> and use it.',
'about_me_p4': 'As a 99er programmer, I am glad to still have great passion for technology after starting work! If you share the same interest in technology, I would love to meet you! Let\'s progress and grow together!',
'my_website_h2': 'My Websites',
'personal_blog_h3': 'Personal Blogs',
'online_apps':'Online Apps',
'personal_blog_link1': 'ittoolman.top - Hosted on Github',
'personal_blog_link2': 'blog.iletter.top - Last Stand for Typecho Users',
'online_apps_h3': 'Online Applications',
'online_app_link1': 'EsayImage2 - Image Uploader Tool',
'online_app_link2': 'Gitea - Private Git Repository',
'online_app_link3': 'OpenList - Online Cloud Disk Collection',
'online_app_link4': 'Umami - Website visit analysis',
'online_app_link5': 'Beszel - Server Monitoring',
'contact_me_h2': 'Contact Me',
'contact_intro': 'Feel free to reach out via the following methods:',
'email_label': 'Email:',
'wechat_label': 'WeChat:',
'qq_label': 'QQ:',
'address_label': 'Address:',
'address_value': 'Shizhong District, Jinan, Shandong Province',
'social_media_h3': 'Social Media',
'social_link1': 'GitHub',
'social_link2': 'Weibo',
'social_link3': 'Zhihu',
'social_link4': 'Bilibili',
'social_link5': '52pojie',
'leave_message_h3': 'Leave a Message',
'leave_message_p': 'If you have any questions or suggestions, feel free to <a href="https://blog.iletter.top/401.html" target="_blank">click here</a> to leave me a message on my blog!',
'leave_message_link': 'click here',
'my_tools_h2': 'My Tools',
'loading_tools': 'Loading tools list...',
'my_games_h2': 'Games I Often Play',
'loading_games': 'Loading games list...',
'memos_h2': 'Memos',
'loading_memos': 'Loading...',
'footer_text': 'I still choose to spend the rest of my life in my own way although I am a failure.',
'visitor_count_label': 'Visitors:',
'visit_count_label': 'Total Visits:',
// --- 新增: 游戏翻译 (en) ---
'game_name_0': 'Naraka',
'game_desc_0': 'I am unrestricted, martial arts are boundless',
'game_name_1': 'Minecraft',
'game_desc_1': 'This little box is my forever home',
'game_name_2': 'Gates of Havoc',
'game_desc_2': 'Played like an interactive Xianxia novel, quite fun.',
'game_name_3': 'Jiangcheng Entrepreneurship Story',
'game_desc_3': 'A mashup game, but fully mashed. But a favorite game of mine.',
'game_name_4': 'Chinese Parents',
'game_desc_4': 'I admit I\'m not good at being a child, nor a parent.',
'game_name_5': 'Arena of Valor',
'game_desc_5': 'Bought many skins, real joy comes from playing with friends (quit now).',
'game_name_6': 'QQ Speed Mobile',
'game_desc_6': 'Spent a lot, now a bit regretful (quit now).',
'game_name_7': 'Dragon Raja',
'game_desc_7': 'Got into it because of the Dragon Raja series, spent a little (quit now).',
'game_name_8': 'Roco Kingdom',
'game_desc_8': 'Remember secretly buying cards with New Year money as a kid (quit now).',
'game_name_9': 'Dream Journey West',
'game_desc_9': 'Playing Dream Journey 3 from elementary to college, still enjoy it occasionally (quit now).',
// --- 新增: 工具翻译 (en) ---
'tool_name_0': 'Rolan',
'tool_desc_0': 'App launcher, used since college',
'tool_name_1': 'Chrome',
'tool_desc_1': 'Truly useful, but heading towards poop.',
'tool_name_2': 'VS Code',
'tool_desc_2': 'Code editor, lightweight? Efficient!',
'tool_name_3': 'IntelliJ IDEA',
'tool_desc_3': 'JAVA Code Editor',
'tool_name_4': 'PyCharm',
'tool_desc_4': 'Python Code Editor',
'tool_name_5': 'Android Studio',
'tool_desc_5': 'Android app development, always learning (create new folder).',
'tool_name_6': 'HbuilderX',
'tool_desc_6': 'Great for writing mini-programs, Vue+JS is awesome! Reduces learning pressure.',
'tool_name_7': 'Navicat',
'tool_desc_7': 'A神器 for connecting MySQL, what\'s SQLyog? Never heard of it.',
'tool_name_8': 'Xshell',
'tool_desc_8': 'Easy to connect to servers, works well with Xftp.',
'tool_name_9': 'Postman',
'tool_desc_9': 'API testing and debugging tool',
'tool_name_10': 'VMware',
'tool_desc_10': 'Virtual machine management, tool toxic? Try this first.',
'tool_name_11': 'Photoshop',
'tool_desc_11': 'PS image dedicated, masks and lasso are very useful.',
'tool_name_12': 'Obsidian',
'tool_desc_12': 'Knowledge management, bidirectional linking notes. A blessing for local note lovers.',
'tool_name_13': 'Typora',
'tool_desc_13': 'Markdown editor, simple and beautiful.',
'tool_name_14': 'Docker',
'tool_desc_14': 'Containerized deployment, docker-compose rules them all.',
'tool_name_15': 'Premiere Pro',
'tool_desc_15': 'Video editing, audio adjustment, simple effects, easy to use.',
'tool_name_16': 'After Effects',
'tool_desc_16': 'Special effects creation神器, but not friendly to GPU and CPU.',
'tool_name_17': 'Cheat Engine',
'tool_desc_17': 'Used it before Feng Ling Yue Ying came out, also a great choice for memory hooks.',
'tool_name_18': 'x64dbg',
'tool_desc_18': 'Kid! Want to master reverse power? Start with x64dbg!',
'tool_name_19': 'WireGuard',
'tool_desc_19': 'Networking神器, screw Sunflower, screw Todesk.',
'tool_name_20': 'Syncthing',
'tool_desc_20': 'File sync, super handy, great choice for syncing notes.',
'tool_name_21': 'NeatReader',
'tool_desc_21': 'Reading software, a bit regret spending 188 for lifetime.',
'tool_name_22': 'Corel VideoStudio',
'tool_desc_22': 'Evil Suzhou CorelMark! Give me back Pinnacle Studio.',
'tool_name_23': 'Netease Cloud Music',
'tool_desc_23': 'Listen and use daily, premium annual VIP + copyright unlock Unblock Netease.',
'tool_name_24': 'Ximalaya Audiobook',
'tool_desc_24': 'Listen and use daily. Great books like Three Body, Poor Man\'s Past and Future etc.',
'tool_name_25': 'A Diary',
'tool_desc_25': 'Who writes a diary anyway?!',
'tool_name_26': 'Ollama',
'tool_desc_26': 'Local AI deployment, fine-tuning models, 32b is too much for 4070.',
'tool_name_27': 'AnythingLLM',
'tool_desc_27': 'An excellent choice for AI knowledge base, better than my own fine-tuning + vectorization.',
'tool_name_28': 'UVR5',
'tool_desc_28': 'Audio processing神器, what hardships were separating audio back then!',
'tool_name_29': 'Remote Desktop Manager',
'tool_desc_29': 'Super powerful remote control software.',
'tool_name_30': 'SyncClipboard',
'tool_desc_30': 'Clipboard sync, handy, love it. No more copying/pasting via WeChat messages.',
'tool_name_31': 'QtScrcpy',
'tool_desc_31': 'LAN phone control tool, remote adb is awesome.',
'tool_name_32': 'RaiDrive',
'tool_desc_32': 'Cloud storage mounting, super easy to use',
}
page_title: "BaiTu - BAITU",
// nav选择
nav_about: "About Me",
nav_website: "My Websites",
nav_tools: "Tools",
nav_games: "Games",
nav_footprint: "Footprints",
nav_memos: "Memos",
nav_contact: "Contact Me",
// 描述
hello: "Hello",
greeting: "",
my_name: "BaiTu",
about_me_p1:
"My name is <strong>BaiTu</strong>, from Hengshui, Hebei, a native northerner. I graduated from an obscure second-tier university with a major in Information Management and Information Systems. I self-studied development technologies like Java, Vue, Linux, MySQL, Python, and some software reverse engineering techniques. I also have basic skills in PS, PR, and AE. I have experience with systems, networking, PLCs, microcontrollers, server maintenance, and managing public accounts and websites. In short, I know a little bit about many things. Others might think I'm an expert, but actually, I'm just a rookie. However, I won't give up and will strive to become an expert. As Liu Lian from NARAKA said, \"The most important skill in the world is perseverance.\"",
about_me_p2:
'My hobbies usually involve playing games, reading books, watching anime, and I prefer staying indoors. I hate repetitive and simple work, so I often write small tools to make life easier because laziness drives my productivity. I frequently lurk on various forums and blogs, shouting "666" or "Amazing" under tech bloggers. My own blog updates sporadically, mainly recording daily life notes and technical challenges encountered.',
about_me_p3:
'This site follows a minimalist style, using pure HTML. Umami tracks UV/PV at the bottom. The Memos section uses the Memos API. It also employs Baota Cloud WAF protection and nginx caching for static resources. If you are interested, you can directly download the source code from <a href="https://gitea.iletter.top/dellevin/BaiTu-homepage" target="_blank">https://gitea.iletter.top/dellevin/BaiTu-homepage </a> and use it.',
about_me_p4:
"As a 99er programmer, I am glad to still have great passion for technology after starting work! If you share the same interest in technology, I would love to meet you! Let's progress and grow together!",
// 联系方式
contact_intro: "Feel free to reach out via the following methods:",
email_label: "Email:",
wechat_label: "WeChat:",
qq_label: "QQ:",
address_label: "Address:",
address_value: "Shizhong District, Jinan, Shandong Province",
// 社交媒体
social_link1: "GitHub",
social_link2: "Weibo",
social_link3: "Zhihu",
social_link4: "Bilibili",
social_link5: "52pojie",
// 留言
leave_message_h3: "Leave a Message",
leave_message_p:
'If you have any questions or suggestions, feel free to <a href="https://blog.iletter.top/401.html" target="_blank">click here</a> to leave me a message on my blog!',
leave_message_link: "click here",
// 小界面标题
my_website_h2: "My Websites",
personal_blog_h3: "Personal Blogs",
contact_me_h2: "Contact Me",
my_tools_h2: "My Tools",
footprint_map_h2: "history footprint",
memos_h2: "Memos",
my_games_h2: "Games I Often Play",
social_media_h3: "Social Media",
// 加载状态
loading_tools: "Loading tools list...",
loading_games: "Loading games list...",
loading_memos: "Loading...",
loading_website:"Loading...",
// 底部
footer_text:
"I still choose to spend the rest of my life in my own way although I am a failure.",
visitor_count_label: "Visitors:",
visit_count_label: "Total Visits:",
};

View File

@@ -1,143 +1,60 @@
const translationsZH = {
'page_title': '白荼 - BAITU',
'nav_about': '关于我',
'nav_website': '我的网站',
'nav_tools': '常用工具',
'nav_games': '常玩游戏',
'nav_footprint':'历史足迹',
'nav_memos': '闲言碎语',
'nav_contact': '联系我',
'hello': '你好',
'greeting': '见字如面,展信舒颜。',
'my_name': '白荼',
'about_me_p1': '我叫<strong>白荼</strong>河北衡水人土生土长的北方汉子毕业于一所不知名的二本院校。信息管理与信息系统专业。自学java、vue、linux、mysql、python等开发技术和一些软件逆向技术同时还略懂些PS图片、PR视频、AE特效制作。做过系统剪过网线搞过plc耍过单片机维护修理过服务器运营过公众号和网站等等。可谓是会但只会一点点。别人都觉得我是个大佬其实我就是个小菜鸡。但我不会气馁努力成为一个大佬的。正如永劫无间刘炼的一句话“天下万般之绝学莫过于恒心。”',
'about_me_p2': '平时的爱好也就是玩玩游戏,看看书,刷刷动漫,不爱出门,肥宅一个。讨厌重复简单的工作,会想办法偷懒写写自己的顺手的小工具什么的,毕竟懒惰是我的生产力。我也经常混迹于各大网络论坛博客并在各个技术博主下面直呼"大佬666"、"大佬牛牛牛"。自己的博客也在断断续续中更新。主要是记录生活随笔和碰到的技术难题。',
'about_me_p3': '本站秉承简约风格采用纯HTMLUmami做网站底部的uv/pv 闲言碎语模块使用memos接口。同时使用堡塔云WAF防护nginx缓存加速网站静态资源。如果您有兴趣可以直接下载本站的 <a href="https://gitea.iletter.top/dellevin/BaiTu-homepage" target="_blank">https://gitea.iletter.top/dellevin/BaiTu-homepage </a> 并使用。',
'about_me_p4': '作为一个99年的码农很庆幸我在工作之后还对技术有着极大的热情如果你也对技术有着同样的兴趣也很希望认识你一同进步共同成长',
'my_website_h2': '我的网站',
'personal_blog_h3': '个人博客',
'online_apps':'在线应用',
'personal_blog_link1': 'ittoolman.top - Github托管',
'personal_blog_link2': 'blog.iletter.top - typecho用户的最后坚守',
'online_apps_h3': '在线应用',
'online_app_link1': '简单图床 - 私人图床工具',
'online_app_link2': 'Gitea - 私人git托管仓库',
'online_app_link3': 'OpenList - 在线云盘合集',
'online_app_link4': 'Umami - 网站访问分析',
'online_app_link5': 'Beszel - 服务器监控',
'contact_me_h2': '联系我',
'contact_intro': '欢迎通过以下方式与我联系:',
'email_label': '邮箱:',
'wechat_label': '微信:',
'qq_label': '扣扣:',
'address_label': '地址:',
'address_value': '山东省·济南市·市中区',
'social_media_h3': '社交媒体',
'social_link1': 'GitHub',
'social_link2': '微博',
'social_link3': '知乎',
'social_link4': 'B站',
'social_link5': '52pojie',
'leave_message_h3': '留言',
'leave_message_p': '如果你有任何问题或建议,欢迎<a href="https://blog.iletter.top/401.html" target="_blank">点击此链接</a>去我的博客下面给我留言!',
'leave_message_link': '点击此链接',
'my_tools_h2': '我的工具',
'loading_tools': '正在加载工具列表...',
'my_games_h2': '我常玩的游戏',
'loading_games': '正在加载游戏列表...',
'memos_h2': '闲言碎语',
'loading_memos': '正在加载...',
'footer_text': '我虽然是个废物,但我仍然选择用自己喜欢的方式度过自己的余生',
'visitor_count_label': '本站访客数 :',
'visit_count_label': '本站总访问量 :',
// --- 新增: 游戏翻译 (zh) ---
'game_name_0': '《永劫无间》',
'game_desc_0': '我身无拘,武道无穷',
'game_name_1': '《我的世界》',
'game_desc_1': '这个小盒才是我的永远的家。',
'game_name_2': '《鬼谷八荒》',
'game_desc_2': '立绘无敌,剧情无敌,非常不错的修仙游戏!',
'game_name_3': '《江城创业记》',
'game_desc_3': '自动化、闯关、结婚总有一款方式适合你。',
'game_name_4': '《中国式家长》',
'game_desc_4': '我承认我做不好一个孩子,也做不好一个家长。',
'game_name_5': '《王者荣耀》',
'game_desc_5': '和朋友玩才是真快乐,一个人的王者,一群人的荣耀(已退坑)',
'game_name_6': '《QQ飞车手游》',
'game_desc_6': '一辆天行者陪我从大学跑到工作(已退坑)',
'game_name_7': '《龙族幻想》',
'game_desc_7': '因为龙族入坑的,只能说剧情还不错!(已退坑)',
'game_name_8': '《洛克王国》',
'game_desc_8': '还记得小时候拿压岁钱偷偷买点卡,可惜号被盗了(已退坑)',
'game_name_9': '《造梦西游》',
'game_desc_9': '造梦3从小学玩到大学现在还时不时的回味一下已退坑',
// --- 新增: 工具翻译 (zh) ---
'tool_name_0': 'Rolan',
'tool_desc_0': '应用启动器,从大学用到现在',
'tool_name_1': 'Chrome',
'tool_desc_1': '确实好用,正在向着屎迈进',
'tool_name_2': 'VS Code',
'tool_desc_2': '代码编辑器,轻量?高效!',
'tool_name_3': 'IntelliJ IDEA',
'tool_desc_3': 'JAVA代码编辑器',
'tool_name_4': 'PyCharm',
'tool_desc_4': 'python代码编辑器',
'tool_name_5': 'Android Studio',
'tool_desc_5': '安卓软件开发,一直在学(新建文件夹)',
'tool_name_6': 'HbuilderX',
'tool_desc_6': '写小程序真心不错vue+js很棒减少了学习压力',
'tool_name_7': 'Navicate',
'tool_desc_7': '链接mysql的神器sqlyog是谁真不熟。',
'tool_name_8': 'Xshell',
'tool_desc_8': '链接服务器简单易用配合xftp很好用',
'tool_name_9': 'Postman',
'tool_desc_9': 'API测试与调试工具',
'tool_name_10': 'VMware',
'tool_desc_10': '虚拟机管理,工具有毒?先拿这个试试',
'tool_name_11': 'PhotoShop',
'tool_desc_11': 'ps图片专用蒙版套索十分好用',
'tool_name_12': 'Obsidian',
'tool_desc_12': '知识管理,双链接笔记。本地笔记爱好者的福音。',
'tool_name_13': 'Typora',
'tool_desc_13': 'Markdown编辑器简洁美观。',
'tool_name_14': 'Docker',
'tool_desc_14': '容器化部署一个docker-compose走天下',
'tool_name_15': 'Premiere Pro',
'tool_desc_15': '剪辑视频调音做简单的效果,简单易用',
'tool_name_16': 'After Effects',
'tool_desc_16': '特效制作神器就是对显卡和cpu比较不友好',
'tool_name_17': 'CheatEngine',
'tool_desc_17': '风灵月影没出来就用它亦是内存hook的极佳选择',
'tool_name_18': 'x64dbg',
'tool_desc_18': '孩子你想掌握逆向之力嘛先从x64dbg开始吧',
'tool_name_19': 'WireGuard',
'tool_desc_19': '组网神器去你妹的向日葵去你妹的todesk',
'tool_name_20': 'Syncthing',
'tool_desc_20': '文件同步,超级好用,同步笔记的不错选择。',
'tool_name_21': 'NeatReader',
'tool_desc_21': '看书软件有点后悔花了188买了永久',
'tool_name_22': 'Corel VideoStudio',
'tool_desc_22': '万恶的苏州思杰马克丁!还我会声会影',
'tool_name_23': '网易云音乐',
'tool_desc_23': '每天听每天用尊贵的年费vip+版权破解Unblock Netease',
'tool_name_24': '喜马拉雅听书',
'tool_desc_24': '每天听,每天用。三体,穷鬼的上下两千年等优秀书籍',
'tool_name_25': '一本日记',
'tool_desc_25': '正经人谁写日记啊!',
'tool_name_26': 'Ollama',
'tool_desc_26': '本地部署对话AI微调模型32b是4070遭不住',
'tool_name_27': 'AnythingLLM',
'tool_desc_27': 'AI知识库的极佳选择比我自己微调+向量化好',
'tool_name_28': 'UVR5',
'tool_desc_28': '处理音频的神器,想当年苦逼的分离音频算什么!',
'tool_name_29': 'Remote Desktop Manager',
'tool_desc_29': '超级强大的远控软件',
'tool_name_30': 'SyncClipboard',
'tool_desc_30': '剪切板同步,好用,爱用。再也不用微信消息复制粘贴了',
'tool_name_31': 'QtScrcpy',
'tool_desc_31': '局域网控制手机神器远程adb牛逼',
'tool_name_32': 'RaiDrive',
'tool_desc_32': '网盘挂载,超级好用',
}
page_title: "白荼 - BAITU",
// nav选择
nav_about: "关于我",
nav_website: "我的网站",
nav_tools: "常用工具",
nav_games: "常玩游戏",
nav_footprint: "历史足迹",
nav_memos: "闲言碎语",
nav_contact: "联系我",
// 描述
hello: "你好",
greeting: "见字如面,展信舒颜。",
my_name: "白荼",
about_me_p1:
"我叫<strong>白荼</strong>河北衡水人土生土长的北方汉子毕业于一所不知名的二本院校。信息管理与信息系统专业。自学java、vue、linux、mysql、python等开发技术和一些软件逆向技术同时还略懂些PS图片、PR视频、AE特效制作。做过系统剪过网线搞过plc耍过单片机维护修理过服务器运营过公众号和网站等等。可谓是会但只会一点点。别人都觉得我是个大佬其实我就是个小菜鸡。但我不会气馁努力成为一个大佬的。正如永劫无间刘炼的一句话“天下万般之绝学莫过于恒心。”",
about_me_p2:
'平时的爱好也就是玩玩游戏,看看书,刷刷动漫,不爱出门,肥宅一个。讨厌重复简单的工作,会想办法偷懒写写自己的顺手的小工具什么的,毕竟懒惰是我的生产力。我也经常混迹于各大网络论坛博客并在各个技术博主下面直呼"大佬666"、"大佬牛牛牛"。自己的博客也在断断续续中更新。主要是记录生活随笔和碰到的技术难题。',
about_me_p3:
'本站秉承简约风格采用纯HTMLUmami做网站底部的uv/pv 闲言碎语模块使用memos接口。同时使用堡塔云WAF防护nginx缓存加速网站静态资源。如果您有兴趣可以直接下载本站的 <a href="https://gitea.iletter.top/dellevin/BaiTu-homepage" target="_blank">https://gitea.iletter.top/dellevin/BaiTu-homepage </a> 并使用。',
about_me_p4:
"作为一个99年的码农很庆幸我在工作之后还对技术有着极大的热情如果你也对技术有着同样的兴趣也很希望认识你一同进步共同成长",
// 联系方式
contact_intro: "欢迎通过以下方式与我联系:",
email_label: "邮箱:",
wechat_label: "微信:",
qq_label: "扣扣:",
address_label: "地址:",
address_value: "山东省·济南市·市中区",
// 社交媒体
social_link1: "GitHub",
social_link2: "微博",
social_link3: "知乎",
social_link4: "B站",
social_link5: "52pojie",
// 留言
leave_message_h3: "留言",
leave_message_p:
'如果你有任何问题或建议,欢迎<a href="https://blog.iletter.top/401.html" target="_blank">点击此链接</a>去我的博客下面给我留言!',
leave_message_link: "点击此链接",
// 小界面标题
my_website_h2: "我的网站",
personal_blog_h3: "个人博客",
my_tools_h2: "我的工具",
my_games_h2: "我常玩的游戏",
memos_h2: "闲言碎语",
footprint_map_h2: "历史足迹",
social_media_h3: "社交媒体",
contact_me_h2: "联系我",
// 加载状态
loading_tools: "正在加载工具列表...",
loading_games: "正在加载游戏列表...",
loading_memos: "正在加载闲言碎语...",
loading_website:"正在加载网站...",
// 底部
footer_text: "我虽然是个废物,但我仍然选择用自己喜欢的方式度过自己的余生",
visitor_count_label: "本站访客数 :",
visit_count_label: "本站总访问量 :",
};

View File

@@ -1,220 +0,0 @@
/**
* ECharts 地图足迹模块 (使用本地 GeoJSON)
*/
const MapFootprintModule = (() => {
let myChart = null; // ECharts 实例
const MAP_NAME = 'china_custom'; // 为自定义地图起一个名字
const visitedPlaces = [
{ name: "济南市", value: [117.024961, 36.682788] },
{ name: "青岛市", value: [120.384423, 36.065918] },
{ name: "淄博市", value: [118.055915, 36.813547] },
{ name: "潍坊市", value: [119.162775, 36.705759] },
{ name: "北京市", value: [116.407395, 39.904211] },
{ name: "杭州市", value: [120.153576, 30.287459] },
{ name: "苏州市", value: [120.585316, 31.298886] },
{ name: "扬州市", value: [119.421003, 32.393159] },
{ name: "保定市", value: [115.482331, 38.867657] },
{ name: "石家庄市", value: [114.514891, 38.042309] },
{ name: "衡水市", value: [115.665996, 37.739574] },
{ name: "洛阳市", value: [112.454174, 34.618139] },
{ name: "南京市", value: [118.796877, 32.060255] },
{ name: "无锡市", value: [120.311987, 31.490920] },
{ name: "沧州市", value: [116.838581, 38.308094] },
{ name: "镇江市", value: [119.452753, 32.204402] },
];
/**
* 加载 GeoJSON 地图数据并注册
*/
const loadAndRegisterMap = async () => {
try {
// console.log('开始加载 GeoJSON 地图数据...');
const response = await fetch('./static/js/echarts/china.geojson'); // 确保路径正确
if (!response.ok) {
throw new Error(`加载 GeoJSON 失败: ${response.status} ${response.statusText}`);
}
const geoJsonData = await response.json();
// 注册自定义地图
echarts.registerMap(MAP_NAME, geoJsonData);
// console.log('GeoJSON 地图数据加载并注册成功。');
// 地图注册成功后,初始化图表
const container = document.getElementById(getMapContainerId());
if (container) {
initEChartsMap(container);
} else {
console.error('地图容器 DOM 元素未找到,无法初始化图表。');
}
} catch (error) {
console.error('加载或注册 GeoJSON 地图时出错:', error);
// 可以选择在内容区域显示错误信息
const contentDiv = document.getElementById('footprint-content'); // 假设足迹内容区域ID是这个
if (contentDiv) {
contentDiv.innerHTML = '<p>加载地图数据失败,请检查网络连接或稍后重试。</p>';
}
}
};
/**
* 初始化 ECharts 地图
* @param {HTMLElement} container - 图表容器DOM元素
*/
const initEChartsMap = (container) => {
if (!container) {
console.error('地图容器未找到');
return;
}
// 初始化 ECharts 实例
myChart = echarts.init(container);
// ECharts 配置项 (使用注册的地图名称)
const option = {
title: {
text: '',
subtext: '读万卷书,不如行万里路。见多才会识广~',
left: 'center',
textStyle: {
fontSize: 18
},
subtextStyle: {
fontSize: 12
}
},
tooltip: {
trigger: 'item',
formatter: function(params) {
if (params.value && params.value.length >= 2) {
return params.name + '<br/>经度: ' + params.value[0].toFixed(4) + '<br/>纬度: ' + params.value[1].toFixed(4);
}
return params.name; // 如果没有坐标信息,只显示地名
}
},
toolbox: {
show: false,
orient: 'vertical',
left: 'right',
top: 'center',
feature: {
dataView: { readOnly: false },
restore: {},
saveAsImage: {}
}
},
visualMap: {
show: false, // 是否显示 visualMap 组件,这里设为 false因为我们用 symbolSize 控制点大小
min: 0,
max: 100,
left: 'left',
top: 'bottom',
text: ['High', 'Low'], // 文本,默认为数值文本
calculable: true
},
geo: {
map: MAP_NAME, // 使用注册的自定义地图名称
roam: false, // 是否开启鼠标缩放和平移漫游
zoom: 1.1, // 当前视角的缩放比例
center: [104.06, 30],
emphasis: { // 高亮状态下的样式
itemStyle: {
areaColor: '#f0f0f0', // 高亮时省份的颜色
borderColor: '#409EFF', // 高亮时省份的边框颜色
borderWidth: 1
}
},
itemStyle: { // 普通状态下的样式
areaColor: '#eef2ff', // 未选中时省份的颜色
borderColor: '#333' // 未选中时省份的边框颜色
}
},
series: [
{
name: '足迹',
type: 'scatter', // 使用散点图表示标记点
coordinateSystem: 'geo', // 指定坐标系为地理坐标系
data: visitedPlaces, // 使用我们的数据
symbol: 'pin', // 使用内置符号 'pin'
symbolSize: 18, // 标记点的大小
itemStyle: { // 普通状态下的样式
color: '#FF6B6B', // 设置图标颜色
borderColor: '#fff', // 设置图标边框颜色
borderWidth: 1, // 设置图标边框宽度
},
emphasis: { // 高亮状态
itemStyle: {
color: '#dd4444' // 高亮时点的颜色
}
},
tooltip: {
formatter: function(params) {
// 这里可以更精细地控制 tooltip 内容
return ` ${params.data.name}<br/>坐标: [ ${params.data.value[0].toFixed(4)}, ${params.data.value[1].toFixed(4)}]`;
}
}
}
]
};
// 设置配置项并渲染图表
myChart.setOption(option);
// 监听窗口大小变化,自动适配
window.addEventListener('resize', function () {
if (myChart) {
myChart.resize();
}
});
};
/**
* 获取地图容器ID
* @returns {string}
*/
const getMapContainerId = () => {
return 'echarts-map-container'; // 定义一个唯一的ID
};
/**
* 渲染 ECharts 地图内容到指定容器
* @param {HTMLElement} contentDiv - 标签页的内容容器
*/
const renderMap = (contentDiv) => {
// 清空内容区域
// contentDiv.innerHTML = '';
// 创建地图容器
const mapDiv = document.createElement('div');
mapDiv.id = getMapContainerId();
mapDiv.style.width = '100%';
mapDiv.style.height = '600px';
mapDiv.style.marginTop = '20px';
contentDiv.appendChild(mapDiv);
// 加载 GeoJSON 并初始化地图
// 使用 setTimeout 确保 DOM 元素已渲染后再加载地图数据
setTimeout(() => {
loadAndRegisterMap();
}, 100); // 延迟一点确保渲染完成
};
/**
* 销毁 ECharts 实例 (可选,用于性能优化或切换时清理)
*/
const destroyMap = () => {
if (myChart) {
myChart.dispose(); // 销毁实例,释放资源
myChart = null;
console.log('ECharts 地图已销毁');
}
};
// 返回公共方法
return {
renderMap,
destroyMap // 导出销毁方法,如果需要的话
};
})();

View File

@@ -1,407 +0,0 @@
/**
* ECharts 地图足迹模块 (支持钻取功能 - 点击省份查看下一级, 点击按钮返回)
*/
const MapFootprintModule = (() => {
let myChart = null; // ECharts 实例
let currentMapLevel = 'country'; // 当前地图层级 ('country', 'province')
let currentProvinceCode = null; // 当前显示的省份编码 (如果有的话)
// --- 配置 ---
const COUNTRY_MAP_NAME = 'china_custom';
const COUNTRY_GEOJSON_PATH = './static/js/echarts/china.geojson';
const PROVINCE_GEOJSON_BASE_PATH = './static/js/echarts/province/'; // 假设省份 GeoJSON 存放在这个目录
// --- 数据 ---
// 注意:这里的坐标可能需要根据你实际访问的城市进行微调
const visitedPlaces = [
{ name: "济南市", value: [117.024961, 36.682788], province: "山东省" },
{ name: "青岛市", value: [120.384423, 36.065918], province: "山东省" },
{ name: "淄博市", value: [118.055915, 36.813547], province: "山东省" },
{ name: "潍坊市", value: [119.162775, 36.705759], province: "山东省" },
{ name: "北京市", value: [116.407395, 39.904211], province: "北京" },
{ name: "杭州市", value: [120.153576, 30.287459], province: "浙江" },
{ name: "苏州市", value: [120.585316, 31.298886], province: "江苏" },
{ name: "扬州市", value: [119.421003, 32.393159], province: "江苏" },
{ name: "保定市", value: [115.482331, 38.867657], province: "河北" },
{ name: "石家庄市", value: [114.514891, 38.042309], province: "河北" },
{ name: "衡水市", value: [115.665996, 37.739574], province: "河北" },
{ name: "洛阳市", value: [112.454174, 34.618139], province: "河南" },
{ name: "南京市", value: [118.796877, 32.060255], province: "江苏" },
{ name: "无锡市", value: [120.311987, 31.490920], province: "江苏" },
{ name: "沧州市", value: [116.838581, 38.308094], province: "河北" },
{ name: "镇江市", value: [119.452753, 32.204402], province: "江苏" },
];
// 省份名称到其 GeoJSON 文件名的映射 (需要根据你的文件名调整)
const provinceNameToGeoFile = {
"山东省": "shandong.json",
// 你可以继续添加其他省份的映射,例如:
// "江苏省": "jiangsu.json",
// "河北省": "hebei.json",
// ...
};
// --- 工具函数 ---
/**
* 根据省份中文名获取其对应的 GeoJSON 文件完整路径
* @param {string} provinceName - 省份中文名
* @returns {string|null} - 文件路径或 null
*/
const getProvinceGeoPath = (provinceName) => {
const fileName = provinceNameToGeoFile[provinceName];
if (fileName) {
return ` ${PROVINCE_GEOJSON_BASE_PATH}${fileName}`;
}
console.warn(`未找到省份 " ${provinceName}" 对应的 GeoJSON 文件。`);
return null;
};
/**
* 根据地图层级和可选的省份编码生成 ECharts 配置
* @param {string} level - 地图层级 ('country' or 'province')
* @param {string} [provinceCode] - 省份编码 (当 level='province' 时使用)
* @returns {Object} - ECharts 配置对象
*/
const getEChartsOption = (level, provinceCode = null) => {
let geoMapName, roamSetting, zoomLevel, centerCoord, titleText;
let filteredVisitedPlaces = []; // 用于过滤足迹点
if (level === 'country') {
geoMapName = COUNTRY_MAP_NAME;
roamSetting = false; // 启用缩放和平移
zoomLevel = 1.1;
centerCoord = [104.06, 30]; // 中国中心
titleText = '';
filteredVisitedPlaces = visitedPlaces; // 显示所有足迹
} else if (level === 'province' && provinceCode) {
geoMapName = `province_ ${provinceCode}`; // 为省份地图创建唯一名称
roamSetting = false; // 省份地图也启用缩放平移
zoomLevel = 1.0; // 可以根据需要调整
centerCoord = [118.5, 36.5]; // 示例:山东中心
titleText = ` ${provinceCode} - 我的足迹`;
// 过滤出属于当前省份的足迹点
filteredVisitedPlaces = visitedPlaces.filter(place => place.province === provinceCode);
} else {
console.error("无效的地图层级或省份代码");
return {}; // 返回空配置
}
return {
title: {
text: titleText,
// subtext: level === 'country' ? '点击省份进入详情' : '点击下方返回按钮返回全国地图',
subtext: '读万卷书,不如行万里路。见多才会识广~',
left: 'center',
textStyle: {
fontSize: 18
},
subtextStyle: {
fontSize: 12
}
},
tooltip: {
trigger: 'item',
formatter: function(params) {
if (params.componentType === 'series' && params.seriesType === 'scatter') {
// 足迹点的 tooltip
if (params.value && params.value.length >= 2) {
return params.name + '<br/>经度: ' + params.value[0].toFixed(4) + '<br/>纬度: ' + params.value[1].toFixed(4);
}
return params.name;
} else if (params.componentType === 'geo') {
// 地图区域的 tooltip
return params.name;
}
return params.name;
}
},
geo: {
map: geoMapName,
roam: roamSetting,
zoom: zoomLevel,
center: centerCoord,
emphasis: {
itemStyle: {
areaColor: '#f0f0f0',
borderColor: '#409EFF',
borderWidth: 1
}
},
itemStyle: {
areaColor: '#eef2ff',
borderColor: '#333'
}
},
series: [
{
name: '足迹',
type: 'scatter',
coordinateSystem: 'geo',
data: filteredVisitedPlaces,
symbol: 'pin',
symbolSize: 18,
itemStyle: {
color: '#FF6B6B',
borderColor: '#fff',
borderWidth: 1,
},
emphasis: {
itemStyle: {
color: '#dd4444'
}
},
tooltip: {
formatter: function(params) {
return ` ${params.data.name}<br/>坐标: [ ${params.data.value[0].toFixed(4)}, ${params.data.value[1].toFixed(4)}]`;
}
}
}
]
};
};
/**
* 加载并注册指定路径的 GeoJSON 地图数据
* @param {string} path - GeoJSON 文件路径
* @param {string} mapName - 注册到 ECharts 的地图名称
* @returns {Promise<boolean>} - 成功或失败
*/
const loadAndRegisterSingleMap = async (path, mapName) => {
try {
console.log(`正在加载 GeoJSON: ${path}`);
const response = await fetch(path);
if (!response.ok) {
throw new Error(`加载 GeoJSON 失败: ${response.status} ${response.statusText}`);
}
const geoJsonData = await response.json();
// 注册地图
echarts.registerMap(mapName, geoJsonData);
console.log(`GeoJSON 地图数据 ${mapName} 加载并注册成功。`);
return true;
} catch (error) {
console.error('加载或注册 GeoJSON 地图时出错:', error);
return false;
}
};
/**
* 切换到指定的地图层级和区域
* @param {string} targetLevel - 目标层级 ('country' or 'province')
* @param {string} [targetProvinceCode] - 目标省份编码 (当 targetLevel='province' 时使用)
*/
const switchMap = async (targetLevel, targetProvinceCode = null) => {
if (!myChart) {
console.error("ECharts 实例不存在,无法切换地图。");
return;
}
let mapNameToUse, geoPathToLoad;
if (targetLevel === 'country') {
mapNameToUse = COUNTRY_MAP_NAME;
geoPathToLoad = COUNTRY_GEOJSON_PATH;
currentMapLevel = 'country';
currentProvinceCode = null;
} else if (targetLevel === 'province' && targetProvinceCode) {
mapNameToUse = `province_ ${targetProvinceCode}`;
const path = getProvinceGeoPath(targetProvinceCode);
if (!path) {
console.error(`无法切换到省份 ${targetProvinceCode}: 找不到对应的 GeoJSON 文件。`);
return;
}
geoPathToLoad = path;
currentMapLevel = 'province';
currentProvinceCode = targetProvinceCode;
} else {
console.error("无效的目标层级或省份代码。");
return;
}
// 尝试加载并注册目标地图数据
const loadSuccess = await loadAndRegisterSingleMap(geoPathToLoad, mapNameToUse);
if (!loadSuccess) {
console.error(`切换地图失败:无法加载 ${geoPathToLoad}`);
return;
}
// 生成新配置并应用
const newOption = getEChartsOption(targetLevel, targetProvinceCode);
myChart.setOption(newOption, true); // 使用 notMerge=true 确保完全替换
// 更新返回按钮的可见性
updateBackButtonVisibility();
};
// --- 新增函数:管理返回按钮 ---
/**
* 获取返回按钮的DOM元素
* @returns {HTMLElement|null}
*/
const getBackButtonElement = () => {
// 假设按钮ID是固定的可以根据需要调整
return document.getElementById('map-back-button');
};
/**
* 创建返回按钮并将其添加到图表容器中
* @param {HTMLElement} chartContainer - ECharts 图表容器
*/
const createBackButton = (chartContainer) => {
const button = document.createElement('button');
button.id = 'map-back-button';
button.textContent = '⬅返回全国地图';
button.style.position = 'absolute';
button.style.top = '10px';
button.style.left = '10px';
button.style.zIndex = 1000; // 确保按钮在图表之上
button.style.padding = '2px 5px'; // 减少内边距以更接近纯文本
button.style.fontSize = '14px'; // 根据需要调整字体大小
button.style.cursor = 'pointer';
button.style.border = 'none'; // 去掉边框
button.style.backgroundColor = 'transparent'; // 设置背景透明
button.style.display = 'none'; // 初始隐藏
button.onclick = () => {
console.log("点击了返回按钮。");
switchMap('country');
};
chartContainer.appendChild(button);
};
/**
* 根据当前地图层级更新返回按钮的可见性
*/
const updateBackButtonVisibility = () => {
const button = getBackButtonElement();
if (!button) {
console.warn("返回按钮 DOM 元素未找到,无法更新其可见性。");
return;
}
// 当前在省份地图时显示按钮,否则隐藏
button.style.display = (currentMapLevel === 'province') ? 'block' : 'none';
};
// --- /新增函数 ---
/**
* 初始化 ECharts 地图 (初始加载国家地图)
* @param {HTMLElement} container - 图表容器DOM元素
*/
const initEChartsMap = async (container) => {
if (!container) {
console.error('地图容器未找到');
return;
}
// 初始化 ECharts 实例
myChart = echarts.init(container);
// 创建并添加返回按钮
createBackButton(container);
// 首先加载并注册国家地图
const countryLoadSuccess = await loadAndRegisterSingleMap(COUNTRY_GEOJSON_PATH, COUNTRY_MAP_NAME);
if (!countryLoadSuccess) {
console.error("初始化失败:无法加载国家 GeoJSON。");
return; // 或者显示错误信息给用户
}
// 应用初始配置 (国家地图)
const initialOption = getEChartsOption('country');
myChart.setOption(initialOption);
// --- 添加事件监听 ---
// 监听地图区域点击事件
myChart.on('click', 'geo', function (params) {
if (currentMapLevel === 'country') {
// 当前是国家地图,点击省份则进入
const clickedProvinceName = params.name;
console.log(`点击了省份: ${clickedProvinceName}`);
// 尝试获取省份对应的 GeoJSON 文件名
const fileName = Object.keys(provinceNameToGeoFile).find(key => key === clickedProvinceName);
console.log(fileName)
if (fileName) {
const provinceCode = fileName; // 这里假设文件名就是省份简称
console.log(`即将切换到省份: ${provinceCode}`);
switchMap('province', provinceCode);
} else {
console.log(`未配置省份 " ${clickedProvinceName}" 的详细地图。`);
}
}
// 如果在省份地图上点击,可以有其他逻辑,比如不处理
});
// 监听窗口大小变化,自动适配
window.addEventListener('resize', function () {
if (myChart) {
myChart.resize();
}
});
};
/**
* 获取地图容器ID
* @returns {string}
*/
const getMapContainerId = () => {
return 'echarts-map-container';
};
/**
* 渲染 ECharts 地图内容到指定容器
* @param {HTMLElement} contentDiv - 标签页的内容容器
*/
const renderMap = (contentDiv) => {
// 清空内容区域
// contentDiv.innerHTML = '';
// 创建地图容器
const mapDiv = document.createElement('div');
mapDiv.id = getMapContainerId();
mapDiv.style.width = '100%';
mapDiv.style.height = '600px';
mapDiv.style.marginTop = '20px';
mapDiv.style.position = 'relative'; // 为绝对定位的按钮提供参考
contentDiv.appendChild(mapDiv);
// 加载 GeoJSON 并初始化地图
setTimeout(async () => {
const container = document.getElementById(getMapContainerId());
if (container) {
await initEChartsMap(container); // 使用 await 确保初始化完成
} else {
console.error('地图容器 DOM 元素未找到,无法初始化图表。');
}
}, 100);
};
/**
* 销毁 ECharts 实例 (可选,用于性能优化或切换时清理)
*/
const destroyMap = () => {
if (myChart) {
myChart.dispose();
myChart = null;
console.log('ECharts 地图已销毁');
// 可以考虑移除返回按钮
const button = getBackButtonElement();
if (button && button.parentNode) {
button.parentNode.removeChild(button);
}
}
};
// 返回公共方法
return {
renderMap,
destroyMap
};
})();

View File

@@ -14,22 +14,88 @@ const MapFootprintModule = (() => {
// --- 数据 ---
// 注意:这里的坐标可能需要根据你实际访问的城市进行微调
const visitedPlaces = [
{ name: "济南市", value: [117.024961, 36.682788], province: "山东省" },
{ name: "青岛市", value: [120.384423, 36.065918], province: "山东省" },
{ name: "淄博市", value: [118.055915, 36.813547], province: "山东省" },
{ name: "潍坊市", value: [119.162775, 36.705759], province: "山东省" },
{
name: "济南市",
province: "山东省",
value: [117.024961, 36.682788],
desc:'正在这里努力打工!',
imgList: [
"./static/img/location/jinan/1.jpg",
"./static/img/location/jinan/2.jpg",
"./static/img/location/jinan/3.jpg",
"./static/img/location/jinan/4.jpg",
],
},
{
name: "青岛市",
value: [120.384423, 36.065918],
province: "山东省" ,
desc:'这是我人生第一次看见海,我永远不会忘记。同样的还有你们。',
imgList: [
"./static/img/location/qingdao/1.jpg",
"./static/img/location/qingdao/2.jpg",
"./static/img/location/qingdao/3.jpg",
],
},
{
name: "淄博市",
desc:'也算是吃上淄博烧烤了。',
value: [118.055915, 36.813547],
province: "山东省"
},
{
name: "潍坊市",
desc:'风筝节!不错不错!',
value: [119.162775, 36.705759],
province: "山东省"
},
{ name: "保定市", value: [115.482331, 38.867657], province: "河北省" },
{ name: "衡水市", value: [115.665996, 37.739574], province: "河北省" },
{ name: "沧州市", value: [116.838581, 38.308094], province: "河北省" },
{ name: "石家庄市", value: [114.514891, 38.042309], province: "河北省" },
{ name: "张家口市", value: [114.886714, 40.811943], province: "河北省" },
{
name: "保定市",
desc:'大学四年,那是最自由的时候。',
value: [115.482331, 38.867657],
province: "河北省"
},
{ name: "衡水市",
desc: '生我养我的地方,也是埋葬我的地方',
value: [115.665996, 37.739574], province: "河北省" },
{ name: "沧州市",
desc: 'hei !你一定要幸福啊!带着我们几个的祝福,走下去啊!',
value: [116.838581, 38.308094], province: "河北省" },
{
name: "廊坊市",
value: [116.704374, 39.523949],
province: "河北省" ,
desc:'五一寻友,一起夜间烧烤,宿醉到天明',
imgList: [
"./static/img/location/langfang/1.jpg",
"./static/img/location/langfang/2.jpg",
"./static/img/location/langfang/3.jpg",
],
},
{ name: "石家庄市",
desc: '我曾经来过。',
value: [114.514891, 38.042309], province: "河北省" },
{ name: "张家口市",
desc: '三个准大学生,傻傻的做七八个小时的绿皮火车来到这里,只为某人一份未知的爱情。',
value: [114.886714, 40.811943], province: "河北省" },
{ name: "洛阳市", value: [112.454174, 34.618139], province: "河南省" },
{ name: "洛阳市",
province: "河南省",
value: [112.454174, 34.618139],
desc:'群山巍峨,银装素裹,不虚此行',
imgList: [
"./static/img/location/luoyang/1.jpg",
"./static/img/location/luoyang/2.jpg",
"./static/img/location/luoyang/3.jpg",
],
},
{ name: "昌平区", value: [116.235904, 40.218086], province: "北京市" },
{ name: "杭州市", value: [120.153576, 30.287459], province: "浙江省" },
{ name: "杭州市",
desc: '细雨朦胧的西湖才是最美的。',
value: [120.153576, 30.287459], province: "浙江省" },
{ name: "苏州市", value: [120.585316, 31.298886], province: "江苏省" },
{ name: "扬州市", value: [119.421003, 32.393159], province: "江苏省" },
@@ -41,11 +107,9 @@ const MapFootprintModule = (() => {
// 省份名称到其 GeoJSON 文件名的映射 (需要根据你的文件名调整)
const provinceNameToGeoFile = {
北京市: "北京市.geojson",
山东省: "山东省.geojson",
河北省: "河北省.geojson",
河南省: "河南省.geojson",
浙江省: "浙江省.geojson",
江苏省: "江苏省.geojson",
};
@@ -106,8 +170,48 @@ const MapFootprintModule = (() => {
},
},
tooltip: {
// --- 核心修改Formatter 函数 ---
formatter: function (params) {
return ` ${params.data.name}<br/>坐标: [ ${params.data.value[0].toFixed(4)}, ${params.data.value[1].toFixed(4)}]`;
// 确保 params.data 存在
if (!params.data) {
return params.name || "未知地点"; // 如果没有数据,至少显示名称或默认文字
}
// 获取地点名称和坐标
const name = params.data.name || "未知地点";
const longitude =
params.data.value && params.data.value[0]
? params.data.value[0].toFixed(4)
: "N/A";
const latitude =
params.data.value && params.data.value[1]
? params.data.value[1].toFixed(4)
: "N/A";
// 初始化 tooltip 内容字符串
let tooltipHtml = `${name}<br/>坐标: [${longitude}, ${latitude}]<br/>`; // 添加一些换行和分割
// 如果还需要显示其他描述信息(例如可以从 params.data 中获取),可以在此处添加
const description = params.data.desc || "博主也不知道说啥了。。。";
if (description) {
tooltipHtml += `<strong>描述:</strong>${description}<br/>`;
}
// 获取图片列表
const imageList = params.data.imgList; // 直接从数据对象获取 imgList
// 检查是否有图片列表且不为空
if (Array.isArray(imageList) && imageList.length > 0) {
// 添加标题
tooltipHtml += "<strong>照片:</strong><br/>";
// 遍历图片列表,生成 HTML 图像标签
imageList.forEach((imgSrc) => {
tooltipHtml += `<img src="${imgSrc}" alt="${name} 图片" style="max-width: 150px; max-height: 100px; margin: 5px; border-radius: 4px;" onerror="this.style.display='none';"/>`;
});
} else {
// 如果没有图片或 imgList 不存在/为空,则显示提示
tooltipHtml += "<strong>照片:</strong>看来博主不是很爱拍照~";
}
return tooltipHtml; // 返回构建好的 HTML 字符串
},
},
},
@@ -140,40 +244,28 @@ const MapFootprintModule = (() => {
centerCoord = [116.407395, 40.3];
}
console.log(firstPlaceInProvince.value);
titleText = ` ${provinceCode} - “印迹”`;
// 过滤出属于当前省份的足迹点
filteredVisitedPlaces = visitedPlaces.filter(
(place) => place.province === provinceCode,
);
// --- 为省份地图准备高亮数据 ---
// 提取当前省份内访问过的城市名称
const visitedCitiesInProvince = filteredVisitedPlaces.map(
(place) => place.name,
);
// 构造 map 系列的数据,访问过的城市 value 设为 1未访问的可以设为 0
// 为了实现高亮,我们只需要列出访问过的城市即可,未访问的城市会使用默认颜色
const provinceMapData = visitedCitiesInProvince.map((cityName) => ({
name: cityName, // GeoJSON 中区域的名称必须与此 name 匹配
const provinceMapData = filteredVisitedPlaces.map((city) => ({
name: city.name,
desc:city.desc || "",
imgList:city.imgList || [],
value: 1, // 值为 1 表示访问过,用于 visualMap 区分
}));
// console.log(provinceMapData)
// --- 配置 visualMap ---
visualMapConfig = {
show: false, // 通常不显示 visualMap 组件条
min: 0, // 最小值
max: 1, // 最大值
inRange: {
// 当 data 中 item.value 为 1 时,使用的颜色
color: ["#FF6B6B"], // 高亮颜色 (访问过的城市)
},
// outOfRange: {
// // 当 value 超出范围或未定义时,使用的颜色
// // 此颜色由 series.itemStyle.areaColor 控制
// },
calculable: true,
};
@@ -204,6 +296,7 @@ const MapFootprintModule = (() => {
// areaColor: '#ffcccc', // 也可以设置悬停颜色,但会受 visualMap 影响
},
},
// 数据驱动颜色
data: provinceMapData,
// 默认样式(未在 data 中定义的区域将使用此样式)
@@ -239,35 +332,7 @@ const MapFootprintModule = (() => {
},
},
tooltip: {
trigger: "item",
formatter: function (params) {
if (params.seriesType === "scatter" && level === "country") {
// 国家地图的足迹点 tooltip
if (params.value && params.value.length >= 2) {
return (
params.name +
"<br/>经度: " +
params.value[0].toFixed(4) +
"<br/>纬度: " +
params.value[1].toFixed(4)
);
}
return params.name;
} else if (params.seriesType === "map" && level === "province") {
// 省份地图的区域 tooltip
// params.value 是 data 中对应的 value
const status = params.value === 1 ? "访问过" : "未访问";
return `区域: ${params.name}<br/>状态: ${status}`;
} else if (
params.componentType === "geo" &&
level === "province" &&
params.componentSubType === "map"
) {
// 省份地图的 geo 区域 tooltip (如果需要)
return `点击返回全国: ${params.name}`;
}
return params.name;
},
},
// geo 组件配置
geo:
@@ -420,7 +485,7 @@ const MapFootprintModule = (() => {
button.id = "map-back-button";
button.textContent = "⬅返回全国地图";
button.style.position = "absolute";
button.style.top = "10px";
button.style.top = "50px";
button.style.left = "10px";
button.style.zIndex = 1000; // 确保按钮在图表之上
button.style.padding = "2px 5px"; // 减少内边距以更接近纯文本
@@ -544,9 +609,9 @@ const MapFootprintModule = (() => {
const mapDiv = document.createElement("div");
mapDiv.id = getMapContainerId();
mapDiv.style.width = "100%";
mapDiv.style.height = "600px";
mapDiv.style.height = "90vh";
mapDiv.style.marginTop = "20px";
mapDiv.style.position = "relative"; // 为绝对定位的按钮提供参考
mapDiv.style.position = "relative";
contentDiv.appendChild(mapDiv);
// 加载 GeoJSON 并初始化地图
@@ -587,30 +652,29 @@ function showModal() {
document.getElementById("modalOverlay").style.display = "block";
}
const closeModal = () => {
document.getElementById('modalOverlay').style.display = 'none';
document.getElementById("modalOverlay").style.display = "none";
};
// --- 页面加载完成后执行 ---
document.addEventListener('DOMContentLoaded', function() {
// 获取遮罩层和内容区域元素
const modalOverlay = document.getElementById('modalOverlay');
const modalContent = document.getElementById('modalContent');
document.addEventListener("DOMContentLoaded", function () {
// 获取遮罩层和内容区域元素
const modalOverlay = document.getElementById("modalOverlay");
const modalContent = document.getElementById("modalContent");
// 定义点击遮罩层的处理函数
function handleOverlayClick(event) {
// 检查点击的目标是否是遮罩层本身(而不是它的子元素,比如里面的 p 或 button
if (event.target === modalOverlay) {
closeModal(); // 如果是,就关闭弹窗
}
// 定义点击遮罩层的处理函数
function handleOverlayClick(event) {
// 检查点击的目标是否是遮罩层本身(而不是它的子元素,比如里面的 p 或 button
if (event.target === modalOverlay) {
closeModal(); // 如果是,就关闭弹窗
}
}
// 为遮罩层添加点击事件监听器
modalOverlay.addEventListener('click', handleOverlayClick);
// 为遮罩层添加点击事件监听器
modalOverlay.addEventListener("click", handleOverlayClick);
// --- 示例:如何显示弹窗 (供参考) ---
// window.showModal = function() {
// modalOverlay.style.display = 'block';
// // 注意:如果每次都显示,事件监听器只需要添加一次,或者在这里检查是否已存在
// }
});
// --- 示例:如何显示弹窗 (供参考) ---
// window.showModal = function() {
// modalOverlay.style.display = 'block';
// // 注意:如果每次都显示,事件监听器只需要添加一次,或者在这里检查是否已存在
// }
});

View File

@@ -1,99 +0,0 @@
// 定义网站数据
const websitesData = [
{
category: 'personal_blog_h3', // 分类标识符,对应国际化 key
items: [
{
url: 'https://www.ittoolman.top/',
linkTextKey: 'personal_blog_link1'
},
{
url: 'https://blog.iletter.top/',
linkTextKey: 'personal_blog_link2'
}
]
},
{
category: 'online_apps',
items: [
{
url: 'https://img.iletter.top/',
linkTextKey: 'online_app_link1'
},
{
url: 'https://gitea.iletter.top/',
linkTextKey: 'online_app_link2'
},
{
url: 'http://openlist.iletter.top/',
linkTextKey: 'online_app_link3'
},
{
url: 'http://umami.iletter.top/',
linkTextKey: 'online_app_link4'
},
{
url: 'http://beszel.iletter.top/',
linkTextKey: 'online_app_link5'
}
]
}
];
function renderWebsites(data, translations, lang = 'zh') {
const container = document.getElementById('dynamic-website-links');
if (!container) {
console.error("Container '#dynamic-website-links' not found.");
return;
}
// 清空容器
container.innerHTML = '';
data.forEach(categoryObj => {
const catKey = categoryObj.category;
const items = categoryObj.items;
// 创建分类标题
const heading = document.createElement('h3');
// 使用传入的语言获取翻译文本
const translatedHeadingText = translations[lang][catKey];
if (translatedHeadingText !== undefined) {
heading.textContent = translatedHeadingText;
} else {
// 如果找不到翻译,则显示 key 或一个默认值
console.warn(`Translation key '${catKey}' not found for language '${lang}'`);
heading.textContent = catKey; // 或者设置为 "Unknown Category"
}
container.appendChild(heading);
// 创建一个 div 来包裹该分类下的所有链接
const linksDiv = document.createElement('div');
linksDiv.className = 'category-links'; // 可选,方便 CSS 样式化
items.forEach(item => {
const linkElement = document.createElement('a');
linkElement.href = item.url;
linkElement.target = '_blank';
// 获取链接的显示文本
const linkTextKey = item.linkTextKey;
const translatedLinkText = translations[lang][linkTextKey];
if (translatedLinkText !== undefined) {
linkElement.textContent = translatedLinkText;
} else {
// 如果找不到翻译,则显示 key 或一个默认值
console.warn(`Translation key '${linkTextKey}' not found for language '${lang}'`);
linkElement.textContent = item.url; // 或者设置为 "Link"
}
linksDiv.appendChild(linkElement);
// 添加一个间隔(例如空格或换行符,取决于你的 CSS 布局)
const space = document.createTextNode(' ');
linksDiv.appendChild(space);
});
container.appendChild(linksDiv);
});
}

View File

@@ -1,60 +0,0 @@
// ==================== 标签切换功能 ====================
document.addEventListener("DOMContentLoaded", function () {
const tabs = document.querySelectorAll(".nav-tab");
const contents = document.querySelectorAll(".tab-content");
tabs.forEach((tab) => {
tab.addEventListener("click", function () {
const tabId = this.getAttribute("data-tab");
// 移除所有激活状态
tabs.forEach((t) => t.classList.remove("active"));
contents.forEach((c) => c.classList.remove("active"));
// 添加当前激活状态
this.classList.add("active");
document.getElementById(`${tabId}-content`).classList.add("active");
});
});
const websiteTab = document.querySelector('.nav-tab[data-tab="website"]');
const websiteContent = document.getElementById("website-content");
if (websiteTab && websiteContent) {
websiteTab.addEventListener("click", function () {
const hasRendered =
websiteContent.querySelector(".category-links") !== null;
if (!hasRendered) {
const savedLang = localStorage.getItem(LANG_KEY) || "zh";
renderWebsites(websitesData, translations, savedLang);
}
});
} else {
console.warn("未找到“我的网站”标签或内容区域。");
}
// 点击我的工具函数
const toolsTab = document.querySelector('.nav-tab[data-tab="tools"]');
const toolsContent = document.getElementById("tools-content");
if (toolsTab && toolsContent) {
toolsTab.addEventListener("click", function () {
const hasRendered = toolsContent.querySelector(".tool-item") !== null;
if (!hasRendered) {
renderTools(toolsData);
}
});
} else {
console.warn("未找到“我的工具”标签或内容区域,将在页面加载时尝试渲染。");
renderTools(toolsData);
}
// 点击常玩游戏函数
const gamesTab = document.querySelector('.nav-tab[data-tab="games"]');
const gamesContent = document.getElementById("games-content");
if (gamesTab && gamesContent) {
gamesTab.addEventListener("click", function () {
const hasRendered = gamesContent.querySelector(".game-item") !== null;
if (!hasRendered) {
renderGames(gamesData);
}
});
} else {
console.warn("未找到“常玩游戏”标签或内容区域。");
renderGames(gamesData);
}
});

View File

@@ -1,47 +1,212 @@
// ========== 工具配置数据 ==========
// 将工具数据集中管理
// ========== 工具配置数据 (包含中英双语) ==========
// 将工具数据集中管理,每个工具项包含中英文描述
const toolsData = [
{ name: "Rolan", desc: "应用启动器,从大学用到现在" },
{ name: "Chrome", desc: "确实好用,正在向着屎迈进" },
{ name: "VS Code", desc: "代码编辑器,轻量?高效!" },
{ name: "IntelliJ IDEA", desc: "JAVA代码编辑器" },
{ name: "PyCharm", desc: "python代码编辑器" },
{ name: "Android Studio", desc: "安卓软件开发,一直在学(新建文件夹)" },
{ name: "HbuilderX", desc: "写小程序真心不错vue+js很棒减少了学习压力" },
{ name: "Navicate", desc: "链接mysql的神器sqlyog是谁真不熟。" },
{ name: "Xshell", desc: "链接服务器简单易用配合xftp很好用" },
{ name: "Postman", desc: "API测试与调试工具" },
{ name: "VMware", desc: "虚拟机管理,工具有毒?先拿这个试试" },
{ name: "PhotoShop", desc: "ps图片专用蒙版套索十分好用" },
{ name: "Obsidian", desc: "知识管理,双链接笔记。本地笔记爱好者的福音。" },
{ name: "Typora", desc: "Markdown编辑器简洁美观。" },
{ name: "Docker", desc: "容器化部署一个docker-compose走天下" },
{ name: "Premiere Pro", desc: "剪辑视频调音做简单的效果,简单易用" },
{ name: "After Effects", desc: "特效制作神器就是对显卡和cpu比较不友好" },
{ name: "CheatEngine", desc: "风灵月影没出来就用它亦是内存hook的极佳选择" },
{ name: "x64dbg", desc: "孩子你想掌握逆向之力嘛先从x64dbg开始吧" },
{ name: "WireGuard", desc: "组网神器去你妹的向日葵去你妹的todesk" },
{ name: "Syncthing", desc: "文件同步,超级好用,同步笔记的不错选择。" },
{ name: "NeatReader", desc: "看书软件有点后悔花了188买了永久" },
{ name: "Corel VideoStudio", desc: "万恶的苏州思杰马克丁!还我会声会影" },
{
name: "网易云音乐",
desc: "每天听每天用尊贵的年费vip+版权破解Unblock Netease",
name_zh: "Rolan",
desc_zh: "应用启动器,从大学用到现在",
name_en: "Rolan",
desc_en: "Application launcher, used since college.",
},
{
name: "喜马拉雅听书",
desc: "每天听,每天用。三体,穷鬼的上下两千年等优秀书籍",
name_zh: "Chrome",
desc_zh: "确实好用,正在向着屎迈进",
name_en: "Chrome",
desc_en: "Truly useful, but seems to be heading towards bloat.",
},
{ name: "一本日记", desc: "正经人谁写日记啊!" },
{ name: "Ollama", desc: "本地部署对话AI微调模型32b是4070遭不住" },
{ name: "AnythingLLM", desc: "AI知识库的极佳选择比我自己微调+向量化好" },
{ name: "UVR5", desc: "处理音频的神器,想当年苦逼的分离音频算什么!" },
{ name: "Remote Desktop Manager", desc: "超级强大的远控软件" },
{
name: "SyncClipboard",
desc: "剪切板同步,好用,爱用。再也不用微信消息复制粘贴了",
name_zh: "VS Code",
desc_zh: "代码编辑器,轻量?高效!",
name_en: "VS Code",
desc_en: "Code editor, Lightweight? Efficient!",
},
{
name_zh: "IntelliJ IDEA",
desc_zh: "JAVA代码编辑器我拿来吃饭的东西",
name_en: "IntelliJ IDEA",
desc_en: "JAVA Code Editor",
},
{
name_zh: "PyCharm",
desc_zh: "python代码编辑器偷懒专用",
name_en: "PyCharm",
desc_en: "Python Code Editor",
},
{
name_zh: "Android Studio",
desc_zh: "安卓软件开发,一直在学(新建文件夹)",
name_en: "Android Studio",
desc_en: "Android app development, always learning (Creating new folders).",
},
{
name_zh: "HbuilderX",
desc_zh: "写小程序真心不错vue+js很棒减少了学习压力",
name_en: "HbuilderX",
desc_en:
"Great for writing mini-programs, Vue+JS is awesome! Reduces learning pressure.",
},
{
name_zh: "Navicate",
desc_zh: "链接mysql的神器sqlyog是谁真不熟。",
name_en: "Navicat",
desc_en:
"A powerful tool for connecting to MySQL. Who is SQLyog? Never heard of it.",
},
{
name_zh: "Xshell",
desc_zh: "链接服务器简单易用配合xftp很好用",
name_en: "Xshell",
desc_en:
"Simple and easy-to-use for connecting to servers, works well with Xftp.",
},
{
name_zh: "Postman",
desc_zh: "API测试与调试工具",
name_en: "Postman",
desc_en: "API Testing and Debugging Tool",
},
{
name_zh: "VMware",
desc_zh: "虚拟机管理,工具有毒?先拿这个试试",
name_en: "VMware",
desc_en: "Virtual Machine Management. Is the tool toxic? Try this first.",
},
{
name_zh: "PhotoShop",
desc_zh: "ps图片专用蒙版套索十分好用",
name_en: "Photoshop",
desc_en: "PS, dedicated to image editing. Masks and Lasso are super handy.",
},
{
name_zh: "Obsidian",
desc_zh: "知识管理,双链接笔记。本地笔记爱好者的福音。",
name_en: "Obsidian",
desc_en:
"Knowledge management, bidirectional linking notes. A blessing for local note enthusiasts.",
},
{
name_zh: "Typora",
desc_zh: "Markdown编辑器简洁美观。",
name_en: "Typora",
desc_en: "Markdown editor, simple and beautiful.",
},
{
name_zh: "Docker",
desc_zh: "容器化部署一个docker-compose走天下",
name_en: "Docker",
desc_en: "Containerized deployment, docker-compose handles everything.",
},
{
name_zh: "Premiere Pro",
desc_zh: "剪辑视频调音做简单的效果,简单易用",
name_en: "Premiere Pro",
desc_en: "Video editing, audio adjustment, simple effects, easy to use.",
},
{
name_zh: "After Effects",
desc_zh: "特效制作神器就是对显卡和cpu比较不友好",
name_en: "After Effects",
desc_en:
"Special effects creation powerhouse, just not very GPU/CPU friendly.",
},
{
name_zh: "CheatEngine",
desc_zh: "风灵月影没出来就用它亦是内存hook的极佳选择",
name_en: "Cheat Engine",
desc_en:
"Used it before Fenglingyueying came out, also an excellent choice for memory hooking.",
},
{
name_zh: "x64dbg",
desc_zh: "孩子你想掌握逆向之力嘛先从x64dbg开始吧",
name_en: "x64dbg",
desc_en: "Kid! Want to master reverse engineering? Start with x64dbg!",
},
{
name_zh: "WireGuard",
desc_zh: "组网神器去你妹的向日葵去你妹的todesk",
name_en: "WireGuard",
desc_en: "The best networking tool, forget Sunlogin and Todesk.",
},
{
name_zh: "Syncthing",
desc_zh: "文件同步,超级好用,同步笔记的不错选择。",
name_en: "Syncthing",
desc_en:
"File synchronization, super useful, a great choice for syncing notes.",
},
{
name_zh: "NeatReader",
desc_zh: "看书软件有点后悔花了188买了永久",
name_en: "NeatReader",
desc_en:
"E-book reading software, kind of regret spending 188 for lifetime access.",
},
{
name_zh: "Corel VideoStudio",
desc_zh: "万恶的苏州思杰马克丁!还我会声会影",
name_en: "Corel VideoStudio",
desc_en: "Curse you, Suzhou CorelMarkding! Give me back Pinnacle Studio.",
},
{
name_zh: "网易云音乐",
desc_zh: "每天听每天用尊贵的年费vip+版权破解Unblock Netease",
name_en: "Netease Cloud Music",
desc_en:
"Listen and use daily. Premium annual VIP + copyright unlock (Unblock Netease).",
},
{
name_zh: "喜马拉雅听书",
desc_zh: "每天听,每天用。三体,穷鬼的上下两千年等优秀书籍",
name_en: "Ximalaya Audiobooks",
desc_en:
"Listen and use daily. Great books like 'The Three-Body Problem', 'Poor Man's Two Millennia', etc.",
},
{
name_zh: "一本日记",
desc_zh: "正经人谁写日记啊!",
name_en: "A Diary",
desc_en: "Who writes a diary, seriously?!",
},
{
name_zh: "Ollama",
desc_zh: "本地部署对话AI微调模型32b是4070遭不住",
name_en: "Ollama",
desc_en:
"Locally deployed conversational AI, fine-tuning models. 32b is too much for a 4070.",
},
{
name_zh: "AnythingLLM",
desc_zh: "AI知识库的极佳选择比我自己微调+向量化好",
name_en: "AnythingLLM",
desc_en:
"An excellent choice for an AI knowledge base, better than my own fine-tuning + vectorization.",
},
{
name_zh: "UVR5",
desc_zh: "处理音频的神器,想当年苦逼的分离音频算什么!",
name_en: "UVR5",
desc_en:
"A good tool for processing audio. What were those tough days of manually separating audio!",
},
{
name_zh: "Remote Desktop Manager",
desc_zh: "超级强大的远控软件",
name_en: "Remote Desktop Manager",
desc_en: "Super powerful remote control software.",
},
{
name_zh: "SyncClipboard",
desc_zh: "剪切板同步,好用,爱用。再也不用微信消息复制粘贴了",
name_en: "SyncClipboard",
desc_en:
"Clipboard sync, good and loved. No more copying/pasting via WeChat messages.",
},
{
name_zh: "QtScrcpy",
desc_zh: "局域网控制手机神器远程adb牛逼",
name_en: "QtScrcpy",
desc_en: "LAN phone control tool, remote adb is awesome.",
},
{ name: "QtScrcpy", desc: "局域网控制手机神器远程adb牛逼" },
];
// ========== 渲染工具列表的函数 ==========
@@ -52,27 +217,33 @@ function renderTools(toolsArray) {
return;
}
if (!toolsArray || toolsArray.length === 0) {
container.innerHTML =
'<div class="memo-error" data-i18n="no_tools_data">暂无工具数据</div>'; // 可选:无数据提示
const arrayToRender = toolsArray || toolsData;
if (!arrayToRender || !Array.isArray(arrayToRender) || arrayToRender.length === 0) {
const currentLang = localStorage.getItem(LANG_KEY) || "zh";
let noDataMessage = currentLang === 'en' ? 'No tools data available.' : '暂无工具数据';
container.innerHTML = `<div class="memo-error">${noDataMessage}</div>`;
return;
}
const currentLang = localStorage.getItem(LANG_KEY) || "zh";
let html = "";
toolsArray.forEach((tool, index) => {
const nameKey = `tool_name_${index}`;
const descKey = `tool_desc_${index}`;
const nameTranslation = translations[currentLang][nameKey] || tool.name; // 如果没有翻译,则回退到原始数据
const descTranslation = translations[currentLang][descKey] || tool.desc;
toolsArray.forEach((tool) => {
const name = currentLang === "en" ? tool.name_en : tool.name_zh;
const desc = currentLang === "en" ? tool.desc_en : tool.desc_zh;
html += `
<div class="tool-item">
<div class="tool-name">${nameTranslation}</div>
<div class="tool-desc">${descTranslation}</div>
</div>
`;
<div class="tool-item">
<div class="tool-name">${name}</div>
<div class="tool-desc">${desc}</div>
</div>
`;
});
container.innerHTML = html;
}
// 如果需要在页面加载后自动渲染,可以取消下面的注释
// document.addEventListener('DOMContentLoaded', function() {
// renderTools();
// });

133
static/js/websites_my.js Normal file
View File

@@ -0,0 +1,133 @@
// ========== 网站链接数据 (包含中英双语) ==========
const websitesData = [
{
// 分类标题
category_zh: '个人博客',
category_en: 'Personal Blogs',
items: [
{
url: 'https://www.ittoolman.top/',
linkText_zh: 'ittoolman.top - Github托管',
linkText_en: 'ittoolman.top - Github Page'
},
{
url: 'https://blog.iletter.top/',
linkText_zh: 'blog.iletter.top - typecho用户的最后坚守',
linkText_en: 'blog.iletter.top - Last Stand for Typecho Users'
}
]
},
{
category_zh: '私人在线应用',
category_en: 'Personal Online Apps',
items: [
{
url: 'https://img.iletter.top/',
linkText_zh: '简单图床 - 私人图床工具',
linkText_en: 'EsayImage2 - Image Uploader Tool'
},
{
url: 'https://gitea.iletter.top/',
linkText_zh: 'Gitea - 私人git托管仓库',
linkText_en: 'Gitea - Private Git Repository'
},
{
url: 'http://openlist.iletter.top/',
linkText_zh: 'OpenList - 在线云盘合集',
linkText_en: 'OpenList - Online Cloud Disk Collection'
},
{
url: 'http://umami.iletter.top/',
linkText_zh: 'Umami - 网站访问分析',
linkText_en: 'Umami - Website visit analysis'
},
{
url: 'http://beszel.iletter.top/',
linkText_zh: 'Beszel - 服务器监控',
linkText_en: 'Beszel - Server Monitoring'
},
{
url: 'http://py.iletter.top/en-de-code',
linkText_zh: 'Base64在线加密解密 - 自写demo',
linkText_en: 'Base64 Online Encryption and Decryption - My Demo'
},
]
}
// 如果你想添加新分类,可以仿照上面的格式:
/*
{
category_zh: '新的分类名',
category_en: 'New Category Name',
items: [
{
url: 'https://example.com/',
linkText_zh: '示例网站',
linkText_en: 'Example Site'
}
]
}
*/
];
// ========== 渲染网站链接的函数 ==========
function renderWebsites(websitesArray) {
const container = document.getElementById('dynamic-website-links');
if (!container) {
console.error("未找到网站链接容器 #dynamic-website-links");
return;
}
// 检查数组是否为空或未定义/未初始化
const arrayToRender = websitesArray || websitesData;
if (!arrayToRender || !Array.isArray(arrayToRender) || arrayToRender.length === 0) {
const currentLang = localStorage.getItem("preferred_language") || "zh";
let noDataMessage = currentLang === 'en' ? 'No website data available.' : '暂无网站数据';
container.innerHTML = `<div class="memo-error">${noDataMessage}</div>`;
return; // 提前结束函数
}
const currentLang = localStorage.getItem("preferred_language") || "zh";
// 清空容器
container.innerHTML = '';
arrayToRender.forEach(categoryObj => {
// 根据当前语言选择分类标题
const categoryName = currentLang === 'en' ? categoryObj.category_en : categoryObj.category_zh;
// 创建分类标题
const heading = document.createElement('h3');
heading.textContent = categoryName;
container.appendChild(heading);
// 创建一个 div 来包裹该分类下的所有链接
const linksDiv = document.createElement('div');
linksDiv.className = 'category-links'; // 可选,方便 CSS 样式化
categoryObj.items.forEach(item => {
const linkElement = document.createElement('a');
linkElement.href = item.url;
linkElement.target = '_blank';
// 根据当前语言选择链接文本
const linkText = currentLang === 'en' ? item.linkText_en : item.linkText_zh;
linkElement.textContent = linkText;
linksDiv.appendChild(linkElement);
// 添加一个间隔(例如空格或换行符,取决于你的 CSS 布局)
const space = document.createTextNode(' ');
linksDiv.appendChild(space);
});
container.appendChild(linksDiv);
});
}
// ========== 初始化 (可选) ==========
// 如果你想在 DOM 加载完成后自动渲染,可以取消下面的注释
/*
document.addEventListener("DOMContentLoaded", function () {
renderWebsites(); // 不传参数,默认使用 websitesData
});
*/