v39 第 39 批:观影弹框(antd 中文化 + 影院搜索 + 简介备注各占一行 + 海报照片对齐)

用户原话(四条):
  ①「观影模块的时间控件,比如说时间呀,还有下面的确定按钮啊。现在显示的都是英文,
     应该给它设置成中文。」
  ②「影院那个地方应该有一个搜索的功能,下拉的地方上面有一个搜索筛选的功能。」
  ③「简介和备注不要放在一行,也就是说一个字段它就占一行。所以简介后面是一行,
     备注后面是一行。」
  ④「海报和观影照片为什么和上面的标题不一样呢?海报的字体和简介是一样的,也带一个冒号。
     然后把后面点击或拖动进行上传往后放,和简介后面的输入框保持一样大小。观影照片也是
     类似的,观影照片也是加粗。和简介的字体保持一致,不带冒号,后面放上点击或拖动进行上传。」

① antd 中文化 —— 三个条件缺一不可(缺任一条按钮就还是英文)
  · 🔴 **英文的真根因**:`antd.min.js` 这个产物**不含 locale 包**
    (实测 `window.antd.locales` 是 undefined)⇒ 只能用它内置的英文默认值。
    **换文件**才解决,不是加参数 ⇒ 改用 `antd-with-locales.min.js`
    (多带各语言包,全局多挂 `antd.locales`,`antd.locales.zh_CN` 即简体中文包)。
  · 🔴 dayjs 的中文 locale 是**单独一个文件**(核心包只有 en)⇒ 引 `dayjs-zh-cn.js`
    并调 `dayjs.locale('zh-cn')`(星期/月份由 dayjs 出)。
  · 🔴 必须用 `<ConfigProvider locale={zh_CN}>` 包住控件 —— antd 读的是**上下文里的**
    locale,不包的话**日历文字会变中文、按钮却仍是英文**(最容易误判成"已经中文化了")。
  · 独立探针 `_tooling/_antd_probe/{zh.html,zh_probe.py}` 9/0 把这三条钉死。
  · 删除不再引用的 `assets/vendor/antd/antd.min.js`(1.5MB 冗余,留着会让
    "两个 antd 哪个是真的"变成下一个坑);部署断言改为钉四资源 + ConfigProvider + zh-cn。

② 影院下拉加搜索筛选
  · 搜索框放在弹出列表**内部**(`.cs-search` 吸顶),打开下拉**即聚焦**;回车选第一项。
  · 🔴 `.cs-item[hidden] { display: none; }` **必须显式写** —— `.cs-item` 自己声明了
    `display:flex`,会盖过浏览器默认的 `[hidden]{display:none}` ⇒ 过滤"看着完全没生效"
    (项目第 11 批选人组件踩过同一个坑)。
  · 三个事件都 stopPropagation,否则冒到 document 的"点外面关"⇒ 一点搜索框下拉就没了。
  · 「不记录影院」恒保留(清空选择的出口);收起时清空搜索词(否则下次带着旧过滤)。

③ 简介 / 备注各占一行
  · 撤销 v38 的 `.mv-pair` 并排(半宽的多行文本框每行都换行,更难读);
    CSS 与模板里的并排容器**全部清干净**。

④ 海报 / 观影照片与简介对齐 —— 它们本来就不需要特殊规则
  · 用户看到的"更窄/不一样"有两个原因:① 海报那块被误包进 `.mv-grid.mv-pair`
    且**里面只有一个孩子** ⇒ grid 两列让它只占**半宽**;② 标签被一条
    "宽度自适应、左对齐"的规则覆盖掉了固定 62px 右对齐 + 冒号。
  · ⇒ 两块都改成普通 `.mv-row.mv-row-media`,标签用 `.mv-lb`(**继承**简介那套),
    内容包 `.mv-media-body`(占满剩余);观影照片标签加 `.no-colon`。
    实测:所有控件左缘 **424**、宽度 **624**,与简介**完全一致**。
  · 🔴🔴 **通识**:v38 的 `.mv-media`(手填 margin-left:72px)是**被一个取错元素的
    探针误导出来的** —— 它按文档序取"第一个非标签子元素",而 `_stage_drop.php` 吐的
    `.sr-only` 文件框是 `position:absolute`(**脱离文档流、不参与 flex**),却被算成
    "控件",于是得出"1px 隐框把上传框推走 130px"的**假结论**。
    ⇒ "对齐"类断言必须取直接子元素、且只取真正参与布局的那个;量到反常数字先怀疑探针。

