更新显示架构

This commit is contained in:
2026-03-09 09:40:36 +08:00
parent 99fc99f8ad
commit 4cbc52c51b
14 changed files with 1162 additions and 1 deletions
+213
View File
@@ -0,0 +1,213 @@
/**
* 模板数据注入脚本
* 功能:监听 postMessage 事件,当收到 SET_SITE_DATA 时,
* 自动寻找带有 data-field 属性的 HTML 标签,替换为对应的数据
*/
(function() {
'use strict';
// 日志开关
const DEBUG = false;
function log(...args) {
if (DEBUG) {
console.log('[ThemeLoader]', ...args);
}
}
/**
* 替换元素内容或属性
* @param {HTMLElement} element - 目标元素
* @param {string} field - 字段名
* @param {any} value - 数据值
*/
function applyDataToElement(element, field, value) {
if (value === undefined || value === null) {
log(`字段 ${field} 值为空,跳过`);
return;
}
// 如果是数组或对象,尝试解析
if (typeof value === 'string') {
try {
const parsed = JSON.parse(value);
if (parsed) {
value = parsed;
}
} catch (e) {
// 保持原值
}
}
// 处理数组类型(用于轮播图、列表等)
if (Array.isArray(value)) {
handleArrayField(element, field, value);
return;
}
// 处理对象类型
if (typeof value === 'object') {
handleObjectField(element, field, value);
return;
}
// 处理基础类型(字符串、数字)
// 1. 如果是 input/textarea/select,设置为 value
const tagName = element.tagName.toLowerCase();
if (tagName === 'input' || tagName === 'textarea' || tagName === 'select') {
element.value = value;
return;
}
// 2. 如果有 src 属性,设置为 src(图片等)
if (element.hasAttribute('src') && !element.hasAttribute('data-keep-src')) {
// 检查是否是占位符图片
const currentSrc = element.getAttribute('src');
if (!currentSrc || currentSrc.indexOf('placeholder') > -1) {
element.src = value;
}
return;
}
// 3. 否则设置为 innerText
element.innerText = value;
}
/**
* 处理数组类型的字段(如轮播图、列表)
*/
function handleArrayField(container, field, dataList) {
// 查找模板元素(带有 data-template 属性的元素)
const templateElement = container.querySelector('[data-template]');
if (!templateElement) {
log(`字段 ${field} 未找到模板元素`);
return;
}
const template = templateElement.cloneNode(true);
template.removeAttribute('data-template');
template.style.display = '';
// 清空容器
container.innerHTML = '';
// 渲染每个数据项
dataList.forEach((item, index) => {
const itemElement = template.cloneNode(true);
applyDataToObject(itemElement, item, index);
container.appendChild(itemElement);
});
}
/**
* 处理对象类型的字段
*/
function handleObjectField(element, field, data) {
// 递归处理对象属性
applyDataToObject(element, data);
}
/**
* 将数据应用到元素及其子元素
*/
function applyDataToObject(element, data, index = 0) {
// 处理 data-field 属性
const fieldElements = element.querySelectorAll('[data-field]');
fieldElements.forEach(el => {
const field = el.getAttribute('data-field');
// 支持点号分隔的路径,如 "banner.0.image"
const value = getNestedValue(data, field);
if (value !== undefined) {
applyDataToElement(el, field, value);
}
});
// 也处理元素本身的 data-field
if (element.hasAttribute('data-field')) {
const field = element.getAttribute('data-field');
const value = getNestedValue(data, field);
if (value !== undefined) {
applyDataToElement(element, field, value);
}
}
}
/**
* 获取嵌套属性值
* @param {object} obj - 数据对象
* @param {string} path - 属性路径,如 "banner.0.image"
* @returns {any}
*/
function getNestedValue(obj, path) {
if (!path) return obj;
const keys = path.split('.');
let value = obj;
for (const key of keys) {
if (value === null || value === undefined) {
return undefined;
}
value = value[key];
}
return value;
}
/**
* 初始化数据注入
*/
function init() {
log('ThemeLoader 初始化');
// 监听来自父窗口的消息
window.addEventListener('message', function(event) {
log('收到消息:', event.data);
// 验证消息类型
if (!event.data || event.data.type !== 'SET_SITE_DATA') {
return;
}
const siteData = event.data.data;
if (!siteData) {
log('未收到有效数据');
return;
}
log('开始注入数据:', siteData);
// 查找所有带有 data-field 属性的元素
const elements = document.querySelectorAll('[data-field]');
elements.forEach(element => {
const field = element.getAttribute('data-field');
const value = siteData[field];
if (value !== undefined) {
applyDataToElement(element, field, value);
}
});
// 触发自定义事件,通知数据已加载
window.dispatchEvent(new CustomEvent('themeDataLoaded', {
detail: siteData
}));
log('数据注入完成');
});
// 发送就绪消息给父窗口
window.parent.postMessage({
type: 'THEME_READY'
}, '*');
}
// DOM 加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();