106 lines
3.4 KiB
TypeScript
106 lines
3.4 KiB
TypeScript
import { useState, useEffect } from 'react'
|
||
import Taro from '@tarojs/taro'
|
||
import { View, Text } from '@tarojs/components'
|
||
import { useAppDispatch, useAppSelector } from '@/store/hooks'
|
||
import { setScene, type Scene } from '@/store/sceneSlice'
|
||
import { Navbar } from '@/components/navbar'
|
||
import { Loading } from '@/components/loading'
|
||
import { Empty } from '@/components/empty'
|
||
import { StateView } from '@/components/state-view'
|
||
import { getSceneList, addScene } from '@/api/scene'
|
||
import { showToast } from '@/utils/toast'
|
||
import { navigateBack } from '@/utils/navigation'
|
||
import styles from './select.module.scss'
|
||
|
||
// Taro 类型未跟进 wx.showModal 的 editable 扩展(微信 2.17.1+),运行时真实存在
|
||
interface EditableModalOption extends Taro.showModal.Option {
|
||
editable?: boolean
|
||
placeholderText?: string
|
||
}
|
||
|
||
interface EditableModalResult extends Taro.showModal.SuccessCallbackResult {
|
||
content?: string
|
||
}
|
||
|
||
const showEditableModal = (option: EditableModalOption) =>
|
||
Taro.showModal(option as Taro.showModal.Option) as Promise<EditableModalResult>
|
||
|
||
export default function SceneSelect() {
|
||
const dispatch = useAppDispatch()
|
||
const current = useAppSelector((s) => s.scene.current)
|
||
|
||
const [loading, setLoading] = useState(true)
|
||
const [loadError, setLoadError] = useState(false)
|
||
const [list, setList] = useState<Scene[]>([])
|
||
|
||
const load = async () => {
|
||
setLoading(true)
|
||
setLoadError(false)
|
||
try {
|
||
const data = await getSceneList()
|
||
setList(data ?? [])
|
||
} catch {
|
||
setLoadError(true)
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
useEffect(() => { load() }, [])
|
||
|
||
const selectScene = (scene: Scene) => {
|
||
dispatch(setScene(scene))
|
||
navigateBack()
|
||
}
|
||
|
||
// 添加房间:可编辑弹窗输入名称,成功后刷新列表
|
||
const addRoom = () => {
|
||
showEditableModal({
|
||
title: '添加房间',
|
||
editable: true,
|
||
placeholderText: '请输入房间名称(如:阳台)'
|
||
}).then(async ({ confirm, content }) => {
|
||
if (!confirm) return
|
||
try {
|
||
await addScene(content ?? '')
|
||
showToast('添加成功')
|
||
await load()
|
||
} catch (error) {
|
||
showToast(error instanceof Error ? error.message : '添加失败')
|
||
}
|
||
})
|
||
}
|
||
|
||
return (
|
||
<View className={styles.page}>
|
||
<Navbar title="房间管理" variant="hero" />
|
||
{loading ? (
|
||
<View className={styles.stateWrap}><Loading /></View>
|
||
) : loadError ? (
|
||
<StateView status="error" onRetry={load} />
|
||
) : (
|
||
<View className={styles.list}>
|
||
{list.map((scene) => (
|
||
<View
|
||
key={scene.id}
|
||
className={`${styles.item} ${scene.id === current?.id ? styles.itemActive : ''}`}
|
||
onClick={() => selectScene(scene)}
|
||
>
|
||
<Text className={styles.name}>{scene.name}</Text>
|
||
<Text className={styles.check}>{scene.id === current?.id ? '✓' : '›'}</Text>
|
||
</View>
|
||
))}
|
||
{/* 空列表时也保留添加入口 */}
|
||
{list.length === 0 && (
|
||
<View className={styles.stateWrap}><Empty text="暂无房间,点击下方添加" /></View>
|
||
)}
|
||
<View className={styles.addBtn} hoverClass={styles.addBtnPressed} onClick={addRoom}>
|
||
<Text className={styles.addIcon}>+</Text>
|
||
<Text className={styles.addText}>添加房间</Text>
|
||
</View>
|
||
</View>
|
||
)}
|
||
</View>
|
||
)
|
||
}
|