节点表单内支持快捷新建参与人成员(批 29)

This commit is contained in:
2026-09-29 22:34:34 +08:00
parent 3c7c3e072a
commit 2aaabce8e3
4 changed files with 128 additions and 0 deletions
+9
View File
@@ -1321,6 +1321,15 @@ video.video-thumb.video-thumb-mini {
.member-pick-avatar {
width: 22px; height: 22px; border-radius: 50%; object-fit: cover; display: block;
}
/* 快捷新建成员(批 29):参与人勾选区下的一行「名字输入 + 新建」 */
.member-quickadd {
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
margin-top: 12px; padding-top: 12px; border-top: 1px dashed var(--border);
}
.member-quickadd input[type="text"] { flex: 1 1 200px; min-width: 0; }
.member-quickadd-msg { font-size: 12.5px; }
.member-quickadd-msg.is-ok { color: var(--ok, #1a7f37); }
.member-quickadd-msg.is-err { color: var(--danger, #c62828); }
/* 联想选中后回填的详细地址(让人看见记下来的是什么) */
.loc-detail { margin-top: 6px; font-size: 12.5px; word-break: break-all; }
+1
View File
@@ -145,6 +145,7 @@ $map = [
'users/index' => [UserController::class, 'index'],
'users/create' => [UserController::class, 'create'],
'users/store' => [UserController::class, 'store'],
'users/quickCreate' => [UserController::class, 'quickCreate'],
'users/edit' => [UserController::class, 'edit'],
'users/update' => [UserController::class, 'update'],
'users/destroy' => [UserController::class, 'destroy'],
+40
View File
@@ -100,6 +100,46 @@ class UserController
redirect('users/index');
}
/**
* 节点表单内「快捷新建成员」(批 29):只填名字,建一个 guest 成员,JSON 应答。
* 手机号 / 密码 / 头像都不收 —— 需要时去「成员管理」完善。AJAX 专用,不做页面跳转。
*/
public function quickCreate(): void
{
$this->requireAdmin();
csrf_check();
header('Content-Type: application/json; charset=utf-8');
$db = Database::get();
$familyId = Auth::familyId();
$name = trim((string)($_POST['name'] ?? ''));
$fail = static function (int $code, string $error): void {
http_response_code($code);
echo json_encode(['ok' => false, 'error' => $error], JSON_UNESCAPED_UNICODE);
exit;
};
if ($name === '') {
$fail(422, '名字不能为空');
}
if (mb_strlen($name) > 50) {
$fail(422, '名字太长(最多 50 字)');
}
$chk = $db->prepare('SELECT id FROM users WHERE family_id = ? AND name = ? LIMIT 1');
$chk->execute([$familyId, $name]);
if ($chk->fetch()) {
$fail(409, '已有同名成员(直接勾选即可)');
}
$stmt = $db->prepare('INSERT INTO users (family_id, name) VALUES (?, ?)');
$stmt->execute([$familyId, $name]);
echo json_encode(['ok' => true, 'id' => (int)$db->lastInsertId(), 'name' => $name],
JSON_UNESCAPED_UNICODE);
exit;
}
public function edit(): void
{
$id = (int)($_GET['id'] ?? 0);
+78
View File
@@ -257,6 +257,18 @@ $tripField = [
</div>
<p class="form-hint">勾选后保存即生效;已选的人重新勾选不会清掉之前记的签到 / 心情。</p>
<?php endif; ?>
<?php /* 快捷新建成员(批 29):只填名字建一个 guest 成员并自动勾上,不离开表单。
想补手机号/头像/角色,去「成员管理」完善。 */ ?>
<?php if (\App\Auth::isAdmin()): ?>
<div class="member-quickadd" id="member-quickadd"
data-url="<?= e(url('users/quickCreate')) ?>" data-csrf="<?= e(csrf_token()) ?>">
<input type="text" id="new-member-name" maxlength="50"
placeholder="没找到?输入名字直接新建成员" autocomplete="off">
<button type="button" class="btn" id="new-member-add">+ 新建并勾选</button>
<span class="member-quickadd-msg" id="new-member-msg" aria-live="polite"></span>
</div>
<?php endif; ?>
</fieldset>
<?php
@@ -1223,6 +1235,72 @@ $tripField = [
restoreDraft(saved);
setStatus('已恢复上次没保存成功的内容(照片需要重新选择)。', false);
})();
// ===== 快捷新建成员(批 29):AJAX 建 guest 成员 → 插入已勾选的参与人 =====
(function initQuickAdd() {
var box = document.getElementById('member-quickadd');
if (!box) return;
var input = document.getElementById('new-member-name');
var btn = document.getElementById('new-member-add');
var msg = document.getElementById('new-member-msg');
var list = document.querySelector('#member-field .member-pick');
if (!input || !btn || !msg || !list) return;
function say(text, ok) {
msg.textContent = text;
msg.classList.toggle('is-ok', !!ok);
msg.classList.toggle('is-err', !ok);
}
function addOption(name, id) {
var label = document.createElement('label');
label.className = 'member-pick-item';
var cb = document.createElement('input');
cb.type = 'checkbox';
cb.name = 'members[]';
cb.value = String(id);
cb.checked = true;
label.appendChild(cb);
var span = document.createElement('span');
span.className = 'member-pick-name';
span.textContent = name;
label.appendChild(span);
list.appendChild(label);
}
function submit() {
var name = input.value.replace(/\s+/g, ' ').trim();
if (!name) { say('先输入名字', false); input.focus(); return; }
btn.disabled = true;
say('创建中…', true);
var fd = new FormData();
fd.append('name', name);
fd.append('_csrf', box.dataset.csrf || '');
fetch(box.dataset.url, { method: 'POST', body: fd, credentials: 'same-origin' })
.then(function (r) {
return r.json().then(function (j) { return { status: r.status, j: j }; });
})
.then(function (res) {
if (res.j && res.j.ok) {
addOption(res.j.name, res.j.id);
input.value = '';
say('已创建「' + res.j.name + '」并勾选', true);
} else {
say((res.j && res.j.error) || ('创建失败(' + res.status + ')'), false);
}
})
.catch(function () { say('网络错误,请重试', false); })
.finally(function () {
btn.disabled = false;
input.focus();
});
}
btn.addEventListener('click', submit);
input.addEventListener('keydown', function (ev) {
if (ev.key === 'Enter') { ev.preventDefault(); submit(); }
});
})();
})();
})();
</script>