路线地图体验修正 + 节点表单裁剪改按钮触发(批 11)

地图四项调整(用户反馈):
- 位置移到封面之下、时间轴之前 —— 行程一长不用拉到底才看到地图;
- 圆形标记放在该段行程线的中点(起终点都有才取中点,缺一头放
  有的一头),不再是起点位置;anchor 改 center 居中骑在线上;
- 只有地名、没坐标的地点(偏僻地方下拉选不到点位):浏览器端调
  AMap.Geocoder 按地名查坐标,查到补进路线全量重画(drawRoute 幂等),
  查不到保持缺省不硬造位置;data-route 新增 sname/ename 地名;
- 提示文案同步更新。

裁剪交互(用户反馈):
- 选图后不再自动弹出裁剪框;每张能裁的缩略图旁放「✂ 裁剪」按钮
  (左上角,与右上角 × 对称),点了才弹框,只处理这一张;
- 「逐张裁剪」批量入口保留:确定后仍自动弹下一张(cropChain 标志
  区分两种流程);删除 addedCroppable 自动触发逻辑。

已验证:本地全链 11 步全绿(渲染检查 339 项、真实浏览器交互更新为
按钮触发流程 75 项);远端 e2e 340 项 0 失败(新增 9 项断言);
部署静态断言/双 PHP 渲染/内容断言全过后提交。
This commit is contained in:
2026-09-24 11:41:11 +08:00
parent 69279db1b2
commit 00cd99d07d
3 changed files with 190 additions and 135 deletions
+22 -12
View File
@@ -615,6 +615,7 @@ $tripField = [
var cropper = null;
var cropUid = null;
var cropStopped = false; // 用户点了「取消」就结束本次逐张裁剪
var cropChain = false; // true = 「逐张裁剪」的连裁流程(确定后自动下一张)
function extOf(name) {
var i = String(name || '').lastIndexOf('.');
@@ -711,6 +712,10 @@ $tripField = [
? '<button type="button" class="photo-del" data-idx="' + idx + '" title="移除">×</button>'
: '')
+ (it.cropped ? '<span class="photo-badge">已裁剪</span>' : '')
+ (it.croppable && modal
? '<button type="button" class="photo-crop-btn" data-crop="' + idx
+ '" title="裁剪这张图">✂ 裁剪</button>'
: '')
+ '<div class="photo-cap">'
+ '<span class="photo-cap-name" title="' + esc(it.name) + '">' + esc(it.name) + '</span>'
+ '<span class="photo-cap-sub">' + esc(fmtSize(bytesOf(it))) + '</span>'
@@ -757,7 +762,6 @@ $tripField = [
if (!arr.length) return;
var errors = [];
var addedCroppable = false;
arr.forEach(function (f) {
if (picked.length >= MAX_FILES) {
errors.push(f.name + ':一次最多 ' + MAX_FILES + ' 个');
@@ -779,7 +783,6 @@ $tripField = [
var image = isImageFile(f);
var url = image ? URL.createObjectURL(f) : '';
var croppable = image && canvasSafe(f);
if (croppable) addedCroppable = true;
picked.push({
uid: ++seq,
file: f,
@@ -799,11 +802,8 @@ $tripField = [
render();
if (errors.length) { setStatus(errors.join(';'), true); return; }
// 新选进来的图片里有能裁的 → 自动开始逐张裁剪;不想裁就一路点「确定」或直接「取消」
if (addedCroppable && modal && typeof window.Cropper !== 'undefined') {
cropStopped = false;
advanceCrop();
}
/* 批 11:选图后**不再自动弹出**裁剪框 —— 每张缩略图旁有「✂ 裁剪」按钮,
想裁哪张点哪张;想批量处理就用上面的「逐张裁剪」。 */
}
function removeAt(idx) {
@@ -816,7 +816,15 @@ $tripField = [
}
preview.addEventListener('click', function (ev) {
var btn = ev.target.closest ? ev.target.closest('button[data-idx]') : null;
if (!ev.target.closest) return;
/* 「✂ 裁剪」:只打开这一张的裁剪框,确定/取消都不自动跳下一张 */
var cropBtn = ev.target.closest('button[data-crop]');
if (cropBtn) {
var ci = parseInt(cropBtn.getAttribute('data-crop'), 10);
if (picked[ci]) openCrop(picked[ci].uid, false);
return;
}
var btn = ev.target.closest('button[data-idx]');
if (!btn) return;
removeAt(parseInt(btn.getAttribute('data-idx'), 10));
});
@@ -856,13 +864,13 @@ $tripField = [
if (cropStopped) return;
for (var i = 0; i < picked.length; i++) {
if (picked[i].croppable && !picked[i].reviewed) {
openCrop(picked[i].uid);
openCrop(picked[i].uid, true);
return;
}
}
}
function openCrop(uid) {
function openCrop(uid, chained) {
var it = find(uid);
if (!it || !it.croppable) return;
if (typeof window.Cropper === 'undefined') {
@@ -871,8 +879,10 @@ $tripField = [
return;
}
cropUid = uid;
cropChain = !!chained;
var pending = picked.filter(function (x) { return x.croppable && !x.reviewed; }).length;
cropTitle.textContent = '裁剪:' + it.name + (pending > 1 ? '(还有 ' + (pending - 1) + ' 张待处理)' : '');
cropTitle.textContent = '裁剪:' + it.name
+ (chained && pending > 1 ? '(还有 ' + (pending - 1) + ' 张待处理)' : '');
// 每次都换一个新的 img 节点,避免 Cropper 复用旧实例时状态残留
var fresh = document.createElement('img');
@@ -986,7 +996,7 @@ $tripField = [
it.reviewed = true;
closeCrop();
render();
advanceCrop(); // 还有没处理过的图就继续弹下一张
if (cropChain) advanceCrop(); // 只有「逐张裁剪」流程才自动弹下一张
}
if (cropAll) {