/** * 模板数据注入脚本 * 功能:监听 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(); } })();