Files
laoyang 403ecd9dea 日程表单:全天行补 62px 标签占位,与上方输入框左对齐
用户 2026-11-05:「全天那个应该是输入框,它应该往右和那个输入框对齐。」

根因:观影式排版(`.mv-row`)里每行左侧有一个 62px 的 `.mv-lb` 标签列,
控件从 62px 之后开始,上面所有输入框左边缘都在那条线上。
而「全天」这一行没有标签(checkbox 自带"全天"二字),
`<label class="chk-inline">` 直接贴在行的最左边(x=0),
比上面的输入框往左偏了 62px,看着就是错位。

修法:补一个空的 `.mv-lb.no-colon` 占位。

不用 padding-left:62px 的原因:那条 62px 是 `.mv-row > .mv-lb`
的 `flex:0 0 62px` 算出来的,而 .mv-lb 宽度在窄屏媒体查询里会变
(app.css 6196 行附近),写死 padding 会跟它脱节。
用空标签则天然跟随同一套规则。

仅在 $evMv(观影式排版)下补 —— 整页表单是 .form-grid 两列,
没有 62px 标签列,补了反而多出缩进。
2026-11-05 16:00:19 +08:00

386 lines
24 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 日程表单的字段块(新建与编辑共用一份 —— 复制一遍迟早改漏一处)。
*
* 字段按 **RFC 5545(iCalendar)/ RFC 4791(CalDAV)** 的 VEVENT 属性定:
* 标题 = SUMMARY
* 地点 = LOCATION
* 开始/结束 = DTSTART / DTEND(DATETIME,精确到秒)
* 全天 —— VEVENT 没有"全天"这个属性,约定俗成是把 DTSTART 写成 DATE 值;
* 我们统一存 DATETIME,全天时时间部分为 00:00:00,导出 ics 时再落成 DATE 值。
* 提醒 = VALARM 的 TRIGGER(提前多久);不提醒 = 不写 VALARM
* 状态 = STATUS(CONFIRMED / TENTATIVE / CANCELLED)
* 忙闲 = TRANSP(OPAQUE=忙 / TRANSPARENT=空闲)
* 备注 = DESCRIPTION
* 附件 = ATTACH
*
* 用法(include 前设好):
* $evFields 正在编辑的日程(新建时传空数组)
* $evAllDay 全天的默认值(新建默认**不勾**,用户 2026-10-02 第 6 条)
* $evMvStyle 🔴 v43:「标签 62px 右对齐 + 冒号 + 控件」的**观影式**排版开关。
* 新建日程那个弹框要它(用户:日程新建页排版和观影/行程一样);
* 编辑页(`calendar/event.php`)继续用原来的 `.form-grid` 两列。
*/
$evF = is_array($evFields ?? null) ? $evFields : [];
$evAllDayDefault = (bool)($evAllDay ?? false);
$evIsAllDay = array_key_exists('all_day', $evF) ? !empty($evF['all_day']) : $evAllDayDefault;
/* 🔴 v43(用户 2026-10-11 第 5 条):观影式排版开关。 */
$evMv = !empty($evMvStyle);
/* 标签包装:观影式用 `.mv-row` + `.mv-lb`(冒号由 CSS ::after 给);
否则用原来的 `.field` + `<label>`。
⚠️ 抽成闭包是为了**不在下面每处 if/else 手抄两遍结构** ——
抄两遍必然有一天两边不同步(v37 那种"改了 A 忘了 B"的坑)。
⚠️ 「是否必填」作为**参数**传(不要用 `use` 捕获 —— 闭包定义时那个变量还没赋值,
捕获到的是 null,标记永远不出现)。
🔴🔴 v94(用户 2026-10-25):必填的表示法从「末尾加红色星号」改成
「标签本身红色加粗」,由 CSS 类 `.is-req` 驱动(样式见 `app.css` 末尾
「必填项统一标记」那段)。
⚠️ 第三个参数 v94 起从 `$star` 更名为 `$req`:它原来叫"画星",现在只表示
"这是必填" —— 名字不改会让后来者以为还在画星号,去 CSS 里找不存在的规则。 */
$evRowOpen = static function (string $full = '', bool $hidden = false) use ($evMv): string {
return '<div class="' . ($evMv ? 'mv-row' : 'field') . ($full !== '' ? ' ' . $full : '') . '"'
. ($hidden ? ' hidden' : '') . '>';
};
/* 重复规则**先算好**:下面"结束条件 / 重复次数 / 重复到"三行要不要默认藏起来,
取决于它(见 v44 第 2 条)。原来 `$rp` 是在「重复」那一行才 parse 的,
那三行排在它后面 ⇒ 拿不到。提到最前面,只算一次。 */
$evRp = \App\Repeat::parse((string)($evF['rrule'] ?? ''));
$evRepeating = (string)($evRp['freq'] ?? '') !== '';
$evLab = static function (string $text, string $for = '', bool $req = false) use ($evMv): string {
$cls = $req ? ' is-req' : '';
if ($evMv) {
// 观影式:标签是 <span class="mv-lb">,冒号由 CSS ::after 给
return '<span class="mv-lb' . $cls . '">' . $text . '</span>';
}
return '<label' . ($for !== '' ? ' for="' . e($for) . '"' : '')
. ($cls !== '' ? ' class="' . trim($cls) . '"' : '') . '>' . $text . '</label>';
};
?>
<?php echo $evRowOpen('full'); ?>
<?= $evLab('标题', 'ev-title', true) ?>
<input class="input" id="ev-title" name="title" required
value="<?= e((string)($evF['title'] ?? '')) ?>" placeholder="如:家长会">
</div>
<?php echo $evRowOpen(); ?>
<?= $evLab('地点', 'ev-location') ?>
<?php /* 🔴 v47(用户 2026-10-13):「日程中,当我输入地点的时候,也应该调用高德的接口,
补齐详细地址。」⇒ 地点框挂高德联想。
⚠️ 日程表里**没有**单独的"详细地址"字段(地点就一栏),所以这里不做"回填",
只把**联想选中的正式地名**写回输入框 —— 价值是"把口语化的'学校三楼会议室'
换成高德认得的规范地名",后续要查经纬度/导航才查得到。
⚠️ 这段表单既在整页(各视图的日程弹框)也在 AJAX 片段里出现,
所以打好 `data-addr` 标记即可 —— 由 layout 的 loader 与
弹框打开时的 `fbLoadAddr(scope)` 负责绑定(与 person_picker 同一套)。
🔴 v68(用户 2026-10-20):「输入地点,应该调用高德接口,获得**准确的详细地址**,
现在这个功能不好用了。」
① 不好用的真根因不在这一行(是 `layout.php` 的加载器取了不带
`plugin=AMap.AutoComplete` 的 SDK 地址,见那里的注释);
② 「详细地址」这一条要额外做点事:这一栏**只有它一个字段**,
光把名字写回来,门牌号仍然没有 ⇒ 加 `data-addr-detail="1"`:
选中候选时把地址一并写进 value(`名字 · 门牌`),
并在这一行下面用一行灰字显示「省市区 · 门牌」。 */ ?>
<input class="input" id="ev-location" name="location"
value="<?= e((string)($evF['location'] ?? '')) ?>" placeholder="如:学校三楼会议室"
data-addr data-addr-detail="1">
</div>
<?php echo $evRowOpen(); ?>
<?= $evLab('提醒') ?>
<?php /* v24 第 20 批 第 8 条:**多选**(用户原话:「一个日程,我可以选择让它
5分钟提醒,10分钟提醒等等。」)。
⚠️ 用 checkbox 而不是 `<select multiple>`:后者在手机上要"按住再点",
几乎没法用(任务共享那个 multiple 下拉就是因此被换掉的)。
⚠️ 字段名必须是 `reminder_minutes[]` —— Calendar::reminderMinutes() 认得数组。
写成 `reminder_minutes`(不带 [])会让 PHP 只留最后一个勾选。
🔴 v43(用户第 5 条):他要求把这一栏底下那句「多选与不提醒」的说明去掉
⇒ 这里删掉那个 `.tip`(多选语义由复选框本身表达)。 */
$curReminders = \App\Calendar::reminderList($evF['reminder_minutes'] ?? null); ?>
<div class="rem-picks">
<?php foreach (\App\Calendar::REMINDERS as $mins => $word): ?>
<label class="chk-inline rem-pick">
<input type="checkbox" name="reminder_minutes[]" value="<?= (int)$mins ?>"
<?= in_array((int)$mins, $curReminders, true) ? 'checked' : '' ?>>
<span><?= e($word) ?></span>
</label>
<?php endforeach; ?>
</div>
</div>
<?php /* v24 第 5、6 条:起止各一个「日期 + 时分秒」合在一起的控件(全局统一),
不再拆成"日期一格 + 时刻一格"。两个控件长得一样,扫一眼就知道哪个是开始。 */ ?>
<?php /* 用户 2026-10-04 第 7 条:日程的起止也是一个**范围** ——
升级后由一个 Semi dateTimeRange 一次选完。服务端仍收两组 *_date/_hour/… 字段。 */ ?>
<?php /* 🔴 v24 第 21 批 第 7 条:「开始时间和结束时间那里,用我们统一的时间范围选择器
重做,现在功能无法使用。」
「功能无法使用」的真根因(真机探针实测出来的,不是猜的):
app.js 的 upgradeRange() 有一道「**两端都得可见**才升级成范围控件」的判断
(`boxes.every(bx => bx.offsetParent !== null)`)。而这一版**结束那一端默认
带 `hidden`**(要点「+ 添加结束时间」才出现)→ 判断永远不成立 →
范围控件**从来没被挂上去过**(实测 data-semi-range-done 恒为 null)。
更糟的是 CSS 还有 `html.js .dt-range:not(.is-semi-range) > .dt-input
{ visibility: hidden }` —— 两端于是都是**不可见**的:
屏幕上一片空白、点哪儿都没反应。这就是「无法使用」的全部。
修法:**结束那一端不再默认藏起来**,范围控件一次性就挂上。
起止成对本来就该一起填。想表达"只有一个时刻"的话,范围控件 clear 一下即可。
⚠️ 顺带删掉「+ 添加结束时间 / 去掉结束时间」那套开关:
它切换显隐会让范围控件反复重挂;范围控件自己就能表达"没有结束"。
🔴🔴 v43(用户 2026-10-11 第 5 条):「开始和结束时间也没有用我们最新的控件,
直接就是四个字"时间范围"。」
—— 两处真缺陷一起修:
① 这个字段块**没传 `$dtPlugin='antd'`** ⇒ 用户看到的还是年/月/日 + 时/分
五个原生下拉,而不是他点名要的 Ant Design 控件;
② 标签写的是「时间范围」四个字,没告诉人"这是开始 / 结束"。
⇒ `$dtPlugin='antd'`(与观影/行程同一套:面板有可点的时/分列、中文「确定」),
标签改「开始 / 结束」(并去掉那颗必填星号的位置歧义 —— 只标在开始上)。
⚠️ 契约一字未改:隐藏字段仍是 `*_date/_hour/_minute`;antd 拉不动时
自动退回自建下拉(渐进增强)。
🔴🔴 v44(用户 2026-10-12 第 2 条):「开始/结束,改为:日期范围:。
之前的日期选择控件还有,需要删除。」
—— 用户要三种东西:① 标签「日期范围」;② 标签与**最新控件同行**;
③ 原生那套(年/月/日 + 时/分 五个下拉)**真的消失**。
🔴🔴 **"还在"的真根因(结构性,不是样式没写)**:
观影那对是 `‹div.mv-row.dt-range› ‹div.dt-input›`(**直接子**),
而日程这对是 `‹div.dt-range› ‹div.field.ev-dt› ‹div.dt-input›`
—— `.dt-input` 是**孙子**。
于是那条最常见的收起规则 `.dt-range.is-antd > .dt-input`(**直接子**选择器)
**在日程上根本不命中** ⇒ antd 挂上了、原生五个下拉照样显示
(用户看到两个控件并排,"之前的日期选择控件还有")。
⚠️ 与 v43 纪念日那次是**同一个家族**:结构一变,直接子选择器就静默失效。
⇒ 两手都做:① 把日程的结构改成与观影**一致**(`.dt-input` 直接子);
② CSS 里把 `>` 放宽成后代选择器(0,2,0 → 仍高于基类),
将来结构再变也不会又冒出第二个控件。
⇒ 形态:由 `_datetime.php` 的 `$dtLabel` 把「日期范围:」
**内联在控件那一行**(不靠宿主的行结构),一个 `.mv-lb` 挂在
`.dt-input` 里 → 三样天然同行,宽度不够时整体换行。 */ ?>
<div class="dt-range<?= $evMv ? ' mv-row is-tagged' : '' ?>">
<?php /* ⚠️ 标签只有**一个**,挂在**第一个端点**上(不是各挂各的):
升级成一个范围控件之后,"开始"与"结束"就是同一个控件的两个端点,
两个标签会变成"看着像两个字段、其实是一个"的错觉。
⚠️ 观影式:标签由 `_datetime.php` 内联(`$dtLabel`)—— 要的是与控件同行;
两列式(编辑整页):标签仍是 `.dt-range-label`,在两端之上。 */ ?>
<?php if (!$evMv): ?>
<span class="dt-range-label is-req">日期范围</span>
<?php endif; ?>
<?php $dtName = 'starts_at'; $dtId = 'ev-start';
$dtValue = $evF['starts_at'] ?? null; $dtRequired = true;
$dtRange = 'start'; $dtPlugin = 'antd'; $dtClass = 'field ev-dt'; $dtFieldId = '';
if ($evMv) { $dtLabel = '日期范围'; $dtLabelReq = true; }
require __DIR__ . '/../_datetime.php'; ?>
<?php $dtName = 'ends_at'; $dtId = 'ev-end';
$dtValue = $evF['ends_at'] ?? null; $dtRequired = false;
$dtRange = 'end'; $dtPlugin = 'antd';
$dtClass = 'field ev-dt'; $dtFieldId = 'ev-end-field';
/* 结束端点不重复标签(同一个范围控件,标签已经挂在开始那一端上) */
$dtLabel = ''; $dtLabelReq = false;
require __DIR__ . '/../_datetime.php'; ?>
</div><?php /* /dt-range */ ?>
<?php /* 🔴 v43(用户第 5 条):他要求把「结束留空即无明确结束」的那条提示去掉
⇒ 那句 `.field-hint` 整块删除(不再输出)。 */ ?>
<?php echo $evRowOpen(); ?>
<?php /* 🔴 2026-11-05(用户:「全天那个应该是输入框,它应该往右和那个输入框对齐」):
观影式排版(`.mv-row`)里每一行的左侧都有一个 62px 的 `.mv-lb` 标签列,
控件从 62px 之后开始 —— 上面所有输入框的左边缘都在那条线上。
而「全天」这一行**没有标签**(它不需要文字标签,checkbox 自带「全天」二字),
`<label class="chk-inline">` 就直接贴在行的最左边(x=0),
比上面的输入框往左偏了 62px,看着像"错位了"。
⇒ 补一个**空的** `.mv-lb` 占位(`no-colon` 免得画出「:」)。
不用 `padding-left:62px` 的原因:那条 62px 是 `.mv-row > .mv-lb`
的 `flex:0 0 62px` 算出来的,而 `.mv-lb` 的宽度在窄屏媒体查询里
会变(见 app.css 的 6196 行附近),写死 padding 会跟它脱节。
用空标签则**天然跟随**同一套规则,改一处两边都对。
⚠️ 仅在观影式排版(`$evMv`)下补:整页表单是 `.form-grid` 两列,
没有 62px 标签列,补了反而会多出缩进。 */ ?>
<?php if ($evMv): ?><span class="mv-lb no-colon" aria-hidden="true"></span><?php endif; ?>
<label class="chk-inline">
<input type="checkbox" id="ev-allday" name="all_day" value="1"
<?= $evIsAllDay ? 'checked' : '' ?>>
<span>全天</span>
</label>
<?php /* 🔴 v43(用户第 5 条):他要求把「全天只管日期、不管几点」的说明也去掉
⇒ 那句 `.field-hint` 整块删除。 */ ?>
</div>
<?php /* 用户 2026-10-03 第 5、6 条:重复日程。
放在「全天」之后的自然位置:先定"哪一天/几点",再定"要不要重复"。
周期用 FREQ 记号,结束条件三档(一直 / N 次 / 到某天),另加"工作日"。 */ ?>
<?php echo $evRowOpen(); ?>
<?= $evLab('重复', 'ev-freq') ?>
<select class="select" id="ev-freq" name="freq">
<?php foreach (\App\Repeat::FREQS as $k => $w): ?>
<option value="<?= e($k) ?>"<?= (string)$evRp['freq'] === (string)$k ? ' selected' : '' ?>><?= e($w) ?></option>
<?php endforeach; ?>
</select>
</div>
<?php /* 🔴 v44(用户 2026-10-12 第 2 条):「重复这里,只有选择重复的时候,
下面的结束条件和重复次数、重复到,这几个选项才出现,否则就隐藏。」
⇒ 三行**服务端**就按"是否重复"给出 `hidden`(不是只靠 JS):
· 回填编辑(本来就有 rrule)时它们该出现;
· 新建(默认"不重复")时它们从一开始就是隐藏的 —— 页面上不会闪一下才收起来。
⚠️ 行**本身**也藏:原来只有行**里面**那个 box 带 hidden,
于是标签还孤零零留在表单里(用户看到的"还在")。
⚠️ 用户同条还要「重复次数(含第一次)」那个括号去掉 ⇒ 标签改「重复次数」。 */ ?>
<?php echo $evRowOpen('', !$evRepeating); ?>
<?= $evLab('结束条件', 'ev-rep-end') ?>
<div id="ev-rep-end-box"<?= $evRepeating ? '' : ' hidden' ?>>
<select class="select" id="ev-rep-end" name="rep_end">
<?php foreach (\App\Repeat::ENDS as $k => $w): ?>
<option value="<?= e($k) ?>"><?= e($w) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<?php echo $evRowOpen('', !$evRepeating || (int)$evRp['count'] <= 0); ?>
<?= $evLab('重复次数', 'ev-rep-count') ?>
<div id="ev-rep-count-box"<?= ($evRepeating && (int)$evRp['count'] > 0) ? '' : ' hidden' ?>>
<input class="input" type="number" min="1" max="2000" id="ev-rep-count" name="rep_count"
value="<?= (int)($evRp['count'] ?: 10) ?>">
</div>
</div>
<?php echo $evRowOpen('', !$evRepeating || (string)$evRp['until'] === ''); ?>
<?= $evLab('重复到', 'ev-rep-until') ?>
<div id="ev-rep-until-box"<?= ($evRepeating && (string)$evRp['until'] !== '') ? '' : ' hidden' ?>>
<?php /* 🔴 v45(用户 2026-10-12 第 1 条):「添加日程这里,当选择了重复,
并且选择了重复到某天为止,这时候重复到会出现,有一个日历选择框,
这个日历选择框也改为我们最新的控件。」
⇒ 把原来那个原生 `<input type="date">` 换成 AntD **纯日期** DatePicker。
⚠️ 做法与「纪念日 → 日期」完全同一套(v42/v43 已验证):
· 外层 `.dt-input` + `data-antd-date="1"` → app.js 的 `mountAntdDate` 接管;
· 值载体仍是**同一个** `name="rep_until"` 的 `<input type="date">`,
只是被 `.dt-native` 包起来(否则那条"收起原生框"的 CSS 不命中 ⇒
会出现"多了一个控件",正是 v43 纪念日踩过的坑);
· 服务端契约**一字不改**:`Repeat::build($_POST)` 照旧读 `rep_until`。
⚠️ 千万别给这个 `.dt-input` 加 `data-dt="…"`:那是"年/月/日+时/分"那套
四字段契约的标记,加上去会被 `upgrade()` 抢走、反而绑错处理器。 */ ?>
<div class="dt-input dt-antd-single" data-antd-date="1" id="ev-rep-until-wrap">
<span class="dt-native">
<input type="date" class="dt-date" id="ev-rep-until" name="rep_until"
value="<?= e((string)($evRp['until'] ?? '')) ?>" aria-label="重复到">
</span>
</div>
</div>
</div>
<?php /* 🔴 v47(用户 2026-10-13):「日程的那个状态,什么确定、待定、取消,
我觉得没有什么意义,把它给删掉吧,不要了。」
⇒ 「状态」整块**删除**。
⚠️ 服务端契约**刻意保留**(用户明确说"数据库可以保留,后续会有一个整体的清理"):
- `events.status` 列留着、不删;
- `Calendar::save()` 收到不带 status 的表单时,用**原值**(更新)或
`'confirmed'`(新建),与删掉这一栏之前**完全一致** ——
不会因为删了控件就把已有日程的状态改写掉(见 index.php 里那两处
`(string)($ev2['status'] ?? 'confirmed')`)。
- 导出 iCalendar 时 STATUS 照旧按原值走,别人的日历里不会看到状态变化。
下方原来还有一段给 `ev-status` 的联动脚本,也一并删。 */ ?>
<?php /* 🔴 v44(用户 2026-10-12 第 2 条):「这段时间算忙碌,这一块直接给它去掉就行了,
没有什么用。」⇒ 「这段时间算 / 忙碌 / 空闲」整块删除。
⚠️ 服务端契约**没变**:`transp` 字段没随表单提交时,`!empty($_POST['transp'])`
一律得 false(= 忙碌),与原来的默认值一致 —— 不会因为删了这一块
而把已有日程的忙闲改掉(更新时传的是 `(int)($ev2['transp'] ?? 0)` 原值)。
下方 script 里原来还有一段给 `ev-transp` 的联动,也一并删。 */ ?>
<?php echo $evRowOpen('full'); ?>
<?= $evLab('备注', 'ev-note') ?>
<textarea class="input" id="ev-note" name="note" rows="3"
placeholder="要带什么、跟谁一起…"><?= e((string)($evF['note'] ?? '')) ?></textarea>
</div>
<?php echo $evRowOpen('full'); ?>
<?= $evLab('附件(可多选)') ?>
<?php /* v24 第 21 批 第 7 条:统一成「点击或拖动进行上传」的框。
⚠️ 文件框的 id 保持 `ev-attach`(原来就叫这个)—— e2e 与部署断言里
有按 id 定位的写法,改名会把它们静默打成假红。 */
$sdName = 'attach[]'; $sdId = 'ev-attach'; $sdSlot = 'photo';
require __DIR__ . '/../_stage_drop.php'; ?>
</div>
<script>
(function () {
/* 「全天」勾上就把两个时刻控件的**时间部分**收起来 —— 日期还得留着。
只藏 .dt-time(时/分/秒那一段),.dt-date 是日期,藏了就没法选哪天了。 */
var box = document.getElementById('ev-allday');
if (!box) return;
function sync() {
Array.prototype.forEach.call(document.querySelectorAll('[data-dt] .dt-time'), function (el) {
el.hidden = box.checked;
});
}
box.addEventListener('change', sync);
sync();
/* ⚠️ v24 第 21 批 第 7 条:原来这里有一整段「结束时间显隐 + 跟着开始时间走」的 JS。
现在起止两端**始终可见**,并且由范围控件统一接管(一次选完两端),
所以那段"同步两端"的逻辑整个删掉 —— 留着不但没用,还会和范围控件抢着改那两个
原生字段(两处都写 → 值互相覆盖)。范围控件选完会自己把两端分别写回原生字段
(见 app.js 的 onRange → writeDT),服务端契约不变。 */
/* 重复:选了周期才显示"结束条件";结束条件选"次数"才显示次数框、选"到某天"才显示日期框。
三行默认都 hidden —— 不重复时一个都不该出现(少即是清楚)。
🔴 v44(用户第 2 条):藏的是**整行**(`.mv-row` / `.field`),不是行里那个 box ——
只藏 box 的话标签还孤零零留在表单里。行 = box 的 `closest('.mv-row, .field')`。 */
var freq = document.getElementById('ev-freq');
var endBox = document.getElementById('ev-rep-end-box');
var endSel = document.getElementById('ev-rep-end');
var cntBox = document.getElementById('ev-rep-count-box');
var untilBox = document.getElementById('ev-rep-until-box');
function rowOf(el) {
return el && el.closest ? el.closest('.mv-row, .field') : null;
}
function toggle(el, on) {
if (!el) { return; }
el.hidden = !on;
var row = rowOf(el);
if (row) { row.hidden = !on; } // 连带整行一起显隐
}
function syncRep() {
if (!freq || !endBox) { return; }
var repeating = freq.value !== '';
toggle(endBox, repeating);
var how = endSel ? endSel.value : 'never';
toggle(cntBox, repeating && how === 'count');
toggle(untilBox, repeating && how === 'until');
}
if (freq) { freq.addEventListener('change', syncRep); }
if (endSel) { endSel.addEventListener('change', syncRep); }
// 回填:编辑一条已有的重复日程时,把结束条件按库里的值选对
<?php if ((int)$evRp['count'] > 0): ?>
if (endSel) { endSel.value = 'count'; }
<?php elseif ((string)$evRp['until'] !== ''): ?>
if (endSel) { endSel.value = 'until'; }
<?php endif; ?>
syncRep();
/* 「全天」+「重复」的组合提示:重复日程必须有开始日期,
否则展开时找不到锚点(后端会当成"不重复"处理)。 */
var startDate = document.querySelector('[data-dt] input.dt-date');
if (freq && startDate) {
freq.addEventListener('change', function () {
if (freq.value !== '' && startDate.value === '') {
startDate.setCustomValidity('重复日程要先选开始日期');
} else {
startDate.setCustomValidity('');
}
});
}
})();
</script>
<?php /* 🔴 v94:顺手去掉了 unset 列表里的 `$evLabelStar` —— 它在整个仓库里
**从来没有被赋过值**(只有这一行 unset 提到它),是历史遗留的死引用。 */ ?>
<?php unset($evF, $evAllDayDefault, $evIsAllDay, $evFields, $evAllDay, $evMv, $evMvStyle,
$evRowOpen, $evLab, $evRp, $evRepeating); ?>