Files
smartDriveEEUniApp/pages/ucenter/components/DeviceListPopup.vue
2026-01-11 12:11:01 +08:00

520 lines
13 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>
<uni-popup ref="deviceListPopup" type="bottom" :safe-area="false">
<view class="device-list-popup">
<!-- 顶部标题栏 -->
<view class="popup-header">
<text class="popup-title">设备列表</text>
<view class="popup-close" @click="close">
<uni-icons type="close" size="20" color="#666" />
</view>
</view>
<!-- 设备列表内容 -->
<scroll-view
class="device-list-content"
scroll-y
@scrolltolower="onScrollToLower"
>
<!-- 顶部工具栏总数 + 搜索条件 + 刷新 -->
<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>
</view>
</uni-popup>
</template>
<script>
import { get } from '@/common/request.js';
export default {
name: 'DeviceListPopup',
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;
});
},
},
methods: {
/**
* 打开设备列表弹窗
*/
open() {
if (this.$refs.deviceListPopup) {
this.$refs.deviceListPopup.open();
// 打开时重新加载数据
this.fetchDeviceList(true);
}
},
/**
* 关闭设备列表弹窗
*/
close() {
if (this.$refs.deviceListPopup) {
this.$refs.deviceListPopup.close();
}
},
// 将 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 = [];
}
let msg = '获取设备列表失败,请稍后重试';
if (error.errMsg && error.errMsg.includes('timeout')) {
msg = '请求超时,请检查网络后重试';
}
uni.showToast({
title: msg,
icon: 'none',
});
} finally {
this.loading = false;
}
},
// 滚动到底部时加载更多
onScrollToLower() {
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 lang="scss" scoped>
.device-list-popup {
background-color: #f5f5f5;
border-radius: 24rpx 24rpx 0 0;
max-height: 80vh;
display: flex;
flex-direction: column;
}
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 32rpx;
background-color: #ffffff;
border-bottom: 1px solid #e0e0e0;
border-radius: 24rpx 24rpx 0 0;
}
.popup-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
}
.popup-close {
padding: 8rpx;
display: flex;
align-items: center;
justify-content: center;
}
.device-list-content {
flex: 1;
padding: 16rpx;
box-sizing: border-box;
}
.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>