321 lines
7.3 KiB
Vue
321 lines
7.3 KiB
Vue
<!-- 首页:切换为 uview-plus 组件化结构,保留原有设备添加与配置入口 -->
|
||
<template>
|
||
<view class="page page-base">
|
||
<up-navbar title="智慧养老" :fixed="false" :safe-area-inset-top="true" :border="false" left-icon="" />
|
||
|
||
<view class="banner">
|
||
<view class="banner-left">
|
||
<text class="banner-title">智慧养老云助手</text>
|
||
<text class="banner-sub">一键开启智慧好生活</text>
|
||
</view>
|
||
<view class="banner-illus">
|
||
<up-icon name="home" color="#ffffff" size="52" />
|
||
</view>
|
||
</view>
|
||
|
||
<up-notice-bar
|
||
mode="link"
|
||
:text="noticeText"
|
||
color="#2f5a3a"
|
||
bg-color="#e9fff3"
|
||
@click="goConfig"
|
||
/>
|
||
|
||
<view class="site-row" @click="goSceneSelect">
|
||
<view class="site-left">
|
||
<up-icon name="list" size="18" color="#26c996" />
|
||
<text class="site-text">{{ sceneName || '请选择场所' }}</text>
|
||
</view>
|
||
<up-icon name="arrow-right" size="14" color="#26c996" />
|
||
</view>
|
||
|
||
<!-- 设备列表:loading / 错误 / 空态 / 双列卡片网格 -->
|
||
<view v-if="loading" class="state-wrap">
|
||
<up-loading-icon mode="circle" size="48" text="加载中..." />
|
||
</view>
|
||
|
||
<view v-else-if="loadError" class="state-wrap">
|
||
<up-empty mode="wifi" text="加载失败,请检查网络" />
|
||
<up-button type="success" shape="circle" text="重试" class="retry-btn" @click="loadDevices" />
|
||
</view>
|
||
|
||
<view v-else-if="deviceList.length === 0" class="state-wrap">
|
||
<up-empty mode="list" text="暂无设备" />
|
||
<up-button type="success" shape="circle" text="添加设备" class="retry-btn" @click="goAdd" />
|
||
</view>
|
||
|
||
<view v-else class="device-grid">
|
||
<view
|
||
v-for="device in deviceList"
|
||
:key="device.id"
|
||
class="device-card"
|
||
@click="goDetail(device)"
|
||
>
|
||
<view class="device-icon" :class="{ 'device-icon--offline': !device.online }">
|
||
<up-icon :name="iconOf(device)" :color="device.online ? '#ffffff' : '#b5b9c0'" size="30" />
|
||
</view>
|
||
<text class="device-name">{{ device.name }}</text>
|
||
<view class="device-status">
|
||
<view class="status-dot" :class="device.online ? 'status-dot--on' : 'status-dot--off'" />
|
||
<text class="status-text" :class="{ 'status-text--off': !device.online }">
|
||
{{ device.online ? '在线' : '离线' }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="fab-wrap" @click="goAdd">
|
||
<up-icon name="plus" color="#ffffff" size="28" />
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { getDeviceList } from '@/api/device'
|
||
import { ROUTES } from '@/constants/routes'
|
||
import { navigateTo } from '@/utils/navigation'
|
||
import { getCurrentScene } from '@/store/scene'
|
||
|
||
// 设备型号 -> 图标映射,未命中走兜底图标。
|
||
const DEVICE_ICON_MAP = {
|
||
ED713: 'home',
|
||
ED719: 'bell'
|
||
}
|
||
const FALLBACK_ICON = 'setting'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
noticeText: '点击选择场所后可进入设备配置流程',
|
||
sceneName: '',
|
||
loading: true,
|
||
loadError: false,
|
||
deviceList: []
|
||
}
|
||
},
|
||
onShow() {
|
||
this.refreshScene()
|
||
this.loadDevices()
|
||
},
|
||
methods: {
|
||
// 从全局场景状态刷新回显文案。
|
||
refreshScene() {
|
||
const scene = getCurrentScene()
|
||
this.sceneName = scene ? scene.name : ''
|
||
},
|
||
// 拉取当前场所的设备列表;在线状态需新鲜,每次 onShow 重拉。
|
||
async loadDevices() {
|
||
this.loading = true
|
||
this.loadError = false
|
||
try {
|
||
const scene = getCurrentScene()
|
||
const data = await getDeviceList(scene ? scene.id : null)
|
||
this.deviceList = Array.isArray(data) ? data : []
|
||
} catch (error) {
|
||
this.loadError = true
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
},
|
||
// 设备型号对应图标。
|
||
iconOf(device) {
|
||
return DEVICE_ICON_MAP[device.model] || FALLBACK_ICON
|
||
},
|
||
// 进入场景选择页。
|
||
goSceneSelect() {
|
||
navigateTo(ROUTES.SCENE_SELECT)
|
||
},
|
||
// 进入设备添加页(后续会承接蓝牙配网流程入口)。
|
||
goAdd() {
|
||
navigateTo(ROUTES.DEVICE_ADD)
|
||
},
|
||
// 进入设备配置页。
|
||
goConfig() {
|
||
navigateTo(ROUTES.DEVICE_CONFIG)
|
||
},
|
||
// 进入设备详情页:携带基础信息,后续接详情接口后改为仅传 id。
|
||
goDetail(device) {
|
||
const name = encodeURIComponent(device.name || '')
|
||
navigateTo(`${ROUTES.DEVICE_DETAIL}?id=${device.id}&name=${name}&model=${device.model || ''}&online=${device.online ? 1 : 0}`)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@import '@/styles/common.css';
|
||
|
||
.page {
|
||
position: relative;
|
||
}
|
||
|
||
.banner {
|
||
margin: 0;
|
||
min-height: 360rpx;
|
||
background: linear-gradient(135deg, #1fca97 0%, #2dbf9c 100%);
|
||
padding: 48rpx 44rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.banner-left {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.banner-title {
|
||
font-size: 52rpx;
|
||
color: #ffffff;
|
||
font-weight: 700;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.banner-sub {
|
||
font-size: 34rpx;
|
||
color: #e9fff8;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.banner-illus {
|
||
width: 180rpx;
|
||
height: 180rpx;
|
||
border-radius: 24rpx;
|
||
background: rgba(255, 255, 255, 0.18);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.site-row {
|
||
height: 108rpx;
|
||
padding: 0 32rpx;
|
||
background: #ffffff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.site-left {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.site-text {
|
||
margin-left: 14rpx;
|
||
font-size: 34rpx;
|
||
color: #222;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* 列表三态容器 */
|
||
.state-wrap {
|
||
margin-top: 80rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.retry-btn {
|
||
margin-top: 40rpx;
|
||
width: 240rpx;
|
||
}
|
||
|
||
/* 设备双列网格 */
|
||
.device-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: space-between;
|
||
padding: 24rpx 24rpx 0;
|
||
}
|
||
|
||
.device-card {
|
||
width: 48.5%;
|
||
margin-bottom: 24rpx;
|
||
padding: 36rpx 20rpx 28rpx;
|
||
box-sizing: border-box;
|
||
background: #ffffff;
|
||
border-radius: 24rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
&:active {
|
||
background: #f7f8fa;
|
||
}
|
||
}
|
||
|
||
.device-icon {
|
||
width: 108rpx;
|
||
height: 108rpx;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #1fca97 0%, #2dbf9c 100%);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 20rpx;
|
||
|
||
&--offline {
|
||
background: #eef0f4;
|
||
}
|
||
}
|
||
|
||
.device-name {
|
||
max-width: 100%;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #2a2d33;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.device-status {
|
||
margin-top: 12rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.status-dot {
|
||
width: 14rpx;
|
||
height: 14rpx;
|
||
border-radius: 50%;
|
||
margin-right: 10rpx;
|
||
|
||
&--on {
|
||
background: #2ccb98;
|
||
}
|
||
|
||
&--off {
|
||
background: #c0c4cc;
|
||
}
|
||
}
|
||
|
||
.status-text {
|
||
font-size: 24rpx;
|
||
color: #12a37a;
|
||
|
||
&--off {
|
||
color: #9ea1a6;
|
||
}
|
||
}
|
||
|
||
.fab-wrap {
|
||
position: fixed;
|
||
right: 40rpx;
|
||
bottom: 60rpx;
|
||
width: 112rpx;
|
||
height: 112rpx;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #1fca97 0%, #2dbf9c 100%);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-shadow: 0 8rpx 24rpx rgba(31, 202, 151, 0.4);
|
||
/* 点击态缩放反馈 */
|
||
&:active {
|
||
transform: scale(0.92);
|
||
}
|
||
}
|
||
</style>
|