diff --git a/src/app.config.ts b/src/app.config.ts
index 2e718c3..cc68536 100644
--- a/src/app.config.ts
+++ b/src/app.config.ts
@@ -12,6 +12,7 @@ export default {
'pages/family/index',
'pages/family/detail',
'pages/family/contact/add',
+ 'pages/family/contact/detail',
'pages/event/index'
],
window: {
diff --git a/src/constants/routes.ts b/src/constants/routes.ts
index d8e64b7..a31d18e 100644
--- a/src/constants/routes.ts
+++ b/src/constants/routes.ts
@@ -11,5 +11,6 @@ export const ROUTES = Object.freeze({
FAMILY: '/pages/family/index',
FAMILY_DETAIL: '/pages/family/detail',
FAMILY_CONTACT_ADD: '/pages/family/contact/add',
+ FAMILY_CONTACT_DETAIL: '/pages/family/contact/detail',
SCENE_SELECT: '/pages/scene/select'
})
diff --git a/src/pages/family/contact/add.module.scss b/src/pages/family/contact/add.module.scss
index 7502696..2616544 100644
--- a/src/pages/family/contact/add.module.scss
+++ b/src/pages/family/contact/add.module.scss
@@ -5,6 +5,13 @@
padding-bottom: 40px;
}
+.stateWrap {
+ margin-top: 80px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
.card,
.contactsCard {
margin: 24px 24px 0;
diff --git a/src/pages/family/contact/add.tsx b/src/pages/family/contact/add.tsx
index 268d247..42406fd 100644
--- a/src/pages/family/contact/add.tsx
+++ b/src/pages/family/contact/add.tsx
@@ -1,8 +1,9 @@
import { useState } from 'react'
-import { useRouter } from '@tarojs/taro'
+import { useRouter, useDidShow } from '@tarojs/taro'
import { View, Text, Input } from '@tarojs/components'
import { Navbar } from '@/components/navbar'
-import { saveCareMember, type CareContact } from '@/api/careMember'
+import { Loading } from '@/components/loading'
+import { saveCareMember, getCareMemberDetail, type CareContact } from '@/api/careMember'
import { MAX_EMERGENCY_CONTACTS, MAX_FAMILY_NAME_LEN } from '@/constants/family'
import { navigateBack } from '@/utils/navigation'
import { showToast } from '@/utils/toast'
@@ -17,16 +18,39 @@ const emptyContact = (): (CareContact & { tmpId: string }) => ({
relationShip: ''
})
+// 编辑模式预填:后端 contacts 可能 null,统一补 tmpId
+const toRows = (contacts: CareContact[] | null): (CareContact & { tmpId: string })[] =>
+ (contacts ?? []).map((c) => ({ ...c, tmpId: `tmp-${Date.now()}-${(tmpSeq += 1)}` }))
+
export default function ContactAdd() {
const router = useRouter()
const familyId = router.params.familyId ?? ''
+ const memberId = router.params.memberId ?? ''
+ const isEdit = memberId !== ''
+ const [loading, setLoading] = useState(isEdit)
const [name, setName] = useState('')
const [idNumber, setIdNumber] = useState('')
const [address, setAddress] = useState('')
const [contacts, setContacts] = useState<(CareContact & { tmpId: string })[]>([])
const [submitting, setSubmitting] = useState(false)
+ // 编辑模式进入时拉取原数据预填
+ useDidShow(() => {
+ if (!isEdit) return
+ getCareMemberDetail(memberId)
+ .then((m) => {
+ setName(m.name)
+ setIdNumber(m.idNumber)
+ setAddress(m.address)
+ setContacts(toRows(m.contacts))
+ })
+ .catch((e: unknown) => {
+ showToast(e instanceof Error ? e.message : '加载失败')
+ })
+ .finally(() => setLoading(false))
+ })
+
const patchContact = (id: string, key: keyof CareContact, value: string) => {
setContacts((prev) => prev.map((c) => (c.tmpId === id ? { ...c, [key]: value } : c)))
}
@@ -46,13 +70,14 @@ export default function ContactAdd() {
setSubmitting(true)
try {
await saveCareMember({
+ memberId: memberId || undefined,
homeId: familyId,
name: name.trim(),
idNumber: idNumber.trim(),
address: address.trim(),
contacts: result.contacts
})
- showToast('添加成功')
+ showToast(isEdit ? '修改成功' : '添加成功')
setTimeout(() => navigateBack(), 500)
} catch (error) {
showToast(error instanceof Error ? error.message : '添加失败')
@@ -61,9 +86,18 @@ export default function ContactAdd() {
}
}
+ if (loading) {
+ return (
+
+
+
+
+ )
+ }
+
return (
-
+
@@ -151,7 +185,7 @@ export default function ContactAdd() {
hoverClass={submitting ? 'none' : styles.submitPressed}
onClick={submit}
>
- {submitting ? '提交中...' : '确认添加'}
+ {submitting ? '提交中...' : isEdit ? '保存修改' : '确认添加'}
)
diff --git a/src/pages/family/contact/detail.module.scss b/src/pages/family/contact/detail.module.scss
new file mode 100644
index 0000000..ffe2de6
--- /dev/null
+++ b/src/pages/family/contact/detail.module.scss
@@ -0,0 +1,109 @@
+.page {
+ min-height: 100vh;
+ background: $color-bg;
+ padding-bottom: 40px;
+}
+
+.stateWrap {
+ margin-top: 80px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.body {
+ padding: 24px;
+}
+
+// 顶部概览:圆头像(首字)+ 姓名,对齐 FamilyCard 头部风格
+.profile {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 24px 0 32px;
+}
+
+.avatar {
+ width: 120px;
+ height: 120px;
+ border-radius: $radius-circle;
+ background: $color-primary-grad;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 20px;
+}
+
+.avatarText {
+ font-size: 48px;
+ font-weight: 600;
+ color: #ffffff;
+}
+
+.name {
+ font-size: 34px;
+ font-weight: 600;
+ color: $color-text;
+}
+
+.section {
+ margin-bottom: 24px;
+}
+
+.sectionTitle {
+ display: block;
+ font-size: 26px;
+ color: $color-text-secondary;
+ margin: 8px 8px 16px;
+}
+
+.sectionEmpty {
+ display: block;
+ padding: 32px 8px;
+ font-size: 26px;
+ color: $color-text-tertiary;
+}
+
+// 编辑/删除双按钮:编辑主色实底、删除白底红字描边
+.btnRow {
+ display: flex;
+ gap: 24px;
+ margin-top: 32px;
+}
+
+.editBtn {
+ flex: 1;
+ height: 96px;
+ background: $color-primary-grad;
+ border-radius: $radius-pill;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.editBtnText {
+ font-size: 32px;
+ font-weight: 600;
+ color: #ffffff;
+}
+
+.delBtn {
+ flex: 1;
+ height: 96px;
+ background: $color-bg-white;
+ border: 2px solid $color-danger;
+ border-radius: $radius-pill;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.delBtnText {
+ font-size: 32px;
+ font-weight: 600;
+ color: $color-danger;
+}
+
+.btnPressed {
+ opacity: 0.85;
+}
diff --git a/src/pages/family/contact/detail.tsx b/src/pages/family/contact/detail.tsx
new file mode 100644
index 0000000..d7c653c
--- /dev/null
+++ b/src/pages/family/contact/detail.tsx
@@ -0,0 +1,144 @@
+import { useState } from 'react'
+import { useRouter, useDidShow } from '@tarojs/taro'
+import { View, Text } from '@tarojs/components'
+import Taro from '@tarojs/taro'
+import { Navbar } from '@/components/navbar'
+import { Loading } from '@/components/loading'
+import { StateView } from '@/components/state-view'
+import { ListRow, ListRowGroup } from '@/components/list-row'
+import { getCareMemberDetail, deleteCareMember, type CareMember } from '@/api/careMember'
+import { ROUTES } from '@/constants/routes'
+import { navigateBack, navigateTo } from '@/utils/navigation'
+import { showToast } from '@/utils/toast'
+import styles from './detail.module.scss'
+
+export default function ContactDetail() {
+ const router = useRouter()
+ const memberId = router.params.memberId ?? ''
+ const familyId = router.params.familyId ?? ''
+
+ const [loading, setLoading] = useState(true)
+ const [member, setMember] = useState(null)
+ const [deleting, setDeleting] = useState(false)
+
+ const load = async () => {
+ setLoading(true)
+ try {
+ setMember(await getCareMemberDetail(memberId))
+ } catch {
+ setMember(null)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ // 从编辑表单返回时刷新
+ useDidShow(() => {
+ load()
+ })
+
+ const goEdit = () => {
+ navigateTo(`${ROUTES.FAMILY_CONTACT_ADD}?familyId=${familyId}&memberId=${memberId}`)
+ }
+
+ const confirmDelete = () => {
+ if (deleting) return
+ Taro.showModal({
+ title: '删除关爱对象',
+ content: `确定删除「${member?.name ?? ''}」吗?删除后不可恢复。`,
+ confirmText: '删除',
+ confirmColor: '#EE4B4B'
+ }).then(async ({ confirm }) => {
+ if (!confirm) return
+ setDeleting(true)
+ try {
+ await deleteCareMember(memberId)
+ showToast('已删除')
+ setTimeout(() => navigateBack(), 500)
+ } catch (error) {
+ showToast(error instanceof Error ? error.message : '删除失败')
+ } finally {
+ setDeleting(false)
+ }
+ })
+ }
+
+ if (loading) {
+ return (
+
+
+
+
+ )
+ }
+
+ if (!member) {
+ return (
+
+
+ navigateBack()}
+ />
+
+ )
+ }
+
+ const contacts = member.contacts ?? []
+ // 身份证号脱敏:保留前 4 后 4
+ const maskedId = member.idNumber
+ ? `${member.idNumber.slice(0, 4)}**********${member.idNumber.slice(-4)}`
+ : '--'
+
+ return (
+
+
+
+
+
+
+ {member.name.slice(0, 1)}
+
+ {member.name}
+
+
+
+
+
+
+
+
+
+
+
+ 紧急联系人({contacts.length})
+ {contacts.length === 0 ? (
+ 未填写紧急联系人
+ ) : (
+
+ {contacts.map((c, i) => (
+
+ ))}
+
+ )}
+
+
+
+
+ 编辑信息
+
+
+ {deleting ? '删除中...' : '删除'}
+
+
+
+
+ )
+}
diff --git a/src/pages/family/detail.tsx b/src/pages/family/detail.tsx
index cee8e92..3d6d125 100644
--- a/src/pages/family/detail.tsx
+++ b/src/pages/family/detail.tsx
@@ -123,7 +123,15 @@ export default function FamilyDetail() {
) : (
{caredList.map((c) => (
-
+
+ navigateTo(`${ROUTES.FAMILY_CONTACT_DETAIL}?familyId=${familyId}&memberId=${c.memberId}`)
+ }
+ />
))}
)}