Files
smartDriveEEUniApp/pages/speaking_training_edu/components/DeviceTab.vue
2026-01-09 19:41:18 +08:00

465 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<scroll-view
class="device-list"
scroll-y
>
<!-- 顶部工具栏总数 + 搜索条件 + 刷新 -->
<view class="device-toolbar">
<text class="toolbar-total">{{ filteredDeviceList.length }}</text>
<view class="toolbar-filters">
<!-- 状态筛选 -->
<picker
class="status-picker"
mode="selector"
:range="statusOptions"
:value="statusIndex"
@change="onStatusChange"
>
<view class="status-picker-inner">
<text class="status-label">状态</text>
<text class="status-value">{{ statusOptions[statusIndex] }}</text>
<uni-icons type="down" size="14" color="#999" />
</view>
</picker>
<!-- 设备编号 -->
<input
class="toolbar-input"
v-model="queryDeviceCode"
placeholder="设备编号"
placeholder-style="color:#B0B0B0"
confirm-type="search"
@confirm="onSearch"
/>
<!-- 电话 -->
<input
class="toolbar-input"
v-model="queryPhone"
placeholder="电话"
placeholder-style="color:#B0B0B0"
confirm-type="search"
@confirm="onSearch"
/>
</view>
<view class="toolbar-actions">
<view class="toolbar-btn" @click="onRefresh">
<uni-icons type="refresh" size="18" color="#2A68FF" />
<text>刷新</text>
</view>
</view>
</view>
<!-- 设备卡片列表 -->
<view
class="device-card"
v-for="(item, index) in filteredDeviceList"
:key="item.id || index"
>
<view class="card-header">
<text class="device-code">{{ item.deviceCode || '未填写设备编号' }}</text>
<view
class="bind-tag"
:class="item.bindStatus ? 'bind-tag--on' : 'bind-tag--off'"
>
<text>{{ item.bindStatus ? '已绑定' : '未绑定' }}</text>
</view>
</view>
<view class="card-body">
<view class="card-row">
<text class="card-label">家长电话</text>
<text class="card-value">{{ item.salesPhone || '-' }}</text>
</view>
<view class="card-row">
<text class="card-label">所属门店</text>
<text class="card-value">{{ item.dealershipName || '-' }}</text>
</view>
<view class="card-row">
<text class="card-label">创建时间</text>
<text class="card-value">{{ item.createTime || '-' }}</text>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="!loading && deviceList.length === 0" class="empty-box">
<text class="empty-text">暂无设备记录</text>
</view>
<!-- 底部加载/无更多提示 -->
<view v-if="deviceList.length > 0" class="empty-box">
<text class="empty-text" v-if="loading">加载中...</text>
<text class="empty-text" v-else-if="noMore">没有更多了</text>
</view>
</scroll-view>
</template>
<script>
import { get } from '@/common/request.js';
export default {
name: 'DeviceTab',
data() {
return {
statusOptions: ['全部状态', '已绑定', '未绑定'],
statusIndex: 0,
queryDeviceCode: '',
queryPhone: '',
deviceList: [],
page: {
current: 1,
size: 10,
},
loading: false,
total: 0,
noMore: false,
};
},
computed: {
// 根据筛选条件过滤设备列表
filteredDeviceList() {
return this.deviceList.filter((item) => {
// 状态筛选
if (this.statusIndex === 1 && !item.bindStatus) return false; // 只看已绑定
if (this.statusIndex === 2 && item.bindStatus) return false; // 只看未绑定
// 设备编号模糊匹配
if (
this.queryDeviceCode &&
!(item.deviceCode || '').includes(this.queryDeviceCode.trim())
) {
return false;
}
// 电话模糊匹配
if (
this.queryPhone &&
!(item.salesPhone || '').includes(this.queryPhone.trim())
) {
return false;
}
return true;
});
},
},
mounted() {
// 进入页面时拉取设备列表
this.fetchDeviceList(true);
},
methods: {
// 将 LocalDateTime 字符串格式化为 YYYY-MM-DD HH:mm:ss
formatDateTime(value) {
if (!value) return '-';
try {
const date = new Date(value);
if (Number.isNaN(date.getTime())) return value;
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
} catch (e) {
console.warn('格式化时间失败:', e, value);
return value;
}
},
buildQueryParams() {
const params = {
current: this.page.current,
size: this.page.size,
};
const trimmedDeviceCode = this.queryDeviceCode && this.queryDeviceCode.trim();
const trimmedPhone = this.queryPhone && this.queryPhone.trim();
if (trimmedDeviceCode) {
params.deviceCode = trimmedDeviceCode;
}
if (trimmedPhone) {
params.salesPhone = trimmedPhone;
}
// 绑定状态0=全部1=已绑定(true)2=未绑定(false)
if (this.statusIndex === 1) {
params.bindStatus = true;
} else if (this.statusIndex === 2) {
params.bindStatus = false;
}
// 目前门店筛选暂未在前端提供控件,如后续有需求可在此添加 dealershipId
return params;
},
async fetchDeviceList(reset = false) {
if (this.loading) return;
this.loading = true;
if (reset) {
this.page.current = 1;
this.noMore = false;
}
try {
const queryParams = this.buildQueryParams();
const res = await get('/api/deviceManagement/list', queryParams);
if (res.statusCode === 200 && res.data && res.data.success) {
const records = Array.isArray(res.data.data) ? res.data.data : [];
const mapped = records.map((item) => ({
id: item.id,
deviceCode: item.deviceCode,
salesPhone: item.salesPhone,
dealershipName: item.dealershipName,
bindStatus: item.bindStatus,
createTime: this.formatDateTime(item.createTime),
}));
if (reset) {
this.deviceList = mapped;
} else {
this.deviceList = [...this.deviceList, ...mapped];
}
// 保存分页信息
if (typeof res.data.current === 'number') {
this.page.current = res.data.current;
}
if (typeof res.data.size === 'number') {
this.page.size = res.data.size;
}
if (typeof res.data.total === 'number') {
this.total = res.data.total;
}
// 判断是否还有更多数据
const pages =
typeof res.data.pages === 'number'
? res.data.pages
: Math.ceil((this.total || 0) / this.page.size);
this.noMore = pages > 0 && this.page.current >= pages;
} else {
this.deviceList = [];
uni.showToast({
title: (res.data && res.data.message) || '获取设备列表失败',
icon: 'none',
});
}
} catch (error) {
console.error('获取设备列表失败:', error);
if (reset) {
this.deviceList = [];
}
if (reset) {
this.deviceList = [];
}
let msg = '获取设备列表失败,请稍后重试';
if (error.errMsg && error.errMsg.includes('timeout')) {
msg = '请求超时,请检查网络后重试';
}
uni.showToast({
title: msg,
icon: 'none',
});
} finally {
this.loading = false;
}
},
// 对外暴露的“加载更多”方法,供页面 onReachBottom 调用
loadMore() {
if (this.loading || this.noMore) return;
// 下一页
this.page.current += 1;
this.fetchDeviceList(false);
},
onStatusChange(e) {
this.statusIndex = Number(e.detail.value || 0);
this.onSearch();
},
onSearch() {
// 根据筛选条件重新从后端分页接口拉取列表
this.fetchDeviceList(true);
},
async onRefresh() {
await this.fetchDeviceList(true);
uni.showToast({
title: '刷新成功',
icon: 'none',
});
},
},
};
</script>
<style>
.device-list {
padding: 148rpx 16rpx 32rpx;
box-sizing: border-box;
background-color: #f5f5f5;
}
.device-toolbar {
display: flex;
align-items: center;
padding: 12rpx 12rpx;
margin-bottom: 24rpx;
background-color: #f8f8fa;
border-radius: 8rpx;
border: 1px solid #efeff2;
}
.toolbar-total {
font-size: 24rpx;
color: #666;
margin-right: 12rpx;
flex-shrink: 0;
}
.toolbar-filters {
flex: 1;
display: flex;
align-items: center;
gap: 8rpx;
overflow: hidden;
}
.status-picker {
flex-shrink: 0;
}
.status-picker-inner {
flex-direction: row;
display: flex;
align-items: center;
padding: 0 12rpx;
height: 56rpx;
border-radius: 999rpx;
background-color: #ffffff;
border: 1px solid #d6e3ff;
}
.status-label {
font-size: 24rpx;
color: #666;
margin-right: 6rpx;
}
.status-value {
font-size: 24rpx;
color: #333;
margin-right: 4rpx;
}
.toolbar-input {
flex: 1;
min-width: 120rpx;
height: 56rpx;
line-height: 56rpx;
background-color: #f5f6fa;
border-radius: 8rpx;
padding: 0 12rpx;
font-size: 24rpx;
box-sizing: border-box;
}
.toolbar-actions {
margin-left: auto;
display: flex;
align-items: center;
}
.toolbar-btn {
display: flex;
align-items: center;
padding: 0 16rpx;
height: 56rpx;
border-radius: 28rpx;
background-color: #ffffff;
border: 1px solid #d6e3ff;
}
.toolbar-btn text {
margin-left: 6rpx;
font-size: 24rpx;
color: #2a68ff;
}
.device-card {
background-color: #ffffff;
border-radius: 16rpx;
padding: 24rpx 24rpx 20rpx;
margin-bottom: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.03);
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16rpx;
}
.device-code {
font-size: 30rpx;
font-weight: 600;
color: #333;
}
.bind-tag {
min-width: 104rpx;
padding: 6rpx 16rpx;
border-radius: 999rpx;
font-size: 22rpx;
text-align: center;
}
.bind-tag--on {
background-color: #e6f6ec;
color: #2e7d32;
}
.bind-tag--off {
background-color: #fff4e5;
color: #ff9800;
}
.card-body {
border-top: 1px solid #f0f0f0;
padding-top: 16rpx;
}
.card-row {
display: flex;
align-items: center;
margin-bottom: 8rpx;
}
.card-label {
width: 160rpx;
font-size: 24rpx;
color: #999;
}
.card-value {
font-size: 26rpx;
color: #333;
}
.empty-box {
padding: 80rpx 0;
text-align: center;
}
.empty-text {
font-size: 26rpx;
color: #999;
}
</style>