diff --git a/pigway_frame_flow/web/app.js b/pigway_frame_flow/web/app.js index dc82b47..6d2abf4 100644 --- a/pigway_frame_flow/web/app.js +++ b/pigway_frame_flow/web/app.js @@ -7,6 +7,11 @@ const icons={dashboard:'◫',gallery:'▧','photo-sources':'▣',upload:'↥',te const t=k=>window.MESSAGES[state.lang][k]||k; const esc=v=>String(v??'—').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); const fmt=v=>v?new Date(v).toLocaleString(state.lang==='zh'?'zh-CN':'en-GB',{timeZone:'Asia/Shanghai'}):'—'; +const padTime=(hour,minute)=>`${String(hour).padStart(2,'0')}:${String(minute).padStart(2,'0')}`; +function normaliseSchedule(schedule){const copy={...schedule};if(copy.type!=='cron')return copy;const match=/^(\d{1,2})\s+(\d{1,2})\s+\*\s+\*\s+(\*|1-5|6,0|0,6)$/.exec(copy.cron||'');if(!match)return copy;const type=match[3]==='*'?'daily':match[3]==='1-5'?'weekdays':'weekends';return {type,time:padTime(match[2],match[1])};} +function scheduleTypeLabel(type){if(type!=='cron')return t(type);return state.lang==='zh'?'高级规则(Cron)':'Advanced rule (Cron)';} +function scheduleSummary(schedule){const value=normaliseSchedule(schedule);if(['daily','weekdays','weekends'].includes(value.type))return `${scheduleTypeLabel(value.type)} · ${value.time}(${state.lang==='zh'?'上海时间':'Shanghai time'})`;if(value.type==='cron')return `${scheduleTypeLabel('cron')} · ${value.cron}`;return `${scheduleTypeLabel(value.type)} · ${fmt(value.at)}`;} +function scheduleHint(type){if(type==='cron')return state.lang==='zh'?'仅在需要特殊规则时使用;例如“每天 08:00”请选择“每天”。':'Use only for special rules; choose Daily for a regular time.';if(['date','temporary'].includes(type))return state.lang==='zh'?'选择一次性执行的日期和时间。':'Choose the date and time for this one-time task.';return state.lang==='zh'?'选择执行时间(上海时区)。':'Choose a time in Shanghai time.';} const buttons=(id,text,primary=false)=>``; let toastTimer; function toast(message){$('#toast').textContent=message;$('#toast').style.display='block';clearTimeout(toastTimer);toastTimer=setTimeout(()=>$('#toast').style.display='none',6000);} @@ -71,3 +76,33 @@ async function playlistEditor(p){p=p||{name:'',image_ids:[],interval_minutes:60, $('#lang').onclick=()=>{state.lang=state.lang==='zh'?'en':'zh';localStorage.setItem('ff-language',state.lang);navigate();};$('#theme').onclick=()=>{state.theme=document.documentElement.dataset.theme==='dark'?'light':'dark';localStorage.setItem('ff-theme',state.theme);applyTheme();};$('#device').onchange=()=>{state.device=$('#device').value;navigate();};window.addEventListener('hashchange',()=>{const page=location.hash.slice(1);if(page!==state.page)navigate(page);});matchMedia('(prefers-color-scheme:dark)').addEventListener('change',applyTheme); applyTheme();localStorage.removeItem('ff-login');(async()=>{try{const account=await api('/api/v1/auth/me');state.token=account.username;state.csrf=account.csrf_token;await loadConnection();}catch{forgetLogin();}await navigate();})();api('/api/v1/version').then(v=>$('#footer').innerHTML=`Pigway Frame Flow · ${t('footer')}v${esc(v.version)} · ${esc(v.commit.slice(0,8))} · ${esc(v.build_time)}`).catch(()=>{}); setInterval(()=>{if(state.token&&state.page==='dashboard'&&!$('#modal').open)navigate();},60000); + +// Keep Cron available for exceptional schedules, but make ordinary schedules form-based. +async function jobEditor(job){ + const base={name:'',enabled:true,device_id:state.device,schedule:{type:'daily',time:'07:00'},source:{type:'template',template:'daily-dashboard',data:{}},display_duration:5400,restore_previous:true,priority:50}; + const original=job||base; + const j={...base,...original,schedule:normaliseSchedule(original.schedule||base.schedule)}; + const scheduleValue=j.schedule.time||j.schedule.cron||(j.schedule.at?j.schedule.at.slice(0,16):'07:00'); + showModal(`
Asia/Shanghai · ${t('automation')}
${buttons('new-job','newJob',true)}| ${t(key)} | `).join('')}||||||
|---|---|---|---|---|---|---|
| ${esc(job.name)} ${esc(job.device_id)} | ${esc(scheduleSummary(job.schedule))} | ${t(job.enabled?'enabled':'disabled')} | ${fmt(job.next_run)} | ${fmt(job.last_run)} | ${esc(job.last_result)} |