refactor(device): 解绑入口从列表卡片底部上移至详情页

- 设备列表移除卡片底部"解绑"通栏按钮与配套样式,
  解绑操作改为进入详情页底部 ListRow 触发,避免与卡片点击冲突
- 详情页新增 confirmUnbind/showModal 防误绑流程,成功后停轮询
  返回列表页;unbindHomeDevice 调用从列表页迁入详情页
This commit is contained in:
ozh 2026-09-21 15:41:56 +08:00
parent 213d61bbf6
commit a4b2e13a8e
3 changed files with 37 additions and 61 deletions

View File

@ -6,10 +6,11 @@ import { Navbar } from '@/components/navbar'
import { ListRow, ListRowGroup } from '@/components/list-row' import { ListRow, ListRowGroup } from '@/components/list-row'
import { VitalWave } from '@/components/vital-wave' import { VitalWave } from '@/components/vital-wave'
import { ROUTES } from '@/constants/routes' import { ROUTES } from '@/constants/routes'
import { navigateTo } from '@/utils/navigation' import { navigateTo, navigateBack } from '@/utils/navigation'
import { showToast } from '@/utils/toast'
import { MOVE_STATE_MAP, BODY_STATUS_MAP, ONBED_STATUS_MAP } from '@/constants/enums' import { MOVE_STATE_MAP, BODY_STATUS_MAP, ONBED_STATUS_MAP } from '@/constants/enums'
import { import {
getHomeDeviceDetail, getVitalRecords, type HomeDevice, type VitalSigns getHomeDeviceDetail, getVitalRecords, unbindHomeDevice, type HomeDevice, type VitalSigns
} from '@/api/homeDevice' } from '@/api/homeDevice'
import type { DeviceModel } from '@/constants/enums' import type { DeviceModel } from '@/constants/enums'
import styles from './detail.module.scss' import styles from './detail.module.scss'
@ -29,6 +30,7 @@ export default function Detail() {
const [device, setDevice] = useState<HomeDevice | null>(null) const [device, setDevice] = useState<HomeDevice | null>(null)
const [online, setOnline] = useState(params.online === '1') const [online, setOnline] = useState(params.online === '1')
const [wavePoints, setWavePoints] = useState<number[]>([]) const [wavePoints, setWavePoints] = useState<number[]>([])
const [unbinding, setUnbinding] = useState(false)
const deviceNameRef = useRef<string | null>(null) const deviceNameRef = useRef<string | null>(null)
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null) const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
@ -88,6 +90,37 @@ export default function Detail() {
const goBleConfig = () => navigateTo(ROUTES.DEVICE_BLE_CONFIG) const goBleConfig = () => navigateTo(ROUTES.DEVICE_BLE_CONFIG)
void goBleConfig void goBleConfig
// 解绑确认:防误触,成功后停轮询返回列表页
const confirmUnbind = () => {
if (unbinding || !installId) return
Taro.showModal({
title: '解绑设备',
content: `确定解绑「${deviceName}」吗?解绑后需重新添加。`,
confirmText: '解绑',
confirmColor: '#EE4B4B',
success: (res) => {
if (!res.confirm) return
doUnbind()
}
})
}
const doUnbind = async () => {
setUnbinding(true)
try {
await unbindHomeDevice({ installId })
showToast('已解绑')
if (timerRef.current) {
clearInterval(timerRef.current)
timerRef.current = null
}
navigateBack()
} catch (e) {
showToast(e instanceof Error ? e.message : '解绑失败')
setUnbinding(false)
}
}
return ( return (
<View className={styles.page}> <View className={styles.page}>
<Navbar title={deviceName} variant="hero" /> <Navbar title={deviceName} variant="hero" />
@ -161,6 +194,7 @@ export default function Detail() {
<ListRow label="网络配置" arrow onClick={goWifiConfig} /> <ListRow label="网络配置" arrow onClick={goWifiConfig} />
<ListRow label="设备配置" arrow onClick={goBleConfig} /> <ListRow label="设备配置" arrow onClick={goBleConfig} />
*/} */}
<ListRow label="解绑设备" arrow danger onClick={confirmUnbind} />
</ListRowGroup> </ListRowGroup>
</View> </View>
</View> </View>

View File

@ -211,27 +211,6 @@
.statusText { font-size: 24px; color: $color-primary-deep; } .statusText { font-size: 24px; color: $color-primary-deep; }
.statusTextOff { color: $color-text-secondary; } .statusTextOff { color: $color-text-secondary; }
// 解绑按钮:卡片底部通栏浅红描边,与详情点击区分
.unbindBtn {
width: 100%;
margin-top: 20px;
padding: 12px 0;
border: 1px solid $color-danger;
border-radius: $radius-pill;
display: flex;
align-items: center;
justify-content: center;
}
.unbindBtnPressed {
background: $color-danger-light-bg;
}
.unbindBtnText {
font-size: 24px;
color: $color-danger;
}
// 切换家庭圈弹层内的列表项 // 切换家庭圈弹层内的列表项
.sheetList { .sheetList {
padding-bottom: 16px; padding-bottom: 16px;

View File

@ -6,13 +6,12 @@ import { Navbar } from '@/components/navbar'
import { Loading } from '@/components/loading' import { Loading } from '@/components/loading'
import { StateView } from '@/components/state-view' import { StateView } from '@/components/state-view'
import { BottomSheet } from '@/components/bottom-sheet' import { BottomSheet } from '@/components/bottom-sheet'
import { getHomeDeviceList, unbindHomeDevice, type HomeDevice } from '@/api/homeDevice' import { getHomeDeviceList, type HomeDevice } from '@/api/homeDevice'
import { getRoomList, type HomeRoom } from '@/api/homeRoom' import { getRoomList, type HomeRoom } from '@/api/homeRoom'
import { getHomeList, type HomeInfo } from '@/api/home' import { getHomeList, type HomeInfo } from '@/api/home'
import { resolveCurrentHome, setCurrentHomeId } from '@/store/currentHome' import { resolveCurrentHome, setCurrentHomeId } from '@/store/currentHome'
import { ROUTES } from '@/constants/routes' import { ROUTES } from '@/constants/routes'
import { navigateTo } from '@/utils/navigation' import { navigateTo } from '@/utils/navigation'
import { showToast } from '@/utils/toast'
import styles from './index.module.scss' import styles from './index.module.scss'
const FALLBACK_ICON = '⌂' const FALLBACK_ICON = '⌂'
@ -24,7 +23,6 @@ export default function DeviceList() {
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [loadError, setLoadError] = useState(false) const [loadError, setLoadError] = useState(false)
const [deviceList, setDeviceList] = useState<HomeDevice[]>([]) const [deviceList, setDeviceList] = useState<HomeDevice[]>([])
const [unbinding, setUnbinding] = useState(false)
const [families, setFamilies] = useState<HomeInfo[]>([]) const [families, setFamilies] = useState<HomeInfo[]>([])
const [currentFamily, setCurrentFamily] = useState<HomeInfo | null>(null) const [currentFamily, setCurrentFamily] = useState<HomeInfo | null>(null)
@ -113,38 +111,6 @@ export default function DeviceList() {
const goAdd = () => navigateTo(ROUTES.DEVICE_ADD) const goAdd = () => navigateTo(ROUTES.DEVICE_ADD)
const goRoom = () => navigateTo(ROUTES.SCENE_SELECT) const goRoom = () => navigateTo(ROUTES.SCENE_SELECT)
// 解绑确认:防误触,成功后原地刷新列表
const confirmUnbind = (device: HomeDevice) => {
if (unbinding) return
Taro.showModal({
title: '解绑设备',
content: `确定解绑「${deviceLabel(device)}」吗?解绑后需重新添加。`,
confirmText: '解绑',
confirmColor: '#EE4B4B',
success: (res) => {
if (!res.confirm) return
doUnbind(device)
}
})
}
const doUnbind = async (device: HomeDevice) => {
if (!device.installId) {
showToast('缺少设备安装 ID,无法解绑')
return
}
setUnbinding(true)
try {
await unbindHomeDevice({ installId: device.installId })
showToast('已解绑')
await loadDevices()
} catch (e) {
showToast(e instanceof Error ? e.message : '解绑失败')
} finally {
setUnbinding(false)
}
}
const openFamilySheet = () => { const openFamilySheet = () => {
setPendingFamily(currentFamily) setPendingFamily(currentFamily)
setFamilySheetOpen(true) setFamilySheetOpen(true)
@ -251,9 +217,6 @@ export default function DeviceList() {
</Text> </Text>
</View> </View>
</View> </View>
<View className={styles.unbindBtn} hoverClass={styles.unbindBtnPressed} onClick={() => confirmUnbind(device)}>
<Text className={styles.unbindBtnText}>解绑</Text>
</View>
</View> </View>
))} ))}
</View> </View>