支持绑定卡片管理和名称编辑
This commit is contained in:
@@ -6,9 +6,13 @@ import { DEVICE_UNAVAILABLE_MESSAGE } from '@/services/api'
|
||||
import { getCurrentUserId } from '@/services/session'
|
||||
import {
|
||||
BindingListItem,
|
||||
BindingCardItem,
|
||||
BindingCardGroupItem,
|
||||
clearSelectedBindingDeviceId,
|
||||
getBindingCardGroups,
|
||||
loadCurrentChildBindingContext,
|
||||
setSelectedBindingDeviceId,
|
||||
updateBindingCardName,
|
||||
unbindDevice,
|
||||
} from '@/services/binding'
|
||||
import { Child, clearSelectedChildId, createChild, deleteChild, setSelectedChildId, updateChild } from '@/services/child'
|
||||
@@ -53,6 +57,8 @@ export default function Sleep() {
|
||||
const [firmwareStatus, setFirmwareStatus] = useState<DeviceFirmwareStatus | null>(null)
|
||||
const [deviceRole, setDeviceRole] = useState<DeviceCurrentRole | null>(null)
|
||||
const [roles, setRoles] = useState<DeviceRoleSummary[]>([])
|
||||
const [currentCardGroup, setCurrentCardGroup] = useState<BindingCardGroupItem | null>(null)
|
||||
const [isLoadingCards, setIsLoadingCards] = useState(false)
|
||||
const [isLoadingFirmware, setIsLoadingFirmware] = useState(false)
|
||||
const [isUpdatingFirmware, setIsUpdatingFirmware] = useState(false)
|
||||
const [isLoadingRoles, setIsLoadingRoles] = useState(false)
|
||||
@@ -69,9 +75,13 @@ export default function Sleep() {
|
||||
const [showRoleModal, setShowRoleModal] = useState(false)
|
||||
const [showFamilyModal, setShowFamilyModal] = useState(false)
|
||||
const [showAssignDeviceModal, setShowAssignDeviceModal] = useState(false)
|
||||
const [showCardNameModal, setShowCardNameModal] = useState(false)
|
||||
const [modalType, setModalType] = useState<'add' | 'edit'>('add')
|
||||
const [childName, setChildName] = useState('')
|
||||
const [editingChildId, setEditingChildId] = useState<number | null>(null)
|
||||
const [editingCard, setEditingCard] = useState<BindingCardItem | null>(null)
|
||||
const [cardName, setCardName] = useState('')
|
||||
const [isUpdatingCardName, setIsUpdatingCardName] = useState(false)
|
||||
const [parentInfo, setParentInfo] = useState<Parent | null>(null)
|
||||
const [isAuthorizingPhone, setIsAuthorizingPhone] = useState(false)
|
||||
|
||||
@@ -107,6 +117,7 @@ export default function Sleep() {
|
||||
void loadFirmwareStatus(nextBinding?.device_id)
|
||||
void loadRoleData(nextBinding?.device_id)
|
||||
void loadFamilyData(nextBinding?.device_id)
|
||||
void loadCurrentDeviceCards(nextBinding?.device_id)
|
||||
} catch (error: any) {
|
||||
console.error('[manage] load failed:', error)
|
||||
Taro.showToast({
|
||||
@@ -215,6 +226,31 @@ export default function Sleep() {
|
||||
}
|
||||
}
|
||||
|
||||
const loadCurrentDeviceCards = async (deviceId?: string | null) => {
|
||||
const normalizedDeviceId = String(deviceId || '').trim()
|
||||
if (!normalizedDeviceId) {
|
||||
setCurrentCardGroup(null)
|
||||
setIsLoadingCards(false)
|
||||
return
|
||||
}
|
||||
|
||||
setIsLoadingCards(true)
|
||||
try {
|
||||
const nextCardGroups = await getBindingCardGroups()
|
||||
const nextCurrentGroup = nextCardGroups.items.find((item) => item.device_id === normalizedDeviceId) || null
|
||||
setCurrentCardGroup(nextCurrentGroup)
|
||||
} catch (error: any) {
|
||||
console.error('[manage] card groups load failed:', error)
|
||||
setCurrentCardGroup(null)
|
||||
Taro.showToast({
|
||||
title: error?.message || '卡片信息加载失败',
|
||||
icon: 'none',
|
||||
})
|
||||
} finally {
|
||||
setIsLoadingCards(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleOpenFamilyModal = () => {
|
||||
if (!binding?.device_id) {
|
||||
Taro.showToast({ title: '当前没有可用设备', icon: 'none' })
|
||||
@@ -338,6 +374,7 @@ export default function Sleep() {
|
||||
void loadFirmwareStatus(nextBinding?.device_id)
|
||||
void loadRoleData(nextBinding?.device_id)
|
||||
void loadFamilyData(nextBinding?.device_id)
|
||||
void loadCurrentDeviceCards(nextBinding?.device_id)
|
||||
setShowChildModal(false)
|
||||
Taro.showToast({
|
||||
title: '已切换当前孩子',
|
||||
@@ -351,6 +388,48 @@ export default function Sleep() {
|
||||
handleOpenModal('add')
|
||||
}
|
||||
|
||||
const handleOpenCardNameModal = (card: BindingCardItem) => {
|
||||
setEditingCard(card)
|
||||
setCardName(card.card_name || '')
|
||||
setShowCardNameModal(true)
|
||||
}
|
||||
|
||||
const handleCloseCardNameModal = () => {
|
||||
if (isUpdatingCardName) return
|
||||
setShowCardNameModal(false)
|
||||
setEditingCard(null)
|
||||
setCardName('')
|
||||
}
|
||||
|
||||
const handleSubmitCardName = async () => {
|
||||
if (!editingCard || isUpdatingCardName) return
|
||||
|
||||
const normalizedName = cardName.trim()
|
||||
if (normalizedName.length > 64) {
|
||||
Taro.showToast({ title: '卡片名称最多 64 个字', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
setIsUpdatingCardName(true)
|
||||
try {
|
||||
await updateBindingCardName(editingCard.card_id, {
|
||||
card_name: normalizedName || null,
|
||||
})
|
||||
Taro.showToast({ title: '已保存', icon: 'success' })
|
||||
setShowCardNameModal(false)
|
||||
setEditingCard(null)
|
||||
setCardName('')
|
||||
await loadCurrentDeviceCards(binding?.device_id)
|
||||
} catch (error: any) {
|
||||
Taro.showToast({
|
||||
title: error?.message || '卡片名称保存失败',
|
||||
icon: 'none',
|
||||
})
|
||||
} finally {
|
||||
setIsUpdatingCardName(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleAssignExistingDevice = (targetBinding: BindingListItem) => {
|
||||
if (!targetBinding?.device_id) return
|
||||
setSelectedBindingDeviceId(targetBinding.device_id)
|
||||
@@ -654,6 +733,12 @@ export default function Sleep() {
|
||||
const latestFirmwareLabel = firmwareStatus?.latest_version || '--'
|
||||
const currentUserId = getCurrentUserId()
|
||||
const isFamilyOwner = familyInfo?.current_user_role === 'owner'
|
||||
const canEditCardName = Boolean(
|
||||
binding?.device_id &&
|
||||
currentCardGroup?.device_id === binding.device_id &&
|
||||
familyInfo?.device_id === binding.device_id &&
|
||||
isFamilyOwner
|
||||
)
|
||||
const canUpdateFirmware = Boolean(binding?.device_id && firmwareStatus?.can_update && !isLoadingFirmware && !isUpdatingFirmware)
|
||||
const firmwareActionText = isLoadingFirmware ? '查询中' : isUpdatingFirmware ? '发送中' : '更新'
|
||||
const roleValue = !binding?.device_id
|
||||
@@ -832,6 +917,50 @@ export default function Sleep() {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className={`bound-cards-card ${binding?.device_id ? '' : 'disabled'}`}>
|
||||
<View className='bound-cards-header'>
|
||||
<View className='bound-cards-heading'>
|
||||
<Text className='bound-cards-title'>已绑卡片</Text>
|
||||
<Text className='bound-cards-subtitle'>{binding?.device_id || '当前未绑定设备'}</Text>
|
||||
</View>
|
||||
<Text className='bound-cards-count'>
|
||||
{isLoadingCards ? '查询中' : `${currentCardGroup?.card_count || 0} 张`}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{!binding?.device_id ? (
|
||||
<View className='bound-cards-empty'>
|
||||
<Text className='bound-cards-empty-text'>绑定设备后可查看卡片 UUID</Text>
|
||||
</View>
|
||||
) : isLoadingCards ? (
|
||||
<View className='bound-cards-empty'>
|
||||
<Text className='bound-cards-empty-text'>卡片加载中...</Text>
|
||||
</View>
|
||||
) : currentCardGroup?.cards.length ? (
|
||||
<View className='bound-card-list'>
|
||||
{currentCardGroup.cards.map((card) => (
|
||||
<View className='bound-card-item' key={`${currentCardGroup.device_id}-${card.card_id}`}>
|
||||
<View className='bound-card-main'>
|
||||
<Text className={`bound-card-name ${card.card_name ? '' : 'empty'}`}>
|
||||
{card.card_name || '未命名卡片'}
|
||||
</Text>
|
||||
<Text className='bound-card-uuid'>{card.card_uuid}</Text>
|
||||
</View>
|
||||
{canEditCardName && (
|
||||
<Text className='bound-card-edit' onClick={() => handleOpenCardNameModal(card)}>
|
||||
编辑
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
) : (
|
||||
<View className='bound-cards-empty'>
|
||||
<Text className='bound-cards-empty-text'>当前设备暂无已绑卡片</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<View className='menu-card'>
|
||||
{menuItems.map((item, index) => (
|
||||
<View key={index}>
|
||||
@@ -1018,6 +1147,32 @@ export default function Sleep() {
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{showCardNameModal && (
|
||||
<View className='modal-mask'>
|
||||
<View className='modal-card'>
|
||||
<Text className='modal-title'>编辑卡片名称</Text>
|
||||
<View className='modal-input-wrap'>
|
||||
<Input
|
||||
className='modal-input'
|
||||
value={cardName}
|
||||
placeholder='例如 奶奶的卡'
|
||||
focus
|
||||
disabled={isUpdatingCardName}
|
||||
onInput={(event) => setCardName(event.detail.value)}
|
||||
/>
|
||||
</View>
|
||||
<Text className='card-name-modal-tip'>留空保存后会显示为未命名卡片</Text>
|
||||
<View className='modal-actions'>
|
||||
<Text className='modal-action cancel' onClick={handleCloseCardNameModal}>
|
||||
取消
|
||||
</Text>
|
||||
<Text className={`modal-action confirm ${isUpdatingCardName ? 'disabled' : ''}`} onClick={handleSubmitCardName}>
|
||||
{isUpdatingCardName ? '保存中...' : '确认'}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{showRoleModal && (
|
||||
<View className='modal-mask' onClick={() => setShowRoleModal(false)}>
|
||||
<View
|
||||
|
||||
Reference in New Issue
Block a user