fix(family): 加入家庭圈改用短编号 code,接通复制编号

- HomeInfo 补 code 字段(详情接口返回的家庭短编号,joinHome 认 code
  而非内部 homeId)
- 受邀直达:分享卡携带 code,autoJoin 凭 code 调 joinHome;未登录跳
  登录时回跳链接保留 code 参数
- 分享卡 path 附加 code;"家庭编号"行接通复制(code 优先、homeId 兜底)
This commit is contained in:
ozh 2026-10-09 15:54:18 +08:00
parent 56e990c028
commit 58561e12e0
2 changed files with 41 additions and 11 deletions

View File

@ -11,6 +11,8 @@ export interface HomeInfo {
memberCount: number memberCount: number
careMemberCount: number careMemberCount: number
deviceCount: number deviceCount: number
code?: string // 家庭编号(详情接口返回,如 '1446833'),供他人 joinHome 加入
createTime?: string
} }
// 成员列表项(2026-09-17 联调实测):role=OWNER 为群主,其余为成员 // 成员列表项(2026-09-17 联调实测):role=OWNER 为群主,其余为成员

View File

@ -47,6 +47,8 @@ export default function FamilyDetail() {
const router = useRouter() const router = useRouter()
const familyId = router.params.id ?? '' const familyId = router.params.id ?? ''
const isInviteEnter = router.params.invite === '1' const isInviteEnter = router.params.invite === '1'
// 分享卡携带的家庭短编号(joinHome 用;homeId 是内部 ID 后端 join 不认)
const inviteCode = router.params.code ?? ''
const token = useAppSelector((s) => s.auth.token) const token = useAppSelector((s) => s.auth.token)
const myUserId = useAppSelector((s) => s.auth.userId) const myUserId = useAppSelector((s) => s.auth.userId)
@ -56,6 +58,10 @@ export default function FamilyDetail() {
const [members, setMembers] = useState<HomeMember[]>([]) const [members, setMembers] = useState<HomeMember[]>([])
const [caredList, setCaredList] = useState<CareMember[]>([]) const [caredList, setCaredList] = useState<CareMember[]>([])
// 家庭短编号:joinHome 用。优先分享卡带来的 code(受邀场景),
// 直达/普通进入时从详情接口响应补取
const [homeCode, setHomeCode] = useState(inviteCode)
// 转让群主:底部入口仅群主可见,点击后弹选人 Modal // 转让群主:底部入口仅群主可见,点击后弹选人 Modal
const [transferOpen, setTransferOpen] = useState(false) const [transferOpen, setTransferOpen] = useState(false)
const [transferring, setTransferring] = useState(false) const [transferring, setTransferring] = useState(false)
@ -66,15 +72,16 @@ export default function FamilyDetail() {
// 这两个接口会报权限错,不能因此把整个页面判成「家庭不存在」) // 这两个接口会报权限错,不能因此把整个页面判成「家庭不存在」)
// 返回本次拉到的成员列表(供 autoJoin 用最新数据判断,避免 stale state) // 返回本次拉到的成员列表(供 autoJoin 用最新数据判断,避免 stale state)
const load = async (): Promise<HomeMember[] | null> => { const load = async (): Promise<HomeMember[] | null> => {
// 分享卡直达本页且未登录:先去登录,登录后回跳本页 // 分享卡直达本页且未登录:先去登录,登录后回跳本页(保留 code 参数供回跳后自动加入)
if (!token) { if (!token) {
const target = encodeURIComponent(`${ROUTES.FAMILY_DETAIL}?id=${familyId}&invite=1`) const target = encodeURIComponent(`${ROUTES.FAMILY_DETAIL}?id=${familyId}${inviteCode ? `&code=${inviteCode}` : ''}&invite=1`)
reLaunch(`${ROUTES.LOGIN}?redirect=${target}`) reLaunch(`${ROUTES.LOGIN}?redirect=${target}`)
return null return null
} }
setLoading(true) setLoading(true)
try { try {
const home = await getHomeDetail(familyId) const home = await getHomeDetail(familyId)
setHomeCode(home.code ?? '')
const [memberList, careList] = await Promise.all([ const [memberList, careList] = await Promise.all([
getHomeMemberList(familyId).catch(() => [] as HomeMember[]), getHomeMemberList(familyId).catch(() => [] as HomeMember[]),
getCareMemberList(familyId).catch(() => [] as CareMember[]) getCareMemberList(familyId).catch(() => [] as CareMember[])
@ -99,20 +106,25 @@ export default function FamilyDetail() {
!isInviteEnter || joining || myUserId == null || !isInviteEnter || joining || myUserId == null ||
!memberList || memberList.length === 0 || memberList.some((m) => m.memberId === myUserId) !memberList || memberList.length === 0 || memberList.some((m) => m.memberId === myUserId)
) return ) return
autoJoin(familyId) autoJoin()
}) })
}) })
// 受邀直达:已登录但非成员时自动加入,无需手点 // 受邀直达:已登录但非成员时自动加入,无需手点。code 用分享卡带来的短编号
const autoJoin = async (homeId: string) => { // (homeId 是内部 ID,joinHome 后端不认)
const autoJoin = async () => {
if (joining) return if (joining) return
if (!homeCode) {
showToast('分享卡缺少家庭编号,请重新获取')
return
}
setJoining(true) setJoining(true)
try { try {
await joinHome(homeId) await joinHome(homeCode)
showToast('已加入家庭') showToast('已加入家庭')
await load() await load()
} catch (e) { } catch (e) {
// 加入失败(如接口异常)也要让页面可退出:详情能拉到就展示,拉不到走「家庭不存在」分支(带返回按钮) // 加入失败(如接口异常)也要让页面可退出:详情能拉到就展示,拉不到走错误分支(带回首页按钮)
showToast(e instanceof Error ? e.message : '加入失败') showToast(e instanceof Error ? e.message : '加入失败')
if (!family) await load() if (!family) await load()
} finally { } finally {
@ -120,20 +132,32 @@ export default function FamilyDetail() {
} }
} }
// 邀请分享:接收方点分享卡直达本页,登录后自动加入 // 邀请分享:接收方点分享卡直达本页,登录后凭 code 自动加入
const inviterName = () => { const inviterName = () => {
const me = members.find((m) => m.memberId === myUserId) const me = members.find((m) => m.memberId === myUserId)
return memberName(me ?? members[0]) return memberName(me ?? members[0])
} }
useShareAppMessage(() => ({ useShareAppMessage(() => ({
title: `${inviterName()} 邀请你加入家庭圈「${family?.name ?? ''}」`, title: `${inviterName()} 邀请你加入家庭圈「${family?.name ?? ''}」`,
path: `${ROUTES.FAMILY_DETAIL}?id=${familyId}&invite=1` path: `${ROUTES.FAMILY_DETAIL}?id=${familyId}${family?.code ? `&code=${family.code}` : ''}&invite=1`
})) }))
const addCared = () => { const addCared = () => {
navigateTo(`${ROUTES.FAMILY_CONTACT_ADD}?familyId=${familyId}`) navigateTo(`${ROUTES.FAMILY_CONTACT_ADD}?familyId=${familyId}`)
} }
// 复制家庭编号:优先短编号 code(供 joinHome 用),无 code 回落 homeId
const copyHomeCode = async () => {
const code = family?.code || family?.homeId || ''
if (!code) return
try {
await Taro.setClipboardData({ data: code })
// 微信原生会自带「内容已复制」toast,无需重复提示
} catch {
showToast('复制失败,请长按手动复制')
}
}
// 我在成员列表里的角色:转让入口只对群主开放 // 我在成员列表里的角色:转让入口只对群主开放
const myMember = members.find((m) => m.memberId === myUserId) ?? null const myMember = members.find((m) => m.memberId === myUserId) ?? null
const isOwner = myMember?.role === 'OWNER' const isOwner = myMember?.role === 'OWNER'
@ -226,7 +250,6 @@ export default function FamilyDetail() {
const caredCount = caredList.length const caredCount = caredList.length
const canAdd = caredCount < MAX_CARED_PER_FAMILY const canAdd = caredCount < MAX_CARED_PER_FAMILY
const stub = (tip: string) => () => showToast(tip)
return ( return (
<View className={styles.page}> <View className={styles.page}>
@ -297,7 +320,12 @@ export default function FamilyDetail() {
<View className={styles.section}> <View className={styles.section}>
<ListRowGroup> <ListRowGroup>
<ListRow label="家庭编号" value={family.homeId} onClick={stub('复制成功(占位)')} arrow /> <ListRow
label="家庭编号"
value={family.code || family.homeId}
arrow
onClick={copyHomeCode}
/>
{/* open-type=share 的 Button 铺满整行做命中区,样式与普通 ListRow 一致 */} {/* open-type=share 的 Button 铺满整行做命中区,样式与普通 ListRow 一致 */}
<Button <Button
className={styles.shareRow} className={styles.shareRow}