diff --git a/app/static/workspace.css b/app/static/workspace.css index 51918cf..11332ac 100644 --- a/app/static/workspace.css +++ b/app/static/workspace.css @@ -45,3 +45,19 @@ @media(max-width:620px){.sf-launch-hero{flex-direction:column;align-items:start}.sf-launch-step{padding:16px;gap:11px;grid-template-columns:32px minmax(0,1fr)}.sf-launch-controls{align-items:stretch;flex-direction:column}.sf-launch-field,.sf-launch-controls>.btn{width:100%;max-width:none}.sf-launch-check{align-items:start}} .theme-preview.trade-pro{background:linear-gradient(125deg,#0b3d91,#3d6dc1);color:#e8f1fb}.site-icon.trade-pro{background:#e8f1fb;color:#0b3d91} +/* ── 内容类型 Tab(分段控件)── */ +.type-tabs{display:flex;gap:0;border:1px solid var(--line);border-radius:10px;overflow:hidden;overflow-x:auto;margin-bottom:18px;background:#fff} +.type-tab{display:inline-flex;align-items:center;gap:6px;padding:10px 18px;font-size:13px;font-weight:650;color:#5c7083;background:none;border:0;border-right:1px solid var(--line);white-space:nowrap;transition:background .12s,color .12s;cursor:pointer} +.type-tab:last-child{border-right:0} +.type-tab:hover{background:#f4f7fb;color:#1e4089} +.type-tab.active{background:var(--blue);color:#fff} +.type-tab .count{font-size:11px;font-weight:700;background:rgba(0,0,0,.07);padding:2px 7px;border-radius:99px;line-height:1.3} +.type-tab.active .count{background:rgba(255,255,255,.2)} +/* ── 导航编辑弹窗 ── */ +.nav-edit-row{display:grid;grid-template-columns:1fr 1fr auto auto auto;gap:8px;align-items:center;padding:8px 0;border-bottom:1px solid #eef1f6} +.nav-edit-row:last-child{border-bottom:0} +.nav-edit-row input{padding:8px 10px;font-size:13px;border:1px solid #d9e1ed;border-radius:7px} +.nav-edit-row .handle{color:#94a3b8;font-size:16px;cursor:grab} +.nav-edit-actions{display:flex;justify-content:flex-end;gap:9px;margin-top:20px;padding-top:16px;border-top:1px solid var(--line)} +.nav-add-btn{display:flex;align-items:center;gap:6px;padding:8px 14px;border:1px dashed #b8c8dc;border-radius:8px;color:var(--blue);font-size:13px;font-weight:600;cursor:pointer;background:#f8fafd;margin-top:12px;width:100%;justify-content:center} +.nav-add-btn:hover{background:#edf3ff;border-color:var(--blue)} diff --git a/app/static/workspace.js b/app/static/workspace.js index 55df7aa..d5b2dce 100644 --- a/app/static/workspace.js +++ b/app/static/workspace.js @@ -124,7 +124,7 @@ function content(){if(!currentSite())return noSite();let arr=state.entries.filte const pageItems=arr.slice(start,start+PAGE_SIZE); const typeCounts={};state.entries.forEach(e=>typeCounts[e.type]=(typeCounts[e.type]||0)+1); const tabs=[{key:'',label:'全部',count:state.entries.length},...Object.entries(typeName).filter(([k])=>typeCounts[k]).map(([key,value])=>({key,label:value,count:typeCounts[key]}))]; - const tabHtml=tabs.map(t=>``).join(''); + const tabHtml=`
${tabs.map(t=>``).join('')}
`; const pagination=totalPages>1?`
第 ${state.page} / ${totalPages} 页 · 共 ${arr.length} 条
`:''; return htmlHeading('内容与页面','所有网页、产品、车型和新闻都在这里维护,保存后重新构建即可更新网站。',actionButton('+ 添加内容','new-entry'))+ `

内容列表

共 ${state.entries.length} 条内容。点击”设计页面”可进行可视化排版。

${tabHtml}
`+ @@ -134,7 +134,7 @@ function content(){if(!currentSite())return noSite();let arr=state.entries.filte function design(){if(!currentSite())return noSite();const s=state.site;const cards=Object.entries(themeName).map(([id,name])=>`
${name}

${name} ${s.theme===id?'当前使用':''}

${({automotive:'适合汽车出口、车型及品牌展示',industrial:'适合制造业、产品与技术参数',corporate:'适合集团公司、服务和案例',minimal:'适合作品展示和品牌形象','trade-pro':'适合外贸 B2B 英文站点与询盘'})[id]}

预览页面效果
`).join(''); return htmlHeading('外观与设计','先选一套网站风格,再打开可视化编辑器修改图文、导航和手机端布局。')+ `

第一步 · 选择行业主题

更换主题不会删除您已经录入的业务内容和 URL。

查看完整主题预览 ↗
${cards}
`+ - `

第二步 · 导航菜单

自定义网站的顶部导航:添加、删除、排序菜单项,支持三级下拉。

`+ + `

第二步 · 导航菜单

自定义网站的顶部导航:添加、删除、排序菜单项,支持三级下拉。

`+ `

第三步 · 设计网页

挑选要修改的页面;可在设计器中编辑导航、页眉、页脚及各设备布局。

选择要设计的页面

修改先保存为草稿,正式发布设计后才会参与下一次静态构建。

${state.entries.filter(e=>e.type==='page').slice(0,8).map(e=>`▤

${esc(e.title)}

${esc(e.route)}

›
`).join('')||'
尚无页面,请先添加内容。
'}
提示:网站设置用于公司信息和域名;可视化编辑器用于调整页面外观。两者不会混在同一表单里。
`; } function imports(){if(!currentSite())return noSite();return htmlHeading('导入与迁移','根据您已有的资料选择操作方式:Excel 产品、WordPress 网站,或者外部 API / 数据库。')+ @@ -312,7 +312,7 @@ async function init(){try{const me=await api('/api/auth/me');state.csrf=me.csrf; else {state.view=target==='sites'?'sites':'home';rerender();rememberView(true)} }catch(e){notifyError(e);$('#mainContent').innerHTML=emptyState('后台加载失败',e.message,'')}} // Delegated UI interactions keep navigation and form actions centrally auditable. -document.addEventListener('click',async ev=>{const sidebar=ev.target.closest('.nav-item[data-view]');if(sidebar&&!sidebar.hasAttribute('data-action')){navigate(sidebar.dataset.view);return;}const control=ev.target.closest('[data-action]');if(!control)return;const action=control.dataset.action;try{if(action==='navigate')navigate(control.dataset.view);if(action==='language-refresh')await loadLanguage();if(action==='set-type-tab'){state.typ=control.dataset.type;state.page=1;rerender()}if(action==='page-prev'){state.page=Math.max(1,state.page-1);rerender()}if(action==='page-next'){state.page++;rerender()}if(action==='new-site')openCreateWizard();if(action==='select-site')await selectSite(control.dataset.id);if(action==='new-entry')openEntry();if(action==='edit-entry')openEntry(control.dataset.id);if(action==='set-theme')await setTheme(control.dataset.theme);if(action==='build-now')await buildNow();if(action==='refresh-builds')await refreshBuilds();if(action==='check-readiness')await checkReadiness();if(action==='cancel-job')await cancelJob(control.dataset.id);if(action==='report')await showReport(control.dataset.id);if(action==='release-preflight')await releasePreflight();if(action==='release-approval')await releaseApproval();if(action==='release-deploy')await releaseDeploy();if(action==='release-rollback')await releaseRollback(control.dataset.target,control.dataset.id);if(action==='launch-refresh')await launchRefresh();if(action==='launch-build')await buildNow();if(action==='launch-report')await launchReport(control.dataset.id);if(action==='launch-request-approval')await launchRequestApproval();if(action==='launch-preflight')await launchPreflight();if(action==='launch-deploy')await launchDeploy()}catch(e){notifyError(e)}}); +document.addEventListener('click',async ev=>{const sidebar=ev.target.closest('.nav-item[data-view]');if(sidebar&&!sidebar.hasAttribute('data-action')){navigate(sidebar.dataset.view);return;}const control=ev.target.closest('[data-action]');if(!control)return;const action=control.dataset.action;try{if(action==='navigate')navigate(control.dataset.view);if(action==='language-refresh')await loadLanguage();if(action==='set-type-tab'){state.typ=control.dataset.type;state.page=1;rerender()}if(action==='page-prev'){state.page=Math.max(1,state.page-1);rerender()}if(action==='page-next'){state.page++;rerender()}if(action==='open-nav-editor')await openNavEditor();if(action==='new-site')openCreateWizard();if(action==='select-site')await selectSite(control.dataset.id);if(action==='new-entry')openEntry();if(action==='edit-entry')openEntry(control.dataset.id);if(action==='set-theme')await setTheme(control.dataset.theme);if(action==='build-now')await buildNow();if(action==='refresh-builds')await refreshBuilds();if(action==='check-readiness')await checkReadiness();if(action==='cancel-job')await cancelJob(control.dataset.id);if(action==='report')await showReport(control.dataset.id);if(action==='release-preflight')await releasePreflight();if(action==='release-approval')await releaseApproval();if(action==='release-deploy')await releaseDeploy();if(action==='release-rollback')await releaseRollback(control.dataset.target,control.dataset.id);if(action==='launch-refresh')await launchRefresh();if(action==='launch-build')await buildNow();if(action==='launch-report')await launchReport(control.dataset.id);if(action==='launch-request-approval')await launchRequestApproval();if(action==='launch-preflight')await launchPreflight();if(action==='launch-deploy')await launchDeploy()}catch(e){notifyError(e)}}); document.addEventListener('click',ev=>{const btn=ev.target.closest('[data-close]');if(btn)closeModal(btn.dataset.close)}); $('#siteForm').addEventListener('submit',saveSite);$('#entryForm').addEventListener('submit',saveEntry); document.addEventListener('submit',ev=>{if(ev.target.id==='settingsForm')saveSettings(ev);if(ev.target.id==='importForm')importFile(ev);if(ev.target.id==='langGroupForm')submitLanguageGroup(ev);if(ev.target.id==='langJoinForm')submitLanguageJoin(ev);if(ev.target.id==='langUnitForm')submitLanguageUnit(ev);if(ev.target.id==='langLinkForm')submitLanguageLink(ev)}); @@ -328,4 +328,50 @@ $('#logoutBtn').addEventListener('click',async()=>{try{await api('/api/auth/logo $('#menuToggle').addEventListener('click',()=>{const open=$('#sideMenu').classList.toggle('open');$('#mobileBackdrop').classList.toggle('open',open);$('#menuToggle').setAttribute('aria-expanded',String(open))}); $('#mobileBackdrop').addEventListener('click',closeMobile); document.addEventListener('keydown',ev=>{if(ev.key==='Escape')closeMobile()}); +// ── 导航编辑器 ── +let navItems=[],navDesignRevision=0,navSiteDesign=null; +async function openNavEditor(){ + const s=state.site;if(!s)return; + try{ + const d=await api(`/api/sites/${s.id}/site-design`); + navSiteDesign=d.config||{};navDesignRevision=d.revision||0; + navItems=structuredClone(navSiteDesign.nav||[]); + $('#navModal').showModal();drawNavEditor(); + }catch(e){toast('读取导航失败:'+e.message,true)} +} +function drawNavEditor(){ + const el=$('#navEditorBody'); + el.innerHTML=navItems.length?navItems.map((n,i)=>` + `).join('') + :'

暂无菜单项,点击下方按钮添加。

'; +} +async function saveNavDraft(){ + const s=state.site;if(!s||!navSiteDesign)return; + try{ + navSiteDesign.nav=navItems; + const r=await api(`/api/sites/${s.id}/site-design`,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({config:navSiteDesign,expected_revision:navDesignRevision})}); + navDesignRevision=r.revision||navDesignRevision+1; + toast('导航草稿已保存,点击「发布导航」使其参与下一次构建'); + }catch(e){toast('保存失败:'+e.message,true)} +} +async function publishNav(){ + const s=state.site;if(!s||!navSiteDesign)return; + try{ + await saveNavDraft(); + await api(`/api/sites/${s.id}/site-design/publish`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({expected_revision:navDesignRevision})}); + toast('导航已发布!重新构建网站后生效。'); + $('#navModal').close(); + }catch(e){toast('发布失败:'+e.message,true)} +} +function addNavItem(){navItems.push({title:'新菜单',route:'/'});drawNavEditor()} +function removeNav(i){navItems.splice(i,1);drawNavEditor()} +function moveNav(i,dir){const t=navItems[i];navItems[i]=navItems[i+dir];navItems[i+dir]=t;drawNavEditor()} +window.W={addNavItem,removeNav,moveNav,saveNavDraft,publishNav}; document.addEventListener('DOMContentLoaded',init); diff --git a/app/templates/workspace.html b/app/templates/workspace.html index be0a5f5..7242da8 100644 --- a/app/templates/workspace.html +++ b/app/templates/workspace.html @@ -57,4 +57,5 @@ +