v6:名册联系信息对齐 vCard + 头像与裁剪四项修正

- ① 头像上传失败(超限/非图片)不再挡保存,改为 flash 提示「已加进名册。头像没有保存:…」
  ——这是「添加不了」的真根因(错误被塞进 errors.avatar 把整条新增打回)
- ② 表单顺序:生日整块下移(身份证能读出生日时无需常改)
- ③ 裁剪缩放语义反转:框在屏幕上的位置与大小不动、动它底下的图;
  落库归一化矩形随倍率缩小(放大=同一块区域看得更清楚)
- ④ 取消应用层 5MB 兆数上限;按像素动态上调 memory_limit(px*6+64M,封顶 768M);
  头像输出恒为 512x512
- ⑤ 成员信息对齐 vCard:person_contacts 表(phone/email/address/url 可多行,
  value 加密落库)+ persons.org(工作单位)/title(职务职称);均可留空

Schema v5→v6:person_contacts 建表 + persons.org/title 加列
This commit is contained in:
2026-09-30 15:02:27 +08:00
parent ed38a40c08
commit f7609a3ed9
9 changed files with 432 additions and 120 deletions
+27 -19
View File
@@ -168,28 +168,19 @@
boxEl.style.height = h + 'px';
}
/** 当前框的归一化矩形(相对**显示出来的图片**,与倍率无关) */
function normOf() {
if (!st || !st.dispW || !st.dispH) { return { x: 0, y: 0, w: 1, h: 1 }; }
return {
x: boxEl.offsetLeft / st.dispW,
y: boxEl.offsetTop / st.dispH,
w: boxEl.offsetWidth / st.dispW,
h: boxEl.offsetHeight / st.dispH,
};
}
/**
* 改缩放。
*
* 关键:**框住的归一化区域保持不变**,只是把像素坐标整体重算 ——
* 于是"放大"只让你看得更清楚、能框得更细,绝不会把已经选好的区域改掉。
* 放大后把框滚回视野中间,不然用户会以为框不见了(框在滚动区外面)。
* 关键:**框在屏幕上的位置和大小都不动**,动的是它底下那张图 ——
* 放大=同一块区域看得更清楚;缩小=框进更多内容。
* (2026-09-30 用户反馈:之前的做法是"归一化区域不变",结果框跟着图一起变大,
* 缩放几下框就大得找不到了。)
* 落库的归一化矩形由 place() 夹取后自动重算 —— 缩放改变选中的区域,这是本意。
*/
function setZoom(z) {
if (!st) { return; }
z = Math.max(1, Math.min(MAX_ZOOM, z));
var before = normOf();
var b = { x: boxEl.offsetLeft, y: boxEl.offsetTop, w: boxEl.offsetWidth, h: boxEl.offsetHeight };
st.zoom = z;
st.dispW = Math.max(1, Math.round(st.baseW * z));
st.dispH = Math.max(1, Math.round(st.baseH * z));
@@ -205,10 +196,8 @@
var valEl = overlay.querySelector('.crop-zoom-val');
if (valEl) { valEl.textContent = z.toFixed(1) + '×'; }
place(before.x * st.dispW, before.y * st.dispH,
before.w * st.dispW, before.h * st.dispH);
stage.scrollLeft = Math.max(0, boxEl.offsetLeft - (st.baseW - boxEl.offsetWidth) / 2);
stage.scrollTop = Math.max(0, boxEl.offsetTop - (st.baseH - boxEl.offsetHeight) / 2);
// 框保持屏显大小;位置也保持(place 只做越界夹取,缩小到 1× 时兜底用)
place(b.x, b.y, b.w, b.h);
}
function open(input, file) {
@@ -417,6 +406,25 @@
calc();
})();
// ===== 补充联系方式:「再加一个」克隆出下一行 =====
// 平行数组命名(ct_<kind>_label[] / ct_<kind>_value[])→ 克隆行就是合法的追加,
// 服务端按下标 zip,不需要任何计数器字段。
(function () {
document.addEventListener('click', function (e) {
var btn = e.target.closest ? e.target.closest('.ct-add') : null;
if (!btn) { return; }
var block = btn.closest('.ct-block');
if (!block) { return; }
var rows = block.querySelectorAll('[data-ct-row]');
if (!rows.length) { return; }
var clone = rows[rows.length - 1].cloneNode(true);
Array.prototype.forEach.call(clone.querySelectorAll('input'), function (i) { i.value = ''; });
rows[rows.length - 1].parentNode.insertBefore(clone, btn);
var first = clone.querySelector('input');
if (first) { first.focus(); }
});
})();
// ===== 名册:拖动卡片改顺序 / 拖到别的一栏改分组 =====
//
// 落库的是**整版布局**(每栏的有序 id 列表),一次提交写完 ——