下拉 —— 名册一多就变成"在一长串里瞪眼找",而且三种地方三种手感。 * * 用法(在模板里先准备好变量再 include): * $ppName 表单字段名。多选传 'attendees[]';单选传 'owner_id' 等 * $ppPeople 候选的人(Person::roster() 的行,至少要有 id / name) * $ppSelected 已选的 id 数组(单选传 0 或 1 个) * $ppMode 'multi'(默认)| 'single' * $ppOpenBtn 打开按钮文案(默认 multi=「+ 添加人」/ single=「选择」) * $ppTitle 弹窗标题(默认「选择人」) * $ppAllowEmpty 单选模式下是否给一个「不选」选项(筛选表单用;值为 0) * $ppEmptyText 单选且未选时的占位(默认「未选择」) * $ppTip 可选,渲染在按钮下面的一句话 * * ⚠️ 无 JS 时也能用:选项就是弹窗列表里的原生 checkbox / radio, * noscript 样式把弹窗摊平成一个普通列表,提交语义与有 JS 时完全一致。 * * ⚠️ 同一页出现多个选择器时 $ppId 必须不同 —— 片段自己按出现顺序编号, * 调用方不用管(但同一页重复 include 同一份变量也不会撞)。 */ $ppMode = $ppMode ?? 'multi'; $ppPeople = $ppPeople ?? []; $ppSelected = array_map('intval', (array)($ppSelected ?? [])); $ppOpenBtn = $ppOpenBtn ?? ($ppMode === 'single' ? '选择' : '+ 添加人'); $ppTitle = $ppTitle ?? '选择'; $ppEmptyText = $ppEmptyText ?? '未选择'; $ppAllowEmpty = !empty($ppAllowEmpty); // 同一页多个实例要各有一套 id,否则 document.getElementById 全抓到第一个。 // // ⚠️⚠️ 别用文件顶层的 `static $ppSeq` 做这个计数 —— PHP 里**被 include 的文件**顶层的 // `static` 初值会**每次 include 都重新绑定**(与函数体内的 static 不同,那个才跨调用)。 // 实测:同一页 include 三次,拿到的都是 pp1。资源页今天就有两处(归属人 + 共享给谁), // DOM 里两套 pp1 撞在一起,getElementById / `