支持家庭成员显示名编辑

This commit is contained in:
stu2not
2026-06-08 11:46:14 +08:00
parent dcff337f43
commit ac9d669107
10 changed files with 255 additions and 6 deletions

View File

@@ -825,13 +825,28 @@
color: #666666;
}
.family-member-actions {
flex-shrink: 0;
display: flex;
align-items: center;
gap: 18px;
}
.family-member-edit,
.family-member-remove {
flex-shrink: 0;
font-size: 26px;
color: #E5484D;
font-weight: 600;
}
.family-member-edit {
color: #FF8C42;
}
.family-member-remove {
color: #E5484D;
}
.family-primary-action,
.family-danger-action {
height: 84px;
@@ -878,6 +893,14 @@
text-align: center;
}
.family-name-modal-tip {
display: block;
margin-top: 14px;
font-size: 24px;
line-height: 1.5;
color: #94A3B8;
}
.family-copy-action {
display: block;
margin-top: 14px;

View File

@@ -23,6 +23,7 @@ import {
getFamilyMembers,
leaveFamily,
removeFamilyMember,
updateFamilyMemberDisplayName,
} from '@/services/family'
import {
DeviceCurrentRole,
@@ -67,6 +68,7 @@ export default function Sleep() {
const [isLoadingFamily, setIsLoadingFamily] = useState(false)
const [isCreatingInvite, setIsCreatingInvite] = useState(false)
const [isUpdatingFamily, setIsUpdatingFamily] = useState(false)
const [isUpdatingFamilyName, setIsUpdatingFamilyName] = useState(false)
const [familyInvitePath, setFamilyInvitePath] = useState('')
const [familyInviteExpiresAt, setFamilyInviteExpiresAt] = useState('')
const [showModal, setShowModal] = useState(false)
@@ -74,6 +76,7 @@ export default function Sleep() {
const [showDeleteChildModal, setShowDeleteChildModal] = useState(false)
const [showRoleModal, setShowRoleModal] = useState(false)
const [showFamilyModal, setShowFamilyModal] = useState(false)
const [showFamilyNameModal, setShowFamilyNameModal] = useState(false)
const [showAssignDeviceModal, setShowAssignDeviceModal] = useState(false)
const [showCardNameModal, setShowCardNameModal] = useState(false)
const [modalType, setModalType] = useState<'add' | 'edit'>('add')
@@ -82,6 +85,8 @@ export default function Sleep() {
const [editingCard, setEditingCard] = useState<BindingCardItem | null>(null)
const [cardName, setCardName] = useState('')
const [isUpdatingCardName, setIsUpdatingCardName] = useState(false)
const [editingFamilyMember, setEditingFamilyMember] = useState<FamilyMember | null>(null)
const [familyMemberDisplayName, setFamilyMemberDisplayName] = useState('')
const [parentInfo, setParentInfo] = useState<Parent | null>(null)
const [isAuthorizingPhone, setIsAuthorizingPhone] = useState(false)
@@ -318,6 +323,49 @@ export default function Sleep() {
})
}
const handleOpenFamilyNameModal = (member: FamilyMember) => {
if (!binding?.device_id || !isFamilyOwner) return
setEditingFamilyMember(member)
setFamilyMemberDisplayName(member.display_name || '')
setShowFamilyNameModal(true)
}
const handleCloseFamilyNameModal = () => {
if (isUpdatingFamilyName) return
setShowFamilyNameModal(false)
setEditingFamilyMember(null)
setFamilyMemberDisplayName('')
}
const handleSubmitFamilyName = async () => {
if (!binding?.device_id || !editingFamilyMember || isUpdatingFamilyName) return
const normalizedName = familyMemberDisplayName.trim()
if (normalizedName.length > 64) {
Taro.showToast({ title: '显示名最多 64 个字', icon: 'none' })
return
}
setIsUpdatingFamilyName(true)
try {
await updateFamilyMemberDisplayName(binding.device_id, editingFamilyMember.user_id, {
display_name: normalizedName || null,
})
Taro.showToast({ title: '已保存', icon: 'success' })
setShowFamilyNameModal(false)
setEditingFamilyMember(null)
setFamilyMemberDisplayName('')
await loadFamilyData(binding.device_id)
} catch (error: any) {
Taro.showToast({
title: error?.message || '显示名保存失败',
icon: 'none',
})
} finally {
setIsUpdatingFamilyName(false)
}
}
const handleLeaveFamily = () => {
if (!binding?.device_id || isUpdatingFamily) return
@@ -1246,6 +1294,7 @@ export default function Sleep() {
<View className='family-list'>
{familyInfo.items.map((member) => {
const canRemove = isFamilyOwner && !member.is_owner && !isUpdatingFamily
const canEditName = isFamilyOwner && !isUpdatingFamilyName
const isMe = currentUserId === member.user_id
return (
<View key={member.user_id} className='family-member-item'>
@@ -1266,10 +1315,19 @@ export default function Sleep() {
</View>
<Text className='family-member-role'>{member.is_owner ? '设备归属账号' : '家庭共享成员'}</Text>
</View>
{canRemove && (
<Text className='family-member-remove' onClick={() => handleRemoveFamilyMember(member)}>
</Text>
{(canEditName || canRemove) && (
<View className='family-member-actions'>
{canEditName && (
<Text className='family-member-edit' onClick={() => handleOpenFamilyNameModal(member)}>
</Text>
)}
{canRemove && (
<Text className='family-member-remove' onClick={() => handleRemoveFamilyMember(member)}>
</Text>
)}
</View>
)}
</View>
)
@@ -1320,6 +1378,32 @@ export default function Sleep() {
</View>
</View>
)}
{showFamilyNameModal && (
<View className='modal-mask'>
<View className='modal-card'>
<Text className='modal-title'></Text>
<View className='modal-input-wrap'>
<Input
className='modal-input'
value={familyMemberDisplayName}
placeholder='例如 奶奶'
focus
disabled={isUpdatingFamilyName}
onInput={(event) => setFamilyMemberDisplayName(event.detail.value)}
/>
</View>
<Text className='family-name-modal-tip'></Text>
<View className='modal-actions'>
<Text className='modal-action cancel' onClick={handleCloseFamilyNameModal}>
</Text>
<Text className={`modal-action confirm ${isUpdatingFamilyName ? 'disabled' : ''}`} onClick={handleSubmitFamilyName}>
{isUpdatingFamilyName ? '保存中...' : '确认'}
</Text>
</View>
</View>
</View>
)}
{systemBanner}
</View>
)

View File

@@ -3,6 +3,8 @@ import { request } from './api'
export interface FamilyMember {
user_id: number
nickname?: string | null
display_name?: string | null
account_nickname?: string | null
avatar_url?: string | null
role: 'owner' | 'member'
is_owner: boolean
@@ -64,6 +66,17 @@ export async function removeFamilyMember(deviceId: string, memberUserId: number)
})
}
export async function updateFamilyMemberDisplayName(
deviceId: string,
memberUserId: number,
data: { display_name?: string | null }
): Promise<FamilyMember> {
return request<FamilyMember>(`/banban/family/devices/${deviceId}/members/${memberUserId}`, {
method: 'PATCH',
data,
})
}
export async function leaveFamily(deviceId: string): Promise<void> {
return request<void>(`/banban/family/devices/${deviceId}/me`, {
method: 'DELETE',