functionlogin(){return`<div class="login"><section class="login-visual">${photo(0)}<div class="brand">${icon('logo')}PhotoLive</div><div class="login-copy"><p style="font-size:10px;letter-spacing:3px;margin-bottom:20px">EVERY EVENT. EVERY MOMENT.</p><h1>${T('Bring your events into focus.','每个精彩现场,<br>从这里开始。')}</h1><p>${T('One workspace for your event albums, team and shared moments.','集中管理活动相册、协作团队与照片,让每个精彩瞬间及时抵达观众。')}</p></div><p style="position:relative;z-index:2;color:#8093af;font-size:11px">PhotoLive · Staff workspace</p></section><section class="login-form-area"><form id="loginForm" class="login-form"><div class="brand">${icon('logo')}PhotoLive</div><h1>${T('Welcome back','欢迎回来')}</h1><p>${T('Sign in to your company workspace','登录企业影像工作空间')}</p>${field(T('Account','账号'),'loginAccount','demo.admin')}${field(T('Password','密码'),'loginPassword','','password')}<div class="error" id="formError"></div><button type="submit" class="btn primary">${T('Sign in to preview','进入预览')}</button><p class="small muted" style="margin-top:22px">${T('Accounts are provided by your administrator. No registration or Feishu sign-in.','账号由企业提供,不支持自行注册或飞书登录。')}</p><div class="info-strip" style="margin-top:26px">${icon('help')}${T('Offline prototype. Do not enter real credentials.','离线交互原型,请勿输入真实账号密码。')}</div></form></section></div>`}
functionalbumList(){constrows=albums.map((a,i)=>({a,i})).filter(({a})=>(!albumQuery||JSON.stringify(a).toLowerCase().includes(albumQuery.toLowerCase()))&&(albumStatus==='all'||String(a.public)===albumStatus));returnheading(T('Albums','相册管理'),T('A clear view of every event and the team behind it.','以活动为中心,管理相册、团队和每一张照片。'),btn(T('New album','新建相册'),'go:8','primary','plus'))+`<div class="toolbar"><div class="search">${icon('photo')}<input id="albumSearch" value="${esc(albumQuery)}" placeholder="${T('Search loaded albums','搜索当前已加载相册')}"></div><select id="albumStatus" aria-label="Album status"><option value="all">${T('All access states','全部公开状态')}</option><option value="true" ${albumStatus==='true'?'selected':''}>${T('Public','公开')}</option><option value="false" ${albumStatus==='false'?'selected':''}>${T('Private','未公开')}</option></select><div class="push seg"><button data-action="albumMode:grid" class="${albumMode==='grid'?'active':''}">${icon('photo')}</button><button data-action="albumMode:list" class="${albumMode==='list'?'active':''}">${icon('album')}</button></div></div><div class="summary-line"><span>${T('Loaded sample albums','已加载示例相册')} · ${rows.length}</span><span>${T('Sorted by creation time · cursor-based loading','按创建时间排序 · 沿用游标加载')}</span></div>${albumMode==='grid'?`<div class="album-grid">${rows.map(({a,i})=>`<articleclass="album"><buttonclass="album-cover"data-action="album:${i}"aria-label="${title(a)}">${photo(a.photo)}${pill(a.public?T('Public','公开'):T('Private','未公开'),a.public?'green':'gray')}</button><div class="album-info"><h2><button data-action="album:${i}" style="padding:0;font-weight:650">${title(a)}</button></h2><p class="meta">${a.date} · ${a.venue}</p><div class="album-footer"><div class="person">${avatar(a.owner)}<span>${a.owner}</span></div>${pill(a.upload?T('Uploads on','可上传'):T('Uploads off','上传关闭'),a.upload?'':'gray')}</div></div></article>`).join('')}</div>`:`<divclass="card">${table([T('Album','相册'),T('Owner','创建者'),T('Eventdate','活动日期'),T('Publicaccess','公开访问'),T('Actions','操作')],rows.map(({a,i})=>[title(a),a.owner,a.date,pill(a.public?T('Public','公开'):T('Private','未公开'),a.public?'green':'gray'),btn(T('Manage','管理'),'album:'+i,'smalloutline')]))}</div>`}<divclass="load-more">${rows.length?T('Allsamplealbumsareloaded','示例相册已全部加载'):T('Noalbumsmatchthesefilters','没有符合筛选条件的相册')}</div>`}
functionphotosPage(){constlist=filteredPhotos();returnalbumHeader()+`<div class="toolbar"><div class="seg">${['all','published','hidden','unpublished','deleted'].map(s=>`<button class="${photoFilter===s?'active':''}" data-action="photoFilter:${s}">${s==='all'?T('All photos','全部照片'):visibility(s)}</button>`).join('')}</div><select id="photoCategory" aria-label="Category"><option value="all">${T('All categories','全部分类')}</option>${cats.map((c,i)=>`<option value="${i}" ${String(i)===photoCategory?'selected':''}>${esc(c[lang])}</option>`).join('')}</select><div class="search">${icon('photo')}<input id="photoSearch" value="${esc(photoQuery)}" placeholder="${T('Search sample filenames','搜索示例文件名')}"></div><div class="seg push"><button data-action="photoMode:grid" class="${photoMode==='grid'?'active':''}">${icon('photo')}</button><button data-action="photoMode:list" class="${photoMode==='list'?'active':''}">${icon('album')}</button></div></div><div class="selection-bar" id="selectionBar" ${selected.size?'':'hidden'}><span id="selectedCount">${selected.size}${T('selected','张已选')}</span>${btn(T('Hide selected','隐藏选中照片'),'batchHide','small outline','eye')}<span class="small">${T('Only published photos can be selected','仅已公开照片可批量选择')}</span></div>${photoMode==='grid'?`<div class="photo-grid">${list.map(i=>`<article class="photo-item"><div class="photo-image ${selected.has(i)?'selected':''}"><button data-action="photo:${i}" aria-label="${T('View photo','查看照片')}${i+1}">${photoStates[i]==='deleted'?`<div class="empty">${T('Deleted','已删除')}</div>`:photo(i)}</button><input class="photo-check" type="checkbox" value="${i}" aria-label="${T('Select photo','选择照片')}${i+1}" ${selected.has(i)?'checked':''}${photoStates[i]!=='published'?'disabled':''}>${pill(visibility(photoStates[i]),photoStates[i]==='published'?'green':'gray')}</div><div class="photo-caption"><div><strong>DSC0${4872+i}.jpg</strong><p>${people[i%3].name} · Oct 15, 2026</p></div><button data-action="photo:${i}" aria-label="${T('Photo actions','照片操作')}">${icon('more')}</button></div></article>`).join('')}</div>`:`<div class="card">${table([T('Photo','照片'),T('Processing','处理状态'),T('Visibility','可见性'),T('Uploader','上传者'),T('Likes (replica)','点赞数(副本)'),T('Actions','操作')],list.map(i=>[`<div class="person">${photo(i,'tiny-photo')}DSC0${4872+i}.jpg</div>`,pill(i===10?T('Processing','处理中'):T('Ready','已就绪'),i===10?'':'green'),pill(visibility(photoStates[i]),photoStates[i]==='published'?'green':'gray'),people[i%3].name,[128,86,54,102][i%4],btn(T('Details','详情'),'photo:'+i,'small')]))}</div>`}<div class="load-more">${T('Sample photos · Statistics replica updated at 14:32 · No live API connection','示例照片 · 统计副本更新时间 14:32 · 未连接真实接口')}</div>`}
functionmembersPage(){returnalbumHeader()+`<div class="card"><div class="card-head"><div><h2>${T('Album members','相册成员')}</h2><p class="small muted" style="margin-top:6px">${T('Owners manage all photos. Collaborators manage their own uploads.','创建者管理全部照片;协作者管理自己上传的照片。')}</p></div>${btn(T('Invite colleague','邀请同事'),'invite','primary','users')}</div>${table([T('Member','成员'),T('Account','账号'),T('Role','角色'),T('Scope','权限范围'),T('Actions','操作')],memberIds.map((id,index)=>{constp=people[id];return[`<div class="person">${avatar(p.name)}<strong>${p.name}</strong></div>`,p.account,pill(index===0?T('Owner','创建者'):T('Collaborator','协作者'),index===0?'':'gray'),index===0?T('All photos and members','全部照片及成员'):T('Own uploaded photos','自己上传的照片'),index===0?btn(T('Transfer ownership','交接相册'),'transfer','small'):btn(T('Remove','移除'),'removeMember:'+id,'small danger')]}))}</div>`}
functioncategoriesPage(){returnalbumHeader()+`<div class="card"><div class="card-head"><div><h2>${T('Photo categories','照片分类')}</h2><p class="small muted">${T('Keep both content languages together when saving.','保存时保留所有语言内容,避免覆盖其他语言。')}</p></div>${btn(T('New category','新增分类'),'category:new','primary','plus')}</div>${table([T('Category','分类名称'),T('Chinese','中文'),T('English','English'),T('Actions','操作')],cats.map((c,i)=>[`<div class="person"><span class="avatar">${icon('tag')}</span><strong>${esc(c[lang])}</strong></div>`,esc(c.zh),esc(c.en),btn(T('Edit','编辑'),'category:'+i,'small')+' '+btn(T('Delete','删除'),'deleteCategory:'+i,'small danger')]))}<p class="small muted" style="margin-top:18px">${T('A category with photos cannot be deleted.','已有照片的分类不能直接删除。')}</p></div>`}
functionwatermarkPage(){returnalbumHeader()+`<div class="two-col equal"><div class="card"><div class="card-head"><h2>${T('Watermark settings','水印配置')}</h2>${pill('v'+wmVersion,'green')}</div><form id="watermarkForm">${switchRow(T('Enable watermark','启用水印'),T('Apply to generated viewing variants','应用于生成的浏览变体'),'wmOn',true)}<div class="form-grid" style="margin-top:20px">${field(T('Watermark text','水印文字'),'wmText','PhotoLive')}${select(T('Position','位置'),'wmPosition',[['bottom-right',T('Bottom right','右下')],['bottom-left',T('Bottom left','左下')],['top-right',T('Top right','右上')],['top-left',T('Top left','左上')],['center',T('Center','居中')]],wmPosition)}<label class="field">${T('Opacity','透明度')}<input id="wmOpacity" type="range" min="0" max="100" value="70"></label>${field(T('Font size','字号'),'wmFont','20','number')}</div><button type="submit" class="btn primary">${T('Save and generate','保存并生成')}</button><span class="small muted"> ${T('Demo job only','仅演示任务')}</span></form></div><div class="card"><div class="card-head"><h2>${T('Visual preview','效果预览')}</h2><span class="small muted">CSS ${T('simulation','示意')}</span></div><div class="watermark-preview">${photo(1)}<span class="watermark-text" id="wmPreview">PhotoLive</span></div><p class="small muted" style="margin-top:14px">${T('Preview placement only. Final rendering is produced by the server.','此处仅示意位置,最终水印由服务端生成。')}</p></div></div><div class="card"><div class="card-head"><h2>${T('Generation history','生成任务')}</h2>${btn(T('Regenerate','重新生成'),'regenerate','outline','flip')}</div>${table([T('Version','版本'),T('State','状态'),T('Processed','已处理'),T('Failed','失败'),T('Created','创建时间')],[['v'+wmVersion,pill(T('Completed','已完成'),'green'),'124 / 124','0','2026-09-22 14:20'],['v2',pill(T('Completed','已完成'),'green'),'118 / 118','0','2026-09-21 16:42']])}</div>`}
functionsharingPage(){returnalbumHeader()+`<div class="two-col equal"><div class="card share-card">${photo(albums[albumIndex].photo,'cover')}<h2>${title()}</h2><p>${albums[albumIndex].date} · ${albums[albumIndex].venue}</p><img class="qr" src="assets/demo-qr.svg" alt="Demonstration QR code"><p>${T('Demo code · example.com · not a public event link','演示二维码 · 指向 example.com · 非真实活动链接')}</p></div><div class="card"><h2>${T('Share your event','分享活动相册')}</h2><p class="muted" style="margin:14px 0 22px">${T('Visitors browse without signing in. Public access must be enabled.','观众凭链接或扫码免登录浏览,须先开启相册公开访问。')}</p><div class="info-strip ${albums[albumIndex].public?'':'warn'}">${icon('globe')}${albums[albumIndex].public?T('Public access is enabled in this sample.','当前示例已开启公开访问。'):T('Public access is disabled in this sample.','当前示例已关闭公开访问。')}</div>${field(T('Preview link','预览链接'),'shareLink',newURL('../viewer-preview-2026-09-22.html',location.href).href)}<div class="actions">${btn(T('Copy preview link','复制预览链接'),'copyShare','primary','link')}${btn(T('Open viewer','打开观众页'),'viewer','outline')}</div><p class="small muted" style="margin-top:24px">${T('Local file links work on this computer only. Production QR codes must use the public album URL.','本地链接仅用于本机预览,生产二维码必须使用真实公开相册 URL。')}</p></div></div>`}
functionalbumForm(edit){consta=albums[albumIndex];returnheading(edit?T('Edit album','编辑相册'):T('Create album','新建相册'),T('Event details, content languages and public access in one place.','统一配置活动资料、多语言内容与访问权限。'),btn(T('Cancel','取消'),'go:2'))+`<form id="albumForm"><div class="two-col"><div class="card"><div class="card-head"><h2>${T('Event details','活动资料')}</h2>${pill(T('Chinese + English','中文 + English'))}</div><div class="form-grid">${field(T('Title (Chinese)','标题(中文)'),'titleZh',edit?a.zh:'')}${field(T('Title (English)','标题(English)'),'titleEn',edit?a.title:'')}${field(T('Start date','开始日期'),'startDate','2026-10-15','date')}${field(T('End date','结束日期'),'endDate','2026-10-19','date')}${select(T('Time zone','时区'),'timezone',[['Asia/Shanghai','Asia/Shanghai'],['Europe/London','Europe/London'],['America/New_York','America/New_York']],'Asia/Shanghai')}${select(T('Default language','默认语言'),'contentLang',[['zh','中文'],['en','English']],'zh')}${field(T('Venue (Chinese)','地点(中文)'),'venueZh',edit?'广州':'')}${field(T('Venue (English)','地点(English)'),'venueEn',edit?a.venue:'')}<label class="field wide">${T('Description (Chinese)','说明(中文)')}<textarea id="descriptionZh">${edit?'欢迎浏览本次活动的精彩照片。':''}</textarea></label><label class="field wide">${T('Description (English)','说明(English)')}<textarea id="descriptionEn">${edit?'Discover the moments from this event.':''}</textarea></label></div></div><div><div class="card"><h2>${T('Access settings','访问设置')}</h2>${switchRow(T('Staff uploads','工作人员上传'),T('Team members can upload photos','团队成员可上传照片'),'albumUpload',a.upload)}${switchRow(T('Public access','公开访问'),T('Visitors with the link can browse','凭链接免登录浏览'),'albumPublic',a.public)}${switchRow(T('Photo downloads','允许下载'),T('Download viewing variants','观众可下载浏览图'),'albumDownload',true)}${switchRow(T('Original downloads','原图下载'),T('Requires photo downloads','须先允许下载'),'albumOriginal',false)}</div><div class="info-strip" style="margin-top:18px">${icon('help')}${T('Preview changes are stored in this tab only.','预览变更仅保留在当前标签页内。')}</div><div id="formError" class="error"></div><button class="btn primary" style="width:100%" type="submit">${T('Save album','保存相册')}</button></div></div></form>`}
functionusersPage(){returnheading(T('Accounts','账号管理'),T('Provisioned company accounts, credential state and local access controls.','统一查看企业来源状态、登录凭据与本地账号治理。'),btn(T('View user sync','查看用户同步'),'go:11','outline','flip'))+`<div class="info-strip">${icon('help')}${T('User information is pulled from the company directory. No public registration. Source state and local lock are independent.','用户信息来自企业接口定期拉取,不开放注册;来源状态与本地锁定相互独立。')}</div><div class="card"><div class="toolbar"><div class="search">${icon('user')}<input id="userSearch" placeholder="${T('Search account or name','搜索账号或姓名')}"></div><select id="userStatus" aria-label="Account status"><option value="all">${T('All states','全部状态')}</option><option value="locked">${T('Locally locked','本地锁定')}</option><option value="pending">${T('Pending activation','待激活')}</option></select>${btn(T('Reset filters','重置筛选'),'resetUserFilters')}</div><div id="usersTable">${usersTable()}</div></div>`}
functionslotEditor(){consts=slots[slotIndex];returnheading(T('Configure widget','配置展示位'),esc(lang==='zh'?s.zh:s.name),btn(T('Back to widgets','返回展示位'),'go:13'))+`<div class="two-col equal"><div class="card"><form id="slotForm">${field(T('Name','名称'),'slotName',lang==='zh'?s.zh:s.name)}${select(T('Display mode','展示模式'),'slotMode',[['v2',T('v2 · Vertical stream','v2 · 照片上滚')],['v1',T('v1 · Existing carousel','v1 · 保留旧轮播')]],s.mode)}<div id="v2Fields"><div class="form-grid">${field(T('Width (fixed)','宽度(固定)'),'slotWidth','195','number')}${field(T('Height (fixed)','高度(固定)'),'slotHeight','280','number')}${field(T('Scroll speed (px/s)','滚动速度(px/s)'),'slotSpeed',s.speed,'number')}${select(T('Position','悬浮位置'),'slotPosition',[['right',T('Right','右侧')],['left',T('Left','左侧')]],'right')}</div>${switchRow(T('Like bubbles','点赞气泡'),T('Real reactions in production; simulated here','正式版使用真实事件,此处仅模拟'),'slotReactions',true)}</div><div id="v1Fields" hidden>${field(T('Width','宽度'),'legacyWidth','320','number')}${field(T('Interval (ms)','切图间隔(ms)'),'legacyInterval','5000','number')}<p class="small muted">${T('Legacy settings are retained. The preview at right depicts the new design.','旧版配置继续保留。右侧展示新版设计参考。')}</p></div><div class="form-grid" style="margin-top:18px">${field(T('Horizontal offset','水平偏移'),'offsetX','16','number')}${field(T('Bottom offset','底部偏移'),'offsetY','24','number')}</div>${switchRow(T('Start collapsed on mobile','手机默认收起'),T('User choice has priority','用户主动选择优先'),'slotCollapsed',true)}<div class="error" id="formError"></div><button class="btn primary" type="submit">${T('Save configuration','保存配置')}</button></form></div><div class="card"><div class="card-head"><h2>${T('Unsaved preview','未保存预览')}</h2>${pill('195 × 280 px')}</div>${embedPreview()}<div class="actions" style="margin-top:15px">${btn(T('Simulate a reaction','模拟点赞事件'),'embedHeart','small outline')}${btn(T('Open full widget demo','打开完整组件原型'),'widgetDemo','small')}</div><p class="small muted" style="margin-top:14px">${T('Presentation mock only. The standalone widget demo implements the precise 16 px/s stream.','此面板为样式示意;独立组件原型实现精确 16 px/s 上滚。')}</p></div></div>`}
functionbrandingPage(){returnalbumHeader()+`<div class="two-col equal"><div class="card"><h2>${T('Public album banner','观众页宣传图')}</h2><p class="small muted" style="margin:9px 0 20px">${T('Prefer a wide image. Do not crop away embedded poster text.','建议使用横幅图片,保留海报中的文字内容。')}</p>${photo(albums[albumIndex].photo,'cover')}<div class="actions" style="margin-top:18px">${btn(T('Choose sample banner','选择示例宣传图'),'banner','primary','photo')}${btn(T('Preview public album','预览观众相册'),'viewer','outline')}</div></div><div class="card"><h2>${T('Asset workflow','资产管理')}</h2><div class="info-strip" style="margin-top:20px">${icon('upload')}${T('Init → OSS direct upload → completion verification','初始化凭证 → OSS 直传 → 完成核验')}</div><p class="muted">${T('The existing API accepts banner, cover and logo asset types. Production must keep signature expiry and upload verification behavior. This prototype only swaps sample photographs.','现有接口支持 banner、cover、logo 资产类型。正式实现保留签名过期与上传核验流程。本原型仅切换示例照片。')}</p><h3 style="margin:24px 0 10px">${T('Current sample','当前示例')}</h3><p class="small muted">event-banner.jpg · image/jpeg · ${T('Sample preview','示意预览')}</p></div></div>`}
functionexceptionPage(code){consttext={403:[T('Access restricted','暂无访问权限'),T('Your current account cannot access this workspace.','当前账号无权访问此页面,请联系公司管理员。')],404:[T('Page not found','页面不存在'),T('The link may have changed or the resource is unavailable.','链接可能已失效,或资源暂不可用。')],500:[T('Something went wrong','服务暂时不可用'),T('Please try again. Your saved data has not been changed by this preview.','请稍后重试,已保存的数据不会因本次预览发生变化。')]}[code];return`<section class="exception"><strong>${code}</strong><h1>${text[0]}</h1><p>${text[1]}</p>${btn(T('Back to albums','返回相册'),'go:2','primary')}</section>`}
functionbind(){constlocale=$('#language');if(locale)locale.onchange=e=>{lang=e.target.value;render()};constloginForm=$('#loginForm');if(loginForm)loginForm.onsubmit=e=>{e.preventDefault();if(!$('#loginAccount').value.trim()){$('#formError').textContent=T('Enter a sample account.','请输入演示账号。');return}go(2)};filterInput('albumSearch',albumQuery,x=>albumQuery=x);filterInput('photoSearch',photoQuery,x=>photoQuery=x);if($('#albumStatus'))$('#albumStatus').onchange=e=>{albumStatus=e.target.value;render()};if($('#photoCategory'))$('#photoCategory').onchange=e=>{photoCategory=e.target.value;render()};$$('.photo-check').forEach(el=>el.onchange=()=>{consti=Number(el.value);if(el.checked)selected.add(i);elseselected.delete(i);render()});constfilterUsers=()=>$('#usersTable').innerHTML=usersTable($('#userSearch').value,$('#userStatus').value);if($('#userSearch')){$('#userSearch').oninput=filterUsers;$('#userStatus').onchange=filterUsers}$$('[data-slot-toggle]').forEach(el=>el.onchange=()=>{consts=slots[Number(el.dataset.slotToggle)];s.enabled=el.checked;s.version++;render();toast(T('Sample widget state updated','示例展示位状态已更新'))});
constform=$('#albumForm');if(form){$('#albumDownload').onchange=e=>{$('#albumOriginal').disabled=!e.target.checked;if(!e.target.checked)$('#albumOriginal').checked=false};form.onsubmit=e=>{e.preventDefault();if(!$('#titleZh').value.trim()||!$('#titleEn').value.trim()||!$('#startDate').value||$('#endDate').value<$('#startDate').value){$('#formError').textContent=T('Enter both sample titles and a valid date range.','请填写示例双语标题及有效日期范围。');return}constdata={title:$('#titleEn').value,zh:$('#titleZh').value,date:$('#startDate').value+' – '+$('#endDate').value,venue:$('#venueEn').value||$('#venueZh').value,owner:'Alex Morgan',public:$('#albumPublic').checked,upload:$('#albumUpload').checked,photo:0};if(page===8){albums.unshift(data);albumIndex=0}elsealbums[albumIndex]={...albums[albumIndex],...data};go(3)}}
if($('#watermarkForm')){constpreview=()=>{constel=$('#wmPreview');el.textContent=$('#wmText').value;el.hidden=!$('#wmOn').checked;el.style.opacity=$('#wmOpacity').value/100;el.style.fontSize=Math.min(72,Math.max(8,Number($('#wmFont').value)||20))+'px';constpos=$('#wmPosition').value;el.style.top=pos.startsWith('top')?'18px':pos==='center'?'50%':'auto';el.style.bottom=pos.startsWith('bottom')?'18px':'auto';el.style.left=pos.endsWith('left')?'18px':pos==='center'?'50%':'auto';el.style.right=pos.endsWith('right')?'18px':'auto';el.style.transform=pos==='center'?'translate(-50%,-50%)':''};['wmText','wmOpacity','wmFont','wmPosition','wmOn'].forEach(id=>$('#'+id).oninput=preview);$('#watermarkForm').onsubmit=e=>{e.preventDefault();wmVersion++;toast(T('Sample generation job accepted. No images are modified.','示例生成任务已受理,未修改任何图片。'))}}
if($('#slotForm')){consts=slots[slotIndex];$('#slotWidth').readOnly=true;$('#slotHeight').readOnly=true;$('#slotSpeed').min=8;$('#slotSpeed').max=32;constmode=()=>{$('#v2Fields').hidden=$('#slotMode').value!=='v2';$('#v1Fields').hidden=$('#slotMode').value!=='v1'};$('#slotMode').onchange=mode;mode();$('#offsetX').oninput=()=>{$('#embedded').style.right=Math.max(0,Number($('#offsetX').value))+'px'};$('#offsetY').oninput=()=>{$('#embedded').style.bottom=Math.max(0,Number($('#offsetY').value))+'px'};$('#slotPosition').onchange=e=>{$('#embedded').style.left=e.target.value==='left'?'16px':'auto';$('#embedded').style.right=e.target.value==='right'?'16px':'auto'};$('#slotForm').onsubmit=e=>{e.preventDefault();constspeed=Number($('#slotSpeed').value);if(!$('#slotName').value.trim()||speed<8||speed>32){$('#formError').textContent=T('Name required. Speed must be between 8 and 32.','请填写名称,速度须在 8–32 之间。');return}s.name=$('#slotName').value;s.zh=s.name;s.speed=speed;s.mode=$('#slotMode').value;s.version++;go(13)}}}
functionaction(raw){const[name,...rest]=raw.split(':'),value=rest.join(':');switch(name){case'go':go(value);break;case'close':closeModal();break;case'menu':$('#sidebar').classList.toggle('open');break;case'directory':modal(T('All preview pages','全部预览页面'),`<div class="form-grid">${window.ADMIN_PAGES.map(p=>btn(String(p.id).padStart(2,'0')+' · '+p[lang],'go:'+p.id,'')).join('')}</div>`,'',true);break;case'album':albumIndex=Number(value);go(3);break;case'albumMode':albumMode=value;render();break;case'photoMode':photoMode=value;render();break;case'photoFilter':photoFilter=value;render();break;case'photo':openPhoto(Number(value));break;case'batchHide':if(!selected.size)return;confirm(T('Hide selected photos?','隐藏选中的照片?'),T('This changes sample visibility only. Real batches must report per-photo success and failure.','仅修改示例可见性。真实批处理需逐项反馈成功和失败。'),()=>{selected.forEach(i=>photoStates[i]='hidden');selected.clear();render();toast(T('Sample photos hidden','示例照片已隐藏'))});break;case'visibility':{consti=Number(value);confirm(photoStates[i]==='hidden'?T('Publish this photo?','恢复公开这张照片?'):T('Hide this photo?','隐藏这张照片?'),T('Public visibility changes. The original remains stored.','改变公开可见性,原图仍保留在存储中。'),()=>{photoStates[i]=photoStates[i]==='hidden'?'published':'hidden';selected.delete(i);render()});break}case'deletePhoto':confirm(T('Delete photo?','删除照片?'),T('Soft deletion removes the sample from active management. This does not immediately physically delete OSS files.','软删除会让照片退出有效管理列表,不代表立即物理删除 OSS 文件。'),()=>{photoStates[Number(value)]='deleted';selected.delete(Number(value));render()});break;
case'invite':{modal(T('Invite colleague','邀请同事'),`<div class="search" style="margin-bottom:15px">${icon('user')}<input id="inviteSearch" placeholder="${T('Search internal accounts','搜索企业内部账号')}"></div><div id="inviteRows">${people.map((p,i)=>!memberIds.includes(i)?`<button class="side-link candidate" data-action="addMember:${i}">${avatar(p.name)}<span>${p.name}<small class="muted"> · ${p.account}</small></span></button>`:'').join('')}</div><p>${T('No external email or registration invitation is sent.','仅选择内部账号,不发送外部邮件或注册邀请。')}</p>`);$('#inviteSearch').oninput=e=>$$('.candidate').forEach(el=>el.hidden=!el.textContent.toLowerCase().includes(e.target.value.toLowerCase()));break}case'addMember':memberIds.push(Number(value));closeModal();render();break;case'removeMember':confirm(T('Remove collaborator?','移除协作者?'),T('The sample account loses collaborator access to this album.','该示例账号将不再作为本相册协作者。'),()=>{memberIds=memberIds.filter(i=>i!==Number(value));render()});break;case'transfer':modal(T('Transfer album ownership','交接相册'),select(T('New owner','新创建者'),'newOwner',people.slice(1).map((p,i)=>[String(i+1),p.name]),'1')+`<p>${T('Production must recheck the target account and creator permissions.','正式版需再次校验目标账号与创建者权限。')}</p>`,btn(T('Cancel','取消'),'close')+btn(T('Transfer sample','交接示例'),'confirmTransfer','primary'));break;case'confirmTransfer':{constid=Number($('#newOwner').value);albums[albumIndex].owner=people[id].name;memberIds=[id,...memberIds.filter(i=>i!==id)];closeModal();render();break}
case'category':{consti=value==='new'?-1:Number(value);modal(i<0?T('New category','新增分类'):T('Edit category','编辑分类'),`<form id="categoryForm">${field('中文','catZh',i<0?'':cats[i].zh)}${field('English','catEn',i<0?'':cats[i].en)}<button class="btn primary" type="submit">${T('Save category','保存分类')}</button></form>`);$('#categoryForm').onsubmit=e=>{e.preventDefault();if(!$('#catZh').value.trim()||!$('#catEn').value.trim()){toast(T('Complete both sample language fields','请填写示例双语名称'));return}constitem={zh:$('#catZh').value,en:$('#catEn').value};if(i<0)cats.push(item);elsecats[i]=item;closeModal();render()};break}case'deleteCategory':if(Number(value)<3)toast(T('This sample category contains photos and cannot be deleted.','该示例分类已有照片,不能删除。'));elseconfirm(T('Delete empty category?','删除空分类?'),T('Only this empty sample category is removed.','仅移除该空示例分类。'),()=>{cats.splice(Number(value),1);render()});break;case'regenerate':confirm(T('Regenerate viewing variants?','重新生成浏览变体?'),T('Production creates an asynchronous job. This preview does not modify OSS.','正式版创建异步生成任务,此预览不修改 OSS。'),()=>{wmVersion++;render();toast(T('Sample job created','示例任务已创建'))});break;case'copyShare':copy($('#shareLink').value);break;case'viewer':window.open('../viewer-preview-2026-09-22.html','_blank','noopener,noreferrer');break;
case'userEdit':{consti=Number(value),p=people[i];modal(T('Edit account','编辑账号'),`<form id="userEditForm">${field(T('Display name','显示名称'),'userName',p.name)}${select(T('Role','角色'),'userRole',[['admin',T('Administrator','管理员')],['staff',T('Staff','员工')]],p.role)}<p>${i===0?T('Your own role cannot be changed here.','不能在这里修改自己的角色。'):T('Source status remains controlled by the company directory.','来源状态仍由企业用户接口控制。')}</p><button type="submit" class="btn primary" style="margin-top:20px">${T('Save','保存')}</button></form>`);$('#userRole').disabled=i===0;$('#userEditForm').onsubmit=e=>{e.preventDefault();p.name=$('#userName').value||p.name;if(i!==0)p.role=$('#userRole').value;closeModal();render()};break}case'resetPassword':{constp=people[Number(value)];modal(T('Reset password','重置密码'),`<p style="margin-bottom:16px">${p.name} · ${p.account}</p><form id="resetForm">${field(T('Temporary password (demo only)','临时密码(仅演示)'),'tempPassword','','password')}<p>${T('Do not enter real credentials. Nothing is stored or sent.','请勿输入真实凭据,此处不保存或发送密码。')}</p><button class="btn primary" type="submit" style="margin-top:20px">${T('Confirm reset preview','确认重置演示')}</button></form>`);$('#resetForm').onsubmit=e=>{e.preventDefault();closeModal();toast(T('Password reset flow previewed. No credentials changed.','已演示密码重置流程,未修改真实凭据。'))};break}case'userState':{consti=Number(value);if(i===0)return;constp=people[i];confirm(T('Change local account state?','修改本地账号状态?'),T('The company directory state remains unchanged.','来源接口状态不会随本地治理动作改变。'),()=>{if(p.credential==='pending')p.credential='active';elsep.locked=!p.locked;render()});break}case'resetUserFilters':$('#userSearch').value='';$('#userStatus').value='all';$('#usersTable').innerHTML=usersTable();break;case'sync':if(syncRunning)return;confirm(T('Run a sample pull?','模拟一次用户补拉?'),T('This is a local animation; no company directory is contacted.','仅在本地演示运行状态,不请求企业用户接口。'),()=>{syncRunning=true;render();setTimeout(()=>{syncRunning=false;if([11,20].includes(page))render();toast(T('Sample pull completed','模拟补拉已完成'))},1800)});break;case'syncDetail':syncDetail(Number(value));break;case'thresholds':modal(T('Alert thresholds','告警阈值'),`<form id="thresholdForm">${field(T('Total storage threshold (GB)','总存储阈值(GB)'),'totalThreshold','200','number')}${field(T('Per-album threshold (GB)','单相册阈值(GB)'),'albumThreshold','50','number')}<p>${T('Thresholds trigger alerts, not automatic deletion.','达到阈值仅触发告警,不自动删除照片。')}</p><button class="btn primary" style="margin-top:20px" type="submit">${T('Save sample thresholds','保存示例阈值')}</button></form>`);$('#thresholdForm').onsubmit=e=>{e.preventDefault();constvalue=Number($('#totalThreshold').value);if(value<0){toast(T('Use a non-negative number','请输入非负数字'));return}closeModal();if($('#thresholdValue'))$('#thresholdValue').textContent=value+' GB';toast(T('Sample threshold updated','示例阈值已更新'))};break;
case'newSlot':slots.push({name:'New widget',zh:'新展示位',album:null,enabled:false,mode:'v2',version:1,speed:16});slotIndex=slots.length-1;go(14);break;case'slot':slotIndex=Number(value);go(14);break;case'bind':{slotIndex=Number(value);modal(T('Bind album','绑定相册'),select(T('Album','相册'),'bindAlbum',[['none',T('Unbind','解除绑定')],...albums.map((a,i)=>[String(i),lang==='zh'?a.zh:a.title])],slots[slotIndex].album===null?'none':String(slots[slotIndex].album))+`<p>${T('The same embedded slot will display the newly bound album after configuration refresh.','配置刷新后,使用此展示位的网站将展示新绑定的相册。')}</p>`,btn(T('Cancel','取消'),'close')+btn(T('Confirm binding','确认绑定'),'confirmBind','primary'));break}case'confirmBind':slots[slotIndex].album=$('#bindAlbum').value==='none'?null:Number($('#bindAlbum').value);slots[slotIndex].version++;closeModal();render();break;case'code':{consts=slots[Number(value)];constcode='<script src="https://example.com/embed/'+s.mode+'/loader.js" data-slot="DEMO_SLOT" defer><'+'/script>';modal(T('Embed code','嵌入代码'),`<p>${T('Illustrative integration code. Replace example.com and DEMO_SLOT with real deployment values.','接入格式示例,须替换 example.com 和 DEMO_SLOT 为真实部署信息。')}</p><pre class="mono" id="embedCode">${esc(code)}</pre>`,btn(T('Copy sample code','复制示例代码'),'copyCode','primary'));break}case'copyCode':copy($('#embedCode').textContent);break;case'collapseEmbed':$('#embedded').hidden=true;$('#expandEmbed').hidden=false;break;case'expandEmbed':$('#embedded').hidden=false;$('#expandEmbed').hidden=true;break;case'embedHeart':if(!$('#slotReactions').checked)return;$('#embedded').hidden=false;$('#expandEmbed').hidden=true;$('#embedHearts').classList.remove('play');void$('#embedHearts').offsetWidth;$('#embedHearts').classList.add('play');break;case'widgetDemo':window.open('../embed-preview-2026-09-22.html','_blank','noopener,noreferrer');break;case'banner':modal(T('Choose sample banner','选择示例宣传图'),`<div class="album-grid">${[0,3,5].map(i=>`<button class="album-cover" data-action="setBanner:${i}">${photo(i)}</button>`).join('')}</div><p style="margin-top:20px">${T('No files are uploaded in this prototype.','此原型不会上传文件。')}</p>`,'',true);break;case'setBanner':albums[albumIndex].photo=Number(value);closeModal();render();break}}
functioncopy(text){if(navigator.clipboard)navigator.clipboard.writeText(text).then(()=>toast(T('Copied','已复制'))).catch(()=>prompt(T('Copy this text','复制以下内容'),text));elseprompt(T('Copy this text','复制以下内容'),text)}
Use case: ui-mockup. Create a polished high fidelity desktop admin web interface concept screenshot for PhotoLive, enterprise event photo live streaming management. One single full desktop screen, flat front-on 16:10 composition, about 1600x1000; no devices, no perspective, no outside annotations. It is a redesign on Art Design Pro / Element Plus design language, calm mature SaaS, not a marketing dashboard. White 216px sidebar, pale cool #F5F7FA main canvas, navy #172033 typography, blue #1A73E8 actions, thin #E5EAF1 borders, 12px rounded cards, subtle shadows, spacious structured hierarchy. Left sidebar PhotoLive blue aperture mark, small WORKSPACE label then Albums selected, Embed widgets; ORGANIZATION label then Accounts, User sync, Storage & usage; bottom administrator avatar and company workspace. Topbar breadcrumb Workspace / Albums / Canton Fair, language EN and avatar. Main title Canton Fair · Autumn with small Published and Uploads on status pills, right outlined Edit album and primary Share album. Beneath: compact horizontal hero info card, left venue photograph around 320x145, right event date Oct 15–19, 2026, Guangzhou China, owner Alex Morgan, timezone Asia/Shanghai, muted small Stats updated 2 min ago. Under hero: tabs Photos selected blue underline, Members, Categories, Watermark, Sharing. Photo toolbar with All photos, Published, Hidden, category dropdown, photo filename search, grid/list toggle. Actual main photo grid 4 columns by 2 rows of beautiful documentary international trade fair photos: venue, networking business people, robotics, audience, speakers. Each thumbnail has small checkbox top left and a subtle Published status, below images tiny filename and uploader; one image selected with blue border and checkbox. A small blue subdued selection toolbar '1 selected' and 'Hide selected' may appear within photo area, not floating over website. No giant charts, no fake revenue, no ecommerce, no public like button, no comment/upload/social buttons, no technical token as page headline. Text crisp legible English, consistent thin outline icons and clean typography. All images and counts are illustrative. Overall feel premium restrained professional photography management tool, photos dominate, realistic implementable HTML layout.
Observed variation: the generated image used an illustrative numeric uploads badge instead of the requested Uploads on text. The HTML uses access-state semantics and must take precedence for behavior and data interpretation. Counts and text in the concept image are not production requirements.
"scope":"Offline HTML only; production admin untouched",
"pages":[
{
"file":"01-login.html",
"overflow":false,
"brokenImages":0
},
{
"file":"02-albums.html",
"overflow":false,
"brokenImages":0
},
{
"file":"03-photos.html",
"overflow":false,
"brokenImages":0
},
{
"file":"04-members.html",
"overflow":false,
"brokenImages":0
},
{
"file":"05-categories.html",
"overflow":false,
"brokenImages":0
},
{
"file":"06-watermark.html",
"overflow":false,
"brokenImages":0
},
{
"file":"07-sharing.html",
"overflow":false,
"brokenImages":0
},
{
"file":"08-create-album.html",
"overflow":false,
"brokenImages":0
},
{
"file":"09-edit-album.html",
"overflow":false,
"brokenImages":0
},
{
"file":"10-users.html",
"overflow":false,
"brokenImages":0
},
{
"file":"11-user-sync.html",
"overflow":false,
"brokenImages":0
},
{
"file":"12-usage.html",
"overflow":false,
"brokenImages":0
},
{
"file":"13-embed-slots.html",
"overflow":false,
"brokenImages":0
},
{
"file":"14-embed-editor.html",
"overflow":false,
"brokenImages":0
},
{
"file":"15-branding.html",
"overflow":false,
"brokenImages":0
},
{
"file":"16-photo-viewer.html",
"overflow":false,
"brokenImages":0
},
{
"file":"17-403.html",
"overflow":false,
"brokenImages":0
},
{
"file":"18-404.html",
"overflow":false,
"brokenImages":0
},
{
"file":"19-500.html",
"overflow":false,
"brokenImages":0
},
{
"file":"20-sync-detail.html",
"overflow":false,
"brokenImages":0
}
],
"narrow":[
{
"file":"02-albums.html",
"overflow":false
},
{
"file":"03-photos.html",
"overflow":false
},
{
"file":"10-users.html",
"overflow":false
},
{
"file":"14-embed-editor.html",
"overflow":false
}
],
"errors":[],
"widget":{
"width":195,
"height":280
},
"interactions":[
"album search",
"batch hide",
"visibility filter",
"account search",
"language",
"widget collapse",
"v1 mode",
"watermark preview"
]
}
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.