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:
@@ -108,6 +108,57 @@ function read_birthday_input(array $existing = []): array
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从表单解析「补充联系方式」(vCard 对齐,v6 起)。
|
||||
*
|
||||
* 每类两个平行数组:ct_<kind>_label[] / ct_<kind>_value[] ——
|
||||
* 平行数组而不是二维键名(ct[phone][0][value]),是因为 label 与 value
|
||||
* 分列在两个 input 里,PHP 自动按数字补齐下标,zip 起来最直观。
|
||||
*
|
||||
* @return string[][][] kind => 行列表(label/value,value 保留原样,空行不丢 ——
|
||||
* 交给 Person::replaceContacts 丢弃;校验失败重画表单时还要用它回填)
|
||||
*/
|
||||
function read_contacts_input(): array
|
||||
{
|
||||
$out = [];
|
||||
foreach (['phone', 'email', 'address', 'url'] as $kind) {
|
||||
$labels = $_POST['ct_' . $kind . '_label'] ?? [];
|
||||
$values = $_POST['ct_' . $kind . '_value'] ?? [];
|
||||
if (!is_array($labels) || !is_array($values)) {
|
||||
$out[$kind] = [];
|
||||
continue;
|
||||
}
|
||||
$rows = [];
|
||||
$n = max(count($labels), count($values));
|
||||
for ($i = 0; $i < $n; $i++) {
|
||||
$rows[] = ['label' => (string)($labels[$i] ?? ''), 'value' => trim((string)($values[$i] ?? ''))];
|
||||
}
|
||||
$out[$kind] = $rows;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/** 补充联系方式的宽松校验(主手机号/主邮箱的校验在 Person::validate 里) */
|
||||
function validate_contacts(array $contacts): string
|
||||
{
|
||||
foreach ($contacts as $kind => $rows) {
|
||||
foreach ($rows as $i => $row) {
|
||||
$v = trim((string)($row['value'] ?? ''));
|
||||
if ($v === '') {
|
||||
continue;
|
||||
}
|
||||
$no = $i + 1;
|
||||
if ($kind === 'email' && !filter_var($v, FILTER_VALIDATE_EMAIL)) {
|
||||
return '第 ' . $no . ' 个补充邮箱的格式看起来不对:' . $v;
|
||||
}
|
||||
if ($kind === 'phone' && !preg_match('/^\+?[0-9\- ]{6,25}$/', $v)) {
|
||||
return '第 ' . $no . ' 个补充电话的格式看起来不对:' . $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染「个人设置」页(settings/me)。
|
||||
*
|
||||
@@ -669,6 +720,8 @@ switch ($r) {
|
||||
'id_card' => trim((string)($_POST['id_card'] ?? '')),
|
||||
'email' => trim((string)($_POST['email'] ?? '')),
|
||||
'address' => trim((string)($_POST['address'] ?? '')),
|
||||
'org' => trim((string)($_POST['org'] ?? '')),
|
||||
'title' => trim((string)($_POST['title'] ?? '')),
|
||||
'note' => trim((string)($_POST['note'] ?? '')),
|
||||
'slogan' => trim((string)($_POST['slogan'] ?? '')),
|
||||
'avatar' => '',
|
||||
@@ -679,12 +732,18 @@ switch ($r) {
|
||||
'is_placeholder' => !empty($_POST['is_placeholder']) ? 1 : 0,
|
||||
] + read_birthday_input();
|
||||
$errors = Person::validate($d);
|
||||
$contacts = read_contacts_input();
|
||||
$ctErr = validate_contacts($contacts);
|
||||
if ($ctErr !== '') {
|
||||
$errors['contacts'] = $ctErr;
|
||||
}
|
||||
|
||||
// ⚠️ 头像上传失败**不再挡住整条新增**(用户原话:「添加人员功能不能用了」——
|
||||
// 早先一张 5MB 的手机原图就把整个人挡在名册外面,看起来像功能坏了)。
|
||||
// 现在是:人先存进去,头像的事单独提示。
|
||||
$up = Upload::avatar($familyId, $_FILES['avatar'] ?? [],
|
||||
(string)($_POST['avatar_crop'] ?? ''));
|
||||
if (!$up['ok'] && ($up['error'] ?? '') !== '') {
|
||||
$errors['avatar'] = $up['error'];
|
||||
}
|
||||
$avatarWarn = (!$up['ok'] && ($up['error'] ?? '') !== '') ? (string)$up['error'] : '';
|
||||
if ($errors) {
|
||||
set_old($_POST);
|
||||
View::render('people/form', [
|
||||
@@ -695,6 +754,7 @@ switch ($r) {
|
||||
'idCardValue'=> $d['id_card'],
|
||||
'canEditAll' => true,
|
||||
'isSelf' => false,
|
||||
'contacts' => $contacts,
|
||||
], 'people');
|
||||
exit;
|
||||
}
|
||||
@@ -713,14 +773,20 @@ switch ($r) {
|
||||
'idCardValue'=> $d['id_card'],
|
||||
'canEditAll' => true,
|
||||
'isSelf' => false,
|
||||
'contacts' => $contacts,
|
||||
], 'people');
|
||||
exit;
|
||||
}
|
||||
// 我加的人,自动进**我的**名册(名册是每人一份的,v5 起)。
|
||||
// 分组先留空 —— 用户可以在名册里拖到对应那一栏,也可以在那张卡片的「调整分组」里选。
|
||||
PersonRoster::ensure($familyId, (int)Auth::personId(), $id, null, 'self');
|
||||
Person::replaceContacts($id, $contacts);
|
||||
clear_old();
|
||||
flash('ok', '已把「' . $d['name'] . '」加进名册。');
|
||||
if ($avatarWarn !== '') {
|
||||
flash('ok', '已把「' . $d['name'] . '」加进名册。头像没有保存:' . $avatarWarn);
|
||||
} else {
|
||||
flash('ok', '已把「' . $d['name'] . '」加进名册。');
|
||||
}
|
||||
redirect('people/show', ['id' => $id]);
|
||||
break;
|
||||
|
||||
@@ -743,6 +809,12 @@ switch ($r) {
|
||||
redirect('people');
|
||||
}
|
||||
clear_old();
|
||||
// 补充联系方式:kind=phone 存的是密文,编辑表单里要给本人(能写的人)看明文
|
||||
$editContacts = Person::contactsOf($id);
|
||||
foreach ($editContacts['phone'] as &$r) {
|
||||
$r['value'] = Crypto::decrypt($r['value']);
|
||||
}
|
||||
unset($r);
|
||||
View::render('people/form', [
|
||||
'title' => $isSelf ? '我的资料' : '编辑成员',
|
||||
'person' => $p,
|
||||
@@ -751,6 +823,7 @@ switch ($r) {
|
||||
'isSelf' => $isSelf,
|
||||
'phoneValue' => Crypto::decrypt((string)$p['phone_encrypted']),
|
||||
'idCardValue' => Crypto::decrypt((string)$p['id_card_encrypted']),
|
||||
'contacts' => $editContacts,
|
||||
], 'people');
|
||||
break;
|
||||
|
||||
@@ -777,6 +850,8 @@ switch ($r) {
|
||||
'id_card' => $canEditAll ? trim((string)($_POST['id_card'] ?? '')) : Crypto::decrypt((string)$p['id_card_encrypted']),
|
||||
'email' => trim((string)($_POST['email'] ?? '')),
|
||||
'address' => trim((string)($_POST['address'] ?? '')),
|
||||
'org' => trim((string)($_POST['org'] ?? '')),
|
||||
'title' => trim((string)($_POST['title'] ?? '')),
|
||||
'note' => trim((string)($_POST['note'] ?? '')),
|
||||
'slogan' => trim((string)($_POST['slogan'] ?? '')),
|
||||
'avatar' => (string)$p['avatar'],
|
||||
@@ -786,11 +861,15 @@ switch ($r) {
|
||||
: (int)($p['is_placeholder'] ?? 0),
|
||||
] + read_birthday_input($p);
|
||||
$errors = Person::validate($d);
|
||||
$contacts = read_contacts_input();
|
||||
$ctErr = validate_contacts($contacts);
|
||||
if ($ctErr !== '') {
|
||||
$errors['contacts'] = $ctErr;
|
||||
}
|
||||
// 同 people/create:头像失败不挡保存(见 create 分支里的说明)
|
||||
$up = Upload::avatar($familyId, $_FILES['avatar'] ?? [],
|
||||
(string)($_POST['avatar_crop'] ?? ''));
|
||||
if (!$up['ok'] && ($up['error'] ?? '') !== '') {
|
||||
$errors['avatar'] = $up['error'];
|
||||
}
|
||||
$avatarWarn = (!$up['ok'] && ($up['error'] ?? '') !== '') ? (string)$up['error'] : '';
|
||||
if ($errors) {
|
||||
set_old($_POST);
|
||||
View::render('people/form', [
|
||||
@@ -801,6 +880,7 @@ switch ($r) {
|
||||
'isSelf' => $isSelf,
|
||||
'phoneValue' => $d['phone'],
|
||||
'idCardValue' => $d['id_card'],
|
||||
'contacts' => $contacts,
|
||||
], 'people');
|
||||
exit;
|
||||
}
|
||||
@@ -827,16 +907,22 @@ switch ($r) {
|
||||
'isSelf' => $isSelf,
|
||||
'phoneValue' => $d['phone'],
|
||||
'idCardValue' => $d['id_card'],
|
||||
'contacts' => $contacts,
|
||||
], 'people');
|
||||
exit;
|
||||
}
|
||||
Person::replaceContacts($id, $contacts);
|
||||
if ((int)$p['id'] === (int)Auth::personId()) {
|
||||
Auth::refreshAvatar((string)$d['avatar']);
|
||||
// 姓名也可能改了 —— 不同步会话的话顶栏还是旧名字
|
||||
$_SESSION['name'] = (string)$d['name'];
|
||||
}
|
||||
clear_old();
|
||||
flash('ok', '已保存。');
|
||||
if ($avatarWarn !== '') {
|
||||
flash('ok', '已保存。头像没有更换:' . $avatarWarn);
|
||||
} else {
|
||||
flash('ok', '已保存。');
|
||||
}
|
||||
redirect('people/show', ['id' => $id]);
|
||||
break;
|
||||
|
||||
@@ -873,6 +959,8 @@ switch ($r) {
|
||||
'shown' => $id === $me ? (string)$p['name'] : (Nickname::display($me, $p) ?: (string)$p['name']),
|
||||
'birthdays' => Birthday::describe($p),
|
||||
'birthdayNext'=> Birthday::nextOccurrences($p, date('Y-m-d'), 400),
|
||||
// 补充联系方式(vCard 对齐):kind=phone 的 value 是密文,模板里用 maskPhone 展示
|
||||
'contacts' => Person::contactsOf($id),
|
||||
// 名册(每人一份):他是不是在我的名册里、可以把他的名片分享给谁
|
||||
'inMyRoster' => in_array($id, PersonRoster::visibleIds($familyId, $me), true),
|
||||
], 'people');
|
||||
|
||||
Reference in New Issue
Block a user