Files
iboard/src/components/task/UserPicker.jsx
T
jif2.zhang d6ca7573f3
Docker Build and Push / build-image (push) Successful in 4m9s
fix(V0.2): 三轮反馈 5 项 —— 回滚键盘方案 / 列表卡片拖动 / 看板防误触 / 横滑整屏 / 详情页恢复
1. 回滚键盘遮挡统一方案 (真机测试不通过, 需求搁置): 恢复 TaskDetail/
   UserPicker/CopyTaskPopup/ProjectManage 与 useKeyboardInset 到 9fea1a5^,
   删除 useKeyboardInsetWatcher / .kb-safe / interactive-widget=resizes-content
2. 任务详情页样式随 1 一并恢复提交前状态 (含底部拷贝/删除按钮)
3. 列表视图换列改为卡片拖动: 新 useRowDrag (长按 400ms 行抬起, 左右
   80px/列步进, 行尾徽标显示目标列, 松手落列), 移除长按 ActionSheet
   —— 旧长按与复选框/行点击互相误触, 抬手合成 click 落在控件上触发状态切换
4. 看板拖动防误触: 任务区禁长按文字选择与系统菜单 (输入框除外);
   pointercancel 只放弃不提交; 拖动结束 350ms 内抑制 click 误开详情;
   状态点/⋯/子任务行 pointerdown 不冒泡, 不发起拖动
5. 看板横滑扩展到整屏: .page-transition 补 height:100% 打通
   app-main → page-with-navbar 百分比链, board-wrap 撑满剩余高度

已验证: kb-safe/Watcher/onLongPress 残留引用清零, 逐文件 diff 审阅通过;
本机无 Node 运行时, 未跑 vite build
2026-10-24 23:24:31 +08:00

67 lines
2.3 KiB
React

import { useEffect, useState } from 'react'
import { Popup, Toast } from 'antd-mobile'
// 人员选择 (T-AS-03/04): 只能从"当前项目成员"中选
export default function UserPicker({
visible, members = [], value, multiple = false, title = '选择负责人',
allowEmpty = true, onClose, onConfirm,
}) {
const [sel, setSel] = useState(multiple ? [] : null)
useEffect(() => {
if (!visible) return
setSel(multiple ? (value || []) : (value ?? null))
}, [visible, value, multiple])
const toggle = (uid) => {
if (multiple) {
setSel((prev) => {
const arr = Array.isArray(prev) ? prev : []
return arr.includes(uid) ? arr.filter((x) => x !== uid) : [...arr, uid]
})
} else {
setSel((prev) => (prev === uid ? null : uid))
}
}
const confirm = () => {
if (!multiple && sel === null && !allowEmpty) {
Toast.show({ content: '请选择负责人' })
return
}
onConfirm?.(sel)
onClose?.()
}
return (
<Popup visible={visible} onMaskClick={onClose} bodyStyle={{ borderTopLeftRadius: 12, borderTopRightRadius: 12, padding: '16px 16px calc(env(safe-area-inset-bottom, 0px) + 20px)' }}>
<div className="sheet-title">{title}</div>
<div className="sheet-sub">仅可选择当前项目的成员</div>
{members.length === 0 && <div className="extras-empty">项目暂无成员</div>}
{members.map((m) => {
const on = multiple ? (Array.isArray(sel) && sel.includes(m.userId)) : sel === m.userId
return (
<div className="pick-row" key={m.userId} onClick={() => toggle(m.userId)}>
<div className="avatar">{(m.name || '?').slice(0, 1)}</div>
<span className="pn">{m.name}</span>
<span className={`ck${on ? ' on' : ''}`} />
</div>
)
})}
{allowEmpty && !multiple && (
<div className="pick-row" onClick={() => setSel(null)}>
<div className="avatar" style={{ background: '#f2f3f5', color: '#b6bcc4' }}>—</div>
<span className="pn">清除负责人</span>
<span className={`ck${sel === null ? ' on' : ''}`} />
</div>
)}
<div className="btn-primary" onClick={confirm}>确定</div>
<div className="btn-ghost" onClick={onClose}>取消</div>
</Popup>
)
}