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:
+32
-33
@@ -166,7 +166,7 @@ $selAttendees = array_map('intval', (array)old('attendees', $curAttendees));
|
||||
|
||||
<?php
|
||||
/**
|
||||
* v24 第 20 批 第 1 条:编辑页把**已经存过的**海报 / 票根 / 现场合影**显示出来**。
|
||||
* v24 第 20 批 第 1 条:编辑页把**已经存过的**海报 / 观影照片**显示出来**。
|
||||
*
|
||||
* 用户原话:「观影模块,编辑观影,已经保存的海报和票根、合影,都没带进来。」
|
||||
* ⚠️ 原来这里只有两句灰字提示("已有海报,不选新图就保留原图。")——
|
||||
@@ -186,9 +186,18 @@ $mPhotos = $isEdit ? (array)($m['photos'] ?? []) : [];
|
||||
$mDelForms = []; // 收集起来,在表单**外面**统一渲染
|
||||
?>
|
||||
<div class="form-grid" style="margin-top:4px">
|
||||
<?php /* ---- 海报 ---- */ ?>
|
||||
<?php /* ---- 观影照片(v35:不再分类,合并成一块)----
|
||||
用户原话:「除了海报之外,票根、现场照片等等都不分类别,都只分一个类,
|
||||
放在一块。…只有海报是单独的…其他的都放在一个模块就行,
|
||||
然后所有照片都上传到一起,直接写"观影照片"就可以了。」
|
||||
⚠️ 数据层本来就是平的(`event_photos` 只有 key/position,没有类别列),
|
||||
票根以前是存 `movie_details.ticket_key` 的**单张**槽位。
|
||||
⇒ 归并 = 把票根也当成 `event_photos` 里的一张,页面上只留"观影照片"一块。
|
||||
⚠️ **旧的 ticket_key 仍然要显示**(历史数据不能丢):编辑页把已有票根
|
||||
列进下面这块的网格里(它没有 `event_photos` 行 id,所以只能"看"、
|
||||
不能用照片的删除按钮删;要换就直接上传新的)。 */ ?>
|
||||
<div class="field">
|
||||
<label for="poster">海报</label>
|
||||
<label for="sd-poster">海报</label>
|
||||
<?php if ($mHasPoster): ?>
|
||||
<?php $mDelForms['poster'] = 'movies/media_del'; ?>
|
||||
<div class="mv-slot">
|
||||
@@ -210,42 +219,32 @@ $mDelForms = []; // 收集起来,在表单**外面**统一渲染
|
||||
require __DIR__ . '/../_stage_drop.php'; ?>
|
||||
<span class="tip"><?= $mHasPoster ? '选/拖一张新的会自动替换旧的那张。' : '还没有海报。' ?></span>
|
||||
</div>
|
||||
|
||||
<?php /* ---- 票根 ---- */ ?>
|
||||
<div class="field">
|
||||
<label for="ticket">票根</label>
|
||||
<?php if ($mHasTicket): ?>
|
||||
<div class="mv-slot">
|
||||
<a href="<?= e(media_url((string)$m['ticket_key'])) ?>" data-prev="image"
|
||||
data-src="<?= e(media_url((string)$m['ticket_key'])) ?>"
|
||||
data-dl="<?= e(media_url((string)$m['ticket_key'], ['dl' => 1])) ?>"
|
||||
data-name="票根">
|
||||
<img src="<?= e(media_url((string)$m['ticket_key'])) ?>" alt="票根">
|
||||
</a>
|
||||
<button class="btn btn-ghost btn-sm" type="submit" form="mv-del-ticket"
|
||||
onclick="return confirm('删掉现在这张票根?')">删除这张</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php $sdName = 'ticket'; $sdSlot = 'ticket'; $sdAccept = 'image/*';
|
||||
$sdMultiple = false;
|
||||
$sdMsg = $mHasTicket ? '点击或拖动进行上传(换一张票根)' : '点击或拖动进行上传';
|
||||
require __DIR__ . '/../_stage_drop.php'; ?>
|
||||
<span class="tip"><?= $mHasTicket ? '选/拖一张新的会自动替换旧的那张。' : '还没有票根。' ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php /* ---- 现场合影(用户第 1 条点名的三样之一)----
|
||||
新增时可以一次多选;**编辑时在这里逐张看、逐张删**(与新选的图合并保存)。*/ ?>
|
||||
<?php /* ---- 观影照片:票根 + 合影 + 其它相关图片,**全部放这一块** ---- */ ?>
|
||||
<div class="field full">
|
||||
<label for="photo">现场合影</label>
|
||||
<?php if ($mPhotos): ?>
|
||||
<label for="sd-photo">观影照片</label>
|
||||
<?php if ($mHasTicket || $mPhotos): ?>
|
||||
<div class="photo-grid">
|
||||
<?php if ($mHasTicket): /* 历史数据:旧票根(不在 event_photos 里,只展示) */ ?>
|
||||
<?php $mDelForms['ticket'] = 'movies/media_del'; ?>
|
||||
<figure class="photo-cell">
|
||||
<a href="<?= e(media_url((string)$m['ticket_key'])) ?>" data-prev="image"
|
||||
data-src="<?= e(media_url((string)$m['ticket_key'])) ?>"
|
||||
data-dl="<?= e(media_url((string)$m['ticket_key'], ['dl' => 1])) ?>"
|
||||
data-name="票根">
|
||||
<img src="<?= e(media_url((string)$m['ticket_key'])) ?>" alt="票根">
|
||||
</a>
|
||||
<button class="btn btn-ghost btn-sm" type="submit" form="mv-del-ticket"
|
||||
onclick="return confirm('删掉这张旧票根?')">删除</button>
|
||||
</figure>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($mPhotos as $mp): $mpId = (int)$mp['id']; ?>
|
||||
<figure class="photo-cell">
|
||||
<a href="<?= e(media_url((string)$mp['key'])) ?>" data-prev="image"
|
||||
data-src="<?= e(media_url((string)$mp['key'])) ?>"
|
||||
data-dl="<?= e(media_url((string)$mp['key'], ['dl' => 1])) ?>"
|
||||
data-name="现场照片">
|
||||
data-name="观影照片">
|
||||
<img src="<?= e(media_url((string)$mp['key'])) ?>" alt="">
|
||||
</a>
|
||||
<button class="btn btn-ghost btn-sm" type="submit" form="mv-del-photo-<?= $mpId ?>"
|
||||
@@ -254,12 +253,12 @@ $mDelForms = []; // 收集起来,在表单**外面**统一渲染
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php /* v24 第 21 批 第 7 条:统一成「点击或拖动进行上传」的框(可多次)。 */ ?>
|
||||
<?php /* 观影照片(票根/合影/其它)都从这里进(可多选、可多次拖) */ ?>
|
||||
<?php $sdName = 'photo[]'; $sdSlot = 'photo'; $sdAccept = 'image/*';
|
||||
$sdMsg = '点击或拖动进行上传';
|
||||
require __DIR__ . '/../_stage_drop.php'; ?>
|
||||
<span class="tip"><?= $isEdit ? '可以继续加(可多选);删掉的在保存后生效。'
|
||||
: '可以多选;之后也能在详情页里逐张补传。' ?></span>
|
||||
<span class="tip"><?= $isEdit ? '观影照片都可以放这里(可多选);删掉的在保存后生效。'
|
||||
: '观影照片都可以放这里(可多选);之后也能在详情页里逐张补传。' ?></span>
|
||||
</div>
|
||||
|
||||
<div class="btn-row">
|
||||
|
||||
+22
-12
@@ -2,7 +2,7 @@
|
||||
/**
|
||||
* 观影详情页(v9,展示方式照搬 MovieLog 的 detail.php)。
|
||||
*
|
||||
* 五大块:英雄区(左海报右信息)→ 一起看的人 → 电影院信息 → 影像记录(灯箱)→ 评价与评论。
|
||||
* 五大块:英雄区(左海报右信息)→ 一起看的人 → 电影院信息 → 观影照片(灯箱)→ 评价与评论。
|
||||
* 海报/票根/照片一律走 media_url() 代理;感受标签只存 key,emoji 与文案查 CATALOG。
|
||||
* 访客(只读)看不到编辑/删除/传照片/评价表单 —— 与名册同一口径。
|
||||
* 评价人是「登录账号背后的人」,与同行观影人无关(MovieLog 同款规则)。
|
||||
@@ -243,21 +243,31 @@ $commentBox = static function (int $reviewId, int $parentId, string $summary) us
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- 影像记录(票根 + 现场照片,点开灯箱) -->
|
||||
<!-- 观影照片(v35:票根 + 现场照片 + 其它相关图片,**不再分类**,合成一块) -->
|
||||
<?php if ($hasTicket || $m['photos']): ?>
|
||||
<div class="detail-section">
|
||||
<h3>影像记录 <span class="badge"><?= count($m['photos']) + ($hasTicket ? 1 : 0) ?> 张</span></h3>
|
||||
<h3>观影照片 <span class="badge"><?= count($m['photos']) + ($hasTicket ? 1 : 0) ?> 张</span></h3>
|
||||
<div class="gallery-grid">
|
||||
<?php if ($hasTicket): ?>
|
||||
<div class="gallery-item lb-openable" data-lb-index="1" role="button" tabindex="0" aria-label="放大查看票根" title="点击查看大图">
|
||||
<img src="<?= e(media_url($m['ticket_key'])) ?>" alt="票根" loading="lazy">
|
||||
<span class="label-badge">票根</span>
|
||||
<?php /* ⚠️ 旧票根(movie_details.ticket_key)是历史数据,仍要能看到;
|
||||
归并后它跟 event_photos 里的照片**同一块展示、同一套灯箱**,
|
||||
但删除要走它自己的槽位表单(media_del)。 */ ?>
|
||||
<?php if ($hasTicket): $lbIdx = 1; ?>
|
||||
<div class="gallery-item lb-openable" data-lb-index="<?= $lbIdx ?>" role="button" tabindex="0" aria-label="放大查看" title="点击查看大图">
|
||||
<img src="<?= e(media_url($m['ticket_key'])) ?>" alt="" loading="lazy">
|
||||
<?php if ($canWrite): ?>
|
||||
<form method="post" action="<?= e(url('movies/media_del')) ?>"
|
||||
onsubmit="return confirm('删除这张照片?')">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="id" value="<?= (int)$m['id'] ?>">
|
||||
<input type="hidden" name="slot" value="ticket">
|
||||
<button type="submit" class="photo-del" title="删除这张照片">×</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($m['photos'] as $i => $ph): ?>
|
||||
<div class="gallery-item lb-openable" data-lb-index="<?= $i + 2 ?>" role="button" tabindex="0" aria-label="放大查看照片" title="点击查看大图">
|
||||
<div class="gallery-item lb-openable" data-lb-index="<?= $i + ($hasTicket ? 2 : 1) ?>" role="button" tabindex="0" aria-label="放大查看照片" title="点击查看大图">
|
||||
<img src="<?= e(media_url((string)$ph['key'])) ?>" alt="" loading="lazy">
|
||||
<span class="label-badge">照片</span>
|
||||
<?php if ($canWrite): ?>
|
||||
<form method="post" action="<?= e(url('movies/photo_del')) ?>"
|
||||
onsubmit="return confirm('删除这张照片?')">
|
||||
@@ -277,7 +287,7 @@ $commentBox = static function (int $reviewId, int $parentId, string $summary) us
|
||||
<form method="post" enctype="multipart/form-data" action="<?= e(url('movies/photo_add')) ?>" class="photo-add">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="id" value="<?= (int)$m['id'] ?>">
|
||||
<label>补一张现场照片</label>
|
||||
<label>加照片</label>
|
||||
<?php /* v24 第 21 批 第 7 条:统一成「点击或拖动进行上传」的框。
|
||||
⚠️ 文件框 id 保持 `photo`(原来就叫这个),改名会让既有断言假红。 */
|
||||
$sdName = 'photo'; $sdId = 'photo'; $sdAccept = 'image/*';
|
||||
@@ -444,10 +454,10 @@ $commentBox = static function (int $reviewId, int $parentId, string $summary) us
|
||||
items.push({url: <?= json_encode(media_url((string)$m['poster_key']), JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>, cap: <?= json_encode('海报 · ' . $m['title'], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>});
|
||||
<?php endif; ?>
|
||||
<?php if ($hasTicket): ?>
|
||||
items.push({url: <?= json_encode(media_url((string)$m['ticket_key']), JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>, cap: <?= json_encode('票根 · ' . $m['title'], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>});
|
||||
items.push({url: <?= json_encode(media_url((string)$m['ticket_key']), JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>, cap: <?= json_encode('观影照片 · ' . $m['title'], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>});
|
||||
<?php endif; ?>
|
||||
<?php foreach ($m['photos'] as $ph): ?>
|
||||
items.push({url: <?= json_encode(media_url((string)$ph['key']), JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>, cap: <?= json_encode('照片 · ' . $m['title'], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>});
|
||||
items.push({url: <?= json_encode(media_url((string)$ph['key']), JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>, cap: <?= json_encode('观影照片 · ' . $m['title'], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>});
|
||||
<?php endforeach; ?>
|
||||
var box = document.getElementById('lightbox');
|
||||
var stageEl = document.getElementById('lbStage');
|
||||
|
||||
Reference in New Issue
Block a user