diff --git a/assets/style.css b/assets/style.css
index 3b8deec..ac9258e 100644
--- a/assets/style.css
+++ b/assets/style.css
@@ -817,6 +817,15 @@ input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; padding
}
.photo-del:hover { background: var(--danger); }
+/* 批 11:表单缩略图旁的「✂ 裁剪」按钮 —— 上传后不再自动弹裁剪框,点它才弹 */
+.photo-card > .photo-crop-btn {
+ position: absolute; top: 6px; left: 6px;
+ border: 0; border-radius: 6px; padding: 3px 9px;
+ font-size: 12px; line-height: 1.4; cursor: pointer;
+ background: rgba(20, 24, 34, .62); color: #fff;
+}
+.photo-card > .photo-crop-btn:hover { background: rgba(20, 24, 34, .85); }
+
/* 详情页上的小照片墙 */
.photo-more {
display: flex; flex-direction: column; align-items: center; justify-content: center;
diff --git a/templates/activities/show.php b/templates/activities/show.php
index d400c4d..68d2559 100644
--- a/templates/activities/show.php
+++ b/templates/activities/show.php
@@ -30,6 +30,165 @@
+ (int)$rn['id'],
+ 'title' => (string)($rn['title'] ?: '(无标题节点)'),
+ 'icon' => (string)($rn['node_type_icon'] ?: '📍'),
+ 'img' => $imgId > 0 ? media_att($imgId) : '',
+ 'url' => url('nodes/show', ['id' => (int)$rn['id']]),
+ 'sname' => $sname, // 出发地名:没有坐标时交给高德地理编码兜底
+ 'ename' => $ename, // 到达地名:同上
+ 'start' => $hasS ? ['lng' => (float)$s['lng'], 'lat' => (float)$s['lat']] : null,
+ 'end' => $hasE ? ['lng' => (float)$e['lng'], 'lat' => (float)$e['lat']] : null,
+ ];
+}
+?>
+
+
+ 🗺️ 行程路线
+
+
+ 按时间顺序连出的执行节点路线(节点间虚线衔接),圆标在每段行程的中间,点它可打开对应节点。
+ 红点 = 到达地。只有地名没坐标的地点会自动向高德查询定位,太偏僻的地名可能查不到。
+
+
+
+
+
📋 计划 (= count($planNodes) ?>)
@@ -54,129 +213,6 @@
- (int)$rn['id'],
- 'title' => (string)($rn['title'] ?: '(无标题节点)'),
- 'icon' => (string)($rn['node_type_icon'] ?: '📍'),
- 'img' => $imgId > 0 ? media_att($imgId) : '',
- 'url' => url('nodes/show', ['id' => (int)$rn['id']]),
- 'start' => $hasS ? ['lng' => (float)$s['lng'], 'lat' => (float)$s['lat']] : null,
- 'end' => $hasE ? ['lng' => (float)$e['lng'], 'lat' => (float)$e['lat']] : null,
- ];
-}
-?>
-
-
- 🗺️ 行程路线
-
-
- 按时间顺序连出的执行节点路线(节点间虚线衔接),点标记可打开对应节点。
- 线上圆标 = 节点出发地,红点 = 到达地。
-
-
-
-
×'
: '')
+ (it.cropped ? '已裁剪' : '')
+ + (it.croppable && modal
+ ? ''
+ : '')
+ ''
+ '' + esc(it.name) + ''
+ '' + esc(fmtSize(bytesOf(it))) + ''
@@ -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) {