`,改成纯网页的
* 模态框:预置色板 + 自定义 hex 输入 + 实时预览。
*
* ─────────────────────────────────────────────────────────────────────
* 两种用法(靠 `$cpName` 是否为空区分):
*
* **A. 表单模式** —— 值随外层表单一起提交(新建/编辑日历本的弹表单用这个):
* ```php
* $cpName='color'; $cpValue='#4A7C82'; $cpPalette=\App\Calendar::PRESET_CAL_COLORS;
* require __DIR__ . '/../_color_picker.php';
* ```
* ⇒ 出 ``。
*
* **B. 独立模式**(`$cpName` 留空)—— 值**不参与表单提交**,由调用页自己拿去做 AJAX:
* ```php
* $cpName=''; $cpValue=$cal['color']; $cpId='calColor'.$id;
* $cpData=['cal-id'=>$id]; // 附加到 .cp-field 上的 data-*
* require __DIR__ . '/../_color_picker.php';
* ```
* ⇒ 出 ``(**无 name**)。调用页监听 `cp:change` 事件。
*
* 🔴 为什么要 B 模式:日历本清单是"一张大表单管显隐"(`show[]` 整页一次提交),
* 如果每行再塞一个 `name="color"`,一次提交会带上 N 个同名 `color` 字段,
* 服务端只能拿到最后一个 —— 于是"改了第 3 本的颜色,保存时把第 5 本也改了"。
* 所以行内改色走 AJAX 单独落库,**不混进那张大表单**。
*
* @var string $cpName 隐藏域 name;空串 = 独立模式(不提交)
* @var string $cpValue 初始值('#RRGGBB')
* @var array $cpPalette 预置色板('#RRGGBB' 列表)
* @var string $cpId 可选,值载体的 id;不给则用 'cp_' + $cpName(独立模式必须给)
* @var string $cpLabel 可选,按钮上的无障碍名
* @var array $cpData 可选,附加到容器上的 data-*(键 => 值)
*/
$cpName = (string)($cpName ?? 'color');
$cpValue = strtoupper((string)($cpValue ?? '#4A7C82'));
$cpPalette = array_values(array_filter((array)($cpPalette ?? []), 'is_string'));
$cpData = (array)($cpData ?? []);
$cpLabel = (string)($cpLabel ?? '选择颜色');
/* 🔴🔴 `$cpId` 必须**先给默认值**再用(2026-10-21 实测踩到):
原先只有下面那个 `if (!$cpId)` —— 表单模式下调用页**根本不传** `$cpId`,
于是这里读一个未定义变量 ⇒ `Warning: Undefined variable $cpId`
⇒ 那条 Warning 被 `display_errors` 打成 HTML **塞进页面正文**:
· 页面上会看见一段错字;
· 更糟的是它出现在 `` **之前**,
某些布局下会把表单控件的行内关系弄乱;
· 而**页面照样 200、照样能用** ⇒ 只有"查正文里有没有 PHP 错误"的断言能抓到。
⇒ 教训:可选参数一律 `?? ''` 兜底,**不要靠 `if (!$x)` 当存在性判断**
(读它那一刻 Warning 就已经产生了)。
⚠️ 我的 v72 前端探针当时 47/0 全绿却没抓到它 —— 因为它只查 JS 报错、
不查服务端正文。已给探针补上这条(见 `_fb_v72_ui.py` 的「PHP 正文干净」)。 */
$cpId = (string)($cpId ?? '');
if ($cpId === '') {
// 表单模式用 name 派生;独立模式没有 name ⇒ 调用页必须给 id
$cpId = 'cp_' . preg_replace('/[^A-Za-z0-9_]/', '_', $cpName !== '' ? $cpName : 'color');
}
/* ⚠️ 初始值本身**不是**合法 hex 时(老数据、手工改库),退回落日色而不是把
`background: xxx` 直接拼进 style —— 那样会把整条样式声明弄坏,
旁边几个属性一起失效(用户看到的是"色块变成一个方块")。 */
if (!preg_match('/^#[0-9A-Fa-f]{6}$/', $cpValue)) {
$cpValue = '#4A7C82';
}
$cpDataAttr = '';
foreach ($cpData as $k => $v) {
$cpDataAttr .= ' data-' . e((string)$k) . '="' . e((string)$v) . '"';
}
?>
>
id="= e($cpId) ?>" value="= e($cpValue) ?>">
`(原生取色控件)。
那是**没改完的旧写法**,同时踩了两件事:
① **用户点名要去掉的就是它**(「选颜色那个地方点了之后是弹出 Windows 的
一个颜色选择框,这个不好」)—— 留着它等于需求没做;
② CSS/JS 是照「按钮 + 内部小色点 + hex 文本」设计的:
`.cp-trigger` 是 `inline-flex` 胶囊、`.cp-trigger-dot` / `.cp-trigger-hex`
是它的子元素、模态框靠 `[data-cp-open]` + `[data-cp-target]` 打开并回填。
而原生 `` **不能有子元素**,也没有这两个属性
⇒
· `syncTrigger()` 找不到 `.cp-trigger-dot/hex` ⇒ 改完色**按钮上不刷新**;
· `document.querySelectorAll('[data-cp-open]')` 一个都取不到
⇒ 模态框**永远打不开**,用户点到的是浏览器原生取色框。
⇒ 现在输出的是 `