SEC_Warehouse/src/pages/family/detail.tsx

216 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from 'react'
import { useRouter, useDidShow, useShareAppMessage } from '@tarojs/taro'
import Taro from '@tarojs/taro'
import { View, Text, Button } from '@tarojs/components'
import { useAppSelector } from '@/store/hooks'
import { Navbar } from '@/components/navbar'
import { Loading } from '@/components/loading'
import { StateView } from '@/components/state-view'
import { FamilyCard } from '@/components/family-card'
import { ListRow, ListRowGroup } from '@/components/list-row'
import { getHomeDetail, getHomeMemberList, joinHome, type HomeInfo, type HomeMember } from '@/api/home'
import { getCareMemberList, type CareMember } from '@/api/careMember'
import { MAX_CARED_PER_FAMILY } from '@/constants/family'
import { ROUTES } from '@/constants/routes'
import { navigateBack, navigateTo, reLaunch } from '@/utils/navigation'
import { showToast } from '@/utils/toast'
import styles from './detail.module.scss'
// 成员显示名:家庭内昵称优先,未设置回落微信昵称
const memberName = (m: HomeMember): string => m.nickName ?? m.userNickName ?? '成员'
// 关爱对象联系人摘要:最多显示前 2 位姓名,其余以 +N 概括;未填则不显示
const contactsSummary = (c: CareMember): string => {
const list = c.contacts ?? []
if (list.length === 0) return ''
const head = list.slice(0, 2).map((ct) => ct.name).join('、')
const rest = list.length > 2 ? ` 等 ${list.length} 人` : ''
return `${head}${rest}`
}
export default function FamilyDetail() {
const router = useRouter()
const familyId = router.params.id ?? ''
const isInviteEnter = router.params.invite === '1'
const token = useAppSelector((s) => s.auth.token)
const myUserId = useAppSelector((s) => s.auth.userId)
const [loading, setLoading] = useState(true)
const [joining, setJoining] = useState(false)
const [family, setFamily] = useState<HomeInfo | null>(null)
const [members, setMembers] = useState<HomeMember[]>([])
const [caredList, setCaredList] = useState<CareMember[]>([])
const load = async () => {
// 分享卡直达本页且未登录:先去登录,登录后回跳本页
if (!token) {
const target = encodeURIComponent(`${ROUTES.FAMILY_DETAIL}?id=${familyId}&invite=1`)
reLaunch(`${ROUTES.LOGIN}?redirect=${target}`)
return
}
setLoading(true)
try {
const [home, memberList, careList] = await Promise.all([
getHomeDetail(familyId),
getHomeMemberList(familyId),
getCareMemberList(familyId)
])
setFamily(home)
setMembers(memberList)
// 上限判定以关爱对象实际列表为准,家庭卡计数仅展示
setCaredList(careList)
} catch {
setFamily(null)
} finally {
setLoading(false)
}
}
// 从添加表单返回时刷新;受邀进入且尚未是成员时自动加入(跳过手动点击步骤)
useDidShow(() => {
load().then(() => {
if (
!isInviteEnter || joining || myUserId == null ||
members.length === 0 || members.some((m) => m.memberId === myUserId)
) return
autoJoin(familyId)
})
})
// 受邀直达:已登录但非成员时自动加入,无需手点
const autoJoin = async (homeId: string) => {
if (joining) return
setJoining(true)
try {
await joinHome(homeId)
showToast('已加入家庭')
await load()
} catch (e) {
showToast(e instanceof Error ? e.message : '加入失败')
} finally {
setJoining(false)
}
}
// 邀请分享:接收方点分享卡直达本页,登录后自动加入
const inviterName = () => {
const me = members.find((m) => m.memberId === myUserId)
return memberName(me ?? members[0])
}
useShareAppMessage(() => ({
title: `${inviterName()} 邀请你加入家庭圈「${family?.name ?? ''}」`,
path: `${ROUTES.FAMILY_DETAIL}?id=${familyId}&invite=1`
}))
const addCared = () => {
navigateTo(`${ROUTES.FAMILY_CONTACT_ADD}?familyId=${familyId}`)
}
if (loading) {
return (
<View className={styles.page}>
<Navbar title="关爱对象信息" variant="hero" />
<View className={styles.stateWrap}><Loading /></View>
</View>
)
}
if (!family) {
return (
<View className={styles.page}>
<Navbar title="关爱对象信息" variant="hero" />
<StateView
status="error"
mode="list"
text="该家庭不存在"
retryText="返回列表"
onRetry={() => navigateBack()}
/>
</View>
)
}
const caredCount = caredList.length
const canAdd = caredCount < MAX_CARED_PER_FAMILY
const stub = (tip: string) => () => showToast(tip)
return (
<View className={styles.page}>
<Navbar title="关爱对象信息" variant="hero" />
<View className={styles.body}>
<FamilyCard family={family} />
{/* 添加关爱对象卡:达上限后灰化静态 */}
<View
className={`${styles.addCared} ${canAdd ? '' : styles.addCaredDisabled}`}
hoverClass={canAdd ? styles.addCaredPressed : 'none'}
onClick={canAdd ? addCared : undefined}
>
<View className={`${styles.addCaredIcon} ${canAdd ? '' : styles.addCaredIconDisabled}`}>
<Text
className={`${styles.addCaredIconText} ${canAdd ? '' : styles.addCaredIconTextDisabled}`}
>
+
</Text>
</View>
<Text className={`${styles.addCaredText} ${canAdd ? '' : styles.addCaredTextDisabled}`}>
{canAdd ? `+ 最多可添加 ${MAX_CARED_PER_FAMILY} 人` : '已达上限'}
</Text>
</View>
<View className={styles.section}>
<Text className={styles.sectionTitle}>关爱对象</Text>
{caredCount === 0 ? (
<Text className={styles.sectionEmpty}>暂无关爱对象,点击上方卡片添加</Text>
) : (
<ListRowGroup>
{caredList.map((c) => (
<ListRow
key={c.memberId}
label={c.name}
value={contactsSummary(c)}
arrow
onClick={() =>
navigateTo(`${ROUTES.FAMILY_CONTACT_DETAIL}?familyId=${familyId}&memberId=${c.memberId}`)
}
/>
))}
</ListRowGroup>
)}
</View>
<View className={styles.section}>
<Text className={styles.sectionTitle}>家庭成员</Text>
{members.length === 0 ? (
<Text className={styles.sectionEmpty}>暂无其他成员</Text>
) : (
<ListRowGroup>
{members.map((m) => (
<ListRow key={m.memberId} label={memberName(m)} value={m.role === 'OWNER' ? '群主' : '成员'} />
))}
</ListRowGroup>
)}
</View>
<View className={styles.section}>
<ListRowGroup>
<ListRow label="家庭编号" value={family.homeId} onClick={stub('复制成功(占位)')} arrow />
{/* open-type=share 的 Button 铺满整行做命中区,样式与普通 ListRow 一致 */}
<Button
className={styles.shareRow}
hoverClass={styles.shareRowPressed}
openType="share"
>
<Text className={styles.shareLabel}>邀请他人</Text>
<Text className={styles.shareArrow}>›</Text>
</Button>
<ListRow label="转让群主" arrow onClick={stub('转让功能即将上线')} />
<ListRow label="修改群昵称" arrow onClick={stub('修改昵称功能即将上线')} />
</ListRowGroup>
</View>
</View>
</View>
)
}