v35 第 35 批:日期控件改用原生(Semi 时间面板不可用)+ 观影附件上传真修好 + 票根/合影归并成「观影照片」
用户原话(三段):
①「选择日期那个地方给,非常不好用。如果我不选日期,只选时间,框里面仍然是空白的,
也没有确定按钮,这个需要重新调整一下。」
②「现在上传附件,也就是在观影模块里面上传附件,怎么还是有问题?上传的附件现在
都没有保存,有时候我上传两个附件,它只保存一张。」
③「除了海报之外,票根、现场照片等等都不分类别,都只分一个类,放在一块。…
只有海报是单独的,因为它是从豆瓣里面下载回来的。其他的都放在一个模块就行,
然后所有照片都上传到一起,直接写"观影照片"就可以了。」
① 日期控件:**Semi 的时间面板根本不可用**(这条最费时间)
实测(独立最小复现页 + 真机):Semi 的 dateTimeRange 面板**只给日期网格**,
时间藏在 `.semi-datepicker-switch-time` 那个小时钟按钮后面;**点了它切到时间面板,
面板仍然是空的**(timeLists=0、无选项、无 footer、无"确定")→ 根本无法"只选时间"。
单点 dateTime 同样。⇒ 与用户描述完全一致。
修:弃用 Semi 的时间选择,改用**原生** date + time(step=1)
(24 小时制、精确到秒、点了就选、不选日期也有值、**不需要**确认按钮)。
原来的 时/分/秒 三个下拉**仍留在 DOM**(POST 契约 *_date/_hour/_minute/_second
一字不改),只是加 .dt-merged 视觉收起。
app.js 的 upgradeRange()/upgrade() 都不再挂 Semi;CSS 加 .is-native-range 系列
把 Semi 时代的"藏原生 / 两端让位"规则覆盖回来。
② 观影附件上传丢文件:**两条路径都断了**
· 暂存路径(拖放):上传框走"拖进来即刻上传到暂存区",令牌进 stage_token[];
而 movies/create|update 与 movies/photo_add **只读 $_FILES['photo']**
⇒ stage_token[] **从来没人认领** ⇒ 拖进去的一个都没存。
· Movie::update() **完全不写 photos**(那句"photos 由单独的删除路由维护"的注释下
根本没写),而路由明明算好了 $d['photos'] 传进来 ⇒ 编辑保存丢照片。
修:路由新增 stage_token[] 认领循环(与 $_FILES 路径并存);photo_add 加兜底;
Movie::update 补上 savePhotos。
③ 归并:表单去掉独立"票根"上传框,"现场合影"并入**一个**「观影照片」块;
详情页「影像记录」→「观影照片」、去掉票根/照片分类标签。
数据层本来就是平的(event_photos 只有 key/position);
⚠️ 旧数据 movie_details.ticket_key **仍要显示**(历史票根不能丢)+ 服务端仍接收
$_FILES['ticket'](向后兼容,直接 POST 仍可存)。
验证(全部真机跑,不是"看代码觉得对"):
· _fb_b34_dtrange.py 33/0(四个表单页:组可见 + 两端日期框可见 + 两端时间框
可见且 step=1 + 三个原生下拉仍在 DOM + 只选时间也能写回 *_hour/_minute/_second);
· _fb_b35_movie_media.py 14/0(点击上传 2 张→库里 2 行;真 HTTP 打 upload_stage.php
拿 stoken→随表单提交→落库;编辑补传→3 行;详情页补传→4 行;归并断言);
· 反向验证:_fb_b34_dtrange_falsify 4/0、_fb_b35_media_falsify 8/0;
· e2e 1503/0(两条断言随 v35 语义更新)、几何交互 92/0、各批真机/反向全绿、
部署脚本静态断言空跑通过;线上 app.js / form.php 的 SHA256 与本地一致。
顺带修掉的工具链脆弱(本轮暴露):
· push.sh 开头加"确保本地测试库在跑"的守卫(长时间跑流水线时 mysqld 会被环境
回收 → e2e/真机探针全崩,看着像代码坏了);
· 探针别用固定字符窗口找函数体(注释一长就假红)、断言别写死字段名前缀
(改期表单是 rev_ends_at_*)、set_input_files 的 id 要照 _stage_drop 的净化规则
(photo[] → sd-photo)、提交按钮要精确定位(编辑页有多个 submit)。
This commit is contained in:
@@ -2749,22 +2749,58 @@ switch ($r) {
|
||||
];
|
||||
}
|
||||
}
|
||||
/* 🔴🔴 v35:**拖动/点击上传的照片走的是暂存区**(`stage_token[]`),
|
||||
而这里原来**只读 `$_FILES['photo']`** —— 暂存令牌**从来没人认领**,
|
||||
于是"上传的照片全没了"。用户报的「上传的附件都没有保存」的真根因。
|
||||
⚠️ 为什么照片与海报/票根不一样:海报/票根各有独立字段(`stage_poster` /
|
||||
`stage_ticket`,见下面那段),照片是**多个**,所以走通用那组
|
||||
`stage_token[]`(`_stage_drop.php` 里 `$sdField` 的规则)。
|
||||
海报/票根那两段只认自己的字段,通用那组**没有落点**。
|
||||
⇒ 这里把 `stage_token[]` 逐个读出来、按 'photo' 槽位重跑 Upload::image。 */
|
||||
$stagedPhotoTokens = (array)($_POST['stage_token'] ?? []);
|
||||
$photoKeys = $isEdit
|
||||
? array_map('strval', array_column(Movie::photos($id), 'key')) // 保住已有的
|
||||
: [];
|
||||
$photoTouched = false;
|
||||
foreach ($stagedPhotoTokens as $stoken) {
|
||||
$stoken = trim((string)$stoken);
|
||||
if ($stoken === '') {
|
||||
continue;
|
||||
}
|
||||
$sf = \App\StagedUpload::read($familyId, (int)Auth::personId(), $stoken);
|
||||
if ($sf === null) {
|
||||
$warns[] = '有一张照片:暂存文件已失效,请重传一次。';
|
||||
continue;
|
||||
}
|
||||
$fake = [
|
||||
'name' => (string)$sf['name'],
|
||||
'type' => (string)$sf['mime'],
|
||||
'tmp_name' => (string)$sf['path'],
|
||||
'error' => UPLOAD_ERR_OK,
|
||||
'size' => (int)@filesize((string)$sf['path']),
|
||||
];
|
||||
$up = Upload::image($familyId, $fake, 'photo');
|
||||
if ($up['ok']) {
|
||||
$photoKeys[] = (string)$up['file'];
|
||||
$photoTouched = true;
|
||||
\App\StagedUpload::forget($stoken);
|
||||
} elseif ((string)($up['error'] ?? '') !== '') {
|
||||
$warns[] = '有一张照片没有保存:' . $up['error'];
|
||||
}
|
||||
}
|
||||
if ($photoFiles) {
|
||||
$photoKeys = $isEdit
|
||||
? array_map('strval', array_column(Movie::photos($id), 'key')) // 保住已有的
|
||||
: [];
|
||||
foreach ($photoFiles as $f) {
|
||||
$up = Upload::image($familyId, $f, 'photo');
|
||||
if ($up['ok']) {
|
||||
$photoKeys[] = (string)$up['file'];
|
||||
$photoTouched = true;
|
||||
} elseif ((string)($up['error'] ?? '') !== '') {
|
||||
$warns[] = '有一张照片没有保存:' . $up['error'];
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($photoTouched || !$isEdit) {
|
||||
$d['photos'] = $photoKeys;
|
||||
} elseif (!$isEdit) {
|
||||
// 新增且一张都没选 → 明确给空(否则 create 会拿到一个不存在的键)
|
||||
$d['photos'] = [];
|
||||
}
|
||||
|
||||
/* ---- v24 第 20 批 第 4 条:拖动**即时上传**的海报 / 票根 ----
|
||||
@@ -2855,7 +2891,28 @@ switch ($r) {
|
||||
flash('err', '找不到这条观影记录。');
|
||||
redirect('movies');
|
||||
}
|
||||
$staged = (array)($_POST['stage_token'] ?? []);
|
||||
$up = Upload::image($familyId, $_FILES['photo'] ?? [], 'photo');
|
||||
if (!$up['ok'] && $staged) {
|
||||
// 🔴 v35:详情页"补一张照片"的框也是**暂存式**(_stage_drop 默认走暂存),
|
||||
// 所以这里原来只读 $_FILES['photo'] 时**永远拿不到文件**(用户报的
|
||||
// "上传的附件都没有保存")。暂存令牌 → 重跑 Upload::image。
|
||||
$stoken = trim((string)($staged[0] ?? ''));
|
||||
$sf = $stoken !== ''
|
||||
? \App\StagedUpload::read($familyId, (int)Auth::personId(), $stoken) : null;
|
||||
if ($sf !== null) {
|
||||
$up = Upload::image($familyId, [
|
||||
'name' => (string)$sf['name'],
|
||||
'type' => (string)$sf['mime'],
|
||||
'tmp_name' => (string)$sf['path'],
|
||||
'error' => UPLOAD_ERR_OK,
|
||||
'size' => (int)@filesize((string)$sf['path']),
|
||||
], 'photo');
|
||||
if ($up['ok']) {
|
||||
\App\StagedUpload::forget($stoken);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($up['ok']) {
|
||||
// savePhotos 是整组替换 —— 现有键取出来再追加一张,位置接着排
|
||||
$keys = array_map('strval', array_column(Movie::photos($id), 'key'));
|
||||
|
||||
Reference in New Issue
Block a user