fn_b/resources/js/modules/articles 2.js
2026-02-12 12:36:53 +08:00

370 lines
13 KiB
JavaScript
Executable File
Raw Permalink 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.

/**
* fInitArticleLoader - 插件版
*/
/**
* articles.js
*/
//export const fInitArticleLoader = () => {
// // 直接用全局 $, 别 import
// const $oContainer = $('.list-home');
// const sSlug = $('meta[name="article-slug"]').attr('content') || "freenode";
//
// if (!$oContainer.length) return;
//
// // 插件初始化
// $oContainer.infiniteScroll({
// path: `/${sSlug}?page={{#}}`,
// append: '.ajax-item',
// history: false,
// prefill: true, // 解决大屏不加载的救星
// status: '.page-load-status',
// scrollThreshold: 400,
// responseBody: 'text' // 后端返回 HTML 片段
// });
//
// console.log('[ArticleLoader] 监听已启动');
//};
/**
* fInitArticleLoader - 使用成熟插件的艺术级实现
*/
//export const fInitArticleLoader = () => {
// const $oContainer = $('.list-home');
// const sSlug = $('meta[name="article-slug"]').attr('content') || "freenode";
//
// if (!$oContainer.length) return;
//
// // 先销毁可能存在的旧实例,防止重复绑定没反应
// if ($oContainer.data('infiniteScroll')) {
// $oContainer.infiniteScroll('destroy');
// }
//
// $oContainer.infiniteScroll({
// // 自动拼接分页 URL/news?page=2
// path: `/${sSlug}?page={{#}}`,
//
// // 关键:插件会从加载的页面里找这个类名的元素追加进来
// append: '.ajax-item',
//
// // 关键:如果后端返回的是 JSON 里的 HTML 字符串,必须开启响应类型
// // 但通常我们建议后端直接返回渲染好的 HTML 片段,最省心
// responseBody: 'text',
//
// history: false,
// prefill: true, // 大屏救星,自动填满
// status: '.page-load-status',
// scrollThreshold: 400
// });
//
// // 调试暗号:看看插件到底动没动
// $oContainer.on('request.infiniteScroll', () => console.log('正在请求新货...'));
// $oContainer.on('load.infiniteScroll', () => console.log('请求成功,正在抠图...'));
// $oContainer.on('append.infiniteScroll', () => console.log('艺术品已挂墙!'));
// $oContainer.on('error.infiniteScroll', (oEv, oErr) => console.error('坏了:', oErr));
//};
/**
* fInitArticleLoader - 究极稳定版(解决大屏不触发顽疾)
*/
/**
* fInitArticleLoader - 终极防御版
* 解决大屏不触发、Observer不更新状态的终极方案
*/
/**
* fInitArticleLoader - 艺术级无限滚动加载器 (大屏适配 + 精准位置版)
* s - string / i - integer / b - boolean / o - object / x - unknown
*/
//export const fInitArticleLoader = () => {
// const oArticleLoader = {
// iCurrentPage: 1,
// bLoading: false,
// sSlug: $('meta[name="article-slug"]').attr('content') || "freenode",
// oObserver: null,
//
// init() {
// const oSentinelEl = document.querySelector('#sentinel');
// if (!oSentinelEl) return;
//
// // 1. 哨兵物理补强:必须有高度和清浮动,确保它老老实实待在列表最下面
// $(oSentinelEl).css({
// 'height': '1px',
// 'clear': 'both',
// 'display': 'block',
// 'margin-top': '10px'
// });
//
// this.fSetupObserver(oSentinelEl);
//
// // 2. 初始自检:如果大屏幕一上来就看到哨兵,直接开火
// this.fCheckAndRefill();
// },
//
// // 设置/重置监听器
// fSetupObserver(oTarget) {
// if (this.oObserver) this.oObserver.disconnect();
//
// this.oObserver = new IntersectionObserver((aEntries) => {
// const oEntry = aEntries[0];
// // 只有真进入视口才加载
// if (oEntry.isIntersecting && !this.bLoading) {
// this.load();
// }
// }, {
// // 3. 精准控制rootMargin 调小,防止加载过于提前
// rootMargin: '100px',
// threshold: 0.01
// });
//
// this.oObserver.observe(oTarget);
// },
//
// load() {
// if (this.bLoading) return;
// this.bLoading = true;
//
// $.get(`/${this.sSlug}`, {
// page: this.iCurrentPage + 1
// })
// .done((xResponse) => {
// const { sHtml, bHasMore, iPage } = xResponse;
//
// if (sHtml && sHtml.trim().length > 10) {
// // 渲染并渐显
// const oNewItems = $(sHtml).addClass('ajax-item-fade-in');
// $('.list-home').append(oNewItems);
//
// setTimeout(() => oNewItems.addClass('is-visible'), 50);
//
// // 4. 同步后端页码:单一事实来源
// this.iCurrentPage = iPage;
//
// if (!bHasMore) {
// this.fTerminateLoader();
// } else {
// // 5. 加载完后,递归检查是否填满了大屏幕
// this.fCheckAndRefill();
// }
// } else {
// this.fTerminateLoader();
// }
// })
// .fail(() => {
// console.error("[ArticleLoader] 链路异常");
// })
// .always(() => {
// // 6. 核心防抖:给浏览器 500ms 重新计算布局,防止哨兵位置还没更新就解锁
// setTimeout(() => {
// this.bLoading = false;
// }, 500);
// });
// },
//
// // 大屏适配逻辑:如果哨兵还在视口内,主动追载
// fCheckAndRefill() {
// // 给渲染留出足够的物理时间
// setTimeout(() => {
// const oSentinel = document.querySelector('#sentinel');
// if (!oSentinel || this.bLoading) return;
//
// const oRect = oSentinel.getBoundingClientRect();
// // 判定:哨兵顶部坐标 < 视口高度,说明它还在屏幕里
// if (oRect.top < window.innerHeight) {
// console.log("[ArticleLoader] 空间尚存,自动补货...");
// this.load();
// }
// }, 600); // 这里的延迟是解决“提前加载”的关键,给 DOM 变长的机会
// },
//
// fTerminateLoader() {
// if (this.oObserver) {
// this.oObserver.disconnect();
// this.oObserver = null;
// }
// $('#sentinel').fadeOut(300, function() { $(this).remove(); });
// console.log("[ArticleLoader] 任务圆满完成。");
// }
// };
//
// oArticleLoader.init();
//};
/**
* 匈牙利命名法oObserver (对象), iCurrentPage (整数), bLoading (布尔)
*/
//export const fInitArticleLoader = () => {
// const oArticleLoader = {
// iCurrentPage: 1,
// bLoading: false,
// sSlug: $('meta[name="article-slug"]').attr('content') || "freenode",
// oObserver: null,
//
// init() {
// // 直接抓你页面上的哨兵
// const oSentinelEl = document.querySelector('#sentinel');
// if (!oSentinelEl) return;
//
// // 给哨兵一点颜色和高度(调试完可以把背景去掉,但高度要留着)
// $(oSentinelEl).css({
// 'height': '20px',
// 'width': '100%',
// 'display': 'block',
// 'clear': 'both',
// 'color': 'red',
// });
//
// this.fSetupObserver(oSentinelEl);
// },
//
// fSetupObserver(oTarget) {
// // 高标准rootMargin 设为 0让我们先精准捕获“露头”瞬间
// this.oObserver = new IntersectionObserver((aEntries) => {
// const oEntry = aEntries[0];
//
// // 只有当 isIntersecting 从 false 变为 true 时才会触发
// if (oEntry.isIntersecting && !this.bLoading) {
// this.load();
// }
// }, {
// threshold: [0, 0.1, 1.0] // 多阈值监听,增加灵敏度
// });
//
// this.oObserver.observe(oTarget);
// },
//
// load() {
// if (this.bLoading) return;
// this.bLoading = true;
//
// $.get(`/${this.sSlug}`, {
// page: this.iCurrentPage + 1
// })
// .done((xResponse) => {
// const { sHtml, bHasMore } = xResponse;
//
// if (sHtml && sHtml.trim().length > 5) {
// const oNewItems = $(sHtml).addClass('ajax-item-fade-in');
// $('.list-home').append(oNewItems);
//
// setTimeout(() => oNewItems.addClass('is-visible'), 50);
// this.iCurrentPage++;
//
// // 如果没数据了,当场撤编
// if (!bHasMore) {
// this.fTerminateLoader();
// }
// } else {
// this.fTerminateLoader();
// }
// })
// .fail(() => console.error("加载失败"))
// .always(() => {
// /**
// * 核心:这是解决“只加载一次”的关键!
// * 必须延迟解锁。如果内容渲染太快,浏览器还没来得及把哨兵推下视口,
// * 我们就解锁了,那么下一次滚动就不会触发进入事件。
// */
// setTimeout(() => {
// this.bLoading = false;
// console.log(`第 ${this.iCurrentPage} 页解锁,哨兵当前可见性:`, document.querySelector('#sentinel').getBoundingClientRect().top < window.innerHeight);
// }, 500); // 稍微长一点的延迟,确保 DOM 撑开
// });
// },
//
// fTerminateLoader() {
// if (this.oObserver) this.oObserver.disconnect();
// $('#sentinel').hide(); // 隐藏哨兵,防止它还占位
// }
// };
//
// oArticleLoader.init();
//};
/**
* 匈牙利命名法说明:
* f - function / o - object / i - integer / b - boolean / s - string
*/
export const fInitArticleLoader = () => {
const oArticleLoader = {
iCurrentPage: 1,
bLoading: false,
sSlug: "",
// 从 Meta 标签安全获取配置
iPageLimit: parseInt($('meta[name="page-limit"]').attr('content')) || 10,
init() {
const oBtn = $('.btn-more');
if (!oBtn.length) return;
this.sSlug = oBtn.data('slug') || "";
// 绑定点击事件
$(document).on('click', '.btn-more', (e) => {
e.preventDefault();
this.load();
});
console.log(`[ArticleLoader] 启动成功Limit: ${this.iPageLimit}`);
},
load() {
if (this.bLoading) return;
const oBtn = $('.btn-more');
const oSpan = oBtn.find('span');
const sOriginalText = oSpan.text();
this.bLoading = true;
oBtn.addClass('is-loading');
oSpan.text('正在加载...');
$.get(`/${this.sSlug}`, {
page: this.iCurrentPage + 1
})
.done((xResponse) => {
// 解构 JSON 对象,拿到艺术的结晶
const { sHtml, bHasMore } = xResponse;
if (sHtml && sHtml.trim().length > 5) {
const oNewItems = $(sHtml).addClass('ajax-item-fade-in');
$('.list-home').append(oNewItems);
// 必须延迟,给浏览器留出渲染动画的时间
setTimeout(() => {
oNewItems.addClass('is-visible');
}, 50);
this.iCurrentPage++;
// 根据后端返回的 bHasMore 决定按钮生死
if (!bHasMore) {
this.fTerminateLoader(oBtn);
} else {
oSpan.text(sOriginalText);
}
} else {
this.fTerminateLoader(oBtn);
}
})
.fail((oXhr) => {
console.error("[ArticleLoader] 异常:", oXhr.status);
oSpan.text('加载失败,点击重试');
})
.always(() => {
this.bLoading = false;
oBtn.removeClass('is-loading');
});
},
// 抽取公用逻辑,这叫“代码洁癖”
fTerminateLoader(oBtn) {
oBtn.fadeOut(400, () => oBtn.remove());
console.log("[ArticleLoader] 全部加载完毕,任务结束。");
}
};
oArticleLoader.init();
};