验证(全部真机 + 反向验证):
  · _fb_b39_movie.py **39/0**(中文化三条 + 搜索框打开即聚焦/输入即过滤/无匹配空态/
    点搜索框不关下拉/回车选中/重开已清词 + 简介备注各占一行且占整行宽 +
    海报照片标签字重字号同简介/海报带冒号/照片不带/上传框左缘与宽度同简介);
  · _fb_b39_narrow.py **21/0**(宽 1440 / 平板 768 / 窄屏 390:不横向溢出、
    标签与控件仍同行、影院下拉不出视口、搜索框可用);
  · _fb_b39_falsify.py **14/0**(四处各自证伪成功:拿掉 ConfigProvider→按钮变英文、
    藏起搜索框→②红、塞回两列并排→③红、标签退回普通 span→④红;复原回全绿);
  · v38 两套按 v39 更新后 **36/0**(新增"已无 mv-pair / 主列 6 行");
    v38 的旧窄屏套件(断言"简介备注并排")已作废删除,由 v39 窄屏完全覆盖;
  · 全路由 e2e **1503/0**、日期控件真机 **53/0** + 反向 **7/0**、v37 两套、
    观影照片两套、b30 三件套、各批真机与 DAV 三套、部署静态断言空跑 —— 全绿;
  · 线上四个 antd 资源 HTTP **200 可达**;app.js / app.css / form.php / layout.php
    的 SHA256 与本地**逐字节一致**。

⚠️ 顺带修的探针/夹具问题(都不是页面 bug):
  · 夹具库重铺后 `cinemas` 是**空的** ⇒ v39 探针**自带播种**(造两家影院),
    不再依赖别的套件先跑过(与 v38 的"名册只有 1 人"同类:页面没问题,是夹具没数据);
  · 部署断言第一版按裸词 grep `mv-pair`,把注释里那句"已撤销 .mv-pair"当成回归
    ⇒ 改为只认 `class="...mv-pair..."` 这种**真的类名用法**;
  · v39 探针第一版判"简介/备注各占一行"看的是"是不是同一个 DOM 节点"——
    那个在**并排**时也是 false(并排仍是两个 .mv-row),连并排都抓不住
    (反向验证当场抓到 13/1)⇒ 改为按**垂直位置**判(top 相差 ≥20px)。
