|
|
@@ -4,19 +4,21 @@
|
|
|
<view class="search-section">
|
|
|
<view class="search-box" :class="{ 'search-focus': isSearchFocused }">
|
|
|
<view class="search-icon">
|
|
|
- <text class="iconfont icon-search"></text>
|
|
|
+ <!-- <text class="iconfont icon-search"></text> -->
|
|
|
+ <image src="@/static/icons/search.png" style="width: 40rpx; height: 40rpx; padding-right: 10rpx;"
|
|
|
+ mode="widthFix" />
|
|
|
</view>
|
|
|
<input
|
|
|
class="search-input"
|
|
|
type="text"
|
|
|
- v-model="searchKeyword"
|
|
|
+ v-model="searchKey"
|
|
|
placeholder="搜索设备名称 / 编号"
|
|
|
confirm-type="search"
|
|
|
- @confirm="handleSearch"
|
|
|
+ @confirm="onSearch"
|
|
|
@focus="isSearchFocused = true"
|
|
|
@blur="isSearchFocused = false"
|
|
|
/>
|
|
|
- <view class="clear-icon" v-if="searchKeyword" @click="handleClearSearch">
|
|
|
+ <view class="clear-icon" v-if="searchKey" @click="clearSearch">
|
|
|
<text class="iconfont icon-close"></text>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -26,34 +28,42 @@
|
|
|
<view class="filter-section">
|
|
|
<view
|
|
|
class="filter-item"
|
|
|
- :class="{ active: statusFilter === 'all' }"
|
|
|
- @click="setStatusFilter('all')"
|
|
|
+ :class="{ active: currentStatus === -1 }"
|
|
|
+ @click="selectStatus(-1)"
|
|
|
>
|
|
|
全部
|
|
|
</view>
|
|
|
<view
|
|
|
class="filter-item"
|
|
|
- :class="{ active: statusFilter === 'online' }"
|
|
|
- @click="setStatusFilter('online')"
|
|
|
+ :class="{ active: currentStatus === 1 }"
|
|
|
+ @click="selectStatus(1)"
|
|
|
>
|
|
|
<view class="filter-dot online-dot"></view>
|
|
|
- 在线 ({{ onlineCount }})
|
|
|
+ 在线 ({{ onlineDevices }})
|
|
|
</view>
|
|
|
<view
|
|
|
class="filter-item"
|
|
|
- :class="{ active: statusFilter === 'offline' }"
|
|
|
- @click="setStatusFilter('offline')"
|
|
|
+ :class="{ active: currentStatus === 0 }"
|
|
|
+ @click="selectStatus(0)"
|
|
|
>
|
|
|
<view class="filter-dot offline-dot"></view>
|
|
|
- 离线 ({{ offlineCount }})
|
|
|
+ 离线 ({{ offlineDevices }})
|
|
|
</view>
|
|
|
+<!-- <view
|
|
|
+ class="filter-item"
|
|
|
+ :class="{ active: currentStatus === 2 }"
|
|
|
+ @click="selectStatus(2)"
|
|
|
+ >
|
|
|
+ <view class="filter-dot fault-dot"></view>
|
|
|
+ 故障 ({{ getStatusCount(2) }})
|
|
|
+ </view> -->
|
|
|
</view>
|
|
|
|
|
|
<!-- 设备列表区域 -->
|
|
|
<scroll-view
|
|
|
scroll-y
|
|
|
class="device-list"
|
|
|
- @scrolltolower="loadMore"
|
|
|
+ @scrolltolower="onReachBottom"
|
|
|
:scroll-with-animation="true"
|
|
|
:enable-back-to-top="true"
|
|
|
:refresher-enabled="true"
|
|
|
@@ -63,91 +73,97 @@
|
|
|
@refresherrestore="isRefreshing = false"
|
|
|
>
|
|
|
<!-- 无数据提示 -->
|
|
|
- <view v-if="filteredDeviceList.length === 0 && !isLoading" class="empty-tips">
|
|
|
+ <view v-if="deviceList.length === 0 && !loading" class="empty-tips">
|
|
|
<view class="empty-icon">
|
|
|
<text class="iconfont icon-empty"></text>
|
|
|
</view>
|
|
|
- <text class="empty-text">{{ searchKeyword ? '未找到匹配的设备' : '暂无设备' }}</text>
|
|
|
- <view v-if="searchKeyword" class="empty-action" @click="handleClearSearch">
|
|
|
+ <text class="empty-text">{{ searchKey ? '未找到匹配的设备' : '暂无设备' }}</text>
|
|
|
+ <view v-if="searchKey" class="empty-action" @click="clearSearch">
|
|
|
<text>清除搜索条件</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 首次加载中状态 -->
|
|
|
- <view v-if="isLoading && deviceList.length === 0" class="loading-container">
|
|
|
+ <view v-if="loading && deviceList.length === 0" class="loading-container">
|
|
|
<view class="loading-spinner"></view>
|
|
|
<text class="loading-text">加载中...</text>
|
|
|
</view>
|
|
|
|
|
|
<!-- 设备列表 -->
|
|
|
<view
|
|
|
- v-for="(item, index) in filteredDeviceList"
|
|
|
+ v-for="(item, index) in deviceList"
|
|
|
:key="index"
|
|
|
class="device-card"
|
|
|
:class="{
|
|
|
- 'has-alert': item.alarmCount > 0,
|
|
|
- 'is-offline': item.status === 'offline'
|
|
|
+ 'has-alert': item.hasAlert,
|
|
|
+ 'is-offline': item.status === 0
|
|
|
}"
|
|
|
hover-class="device-card-hover"
|
|
|
hover-stay-time="70"
|
|
|
- @click="navigateToDetail(item)"
|
|
|
+ @click="navigateToDeviceDetail(item)"
|
|
|
>
|
|
|
<!-- 设备基本信息 -->
|
|
|
<view class="device-info">
|
|
|
<view class="device-icon-wrapper">
|
|
|
- <!-- 告警角标 -->
|
|
|
- <view v-if="item.alarmCount > 0" class="alarm-badge">
|
|
|
- {{ item.alarmCount }}
|
|
|
+ <!-- 告警标记 -->
|
|
|
+ <view v-if="item.hasAlert" class="alarm-badge">
|
|
|
+ 1
|
|
|
</view>
|
|
|
|
|
|
- <view class="device-icon-container" :class="{'offline-icon': item.status === 'offline'}">
|
|
|
- <image :src="getDeviceIcon(item.type)" mode="aspectFit" class="device-icon"></image>
|
|
|
+ <view class="device-icon-container" :class="{'offline-icon': item.status === 0}">
|
|
|
+ <image :src="getDeviceTypeIcon(item.deviceTypeId)" mode="aspectFit" class="device-icon"></image>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<view class="device-meta">
|
|
|
<view class="device-name-row">
|
|
|
- <text class="device-name" :class="{'offline-text': item.status === 'offline'}">{{ item.name }}</text>
|
|
|
+ <text class="device-name" :class="{'offline-text': item.status === 0}">{{ item.deviceName }}</text>
|
|
|
<view
|
|
|
class="status-tag"
|
|
|
- :class="item.status === 'online' ? 'status-online' : 'status-offline'"
|
|
|
+ :class="{
|
|
|
+ 'status-online': item.status === 1,
|
|
|
+ 'status-offline': item.status === 0,
|
|
|
+ 'status-fault': item.status === 2,
|
|
|
+ 'status-maintain': item.status === 3
|
|
|
+ }"
|
|
|
>
|
|
|
- <text class="status-dot" :class="{'offline-dot': item.status === 'offline'}"></text>
|
|
|
- {{ item.status === 'online' ? '在线' : '离线' }}
|
|
|
+ <text class="status-dot" :class="{
|
|
|
+ 'offline-dot': item.status === 0,
|
|
|
+ 'fault-dot': item.status === 2,
|
|
|
+ 'maintain-dot': item.status === 3
|
|
|
+ }"></text>
|
|
|
+ {{ getStatusText(item.status) }}
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<view class="device-id">
|
|
|
<text class="id-label">设备编号:</text>
|
|
|
- <text class="id-value">{{ item.code }}</text>
|
|
|
+ <text class="id-value">{{ item.deviceId }}</text>
|
|
|
</view>
|
|
|
|
|
|
<view class="device-location">
|
|
|
<text class="location-label">安装位置:</text>
|
|
|
- <text class="location-value">{{ item.location }}</text>
|
|
|
+ <text class="location-value">{{ item.fieldName || '未指定位置' }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 底部信息栏 -->
|
|
|
<view class="device-footer">
|
|
|
- <text class="update-time">{{ item.updateTime }}</text>
|
|
|
- <view class="device-actions">
|
|
|
- <text class="iconfont icon-right"></text>
|
|
|
- </view>
|
|
|
+ <text class="update-time">最后活跃: {{ formatDate(item.lastActiveTime) }}</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 加载更多提示 -->
|
|
|
- <view v-if="filteredDeviceList.length > 0" class="load-more">
|
|
|
+ <view v-if="deviceList.length > 0" class="load-more">
|
|
|
<view class="load-more-content" v-if="loadMoreStatus === 'loading'">
|
|
|
<view class="loading-icon"></view>
|
|
|
<text>正在加载...</text>
|
|
|
</view>
|
|
|
- <view class="load-more-content" v-if="loadMoreStatus === 'nomore'">
|
|
|
+ <view class="load-more-content" v-if="loadMoreStatus === 'noMore'">
|
|
|
<text>没有更多了</text>
|
|
|
</view>
|
|
|
- <view class="load-more-content" v-if="loadMoreStatus === 'loadmore'" @click="loadMore">
|
|
|
+ <view class="load-more-content" v-if="loadMoreStatus === 'more'" @click="onReachBottom">
|
|
|
<text>点击加载更多</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
@@ -156,225 +172,335 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
+import { fetchDevicesByType } from "@/api/services/device.js";
|
|
|
+import storage from "@/utils/storage.js";
|
|
|
+
|
|
|
export default {
|
|
|
data() {
|
|
|
return {
|
|
|
- deviceType: '',
|
|
|
- searchKeyword: '',
|
|
|
- isSearchFocused: false,
|
|
|
- statusFilter: 'all', // 'all', 'online', 'offline'
|
|
|
+ deviceType: '', // monitor, sensor, control, irrigation, tractor
|
|
|
+ deviceTypeName: '',
|
|
|
deviceList: [],
|
|
|
- page: 1,
|
|
|
- limit: 10,
|
|
|
- loadMoreStatus: 'loading', // 'loadmore', 'loading', 'nomore'
|
|
|
- isLoading: true,
|
|
|
- isRefreshing: false
|
|
|
- }
|
|
|
+ searchKey: '',
|
|
|
+ isSearchFocused: false,
|
|
|
+ currentStatus: -1, // -1代表全部
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ total: 0,
|
|
|
+ loading: false,
|
|
|
+ isRefreshing: false,
|
|
|
+ loadMoreStatus: 'more', // 加载更多状态: more-加载更多 loading-加载中 noMore-没有更多了
|
|
|
+ deviceTypeMap: {
|
|
|
+ 'monitor': { name: '监控设备', icon: '/static/icons/camera.png', class: 'type-monitor' },
|
|
|
+ 'sensor': { name: '采集设备', icon: '/static/icons/sensor.png', class: 'type-sensor' },
|
|
|
+ 'control': { name: '控制设备', icon: '/static/icons/control.png', class: 'type-control' },
|
|
|
+ 'irrigation': { name: '灌溉设备', icon: '/static/icons/water.png', class: 'type-irrigation' },
|
|
|
+ 'tractor': { name: '农机设备', icon: '/static/icons/tractor.png', class: 'type-tractor' }
|
|
|
+ },
|
|
|
+ currentFieldId: null,
|
|
|
+ onlineDevices:0,
|
|
|
+ offlineDevices:0
|
|
|
+ };
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
- // 过滤后的设备列表
|
|
|
- filteredDeviceList() {
|
|
|
- let result = this.deviceList;
|
|
|
-
|
|
|
- // 按状态筛选
|
|
|
- if (this.statusFilter !== 'all') {
|
|
|
- result = result.filter(device => device.status === this.statusFilter);
|
|
|
- }
|
|
|
-
|
|
|
- // 按关键词搜索
|
|
|
- if (this.searchKeyword) {
|
|
|
- const keyword = this.searchKeyword.toLowerCase();
|
|
|
- result = result.filter(device =>
|
|
|
- device.name.toLowerCase().includes(keyword) ||
|
|
|
- device.code.toLowerCase().includes(keyword)
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- },
|
|
|
-
|
|
|
- // 在线设备数量
|
|
|
- onlineCount() {
|
|
|
- return this.deviceList.filter(device => device.status === 'online').length;
|
|
|
- },
|
|
|
-
|
|
|
- // 离线设备数量
|
|
|
- offlineCount() {
|
|
|
- return this.deviceList.filter(device => device.status === 'offline').length;
|
|
|
+ // 设备类型对应的样式类
|
|
|
+ deviceTypeClass() {
|
|
|
+ return this.deviceTypeMap[this.deviceType]?.class || '';
|
|
|
}
|
|
|
},
|
|
|
|
|
|
onLoad(options) {
|
|
|
- // 获取路由参数
|
|
|
- if (options.type) {
|
|
|
- this.deviceType = options.type;
|
|
|
- this.setPageTitle();
|
|
|
+ // 获取传递的设备类型
|
|
|
+ console.log("options类型:", options);
|
|
|
+ const { type, typeOnline, typeOffline } = options;
|
|
|
+ // 处理传递过来的统计数量
|
|
|
+ this.offlineDevices = typeOffline
|
|
|
+ this.onlineDevices = typeOnline
|
|
|
+ if (type && this.deviceTypeMap[type]) {
|
|
|
+ this.deviceType = type;
|
|
|
+ this.deviceTypeName = this.deviceTypeMap[type].name;
|
|
|
}
|
|
|
|
|
|
- // 加载设备数据
|
|
|
- this.loadDeviceData();
|
|
|
+ // 获取当前地块ID
|
|
|
+ this.initFieldInfo();
|
|
|
+
|
|
|
+ // 加载设备列表
|
|
|
+ this.loadDeviceList();
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
- // 设置页面标题
|
|
|
- setPageTitle() {
|
|
|
- const titleMap = {
|
|
|
- 'monitor': '监控设备列表',
|
|
|
- 'sensor': '采集设备列表',
|
|
|
- 'control': '控制设备列表',
|
|
|
- 'irrigation': '灌溉设备列表',
|
|
|
- 'tractor': '农机设备列表'
|
|
|
+ // 初始化地块信息
|
|
|
+ initFieldInfo() {
|
|
|
+ const currentPlots = JSON.parse(storage.getPlots() || '{}');
|
|
|
+ if (currentPlots) {
|
|
|
+ this.currentFieldId = currentPlots.id;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取特定状态的设备数量
|
|
|
+ getStatusCount(status) {
|
|
|
+ return this.deviceList.filter(device => device.status === status).length;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载设备列表
|
|
|
+ loadDeviceList(reset = true) {
|
|
|
+ if (this.loading) return;
|
|
|
+
|
|
|
+ if (reset) {
|
|
|
+ this.pageNum = 1;
|
|
|
+ this.deviceList = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ this.loading = true;
|
|
|
+ this.loadMoreStatus = 'loading';
|
|
|
+
|
|
|
+ // 构建查询参数
|
|
|
+ const params = {
|
|
|
+ pageNum: this.pageNum,
|
|
|
+ pageSize: this.pageSize,
|
|
|
+ deviceQueryParams: this.searchKey || undefined,
|
|
|
+ deviceType: this.deviceType || undefined,
|
|
|
+ fieldId: this.currentFieldId || undefined
|
|
|
};
|
|
|
|
|
|
- const title = titleMap[this.deviceType] || '设备列表';
|
|
|
- uni.setNavigationBarTitle({
|
|
|
- title: title
|
|
|
+ // 如果状态不是全部,添加状态筛选
|
|
|
+ if (this.currentStatus !== -1) {
|
|
|
+ params.status = this.currentStatus;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 调用API获取设备列表
|
|
|
+ fetchDevicesByType(params)
|
|
|
+ .then(res => {
|
|
|
+ console.log("res", res);
|
|
|
+ if (res.data.code === 200 && res.data.rows) {
|
|
|
+ const { rows, total } = res.data;
|
|
|
+
|
|
|
+ // 更新设备列表
|
|
|
+ if (reset) {
|
|
|
+ this.deviceList = rows;
|
|
|
+ } else {
|
|
|
+ this.deviceList = [...this.deviceList, ...rows];
|
|
|
+ }
|
|
|
+
|
|
|
+ this.total = total;
|
|
|
+
|
|
|
+ // 标记有告警的设备
|
|
|
+ this.deviceList.forEach(device => {
|
|
|
+ // 这里可以根据实际情况设置hasAlert属性
|
|
|
+ device.hasAlert = false; // 示例,实际应该根据后台数据判断
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新加载更多状态
|
|
|
+ if (this.deviceList.length >= total) {
|
|
|
+ this.loadMoreStatus = 'noMore';
|
|
|
+ } else {
|
|
|
+ this.loadMoreStatus = 'more';
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.handleApiError(res);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(error => {
|
|
|
+ console.error('获取设备列表失败', error);
|
|
|
+ uni.showToast({
|
|
|
+ title: '获取设备列表失败',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ this.loadMoreStatus = 'more';
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ this.loading = false;
|
|
|
+ this.isRefreshing = false;
|
|
|
+ uni.hideLoading();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 处理API错误
|
|
|
+ handleApiError(res) {
|
|
|
+ console.error('API错误', res);
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg || '获取数据失败',
|
|
|
+ icon: 'none'
|
|
|
});
|
|
|
},
|
|
|
|
|
|
- // 获取设备图标
|
|
|
- getDeviceIcon(type) {
|
|
|
- const iconMap = {
|
|
|
- 'monitor': '/static/icons/camera.png',
|
|
|
- 'sensor': '/static/icons/sensor.png',
|
|
|
- 'control': '/static/icons/control.png',
|
|
|
- 'irrigation': '/static/icons/water.png',
|
|
|
- 'tractor': '/static/icons/tractor.png'
|
|
|
- };
|
|
|
-
|
|
|
- return iconMap[type] || '/static/icons/device-default.png';
|
|
|
+ // 搜索
|
|
|
+ onSearch() {
|
|
|
+ this.loadDeviceList();
|
|
|
},
|
|
|
|
|
|
- // 设置状态筛选
|
|
|
- setStatusFilter(status) {
|
|
|
- this.statusFilter = status;
|
|
|
+ // 清除搜索
|
|
|
+ clearSearch() {
|
|
|
+ this.searchKey = '';
|
|
|
+ this.loadDeviceList();
|
|
|
},
|
|
|
|
|
|
- // 加载设备数据
|
|
|
- loadDeviceData() {
|
|
|
- this.isLoading = true;
|
|
|
-
|
|
|
- // 模拟API请求数据
|
|
|
- setTimeout(() => {
|
|
|
- // 这里应该是真实的API请求
|
|
|
- // 模拟一些设备数据用于展示
|
|
|
- const newDevices = this.generateMockDevices();
|
|
|
- this.deviceList = [...this.deviceList, ...newDevices];
|
|
|
-
|
|
|
- if (this.deviceList.length >= 30) {
|
|
|
- this.loadMoreStatus = 'nomore';
|
|
|
- } else {
|
|
|
- this.loadMoreStatus = 'loadmore';
|
|
|
- }
|
|
|
-
|
|
|
- this.isLoading = false;
|
|
|
- this.isRefreshing = false;
|
|
|
- }, 1000);
|
|
|
+ // 选择状态
|
|
|
+ selectStatus(value) {
|
|
|
+ this.currentStatus = value;
|
|
|
+ this.loadDeviceList();
|
|
|
},
|
|
|
|
|
|
- // 生成模拟设备数据
|
|
|
- generateMockDevices() {
|
|
|
- const devices = [];
|
|
|
- const locations = ['东区A1地块', '西区B2地块', '南区C3地块', '北区D4地块'];
|
|
|
- const updateTimes = ['刚刚更新', '1分钟前更新', '5分钟前更新', '10分钟前更新', '1小时前更新'];
|
|
|
-
|
|
|
- // 根据当前页码和限制数量生成对应数量的模拟数据
|
|
|
- const startIndex = (this.page - 1) * this.limit;
|
|
|
- for (let i = 0; i < this.limit; i++) {
|
|
|
- const index = startIndex + i;
|
|
|
-
|
|
|
- // 如果已经生成了30条数据,则停止
|
|
|
- if (index >= 30) break;
|
|
|
-
|
|
|
- // 对于采集设备类型,生成随机的气象或土壤设备
|
|
|
- let deviceType = this.deviceType;
|
|
|
- let deviceCode = `DEV${String(index + 1001).padStart(4, '0')}`;
|
|
|
-
|
|
|
- // 如果是采集设备,随机生成气象站或土壤墒情设备
|
|
|
- if (this.deviceType === 'sensor') {
|
|
|
- // 随机分配采集设备子类型:气象站或土壤墒情
|
|
|
- const sensorSubType = Math.random() > 0.5 ? 'weather' : 'soil';
|
|
|
- deviceCode = sensorSubType === 'weather' ? `W${deviceCode}` : `S${deviceCode}`;
|
|
|
- }
|
|
|
-
|
|
|
- devices.push({
|
|
|
- id: `device-${index + 1}`,
|
|
|
- name: `${this.getDeviceTypeName(this.deviceType)}-${index + 1}`,
|
|
|
- code: deviceCode,
|
|
|
- type: deviceType,
|
|
|
- status: Math.random() > 0.3 ? 'online' : 'offline', // 70% 概率在线
|
|
|
- location: locations[Math.floor(Math.random() * locations.length)],
|
|
|
- updateTime: updateTimes[Math.floor(Math.random() * updateTimes.length)],
|
|
|
- alarmCount: Math.random() > 0.7 ? Math.floor(Math.random() * 3) + 1 : 0 // 30% 概率有告警
|
|
|
- });
|
|
|
+ // 处理下拉刷新
|
|
|
+ handleRefresh() {
|
|
|
+ this.isRefreshing = true;
|
|
|
+ this.loadDeviceList();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 跳转到设备详情页
|
|
|
+ navigateToDeviceDetail(device) {
|
|
|
+ console.log("device",device);
|
|
|
+ // 根据设备类型跳转到不同的详情页
|
|
|
+ let url = '';
|
|
|
+ // 先发送事件
|
|
|
+ uni.$emit('passDeviceData', {
|
|
|
+ deviceId: device.deviceId,
|
|
|
+ deviceTypeId: device.deviceTypeId,
|
|
|
+ fieldName: device.fieldName,
|
|
|
+ deviceName: device.deviceName
|
|
|
+ });
|
|
|
+ if (device.deviceTypeId === '2') {
|
|
|
+ url = `/pages/device/device-list/detail-camera?id=${device.id}`;
|
|
|
+ } else if (device.deviceTypeId === '1') {
|
|
|
+ // 采集设备跳转到采集设备详情页,同时传递设备编码,便于判断设备子类型
|
|
|
+ // url = `/pages/device/device-list/detail-collector?id=${device.deviceId}&deviceTypeId=${device.deviceTypeId}&fieldName=${device.fieldName}&deviceName=${device.deviceName}`;
|
|
|
+ url = `/pages/device/device-list/detail-collector`;
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // 其他类型设备暂时使用通用详情页
|
|
|
+ url = `/pages/device/device-detail/index?id=${device.id}&type=${device.type}`;
|
|
|
}
|
|
|
|
|
|
- return devices;
|
|
|
+ // 先跳转
|
|
|
+ uni.navigateTo({
|
|
|
+ url: url,
|
|
|
+ success: () => {
|
|
|
+ // 跳转成功后再发送事件,延迟一点确保页面onLoad注册完成
|
|
|
+ setTimeout(() => {
|
|
|
+ uni.$emit('passDeviceData', {
|
|
|
+ deviceId: device.deviceId,
|
|
|
+ deviceTypeId: device.deviceTypeId,
|
|
|
+ fieldName: device.fieldName,
|
|
|
+ deviceName: device.deviceName,
|
|
|
+ status:device.status
|
|
|
+ });
|
|
|
+ }, 100); // 100ms 通常足够,必要时可加到 200
|
|
|
+ }
|
|
|
+ });
|
|
|
},
|
|
|
|
|
|
- // 获取设备类型名称
|
|
|
- getDeviceTypeName(type) {
|
|
|
- const nameMap = {
|
|
|
- 'monitor': '监控设备',
|
|
|
- 'sensor': '采集设备',
|
|
|
- 'control': '控制设备',
|
|
|
- 'irrigation': '灌溉设备',
|
|
|
- 'tractor': '农机设备'
|
|
|
+ // 获取设备类型图标
|
|
|
+ getDeviceTypeIcon(typeId) {
|
|
|
+ // 根据后端设备类型ID获取对应前端类型的图标
|
|
|
+ const typeMapping = {
|
|
|
+ '1': 'sensor', // 传感器
|
|
|
+ '2': 'monitor', // 摄像头
|
|
|
+ '3': 'control', // 控制器
|
|
|
+ '4': 'irrigation', // 气象设备/灌溉设备
|
|
|
+ '5': 'tractor' // 农机设备
|
|
|
};
|
|
|
|
|
|
- return nameMap[type] || '未知设备';
|
|
|
+ const frontendType = typeMapping[typeId] || this.deviceType;
|
|
|
+ return this.deviceTypeMap[frontendType]?.icon || '/static/icons/device.png';
|
|
|
},
|
|
|
|
|
|
- // 处理搜索
|
|
|
- handleSearch() {
|
|
|
- // 执行搜索逻辑
|
|
|
- console.log('搜索关键词:', this.searchKeyword);
|
|
|
+ // 获取设备类型名称
|
|
|
+ getDeviceTypeName(typeId) {
|
|
|
+ const typeNames = {
|
|
|
+ '1': '采集设备',
|
|
|
+ '2': '监控设备',
|
|
|
+ '3': '控制设备',
|
|
|
+ '4': '灌溉设备',
|
|
|
+ '5': '农机设备'
|
|
|
+ };
|
|
|
+
|
|
|
+ return typeNames[typeId] || '未知类型';
|
|
|
},
|
|
|
|
|
|
- // 处理清空搜索
|
|
|
- handleClearSearch() {
|
|
|
- this.searchKeyword = '';
|
|
|
+ // 获取状态文本
|
|
|
+ getStatusText(status) {
|
|
|
+ const statusMap = {
|
|
|
+ 0: '离线',
|
|
|
+ 1: '在线',
|
|
|
+ 2: '故障',
|
|
|
+ 3: '维护中'
|
|
|
+ };
|
|
|
+
|
|
|
+ return statusMap[status] || '未知状态';
|
|
|
},
|
|
|
|
|
|
- // 加载更多数据
|
|
|
- loadMore() {
|
|
|
- if (this.loadMoreStatus !== 'nomore') {
|
|
|
- this.loadMoreStatus = 'loading';
|
|
|
- this.page += 1;
|
|
|
- this.loadDeviceData();
|
|
|
+ // 格式化日期
|
|
|
+ formatDate(dateStr) {
|
|
|
+ if (!dateStr) return '未知';
|
|
|
+
|
|
|
+ // 解析为 Date
|
|
|
+ const parsedStr = dateStr.replace(' ', 'T');
|
|
|
+ const date = new Date(parsedStr);
|
|
|
+ if (isNaN(date)) return '无效时间';
|
|
|
+
|
|
|
+ const now = new Date();
|
|
|
+
|
|
|
+ // 计算差时长(分钟)
|
|
|
+ const diff = Math.floor((now - date) / 1000 / 60);
|
|
|
+
|
|
|
+ if (diff < 1) return '刚刚更新';
|
|
|
+ if (diff < 5) return '1分钟前更新';
|
|
|
+ if (diff < 10) return '5分钟前更新';
|
|
|
+ if (diff < 60) return `${diff}分钟前更新`;
|
|
|
+
|
|
|
+ if (diff < 120) return '1小时前更新';
|
|
|
+ if (diff < 24 * 60) return `${Math.floor(diff / 60)}小时前更新`;
|
|
|
+ if (diff < 7 * 24 * 60) return `${Math.floor(diff / (60 * 24))}天前更新`;
|
|
|
+
|
|
|
+ return parsedStr.split('T')[0] + ' 更新';
|
|
|
+ },
|
|
|
+/* formatDate(dateStr) {
|
|
|
+ if (!dateStr) return '未知';
|
|
|
+
|
|
|
+ const date = new Date(dateStr);
|
|
|
+ const now = new Date();
|
|
|
+
|
|
|
+ // 今天的日期
|
|
|
+ if (date.toDateString() === now.toDateString()) {
|
|
|
+ return `今天 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
|
|
}
|
|
|
- },
|
|
|
-
|
|
|
- // 跳转到设备详情页
|
|
|
- navigateToDetail(device) {
|
|
|
- // 根据设备类型跳转到不同的详情页
|
|
|
- let url = '';
|
|
|
|
|
|
- if (device.type === 'monitor') {
|
|
|
- url = `/pages/device-list/detail-camera?id=${device.id}`;
|
|
|
- } else if (device.type === 'sensor') {
|
|
|
- // 采集设备跳转到采集设备详情页,同时传递设备编码,便于判断设备子类型
|
|
|
- url = `/pages/device-list/detail-collector?id=${device.id}&code=${device.code}`;
|
|
|
- } else {
|
|
|
- // 其他类型设备暂时使用通用详情页
|
|
|
- url = `/pages/device-detail/index?id=${device.id}&type=${device.type}`;
|
|
|
+ // 一周内
|
|
|
+ const days = ['日', '一', '二', '三', '四', '五', '六'];
|
|
|
+ const dayDiff = Math.floor((now - date) / (24 * 60 * 60 * 1000));
|
|
|
+ if (dayDiff < 7) {
|
|
|
+ return `周${days[date.getDay()]} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
|
|
|
}
|
|
|
|
|
|
- uni.navigateTo({
|
|
|
- url: url
|
|
|
- });
|
|
|
- },
|
|
|
+ // 超过一周
|
|
|
+ return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
|
|
|
+ }, */
|
|
|
|
|
|
- // 处理刷新
|
|
|
- handleRefresh() {
|
|
|
- this.isRefreshing = true;
|
|
|
- this.page = 1;
|
|
|
- this.deviceList = [];
|
|
|
- this.loadDeviceData();
|
|
|
+ // 页面上拉触底事件
|
|
|
+ onReachBottom() {
|
|
|
+ if (this.loadMoreStatus === 'more') {
|
|
|
+ this.pageNum++;
|
|
|
+ this.loadDeviceList(false);
|
|
|
+ }
|
|
|
}
|
|
|
+ },
|
|
|
+
|
|
|
+ // 下拉刷新
|
|
|
+ onPullDownRefresh() {
|
|
|
+ this.loadDeviceList();
|
|
|
+ setTimeout(() => {
|
|
|
+ uni.stopPullDownRefresh();
|
|
|
+ }, 1000);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 页面显示
|
|
|
+ onShow() {
|
|
|
+ uni.setNavigationBarTitle({
|
|
|
+ title: this.deviceTypeName || '设备列表'
|
|
|
+ });
|
|
|
}
|
|
|
-}
|
|
|
+};
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
@@ -412,8 +538,6 @@ export default {
|
|
|
font-size: 80rpx;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
/* 容器样式 */
|
|
|
.container {
|
|
|
display: flex;
|
|
|
@@ -478,12 +602,13 @@ export default {
|
|
|
/* 状态筛选区域 */
|
|
|
.filter-section {
|
|
|
display: flex;
|
|
|
- padding: 24rpx 30rpx;
|
|
|
+ padding: 24rpx 24rpx;
|
|
|
background-color: #FFFFFF;
|
|
|
margin-bottom: 20rpx;
|
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
|
|
|
position: relative;
|
|
|
z-index: 4;
|
|
|
+ flex-wrap: wrap;
|
|
|
}
|
|
|
|
|
|
.filter-item {
|
|
|
@@ -496,6 +621,7 @@ export default {
|
|
|
border-radius: 30rpx;
|
|
|
transition: all 0.2s ease;
|
|
|
position: relative;
|
|
|
+ margin-bottom: 10rpx;
|
|
|
}
|
|
|
|
|
|
.filter-item.active {
|
|
|
@@ -504,8 +630,6 @@ export default {
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
.filter-dot {
|
|
|
width: 14rpx;
|
|
|
height: 14rpx;
|
|
|
@@ -523,6 +647,16 @@ export default {
|
|
|
box-shadow: 0 0 6rpx rgba(245, 108, 108, 0.5);
|
|
|
}
|
|
|
|
|
|
+.fault-dot {
|
|
|
+ background-color: #FF9800;
|
|
|
+ box-shadow: 0 0 6rpx rgba(255, 152, 0, 0.5);
|
|
|
+}
|
|
|
+
|
|
|
+.maintain-dot {
|
|
|
+ background-color: #2196F3;
|
|
|
+ box-shadow: 0 0 6rpx rgba(33, 150, 243, 0.5);
|
|
|
+}
|
|
|
+
|
|
|
/* 设备列表区域 */
|
|
|
.device-list {
|
|
|
flex: 1;
|
|
|
@@ -531,6 +665,7 @@ export default {
|
|
|
width: 100%;
|
|
|
position: relative;
|
|
|
z-index: 3;
|
|
|
+ height: calc(100vh - 220rpx);
|
|
|
}
|
|
|
|
|
|
/* 空数据提示 */
|
|
|
@@ -756,6 +891,24 @@ export default {
|
|
|
border-color: rgba(76, 175, 80, 0.3);
|
|
|
}
|
|
|
|
|
|
+.status-offline {
|
|
|
+ background-color: rgba(245, 108, 108, 0.1);
|
|
|
+ color: #F56C6C;
|
|
|
+ border-color: rgba(245, 108, 108, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+.status-fault {
|
|
|
+ background-color: rgba(255, 152, 0, 0.1);
|
|
|
+ color: #FF9800;
|
|
|
+ border-color: rgba(255, 152, 0, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+.status-maintain {
|
|
|
+ background-color: rgba(33, 150, 243, 0.1);
|
|
|
+ color: #2196F3;
|
|
|
+ border-color: rgba(33, 150, 243, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
.status-dot {
|
|
|
position: absolute;
|
|
|
width: 8rpx;
|
|
|
@@ -775,11 +928,14 @@ export default {
|
|
|
box-shadow: 0 0 4rpx rgba(245, 108, 108, 0.8);
|
|
|
}
|
|
|
|
|
|
-.status-offline {
|
|
|
- background-color: rgba(245, 108, 108, 0.1);
|
|
|
- color: #F56C6C;
|
|
|
- border-color: rgba(245, 108, 108, 0.3);
|
|
|
- padding-left: 32rpx;
|
|
|
+.status-dot.fault-dot {
|
|
|
+ background-color: #FF9800;
|
|
|
+ box-shadow: 0 0 4rpx rgba(255, 152, 0, 0.8);
|
|
|
+}
|
|
|
+
|
|
|
+.status-dot.maintain-dot {
|
|
|
+ background-color: #2196F3;
|
|
|
+ box-shadow: 0 0 4rpx rgba(33, 150, 243, 0.8);
|
|
|
}
|
|
|
|
|
|
@keyframes blink {
|
|
|
@@ -864,6 +1020,4 @@ export default {
|
|
|
0% { transform: rotate(0deg); }
|
|
|
100% { transform: rotate(360deg); }
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
</style>
|