feat(family): 添加关爱对象表单页——必填校验、紧急联系人动态增删

This commit is contained in:
ozh 2026-09-09 15:22:15 +08:00
parent a9dfb2485b
commit 9d1ea0abbc
3 changed files with 271 additions and 0 deletions

View File

@ -10,6 +10,7 @@ export default {
'pages/device/config', 'pages/device/config',
'pages/family/index', 'pages/family/index',
'pages/family/detail', 'pages/family/detail',
'pages/family/contact/add',
'pages/event/index' 'pages/event/index'
], ],
window: { window: {

View File

@ -0,0 +1,115 @@
.page {
min-height: 100vh;
background: $color-bg;
padding: 24px 24px 40px;
}
.card,
.contactsCard {
background: $color-bg-white;
border-radius: $radius-lg;
box-shadow: $shadow-card;
padding: 8px 32px;
}
.contactsCard {
margin-top: 24px;
}
.formRow {
min-height: 104px;
display: flex;
align-items: center;
border-bottom: 1px solid $color-border;
}
.contactsCard .contactItem:last-child .formRow:last-child {
border-bottom: none;
}
.card .formRow:last-child {
border-bottom: none;
}
.label {
width: 180px;
flex-shrink: 0;
font-size: 28px;
color: $color-text;
}
.required {
color: $color-danger;
margin-right: 4px;
}
.input {
flex: 1;
min-width: 0;
height: 80px;
font-size: 28px;
color: $color-text;
}
.contactsHead {
min-height: 96px;
display: flex;
align-items: center;
justify-content: space-between;
}
.contactsTitle {
font-size: 30px;
font-weight: 600;
color: $color-text;
}
.contactsAdd {
font-size: 28px;
color: $color-primary-deep;
}
.contactsTip {
display: block;
padding-bottom: 16px;
font-size: 24px;
color: $color-text-tertiary;
}
.contactItem {
position: relative;
border-top: 1px solid $color-border;
}
.contactDel {
position: absolute;
top: 8px;
right: -8px;
width: 64px;
height: 64px;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
}
.contactDelText {
font-size: 36px;
color: $color-danger;
}
.submit {
height: 96px;
margin-top: 40px;
background: $color-primary-grad;
border-radius: 48px;
display: flex;
align-items: center;
justify-content: center;
}
.submitText {
font-size: 32px;
font-weight: 600;
color: #ffffff;
}

View File

@ -0,0 +1,155 @@
import { useState } from 'react'
import { useRouter } from '@tarojs/taro'
import { View, Text, Input } from '@tarojs/components'
import { Navbar } from '@/components/navbar'
import { addCared, type EmergencyContact } from '@/api/family'
import { MAX_EMERGENCY_CONTACTS, MAX_FAMILY_NAME_LEN } from '@/constants/family'
import { navigateBack } from '@/utils/navigation'
import { showToast } from '@/utils/toast'
import styles from './add.module.scss'
const emptyContact = (): EmergencyContact => ({
id: `tmp-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
name: '',
phone: '',
relation: ''
})
export default function ContactAdd() {
const router = useRouter()
const familyId = router.params.familyId ?? ''
const [name, setName] = useState('')
const [idNumber, setIdNumber] = useState('')
const [address, setAddress] = useState('')
const [contacts, setContacts] = useState<EmergencyContact[]>([])
const [submitting, setSubmitting] = useState(false)
const patchContact = (id: string, key: keyof EmergencyContact, value: string) => {
setContacts((prev) => prev.map((c) => (c.id === id ? { ...c, [key]: value } : c)))
}
const removeContact = (id: string) => {
setContacts((prev) => prev.filter((c) => c.id !== id))
}
const submit = async () => {
if (submitting) return
if (!name.trim() || !idNumber.trim() || !address.trim()) {
showToast('请填写完整')
return
}
// 跳过完全未填的联系人卡,只提交有内容的
const filled = contacts.filter((c) => c.name.trim() || c.phone.trim())
if (filled.some((c) => !c.name.trim() || !c.phone.trim())) {
showToast('请完整填写联系人')
return
}
setSubmitting(true)
try {
await addCared({
familyId,
name: name.trim(),
idNumber: idNumber.trim(),
address: address.trim(),
contacts: filled
})
showToast('添加成功')
setTimeout(() => navigateBack(), 500)
} catch (error) {
showToast(error instanceof Error ? error.message : '添加失败')
} finally {
setSubmitting(false)
}
}
return (
<View className={styles.page}>
<Navbar title="添加关爱对象" />
<View className={styles.card}>
<View className={styles.formRow}>
<Text className={styles.label}><Text className={styles.required}>*</Text>姓名</Text>
<Input
className={styles.input}
placeholder="请输入最多 10 个字"
maxLength={MAX_FAMILY_NAME_LEN}
value={name}
onInput={(e) => setName(e.detail.value)}
/>
</View>
<View className={styles.formRow}>
<Text className={styles.label}><Text className={styles.required}>*</Text>身份证号</Text>
<Input
className={styles.input}
placeholder="请输入身份证号"
value={idNumber}
onInput={(e) => setIdNumber(e.detail.value)}
/>
</View>
<View className={styles.formRow}>
<Text className={styles.label}><Text className={styles.required}>*</Text>关爱地址</Text>
<Input
className={styles.input}
placeholder="请输入关爱地址"
value={address}
onInput={(e) => setAddress(e.detail.value)}
/>
</View>
</View>
<View className={styles.contactsCard}>
<View className={styles.contactsHead}>
<Text className={styles.contactsTitle}>紧急联系人</Text>
{contacts.length < MAX_EMERGENCY_CONTACTS && (
<Text className={styles.contactsAdd} onClick={() => setContacts((p) => [...p, emptyContact()])}>
+ 添加
</Text>
)}
</View>
<Text className={styles.contactsTip}>最多可添加 {MAX_EMERGENCY_CONTACTS} 位紧急联系人,可不填</Text>
{contacts.map((c) => (
<View key={c.id} className={styles.contactItem}>
<View className={styles.contactDel} onClick={() => removeContact(c.id)}>
<Text className={styles.contactDelText}>×</Text>
</View>
<View className={styles.formRow}>
<Text className={styles.label}>姓名</Text>
<Input
className={styles.input}
placeholder="联系人姓名"
value={c.name}
onInput={(e) => patchContact(c.id, 'name', e.detail.value)}
/>
</View>
<View className={styles.formRow}>
<Text className={styles.label}>电话</Text>
<Input
className={styles.input}
type="number"
placeholder="联系人电话"
value={c.phone}
onInput={(e) => patchContact(c.id, 'phone', e.detail.value)}
/>
</View>
<View className={styles.formRow}>
<Text className={styles.label}>关系</Text>
<Input
className={styles.input}
placeholder="如:配偶、儿子"
value={c.relation}
onInput={(e) => patchContact(c.id, 'relation', e.detail.value)}
/>
</View>
</View>
))}
</View>
<View className={styles.submit} onClick={submit}>
<Text className={styles.submitText}>{submitting ? '提交中...' : '确认添加'}</Text>
</View>
</View>
)
}