[CMS-VISUAL] F2: 门户布局区块输出稳定 id + data-plb-id 锚点(display:contents 零布局影响)+ postMessage 预览监听脚本(门户侧 50 测试绿)
This commit is contained in:
@@ -353,10 +353,14 @@ public class PortalPageRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
/** 区块公共头:type/title,并合成为 category.name 供主题 fragment 复用(block.category.name 取标题) */
|
||||
/** 区块公共头:type/title,并合成为 category.name 供主题 fragment 复用(block.category.name 取标题)。
|
||||
* F2(docs/20):透传区块 id(编辑器实时预览 postMessage 重排的 DOM 锚点 data-plb-id)。 */
|
||||
private Map<String, Object> baseBlock(Map<String, Object> spec, String type, String fallbackTitle) {
|
||||
Map<String, Object> block = new HashMap<>();
|
||||
block.put("type", type);
|
||||
if (spec.get("id") != null) {
|
||||
block.put("id", String.valueOf(spec.get("id")));
|
||||
}
|
||||
String title = spec.get("title") == null ? "" : String.valueOf(spec.get("title")).trim();
|
||||
if (title.isEmpty()) {
|
||||
title = fallbackTitle;
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
注意坑④:th:replace(优先级 100)先于 th:case(300)执行——case 必须放父元素、replace 放子元素。 -->
|
||||
<th:block th:if="${layoutBlocks != null}">
|
||||
<th:block th:replace="~{'themes/common/fragments/home-blocks' :: style}" />
|
||||
<th:block th:replace="~{'themes/common/fragments/home-blocks' :: previewScript}" />
|
||||
<th:block th:each="block : ${layoutBlocks}">
|
||||
<div class="plb-anchor" th:attr="data-plb-id=${block['id']}" style="display:contents">
|
||||
<th:block th:switch="${block.type}">
|
||||
<th:block th:case="'article_list'">
|
||||
<th:block th:switch="${block.homeStyle}">
|
||||
@@ -48,6 +50,7 @@
|
||||
<th:block th:case="'notice_bar'"><th:block th:replace="~{${themeBase} + '/fragments/home-blocks' :: notice_bar(${block})}" /></th:block>
|
||||
<th:block th:case="*"><th:block th:replace="~{'themes/common/fragments/home-blocks' :: render(${block})}" /></th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
|
||||
@@ -83,11 +83,17 @@
|
||||
.plb-notice-warn{background:var(--plb-warn-bg);border-color:var(--plb-warn-border)}
|
||||
.plb-notice-warn .plb-notice-tag{color:var(--plb-warn-fg)}
|
||||
/* 全宽区块必须跨满整行:网格型主题(mint-breeze 系 .home-main 是三列)会把它们塞进单列,
|
||||
非网格父容器下 grid-column 无副作用,故由骨架自带。 */
|
||||
非网格父容器下 grid-column 无副作用,故由骨架自带。
|
||||
F2(docs/20):布局区块经 .plb-anchor(display:contents 预览锚点)包裹时按子选择器等效覆盖。 */
|
||||
main > .plb-ticker{grid-column:1 / -1}
|
||||
main > .plb-newshot{grid-column:1 / -1}
|
||||
main > .plb-section{grid-column:1 / -1}
|
||||
main > .plb-notice-bar{grid-column:1 / -1}
|
||||
main > .plb-anchor{display:contents;grid-column:auto}
|
||||
main > .plb-anchor > .plb-ticker{grid-column:1 / -1}
|
||||
main > .plb-anchor > .plb-newshot{grid-column:1 / -1}
|
||||
main > .plb-anchor > .plb-section{grid-column:1 / -1}
|
||||
main > .plb-anchor > .plb-notice-bar{grid-column:1 / -1}
|
||||
@media (max-width:768px){
|
||||
.plb-grid2{columns:1}
|
||||
.plb-imagewall{grid-template-columns:repeat(2,1fr)}
|
||||
@@ -326,3 +332,49 @@ main > .plb-notice-bar{grid-column:1 / -1}
|
||||
th:text="${block['noticeText']}">公告正文</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
F2(docs/20)实时预览监听脚本:管理端首页布局编辑器(iframe 加载正式门户)经 postMessage
|
||||
发送未保存的编排模拟——{type:'cms-layout-preview', order:[区块id…], cssVars:{…}}。
|
||||
行为:按 order 重排/隐藏 [data-plb-id] 锚点(display:contents 包裹层,零布局影响);
|
||||
未知 id(未保存的新区块)静默忽略 → 编辑器侧提供手动刷新兜底(D-3 降级路径)。
|
||||
安全:仅 DOM 移动/显隐与 CSS 变量赋值(textContent/innerHTML 一概不碰),payload 严格校形。
|
||||
引用位置:主题 home.html 布局分支随 :: style 一并引入一次。
|
||||
-->
|
||||
<th:block th:fragment="previewScript">
|
||||
<script>
|
||||
(function () {
|
||||
if (window.__cmsLayoutPreviewBound) { return; }
|
||||
window.__cmsLayoutPreviewBound = true;
|
||||
window.addEventListener('message', function (ev) {
|
||||
var data = ev.data;
|
||||
if (!data || data.type !== 'cms-layout-preview') { return; }
|
||||
var anchors = Array.prototype.slice.call(document.querySelectorAll('[data-plb-id]'));
|
||||
if (!anchors.length) { return; }
|
||||
var order = Array.isArray(data.order) ? data.order.filter(function (x) {
|
||||
return typeof x === 'string' && x.length <= 64;
|
||||
}) : null;
|
||||
if (order) {
|
||||
var byId = {};
|
||||
anchors.forEach(function (a) { byId[a.getAttribute('data-plb-id')] = a; });
|
||||
var parent = anchors[0].parentNode;
|
||||
order.forEach(function (id) {
|
||||
var a = byId[id];
|
||||
if (a) { a.style.display = 'contents'; parent.appendChild(a); }
|
||||
});
|
||||
anchors.forEach(function (a) {
|
||||
if (order.indexOf(a.getAttribute('data-plb-id')) < 0) { a.style.display = 'none'; }
|
||||
});
|
||||
}
|
||||
if (data.cssVars && typeof data.cssVars === 'object') {
|
||||
var bad = /[^a-zA-Z0-9-_, .#%()]/;
|
||||
Object.keys(data.cssVars).forEach(function (k) {
|
||||
if (/^--[a-zA-Z][\w-]*$/.test(k) && !bad.test(String(data.cssVars[k]))) {
|
||||
document.documentElement.style.setProperty(k, data.cssVars[k]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</th:block>
|
||||
|
||||
@@ -26,8 +26,12 @@
|
||||
<!-- 首页布局模式(docs/16 §4.4):home_layout 落库后按区块序列渲染(common 兜底骨架,本主题不另设形态) -->
|
||||
<th:block th:if="${layoutBlocks != null}">
|
||||
<th:block th:replace="~{'themes/common/fragments/home-blocks' :: style}" />
|
||||
<th:block th:replace="~{'themes/common/fragments/home-blocks' :: previewScript}" />
|
||||
<!-- F2:data-plb-id 锚点(display:contents)供编辑器实时预览 postMessage 重排 -->
|
||||
<th:block th:each="block : ${layoutBlocks}">
|
||||
<th:block th:replace="~{'themes/common/fragments/home-blocks' :: render(${block})}" />
|
||||
<div class="plb-anchor" th:attr="data-plb-id=${block['id']}" style="display:contents">
|
||||
<th:block th:replace="~{'themes/common/fragments/home-blocks' :: render(${block})}" />
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
否则未命中的 case 分支也会求值 fragment 表达式(缺键 Map 访问直接抛错整页 500)。 -->
|
||||
<th:block th:if="${layoutBlocks != null}">
|
||||
<th:block th:replace="~{'themes/common/fragments/home-blocks' :: style}" />
|
||||
<th:block th:replace="~{'themes/common/fragments/home-blocks' :: previewScript}" />
|
||||
<th:block th:each="block : ${layoutBlocks}">
|
||||
<div class="plb-anchor" th:attr="data-plb-id=${block['id']}" style="display:contents">
|
||||
<th:block th:switch="${block.type}">
|
||||
<th:block th:case="'article_list'">
|
||||
<th:block th:switch="${block.homeStyle}">
|
||||
@@ -48,6 +50,7 @@
|
||||
<th:block th:case="'notice_bar'"><th:block th:replace="~{${themeBase} + '/fragments/home-blocks' :: notice_bar(${block})}" /></th:block>
|
||||
<th:block th:case="*"><th:block th:replace="~{'themes/common/fragments/home-blocks' :: render(${block})}" /></th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user