functionlogin(){return`<div class="safe-top"></div><div class="scroll flush white"><div class="login-top"><button data-action="language">${lang==='en'?'EN':'中文'}⌄</button></div><div class="mosaic">${[2,4,0,3,6,1].map(i=>photo(i)).join('')}</div><div class="login-content"><div class="wordmark">${icon('logo')}PhotoLive</div><h1>${T('Your event. In focus.','让每个现场,都被看见。')}</h1><p class="muted">${T('Sign in to your company workspace','登录您的企业工作空间')}</p><form id="loginForm" class="login-form"><label class="field"><span class="input-wrap">${icon('user')}<input id="account" autocomplete="off" placeholder="${T('Account','账号')}" value="demo.staff"></span></label>${field('','password','password','','••••••••')}<button class="btn primary full" type="submit">${T('Sign in','登录')}</button><div class="error" id="formError"></div></form><p class="login-note">${T('Accounts are provided by your administrator.','账号由企业管理员提供。')}</p><p class="demo-label">${T('Demo mode · do not enter real credentials','演示模式 · 请勿输入真实账号密码')}</p></div></div>`}
functionpassword(){returnbar(T('Change password','修改密码'),2)+`<div class="scroll"><div class="hint-card" style="margin-bottom:24px">${icon('lock')}<div><strong>${T('Keep your account secure','保障您的账号安全')}</strong><p class="note">${T('Update the password for your company account.','修改用于企业账号登录的密码。')}</p></div></div><form id="passwordForm">${field(T('Current password','当前密码'),'oldPassword','password','',T('Enter current password','输入当前密码'))}${field(T('New password','新密码'),'newPassword','password','',T('Enter new password','输入新密码'))}${field(T('Confirm password','确认密码'),'confirmPassword','password','',T('Enter new password again','再次输入新密码'))}<div id="formError" class="error"></div><button class="btn primary full" type="submit">${T('Save password','保存密码')}</button></form><div class="hint-card" style="margin-top:24px">${icon('help')}<p>${T('Preview only. No password is saved or sent. Production rules are enforced by the account service.','仅演示校验,不保存或发送密码。正式密码规则由账号服务执行。')}</p></div></div>`}
functiondetail(empty=false){consttitle=empty?T('Team Stories','团队故事'):albumTitle;returnbar('',5,`<button class="right" data-go="7" aria-label="${T('Album actions','相册操作')}">${icon('more')}</button>`)+`<div class="scroll flush white">${photo(empty?2:6,'album-hero')}<div class="album-info"><h2>${esc(title)}</h2><div class="metadata">${icon('calendar')}${empty?'Aug 12–15, 2026':'Oct 15–19, 2026'}</div><div class="metadata">${icon('pin')}${empty?'Shanghai, China':'Guangzhou, China'}</div>${status()}</div>${empty?`<div class="empty"><div class="empty-illustration">${icon('photo')}</div><h3>${T('Ready for your first photo','准备好记录第一张照片')}</h3><p>${T('Capture a moment or choose photos to begin.','拍摄精彩瞬间,或从手机相册选择照片。')}</p></div>`:`<div class="quick">${[[17,'photo',T('Photos','照片')],[11,'users',T('Members','成员')],[12,'tag',T('Categories','分类')],[19,'share',T('Share','分享')]].map(([id,ic,label])=>`<button data-go="${id}">${icon(ic)}${label}</button>`).join('')}</div><div class="recent"><div class="heading-row"><h3>${T('Recent photos','最近照片')}</h3><button data-go="17">${T('See all','查看全部')}</button></div>${grid(9)}</div>`}</div><div class="actions">${button(T('Take photos','拍摄照片'),uploadAllowed?'go:13':'uploadOff','primary','camera')}${button(T('Choose photos','选择照片'),uploadAllowed?'choose':'uploadOff','secondary','photo')}</div>`}
functionalbumForm(edit=false){constdetails=`<div class="card">${field(T('Album title','相册名称'),'albumTitle','text',edit?albumTitle:T('Canton Fair · Autumn','广交会 · 秋季'))}</div><div class="card">${field(T('Starts','开始日期'),'startDate','date','2026-10-15')}${field(T('Ends','结束日期'),'endDate','date','2026-10-19')}<label class="field">${T('Time zone','时区')}<select id="timezone"><option>Asia/Shanghai</option><option>Europe/London</option><option>America/New_York</option></select></label>${field(T('Venue','地点'),'venue','text','Guangzhou')}${field(T('Address','详细地址'),'address','text',T('China Import and Export Fair Complex','中国进出口商品交易会展馆'))}</div><div class="card"><h3 style="margin-bottom:16px">${T('Description & languages','说明与语言')}</h3><label class="field">${T('Default language','默认语言')}<select id="contentLanguage"><option>English</option><option>中文</option></select></label><label class="field">${T('Description','活动说明')}<textarea id="description">${T('Event photos from the Canton Fair Autumn session.','广交会秋季展会现场照片。')}</textarea></label></div>`;constaccess=`<div class="group">${[['uploads',T('Allow uploads','允许上传'),T('Team members can upload photos to this album.','团队成员可以向本相册上传照片。'),uploadAllowed],['public',T('Public access','公开访问'),T('Anyone with the link can view this album.','获得链接的观众可以浏览相册。'),publicAccess],['downloads',T('Allow downloads','允许下载'),T('Viewers can download photos from this album.','观众可以下载相册照片。'),downloads],['original',T('Original downloads','原图下载'),T('Allow downloading full-resolution files.','允许观众下载完整分辨率的原图。'),original]].map(([id,title,desc,on])=>`<label class="row access-row"><span class="grow"><strong>${title}</strong><p>${desc}</p></span><input class="switch" id="access-${id}" type="checkbox" ${on?'checked':''}${id==='original'&&!downloads?'disabled':''} aria-label="${title}"></label>`).join('')}</div><div class="group">${row('globe',T('Content language','内容语言'),T('Default language for album content','相册内容的默认语言'),'contentLanguage','English')}</div>`;returnbar(edit?T('Edit album','编辑相册'):T('New album','新建相册'),6)+`<div class="scroll"><form id="albumForm">${edit?seg([['details',T('Details','详情')],['access',T('Access','权限')]],editTab,'editTab'):''}<div class="stack" id="detailsFields" ${edit&&editTab!=='details'?'hidden':''}>${details}</div>${edit?`<div class="stack" id="accessFields" ${editTab!=='access'?'hidden':''}>${access}</div>`:''}<div class="error" id="formError"></div></form></div><div class="actions"><button class="btn primary" type="submit" form="albumForm">${edit?T('Save changes','保存修改'):T('Create album','创建相册')}</button></div>`}
functionmemberPage(){returnbar(T('Members','成员'),6,`<button class="right blue" data-action="invite" aria-label="${T('Invite colleague','邀请同事')}">${icon('plus')}</button>`)+`<div class="scroll"><h3 class="section-label">${T('Owner','创建者')}</h3><div class="group"><div class="row"><div class="avatar">AM</div><div class="grow"><strong>Alex Morgan</strong><p>alex.morgan</p></div><span class="tag">${T('Owner','创建者')}</span></div></div><h3 class="section-label" style="margin-top:26px">${T('Collaborators','协作者')}</h3><div class="group">${members.map((name,i)=>`<div class="row"><div class="avatar ${i%2?'lilac':''}">${name.split(' ').map(n=>n[0]).join('')}</div><div class="grow"><strong>${name}</strong><p>${name.toLowerCase().replace(' ','.')}</p><p>${T('Can manage their own photos','可管理自己上传的照片')}</p></div><button data-action="removeMember:${i}" aria-label="${T('Remove member','移除成员')}">${icon('more')}</button></div>`).join('')}</div><div class="hint-card" style="margin-top:20px">${icon('help')}${T('Album owners manage all photos and members.','相册创建者管理全部照片和成员。')}</div></div><div class="actions">${button(T('Invite colleague','邀请同事'),'invite','secondary','users')}</div>`}
functioncategories(){returnbar(T('Categories','分类'),6,`<button class="right blue" data-action="category:new" aria-label="${T('New category','新建分类')}">${icon('plus')}</button>`)+`<div class="scroll stack">${categoryNames.map((name,i)=>`<div class="card" style="display:flex;align-items:center;gap:14px"><div class="avatar" style="border-radius:12px">${icon('tag')}</div><strong style="flex:1">${esc(name)}</strong><button data-action="category:${i}" aria-label="${T('Edit category','编辑分类')}">${icon('edit')}</button></div>`).join('')}<p class="note">${T('Categories help your team organize event photos.','通过分类整理团队的活动照片。')}</p></div>`}
functioncamera(){returnbar(`<span style="font-size:13px">${esc(albumTitle)}</span><br><button style="min-height:28px;padding:0;font-size:11px;color:#aab9cf" data-action="cameraCategory">${T('Exhibition','展会现场')} ⌄</button>`,6,`<button class="right" data-go="14" aria-label="${T('Upload queue','上传队列')}">${icon('upload')}</button>`)+`<div class="camera-view">${photo(1)}<div class="camera-note" id="cameraNote">${T('Camera preview · sample photo','相机效果演示 · 示例照片')}</div></div><div class="camera-chrome"><button class="camera-thumb" data-go="17" aria-label="${T('Recent photos','最近照片')}">${photo(0)}</button><button class="shutter" data-action="shutter" aria-label="${T('Simulate taking a photo','模拟拍照')}"></button><button data-action="flip" aria-label="${T('Switch camera preview','切换相机预览')}">${icon('flip')}</button></div><p class="demo-label">${T('No device camera or OSS connection','未调用设备相机,未连接 OSS')}</p>`}
functionuploadItem(i,type='upload'){letname='IMG_'+String(85+i).padStart(3,'0')+'.jpg';constattention=type==='attention',history=type==='history';letstate=attention?(i===0?T('Connection interrupted','连接中断'):T('Source file unavailable','源文件不可用')):history?[T('Uploaded','已上传'),T('Processing','处理中'),T('Ready','已就绪')][i%3]:[T('Uploading 68%','上传中 68%'),T('Waiting for network','等待网络'),T('Confirming upload','确认上传中'),T('Uploaded','已上传')][i%4];return`<article class="upload-row ${history?'history-row':''}">${photo(i+2,'upload-thumb')}<div class="upload-data"><strong>${name}</strong><p>${history?'Today 14:'+String(32-i*3):esc(albumTitle)}</p>${!history?`<p>${T('Exhibition','展会现场')}</p>`:''}<div class="upload-status ${attention?'red':history?(i%3===1?'blue':'green'):['blue','amber','blue','green'][i%4]}">${!attention&&i===0&&!history?'<div class="progress"><span></span></div>':icon(attention?'help':i%3===0?'check':'clock')}${state}</div>${attention?`<p style="margin-top:7px">${i===0?T('The network was lost during upload. You can retry this file.','上传时网络断开,可以重试此任务。'):T('The original file cannot be found on this device. Please select it again.','本机找不到源文件,请重新选择。')}</p>${button(i===0?T('Retry','重试'):T('Select again','重新选择'),i===0?'retry':'reselect','secondary')}`:history?`<p style="margin-top:7px">${i%2?T('Other device','其他设备'):T('This device','本机')}</p>`:''}</div></article>`}
functionhistoryPage(){returnbar(T('Upload history','上传历史'),14)+`<div class="scroll"><p class="muted" style="margin-bottom:16px">${T('Server records · All devices','服务端记录 · 所有设备')}</p><div style="display:flex;gap:10px;margin-bottom:10px"><label class="field" style="flex:2"><select id="historyAlbum" aria-label="Album"><option>${esc(albumTitle)}</option><option>Product Launch</option></select></label><label class="field" style="flex:1"><select id="historyDate" aria-label="Date"><option>${T('Today','今天')}</option><option>${T('Yesterday','昨天')}</option></select></label></div><div id="historyRows">${Array.from({length:5},(_,i)=>uploadItem(i,'history')).join('')}</div><p class="demo-label">${T('Sample server records · not combined with the local queue','示例服务端记录 · 不与本机队列合并计数')}</p></div>`}
functionsharePage(){returnbar(T('Share album','分享相册'),6)+`<div class="scroll"><div class="card share-card">${photo(6,'cover')}<div class="share-info"><h2>${esc(albumTitle)}</h2><div class="metadata">${icon('pin')}Guangzhou, China</div><div class="metadata">${icon('calendar')}Oct 15–19, 2026</div></div><div class="qr-area"><img src="assets/demo-qr.svg" alt="Demo QR code"><p>${T('Illustrative QR · opens example.com','演示二维码 · 指向 example.com')}</p></div>${button(T('Copy preview link','复制预览链接'),'copy','primary full','link')}<button class="btn full" data-action="openViewer">${T('Open viewer preview','打开观众页预览')} ↗</button><p class="share-note">${publicAccess?T('In production, anyone with the public link can view.','正式接入后,获得公开链接的观众可浏览。'):T('Public access is off. Viewers cannot open this album.','相册尚未公开,观众暂时无法浏览。')}</p></div></div>`}
functionlanguageSheet(){return`<h2>${T('App language','应用语言')}</h2>${[['auto','Follow device','跟随系统'],['en','English','English'],['zh','中文','中文']].map(([value,en,zh])=>`<label class="row"><input type="radio" name="language" value="${value}" ${draftLanguage===value?'checked':''}><span class="grow"><strong>${T(en,zh)}</strong><p>${value==='auto'?T('Use your device’s system language','使用设备系统语言'):value==='en'?'English':'中文'}</p></span></label>`).join('')}${button(T('Done','完成'),'saveLanguage')}`}
functionhideSheet(){return`<h2>${T('Hide this photo?','隐藏这张照片?')}</h2><div class="preview-row">${photo(currentPhoto)}<p>${T('Viewers will no longer see this photo. Authorized staff can publish it again.','观众将无法继续看到这张照片,有权限的工作人员可以重新公开。')}</p></div>${button(T('Hide photo','隐藏照片'),'confirmHide')}${button(T('Cancel','取消'),'closeSheet','ghost cancel')}`}
functioninvite(){overlay(`<button class="sheet-close" data-action="closeSheet" aria-label="Close">${icon('close')}</button><h2>${T('Invite colleague','邀请同事')}</h2>${field(T('Search company accounts','搜索企业账号'),'searchMember','search','','Search')}<div id="candidates">${['Olivia Wang','James Wilson','Sophia Li'].map(name=>`<button class="row candidate" data-action="inviteName:${name}"><div class="avatar mint">${name.split(' ').map(x=>x[0]).join('')}</div><span class="grow"><strong>${name}</strong><p>${name.toLowerCase().replace(' ','.')}</p></span>${icon('plus')}</button>`).join('')}</div><p class="note">${T('Demo internal directory · no invitation is sent','示例企业通讯录 · 不发送真实邀请')}</p>`);$('#searchMember').oninput=e=>$$('.candidate').forEach(el=>el.hidden=!el.textContent.toLowerCase().includes(e.target.value.toLowerCase()))}
functionaction(raw){const[name,...rest]=raw.split(':'),value=rest.join(':');switch(name){case'go':go(value);break;case'language':returnPage=page;draftLanguage=lang;go(4);break;case'saveLanguage':{constselected=$('input[name=language]:checked').value;lang=selected==='auto'?(navigator.language.startsWith('zh')?'zh':'en'):selected;go(returnPage);break}case'closeSheet':closeSheet();break;case'logout':overlay(`<h2>${T('Sign out of the preview?','退出预览账号?')}</h2><p class="note">${T('Production uploads remain tied to your account. This prototype contains sample tasks only.','正式版任务按账号保留,此原型中仅有示例任务。')}</p>${button(T('Sign out','退出'),'go:1','danger')}${button(T('Cancel','取消'),'closeSheet','ghost')}`);break;case'help':overlay(`<button class="sheet-close" data-action="closeSheet" aria-label="Close">${icon('close')}</button><h2>${T('Upload help','上传帮助')}</h2><div class="help-copy"><p>${T('Keep the app connected to the network. Interrupted uploads can be retried from Uploads.','请保持网络连接。中断的上传可从上传中心重试。')}</p><p>${T('Background and lock-screen uploads depend on the operating system, permissions and power settings. Reopen the app after a force quit to resume pending work.','后台和锁屏上传受到操作系统、权限和省电设置影响。强制退出后请重新打开应用恢复任务。')}</p><p>${T('Uploaded does not mean publicly available; server processing and album access still apply.','已上传不代表已公开,仍需完成服务端处理并遵守相册权限。')}</p></div>${button(T('Got it','知道了'),'closeSheet')}`);break;case'uploadOff':toast(T('Uploads are disabled for this album.','该相册已关闭上传。'));break;case'choose':case'reselect':overlay(`<h2>${T('Choose sample photos','选择示例照片')}</h2><p class="note">${T('This demonstrates the photo selection flow without accessing your device library.','这里演示选图流程,不访问您的设备相册。')}</p><div class="photo-grid" style="margin-top:16px">${[0,1,2].map(i=>`<button data-action="selectSample:${i}" aria-label="Select sample">${photo(i)}</button>`).join('')}</div>${button(T('Cancel','取消'),'closeSheet','ghost')}`);break;case'selectSample':extraTasks++;closeSheet();toast(T('Sample photo added to the demo upload queue.','示例照片已加入演示上传队列。'));break;case'editTab':{editTab=value;$('#detailsFields').hidden=value!=='details';$('#accessFields').hidden=value!=='access';$$('[data-action^="editTab:"]').forEach(el=>el.classList.toggle('active',el.dataset.action.endsWith(value)));break}case'contentLanguage':overlay(`<h2>${T('Content language','内容语言')}</h2>${button('English','setContent:English','ghost')}${button('中文','setContent:中文','ghost')}`);break;case'setContent':$('#contentLanguage').value=value;closeSheet();toast(T('Content language draft updated.','内容语言草稿已更新。'));break;case'invite':invite();break;case'inviteName':if(!members.includes(value))members.push(value);closeSheet();render();toast(T('Added to the sample team.','已加入示例协作团队。'));break;case'removeMember':overlay(`<h2>${T('Remove collaborator?','移除协作者?')}</h2><p>${esc(members[Number(value)])}</p>${button(T('Remove from sample team','从示例团队移除'),'confirmRemove:'+value,'danger')}${button(T('Cancel','取消'),'closeSheet','ghost')}`);break;case'confirmRemove':members.splice(Number(value),1);render();break;case'category':overlay(categorySheet(value));break;case'categoryLang':{constfield=$('#categoryName');field.dataset[$('.tabs button.active')?.dataset.action.endsWith('zh')?'zh':'en']=field.value;field.value=field.dataset[value]||'';$$('[data-action^="categoryLang:"]').forEach(el=>el.classList.toggle('active',el.dataset.action.endsWith(value)));break}case'cameraCategory':overlay(`<h2>${T('Select category','选择分类')}</h2>${categoryNames.map(n=>button(esc(n),'chooseCategory:'+n,'ghost')).join('')}`);break;case'chooseCategory':closeSheet()
functionbindForms(){constlogin=$('#loginForm');if(login)login.onsubmit=e=>{e.preventDefault();if(!$('#account').value.trim()){$('#formError').textContent=T('Enter a demo account.','请输入演示账号。');return}go(5)};constpassword=$('#passwordForm');if(password)password.onsubmit=e=>{e.preventDefault();consta=$('#newPassword').value,b=$('#confirmPassword').value;if(!$('#oldPassword').value||!a||a!==b){$('#formError').textContent=T('Complete all fields and make sure the new passwords match.','请填写全部字段,并确保两次新密码一致。');return}go(2);setTimeout(()=>toast(T('Validation passed · no password was saved','演示校验通过 · 未保存密码')),60)};constform=$('#albumForm');if(form)form.onsubmit=e=>{e.preventDefault();if(!$('#albumTitle').value.trim()||!$('#startDate').value||!$('#endDate').value||$('#endDate').value<$('#startDate').value){$('#formError').textContent=T('Enter a title and valid start/end dates.','请填写相册名称及有效起止日期。');return}albumTitle=$('#albumTitle').value.trim();if(page===10){publicAccess=$('#access-public').checked;uploadAllowed=$('#access-uploads').checked;downloads=$('#access-downloads').checked;original=downloads&&$('#access-original').checked}go(6)};constcat=$('#categoryForm');if(cat)cat.onsubmit=e=>{e.preventDefault();constval=$('#categoryName').value.trim();if(!val){$('#categoryError').textContent=T('A name is required.','请填写分类名称。');return}if(cat.dataset.index==='new')categoryNames.push(val);elsecategoryNames[Number(cat.dataset.index)]=val;closeSheet();$('#app').innerHTML=categories();toast(T('Sample category saved.','示例分类已保存。'))};constdownload=$('#access-downloads');if(download)download.onchange=()=>{constoriginal=$('#access-original');original.disabled=!download.checked;if(!download.checked)original.checked=false};['historyAlbum','historyDate'].forEach(id=>{constel=$('#'+id);if(el)el.onchange=()=>{$('#historyRows').innerHTML=Array.from({length:el.selectedIndex?2:5},(_,i)=>uploadItem(i+el.selectedIndex,'history')).join('');toast(T('Sample history filter updated','示例历史筛选已更新'))}})}