This commit is contained in:
2026-10-10 12:42:43 +08:00
parent 8661ab6475
commit f8b1b96fd7
7 changed files with 592 additions and 416 deletions
+60 -27
View File
@@ -2884,11 +2884,35 @@ body[class] .semi-datepicker-day-inhover { background-color: color-mix(in srgb,
padding: 4px; max-height: 280px; overflow-y: auto;
}
.cs-menu[hidden] { display: none; }
/* 🔴 v39(用户 2026-10-10):「影院那个地方应该有一个搜索的功能,下拉的地方上面
有一个搜索筛选的功能。」
⇒ 弹出列表顶部一条搜索框。⚠️ 它要**吸顶**(列表长时往下滚,它跟着走)——
不然滚两下搜索框就没了,"搜索筛选"等于白加。 */
.cs-search {
position: sticky; top: -4px; z-index: 2;
margin: -4px -4px 4px; padding: 6px 6px 6px;
background: var(--surface); border-bottom: 1px solid var(--line-2);
}
.cs-search-input {
width: 100%; box-sizing: border-box;
padding: 7px 10px; font: inherit; font-size: 13px; color: var(--ink);
background: var(--paper, #FDFBF7); border: 1px solid var(--line);
border-radius: 7px;
}
.cs-search-input::placeholder { color: var(--muted); }
.cs-search-input:focus { outline: none; border-color: var(--brand); }
/* 没有匹配项时的提示(没搜过 / 有结果时都 hidden,不占高度) */
.cs-empty { padding: 10px 12px; font-size: 13px; color: var(--muted); text-align: center; }
.cs-empty[hidden] { display: none; }
.cs-item {
display: flex; align-items: center; gap: 8px; width: 100%;
padding: 8px 10px; border: 0; border-radius: 7px; background: transparent;
font: inherit; font-size: 13.5px; color: var(--ink); cursor: pointer; text-align: left;
}
/* ⚠️ 过滤用的 `hidden` 必须写出来:`.cs-item` 自己声明了 `display:flex`,
它会**盖过**浏览器默认的 `[hidden]{display:none}` ——
这正是第 11 批"选人过滤一直是坏的"的同一个坑,别再踩第二次。 */
.cs-item[hidden] { display: none; }
.cs-item:hover, .cs-item:focus { background: var(--line-2); outline: none; }
/* 新建影院:居中弹框(与名册的动作弹框同一形态,只是更宽) */
@@ -5204,21 +5228,37 @@ h5.md-h, h6.md-h { font-size: 14px; }
}
.mv-row.dt-range .err { flex: 1 1 100%; margin-left: 72px; }
/* 海报 / 观影照片:**标签一行、内容另起一行**(不用 .mv-row)。
🔴 为什么不能跟上面的字段一样"标签在左、控件在右"(真机量出来的):
这两块的控件区里有好几个东西,而且 `_stage_drop.php` 会在最前面插一个
`.sr-only` 的 `<input>`(绝对定位、1px)。按 flex 排的话,那个 1px 的隐框
会占掉一整份 `flex:1 1 auto`,把真正的上传框**推到 130px 之外**
(实测:左缘 443 vs 隔壁 573 —— 一眼就是歪的)。
⇒ 纵向结构最省心:内容从**标签列右侧**(72px = 62 标签 + 10 间距)开始,
与上面各行的控件左缘**同一条竖线**。 */
/* 海报 / 观影照片:**与简介同一套 `.mv-row`**(标签在左、内容在右)。
🔴🔴 v39(用户 2026-10-10):「海报和观影照片为什么和上面的标题不一样呢?
海报的字体和简介是一样的,也带一个冒号。然后把后面点击或拖动进行上传往后放,
和简介后面的输入框保持一样大小。观影照片…也是加粗。和简介的字体保持一致,
不带冒号。]
⇒ 关键认识:**这两块不需要任何特殊规则**,它们就是普通字段行 ——
标签 = `.mv-lb`(继承上面那条:14px / 600 / var(--ink) / 固定 62px / 右对齐
+ `::after` 冒号),内容 = 行里唯一参与弹性布局的那一项(占满剩余、左缘 444)。
v38 之所以把它们写成 `.mv-media`(另起一行 + 手填 `margin-left:72px`),
是因为当时**探针取错了元素** —— 它按文档序取"第一个非标签子元素",
而 `.sr-only` 的文件框是 `position:absolute`(**脱离文档流、不参与 flex**),
却被算成了"控件",于是量出"1px 隐框把上传框推走 130px"这个**假结论**。
⇒ 这轮改用真机逐行量左缘来定,不再靠推断。
⚠️ 想让它别再"另起一行":这里**不设** flex-direction —— `.mv-row` 本来就是
`display:flex; align-items:flex-start`,标签与内容天然同一行。 */
.mv-grid-media { margin-top: 2px; gap: 14px; }
.mv-media { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.mv-media > .mv-lb { flex: 0 0 auto; width: auto; text-align: left; padding-top: 0; }
.mv-media-body { margin-left: 72px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
/* 内容区:纵向排(缩略图 + 上传框),并**占满**标签右侧的剩余宽度
(`flex:1 1 auto` + `min-width:0` 与 `.mv-row > *:not(.mv-lb)` 那条一致,
这里显式写一遍是为了让"它的宽度 = 简介输入框的宽度"这件事一眼可读)。 */
.mv-row-media > .mv-media-body {
flex: 1 1 auto; min-width: 0;
display: flex; flex-direction: column; gap: 8px;
}
.mv-media-body .photo-grid { margin-top: 0; }
/* 观影照片的标签**不带冒号**(用户原话:「观影照片也是加粗。和简介的字体保持一致,
不带冒号。」)—— 这类"有标签但不带冒号"的情况靠一个类开关,别去改 ::after 的全局规则。 */
.mv-lb.no-colon::after { content: none; }
/* ⚠️ `.sr-only` 是**绝对定位**的(见 .sr-only 的定义);它不该影响任何布局。
这里再钉一次"不参与 flex 分配",免得又被当成一个控件。 */
这里再钉一次"不参与 flex 分配"(万一将来有人把 .sr-only 的定义改了)。 */
.mv-media-body > .sr-only,
.mv-row > .sr-only { position: absolute; flex: 0 0 0; }
@@ -5275,18 +5315,11 @@ h5.md-h, h6.md-h { font-size: 14px; }
.mv-row.dt-range.dt-cols > .dt-range-label { flex: 0 0 52px; width: 52px; font-size: 13px; }
}
/* ---- v38 ②:能并排的都并排(用户:「能把它排成一行的都排成一行」)----
两处成对的字段:简介+备注、海报+观影照片。
⚠️ 用 **grid 两列**而不是 flex-basis:fr 单位保证两列**永远等宽**,
不会因为左边内容天然更宽(比如海报的图)就把右边挤窄 —— 那正是"不对齐"。
⚠️ 窄屏(≤560px)回到单列:手机上一行放两个多行文本框会窄到没法用。 */
.mv-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 20px; }
.mv-pair > .mv-row, .mv-pair > .mv-media { min-width: 0; }
/* 并排里每块自己的标签列窄一点(总宽只有一半了) */
.mv-pair > .mv-row > .mv-lb { flex: 0 0 52px; width: 52px; }
/* 并排里的媒体块:内容不再缩进 72px(本来就只占半列,再缩就没宽度了) */
.mv-pair > .mv-media > .mv-media-body { margin-left: 0; }
@media (max-width: 560px) {
.mv-pair { grid-template-columns: 1fr; }
.mv-pair > .mv-row > .mv-lb { flex: 0 0 62px; width: 62px; }
}
/* ---- 🔴 v39:撤销 v38 的"并排对" ----
用户 2026-10-10:「简介和备注不要放在一行,也就是说一个字段它就占一行。
所以简介后面是一行,备注后面是一行。」—— 简介/备注各占一整行。
⇒ `.mv-pair` 那套(grid 两列)整个删掉:它是 v38 为了压高度加的,
但半宽的多行文本框反而每行都换行、更难读。海报/观影照片也不再并排
(它们本来就只有一块,当时的"并排网格"里只有一个孩子,
grid 让它只占半宽 —— 那正是"海报的上传框比简介窄"的原因)。
⚠️ 不留悬念:全站搜 `.mv-pair` 应该**零命中**(模板与 CSS 都清干净)。 */