first commit

This commit is contained in:
chz
2026-01-15 15:05:29 +08:00
commit 0ec405e137
66 changed files with 8572 additions and 0 deletions
+176
View File
@@ -0,0 +1,176 @@
const { deviceApi } = require("../../utils/api");
Page({
data: {
device_id: null,
device: null,
subDevices: [],
loading: true,
loadError: false,
// 子设备状态相关
statusDialogVisible: false,
selectedStatus: 0,
currentSubDeviceForStatus: null,
deviceStatusMap: {
0: "空闲",
1: "使用中",
2: "维护中",
},
// 状态选择选项
statusOptions: [
{ value: 0, label: "空闲", color: "#06a56c" },
{ value: 1, label: "使用中", color: "#1890ff" },
{ value: 2, label: "维护中", color: "#fa8c16" },
],
},
onLoad(options) {
if (options.device_id) {
this.setData({ device_id: options.device_id });
this.loadDeviceInfo();
this.loadSubDevices();
} else {
wx.showToast({
title: "缺少设备ID",
icon: "error",
});
wx.navigateBack();
}
},
onShow() {
// 页面显示时刷新数据
if (this.data.device_id) {
this.loadSubDevices();
}
},
// 加载设备信息
async loadDeviceInfo() {
try {
const response = await deviceApi.getDevices();
const deviceList = response.data.list || response.data || [];
const device = deviceList.find((d) => d.id === parseInt(this.data.device_id));
if (device) {
this.setData({ device });
}
} catch (error) {
// 加载设备信息失败,继续执行
}
},
// 加载子设备列表
async loadSubDevices() {
this.setData({ loading: true, loadError: false });
try {
const response = await deviceApi.getSubDevicesByDeviceId(this.data.device_id);
const subDevices = response.data.list || response.data || [];
// 为每个子设备添加状态信息
const formattedSubDevices = subDevices.map((subDevice) => ({
...subDevice,
status: subDevice.status !== undefined ? subDevice.status : 0,
statusText: this.data.deviceStatusMap[subDevice.status !== undefined ? subDevice.status : 0],
}));
this.setData({
subDevices: formattedSubDevices,
loading: false,
});
} catch (error) {
this.setData({
loadError: true,
loading: false,
});
}
},
// 显示状态更改对话框
showStatusDialog(e) {
const subDevice = e.currentTarget.dataset.subdevice;
this.setData({
statusDialogVisible: true,
selectedStatus: subDevice.status !== undefined ? subDevice.status : 0,
currentSubDeviceForStatus: subDevice,
});
},
// 关闭状态对话框
closeStatusDialog() {
this.setData({
statusDialogVisible: false,
selectedStatus: 0,
currentSubDeviceForStatus: null,
});
},
// 选择状态
selectStatus(e) {
const status = parseInt(e.currentTarget.dataset.status);
this.setData({
selectedStatus: status,
});
},
// 确认状态更改
async confirmStatusChange() {
const { selectedStatus, currentSubDeviceForStatus } = this.data;
try {
await deviceApi.updateSubDevice(currentSubDeviceForStatus.id, {
status: selectedStatus,
name: currentSubDeviceForStatus.name,
});
wx.showToast({
title: "状态更新成功",
icon: "success",
});
this.closeStatusDialog();
this.loadSubDevices();
} catch (error) {
wx.showToast({
title: "状态更新失败",
icon: "error",
});
}
},
// 查看子设备保养记录
viewMaintenance(e) {
const subDevice = e.currentTarget.dataset.subdevice;
wx.navigateTo({
url: `/pages/device_maintenance/index?sub_device_id=${subDevice.id}`,
});
},
// 添加保养记录
addMaintenance(e) {
const subDevice = e.currentTarget.dataset.subdevice;
wx.navigateTo({
url: `/pages/device_maintenance/create/index?sub_device_id=${subDevice.id}`,
});
},
// 返回上一页
onBack() {
wx.navigateBack();
},
// 下拉刷新
onPullDownRefresh() {
this.loadSubDevices()
.then(() => {
wx.stopPullDownRefresh();
})
.catch(() => {
wx.stopPullDownRefresh();
});
},
});
+16
View File
@@ -0,0 +1,16 @@
{
"navigationBarTitleText": "子设备管理",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"backgroundColor": "#f7f8fa",
"enablePullDownRefresh": true,
"usingComponents": {
"t-toast": "tdesign-miniprogram/toast/toast",
"t-dialog": "tdesign-miniprogram/dialog/dialog",
"t-skeleton": "tdesign-miniprogram/skeleton/skeleton",
"t-empty": "tdesign-miniprogram/empty/empty",
"t-icon": "tdesign-miniprogram/icon/icon",
"t-button": "tdesign-miniprogram/button/button",
"t-tag": "tdesign-miniprogram/tag/tag"
}
}
+122
View File
@@ -0,0 +1,122 @@
<view class="subdevices-page">
<!-- 页面头部 -->
<view class="page-header">
<view class="device-info">
<view class="device-name">{{device.name}}</view>
<view class="device-meta">
<text class="subdevice-count">子设备: {{subDevices.length}}个</text>
</view>
</view>
</view>
<!-- 加载状态 -->
<block wx:if="{{loading}}">
<view class="loading-container">
<t-skeleton theme="paragraph" loading />
<t-skeleton theme="paragraph" loading />
<t-skeleton theme="paragraph" loading />
</view>
</block>
<!-- 错误状态 -->
<block wx:elif="{{loadError}}">
<view class="error-container">
<t-empty icon="error-circle" description="加载失败,请下拉刷新重试" />
</view>
</block>
<!-- 子设备列表 -->
<block wx:elif="{{subDevices.length > 0}}">
<view class="subdevices-list">
<view
wx:for="{{subDevices}}"
wx:key="id"
class="subdevice-card">
<view class="card-header">
<view class="subdevice-info">
<view class="subdevice-name">{{item.name}}</view>
<view class="subdevice-meta">
<text class="subdevice-status">
状态: <text class="status-badge status-{{item.status}}">{{item.statusText}}</text>
</text>
</view>
</view>
<view class="usage-info">
<text class="usage-count">{{item.usage_count || 0}}次</text>
<text class="usage-label">使用</text>
</view>
</view>
<view wx:if="{{item.description}}" class="subdevice-desc">
<text>{{item.description}}</text>
</view>
<view class="card-actions">
<t-button
size="small"
variant="outline"
bind:tap="viewMaintenance"
data-subdevice="{{item}}">
保养记录
</t-button>
<t-button
size="small"
variant="outline"
bind:tap="addMaintenance"
data-subdevice="{{item}}">
添加保养
</t-button>
<t-button
size="small"
variant="outline"
bind:tap="showStatusDialog"
data-subdevice="{{item}}">
更改状态
</t-button>
</view>
</view>
</view>
</block>
<!-- 空状态 -->
<block wx:else>
<view class="empty-container">
<t-empty icon="device" description="暂无子设备" />
</view>
</block>
<!-- 状态更改对话框 -->
<view class="dialog-mask" wx:if="{{statusDialogVisible}}" bindtap="closeStatusDialog">
<view class="dialog-container" catchtap="">
<view class="dialog-header">
<text class="dialog-title">更改子设备状态</text>
<text class="dialog-close" bindtap="closeStatusDialog">×</text>
</view>
<view class="dialog-body">
<view class="status-current">
<text>当前状态:{{currentSubDeviceForStatus ? deviceStatusMap[currentSubDeviceForStatus.status] : '未知'}}</text>
</view>
<view class="status-options">
<view
wx:for="{{statusOptions}}"
wx:key="value"
class="status-option {{selectedStatus === item.value ? 'selected' : ''}}"
catchtap="selectStatus"
data-status="{{item.value}}">
<text class="status-dot" style="background-color: {{item.color}}"></text>
<text class="status-label">{{item.label}}</text>
<text class="status-check" wx:if="{{selectedStatus === item.value}}">✓</text>
</view>
</view>
</view>
<view class="dialog-footer">
<button class="dialog-btn cancel-btn" bindtap="closeStatusDialog">取消</button>
<button class="dialog-btn confirm-btn" bindtap="confirmStatusChange">确定</button>
</view>
</view>
</view>
</view>
+340
View File
@@ -0,0 +1,340 @@
.subdevices-page {
padding: 0;
background: #f5f5f5;
min-height: 100vh;
}
/* 页面头部 */
.page-header {
display: flex;
align-items: center;
padding: 20rpx 30rpx;
background: #fff;
border-bottom: 1rpx solid #e8e8e8;
}
.header-left {
display: flex;
align-items: center;
flex: 1;
}
.back-btn {
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.device-info {
flex: 1;
}
.device-name {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 8rpx;
}
.device-meta {
font-size: 24rpx;
color: #666;
}
.subdevice-count {
color: #0052d9;
font-weight: 500;
}
/* 加载和错误状态 */
.loading-container,
.error-container,
.empty-container {
padding: 100rpx 30rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* 子设备列表 */
.subdevices-list {
padding: 20rpx 30rpx;
}
.subdevice-card {
background: #fff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 16rpx;
}
.subdevice-info {
flex: 1;
}
.subdevice-name {
font-size: 30rpx;
font-weight: 500;
color: #333;
margin-bottom: 8rpx;
}
.subdevice-meta {
display: flex;
align-items: center;
gap: 20rpx;
font-size: 24rpx;
color: #666;
}
.subdevice-id {
color: #999;
}
.subdevice-status {
display: flex;
align-items: center;
}
.status-badge {
font-size: 24rpx;
padding: 2rpx 8rpx;
border-radius: 4rpx;
display: inline-block;
font-weight: 500;
margin-left: 8rpx;
}
.status-0 {
color: #06a56c;
background-color: #e8f6f1;
}
.status-1 {
color: #1890ff;
background-color: #e6f7ff;
}
.status-2 {
color: #fa8c16;
background-color: #fff7e6;
}
.usage-info {
text-align: center;
padding: 12rpx;
background: #f0f2f5;
border-radius: 8rpx;
min-width: 100rpx;
}
.usage-count {
display: block;
font-size: 28rpx;
font-weight: bold;
color: #0052d9;
}
.usage-label {
font-size: 22rpx;
color: #666;
}
.subdevice-desc {
font-size: 26rpx;
color: #666;
margin-bottom: 16rpx;
padding: 12rpx;
background: #f8f9fa;
border-radius: 6rpx;
line-height: 1.4;
}
.card-actions {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
.card-actions .t-button {
flex: 1;
min-width: 120rpx;
font-size: 24rpx;
height: 60rpx;
line-height: 60rpx;
}
/* 状态选择器 */
.status-options {
display: flex;
flex-direction: column;
gap: 16rpx;
padding: 20rpx 0;
}
.status-option {
display: flex;
align-items: center;
padding: 24rpx 32rpx;
border-radius: 8rpx;
background-color: #f7f8fa;
border: 2rpx solid transparent;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
}
.status-option.selected {
background-color: #eef4ff;
border-color: #0052d9;
}
.status-option:active {
background-color: #e7e9eb;
}
.status-label {
margin-left: 16rpx;
font-size: 28rpx;
color: #333;
flex: 1;
}
.status-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
display: inline-block;
flex-shrink: 0;
}
.status-check {
position: absolute;
right: 32rpx;
color: #0052d9;
font-size: 32rpx;
font-weight: bold;
}
/* 自定义弹窗样式 */
.dialog-mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.dialog-container {
width: 600rpx;
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
animation: slideUp 0.3s ease-out;
}
@keyframes slideUp {
from {
transform: translateY(100rpx);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.dialog-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.dialog-close {
font-size: 40rpx;
color: #999;
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
.dialog-close:active {
background-color: #f5f5f5;
}
.dialog-body {
padding: 30rpx;
max-height: 600rpx;
overflow-y: auto;
}
.dialog-footer {
display: flex;
border-top: 1rpx solid #f0f0f0;
}
.dialog-btn {
flex: 1;
height: 100rpx;
border: none;
border-radius: 0;
font-size: 28rpx;
background-color: #fff;
}
.dialog-btn:active {
opacity: 0.8;
}
.cancel-btn {
color: #666;
border-right: 1rpx solid #f0f0f0;
}
.confirm-btn {
color: #0052d9;
font-weight: 500;
}
/* 弹窗内容区域 */
.dialog-content {
padding: 20rpx;
}
.status-current {
font-size: 26rpx;
color: #666;
margin-bottom: 20rpx;
padding: 16rpx;
background-color: #f8f9fa;
border-radius: 6rpx;
text-align: center;
}