198 lines
8.7 KiB
PHP
198 lines
8.7 KiB
PHP
<?php
|
|||
|
|
/**
|
||
|
|
* 日程 · 月视图(v12)。
|
||
|
|
*
|
||
|
|
* 参照群晖 Calendar 的外观:左边一列"我的日历本"(可勾选显隐),右边 7×6 的月网格,
|
||
|
|
* 有日程的日子在格子里露出彩色小条,点小条跳到对应模块(观影/旅程/任务)。
|
||
|
|
*
|
||
|
|
* 两条设计:
|
||
|
|
* ① **日历本显隐是纯前端的**(勾选框 + 给日程条加 data-cal):
|
||
|
|
* 不做成"勾一次刷一次页面",否则翻月、勾选都在等服务端。没有 JS 时全部照常显示。
|
||
|
|
* ② **观影 / 旅行 / 任务不复制进日历**,是从各自模块实时汇总出来的(Calendar::month),
|
||
|
|
* 所以改了观影日期,日历里立刻跟着变。反过来在日历里给观影本加日程 = 真的建一条观影记录。
|
||
|
|
*
|
||
|
|
* @param string $ym 'Y-m'
|
||
|
|
* @param array $weeks 月网格(build_month_grid 产出)
|
||
|
|
* @param array $cals 我可见的日历本(含 perm)
|
||
|
|
* @param array $people 名册(共享给别人时用)
|
||
|
|
*/
|
||
|
|
$canWrite = \App\Auth::canWrite();
|
||
|
|
$ts = strtotime($ym . '-01') ?: time();
|
||
|
|
$prevYm = date('Y-m', strtotime('-1 month', $ts));
|
||
|
|
$nextYm = date('Y-m', strtotime('+1 month', $ts));
|
||
|
|
$thisYm = date('Y-m');
|
||
|
|
$writable = array_values(array_filter($cals, static fn($c) => \App\Calendar::canWrite($c, (int)\App\Auth::personId())));
|
||
|
|
?>
|
||
|
|
<div class="page-head">
|
||
|
|
<h1>日程</h1>
|
||
|
|
<span class="sub"><?= e(date('Y 年 n 月', $ts)) ?></span>
|
||
|
|
<div class="actions">
|
||
|
|
<a class="btn btn-ghost" href="<?= e(url('calendar', ['ym' => $prevYm])) ?>">上一月</a>
|
||
|
|
<a class="btn btn-ghost" href="<?= e(url('calendar', ['ym' => $thisYm])) ?>">今天</a>
|
||
|
|
<a class="btn btn-ghost" href="<?= e(url('calendar', ['ym' => $nextYm])) ?>">下一月</a>
|
||
|
|
<a class="btn btn-ghost" href="<?= e(url('calendar/calendars')) ?>">日历本</a>
|
||
|
|
<?php if ($canWrite && $writable): ?>
|
||
|
|
<button class="btn" type="button" id="calAddBtn">新建日程</button>
|
||
|
|
<?php endif; ?>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="cal-wrap">
|
||
|
|
<?php /* ---- 左:日历本 ---- */ ?>
|
||
|
|
<aside class="cal-side">
|
||
|
|
<h2 class="cinema-h2">我的日历本</h2>
|
||
|
|
<?php if (!$cals): ?>
|
||
|
|
<p class="empty-hint">还没有日历本。</p>
|
||
|
|
<?php else: ?>
|
||
|
|
<ul class="cal-list">
|
||
|
|
<?php foreach ($cals as $c): ?>
|
||
|
|
<li class="cal-item">
|
||
|
|
<label class="cal-toggle">
|
||
|
|
<input type="checkbox" class="cal-cb" data-cal="<?= (int)$c['id'] ?>" checked>
|
||
|
|
<span class="color-preview" style="background: <?= e((string)$c['color']) ?>;"></span>
|
||
|
|
<span class="cal-name"><?= e((string)$c['name']) ?></span>
|
||
|
|
</label>
|
||
|
|
<?php if ((int)$c['shared'] === 1): ?>
|
||
|
|
<span class="node-badge"><?= ($c['perm'] ?? '') === 'write' ? '共享·可写' : '共享·只读' ?></span>
|
||
|
|
<?php endif; ?>
|
||
|
|
</li>
|
||
|
|
<?php endforeach; ?>
|
||
|
|
</ul>
|
||
|
|
<?php /* 观影/旅行/任务三路是**自动汇总**的,没有本子开关,这里说明一下来源 */ ?>
|
||
|
|
<p class="cal-note">观影、旅行、有截止时间的任务会自动出现在这里,不需要另建本子。</p>
|
||
|
|
<?php endif; ?>
|
||
|
|
</aside>
|
||
|
|
|
||
|
|
<?php /* ---- 右:月网格 ---- */ ?>
|
||
|
|
<div class="cal-main">
|
||
|
|
<?php if ($canWrite && $writable): ?>
|
||
|
|
<div class="card card-pad cal-add" id="calAddCard" hidden>
|
||
|
|
<h2 class="cinema-h2">新建日程</h2>
|
||
|
|
<form method="post" action="<?= e(url('calendar/event_add')) ?>">
|
||
|
|
<?= csrf_field() ?>
|
||
|
|
<input type="hidden" name="ym" value="<?= e($ym) ?>">
|
||
|
|
<div class="form-grid">
|
||
|
|
<div class="field">
|
||
|
|
<label for="ce-cal">放在哪本</label>
|
||
|
|
<select class="select" id="ce-cal" name="calendar_id">
|
||
|
|
<?php foreach ($writable as $c): ?>
|
||
|
|
<option value="<?= (int)$c['id'] ?>"><?= e((string)$c['name']) ?></option>
|
||
|
|
<?php endforeach; ?>
|
||
|
|
</select>
|
||
|
|
</div>
|
||
|
|
<div class="field">
|
||
|
|
<label for="ce-title">标题 *</label>
|
||
|
|
<input class="input" id="ce-title" name="title" required placeholder="如:家长会">
|
||
|
|
</div>
|
||
|
|
<div class="field">
|
||
|
|
<label for="ce-date">日期 *</label>
|
||
|
|
<input class="input" type="date" id="ce-date" name="date" required
|
||
|
|
value="<?= e($ym === $thisYm ? date('Y-m-d') : $ym . '-01') ?>">
|
||
|
|
</div>
|
||
|
|
<div class="field">
|
||
|
|
<label for="ce-end">结束日期(跨天时填)</label>
|
||
|
|
<input class="input" type="date" id="ce-end" name="end_date">
|
||
|
|
</div>
|
||
|
|
<div class="field">
|
||
|
|
<label><input type="checkbox" id="ce-allday" name="all_day" value="1" checked> 全天(不填具体时分)</label>
|
||
|
|
</div>
|
||
|
|
<div class="field">
|
||
|
|
<label for="ce-note">备注</label>
|
||
|
|
<input class="input" id="ce-note" name="note">
|
||
|
|
</div>
|
||
|
|
<div class="field" id="ce-time-s">
|
||
|
|
<label for="ce-st">开始时刻</label>
|
||
|
|
<input class="input" type="time" id="ce-st" name="start_time">
|
||
|
|
</div>
|
||
|
|
<div class="field" id="ce-time-e">
|
||
|
|
<label for="ce-et">结束时刻</label>
|
||
|
|
<input class="input" type="time" id="ce-et" name="end_time">
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="btn-row">
|
||
|
|
<button class="btn btn-sm" type="submit">加上</button>
|
||
|
|
<button class="btn btn-ghost btn-sm" type="button" id="calAddCancel">取消</button>
|
||
|
|
</div>
|
||
|
|
</form>
|
||
|
|
</div>
|
||
|
|
<?php endif; ?>
|
||
|
|
|
||
|
|
<div class="card cal-grid-card">
|
||
|
|
<div class="cal-head">
|
||
|
|
<?php foreach (['日', '一', '二', '三', '四', '五', '六'] as $wd): ?>
|
||
|
|
<div class="cal-wd"><?= e($wd) ?></div>
|
||
|
|
<?php endforeach; ?>
|
||
|
|
</div>
|
||
|
|
<div class="cal-grid">
|
||
|
|
<?php foreach ($weeks as $wk): ?>
|
||
|
|
<?php foreach ($wk as $cell): ?>
|
||
|
|
<div class="cal-cell<?= $cell['in_month'] ? '' : ' is-out' ?><?= $cell['is_today'] ? ' is-today' : '' ?>">
|
||
|
|
<div class="cal-dnum"><?= (int)$cell['day'] ?></div>
|
||
|
|
<div class="cal-evs">
|
||
|
|
<?php $n = 0; foreach ($cell['events'] as $ev): $n++; ?>
|
||
|
|
<?php if ($n <= 3): ?>
|
||
|
|
<a class="cal-ev<?= empty($ev['all_day']) ? '' : ' is-allday' ?>"
|
||
|
|
data-cal="<?= (int)$ev['cal'] ?>"
|
||
|
|
style="background: <?= e((string)$ev['color']) ?>;"
|
||
|
|
href="<?= e((string)$ev['href']) ?>"
|
||
|
|
title="<?= e((string)$ev['title']) ?>">
|
||
|
|
<?php if (($ev['time'] ?? '') !== ''): ?><b><?= e((string)$ev['time']) ?></b> <?php endif; ?>
|
||
|
|
<?= e((string)$ev['title']) ?>
|
||
|
|
</a>
|
||
|
|
<?php endif; ?>
|
||
|
|
<?php endforeach; ?>
|
||
|
|
<?php if (count($cell['events']) > 3): ?>
|
||
|
|
<span class="cal-more">+<?= count($cell['events']) - 3 ?> 条</span>
|
||
|
|
<?php endif; ?>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<?php endforeach; ?>
|
||
|
|
<?php endforeach; ?>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
(function () {
|
||
|
|
/* 日历本显隐:只切 display,不重新请求 —— 翻月/勾选都是立刻的。
|
||
|
|
没有 JS 时全部照常显示(勾选框失效而已),不会丢内容。 */
|
||
|
|
var boxes = document.querySelectorAll('.cal-cb');
|
||
|
|
function apply() {
|
||
|
|
var off = {};
|
||
|
|
Array.prototype.forEach.call(boxes, function (b) {
|
||
|
|
if (!b.checked) { off[b.getAttribute('data-cal')] = true; }
|
||
|
|
});
|
||
|
|
Array.prototype.forEach.call(document.querySelectorAll('.cal-ev'), function (el) {
|
||
|
|
el.style.display = off[el.getAttribute('data-cal')] ? 'none' : '';
|
||
|
|
});
|
||
|
|
}
|
||
|
|
Array.prototype.forEach.call(boxes, function (b) { b.addEventListener('change', apply); });
|
||
|
|
apply();
|
||
|
|
|
||
|
|
var btn = document.getElementById('calAddBtn');
|
||
|
|
var card = document.getElementById('calAddCard');
|
||
|
|
var cancel = document.getElementById('calAddCancel');
|
||
|
|
if (btn && card) {
|
||
|
|
btn.addEventListener('click', function () {
|
||
|
|
card.hidden = !card.hidden;
|
||
|
|
if (!card.hidden) { card.scrollIntoView({ block: 'nearest' }); }
|
||
|
|
});
|
||
|
|
}
|
||
|
|
if (cancel && card) { cancel.addEventListener('click', function () { card.hidden = true; }); }
|
||
|
|
|
||
|
|
/* 「全天」勾上就把两个时刻输入框藏掉 —— 没有具体时分就是全天(用户第 7 条) */
|
||
|
|
var allday = document.getElementById('ce-allday');
|
||
|
|
var ts = document.getElementById('ce-time-s');
|
||
|
|
var te = document.getElementById('ce-time-e');
|
||
|
|
if (allday && ts && te) {
|
||
|
|
function syncTime() {
|
||
|
|
var hide = allday.checked;
|
||
|
|
ts.hidden = hide; te.hidden = hide;
|
||
|
|
}
|
||
|
|
allday.addEventListener('change', syncTime);
|
||
|
|
syncTime();
|
||
|
|
}
|
||
|
|
})();
|
||
|
|
</script>
|