add 小程序支持角色切换
This commit is contained in:
@@ -380,6 +380,10 @@
|
||||
max-height: 70vh;
|
||||
}
|
||||
|
||||
.role-switch-card {
|
||||
max-height: 74vh;
|
||||
}
|
||||
|
||||
.device-switch-empty {
|
||||
display: block;
|
||||
font-size: 28px;
|
||||
@@ -392,6 +396,11 @@
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.role-switch-list {
|
||||
max-height: 56vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.device-switch-footer {
|
||||
margin-top: 20px;
|
||||
}
|
||||
@@ -424,6 +433,48 @@
|
||||
}
|
||||
}
|
||||
|
||||
.role-switch-item {
|
||||
padding: 24px;
|
||||
border-radius: 18px;
|
||||
background: #F7F8FA;
|
||||
margin-bottom: 16px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: #FFF2E7;
|
||||
border: 2px solid #FFBE94;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.58;
|
||||
}
|
||||
}
|
||||
|
||||
.role-switch-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.role-switch-name {
|
||||
min-width: 0;
|
||||
font-size: 30px;
|
||||
color: #1A1A1A;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.role-switch-desc {
|
||||
display: block;
|
||||
font-size: 25px;
|
||||
line-height: 1.55;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.device-switch-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -10,7 +10,16 @@ import {
|
||||
unbindDevice,
|
||||
} from '@/services/binding'
|
||||
import { Child, clearSelectedChildId, createChild, setSelectedChildId, updateChild } from '@/services/child'
|
||||
import { DeviceFirmwareStatus, getDeviceFirmwareStatus, startDeviceFirmwareUpdate } from '@/services/device'
|
||||
import {
|
||||
DeviceCurrentRole,
|
||||
DeviceFirmwareStatus,
|
||||
DeviceRoleSummary,
|
||||
getDeviceFirmwareStatus,
|
||||
getDeviceRole,
|
||||
getDeviceRoles,
|
||||
startDeviceFirmwareUpdate,
|
||||
updateDeviceRole,
|
||||
} from '@/services/device'
|
||||
import { useSystemBanner } from '@/components/system-banner/use-system-banner'
|
||||
import './index.scss'
|
||||
|
||||
@@ -31,10 +40,15 @@ export default function Sleep() {
|
||||
const [binding, setBinding] = useState<BindingListItem | null>(null)
|
||||
const [currentChild, setCurrentChild] = useState<Child | null>(null)
|
||||
const [firmwareStatus, setFirmwareStatus] = useState<DeviceFirmwareStatus | null>(null)
|
||||
const [deviceRole, setDeviceRole] = useState<DeviceCurrentRole | null>(null)
|
||||
const [roles, setRoles] = useState<DeviceRoleSummary[]>([])
|
||||
const [isLoadingFirmware, setIsLoadingFirmware] = useState(false)
|
||||
const [isUpdatingFirmware, setIsUpdatingFirmware] = useState(false)
|
||||
const [isLoadingRoles, setIsLoadingRoles] = useState(false)
|
||||
const [isUpdatingRole, setIsUpdatingRole] = useState(false)
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const [showChildModal, setShowChildModal] = useState(false)
|
||||
const [showRoleModal, setShowRoleModal] = useState(false)
|
||||
const [modalType, setModalType] = useState<'add' | 'edit'>('add')
|
||||
const [childName, setChildName] = useState('')
|
||||
const [editingChildId, setEditingChildId] = useState<number | null>(null)
|
||||
@@ -60,6 +74,7 @@ export default function Sleep() {
|
||||
setBinding(nextBinding)
|
||||
setParentInfo(Taro.getStorageSync('userInfo') || {})
|
||||
void loadFirmwareStatus(nextBinding?.device_id)
|
||||
void loadRoleData(nextBinding?.device_id)
|
||||
} catch (error: any) {
|
||||
console.error('[manage] load failed:', error)
|
||||
Taro.showToast({
|
||||
@@ -97,6 +112,36 @@ export default function Sleep() {
|
||||
}
|
||||
}
|
||||
|
||||
const loadRoleData = async (deviceId?: string | null) => {
|
||||
const normalizedDeviceId = String(deviceId || '').trim()
|
||||
if (!normalizedDeviceId) {
|
||||
setDeviceRole(null)
|
||||
setRoles([])
|
||||
setIsLoadingRoles(false)
|
||||
return
|
||||
}
|
||||
|
||||
setIsLoadingRoles(true)
|
||||
try {
|
||||
const [nextRoles, nextDeviceRole] = await Promise.all([
|
||||
getDeviceRoles(),
|
||||
getDeviceRole(normalizedDeviceId),
|
||||
])
|
||||
setRoles(nextRoles)
|
||||
setDeviceRole(nextDeviceRole)
|
||||
} catch (error: any) {
|
||||
console.error('[manage] role load failed:', error)
|
||||
setRoles([])
|
||||
setDeviceRole(null)
|
||||
Taro.showToast({
|
||||
title: error?.message || '角色信息加载失败',
|
||||
icon: 'none',
|
||||
})
|
||||
} finally {
|
||||
setIsLoadingRoles(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleOpenModal = (type: 'add' | 'edit', child?: Child) => {
|
||||
setModalType(type)
|
||||
setChildName(child?.child_name || '')
|
||||
@@ -121,6 +166,7 @@ export default function Sleep() {
|
||||
setCurrentChild(child)
|
||||
setBinding(nextBinding)
|
||||
void loadFirmwareStatus(nextBinding?.device_id)
|
||||
void loadRoleData(nextBinding?.device_id)
|
||||
setShowChildModal(false)
|
||||
Taro.showToast({
|
||||
title: '已切换当前孩子',
|
||||
@@ -192,6 +238,37 @@ export default function Sleep() {
|
||||
})
|
||||
}
|
||||
|
||||
const handleOpenRoleModal = () => {
|
||||
if (!binding?.device_id) {
|
||||
Taro.showToast({ title: '当前没有可用设备', icon: 'none' })
|
||||
return
|
||||
}
|
||||
setShowRoleModal(true)
|
||||
if (roles.length === 0) {
|
||||
void loadRoleData(binding.device_id)
|
||||
}
|
||||
}
|
||||
|
||||
const handleSelectRole = async (role: DeviceRoleSummary) => {
|
||||
if (!binding?.device_id || isUpdatingRole) return
|
||||
|
||||
setIsUpdatingRole(true)
|
||||
try {
|
||||
const language = deviceRole?.preferred_language || role.default_language || role.languages?.[0] || null
|
||||
const nextRole = await updateDeviceRole(role.role_key, language, binding.device_id)
|
||||
setDeviceRole(nextRole)
|
||||
setShowRoleModal(false)
|
||||
Taro.showToast({ title: 'AI 角色已切换', icon: 'success' })
|
||||
} catch (error: any) {
|
||||
Taro.showToast({
|
||||
title: error?.message || '角色切换失败',
|
||||
icon: 'none',
|
||||
})
|
||||
} finally {
|
||||
setIsUpdatingRole(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleUnbind = () => {
|
||||
if (!binding) return
|
||||
|
||||
@@ -268,6 +345,11 @@ export default function Sleep() {
|
||||
return
|
||||
}
|
||||
|
||||
if (item.name === 'AI 角色') {
|
||||
handleOpenRoleModal()
|
||||
return
|
||||
}
|
||||
|
||||
if (item.name === '解除设备绑定') {
|
||||
handleUnbind()
|
||||
}
|
||||
@@ -289,6 +371,11 @@ export default function Sleep() {
|
||||
const latestFirmwareLabel = firmwareStatus?.latest_version || '--'
|
||||
const canUpdateFirmware = Boolean(binding?.device_id && firmwareStatus?.can_update && !isLoadingFirmware && !isUpdatingFirmware)
|
||||
const firmwareActionText = isLoadingFirmware ? '查询中' : isUpdatingFirmware ? '发送中' : '更新'
|
||||
const roleValue = !binding?.device_id
|
||||
? '未绑定'
|
||||
: isLoadingRoles
|
||||
? '查询中'
|
||||
: deviceRole?.name || '未设置'
|
||||
const firmwareSubtitle = !binding?.device_id
|
||||
? '绑定设备后可查看系统版本'
|
||||
: isLoadingFirmware
|
||||
@@ -325,6 +412,14 @@ export default function Sleep() {
|
||||
value: binding?.device_id ? `当前: ${binding.device_id}` : '未绑定',
|
||||
arrow: true,
|
||||
},
|
||||
{
|
||||
icon: require('../../assets/tab-icons/orange-robot.png'),
|
||||
iconBgClass: 'green',
|
||||
name: 'AI 角色',
|
||||
value: roleValue,
|
||||
arrow: true,
|
||||
disabled: !binding,
|
||||
},
|
||||
{
|
||||
icon: require('../../assets/tab-icons/broken-rings.png'),
|
||||
iconBgClass: 'red',
|
||||
@@ -501,6 +596,49 @@ export default function Sleep() {
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{showRoleModal && (
|
||||
<View className='modal-mask' onClick={() => setShowRoleModal(false)}>
|
||||
<View
|
||||
className='modal-card role-switch-card'
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
}}
|
||||
>
|
||||
<Text className='modal-title'>切换 AI 角色</Text>
|
||||
{isLoadingRoles ? (
|
||||
<Text className='device-switch-empty'>角色加载中...</Text>
|
||||
) : roles.length === 0 ? (
|
||||
<Text className='device-switch-empty'>当前没有可用角色</Text>
|
||||
) : (
|
||||
<View className='role-switch-list'>
|
||||
{roles.map((item) => {
|
||||
const isActive = deviceRole?.role_key === item.role_key
|
||||
return (
|
||||
<View
|
||||
key={item.role_key}
|
||||
className={`role-switch-item ${isActive ? 'active' : ''} ${isUpdatingRole ? 'disabled' : ''}`}
|
||||
onClick={isUpdatingRole ? undefined : () => handleSelectRole(item)}
|
||||
>
|
||||
<View className='role-switch-head'>
|
||||
<Text className='role-switch-name'>{item.name || item.role_key}</Text>
|
||||
{isActive && <Text className='device-switch-tag'>当前</Text>}
|
||||
</View>
|
||||
<Text className='role-switch-desc'>
|
||||
{item.description || `角色标识:${item.role_key}`}
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
)}
|
||||
<View className='modal-actions'>
|
||||
<Text className='modal-action cancel' onClick={() => setShowRoleModal(false)}>
|
||||
关闭
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{systemBanner}
|
||||
</View>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user