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