Refine device-first baby management
This commit is contained in:
@@ -201,7 +201,7 @@ export default function Bind() {
|
|||||||
const isExpiredBind = bindStatus === SESSION_STATUS_EXPIRED
|
const isExpiredBind = bindStatus === SESSION_STATUS_EXPIRED
|
||||||
const canEditDeviceFields = !isPendingBinding && !isPollingBind
|
const canEditDeviceFields = !isPendingBinding && !isPollingBind
|
||||||
const canSubmit = isPendingBinding ? hasSelectedOrNewChild : isExpiredBind ? true : !isPollingBind
|
const canSubmit = isPendingBinding ? hasSelectedOrNewChild : isExpiredBind ? true : !isPollingBind
|
||||||
const pageTitle = isPendingBinding ? '补全绑定' : isAddingCard ? '添加设备卡' : '扫码贴卡绑定设备'
|
const pageTitle = isPendingBinding ? '补全绑定' : isAddingCard ? '添加设备卡片' : '扫码贴卡绑定设备'
|
||||||
|
|
||||||
const goDevicePage = () => {
|
const goDevicePage = () => {
|
||||||
Taro.reLaunch({ url: '/pages/device/index' })
|
Taro.reLaunch({ url: '/pages/device/index' })
|
||||||
@@ -286,7 +286,7 @@ export default function Bind() {
|
|||||||
if (parsed.serialNumber) setSerialNumber(parsed.serialNumber)
|
if (parsed.serialNumber) setSerialNumber(parsed.serialNumber)
|
||||||
setBindHint(
|
setBindHint(
|
||||||
parsed.serialNumber
|
parsed.serialNumber
|
||||||
? '已读取设备二维码信息,请选择儿童后发送绑卡指令'
|
? '已读取设备二维码信息,请选择宝贝后发送绑卡指令'
|
||||||
: '已读取设备号,请补充设备序列号'
|
: '已读取设备号,请补充设备序列号'
|
||||||
)
|
)
|
||||||
return true
|
return true
|
||||||
@@ -364,7 +364,7 @@ export default function Bind() {
|
|||||||
? session.card_uuid ? `新卡添加完成,卡号 ${session.card_uuid}` : '新卡添加完成'
|
? session.card_uuid ? `新卡添加完成,卡号 ${session.card_uuid}` : '新卡添加完成'
|
||||||
: session.child_id
|
: session.child_id
|
||||||
? session.card_uuid ? `绑定完成,卡号 ${session.card_uuid}` : '绑定完成'
|
? session.card_uuid ? `绑定完成,卡号 ${session.card_uuid}` : '绑定完成'
|
||||||
: session.card_uuid ? `绑定完成,卡号 ${session.card_uuid},稍后可关联孩子` : '绑定完成,稍后可关联孩子'
|
: session.card_uuid ? `绑定完成,卡号 ${session.card_uuid},稍后可关联宝贝` : '绑定完成,稍后可关联宝贝'
|
||||||
)
|
)
|
||||||
Taro.showToast({ title: isAddingCard ? '新卡添加成功' : '设备绑定成功', icon: 'success' })
|
Taro.showToast({ title: isAddingCard ? '新卡添加成功' : '设备绑定成功', icon: 'success' })
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -500,7 +500,7 @@ export default function Bind() {
|
|||||||
|
|
||||||
if (isPendingBinding) {
|
if (isPendingBinding) {
|
||||||
if (!childId) {
|
if (!childId) {
|
||||||
throw new Error('请先选择儿童或填写新的儿童昵称')
|
throw new Error('请先选择宝贝或填写新的宝贝昵称')
|
||||||
}
|
}
|
||||||
await setBindingChild(pendingDeviceId!, { child_id: childId })
|
await setBindingChild(pendingDeviceId!, { child_id: childId })
|
||||||
setSelectedBindingDeviceId(pendingDeviceId)
|
setSelectedBindingDeviceId(pendingDeviceId)
|
||||||
@@ -585,7 +585,7 @@ export default function Bind() {
|
|||||||
<View className='icon-bg orange'>
|
<View className='icon-bg orange'>
|
||||||
<Image className='control-icon-img' src={require('../../assets/tab-icons/rings.png')} mode='aspectFit' />
|
<Image className='control-icon-img' src={require('../../assets/tab-icons/rings.png')} mode='aspectFit' />
|
||||||
</View>
|
</View>
|
||||||
<Text className='pending-device-title'>给当前设备添加卡</Text>
|
<Text className='pending-device-title'>给当前设备添加卡片</Text>
|
||||||
<Text className='pending-device-id'>{deviceId}</Text>
|
<Text className='pending-device-id'>{deviceId}</Text>
|
||||||
<Text className='pending-device-desc'>新卡添加后会和已有卡具备相同功能</Text>
|
<Text className='pending-device-desc'>新卡添加后会和已有卡具备相同功能</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -596,7 +596,7 @@ export default function Bind() {
|
|||||||
</View>
|
</View>
|
||||||
<Text className='pending-device-title'>关联现有设备</Text>
|
<Text className='pending-device-title'>关联现有设备</Text>
|
||||||
<Text className='pending-device-id'>{pendingDeviceId}</Text>
|
<Text className='pending-device-id'>{pendingDeviceId}</Text>
|
||||||
<Text className='pending-device-desc'>选择已有孩子,或新建孩子后完成关联</Text>
|
<Text className='pending-device-desc'>选择已有宝贝,或新建宝贝后完成关联</Text>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View className='scan-area'>
|
<View className='scan-area'>
|
||||||
@@ -622,7 +622,7 @@ export default function Bind() {
|
|||||||
<View className='bind-form'>
|
<View className='bind-form'>
|
||||||
{!isAddingCard && (
|
{!isAddingCard && (
|
||||||
<View className='form-card'>
|
<View className='form-card'>
|
||||||
<Text className='form-title'>儿童资料</Text>
|
<Text className='form-title'>宝贝资料</Text>
|
||||||
|
|
||||||
{children.length > 0 && (
|
{children.length > 0 && (
|
||||||
<View className='child-list'>
|
<View className='child-list'>
|
||||||
@@ -635,7 +635,7 @@ export default function Bind() {
|
|||||||
setNewChildName('')
|
setNewChildName('')
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text className='child-item-text'>暂不关联孩子</Text>
|
<Text className='child-item-text'>暂不关联宝贝</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{children.map((child) => (
|
{children.map((child) => (
|
||||||
@@ -658,14 +658,14 @@ export default function Bind() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<View className='field-item'>
|
<View className='field-item'>
|
||||||
<Text className='field-label'>{isPendingBinding ? '新建儿童昵称' : '新建儿童昵称(可选)'}</Text>
|
<Text className='field-label'>{isPendingBinding ? '新建宝贝昵称' : '新建宝贝昵称(可选)'}</Text>
|
||||||
<Input
|
<Input
|
||||||
className='field-input'
|
className='field-input'
|
||||||
value={newChildName}
|
value={newChildName}
|
||||||
placeholder={
|
placeholder={
|
||||||
isPendingBinding
|
isPendingBinding
|
||||||
? children.length > 0 ? '不选已有儿童时,可填写新昵称' : '请输入儿童昵称'
|
? children.length > 0 ? '不选已有宝贝时,可填写新昵称' : '请输入宝贝昵称'
|
||||||
: children.length > 0 ? '不选已有儿童时,可填写新昵称' : '可不填,稍后再关联孩子'
|
: children.length > 0 ? '不选已有宝贝时,可填写新昵称' : '可不填,稍后再关联宝贝'
|
||||||
}
|
}
|
||||||
onInput={(event) => {
|
onInput={(event) => {
|
||||||
setNewChildName(event.detail.value)
|
setNewChildName(event.detail.value)
|
||||||
@@ -680,11 +680,11 @@ export default function Bind() {
|
|||||||
<Text className='field-hint'>
|
<Text className='field-hint'>
|
||||||
{isPendingBinding
|
{isPendingBinding
|
||||||
? children.length > 0
|
? children.length > 0
|
||||||
? '可以选已有儿童,也可以新建一个儿童后关联当前设备'
|
? '可以选已有宝贝,也可以新建一个宝贝后关联当前设备'
|
||||||
: '当前还没有儿童资料,请先创建一个儿童资料后关联设备'
|
: '当前还没有宝贝资料,请先创建一个宝贝资料后关联设备'
|
||||||
: children.length > 0
|
: children.length > 0
|
||||||
? '儿童资料可选;不选择时,设备会先绑定到账号,稍后可关联孩子'
|
? '宝贝资料可选;不选择时,设备会先绑定到账号,稍后可关联宝贝'
|
||||||
: '也可以先不创建儿童,直接绑定设备,稍后再关联孩子'}
|
: '也可以先不创建宝贝,直接绑定设备,稍后再关联宝贝'}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
@@ -742,7 +742,7 @@ export default function Bind() {
|
|||||||
onClick={isExpiredBind && !isPendingBinding && !isAddingCard ? handleScanCode : handleSubmit}
|
onClick={isExpiredBind && !isPendingBinding && !isAddingCard ? handleScanCode : handleSubmit}
|
||||||
>
|
>
|
||||||
{isPendingBinding
|
{isPendingBinding
|
||||||
? '完成儿童关联'
|
? '完成宝贝关联'
|
||||||
: isPollingBind
|
: isPollingBind
|
||||||
? '等待贴卡确认'
|
? '等待贴卡确认'
|
||||||
: isExpiredBind
|
: isExpiredBind
|
||||||
@@ -755,15 +755,15 @@ export default function Bind() {
|
|||||||
<Text className='tips-title'>绑定帮助</Text>
|
<Text className='tips-title'>绑定帮助</Text>
|
||||||
<View className='tip-item'>
|
<View className='tip-item'>
|
||||||
<Text className='tip-number'>1</Text>
|
<Text className='tip-number'>1</Text>
|
||||||
<Text className='tip-text'>{isAddingCard ? '确认当前设备号正确' : isPendingBinding ? '先选择已有儿童,或者在本页新建一个儿童' : '儿童资料可选,不选择时设备会先进入待关联状态'}</Text>
|
<Text className='tip-text'>{isAddingCard ? '确认当前设备号正确' : isPendingBinding ? '先选择已有宝贝,或者在本页新建一个宝贝' : '宝贝资料可选,不选择时设备会先进入待关联状态'}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='tip-item'>
|
<View className='tip-item'>
|
||||||
<Text className='tip-number'>2</Text>
|
<Text className='tip-number'>2</Text>
|
||||||
<Text className='tip-text'>{isAddingCard ? '点击发送加卡指令后,在设备上贴新卡' : isPendingBinding ? '确认当前待关联设备,再选择要关联的孩子' : '扫描设备二维码,确认设备号和序列号正确'}</Text>
|
<Text className='tip-text'>{isAddingCard ? '点击发送加卡指令后,在设备上贴新卡' : isPendingBinding ? '确认当前待关联设备,再选择要关联的宝贝' : '扫描设备二维码,确认设备号和序列号正确'}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='tip-item'>
|
<View className='tip-item'>
|
||||||
<Text className='tip-number'>3</Text>
|
<Text className='tip-number'>3</Text>
|
||||||
<Text className='tip-text'>{isAddingCard ? '加卡成功后,新卡和原来的卡可以执行相同功能' : isPendingBinding ? '点击完成儿童关联,设备会出现在该孩子下面' : '点击发送绑卡指令,然后在 10 分钟内去设备上贴自己的卡完成确认'}</Text>
|
<Text className='tip-text'>{isAddingCard ? '加卡成功后,新卡和原来的卡可以执行相同功能' : isPendingBinding ? '点击完成宝贝关联,设备会出现在该宝贝下面' : '点击发送绑卡指令,然后在 10 分钟内去设备上贴自己的卡完成确认'}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{systemBanner}
|
{systemBanner}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ function formatParticipantLabel(message: ChatMessage): string {
|
|||||||
return name === '我' ? '我' : name || '家长'
|
return name === '我' ? '我' : name || '家长'
|
||||||
}
|
}
|
||||||
if (senderType === 'child') {
|
if (senderType === 'child') {
|
||||||
return name || `儿童ID ${senderId}`
|
return name || `宝贝ID ${senderId}`
|
||||||
}
|
}
|
||||||
if (senderType === 'device') {
|
if (senderType === 'device') {
|
||||||
return name || `设备ID ${senderId}`
|
return name || `设备ID ${senderId}`
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default function Chat() {
|
|||||||
<View className='guest-card'>
|
<View className='guest-card'>
|
||||||
<Text className='guest-card-icon'>💬</Text>
|
<Text className='guest-card-icon'>💬</Text>
|
||||||
<Text className='guest-card-title'>亲子互动会话</Text>
|
<Text className='guest-card-title'>亲子互动会话</Text>
|
||||||
<Text className='guest-card-desc'>绑定设备后,可以在这里查看孩子、家长和 AI 玩伴之间的消息记录。</Text>
|
<Text className='guest-card-desc'>绑定设备后,可以在这里查看宝贝、家长和 AI 玩伴之间的消息记录。</Text>
|
||||||
<View className='guest-card-btn' onClick={handleGuestLogin}>
|
<View className='guest-card-btn' onClick={handleGuestLogin}>
|
||||||
<Text className='guest-card-btn-text'>登录后查看</Text>
|
<Text className='guest-card-btn-text'>登录后查看</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -85,7 +85,7 @@ export default function Chat() {
|
|||||||
</View>
|
</View>
|
||||||
) : conversations.length === 0 ? (
|
) : conversations.length === 0 ? (
|
||||||
<View className='empty-card'>
|
<View className='empty-card'>
|
||||||
<Text className='empty-title'>当前孩子暂无会话</Text>
|
<Text className='empty-title'>当前宝贝暂无会话</Text>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<ScrollView className='conversation-list' scrollY>
|
<ScrollView className='conversation-list' scrollY>
|
||||||
|
|||||||
@@ -93,8 +93,8 @@ function getAlarmChildLabel(alarm: DeviceAlarmItem | null, fallbackChildName?: s
|
|||||||
const fallbackName = String(fallbackChildName || '').trim()
|
const fallbackName = String(fallbackChildName || '').trim()
|
||||||
if (fallbackName) return fallbackName
|
if (fallbackName) return fallbackName
|
||||||
|
|
||||||
if (alarm?.child_id) return `儿童 ${alarm.child_id}`
|
if (alarm?.child_id) return `宝贝 ${alarm.child_id}`
|
||||||
return '未关联儿童'
|
return '未关联宝贝'
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatAlarmCoordinates(alarm: DeviceAlarmItem | null): string {
|
function formatAlarmCoordinates(alarm: DeviceAlarmItem | null): string {
|
||||||
@@ -287,7 +287,7 @@ export default function Device() {
|
|||||||
<View className='guest-device-icon'>
|
<View className='guest-device-icon'>
|
||||||
<Image className='robot-img' src={require('../../assets/tab-icons/robot.png')} mode='aspectFit' />
|
<Image className='robot-img' src={require('../../assets/tab-icons/robot.png')} mode='aspectFit' />
|
||||||
</View>
|
</View>
|
||||||
<Text className='guest-title'>伴伴儿童陪伴设备</Text>
|
<Text className='guest-title'>伴伴宝贝陪伴设备</Text>
|
||||||
<Text className='guest-subtitle'>查看设备状态、定位轨迹、亲子消息和休眠管理。浏览功能后,可在需要绑定设备时登录。</Text>
|
<Text className='guest-subtitle'>查看设备状态、定位轨迹、亲子消息和休眠管理。浏览功能后,可在需要绑定设备时登录。</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -305,18 +305,18 @@ export default function Device() {
|
|||||||
<View className='guest-feature-card'>
|
<View className='guest-feature-card'>
|
||||||
<Image className='guest-feature-icon' src={require('../../assets/tab-icons/chat-icon.png')} mode='aspectFit' />
|
<Image className='guest-feature-icon' src={require('../../assets/tab-icons/chat-icon.png')} mode='aspectFit' />
|
||||||
<Text className='guest-feature-title'>亲子互动</Text>
|
<Text className='guest-feature-title'>亲子互动</Text>
|
||||||
<Text className='guest-feature-desc'>查看孩子和设备的消息会话。</Text>
|
<Text className='guest-feature-desc'>查看宝贝和设备的消息会话。</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='guest-feature-card'>
|
<View className='guest-feature-card'>
|
||||||
<Image className='guest-feature-icon' src={require('../../assets/tab-icons/manage-icon.png')} mode='aspectFit' />
|
<Image className='guest-feature-icon' src={require('../../assets/tab-icons/manage-icon.png')} mode='aspectFit' />
|
||||||
<Text className='guest-feature-title'>设备管理</Text>
|
<Text className='guest-feature-title'>设备管理</Text>
|
||||||
<Text className='guest-feature-desc'>管理孩子资料、绑定设备和休眠设置。</Text>
|
<Text className='guest-feature-desc'>管理宝贝资料、绑定设备和休眠设置。</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View className='guest-action-card'>
|
<View className='guest-action-card'>
|
||||||
<Text className='guest-action-title'>准备绑定或管理设备</Text>
|
<Text className='guest-action-title'>准备绑定或管理设备</Text>
|
||||||
<Text className='guest-action-desc'>登录后可绑定设备,并在绑定流程中创建或选择孩子资料。</Text>
|
<Text className='guest-action-desc'>登录后可绑定设备,并在绑定流程中创建或选择宝贝资料。</Text>
|
||||||
<View className='guest-primary-btn' onClick={() => goLoginWithRedirect('/pages/device/index')}>
|
<View className='guest-primary-btn' onClick={() => goLoginWithRedirect('/pages/device/index')}>
|
||||||
<Text className='guest-primary-btn-text'>登录后继续</Text>
|
<Text className='guest-primary-btn-text'>登录后继续</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -338,14 +338,14 @@ export default function Device() {
|
|||||||
<View className='empty-robot'>
|
<View className='empty-robot'>
|
||||||
<Image className='robot-img' src={require('../../assets/tab-icons/robot.png')} mode='aspectFit' />
|
<Image className='robot-img' src={require('../../assets/tab-icons/robot.png')} mode='aspectFit' />
|
||||||
</View>
|
</View>
|
||||||
<Text className='empty-title'>{hasPendingDevice ? '设备已绑定,待关联孩子' : '先绑定一台伴伴设备'}</Text>
|
<Text className='empty-title'>{hasPendingDevice ? '设备已绑定,待关联宝贝' : '先绑定一台伴伴设备'}</Text>
|
||||||
<Text className='empty-subtitle'>
|
<Text className='empty-subtitle'>
|
||||||
{hasPendingDevice
|
{hasPendingDevice
|
||||||
? '这台设备还没有关联孩子,关联后即可查看定位、聊天和设备状态'
|
? '这台设备还没有关联宝贝,关联后即可查看定位、聊天和设备状态'
|
||||||
: '绑定时可以选择已有孩子,或直接新建孩子昵称'}
|
: '绑定时可以选择已有宝贝,或直接新建宝贝昵称'}
|
||||||
</Text>
|
</Text>
|
||||||
<View className='empty-btn' onClick={() => Taro.navigateTo({ url: hasPendingDevice ? '/pages/bind/index?mode=assign' : '/pages/bind/index' })}>
|
<View className='empty-btn' onClick={() => Taro.navigateTo({ url: hasPendingDevice ? '/pages/bind/index?mode=assign' : '/pages/bind/index' })}>
|
||||||
<Text className='empty-btn-text'>{hasPendingDevice ? '关联孩子' : '添加设备'}</Text>
|
<Text className='empty-btn-text'>{hasPendingDevice ? '关联宝贝' : '添加设备'}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{systemBanner}
|
{systemBanner}
|
||||||
@@ -367,7 +367,7 @@ export default function Device() {
|
|||||||
</View>
|
</View>
|
||||||
<View className='child-focus-info'>
|
<View className='child-focus-info'>
|
||||||
<Text className='child-focus-name'>{child.child_name}</Text>
|
<Text className='child-focus-name'>{child.child_name}</Text>
|
||||||
<Text className='child-focus-desc'>当前孩子已选中,还没有绑定设备</Text>
|
<Text className='child-focus-desc'>当前宝贝已选中,还没有绑定设备</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -375,14 +375,14 @@ export default function Device() {
|
|||||||
<View className='empty-robot'>
|
<View className='empty-robot'>
|
||||||
<Image className='robot-img' src={require('../../assets/tab-icons/robot.png')} mode='aspectFit' />
|
<Image className='robot-img' src={require('../../assets/tab-icons/robot.png')} mode='aspectFit' />
|
||||||
</View>
|
</View>
|
||||||
<Text className='empty-title'>{hasPendingDevice ? '有设备待关联' : '给当前孩子绑定一台设备'}</Text>
|
<Text className='empty-title'>{hasPendingDevice ? '有设备待关联' : '给当前宝贝绑定一台设备'}</Text>
|
||||||
<Text className='empty-subtitle'>
|
<Text className='empty-subtitle'>
|
||||||
{hasPendingDevice
|
{hasPendingDevice
|
||||||
? `设备 ${pendingBinding?.device_id} 还没有关联孩子,可以关联到当前孩子`
|
? `设备 ${pendingBinding?.device_id} 还没有关联宝贝,可以关联到当前宝贝`
|
||||||
: '绑定后,这个孩子的聊天记录、定位和设备状态都会显示在首页'}
|
: '绑定后,这个宝贝的聊天记录、定位和设备状态都会显示在首页'}
|
||||||
</Text>
|
</Text>
|
||||||
<View className='empty-btn' onClick={() => Taro.navigateTo({ url: hasPendingDevice ? '/pages/bind/index?mode=assign' : '/pages/bind/index' })}>
|
<View className='empty-btn' onClick={() => Taro.navigateTo({ url: hasPendingDevice ? '/pages/bind/index?mode=assign' : '/pages/bind/index' })}>
|
||||||
<Text className='empty-btn-text'>{hasPendingDevice ? '关联到当前孩子' : '去绑定设备'}</Text>
|
<Text className='empty-btn-text'>{hasPendingDevice ? '关联到当前宝贝' : '去绑定设备'}</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{systemBanner}
|
{systemBanner}
|
||||||
@@ -550,7 +550,7 @@ export default function Device() {
|
|||||||
<Text className='alarm-value'>{formatAlarmTime(latestAlarm.created_at)}</Text>
|
<Text className='alarm-value'>{formatAlarmTime(latestAlarm.created_at)}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='alarm-row'>
|
<View className='alarm-row'>
|
||||||
<Text className='alarm-label'>来源儿童</Text>
|
<Text className='alarm-label'>来源宝贝</Text>
|
||||||
<Text className='alarm-value'>{latestAlarmChildName}</Text>
|
<Text className='alarm-value'>{latestAlarmChildName}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='alarm-row'>
|
<View className='alarm-row'>
|
||||||
@@ -585,7 +585,7 @@ export default function Device() {
|
|||||||
</View>
|
</View>
|
||||||
<View className='alarm-history-meta'>
|
<View className='alarm-history-meta'>
|
||||||
<Text className='alarm-history-child'>
|
<Text className='alarm-history-child'>
|
||||||
来源儿童:{getAlarmChildLabel(alarm, childName)}
|
来源宝贝:{getAlarmChildLabel(alarm, childName)}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className='alarm-history-device'>{alarm.device_id}</Text>
|
<Text className='alarm-history-device'>{alarm.device_id}</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -621,7 +621,7 @@ export default function Device() {
|
|||||||
{isDetailExpanded && (
|
{isDetailExpanded && (
|
||||||
<View className='detail-body'>
|
<View className='detail-body'>
|
||||||
<View className='detail-row'>
|
<View className='detail-row'>
|
||||||
<Text className='detail-label'>当前儿童</Text>
|
<Text className='detail-label'>当前宝贝</Text>
|
||||||
<Text className='detail-value'>{childName}</Text>
|
<Text className='detail-value'>{childName}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='detail-row'>
|
<View className='detail-row'>
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ function buildLocationDetailLines(
|
|||||||
const lines: string[] = []
|
const lines: string[] = []
|
||||||
|
|
||||||
if (options?.includeIdentity) {
|
if (options?.includeIdentity) {
|
||||||
lines.push(`设备 ${point.device_id} · ${point.child_name || '未命名儿童'}`)
|
lines.push(`设备 ${point.device_id} · ${point.child_name || '未命名宝贝'}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.push(`上报时间 ${formatTime(point.device_time)}`)
|
lines.push(`上报时间 ${formatTime(point.device_time)}`)
|
||||||
@@ -177,10 +177,10 @@ export default function Location() {
|
|||||||
setSelectedPoint(null)
|
setSelectedPoint(null)
|
||||||
setCoordinates(DEFAULT_COORDINATES)
|
setCoordinates(DEFAULT_COORDINATES)
|
||||||
setEmptyState({
|
setEmptyState({
|
||||||
title: '还没有当前孩子',
|
title: '还没有当前宝贝',
|
||||||
desc: '请先去设置与管理页创建或选择一个孩子,再查看定位。',
|
desc: '请先绑定设备,并在绑定流程中创建或选择宝贝,再查看定位。',
|
||||||
actionText: '去设置与管理',
|
actionText: '去绑定设备',
|
||||||
actionUrl: '/pages/sleep/index',
|
actionUrl: '/pages/bind/index',
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -192,8 +192,8 @@ export default function Location() {
|
|||||||
setSelectedPoint(null)
|
setSelectedPoint(null)
|
||||||
setCoordinates(DEFAULT_COORDINATES)
|
setCoordinates(DEFAULT_COORDINATES)
|
||||||
setEmptyState({
|
setEmptyState({
|
||||||
title: '当前孩子还没有绑定设备',
|
title: '当前宝贝还没有绑定设备',
|
||||||
desc: '先给当前孩子绑定一台设备,定位和轨迹页才会有内容。',
|
desc: '先给当前宝贝绑定一台设备,定位和轨迹页才会有内容。',
|
||||||
actionText: '去绑定设备',
|
actionText: '去绑定设备',
|
||||||
actionUrl: '/pages/bind/index',
|
actionUrl: '/pages/bind/index',
|
||||||
})
|
})
|
||||||
@@ -397,7 +397,7 @@ export default function Location() {
|
|||||||
<View className='location-empty-shell'>
|
<View className='location-empty-shell'>
|
||||||
<View className='location-empty-card'>
|
<View className='location-empty-card'>
|
||||||
<Text className='location-empty-title'>设备定位与轨迹</Text>
|
<Text className='location-empty-title'>设备定位与轨迹</Text>
|
||||||
<Text className='location-empty-desc'>绑定设备后,可在这里查看孩子设备的位置、更新时间和定位精度。</Text>
|
<Text className='location-empty-desc'>绑定设备后,可在这里查看宝贝设备的位置、更新时间和定位精度。</Text>
|
||||||
<View
|
<View
|
||||||
className='location-empty-action'
|
className='location-empty-action'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -501,7 +501,7 @@ export default function Location() {
|
|||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View className='location-empty'>
|
<View className='location-empty'>
|
||||||
<Text className='location-empty-title'>{locationNotice || '当前孩子暂无设备位置'}</Text>
|
<Text className='location-empty-title'>{locationNotice || '当前宝贝暂无设备位置'}</Text>
|
||||||
{locationNotice && <Text className='location-empty-desc'>请确认设备已开机并保持网络可用。</Text>}
|
{locationNotice && <Text className='location-empty-desc'>请确认设备已开机并保持网络可用。</Text>}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
@@ -545,7 +545,7 @@ export default function Location() {
|
|||||||
<View className='location-empty'>
|
<View className='location-empty'>
|
||||||
<Text className='location-empty-title'>暂无轨迹</Text>
|
<Text className='location-empty-title'>暂无轨迹</Text>
|
||||||
<Text className='location-empty-desc'>
|
<Text className='location-empty-desc'>
|
||||||
{trajectoryMode === 'today' ? '当前孩子今天还没有新的历史点位。' : '当前孩子最近没有新的历史点位。'}
|
{trajectoryMode === 'today' ? '当前宝贝今天还没有新的历史点位。' : '当前宝贝最近没有新的历史点位。'}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export default function Login() {
|
|||||||
|
|
||||||
<View className='login-info-card'>
|
<View className='login-info-card'>
|
||||||
<Text className='login-info-title'>登录后启用设备管理</Text>
|
<Text className='login-info-title'>登录后启用设备管理</Text>
|
||||||
<Text className='login-info-desc'>仅在绑定设备、查看定位和管理孩子资料时需要登录。</Text>
|
<Text className='login-info-desc'>仅在绑定设备、查看定位和管理宝贝资料时需要登录。</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View className='login-btn-wrapper'>
|
<View className='login-btn-wrapper'>
|
||||||
|
|||||||
@@ -121,11 +121,11 @@ export default function SleepSchedule() {
|
|||||||
<View className='sleep-page'>
|
<View className='sleep-page'>
|
||||||
<View className='page-header'>
|
<View className='page-header'>
|
||||||
<Text className='page-title'>定时休眠</Text>
|
<Text className='page-title'>定时休眠</Text>
|
||||||
<Text className='page-subtitle'>先选择一个孩子,再为对应设备设置休眠时间</Text>
|
<Text className='page-subtitle'>先绑定设备并选择宝贝,再为对应设备设置休眠时间</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='empty-card'>
|
<View className='empty-card'>
|
||||||
<Text className='empty-title'>还没有当前孩子</Text>
|
<Text className='empty-title'>还没有当前宝贝</Text>
|
||||||
<Text className='empty-desc'>请先到管理页添加并选择孩子。</Text>
|
<Text className='empty-desc'>请先去首页绑定设备,并在绑定流程中创建或选择宝贝。</Text>
|
||||||
</View>
|
</View>
|
||||||
{systemBanner}
|
{systemBanner}
|
||||||
</View>
|
</View>
|
||||||
@@ -153,7 +153,7 @@ export default function SleepSchedule() {
|
|||||||
|
|
||||||
const currentRange = formatSleepRange(deviceStatus)
|
const currentRange = formatSleepRange(deviceStatus)
|
||||||
const timezoneText = deviceStatus?.timezone || 'Asia/Shanghai'
|
const timezoneText = deviceStatus?.timezone || 'Asia/Shanghai'
|
||||||
const childName = child.child_name || '当前孩子'
|
const childName = child.child_name || '当前宝贝'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='sleep-page'>
|
<View className='sleep-page'>
|
||||||
|
|||||||
@@ -628,6 +628,10 @@
|
|||||||
max-height: 74vh;
|
max-height: 74vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cards-modal-card {
|
||||||
|
max-height: 76vh;
|
||||||
|
}
|
||||||
|
|
||||||
.family-card {
|
.family-card {
|
||||||
max-height: 76vh;
|
max-height: 76vh;
|
||||||
}
|
}
|
||||||
@@ -677,6 +681,46 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cards-modal-summary {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 18px;
|
||||||
|
padding: 18px 20px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: #F8FAFC;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards-modal-device {
|
||||||
|
min-width: 0;
|
||||||
|
font-size: 26px;
|
||||||
|
color: #1F2937;
|
||||||
|
font-weight: 600;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards-modal-list {
|
||||||
|
max-height: 42vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards-modal-add {
|
||||||
|
height: 84px;
|
||||||
|
margin-top: 22px;
|
||||||
|
border-radius: 18px;
|
||||||
|
background: #FFF2E7;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards-modal-add-text {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #FF8C42;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
.device-switch-item {
|
.device-switch-item {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
@@ -790,6 +834,16 @@
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.device-switch-delete {
|
||||||
|
padding: 6px 14px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border: 2px solid #E5484D;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #E5484D;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.device-switch-name {
|
.device-switch-name {
|
||||||
font-size: 26px;
|
font-size: 26px;
|
||||||
color: #666666;
|
color: #666666;
|
||||||
|
|||||||
@@ -90,11 +90,11 @@ export default function Sleep() {
|
|||||||
const [familyInviteExpiresAt, setFamilyInviteExpiresAt] = useState('')
|
const [familyInviteExpiresAt, setFamilyInviteExpiresAt] = useState('')
|
||||||
const [showModal, setShowModal] = useState(false)
|
const [showModal, setShowModal] = useState(false)
|
||||||
const [showChildModal, setShowChildModal] = useState(false)
|
const [showChildModal, setShowChildModal] = useState(false)
|
||||||
const [showDeleteChildModal, setShowDeleteChildModal] = useState(false)
|
|
||||||
const [showRoleModal, setShowRoleModal] = useState(false)
|
const [showRoleModal, setShowRoleModal] = useState(false)
|
||||||
const [showFamilyModal, setShowFamilyModal] = useState(false)
|
const [showFamilyModal, setShowFamilyModal] = useState(false)
|
||||||
const [showFamilyNameModal, setShowFamilyNameModal] = useState(false)
|
const [showFamilyNameModal, setShowFamilyNameModal] = useState(false)
|
||||||
const [showAssignDeviceModal, setShowAssignDeviceModal] = useState(false)
|
const [showAssignDeviceModal, setShowAssignDeviceModal] = useState(false)
|
||||||
|
const [showCardsModal, setShowCardsModal] = useState(false)
|
||||||
const [showCardNameModal, setShowCardNameModal] = useState(false)
|
const [showCardNameModal, setShowCardNameModal] = useState(false)
|
||||||
const [childName, setChildName] = useState('')
|
const [childName, setChildName] = useState('')
|
||||||
const [editingChildId, setEditingChildId] = useState<number | null>(null)
|
const [editingChildId, setEditingChildId] = useState<number | null>(null)
|
||||||
@@ -126,7 +126,7 @@ export default function Sleep() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useShareAppMessage(() => {
|
useShareAppMessage(() => {
|
||||||
const targetName = currentChild?.child_name || binding?.device_id || '孩子'
|
const targetName = currentChild?.child_name || binding?.device_id || '宝贝'
|
||||||
return {
|
return {
|
||||||
title: `邀请你加入${targetName}的家庭共享`,
|
title: `邀请你加入${targetName}的家庭共享`,
|
||||||
path: familyInvitePath || '/pages/device/index',
|
path: familyInvitePath || '/pages/device/index',
|
||||||
@@ -567,7 +567,7 @@ export default function Sleep() {
|
|||||||
void loadCurrentDeviceCards(nextBinding?.device_id)
|
void loadCurrentDeviceCards(nextBinding?.device_id)
|
||||||
setShowChildModal(false)
|
setShowChildModal(false)
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '已切换当前孩子',
|
title: '已切换当前宝贝',
|
||||||
icon: 'success',
|
icon: 'success',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -621,16 +621,50 @@ export default function Sleep() {
|
|||||||
Taro.navigateTo({ url: '/pages/bind/index?mode=assign' })
|
Taro.navigateTo({ url: '/pages/bind/index?mode=assign' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDeleteUnassignedDevice = (targetBinding: BindingListItem) => {
|
||||||
|
if (!targetBinding?.device_id) return
|
||||||
|
|
||||||
|
Taro.showModal({
|
||||||
|
title: '删除设备',
|
||||||
|
content: `确定要从当前账号删除设备 ${targetBinding.device_id} 吗?删除后需要重新扫码绑定。`,
|
||||||
|
confirmText: '删除',
|
||||||
|
confirmColor: '#E5484D',
|
||||||
|
success: async (result) => {
|
||||||
|
if (!result.confirm) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
await unbindDevice(targetBinding.device_id)
|
||||||
|
if (binding?.device_id === targetBinding.device_id) {
|
||||||
|
clearSelectedBindingDeviceId()
|
||||||
|
}
|
||||||
|
Taro.showToast({ title: '已删除设备', icon: 'success' })
|
||||||
|
await loadData()
|
||||||
|
} catch (error: any) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: error?.message || '删除设备失败,请重试',
|
||||||
|
icon: 'none',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOpenAddCard = () => {
|
||||||
|
if (!binding?.device_id) return
|
||||||
|
setShowCardsModal(false)
|
||||||
|
Taro.navigateTo({ url: `/pages/bind/index?mode=card&device_id=${encodeURIComponent(binding.device_id)}` })
|
||||||
|
}
|
||||||
|
|
||||||
const handleSubmitModal = async () => {
|
const handleSubmitModal = async () => {
|
||||||
const normalizedName = childName.trim()
|
const normalizedName = childName.trim()
|
||||||
if (!normalizedName) {
|
if (!normalizedName) {
|
||||||
Taro.showToast({ title: '请输入儿童昵称', icon: 'none' })
|
Taro.showToast({ title: '请输入宝贝昵称', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!editingChildId) {
|
if (!editingChildId) {
|
||||||
Taro.showToast({ title: '当前没有可修改的孩子', icon: 'none' })
|
Taro.showToast({ title: '当前没有可修改的宝贝', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -651,11 +685,11 @@ export default function Sleep() {
|
|||||||
const childBinding = bindings.find((item) => item.child_id === child.child_id) || null
|
const childBinding = bindings.find((item) => item.child_id === child.child_id) || null
|
||||||
const isCurrentChild = currentChild?.child_id === child.child_id
|
const isCurrentChild = currentChild?.child_id === child.child_id
|
||||||
const content = childBinding?.device_id
|
const content = childBinding?.device_id
|
||||||
? '删除后,' + child.child_name + ' 将不再显示,设备 ' + childBinding.device_id + ' 会保留但不再分配给该孩子。确定删除吗?'
|
? '删除后,' + child.child_name + ' 将不再显示,设备 ' + childBinding.device_id + ' 会保留但不再分配给该宝贝。确定删除吗?'
|
||||||
: '删除后,' + child.child_name + ' 将不再显示。确定删除吗?'
|
: '删除后,' + child.child_name + ' 将不再显示。确定删除吗?'
|
||||||
|
|
||||||
Taro.showModal({
|
Taro.showModal({
|
||||||
title: '删除孩子',
|
title: '删除宝贝',
|
||||||
content,
|
content,
|
||||||
confirmText: '删除',
|
confirmText: '删除',
|
||||||
confirmColor: '#E5484D',
|
confirmColor: '#E5484D',
|
||||||
@@ -670,7 +704,7 @@ export default function Sleep() {
|
|||||||
if (isCurrentChild && childBinding?.device_id && binding?.device_id === childBinding.device_id) {
|
if (isCurrentChild && childBinding?.device_id && binding?.device_id === childBinding.device_id) {
|
||||||
clearSelectedBindingDeviceId()
|
clearSelectedBindingDeviceId()
|
||||||
}
|
}
|
||||||
setShowDeleteChildModal(false)
|
setShowChildModal(false)
|
||||||
Taro.showToast({ title: '已删除', icon: 'success' })
|
Taro.showToast({ title: '已删除', icon: 'success' })
|
||||||
await loadData()
|
await loadData()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@@ -885,16 +919,11 @@ export default function Sleep() {
|
|||||||
const handleMenuClick = (item: MenuItem) => {
|
const handleMenuClick = (item: MenuItem) => {
|
||||||
if (item.disabled) return
|
if (item.disabled) return
|
||||||
|
|
||||||
if (item.name === '当前孩子') {
|
if (item.name === '当前宝贝') {
|
||||||
setShowChildModal(true)
|
setShowChildModal(true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.name === '删除孩子') {
|
|
||||||
setShowDeleteChildModal(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.name === '关联现有设备') {
|
if (item.name === '关联现有设备') {
|
||||||
setShowAssignDeviceModal(true)
|
setShowAssignDeviceModal(true)
|
||||||
return
|
return
|
||||||
@@ -905,9 +934,8 @@ export default function Sleep() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.name === '添加设备卡') {
|
if (item.name === '我的卡片') {
|
||||||
if (!binding?.device_id) return
|
setShowCardsModal(true)
|
||||||
Taro.navigateTo({ url: `/pages/bind/index?mode=card&device_id=${encodeURIComponent(binding.device_id)}` })
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -951,8 +979,8 @@ export default function Sleep() {
|
|||||||
|
|
||||||
<View className='guest-manage-card'>
|
<View className='guest-manage-card'>
|
||||||
<Text className='guest-manage-icon'>⚙️</Text>
|
<Text className='guest-manage-icon'>⚙️</Text>
|
||||||
<Text className='guest-manage-title'>管理孩子资料和设备</Text>
|
<Text className='guest-manage-title'>管理宝贝资料和设备</Text>
|
||||||
<Text className='guest-manage-desc'>登录后可创建孩子资料、绑定伴伴设备、管理卡片、设置休眠时间和家庭成员。</Text>
|
<Text className='guest-manage-desc'>登录后可创建宝贝资料、绑定伴伴设备、管理卡片、设置休眠时间和家庭成员。</Text>
|
||||||
<View
|
<View
|
||||||
className='guest-manage-btn'
|
className='guest-manage-btn'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -966,8 +994,8 @@ export default function Sleep() {
|
|||||||
|
|
||||||
<View className='guest-manage-list'>
|
<View className='guest-manage-list'>
|
||||||
<View className='guest-manage-item'>
|
<View className='guest-manage-item'>
|
||||||
<Text className='guest-manage-item-title'>孩子资料</Text>
|
<Text className='guest-manage-item-title'>宝贝资料</Text>
|
||||||
<Text className='guest-manage-item-desc'>为不同孩子维护独立设备和会话。</Text>
|
<Text className='guest-manage-item-desc'>为不同宝贝维护独立设备和会话。</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='guest-manage-item'>
|
<View className='guest-manage-item'>
|
||||||
<Text className='guest-manage-item-title'>设备绑定</Text>
|
<Text className='guest-manage-item-title'>设备绑定</Text>
|
||||||
@@ -1044,22 +1072,19 @@ export default function Sleep() {
|
|||||||
: firmwareStatus?.update_available
|
: firmwareStatus?.update_available
|
||||||
? '发现可更新版本'
|
? '发现可更新版本'
|
||||||
: '当前设备版本状态'
|
: '当前设备版本状态'
|
||||||
|
const cardValue = !binding?.device_id
|
||||||
|
? '未绑定'
|
||||||
|
: isLoadingCards
|
||||||
|
? '查询中'
|
||||||
|
: `${currentCardGroup?.card_count || 0} 张`
|
||||||
const menuItems: MenuItem[] = [
|
const menuItems: MenuItem[] = [
|
||||||
{
|
{
|
||||||
icon: require('../../assets/tab-icons/orange-robot.png'),
|
icon: require('../../assets/tab-icons/orange-robot.png'),
|
||||||
iconBgClass: 'orange',
|
iconBgClass: 'orange',
|
||||||
name: '当前孩子',
|
name: '当前宝贝',
|
||||||
value: currentChildName,
|
value: currentChildName,
|
||||||
arrow: true,
|
arrow: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
icon: require('../../assets/tab-icons/broken-rings.png'),
|
|
||||||
iconBgClass: 'red',
|
|
||||||
name: '删除孩子',
|
|
||||||
value: children.length > 0 ? `${children.length} 个可选` : '未添加',
|
|
||||||
arrow: true,
|
|
||||||
disabled: children.length === 0,
|
|
||||||
},
|
|
||||||
...(unassignedBindings.length > 0
|
...(unassignedBindings.length > 0
|
||||||
? [{
|
? [{
|
||||||
icon: require('../../assets/tab-icons/rings.png'),
|
icon: require('../../assets/tab-icons/rings.png'),
|
||||||
@@ -1079,8 +1104,8 @@ export default function Sleep() {
|
|||||||
{
|
{
|
||||||
icon: require('../../assets/tab-icons/rings.png'),
|
icon: require('../../assets/tab-icons/rings.png'),
|
||||||
iconBgClass: 'green',
|
iconBgClass: 'green',
|
||||||
name: '添加设备卡',
|
name: '我的卡片',
|
||||||
value: binding?.device_id ? '给当前设备加卡' : '未绑定',
|
value: cardValue,
|
||||||
arrow: true,
|
arrow: true,
|
||||||
disabled: !binding,
|
disabled: !binding,
|
||||||
},
|
},
|
||||||
@@ -1155,11 +1180,11 @@ export default function Sleep() {
|
|||||||
|
|
||||||
<View className='summary-card'>
|
<View className='summary-card'>
|
||||||
<View className='summary-row'>
|
<View className='summary-row'>
|
||||||
<Text className='summary-label'>儿童资料数</Text>
|
<Text className='summary-label'>宝贝资料数</Text>
|
||||||
<Text className='summary-value'>{children.length} 个</Text>
|
<Text className='summary-value'>{children.length} 个</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='summary-row'>
|
<View className='summary-row'>
|
||||||
<Text className='summary-label'>当前儿童</Text>
|
<Text className='summary-label'>当前宝贝</Text>
|
||||||
<Text className='summary-value'>{currentChildName}</Text>
|
<Text className='summary-value'>{currentChildName}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='summary-row'>
|
<View className='summary-row'>
|
||||||
@@ -1197,50 +1222,6 @@ export default function Sleep() {
|
|||||||
</View>
|
</View>
|
||||||
</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'>
|
<View className='menu-card'>
|
||||||
{menuItems.map((item, index) => (
|
{menuItems.map((item, index) => (
|
||||||
<View key={index}>
|
<View key={index}>
|
||||||
@@ -1279,9 +1260,9 @@ export default function Sleep() {
|
|||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text className='modal-title'>切换当前孩子</Text>
|
<Text className='modal-title'>切换当前宝贝</Text>
|
||||||
{children.length === 0 ? (
|
{children.length === 0 ? (
|
||||||
<Text className='device-switch-empty'>当前还没有儿童资料</Text>
|
<Text className='device-switch-empty'>当前还没有宝贝资料</Text>
|
||||||
) : (
|
) : (
|
||||||
<View className='device-switch-list'>
|
<View className='device-switch-list'>
|
||||||
{children.map((item) => {
|
{children.map((item) => {
|
||||||
@@ -1308,6 +1289,15 @@ export default function Sleep() {
|
|||||||
修改
|
修改
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
<Text
|
||||||
|
className='device-switch-delete'
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
handleDeleteChild(item)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Text className='device-switch-name'>{itemBinding?.device_id || '未绑定设备'}</Text>
|
<Text className='device-switch-name'>{itemBinding?.device_id || '未绑定设备'}</Text>
|
||||||
@@ -1325,50 +1315,6 @@ export default function Sleep() {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showDeleteChildModal && (
|
|
||||||
<View className='modal-mask' onClick={() => setShowDeleteChildModal(false)}>
|
|
||||||
<View
|
|
||||||
className='modal-card device-switch-card'
|
|
||||||
onClick={(event) => {
|
|
||||||
event.stopPropagation()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text className='modal-title'>选择要删除的孩子</Text>
|
|
||||||
{children.length === 0 ? (
|
|
||||||
<Text className='device-switch-empty'>当前还没有儿童资料</Text>
|
|
||||||
) : (
|
|
||||||
<View className='device-switch-list'>
|
|
||||||
{children.map((item) => {
|
|
||||||
const isActive = currentChild?.child_id === item.child_id
|
|
||||||
const itemBinding = bindings.find((bindingItem) => bindingItem.child_id === item.child_id) || null
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
key={item.child_id}
|
|
||||||
className='device-switch-item delete-child-item'
|
|
||||||
onClick={() => handleDeleteChild(item)}
|
|
||||||
>
|
|
||||||
<View className='device-switch-head'>
|
|
||||||
<Text className='device-switch-id'>{item.child_name}</Text>
|
|
||||||
<View className='device-switch-tags'>
|
|
||||||
{isActive && <Text className='device-switch-tag'>当前</Text>}
|
|
||||||
<Text className='device-switch-tag danger'>删除</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<Text className='device-switch-name'>{itemBinding?.device_id || '未绑定设备'}</Text>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
<View className='modal-actions'>
|
|
||||||
<Text className='modal-action cancel' onClick={() => setShowDeleteChildModal(false)}>
|
|
||||||
关闭
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{showAssignDeviceModal && (
|
{showAssignDeviceModal && (
|
||||||
<View className='modal-mask' onClick={() => setShowAssignDeviceModal(false)}>
|
<View className='modal-mask' onClick={() => setShowAssignDeviceModal(false)}>
|
||||||
<View
|
<View
|
||||||
@@ -1390,9 +1336,20 @@ export default function Sleep() {
|
|||||||
>
|
>
|
||||||
<View className='device-switch-head'>
|
<View className='device-switch-head'>
|
||||||
<Text className='device-switch-id'>{item.device_id}</Text>
|
<Text className='device-switch-id'>{item.device_id}</Text>
|
||||||
<Text className='device-switch-tag'>待关联</Text>
|
<View className='device-switch-tags'>
|
||||||
|
<Text className='device-switch-tag'>待关联</Text>
|
||||||
|
<Text
|
||||||
|
className='device-switch-delete'
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
handleDeleteUnassignedDevice(item)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Text className='device-switch-name'>未关联孩子</Text>
|
<Text className='device-switch-name'>未关联宝贝</Text>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
@@ -1406,15 +1363,78 @@ export default function Sleep() {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showCardsModal && (
|
||||||
|
<View className='modal-mask' onClick={() => setShowCardsModal(false)}>
|
||||||
|
<View
|
||||||
|
className='modal-card cards-modal-card'
|
||||||
|
onClick={(event) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text className='modal-title'>我的卡片</Text>
|
||||||
|
<View className='cards-modal-summary'>
|
||||||
|
<Text className='cards-modal-device'>{binding?.device_id || '当前未绑定设备'}</Text>
|
||||||
|
<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 cards-modal-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>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{binding?.device_id && (
|
||||||
|
<View className='cards-modal-add' onClick={handleOpenAddCard}>
|
||||||
|
<Text className='cards-modal-add-text'>添加设备卡片</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<View className='modal-actions'>
|
||||||
|
<Text className='modal-action cancel' onClick={() => setShowCardsModal(false)}>
|
||||||
|
关闭
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{showModal && (
|
{showModal && (
|
||||||
<View className='modal-mask'>
|
<View className='modal-mask'>
|
||||||
<View className='modal-card'>
|
<View className='modal-card'>
|
||||||
<Text className='modal-title'>修改儿童昵称</Text>
|
<Text className='modal-title'>修改宝贝昵称</Text>
|
||||||
<View className='modal-input-wrap'>
|
<View className='modal-input-wrap'>
|
||||||
<Input
|
<Input
|
||||||
className='modal-input'
|
className='modal-input'
|
||||||
value={childName}
|
value={childName}
|
||||||
placeholder='请输入儿童昵称'
|
placeholder='请输入宝贝昵称'
|
||||||
focus
|
focus
|
||||||
onInput={(event) => setChildName(event.detail.value)}
|
onInput={(event) => setChildName(event.detail.value)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -178,13 +178,13 @@ interface BindingContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const PEER_META: Record<PeerKind, { icon: string; label: string; description: string }> = {
|
const PEER_META: Record<PeerKind, { icon: string; label: string; description: string }> = {
|
||||||
child: { icon: '🧒', label: '儿童', description: '孩子正在交流的儿童玩伴' },
|
child: { icon: '🧒', label: '宝贝', description: '宝贝正在交流的玩伴' },
|
||||||
parent: { icon: '👨', label: '家长', description: '家长通过微信小程序和孩子沟通' },
|
parent: { icon: '👨', label: '家长', description: '家长通过微信小程序和宝贝沟通' },
|
||||||
ai: { icon: '🤖', label: 'AI', description: '孩子与 AI 的历史交流' },
|
ai: { icon: '🤖', label: 'AI', description: '宝贝与 AI 的历史交流' },
|
||||||
}
|
}
|
||||||
|
|
||||||
const AI_ROLE_META: Record<string, { name: string; icon: string; description: string }> = {
|
const AI_ROLE_META: Record<string, { name: string; icon: string; description: string }> = {
|
||||||
assistant: { name: 'AI', icon: '🤖', description: '孩子与 AI 的历史交流' },
|
assistant: { name: 'AI', icon: '🤖', description: '宝贝与 AI 的历史交流' },
|
||||||
}
|
}
|
||||||
|
|
||||||
type AiMeta = { name: string; icon: string; description: string }
|
type AiMeta = { name: string; icon: string; description: string }
|
||||||
@@ -260,14 +260,14 @@ function getAiMeta(roleKey?: string, roleName?: string | null, roleDescription?:
|
|||||||
return {
|
return {
|
||||||
name: normalizedRoleName,
|
name: normalizedRoleName,
|
||||||
icon: '🤖',
|
icon: '🤖',
|
||||||
description: normalizedRoleDescription || `孩子与 ${normalizedRoleName} 的历史交流`,
|
description: normalizedRoleDescription || `宝贝与 ${normalizedRoleName} 的历史交流`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (roleKey && AI_ROLE_META[roleKey]) return AI_ROLE_META[roleKey]
|
if (roleKey && AI_ROLE_META[roleKey]) return AI_ROLE_META[roleKey]
|
||||||
return {
|
return {
|
||||||
name: roleKey || 'AI',
|
name: roleKey || 'AI',
|
||||||
icon: '🤖',
|
icon: '🤖',
|
||||||
description: `孩子与 ${roleKey || 'AI'} 的历史交流`,
|
description: `宝贝与 ${roleKey || 'AI'} 的历史交流`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,7 +288,7 @@ async function getRoleMetaMap(): Promise<Record<string, AiMeta>> {
|
|||||||
|
|
||||||
function getParticipantDisplayName(type: string, id?: string, name?: string | null): string {
|
function getParticipantDisplayName(type: string, id?: string, name?: string | null): string {
|
||||||
if (name && name.trim()) return name.trim()
|
if (name && name.trim()) return name.trim()
|
||||||
if (type === 'child') return id ? `儿童 ${id}` : '儿童'
|
if (type === 'child') return id ? `宝贝 ${id}` : '宝贝'
|
||||||
if (type === 'parent') return id ? `家长 ${id}` : '家长'
|
if (type === 'parent') return id ? `家长 ${id}` : '家长'
|
||||||
return id || 'AI'
|
return id || 'AI'
|
||||||
}
|
}
|
||||||
@@ -400,8 +400,8 @@ function toImConversation(item: ChildConversationItem, context: BindingContext):
|
|||||||
typeLabel: isParentConversation ? (canSend ? '家长沟通' : '留言记录') : meta.label,
|
typeLabel: isParentConversation ? (canSend ? '家长沟通' : '留言记录') : meta.label,
|
||||||
description: isParentConversation
|
description: isParentConversation
|
||||||
? canSend
|
? canSend
|
||||||
? '家长通过微信小程序和孩子直接沟通'
|
? '家长通过微信小程序和宝贝直接沟通'
|
||||||
: '其他家长和孩子的留言记录'
|
: '其他家长和宝贝的留言记录'
|
||||||
: meta.description,
|
: meta.description,
|
||||||
lastMessage: item.last_message_preview || (isParentConversation ? (canSend ? '还没有消息,点进去发送第一条' : '暂无留言记录') : '暂无消息'),
|
lastMessage: item.last_message_preview || (isParentConversation ? (canSend ? '还没有消息,点进去发送第一条' : '暂无留言记录') : '暂无消息'),
|
||||||
time: formatListTime(sortAt),
|
time: formatListTime(sortAt),
|
||||||
@@ -457,7 +457,7 @@ function createSyntheticParentConversation(context: BindingContext): ChatConvers
|
|||||||
name: '家长沟通',
|
name: '家长沟通',
|
||||||
avatar: PEER_META.parent.icon,
|
avatar: PEER_META.parent.icon,
|
||||||
typeLabel: '家长沟通',
|
typeLabel: '家长沟通',
|
||||||
description: '家长通过微信小程序和孩子直接沟通',
|
description: '家长通过微信小程序和宝贝直接沟通',
|
||||||
lastMessage: '还没有留言,点进去发送第一条',
|
lastMessage: '还没有留言,点进去发送第一条',
|
||||||
time: '',
|
time: '',
|
||||||
sortAt: '',
|
sortAt: '',
|
||||||
@@ -533,8 +533,8 @@ export async function getMessages(
|
|||||||
senderId: item.is_user ? context.deviceId : item.role_key,
|
senderId: item.is_user ? context.deviceId : item.role_key,
|
||||||
receiverType: item.is_user ? 'ai' : 'device',
|
receiverType: item.is_user ? 'ai' : 'device',
|
||||||
receiverId: item.is_user ? item.role_key : context.deviceId,
|
receiverId: item.is_user ? item.role_key : context.deviceId,
|
||||||
senderName: item.is_user ? context.childName || '孩子设备' : meta.name,
|
senderName: item.is_user ? context.childName || '宝贝设备' : meta.name,
|
||||||
receiverName: item.is_user ? meta.name : context.childName || '孩子设备',
|
receiverName: item.is_user ? meta.name : context.childName || '宝贝设备',
|
||||||
channelLabel: 'AI',
|
channelLabel: 'AI',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -569,13 +569,13 @@ export async function getMessages(
|
|||||||
item.sender_type === 'parent' && item.sender_id === String(context.currentUserId)
|
item.sender_type === 'parent' && item.sender_id === String(context.currentUserId)
|
||||||
? '我'
|
? '我'
|
||||||
: item.sender_type === 'child' && item.sender_id === String(context.childId || '')
|
: item.sender_type === 'child' && item.sender_id === String(context.childId || '')
|
||||||
? context.childName || item.sender_name_snapshot || '孩子'
|
? context.childName || item.sender_name_snapshot || '宝贝'
|
||||||
: getParticipantDisplayName(item.sender_type, item.sender_id, item.sender_name_snapshot),
|
: getParticipantDisplayName(item.sender_type, item.sender_id, item.sender_name_snapshot),
|
||||||
receiverName:
|
receiverName:
|
||||||
item.receiver_type === 'parent' && item.receiver_id === String(context.currentUserId)
|
item.receiver_type === 'parent' && item.receiver_id === String(context.currentUserId)
|
||||||
? '我'
|
? '我'
|
||||||
: item.receiver_type === 'child' && item.receiver_id === String(context.childId || '')
|
: item.receiver_type === 'child' && item.receiver_id === String(context.childId || '')
|
||||||
? context.childName || item.receiver_name_snapshot || '孩子'
|
? context.childName || item.receiver_name_snapshot || '宝贝'
|
||||||
: getParticipantDisplayName(item.receiver_type, item.receiver_id, item.receiver_name_snapshot),
|
: getParticipantDisplayName(item.receiver_type, item.receiver_id, item.receiver_name_snapshot),
|
||||||
channelLabel:
|
channelLabel:
|
||||||
isParentConversation && (item.sender_type === 'parent' || item.receiver_type === 'parent')
|
isParentConversation && (item.sender_type === 'parent' || item.receiver_type === 'parent')
|
||||||
@@ -644,7 +644,7 @@ export async function getCharacter(options: {
|
|||||||
return {
|
return {
|
||||||
id: conversationId,
|
id: conversationId,
|
||||||
name: normalizedName,
|
name: normalizedName,
|
||||||
description: canSendParentMessage ? '微信小程序 <-> 儿童设备' : '其他家长和孩子的留言记录',
|
description: canSendParentMessage ? '微信小程序 <-> 宝贝设备' : '其他家长和宝贝的留言记录',
|
||||||
icon: PEER_META.parent.icon,
|
icon: PEER_META.parent.icon,
|
||||||
peerKind: 'parent',
|
peerKind: 'parent',
|
||||||
source,
|
source,
|
||||||
@@ -663,7 +663,7 @@ export async function getCharacter(options: {
|
|||||||
return {
|
return {
|
||||||
id: conversationId,
|
id: conversationId,
|
||||||
name: getPeerDisplayName(peerKind, peerId, name),
|
name: getPeerDisplayName(peerKind, peerId, name),
|
||||||
description: peerKind === 'child' ? '儿童设备间的聊天记录' : meta.description,
|
description: peerKind === 'child' ? '宝贝设备间的聊天记录' : meta.description,
|
||||||
icon: meta.icon,
|
icon: meta.icon,
|
||||||
peerKind,
|
peerKind,
|
||||||
source,
|
source,
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ function getAlarmChildLabel(alarm: DeviceAlarmItem, fallbackChildName?: string |
|
|||||||
const fallbackName = String(fallbackChildName || '').trim()
|
const fallbackName = String(fallbackChildName || '').trim()
|
||||||
if (fallbackName) return fallbackName
|
if (fallbackName) return fallbackName
|
||||||
|
|
||||||
if (alarm.child_id) return `儿童 ${alarm.child_id}`
|
if (alarm.child_id) return `宝贝 ${alarm.child_id}`
|
||||||
return '当前孩子'
|
return '当前宝贝'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAlarmTypeLabel(sourceMsgId?: string | null): string {
|
function getAlarmTypeLabel(sourceMsgId?: string | null): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user