feat(sidepanel): 重构路由系统并优化页面加载逻辑

- 移除冗余的开发预览页面 `index.html`
- 路由配置改为声明式结构,支持自动加载 HTML 和脚本
- 新增 `data-route` 属性用于导航按钮绑定路由路径
- 实现基于事件委托的路由跳转机制
- 重写 `Router.js` 核心逻辑,支持异步加载页面模块
- 改进 `domUtils.js` 中 `addClass` 和 `removeClass` 方法兼容性与健壮性
- 新增 `scriptLoader.js` 工具模块用于动态加载和清理页面脚本
- 时间戳页面初始化方式调整为 IIFE 并挂载至全局作用域
This commit is contained in:
雨霖铃
2025-11-30 23:34:40 +08:00
parent 884fc09750
commit 2761dbe13f
7 changed files with 225 additions and 333 deletions
+30 -12
View File
@@ -98,9 +98,18 @@ export function hasClass(elem, cls) {
* @param {*} cls
*/
export function addClass(elem, cls) {
if (!hasClass(elem, cls)) {
elem.className = elem.className === '' ? cls : elem.className + ' ' + cls;
}
if (!elem || !cls) return;
// 统一空白字符(避免 \n \t 等导致匹配问题)
let current = elem.className.replace(/[\t\r\n]/g, ' ').trim();
// 已存在则忽略
const classes = current.split(/\s+/);
if (classes.includes(cls)) return;
// 添加并规整
classes.push(cls);
elem.className = classes.join(' ').trim();
}
/**
@@ -109,13 +118,22 @@ export function addClass(elem, cls) {
* @param {*} cls
*/
export function removeClass(elem, cls) {
if (hasClass(elem, cls)) {
let newClass = ' ' + elem.className.replace(/[\t\r\n]/g, '') + ' ';
// 删除指定类名
while (newClass.indexOf(' ' + cls + ' ') >= 0) {
newClass = newClass.replace(' ' + cls + ' ', ' ');
}
// 重新赋值
elem.className = newClass.replace(/^\s+|\s+$/);
if (!elem || !cls) return;
// 使用 classList 优先(更安全)
if (elem.classList) {
elem.classList.remove(cls);
return;
}
}
// 传统写法的修复版本
let klass = ' ' + elem.className.replace(/[\t\r\n]/g, ' ') + ' ';
// 持续删除目标 class
while (klass.indexOf(' ' + cls + ' ') !== -1) {
klass = klass.replace(' ' + cls + ' ', ' ');
}
// 过滤多余空格
elem.className = klass.trim().replace(/\s+/g, ' ');
}
+30
View File
@@ -0,0 +1,30 @@
let currentScript = null;
export function loadScript(path, globalInitName) {
console.log('load script', currentScript);
if (currentScript) {
console.log('remove script')
currentScript.remove();
currentScript = null;
}
if (globalInitName && window[globalInitName]) {
delete window[globalInitName];
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = path;
script.type = 'module';
script.onload = () => {
currentScript = document.querySelector(`script[src='${path}']`);
console.log('script loaded', currentScript);
resolve();
};
script.onerror = reject;
document.body.appendChild(script);
});
}