From 2b57bcbbbb0fed6af2229ee313b92fb2b0aaebf6 Mon Sep 17 00:00:00 2001 From: mole404 <248403041+mole404@users.noreply.github.com> Date: Fri, 2 Oct 2026 02:14:21 +0800 Subject: [PATCH] =?UTF-8?q?fix(images):=20=E6=8C=89=E8=80=81=E5=B8=88?= =?UTF-8?q?=E8=A6=81=E6=B1=82=E5=9B=9E=E6=BB=9A=E3=80=8C=E7=82=B9=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E4=B8=8D=E5=8B=BE=E9=80=89=E3=80=8D=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E5=8E=BB=E6=8E=89=E6=A0=87=E7=AD=BE=E8=A1=8C=E5=86=85=E7=9A=84?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=AD=97=E6=A0=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 回滚第 3 条:删掉整套「单击图片不改变勾选」逻辑(捕获阶段记按下前选中 + 松手还原), 小圆勾的 click 恢复全平台生效 —— 点图片本身照旧参与勾选(老师决定)。 保留本轮的另一半修复:Zoom 坐标修正(html zoom:1.1 与 DragSelect 的冲突)与 图片/卡片原生拖拽禁用(这两条是「框选起点偏右、松手不释放」的真因)。 2. 标签管理每一行不再显示任何状态字样(已有的「已有 / 移除 / 部分有」全删,DOM 里也没了), 状态只看左边那个勾:实心勾=有、方框=无、减号=部分有、红色方框=待移除。 验收:jsdom 全仓 396(标签那份 109/109);真机 51/51 全绿(含手机 390 一段)。 --- Dockerfile | 4 +- patches/ios-longpress.patch | 2 +- src/resources/views/user/images.blade.php | 65 ++--------------------- test/tag-feature.test.mjs | 24 ++++----- 4 files changed, 17 insertions(+), 78 deletions(-) diff --git a/Dockerfile b/Dockerfile index 986b2b1..4afbe9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -165,7 +165,7 @@ RUN php -r "file_exists('.env') || copy('.env.example', '.env');" \ RUN printf '%s\n' \ 'c0e513ec8e93fd81b34b3c6de5cf5eb8 ./public/js/context-js/context-js.js' \ 'c0e513ec8e93fd81b34b3c6de5cf5eb8 ./resources/js/context-js.js' \ - '402ff29758a50f5ed992434f434371a2 ./resources/views/user/images.blade.php' \ + '2510df3a1e428177d9ab7affbb6a5489 ./resources/views/user/images.blade.php' \ | md5sum -c - \ && grep -q "assetVersion('js/context-js/context-js.js')" ./resources/views/user/images.blade.php \ && grep -q 'isIOSWebKit' ./public/js/context-js/context-js.js \ @@ -208,7 +208,7 @@ RUN APP_SRC_MD5=$(find app config routes -type f -print0 2>/dev/null | sort -z | "fork_sha=${FORK_SHA}" \ "lsky_commit=${LSKY_COMMIT}" \ "context_js_md5=c0e513ec8e93fd81b34b3c6de5cf5eb8" \ - "images_blade_md5=402ff29758a50f5ed992434f434371a2" \ + "images_blade_md5=2510df3a1e428177d9ab7affbb6a5489" \ "app_src_md5=${APP_SRC_MD5}" \ "app_js_md5=${APP_JS_MD5}" \ "app_css_md5=${APP_CSS_MD5}" \ diff --git a/patches/ios-longpress.patch b/patches/ios-longpress.patch index 7e32300..54b7bfd 100644 --- a/patches/ios-longpress.patch +++ b/patches/ios-longpress.patch @@ -52,7 +52,7 @@ # 三个文件各自的 md5(与 Dockerfile「自证 2」里的期望值一致): # c0e513ec8e93fd81b34b3c6de5cf5eb8 src/resources/js/context-js.js # c0e513ec8e93fd81b34b3c6de5cf5eb8 src/public/js/context-js/context-js.js -# 402ff29758a50f5ed992434f434371a2 src/resources/views/user/images.blade.php +# 2510df3a1e428177d9ab7affbb6a5489 src/resources/views/user/images.blade.php # # 上游原文件 md5(改动前,供比对): # c8e57f6232848ca8277341ddf1a3a7a6 resources/js/context-js.js diff --git a/src/resources/views/user/images.blade.php b/src/resources/views/user/images.blade.php index 7c4b3c6..7d42410 100644 --- a/src/resources/views/user/images.blade.php +++ b/src/resources/views/user/images.blade.php @@ -433,7 +433,6 @@
__name__
__images_count__ 张
-
@@ -934,23 +933,16 @@ classes: ['dragselect'], let pendingRemove = removeIds.indexOf(id) !== -1; let partial = ! checked && ! pendingRemove && someHave(id) && ! allHave(id); + // 行内只留左边那个勾表达状态(老师要求:不要「已有 / 移除」这类字样,太误导) let icon = 'fa-square text-ink-3'; - let label = ''; - let labelCls = 'text-ink-3'; if (checked) { icon = 'fa-check-square text-brand'; - label = hasSelection ? '已有' : ''; - labelCls = pendingAdd ? 'text-brand font-medium' : 'text-ink-3'; } else if (pendingRemove) { icon = 'fa-square text-danger'; - label = '移除'; - labelCls = 'text-danger font-medium'; } else if (partial) { icon = 'fa-minus-square text-brand'; - label = '部分有'; } $row.find('.tag-state-icon').attr('class', 'tag-state-icon fas w-4 shrink-0 ' + icon); - $row.find('.tag-state-label').attr('class', 'tag-state-label shrink-0 text-[13px] ' + labelCls).text(label); if (pendingAdd) { $row.addClass('border-brand bg-brand-soft'); } else if (pendingRemove) { @@ -1273,53 +1265,6 @@ classes: ['dragselect'], }); // 单击(没拖动)不改变勾选 —— 见上面 ② - let pressSel = null; - let pressFrom = null; - let pressMoved = false; - let pressCircle = null; - // 用捕获阶段的原生监听:DragSelect 会给每张可选卡片挂 click/mousedown 并在自己的处理里 - // 停传播,挂在冒泡阶段(jQuery 的 $(document).on)会被它拦掉,实测那些处理根本收不到。 - document.addEventListener('mousedown', e => { - if (! $(e.target).closest(IMAGES_GRID).length) { - return; - } - pressSel = ds.getSelection().slice(); - pressFrom = {x: e.clientX, y: e.clientY}; - pressMoved = false; - // 只有按在右上角小圆勾上才算「要勾选」 - pressCircle = $(e.target).closest('.image-selector').length ? e.target : null; - }, true); - document.addEventListener('mousemove', e => { - if (pressFrom && (Math.abs(e.clientX - pressFrom.x) > 3 || Math.abs(e.clientY - pressFrom.y) > 3)) { - pressMoved = true; - } - }, true); - document.addEventListener('mouseup', () => { - const sel = pressSel; - const circle = pressCircle; - const moved = pressMoved; - pressSel = null; - pressFrom = null; - pressMoved = false; - pressCircle = null; - // 手机上 DragSelect 是停掉的、这条逻辑不参与(勾选仍走 .image-selector 的 click) - if (sel === null || moved || utils.isMobile()) { - return; - } - // 目标选中集合一次算好(幂等,可以反复盖): - // 按下前有什么就还原成什么;如果按的是小圆勾,再把这张图加/去掉。 - const card = circle ? $(circle).closest(IMAGES_ITEM).get(0) : null; - const want = () => (! card ? sel - : (sel.indexOf(card) === -1 ? sel.concat([card]) : sel.filter(item => item !== card))); - const apply = () => { - ds.setSelection(want()); // 单击 → 还原按下前的选中(点图片不会顺手勾上) - bindOperates(); - }; - // 库在 mouseup、click、以及下一帧 rAF 里都可能「点哪个选哪个」,连盖两拍才稳 - setTimeout(apply, 0); - requestAnimationFrame(() => setTimeout(apply, 0)); - }, true); - // 兜底:万一库没收到 mouseup(拖到窗口外等),松手也要解锁,别让界面卡在拖动状态 document.addEventListener('mouseup', () => { if (ds.Interaction && ds.Interaction.isInteracting) { @@ -1393,13 +1338,9 @@ classes: ['dragselect'], }, 320); }); - // 手机上 DragSelect 被 ds.stop() 停了、Interaction:end 不会再来,所以这条 click 路径留给手机; - // 桌面走上面那套(单击不改变勾选、只有小圆勾才切换),两条路径同时生效会对同一张图拨两次。 $photos.on('click', '.image-selector', function () { - if (utils.isMobile()) { - ds.toggleSelection($(this).closest('a')); - bindOperates(); - } + ds.toggleSelection($(this).closest('a')); + bindOperates(); }) /* ---------------- 卡片角标开关(显示图片标签) ---------------- */ diff --git a/test/tag-feature.test.mjs b/test/tag-feature.test.mjs index 56ad2df..b17a6b2 100644 --- a/test/tag-feature.test.mjs +++ b/test/tag-feature.test.mjs @@ -162,8 +162,8 @@ console.log('\n[A. 静态] 标签落点:详情行 / 卡片角标 / 批量打 ! blade.includes('data-state="none"') && ! blade.includes('fa-circle-plus') && ! blade.includes('fa-circle-minus') && blade.includes('const isChecked = (id)') && blade.includes('勾上 = 给这些图片加上') - && blade.includes("label = hasSelection ? '已有' : ''") && blade.includes("label = '移除'") - && ! blade.includes("'添加'")); + && ! blade.includes('tag-state-label') + && ! blade.includes("'已有'") && ! blade.includes("'移除'") && ! blade.includes("'部分有'")); check('「确定」默认禁用、且没选中图片时整个不显示', /id="image-tags-confirm"[^>]*disabled/.test(tpl('image-tags-tpl')) && /\$confirm\.prop\('disabled', true\)\.toggle\(hasSelection\)/.test(blade)); check('批量打标请求形状:ids + tags + remove_tags(同一 route user.images.tags,id 仍是数字)', @@ -233,12 +233,9 @@ console.log('\n[A. 静态] 「显示图片标签」开关 + 框选修复'); && blade.includes("attributeFilter: ['style']") && blade.includes('ds.SelectorArea._rect = undefined')); check('禁掉图片原生拖拽(-webkit-user-drag + dragstart 兜底)—— 否则拖动会「锁定不释放」', blade.includes('-webkit-user-drag: none') && /\$photos\.on\('dragstart'/.test(blade)); - check('单击图片不改勾选:捕获阶段记下按下前的选中,松手没位移就还原(只有小圆勾才切换)', - /pressSel = ds\.getSelection\(\)\.slice\(\)/.test(blade) - && blade.includes("document.addEventListener('mousedown'") - && blade.includes("document.addEventListener('mouseup'") - && /ds\.setSelection\(want\(\)\)/.test(blade) - && blade.includes("closest('.image-selector')")); + check('(已按老师要求回滚)点图片本身照旧参与勾选:不做任何「单击回滚」,小圆勾 click 全平台生效', + ! blade.includes('pressCircle') && ! blade.includes('pressMoved') + && /\$photos\.on\('click', '\.image-selector', function \(\) \{\s*ds\.toggleSelection/.test(blade)); check('标签管理窗口版式:新建行在列表之前、条目行 overflow-hidden、副标题只留已选数量', blade.indexOf('id="image-tags-new"') < blade.indexOf('id="image-tags-list"') && /class="image-tag-row[^"]*overflow-hidden/.test(blade) @@ -530,16 +527,16 @@ console.log('\n[B. 行为] 标签管理:给选中的图片加 / 移除标签 const rows = () => $('#image-tags-list .image-tag-row'); check('两行标签,每行都有改名 / 删除按钮', rows().length === 2 && rows().eq(0).find('.update').length === 1 && rows().eq(0).find('.delete').length === 1); - check('两张图都有的标签默认就是勾上的(fa-check-square + 「已有」)', + check('两张图都有的标签默认就是勾上的(fa-check-square),行内不带任何字样', rows().eq(0).find('.tag-state-icon').attr('class').includes('fa-check-square') - && rows().eq(0).text().includes('已有')); + && ! rows().eq(0).text().includes('已有')); check('没动过任何东西时「确定」是禁用的', $('#image-tags-confirm').prop('disabled') === true); // 取消勾选「风景」(两张图上都有)→ 变成「移除」 rows().eq(0).find('.image-tag-toggle').trigger('click'); check('取消勾选 → 未勾选 + 文案「移除」+ 行描红(危险色)', rows().eq(0).find('.tag-state-icon').attr('class').includes('fa-square') - && rows().eq(0).text().includes('移除') + && ! rows().eq(0).text().includes('移除') && rows().eq(0).find('.tag-state-label').length === 0 && rows().eq(0).attr('class').includes('border-danger')); check('「确定」变成可用', $('#image-tags-confirm').prop('disabled') === false); @@ -577,8 +574,9 @@ console.log('\n[B. 行为] 「部分选中的图片有这个标签」→ 半勾 t.methods.tag(); const row = () => $('#image-tags-list .image-tag-row[data-id="11"]'); - check('半勾态:fa-minus-square + 文案「部分有」', - row().find('.tag-state-icon').attr('class').includes('fa-minus-square') && row().text().includes('部分有')); + check('半勾态:只看图标(fa-minus-square),行内没有任何字样', + row().find('.tag-state-icon').attr('class').includes('fa-minus-square') + && ! row().text().includes('部分有')); row().find('.image-tag-toggle').trigger('click'); check('点一下 → 全勾(打了勾),确定可用', row().find('.tag-state-icon').attr('class').includes('fa-check-square')