schedules.ejs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. <!doctype html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <title><%= title %></title>
  7. <link rel="stylesheet" href="/styles.css">
  8. </head>
  9. <body>
  10. <%- include('partials/nav') %>
  11. <%
  12. const scheduleNextOccurrence = typeof nextOccurrence !== 'undefined' ? nextOccurrence : null;
  13. const scheduleThisWeek = typeof thisWeek !== 'undefined' && Array.isArray(thisWeek) ? thisWeek : [];
  14. const scheduleNextWeek = typeof nextWeek !== 'undefined' && Array.isArray(nextWeek) ? nextWeek : [];
  15. %>
  16. <main>
  17. <section class="hero tile-light compact page-intro">
  18. <p class="eyebrow">Schedule</p>
  19. <h1>定时计划</h1>
  20. <p class="lead">支持每天、工作日、法定节假日、自定义。</p>
  21. <div class="hero-actions compact-actions">
  22. <button class="button-primary" type="button" id="openScheduleDialog">添加计划</button>
  23. </div>
  24. </section>
  25. <% if (message) { %><div class="notice"><%= message %></div><% } %>
  26. <% if (error) { %><div class="notice notice-error"><%= error %></div><% } %>
  27. <section class="tile-parchment compact">
  28. <div class="section-heading dark-text">
  29. <h2>本周与下周计划</h2>
  30. <p>
  31. <% if (scheduleNextOccurrence) { %>
  32. 下一次:<%= actionLabel(scheduleNextOccurrence.action) %>,<%= scheduleNextOccurrence.at %>。
  33. <% } else { %>
  34. 暂无后续计划。
  35. <% } %>
  36. </p>
  37. </div>
  38. <div class="plan-columns">
  39. <%- include('partials/occurrences', { title: '本周计划', items: scheduleThisWeek }) %>
  40. <%- include('partials/occurrences', { title: '下周计划', items: scheduleNextWeek }) %>
  41. </div>
  42. </section>
  43. <section class="tile-light compact">
  44. <div class="card-grid">
  45. <% schedules.forEach((item) => { %>
  46. <article class="utility-card">
  47. <p class="eyebrow"><%= item.is_enabled ? '启用' : '停用' %></p>
  48. <h3><%= item.name %></h3>
  49. <p><%= item.time %> · <%= actionLabel(item.action) %> · <%= targetLabel(item.target_channel) %></p>
  50. <p><%= repeatLabel(item.repeat_type) %><% if (item.weekdays) { %>:<%= item.weekdays.split(',').map(weekdayLabel).join('、') %><% } %></p>
  51. <div class="inline-actions">
  52. <form method="post" action="/schedules/<%= item.id %>/toggle"><button class="button-secondary small"><%= item.is_enabled ? '停用' : '启用' %></button></form>
  53. <form method="post" action="/schedules/<%= item.id %>/delete"><button class="button-secondary small">删除</button></form>
  54. </div>
  55. </article>
  56. <% }) %>
  57. </div>
  58. </section>
  59. </main>
  60. <dialog class="modal" id="scheduleDialog">
  61. <div class="modal-header">
  62. <div>
  63. <p class="eyebrow">Schedule</p>
  64. <h2>添加计划</h2>
  65. </div>
  66. <button class="modal-close" type="button" id="closeScheduleDialog" aria-label="关闭">×</button>
  67. </div>
  68. <form method="post" action="/schedules" class="form-grid modal-form">
  69. <label>名称<input name="name" required placeholder="例如 工作日开灯"></label>
  70. <label>目标
  71. <select name="target_channel">
  72. <option value="0">全部灯</option>
  73. <option value="1">灯1</option>
  74. <option value="2">灯2</option>
  75. <option value="3">灯3</option>
  76. </select>
  77. </label>
  78. <label>动作
  79. <select name="action">
  80. <option value="open">开灯</option>
  81. <option value="close">关灯</option>
  82. </select>
  83. </label>
  84. <label>时间<input name="time" type="text" inputmode="numeric" pattern="([01][0-9]|2[0-3]):[0-5][0-9]" placeholder="24 小时制 HH:mm,例如 09:00、18:30" required></label>
  85. <label>重复
  86. <select name="repeat_type" id="repeatType">
  87. <option value="daily">每天</option>
  88. <option value="workday">工作日</option>
  89. <option value="holiday">法定节假日</option>
  90. <option value="custom">自定义</option>
  91. </select>
  92. </label>
  93. <div class="weekday-picker" id="weekdayPicker" hidden>
  94. <% [1,2,3,4,5,6,7].forEach((day) => { %>
  95. <label><input type="checkbox" name="weekdays" value="<%= day %>"> 周<%= weekdayLabel(day) %></label>
  96. <% }) %>
  97. </div>
  98. <div class="modal-actions">
  99. <button class="button-secondary" type="button" id="cancelScheduleDialog">取消</button>
  100. <button class="button-primary">创建计划</button>
  101. </div>
  102. </form>
  103. </dialog>
  104. <script>
  105. const scheduleDialog = document.getElementById('scheduleDialog');
  106. const openScheduleDialog = document.getElementById('openScheduleDialog');
  107. const closeScheduleDialog = document.getElementById('closeScheduleDialog');
  108. const cancelScheduleDialog = document.getElementById('cancelScheduleDialog');
  109. const repeatType = document.getElementById('repeatType');
  110. const weekdayPicker = document.getElementById('weekdayPicker');
  111. function syncWeekdayPicker() {
  112. const isCustom = repeatType.value === 'custom';
  113. weekdayPicker.hidden = !isCustom;
  114. if (!isCustom) {
  115. weekdayPicker.querySelectorAll('input[type="checkbox"]').forEach((input) => {
  116. input.checked = false;
  117. });
  118. }
  119. }
  120. openScheduleDialog.addEventListener('click', () => scheduleDialog.showModal());
  121. closeScheduleDialog.addEventListener('click', () => scheduleDialog.close());
  122. cancelScheduleDialog.addEventListener('click', () => scheduleDialog.close());
  123. scheduleDialog.addEventListener('click', (event) => {
  124. if (event.target === scheduleDialog) scheduleDialog.close();
  125. });
  126. repeatType.addEventListener('change', syncWeekdayPicker);
  127. syncWeekdayPicker();
  128. </script>
  129. </body>
  130. </html>