Files
cms-SiteForge/examples/Studio_V8_24_深层嵌套可视化编辑器_交互演示.html
浅念 8884e100f9 upgrade: adopt V8.31.1 (theme 1.3/1.4 redesigns, editor deep-canvas, guided launch, component library)
Overlay 190 upstream items (V8.22→8.31) preserving trade-pro theme,
registry tests, and design docs. Re-applied full patch set on identical
anchors (discovery/env_for/blocks.css/type-mapping/locale-override/
theme_color/frontend entries/test_theme_pro_v3 mods) plus compose pins.

Verified: theme suites 35/35; dual-baseline full run 415 passed vs
pristine 386+29 ours, FAILED-set diff EMPTY (same 10 Windows env fails).
Live: container 8.31.1-dev.1 healthy, hesupipe 192 entries intact,
trade-pro build 203 pages 0 errors.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-26 09:43:17 +08:00

55 lines
13 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>SiteForge Studio V8.24 · 深层画布编辑</title><style>
*{box-sizing:border-box}body{margin:0;background:#f3f5f8;color:#263447;font:14px system-ui,-apple-system,'Microsoft YaHei',sans-serif}button,input,select{font:inherit}button{cursor:pointer}header{height:70px;display:flex;align-items:center;gap:16px;padding:0 22px;background:white;border-bottom:1px solid #e3e8ef}header strong{font-size:19px}header small{color:#8795a6}header .sep{flex:1}header button{border:1px solid #e0e5eb;border-radius:8px;padding:9px 15px;background:white;color:#34485e}header .save{background:#ec772f;border-color:#ec772f;color:#fff;font-weight:750}.step{padding:11px 25px;background:#fff8f2;border-bottom:1px solid #f1d9c4;color:#ad642e;font-weight:700}main{display:grid;grid-template-columns:246px minmax(0,1fr) 290px;height:calc(100vh - 112px);min-height:620px}aside{background:#fff;overflow-y:auto;padding:21px;border-right:1px solid #e7eaf0}aside:last-child{border-right:0;border-left:1px solid #e7eaf0}h3{margin:0 0 20px;font-size:16px}p.hint{color:#8995a6;font-size:12px;line-height:1.7;margin-top:6px}.group{margin:16px 0 9px;color:#8b99a9;font-size:11px;font-weight:800;letter-spacing:1px}select,input{border:1px solid #dce3ed;border-radius:8px;padding:10px;width:100%;background:white;color:#293d55}.navitem,.childlist{border:1px solid #e3e9f0;border-radius:9px;padding:11px;margin-bottom:8px;background:#fbfcfe;cursor:pointer}.navitem.selected,.childlist.selected{border-color:#e98037;background:#fff8f2}.navitem small,.childlist small{display:block;color:#8898a6;margin-top:5px}.add{width:100%;margin-top:13px;background:#e8782f;color:white;padding:12px;border:0;border-radius:9px;font-weight:800}.canvas-shell{display:flex;flex-direction:column;min-width:0}.canvas-head{background:#fff;height:51px;display:flex;align-items:center;justify-content:space-between;padding:0 24px;border-bottom:1px solid #e7ebf0;font-size:12px;color:#77879a}.switch button{background:#f1f4f8;padding:8px 10px;border:0;border-radius:6px;margin:0 2px;color:#63768a}.switch button.on{background:#ed7e37;color:#fff}.stage{flex:1;overflow:auto;padding:28px;background:radial-gradient(#d6dde8 1px,transparent 1px);background-size:20px 20px}.page{margin:auto;max-width:1050px;min-width:0;background:white;box-shadow:0 12px 48px #2437471c;transition:width .2s}.page.phone{width:min(390px,100%)}.page.tablet{width:min(780px,100%)}.sitehead{height:70px;border-bottom:1px solid #e7e7e7;display:flex;align-items:center;justify-content:space-between;padding:0 7%;color:#25364d}.logo{font-size:18px;font-weight:900;color:#e8752f}.sitehead small{font-size:12px;color:#6f7e8d}.hero{padding:40px 7% 22px}.hero h1{font-size:clamp(25px,3vw,36px);margin:0 0 8px}.hero p{color:#8793a1;margin:0}.layout{padding:12px 7% 60px;display:grid;gap:16px;grid-template-columns:var(--cols,1fr 1fr)}.page.phone .layout{grid-template-columns:1fr!important}.page.phone .inner-grid{grid-template-columns:1fr!important}.page.phone .inner-grid[style*="1fr 1fr"]{grid-template-columns:1fr 1fr!important}.page.tablet .layout{grid-template-columns:1fr 1fr}.unit{border:2px dashed transparent;background:#f5f7fa;min-height:125px;border-radius:12px;padding:20px 17px;cursor:pointer;position:relative;overflow:visible}.unit:hover{border-color:#e8b48e}.unit.active{border-color:#e8752f;background:#fffaf6}.unit h2{font-size:18px;margin:12px 0 8px}.unit p{color:#7c8b9b;font-size:13px;line-height:1.65}.unit b{font-size:11px;color:#e27530}.unit .mini{display:none;gap:4px;position:absolute;right:7px;top:7px;z-index:2}.unit:hover .mini,.unit.active .mini{display:flex}.mini button{font-size:11px;border:0;background:#34465a;color:#fff;border-radius:5px;padding:5px 7px}.placeholder{background:linear-gradient(135deg,#f7d2aa,#e9edf5);height:100px;border-radius:6px;margin:6px 0}.ctrl{margin-bottom:17px}.ctrl label{display:block;font-size:12px;color:#78899b;font-weight:750;margin-bottom:7px}.ctrl input{font-size:13px}.buttons{display:flex;gap:6px;flex-wrap:wrap}.buttons button{background:#fff8f1;color:#965522;border:1px solid #edc9aa;border-radius:7px;font-size:11px;padding:8px 10px}.buttons button:hover{background:#ffe9d8}.notice{font-size:12px;color:#ba712d;background:#fff4e9;padding:10px 12px;border-radius:9px;line-height:1.6}.meta{padding:12px 25px;color:#8b95a4;font-size:11px;border-top:1px solid #dfe5ed;background:#fafbfd}@media(max-width:1100px){main{grid-template-columns:200px minmax(0,1fr) 240px}aside{padding:15px}.stage{padding:10px}}@media(max-width:750px){main{display:flex;flex-direction:column;height:auto}aside{border:0;overflow:visible}.stage{min-height:480px}.canvas-shell{order:-1}.sitehead{height:50px}}
</style></head><body>
<header><strong><span style="color:#ea792e">◈</span> SiteForge Studio</strong><small>V8.24 · 深层画布编辑演示</small><span class="sep"></span><span id="status" style="font-size:12px;color:#8392a1">演示草稿</span><button onclick="undo()">↶ 撤销</button><button onclick="save()" class="save">保存草稿</button></header>
<div class="step">① 点击画布中的组件 → ② 直接编辑右侧内容或选择布局 → ③ 保存草稿</div>
<main><aside><h3>页面内容</h3><label style="font-size:12px;color:#70839a">当前页面</label><select><option>首页 /</option></select><div class="group">页面结构</div><div class="navitem">▤ 封面横幅<small>网站主标题</small></div><div class="navitem selected">▥ 双列布局<small>可直接操作内层布局和组件</small></div><div id="tree"></div><button class="add" onclick="add()">+ 在本列新增组件</button><p class="hint">此页面使用本地演示数据。实际系统具备草稿、版本校验与构建接口。</p></aside>
<div class="canvas-shell"><div class="canvas-head"><span>● 可视化预览 · 点击卡片直接编辑</span><span class="switch"><button class="on" onclick="device('desktop',this)">电脑</button><button onclick="device('tablet',this)">平板</button><button onclick="device('phone',this)">手机</button></span></div><div class="stage"><div class="page" id="page"><div class="sitehead"><span class="logo">HESU AUTO</span><small>HOME VEHICLES ABOUT CONTACT ☰</small></div><div class="hero"><h1>China Sourcing. Global Delivery.</h1><p>内外层布局、手机列数与媒体选择均可直接操作。</p></div><div class="layout" id="layout"></div></div></div><div class="meta">画布修改只影响演示数据 · 不会连接、修改或发布正式网站</div></div>
<aside><h3>编辑选中的组件</h3><div id="selectedInfo" class="notice">点击中间任一组件开始编辑。</div><div class="group">内容</div><div class="ctrl"><label>组件标题</label><input id="title" oninput="field('title',this.value)" placeholder="请输入标题"></div><div class="ctrl"><label>说明文字</label><input id="description" oninput="field('description',this.value)" placeholder="请输入简介"></div><div class="group">布局设置</div><div class="buttons"><button onclick="preset('equal')">等宽两列</button><button onclick="preset('left')">左宽右窄</button><button onclick="preset('right')">左窄右宽</button></div><p class="hint">手机布局独立显示为单列,不会覆盖电脑端的列宽设置。</p><div class="group">内层响应式</div><div class="buttons"><button onclick="mobileCols(1)">手机 1 列</button><button onclick="mobileCols(2)">手机 2 列</button></div><p id="mobileStatus" class="hint">手机 1 列</p><div class="group">组件操作</div><div class="buttons"><button onclick="duplicate()">复制组件</button><button onclick="up()">上移</button><button onclick="remove()">删除</button></div></aside></main>
<script>
// This is a deliberately isolated offline demonstration. It never calls a server.
let state={
rows:[{id:1,title:'采购流程',description:'中国供应链与整车采购方案',col:0},{id:2,title:'海外枢纽',description:'覆盖不同地区的本地服务网络',col:1}],
nested:[{id:4,title:'车辆选择',description:'帮助客户选到合适车型',col:0},{id:5,title:'运输方案',description:'灵活选择运输方式',col:1}],
selected:4,preset:'equal',innerPreset:'equal',mobile:1
};
let history=[];
const $=id=>document.getElementById(id);
const esc=x=>String(x??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const current=()=>[...state.rows,...state.nested].find(x=>x.id===state.selected);
function snapshot(){history.push(JSON.stringify(state));if(history.length>30)history.shift()}
function changed(){ $('status').textContent='未保存的演示修改';draw() }
function card(r,name){return `<section class="unit ${state.selected===r.id?'active':''}" onclick="selectItem(${r.id})"><div class="mini"><button onclick="event.stopPropagation();selectItem(${r.id})">编辑</button><button onclick="event.stopPropagation();cloneItem(${r.id})">复制</button><button onclick="event.stopPropagation();dropItem(${r.id})">删除</button></div><b>${esc(name)}</b><h2 contenteditable="false" ondblclick="inlineTitle(event,${r.id})">${esc(r.title)}</h2><p>${esc(r.description)}</p></section>`}
function draw(){
const widths=v=>v==='left'?'2fr 1fr':v==='right'?'1fr 2fr':'1fr 1fr';
$('layout').style.setProperty('--cols',widths(state.preset));
const deepcols=[[],[]];state.nested.forEach(r=>deepcols[r.col].push(r));
const inner=`<div class="group" style="padding-top:5px;margin-bottom:10px">▥ 第二层布局 · 可直接点击里面的组件</div><div style="display:grid;grid-template-columns:${widths(state.innerPreset)};gap:10px" class="inner-grid" id="innerGrid">${deepcols.map((items,ci)=>`<div>${items.map(r=>card(r,'内层第 '+(ci+1)+' 列')).join('')}</div>`).join('')}</div>`;
const cols=[[],[]];state.rows.forEach(r=>cols[r.col].push(r));
$('layout').innerHTML=cols.map((items,ci)=>`<div>${items.map(r=>card(r,'外层第 '+(ci+1)+' 列')).join('')}${ci===0?inner:''}</div>`).join('');
if($('page').classList.contains('phone')){
$('innerGrid').style.gridTemplateColumns=state.mobile===1?'1fr':'1fr 1fr';
}
const both=[...state.rows.map(r=>({r,depth:0})),...state.nested.map(r=>({r,depth:1}))];
$('tree').innerHTML=both.map(({r,depth})=>`<div class="childlist ${state.selected===r.id?'selected':''}" onclick="selectItem(${r.id})" style="margin-left:${depth*13}px">${depth?'↳ 第二层':'第1层'} · ${esc(r.title)}<small>第${r.col+1}列 · 点击编辑</small></div>`).join('');
let row=current();$('selectedInfo').textContent=row?(state.nested.includes(row)?'第二层组件:':'外层组件:')+row.title:'点击画布上的组件';
$('title').value=row?.title||'';$('description').value=row?.description||'';
$('mobileStatus').textContent='手机设置:'+state.mobile+' 列,仅影响演示中的手机视图';
}
function selectItem(id){state.selected=id;draw()}
function field(key,value){const row=current();if(!row)return;row[key]=value;changed()}
function preset(value){snapshot();const row=current();if(row && state.nested.includes(row))state.innerPreset=value;else state.preset=value;changed()}
function mobileCols(n){snapshot();state.mobile=n;changed()}
function inlineTitle(event,id){event.stopPropagation();selectItem(id);const el=document.querySelector('.unit.active h2');if(!el)return;el.contentEditable='true';el.focus();const finish=()=>{const row=current();if(row){snapshot();row.title=el.textContent.trim().slice(0,140)}el.contentEditable='false';changed()};el.addEventListener('blur',finish,{once:true});el.addEventListener('keydown',e=>{if(e.key==='Enter'){e.preventDefault();el.blur()}})}
function cloneItem(id){const row=[...state.rows,...state.nested].find(x=>x.id===id);if(!row)return;snapshot();const next={...row,id:Math.max(...[...state.rows,...state.nested].map(x=>x.id))+1};(state.nested.includes(row)?state.nested:state.rows).push(next);state.selected=next.id;changed()}
function duplicate(){cloneItem(state.selected)}
function dropItem(id){snapshot();state.rows=state.rows.filter(x=>x.id!==id);state.nested=state.nested.filter(x=>x.id!==id);state.selected=[...state.nested,...state.rows][0]?.id||null;changed()}
function remove(){dropItem(state.selected)}
function up(){const group=state.nested.some(r=>r.id===state.selected)?state.nested:state.rows;const i=group.findIndex(r=>r.id===state.selected);if(i<1)return;snapshot();[group[i],group[i-1]]=[group[i-1],group[i]];changed()}
function add(){snapshot();const group=current()&&state.nested.includes(current())?state.nested:state.rows;const r={id:Math.max(...[...state.rows,...state.nested].map(x=>x.id))+1,title:'新组件',description:'点击并输入需要展示的内容',col:current()?.col||0};group.push(r);state.selected=r.id;changed()}
function undo(){if(!history.length)return;state=JSON.parse(history.pop());changed()}
function save(){$('status').textContent='演示草稿已保存(未连接服务器)'}
function device(name,button){$('page').className='page '+(name==='desktop'?'':name);document.querySelectorAll('.switch button').forEach(b=>b.classList.toggle('on',b===button));draw()}
draw();
</script></body></html>