{"id":1695,"date":"2025-11-24T22:50:36","date_gmt":"2025-11-24T14:50:36","guid":{"rendered":"http:\/\/www.liujh168.com\/?p=1176"},"modified":"2026-04-21T22:46:47","modified_gmt":"2026-04-21T14:46:47","slug":"shifts-2","status":"publish","type":"post","link":"https:\/\/www.liujh168.com\/index.php\/2025\/11\/24\/shifts-2\/","title":{"rendered":"\u5012\u73ed\u503c\u73ed\u67e5\u8be2\u8868"},"content":{"rendered":"<p>\u7528\u5012\u73ed\u52a9\u624b\u67e5\u73ed\u6b21\u611f\u89c9\u8d8a\u6765\u8d8a\u4e0d\u65b9\u4fbf\u4e86\uff0c\u62bd\u7a7a\u5199\u4e2a\u81ea\u5df1\u7528\u3002\u4e0b\u9762\u8fd9\u4e2a\u8fd0\u884c\u6709\u70b9\u95ee\u9898 \uff0c\u4f46<a href=\"http:\/\/www.liujh168.com\/myapp\/unical.html\" target=\"_blank\" rel=\"noopener\">\u70b9\u6211\u53ef\u6b63\u5e38\u67e5\u8be2\u5012\u73ed\u8868<\/a><\/p>\n<p><!--more--><\/p>\n\n\n<!-- \u25b8 \u82e5\u5d4c\u5165 WordPress\uff0c\u4ece\u6b64\u884c\u5f00\u59cb\u590d\u5236\u5230 <\/script> \u7ed3\u675f -->\n<div class=\"sca-root\" id=\"sca-root\">\n\n  <div class=\"sca-header\">\n    <h1>\ud83d\udcc5 \u5012\u73ed\u65e5\u5386<\/h1>\n    <p>\u81ea\u5b9a\u4e49\u73ed\u6b21\u3001\u5468\u671f\u4e0e\u63d0\u9192\uff0c\u8f7b\u677e\u638c\u63e1\u6392\u73ed\u5b89\u6392<\/p>\n  <\/div>\n\n  <div class=\"sca-layout\">\n\n    <!-- \u5de6\u4fa7\u8bbe\u7f6e\u9762\u677f -->\n    <div style=\"display:flex;flex-direction:column;gap:16px;\">\n\n      <!-- \u73ed\u6b21\u7ba1\u7406 -->\n      <div class=\"sca-card\">\n        <div class=\"sca-card-title\">\n          <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"\/><polyline points=\"12 6 12 12 16 14\"\/><\/svg>\n          \u73ed\u6b21\u7ba1\u7406\n        <\/div>\n        <div class=\"sca-form-group\">\n          <label>\u73ed\u6b21\u540d\u79f0<\/label>\n          <input type=\"text\" id=\"sca-shift-name\" placeholder=\"\u4f8b\u5982\uff1a\u767d\u73ed\u3001\u591c\u73ed\">\n        <\/div>\n        <div class=\"sca-form-group\">\n          <label>\u73ed\u6b21\u989c\u8272<\/label>\n          <input type=\"color\" id=\"sca-shift-color\" value=\"#4f86f7\">\n        <\/div>\n        <button class=\"sca-btn\" id=\"sca-add-shift\">+ \u6dfb\u52a0\u73ed\u6b21<\/button>\n        <div class=\"sca-shift-list\" id=\"sca-shift-list\"><\/div>\n      <\/div>\n\n      <!-- \u5468\u671f\u8bbe\u7f6e -->\n      <div class=\"sca-card\">\n        <div class=\"sca-card-title\">\n          <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"23 4 23 10 17 10\"\/><path d=\"M20.49 15a9 9 0 1 1-2.12-9.36L23 10\"\/><\/svg>\n          \u5012\u73ed\u5468\u671f\n        <\/div>\n        <div class=\"sca-form-group\">\n          <label>\u5468\u671f\u5f00\u59cb\u65e5\u671f<\/label>\n          <input type=\"date\" id=\"sca-cycle-start\">\n        <\/div>\n        <div class=\"sca-form-group\">\n          <label>\u73ed\u6b21\u5e8f\u5217\uff08\u7528\u9017\u53f7\u5206\u9694\u73ed\u6b21\u7f16\u53f7\uff09<\/label>\n          <input type=\"text\" id=\"sca-custom-cycle\" placeholder=\"\u4f8b\uff1a0,1,2,0,1,3,3,3\">\n          <small>\u73ed\u6b21\u7f16\u53f7\u89c1\u4e0a\u65b9\u5217\u8868\u4e2d\u7684\u7070\u8272\u6570\u5b57\uff0c\u4ece 0 \u5f00\u59cb<\/small>\n        <\/div>\n        <button class=\"sca-btn\" id=\"sca-save-cycle\">\u2713 \u4fdd\u5b58\u8bbe\u7f6e<\/button>\n      <\/div>\n\n      <!-- \u63d0\u9192\u4e8b\u9879 -->\n      <div class=\"sca-card\">\n        <div class=\"sca-card-title\">\n          <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9\"\/><path d=\"M13.73 21a2 2 0 0 1-3.46 0\"\/><\/svg>\n          \u63d0\u9192\u4e8b\u9879\n        <\/div>\n        <div class=\"sca-form-group\">\n          <label>\u65e5\u671f<\/label>\n          <input type=\"date\" id=\"sca-reminder-date\">\n        <\/div>\n        <div class=\"sca-form-group\">\n          <label>\u63d0\u9192\u5185\u5bb9<\/label>\n          <textarea id=\"sca-reminder-text\" rows=\"2\" placeholder=\"\u8f93\u5165\u63d0\u9192\u5185\u5bb9\"><\/textarea>\n        <\/div>\n        <button class=\"sca-btn\" id=\"sca-add-reminder\">+ \u6dfb\u52a0\u63d0\u9192<\/button>\n        <div class=\"sca-reminder-scroll\" id=\"sca-reminder-list\"><\/div>\n      <\/div>\n\n    <\/div>\n\n    <!-- \u53f3\u4fa7\u65e5\u5386 -->\n    <div class=\"sca-card\">\n      <div class=\"sca-cal-header\">\n        <button class=\"sca-btn sca-btn-ghost\" id=\"sca-prev-month\">&#8249;<\/button>\n        <div style=\"display:flex;align-items:center;gap:10px;\">\n          <span class=\"sca-month-label\" id=\"sca-month-label\"><\/span>\n          <button class=\"sca-btn sca-btn-today\" id=\"sca-today-btn\">\u4eca\u5929<\/button>\n        <\/div>\n        <button class=\"sca-btn sca-btn-ghost\" id=\"sca-next-month\">&#8250;<\/button>\n      <\/div>\n      <div class=\"sca-grid\" id=\"sca-grid\"><\/div>\n      <p style=\"margin-top:10px;font-size:11px;color:#bbb;text-align:right;\">\u70b9\u51fb\u65e5\u671f\u53ef\u5feb\u901f\u6dfb\u52a0\u63d0\u9192<\/p>\n    <\/div>\n\n  <\/div>\n<\/div>\n\n<!-- \u70b9\u51fb\u65e5\u671f\u5feb\u901f\u6dfb\u52a0\u63d0\u9192\u7684\u5f39\u7a97 -->\n<div class=\"sca-modal-overlay\" id=\"sca-modal\">\n  <div class=\"sca-modal\">\n    <h3 id=\"sca-modal-title\">\u6dfb\u52a0\u63d0\u9192<\/h3>\n    <div class=\"sca-form-group\">\n      <label>\u63d0\u9192\u5185\u5bb9<\/label>\n      <textarea id=\"sca-modal-text\" rows=\"3\" placeholder=\"\u8f93\u5165\u63d0\u9192\u5185\u5bb9...\" style=\"width:100%;padding:8px 12px;border:1.5px solid #e2e8f0;border-radius:6px;font-size:13px;font-family:inherit;outline:none;resize:none;\"><\/textarea>\n    <\/div>\n    <div class=\"sca-modal-footer\">\n      <button class=\"sca-btn sca-btn-outline\" id=\"sca-modal-cancel\">\u53d6\u6d88<\/button>\n      <button class=\"sca-btn\" id=\"sca-modal-confirm\">\u6dfb\u52a0<\/button>\n    <\/div>\n  <\/div>\n<\/div>\n\n<div class=\"sca-toast\" id=\"sca-toast\"><\/div>\n\n<script>\n(function() {\n  'use strict';\n\n  \/\/ \u2500\u2500 \u72b6\u6001 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  const STORAGE_KEY = 'sca_shift_calendar_v2';\n\n  const state = {\n    shifts: [\n      { id: 0, name: '\u4f11\u606f\uff08\u5927\u4f11\uff09', color: '#95a5a6' },\n      { id: 1, name: '\u767d\u73ed',       color: '#4f86f7' },\n      { id: 2, name: '\u591c\u73ed',       color: '#fc5c65' },\n      { id: 3, name: '\u5c0f\u4f11',       color: '#26de81' }\n    ],\n    cyclePattern: [1, 2, 3, 1, 2, 0, 0, 0],\n    cycleStartDate: localToday(),\n    reminders: [],\n    currentDate: localToday(),\n    nextShiftId: 4,\n    modalDate: null\n  };\n\n  \/\/ \u2500\u2500 \u5de5\u5177\u51fd\u6570 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  function localToday() {\n    const d = new Date();\n    return new Date(d.getFullYear(), d.getMonth(), d.getDate());\n  }\n\n  function parseLocalDate(str) {\n    \/\/ \u907f\u514d\u65f6\u533a\u504f\u79fb\uff1a'2024-01-15' \u2192 \u672c\u5730\u65f6\u95f4\n    if (!str) return localToday();\n    const [y, m, d] = str.split('-').map(Number);\n    return new Date(y, m - 1, d);\n  }\n\n  function formatDate(date) {\n    const y = date.getFullYear();\n    const m = String(date.getMonth() + 1).padStart(2, '0');\n    const d = String(date.getDate()).padStart(2, '0');\n    return `${y}-${m}-${d}`;\n  }\n\n  function sameDay(a, b) {\n    return a.getFullYear() === b.getFullYear() &&\n           a.getMonth()    === b.getMonth()    &&\n           a.getDate()     === b.getDate();\n  }\n\n  \/\/ \u2500\u2500 \u672c\u5730\u5b58\u50a8 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  function save() {\n    try {\n      localStorage.setItem(STORAGE_KEY, JSON.stringify({\n        shifts:         state.shifts,\n        cyclePattern:   state.cyclePattern,\n        cycleStartDate: formatDate(state.cycleStartDate),\n        reminders:      state.reminders.map(r => ({ ...r, date: formatDate(r.date) })),\n        nextShiftId:    state.nextShiftId\n      }));\n    } catch(e) {}\n  }\n\n  function load() {\n    try {\n      const raw = localStorage.getItem(STORAGE_KEY);\n      if (!raw) return;\n      const data = JSON.parse(raw);\n      if (data.shifts)         state.shifts         = data.shifts;\n      if (data.cyclePattern)   state.cyclePattern   = data.cyclePattern;\n      if (data.cycleStartDate) state.cycleStartDate = parseLocalDate(data.cycleStartDate);\n      if (data.nextShiftId)    state.nextShiftId    = data.nextShiftId;\n      if (data.reminders) {\n        state.reminders = data.reminders.map(r => ({ ...r, date: parseLocalDate(r.date) }));\n      }\n    } catch(e) {}\n  }\n\n  \/\/ \u2500\u2500 Toast \u63d0\u793a \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  let toastTimer = null;\n  function toast(msg) {\n    const el = document.getElementById('sca-toast');\n    el.textContent = msg;\n    el.classList.add('show');\n    clearTimeout(toastTimer);\n    toastTimer = setTimeout(() => el.classList.remove('show'), 2500);\n  }\n\n  \/\/ \u2500\u2500 \u73ed\u6b21\u7ba1\u7406 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  function getShiftForDate(date) {\n    const diff = Math.round((date - state.cycleStartDate) \/ 86400000);\n    if (diff < 0) return null;\n    const idx = diff % state.cyclePattern.length;\n    const sid = state.cyclePattern[idx];\n    return state.shifts.find(s => s.id === sid) || null;\n  }\n\n  function renderShiftList() {\n    const el = document.getElementById('sca-shift-list');\n    if (!state.shifts.length) {\n      el.innerHTML = '<div class=\"sca-empty\">\u6682\u65e0\u73ed\u6b21<\/div>';\n      return;\n    }\n    el.innerHTML = state.shifts.map(s => `\n      <div class=\"sca-shift-item\">\n        <span class=\"sca-shift-dot\" style=\"background:${s.color}\"><\/span>\n        <span class=\"sca-shift-name\">${s.name}<\/span>\n        <span class=\"sca-shift-id\">${s.id}<\/span>\n        ${s.id !== 0\n          ? `<button class=\"sca-btn sca-btn-sm sca-btn-danger sca-del-shift\" data-id=\"${s.id}\">\u5220\u9664<\/button>`\n          : ''}\n      <\/div>\n    `).join('');\n\n    el.querySelectorAll('.sca-del-shift').forEach(btn => {\n      btn.addEventListener('click', () => {\n        const id = Number(btn.dataset.id);\n        state.shifts = state.shifts.filter(s => s.id !== id);\n        renderShiftList();\n        renderCalendar();\n        save();\n        toast('\u73ed\u6b21\u5df2\u5220\u9664');\n      });\n    });\n  }\n\n  function addShift() {\n    const nameEl  = document.getElementById('sca-shift-name');\n    const colorEl = document.getElementById('sca-shift-color');\n    const name    = nameEl.value.trim();\n    if (!name) { toast('\u8bf7\u8f93\u5165\u73ed\u6b21\u540d\u79f0'); return; }\n\n    state.shifts.push({ id: state.nextShiftId, name, color: colorEl.value });\n    state.nextShiftId++;\n    nameEl.value  = '';\n    colorEl.value = '#4f86f7';\n    renderShiftList();\n    renderCalendar();\n    save();\n    toast('\u73ed\u6b21\u5df2\u6dfb\u52a0');\n  }\n\n  \/\/ \u2500\u2500 \u63d0\u9192\u4e8b\u9879 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  function getRemindersForDate(date) {\n    return state.reminders.filter(r => sameDay(r.date, date));\n  }\n\n  function addReminder(date, text) {\n    if (!text) { toast('\u8bf7\u8f93\u5165\u63d0\u9192\u5185\u5bb9'); return false; }\n    state.reminders.push({ id: Date.now(), date, text });\n    renderReminderList();\n    renderCalendar();\n    save();\n    toast('\u63d0\u9192\u5df2\u6dfb\u52a0');\n    return true;\n  }\n\n  function deleteReminder(id) {\n    state.reminders = state.reminders.filter(r => r.id !== id);\n    renderReminderList();\n    renderCalendar();\n    save();\n    toast('\u63d0\u9192\u5df2\u5220\u9664');\n  }\n\n  function renderReminderList() {\n    const el = document.getElementById('sca-reminder-list');\n    const sorted = [...state.reminders].sort((a, b) => a.date - b.date);\n    if (!sorted.length) {\n      el.innerHTML = '<div class=\"sca-empty\">\u6682\u65e0\u63d0\u9192<\/div>';\n      return;\n    }\n    el.innerHTML = sorted.map(r => `\n      <div class=\"sca-reminder-row\">\n        <div class=\"sca-reminder-info\">\n          <div class=\"sca-reminder-date\">${formatDate(r.date)}<\/div>\n          <div class=\"sca-reminder-text\">${r.text}<\/div>\n        <\/div>\n        <button class=\"sca-btn sca-btn-sm sca-btn-danger sca-del-reminder\" data-id=\"${r.id}\">\u00d7<\/button>\n      <\/div>\n    `).join('');\n\n    el.querySelectorAll('.sca-del-reminder').forEach(btn => {\n      btn.addEventListener('click', () => deleteReminder(Number(btn.dataset.id)));\n    });\n  }\n\n  \/\/ \u2500\u2500 \u65e5\u5386\u6e32\u67d3 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  const MONTH_NAMES = ['\u4e00\u6708','\u4e8c\u6708','\u4e09\u6708','\u56db\u6708','\u4e94\u6708','\u516d\u6708',\n                       '\u4e03\u6708','\u516b\u6708','\u4e5d\u6708','\u5341\u6708','\u5341\u4e00\u6708','\u5341\u4e8c\u6708'];\n  const WEEKDAYS    = ['\u65e5','\u4e00','\u4e8c','\u4e09','\u56db','\u4e94','\u516d'];\n\n  function renderCalendar() {\n    const y     = state.currentDate.getFullYear();\n    const m     = state.currentDate.getMonth();\n    const today = localToday();\n\n    document.getElementById('sca-month-label').textContent =\n      `${y} \u5e74 ${MONTH_NAMES[m]}`;\n\n    const grid = document.getElementById('sca-grid');\n    grid.innerHTML = '';\n\n    \/\/ \u661f\u671f\u6807\u9898\n    WEEKDAYS.forEach((wd, i) => {\n      const h = document.createElement('div');\n      h.className = 'sca-weekday';\n      h.textContent = wd;\n      grid.appendChild(h);\n    });\n\n    const firstDay      = new Date(y, m, 1).getDay();    \/\/ 0=Sun\n    const daysInMonth   = new Date(y, m + 1, 0).getDate();\n    const daysInPrev    = new Date(y, m, 0).getDate();\n    const totalCells    = 42;\n    const nextMonthDays = totalCells - firstDay - daysInMonth;\n\n    \/\/ \u4e0a\u6708\u5c3e\u90e8\n    for (let i = firstDay - 1; i >= 0; i--) {\n      const d = daysInPrev - i;\n      grid.appendChild(makeCell(new Date(y, m - 1, d), true, today));\n    }\n    \/\/ \u672c\u6708\n    for (let d = 1; d <= daysInMonth; d++) {\n      grid.appendChild(makeCell(new Date(y, m, d), false, today));\n    }\n    \/\/ \u4e0b\u6708\u5934\u90e8\n    for (let d = 1; d <= nextMonthDays; d++) {\n      grid.appendChild(makeCell(new Date(y, m + 1, d), true, today));\n    }\n  }\n\n  function makeCell(date, isOther, today) {\n    const cell = document.createElement('div');\n    cell.className = 'sca-day';\n    const dow = date.getDay();\n\n    if (isOther) cell.classList.add('sca-other');\n    if (!isOther &#038;&#038; (dow === 0 || dow === 6)) cell.classList.add('sca-weekend');\n    if (sameDay(date, today)) cell.classList.add('sca-today');\n\n    \/\/ \u65e5\u671f\u6570\u5b57\n    const numRow = document.createElement('div');\n    numRow.className = 'sca-day-num';\n    numRow.textContent = date.getDate();\n    if (sameDay(date, today)) {\n      const badge = document.createElement('span');\n      badge.className = 'sca-today-badge';\n      badge.textContent = '\u4eca';\n      numRow.appendChild(badge);\n    }\n    cell.appendChild(numRow);\n\n    if (!isOther) {\n      \/\/ \u73ed\u6b21\u6807\u7b7e\n      const shift = getShiftForDate(date);\n      if (shift) {\n        const tag = document.createElement('div');\n        tag.className = 'sca-shift-tag';\n        tag.style.background = shift.color;\n        tag.textContent = shift.name;\n        cell.appendChild(tag);\n      }\n\n      \/\/ \u63d0\u9192\u6807\u7b7e\n      getRemindersForDate(date).forEach(r => {\n        const rt = document.createElement('div');\n        rt.className = 'sca-reminder-tag';\n        rt.textContent = r.text;\n        rt.title = r.text;\n        cell.appendChild(rt);\n      });\n\n      \/\/ \u70b9\u51fb\u6dfb\u52a0\u63d0\u9192\n      cell.addEventListener('click', () => openModal(date));\n    }\n\n    return cell;\n  }\n\n  \/\/ \u2500\u2500 \u5f39\u7a97\uff08\u70b9\u51fb\u65e5\u671f\u5feb\u901f\u6dfb\u52a0\u63d0\u9192\uff09 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  function openModal(date) {\n    state.modalDate = date;\n    document.getElementById('sca-modal-title').textContent =\n      `\u6dfb\u52a0\u63d0\u9192 \u2014 ${formatDate(date)}`;\n    document.getElementById('sca-modal-text').value = '';\n    document.getElementById('sca-modal').classList.add('open');\n    setTimeout(() => document.getElementById('sca-modal-text').focus(), 50);\n  }\n\n  function closeModal() {\n    document.getElementById('sca-modal').classList.remove('open');\n    state.modalDate = null;\n  }\n\n  \/\/ \u2500\u2500 \u521d\u59cb\u5316 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  function init() {\n    load();\n\n    const today = localToday();\n    document.getElementById('sca-cycle-start').value  = formatDate(state.cycleStartDate);\n    document.getElementById('sca-custom-cycle').value = state.cyclePattern.join(',');\n    document.getElementById('sca-reminder-date').value = formatDate(today);\n\n    renderShiftList();\n    renderCalendar();\n    renderReminderList();\n\n    \/\/ \u2500\u2500 \u4e8b\u4ef6\u7ed1\u5b9a \u2500\u2500\n    document.getElementById('sca-add-shift').addEventListener('click', addShift);\n\n    document.getElementById('sca-prev-month').addEventListener('click', () => {\n      state.currentDate = new Date(\n        state.currentDate.getFullYear(),\n        state.currentDate.getMonth() - 1,\n        1\n      );\n      renderCalendar();\n    });\n\n    document.getElementById('sca-next-month').addEventListener('click', () => {\n      state.currentDate = new Date(\n        state.currentDate.getFullYear(),\n        state.currentDate.getMonth() + 1,\n        1\n      );\n      renderCalendar();\n    });\n\n    document.getElementById('sca-today-btn').addEventListener('click', () => {\n      state.currentDate = localToday();\n      renderCalendar();\n    });\n\n    document.getElementById('sca-save-cycle').addEventListener('click', () => {\n      const dateStr  = document.getElementById('sca-cycle-start').value;\n      const cycleStr = document.getElementById('sca-custom-cycle').value.trim();\n      if (!dateStr)  { toast('\u8bf7\u9009\u62e9\u5468\u671f\u5f00\u59cb\u65e5\u671f'); return; }\n      if (!cycleStr) { toast('\u8bf7\u8f93\u5165\u73ed\u6b21\u5e8f\u5217');     return; }\n\n      const pattern = cycleStr.split(',').map(s => parseInt(s.trim(), 10));\n      if (pattern.some(isNaN)) { toast('\u73ed\u6b21\u5e8f\u5217\u683c\u5f0f\u9519\u8bef\uff0c\u8bf7\u4f7f\u7528\u9017\u53f7\u5206\u9694\u7684\u6570\u5b57'); return; }\n\n      const validIds = state.shifts.map(s => s.id);\n      const invalid  = pattern.filter(n => !validIds.includes(n));\n      if (invalid.length) { toast(`\u672a\u77e5\u73ed\u6b21\u7f16\u53f7\uff1a${invalid.join(', ')}`); return; }\n\n      state.cycleStartDate = parseLocalDate(dateStr);\n      state.cyclePattern   = pattern;\n      renderCalendar();\n      save();\n      toast('\u5468\u671f\u8bbe\u7f6e\u5df2\u4fdd\u5b58 \u2713');\n    });\n\n    document.getElementById('sca-add-reminder').addEventListener('click', () => {\n      const dateStr = document.getElementById('sca-reminder-date').value;\n      const text    = document.getElementById('sca-reminder-text').value.trim();\n      if (!dateStr) { toast('\u8bf7\u9009\u62e9\u65e5\u671f'); return; }\n      if (addReminder(parseLocalDate(dateStr), text)) {\n        document.getElementById('sca-reminder-text').value = '';\n      }\n    });\n\n    \/\/ \u5f39\u7a97\n    document.getElementById('sca-modal-confirm').addEventListener('click', () => {\n      const text = document.getElementById('sca-modal-text').value.trim();\n      if (state.modalDate && addReminder(state.modalDate, text)) {\n        \/\/ \u540c\u6b65\u4fa7\u8fb9\u65e5\u671f\u8f93\u5165\n        document.getElementById('sca-reminder-date').value = formatDate(state.modalDate);\n        closeModal();\n      }\n    });\n    document.getElementById('sca-modal-cancel').addEventListener('click', closeModal);\n    document.getElementById('sca-modal').addEventListener('click', e => {\n      if (e.target === document.getElementById('sca-modal')) closeModal();\n    });\n    document.getElementById('sca-modal-text').addEventListener('keydown', e => {\n      if (e.key === 'Enter' && !e.shiftKey) {\n        e.preventDefault();\n        document.getElementById('sca-modal-confirm').click();\n      }\n    });\n  }\n\n  \/\/ \u7b49 DOM \u5c31\u7eea\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', init);\n  } else {\n    init();\n  }\n})();\n<\/script>\n<!-- \u25b8 WordPress \u5d4c\u5165\u81f3\u6b64\u7ed3\u675f -->\n","protected":false},"excerpt":{"rendered":"<p>\u7528\u5012\u73ed\u52a9\u624b\u67e5\u73ed\u6b21\u611f\u89c9\u8d8a\u6765\u8d8a\u4e0d\u65b9\u4fbf\u4e86\uff0c\u62bd\u7a7a\u5199\u4e2a\u81ea\u5df1\u7528\u3002\u4e0b\u9762\u8fd9\u4e2a\u8fd0\u884c\u6709\u70b9\u95ee\u9898 \uff0c\u4f46\u70b9\u6211\u53ef\u6b63\u5e38\u67e5\u8be2\u5012\u73ed\u8868<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[41,1],"tags":[],"class_list":["post-1695","post","type-post","status-publish","format-standard","hentry","category-uncategorized","category-unknown"],"_links":{"self":[{"href":"https:\/\/www.liujh168.com\/index.php\/wp-json\/wp\/v2\/posts\/1695","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.liujh168.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.liujh168.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.liujh168.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.liujh168.com\/index.php\/wp-json\/wp\/v2\/comments?post=1695"}],"version-history":[{"count":1,"href":"https:\/\/www.liujh168.com\/index.php\/wp-json\/wp\/v2\/posts\/1695\/revisions"}],"predecessor-version":[{"id":1779,"href":"https:\/\/www.liujh168.com\/index.php\/wp-json\/wp\/v2\/posts\/1695\/revisions\/1779"}],"wp:attachment":[{"href":"https:\/\/www.liujh168.com\/index.php\/wp-json\/wp\/v2\/media?parent=1695"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.liujh168.com\/index.php\/wp-json\/wp\/v2\/categories?post=1695"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.liujh168.com\/index.php\/wp-json\/wp\/v2\/tags?post=1695"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}