myDevice.vue 12 KB
<template>
	<view class="main">
		<view class="toptitle">
			<view class="toptitle_left" @click="ht">
				<u-icon name="arrow-left" color="#fff" size="20"></u-icon>
			</view>
			<view class="toptitle_title">我的设备</view>
		</view>
		<view class="title">
			<view class="title-search-wrapper">
				<u-search shape="square" :clearabled="true" :showAction="false" placeholder="请输入设备名称/生产厂家/设备编号" v-model="form.keyword"></u-search>
				<view class="filter-btn" @click="showFlPicker = true">
					<text class="filter-btn-text">{{ selectedFlName || '关联产品' }}</text>
					<text class="filter-btn-icon">▼</text>
				</view>
			</view>
		</view>
		<view class="main-content">
			<view class="search-placeholder"></view>
			<u-picker 
				:show="showFlPicker" 
				:columns="flColumns" 
				@confirm="flConfirm" 
				@cancel="showFlPicker = false"
				class="fl-picker-custom"
			></u-picker>
			<view v-if="devices.length === 0 && !loading" class="box">
				<view class="top">
					<text class="f2">暂无设备数据</text>
				</view>
			</view>
			<view class="box" v-for="(item, index) in devices" :key="index">
			<view class="top" style="margin-bottom: 10px;">
				<text class="f1">{{ item.sbmc || '-' }}</text>
			</view>
			<view class="top2">
				<text class="f4">生产厂家</text>
				<text class="f5">{{ item.sccj || '-' }}</text>
			</view>
			<view class="top2">
				<text class="f4">购入时间</text>
				<text class="f5">{{ formatDate(item.gmsj) }}</text>
			</view>

			<view class="xx">

			</view>
			<view class="footer">
				<view class="top2">
					<text class="f4">设备状态</text>
					<view class="f5"><text
							:class="['status-pill', statusClass(item.sbzt)]">{{ item.sbzt || '未知状态' }}</text></view>
				</view>
				<view>
					<u-button size="small" type="primary" text="查看" @click="viewDetail(item)"></u-button>
				</view>
			</view>
		</view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				devices: [],
				loading: false,
				form: {
					currentPage: 1,
					pageSize: 20,
					sort: 'desc',
					keyword: "", // 搜索关键字
					fl: "" // 关联产品筛选
				},
				nv_length: 0,
				showFlPicker: false,
				flOptions: [], // 产品选项
				flColumns: [['全部']], // 产品选择器的列数据
				flMap: {}, // 产品名称到ID的映射
				selectedFlName: "", // 选中的产品名称
				searchTimer: null // 搜索防抖定时器
			}
		},
		onLoad() {
			// 重置设备列表和分页
			this.devices = []
			this.form.currentPage = 1
			this.nv_length = 0
			this.getflOptions() // 获取产品选项
			this.fetchDevices()
		},
		onUnload() {
			// 页面卸载时清除防抖定时器
			if(this.searchTimer){
				clearTimeout(this.searchTimer);
				this.searchTimer = null;
			}
		},
		watch: {
			'form.keyword'(newVal, oldVal) {
				console.log('keyword changed', newVal, oldVal)
				// 清除之前的定时器
				if(this.searchTimer){
					clearTimeout(this.searchTimer);
					this.searchTimer = null;
				}
				// 重置分页和列表
				this.form.pageSize = 20
				this.form.currentPage = 1
				this.devices = []
				// 设置防抖,500ms后执行搜索
				this.searchTimer = setTimeout(() => {
					this.fetchDevices();
					this.searchTimer = null;
				}, 500);
			},
			'form.fl'(newVal, oldVal){
				// 关联产品变化时立即搜索,不需要防抖
				this.form.pageSize = 20
				this.form.currentPage = 1
				this.devices = []
				// 如果有关键字搜索的防抖定时器,先清除它
				if(this.searchTimer){
					clearTimeout(this.searchTimer);
					this.searchTimer = null;
				}
				this.fetchDevices();
			},
		},
		onReachBottom() {
			// 如果正在搜索,不进行分页加载(搜索时一次性显示所有结果)
			if (this.form.keyword && this.form.keyword.trim() !== '') {
				uni.showToast({
					icon: "none",
					title: "已显示所有搜索结果"
				})
				return
			}
			
			const page = Math.ceil(this.nv_length / this.form.pageSize)
			if (page > parseInt(this.form.currentPage)) {
				this.form.currentPage = parseInt(this.form.currentPage) + 1
				this.fetchDevices(true)
			} else {
				uni.showToast({
					icon: "error",
					title: "没有更多内容"
				})
			}
		},
		methods: {
			// 获取产品选项(用于关联产品筛选,仅返回当前用户可查看的产品)
			getflOptions(){
				this.API.hqcpglAllowedList({
					currentPage: 1,
					pageSize: 1000 // 获取足够多的产品
				}).then(res => {
					if(res && res.code == 200 && res.data && res.data.list){
						this.flOptions = res.data.list;
						// 构建选择器的列数据(产品名称列表)
						const productNames = res.data.list.map(item => {
							// 显示格式:产品名称 (产品ID)
							const displayName = item.cpmc ? `${item.cpmc}${item.cpid ? ' (' + item.cpid + ')' : ''}` : (item.cpid || item.id);
							return displayName;
						});
						// 构建ID映射(产品名称到ID的映射)
						this.flMap = {};
						res.data.list.forEach(item => {
							const displayName = item.cpmc ? `${item.cpmc}${item.cpid ? ' (' + item.cpid + ')' : ''}` : (item.cpid || item.id);
							this.flMap[displayName] = item.id;
						});
						// 添加"全部"选项
						this.flColumns = [['全部', ...productNames]];
					} else {
						console.error('获取产品列表失败,响应:', res);
						this.flOptions = [];
						this.flColumns = [['全部']];
						this.flMap = {};
					}
				}).catch(err => {
					console.error('获取产品列表失败:', err);
					this.flOptions = [];
					this.flColumns = [['全部']];
					this.flMap = {};
				});
			},
			// 产品选择确认
			flConfirm(e){
				const selectedIndex = e.indexs[0];
				const selectedText = (e.value && e.value[0]) ? String(e.value[0]).trim() : '';
				if(selectedIndex === 0 || selectedText === '' || selectedText === '全部'){
					this.form.fl = '';
					this.selectedFlName = '';
				} else {
					// 优先从 flMap 取,否则按 cpid/产品名匹配(兼容 u-picker 返回值格式差异)
					let idVal = this.flMap[selectedText];
					if (idVal == null && this.flOptions && this.flOptions.length > 0) {
						const match = selectedText.match(/\(([^)]+)\)\s*$/);
						const cpidFromText = match ? match[1].trim() : null;
						const product = this.flOptions.find(p => 
							(cpidFromText && ((p.cpid || '') === cpidFromText || (p.id || '') === cpidFromText)) ||
							((p.cpmc || '') === selectedText) ||
							((p.cpmc || '') + (p.cpid ? ' (' + p.cpid + ')' : '') === selectedText)
						);
						if (product) idVal = product.id;
					}
					this.form.fl = idVal != null ? String(idVal) : '';
					this.selectedFlName = selectedText;
				}
				this.showFlPicker = false;
			},
			ht() {
				uni.navigateBack({
					delta: 1
				})
			},
			viewDetail(item) {
				try {
					// 传递设备ID到详情页,让详情页通过API获取完整数据
					const deviceId = item.id || item.Id || item.ID
					if (deviceId) {
						uni.navigateTo({
							url: `/pages/myDevice/detail?id=${deviceId}`
						})
					} else {
						uni.showToast({
							icon: 'error',
							title: '设备ID不存在'
						})
					}
				} catch (e) {
					console.error('跳转设备详情失败', e)
					uni.showToast({
						icon: 'error',
						title: '跳转失败'
					})
				}
			},
			fetchDevices(append = false) {
				if (this.loading) return
				this.loading = true
				
				// 权限由后端根据登录用户自动处理:
				// 管理员:查看所有设备
				// 非管理员:查看 所属客户 为本人、所属部门、或所属组织 的设备
				// 不再传 sskhbh,后端通过 token 获取当前用户并过滤
				
				// 构建查询参数
				// 如果有搜索关键字,获取更多数据以便前端过滤(搜索时不分页,一次性获取100条)
				const isSearching = this.form.keyword && this.form.keyword.trim() !== ''
				const params = {
					currentPage: isSearching ? 1 : this.form.currentPage,
					pageSize: isSearching ? 100 : this.form.pageSize,
					sort: this.form.sort,
					sidx: this.form.sidx || ''
				};
				
				// 如果有产品筛选,添加产品ID参数(关联产品)
				const flVal = this.form.fl != null ? String(this.form.fl).trim() : '';
				if (flVal !== '') {
					params.fl = flVal;
				}
				
				// 统一使用普通API(支持完整字段返回)
				// 如果有搜索关键字,先获取数据,然后在前端进行多字段过滤
				uni.showLoading({
					title: this.form.keyword && this.form.keyword.trim() !== '' ? '搜索中...' : '加载中...'
				})
				this.API.hqwdsb(params).then(res => {
					console.log('设备列表', res.data)
					uni.hideLoading()
					this.loading = false
					if (res && res.code === 200 && res.data) {
						let rows = Array.isArray(res.data.list) ? res.data.list : []
						
						// 如果有搜索关键字,在前端进行多字段过滤
						if (isSearching) {
							const keyword = this.form.keyword.trim().toLowerCase()
							const allFilteredRows = rows.filter(item => {
								// 搜索设备名称、生产厂家、设备编号、出厂编号等字段
								return (item.sbmc && String(item.sbmc).toLowerCase().includes(keyword)) ||
								       (item.sccj && String(item.sccj).toLowerCase().includes(keyword)) ||
								       (item.id && String(item.id).toLowerCase().includes(keyword)) ||
								       (item.ccbh && String(item.ccbh).toLowerCase().includes(keyword)) ||
								       (item.dysb && String(item.dysb).toLowerCase().includes(keyword)) ||
								       (item.dysbbh && String(item.dysbbh).toLowerCase().includes(keyword))
							})
							
							// 搜索时,重置分页并显示所有过滤结果
							if (!append) {
								rows = allFilteredRows
								this.nv_length = allFilteredRows.length
							} else {
								// 追加模式:合并结果
								rows = allFilteredRows
								this.nv_length = allFilteredRows.length
							}
						} else {
							// 正常分页
							this.nv_length = res.data.pagination ? res.data.pagination.total : 0
						}
						
						if (!append) this.devices = []
						for (let i = 0; i < rows.length; i++) {
							this.devices.push(rows[i])
						}
						if (this.devices.length === 0 && !append) {
							uni.showToast({
								icon: 'none',
								title: '暂无数据'
							})
						}
					} else {
						if (!append) this.devices = []
						uni.showToast({
							icon: 'none',
							title: res?.msg || '加载失败'
						})
					}
				}).catch(err => {
					console.error('获取设备列表失败', err)
					uni.hideLoading()
					this.loading = false
					if (!append) this.devices = []
					uni.showToast({
						icon: "error",
						title: "获取设备列表失败"
					})
				})
			},
			formatDate(ts) {
				if (!ts) return '-'
				const d = new Date(ts)
				const y = d.getFullYear()
				const m = String(d.getMonth() + 1).padStart(2, '0')
				const day = String(d.getDate()).padStart(2, '0')
				return `${y}-${m}-${day}`
			},
			statusClass(status) {
				if (status === null || status === undefined) return 'status-unknown'
				const s = String(status).trim().toLowerCase()
				// 数字编码兼容:1运行、2检修、3停机、4报废
				const num = Number(s)
				if (!isNaN(num)) {
					if (num === 1) return 'status-running'
					if (num === 2) return 'status-repair'
					if (num === 3) return 'status-stop'
					if (num === 4) return 'status-scrap'
				}
				// 中文关键字模糊匹配
				if (s.includes('运')) return 'status-running'
				if (s.includes('检') || s.includes('修')) return 'status-repair'
				if (s.includes('停') || s.includes('关')) return 'status-stop'
				if (s.includes('废')) return 'status-scrap'
				// 英文/拼音兼容
				if (s.includes('running') || s.includes('run') || s.includes('online')) return 'status-running'
				if (s.includes('repair') || s.includes('maint') || s.includes('maintenance') || s.includes('fix'))
				return 'status-repair'
				if (s.includes('stop') || s.includes('down') || s.includes('offline')) return 'status-stop'
				if (s.includes('scrap') || s.includes('decomm')) return 'status-scrap'
				return 'status-unknown'
			}
		}
	}
</script>

<style scoped lang="scss">
	@import 'myDevice.scss';
</style>