更新显示架构

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
+13
View File
@@ -0,0 +1,13 @@
{
"name": "默认模板",
"description": "标准企业官网模板,适用于各类企业展示",
"version": "1.0.0",
"author": "System",
"fields": {
"site_name": "网站名称",
"banner": "轮播图列表",
"news": "新闻列表",
"solutions": "解决方案",
"partners": "合作伙伴"
}
}
+82
View File
@@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title data-field="site_name">企业官网</title>
<link rel="stylesheet" href="./styles/main.css">
</head>
<body>
<!-- 头部 -->
<header class="header">
<div class="container">
<h1 data-field="site_name">企业官网</h1>
<nav class="nav">
<a href="/">首页</a>
<a href="/news">新闻资讯</a>
<a href="/solutions">解决方案</a>
<a href="/contact">联系我们</a>
</nav>
</div>
</header>
<!-- 轮播图 -->
<section class="banner">
<div class="banner-slides">
<div class="slide active" data-field="banner">
<img src="./images/banner1.jpg" alt="Banner 1">
<div class="banner-content">
<h2>欢迎来到我们的网站</h2>
<p>专业的企业数字化解决方案提供商</p>
</div>
</div>
</div>
</section>
<!-- 新闻资讯 -->
<section class="news section">
<div class="container">
<h2 class="section-title">新闻资讯</h2>
<div class="news-list" data-field="news">
<div class="news-item">
<h3>公司动态</h3>
<p>这里是新闻内容...</p>
</div>
</div>
</div>
</section>
<!-- 解决方案 -->
<section class="solutions section">
<div class="container">
<h2 class="section-title">解决方案</h2>
<div class="solutions-grid" data-field="solutions">
<div class="solution-card">
<h3>解决方案一</h3>
<p>详细描述...</p>
</div>
</div>
</div>
</section>
<!-- 合作伙伴 -->
<section class="partners section">
<div class="container">
<h2 class="section-title">合作伙伴</h2>
<div class="partners-grid" data-field="partners">
<div class="partner-logo">Partner Logo</div>
</div>
</div>
</section>
<!-- 底部 -->
<footer class="footer">
<div class="container">
<p>&copy; 2024 <span data-field="site_name">企业官网</span>. All rights reserved.</p>
</div>
</footer>
<!-- 数据注入脚本 -->
<script src="./js/theme-loader.js"></script>
</body>
</html>
+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();
}
})();
+153
View File
@@ -0,0 +1,153 @@
/* 基础样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
line-height: 1.6;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.section {
padding: 60px 0;
}
.section-title {
text-align: center;
font-size: 32px;
margin-bottom: 40px;
color: #333;
}
/* 头部 */
.header {
background: #fff;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
}
.header .container {
display: flex;
justify-content: space-between;
align-items: center;
height: 70px;
}
.header h1 {
font-size: 24px;
color: #1890ff;
}
.nav a {
margin-left: 30px;
text-decoration: none;
color: #333;
transition: color 0.3s;
}
.nav a:hover {
color: #1890ff;
}
/* 轮播图 */
.banner {
margin-top: 70px;
height: 500px;
overflow: hidden;
position: relative;
}
.banner-slides .slide {
display: none;
}
.banner-slides .slide.active {
display: block;
}
.banner-slides .slide img {
width: 100%;
height: 500px;
object-fit: cover;
}
.banner-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: #fff;
}
.banner-content h2 {
font-size: 48px;
margin-bottom: 20px;
}
.banner-content p {
font-size: 20px;
}
/* 新闻 */
.news-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 30px;
}
.news-item {
background: #f5f5f5;
padding: 20px;
border-radius: 8px;
}
/* 解决方案 */
.solutions-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
.solution-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 40px 20px;
border-radius: 8px;
text-align: center;
color: #fff;
}
/* 合作伙伴 */
.partners-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 20px;
}
.partner-logo {
background: #f5f5f5;
padding: 30px;
text-align: center;
border-radius: 8px;
}
/* 底部 */
.footer {
background: #333;
color: #fff;
padding: 30px 0;
text-align: center;
}