You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
862 lines
18 KiB
862 lines
18 KiB
<template> |
|
<ifrm ref="ifrm"> |
|
<view class="container"> |
|
<!-- Content --> |
|
<scroll-view |
|
class="scroll-content" |
|
scroll-y |
|
refresher-enabled |
|
:refresher-triggered="isRefreshing" |
|
@refresherrefresh="onRefresh" |
|
@scrolltolower="handleLoadMore" |
|
lower-threshold="100" |
|
> |
|
<view class="content"> |
|
<!-- 站点码输入区域 --> |
|
<view class="input-section"> |
|
<text class="label">站点码:</text> |
|
<view class="input-wrapper"> |
|
<input |
|
class="input" |
|
type="text" |
|
placeholder="站点码" |
|
:value="siteCode" |
|
@input="handleInput" |
|
@confirm="handleSearch" |
|
confirm-type="search" |
|
/> |
|
<!-- <view class="scan-btn" @tap="handleScan"> |
|
<text class="scan-icon">⚏</text> |
|
</view> --> |
|
</view> |
|
</view> |
|
|
|
<!-- 空状态 --> |
|
<view v-if="sites.length === 0" class="empty-state"> |
|
<view class="empty-icon"> |
|
<text class="icon-large">⚏</text> |
|
</view> |
|
<text class="empty-title">未找到匹配的站点</text> |
|
<text class="empty-desc">请尝试其他站点码</text> |
|
</view> |
|
|
|
<!-- 站点列表 --> |
|
<view v-else class="site-list"> |
|
<view class="list-header"> |
|
<text class="list-title">{{ |
|
hasSearched ? "搜索结果" : "全部站点" |
|
}}</text> |
|
<view class="site-count">{{ page.total }} 个站点</view> |
|
</view> |
|
|
|
<view v-for="(site,index) in displayedSites" :key="index" class="site-card"> |
|
<!-- 头部 --> |
|
<view class="card-header"> |
|
<view class="card-info"> |
|
<text class="site-name">{{ site.stationName }}</text> |
|
<text class="site-code">站点编码: {{ site.stationCode }}</text> |
|
</view> |
|
<view class="status-btn" @tap="handleStatusChange(site)"> |
|
{{ site.stationStatus == "1" ? "闲置" : "占用" }} |
|
</view> |
|
</view> |
|
|
|
<!-- 详情 --> |
|
<view class="card-details"> |
|
<view class="detail-row"> |
|
<text class="detail-label">站点区域</text> |
|
<text class="detail-value">{{ site.stationRegion }}</text> |
|
</view> |
|
|
|
<view class="detail-row"> |
|
<text class="detail-label">站点楼层</text> |
|
<text class="detail-value">{{ site.stationPosition }}楼</text> |
|
</view> |
|
|
|
<view class="detail-row"> |
|
<text class="detail-label">作业中心</text> |
|
<text class="detail-value">{{ site.wcName }}</text> |
|
</view> |
|
|
|
<view class="detail-row"> |
|
<text class="detail-label">站点状态</text> |
|
<view |
|
:class="['status-badge', getStatusClass(site.stationStatus)]" |
|
> |
|
<view class="status-dot"> |
|
<view class="dot-ping"></view> |
|
<view class="dot-core"></view> |
|
</view> |
|
<text class="status-text">{{ |
|
getStatusLabel(site.stationStatus) |
|
}}</text> |
|
</view> |
|
</view> |
|
</view> |
|
</view> |
|
|
|
<!-- 加载中 --> |
|
<view v-if="isLoading" class="loading"> |
|
<text class="loading-text">加载中...</text> |
|
</view> |
|
</view> |
|
</view> |
|
</scroll-view> |
|
|
|
<!-- 确认对话框 --> |
|
<view |
|
v-if="confirmDialog.isOpen" |
|
class="modal-overlay" |
|
@tap="cancelStatusChange" |
|
> |
|
<view class="modal-content" @tap.stop> |
|
<text class="modal-title">确认状态变更</text> |
|
<text class="modal-desc"> |
|
确认将站点 |
|
<text class="bold">{{ confirmDialog.siteName }}</text> 的状态从 |
|
<text |
|
:class="[ |
|
'bold', |
|
confirmDialog.currentStatus === '占用' |
|
? 'text-red' |
|
: 'text-green', |
|
]" |
|
> |
|
{{ confirmDialog.currentStatus }} |
|
</text> |
|
变更为 |
|
<text |
|
:class="[ |
|
'bold', |
|
confirmDialog.newStatus === '占用' ? 'text-red' : 'text-green', |
|
]" |
|
> |
|
{{ confirmDialog.newStatus }} |
|
</text> |
|
吗? |
|
</text> |
|
<view class="modal-actions"> |
|
<view class="modal-btn btn-cancel" @tap="cancelStatusChange"> |
|
取消 |
|
</view> |
|
<view class="modal-btn btn-confirm" @tap="confirmStatusChange"> |
|
确认 |
|
</view> |
|
</view> |
|
</view> |
|
</view> |
|
</view> |
|
</ifrm> |
|
</template> |
|
|
|
<script> |
|
import ifrm from "@/pages/index/ifrm"; |
|
export default { |
|
components: { |
|
ifrm, |
|
}, |
|
onNavigationBarButtonTap(btn) { |
|
this.$refs.ifrm.topMenuClick(btn); |
|
}, |
|
data() { |
|
return { |
|
siteCode: "", |
|
sites: [], // 当前显示的站点列表 |
|
allSites: [], // 缓存所有数据(可选) |
|
hasSearched: false, |
|
isLoading: false, |
|
isRefreshing: false, |
|
noMoreData: false, // 是否没有更多数据 |
|
confirmDialog: { |
|
isOpen: false, |
|
siteId: null, |
|
siteName: "", |
|
currentStatus: "", |
|
newStatus: "", |
|
}, |
|
page: { |
|
pageSize: 10, |
|
currentPage: 1, |
|
total: 0, |
|
}, |
|
searchParams: {}, // 搜索参数 |
|
}; |
|
}, |
|
|
|
computed: { |
|
displayedSites() { |
|
return this.sites.slice(0, this.displayCount); |
|
}, |
|
}, |
|
|
|
mounted() { |
|
this.initData(); |
|
}, |
|
|
|
methods: { |
|
// 添加方法 |
|
onRefresh() { |
|
this.isRefreshing = true; |
|
this.loadSites(true).then(() => { |
|
this.isRefreshing = false; |
|
}); |
|
}, |
|
// 初始化数据 |
|
initData() { |
|
this.loadSites(true); |
|
}, |
|
// 加载站点数据 |
|
loadSites(isRefresh = false) { |
|
if (this.isLoading || (this.noMoreData && !isRefresh)) { |
|
return; |
|
} |
|
|
|
this.isLoading = true; |
|
|
|
if (isRefresh) { |
|
this.page.currentPage = 1; |
|
this.sites = []; |
|
this.noMoreData = false; |
|
} |
|
|
|
this.$u.api |
|
.getStationList({ |
|
pageSize: this.page.pageSize, |
|
currentPage: this.page.currentPage, |
|
stationCode: this.siteCode ? this.siteCode : null, |
|
...this.searchParams, |
|
}) |
|
.then((res) => { |
|
const records = res.data?.records || []; |
|
const total = res.data?.total || 0; |
|
|
|
// 处理数据格式 |
|
const formattedData = records.map((item) => ({ |
|
id: item.id, |
|
code: item.stationCode, |
|
name: item.stationName, |
|
area: item.wcName || "", |
|
floor: item.stationPosition || "", |
|
workCenter: item.wcName || "", |
|
status: item.stationStatus == "1" ? "occupied" : "available", |
|
...item, |
|
})); |
|
|
|
if (isRefresh) { |
|
this.sites = formattedData; |
|
} else { |
|
this.sites = [...this.sites, ...formattedData]; |
|
} |
|
|
|
this.page.total = total; |
|
|
|
// 判断是否还有更多数据 |
|
if (this.sites.length >= total) { |
|
this.noMoreData = true; |
|
} |
|
|
|
this.isLoading = false; |
|
}) |
|
.catch((err) => { |
|
this.isLoading = false; |
|
uni.showToast({ |
|
title: "加载失败", |
|
icon: "error", |
|
}); |
|
}); |
|
}, |
|
|
|
// 输入框输入事件 |
|
handleInput(e) { |
|
const value = e.detail.value; |
|
this.siteCode = value; |
|
}, |
|
|
|
// 回车搜索 |
|
handleSearch() { |
|
this.page.currentPage = 1; |
|
this.sites = []; |
|
this.noMoreData = false; |
|
this.loadSites(true); |
|
// 收起键盘 |
|
uni.hideKeyboard(); |
|
}, |
|
|
|
// 过滤站点 |
|
filterSites(keyword) { |
|
const lowerKeyword = keyword.toLowerCase(); |
|
this.sites = this.allSites.filter( |
|
(site) => |
|
site.code.toLowerCase().includes(lowerKeyword) || |
|
site.name.toLowerCase().includes(lowerKeyword) || |
|
site.area.toLowerCase().includes(lowerKeyword) |
|
); |
|
this.hasSearched = true; |
|
}, |
|
|
|
// 模拟扫描 |
|
handleScan() { |
|
// 使用UniApp的扫码API |
|
uni.scanCode({ |
|
success: (res) => { |
|
this.siteCode = res.result; |
|
this.filterSites(res.result); |
|
}, |
|
fail: () => { |
|
// 扫码失败,使用模拟数据 |
|
const randomSite = |
|
this.allSites[Math.floor(Math.random() * this.allSites.length)]; |
|
this.siteCode = randomSite.code; |
|
this.sites = this.allSites.filter((s) => s.code === randomSite.code); |
|
this.hasSearched = true; |
|
}, |
|
}); |
|
}, |
|
|
|
// 重置 |
|
handleReset() { |
|
this.siteCode = ""; |
|
this.searchParams = {}; |
|
this.page.currentPage = 1; |
|
this.sites = []; |
|
this.noMoreData = false; |
|
this.loadSites(true); |
|
}, |
|
|
|
// 关闭页面 |
|
handleClose() { |
|
uni.navigateBack(); |
|
}, |
|
|
|
// 懒加载更多 |
|
handleLoadMore() { |
|
if (this.isLoading || this.noMoreData) { |
|
return; |
|
} |
|
|
|
this.page.currentPage += 1; |
|
this.loadSites(false); |
|
}, |
|
|
|
// 获取状态样式类 |
|
getStatusClass(status) { |
|
return status == 1 ? "status-occupied" : "status-available"; |
|
}, |
|
|
|
// 获取状态标签 |
|
getStatusLabel(status) { |
|
return status == 0 ? "闲置" : "占用"; |
|
}, |
|
|
|
// 打开状态变更确认对话框 |
|
handleStatusChange(site) { |
|
const newStatus = site.stationStatus == 0 ? 1 : 0; |
|
this.confirmDialog = { |
|
...site, |
|
isOpen: true, |
|
siteName: site.name, |
|
currentStatus: this.getStatusLabel(site.stationStatus), |
|
newStatus: this.getStatusLabel(newStatus), |
|
newStationStatus: newStatus, |
|
}; |
|
}, |
|
|
|
// 确认状态变更 |
|
confirmStatusChange() { |
|
if (this.confirmDialog.siteId !== null) { |
|
this.confirmDialog.stationStatus = this.confirmDialog.newStationStatus; |
|
console.log("获取当前列表的数据", this.confirmDialog); |
|
// 调用接口更新状态 |
|
this.$u.api |
|
.stationUpdate(this.confirmDialog) |
|
.then((res) => { |
|
// 更新本地数据 |
|
const index = this.sites.findIndex( |
|
(s) => s.id == this.confirmDialog.id |
|
); |
|
console.log( |
|
"更新站点状态成功", |
|
this.confirmDialog.id, |
|
this.sites, |
|
index |
|
); |
|
if (index !== -1) { |
|
this.sites[index].stationStatus = |
|
this.confirmDialog.stationStatus; |
|
} |
|
|
|
uni.showToast({ |
|
title: "状态更新成功", |
|
icon: "success", |
|
}); |
|
this.cancelStatusChange(); |
|
}) |
|
.catch((err) => { |
|
uni.showToast({ |
|
title: "状态更新失败", |
|
icon: "error", |
|
}); |
|
this.cancelStatusChange(); |
|
}); |
|
} |
|
|
|
}, |
|
|
|
// 取消确认 |
|
cancelStatusChange() { |
|
this.confirmDialog = { |
|
isOpen: false, |
|
|
|
}; |
|
}, |
|
}, |
|
}; |
|
</script> |
|
|
|
<style scoped> |
|
/* 容器 */ |
|
.container { |
|
width: 100%; |
|
height: 100vh; |
|
background-color: #f7f8fa; |
|
display: flex; |
|
flex-direction: column; |
|
} |
|
|
|
/* Header */ |
|
.header { |
|
background: linear-gradient(135deg, #4a7de8 0%, #5b8ff9 100%); |
|
padding: 32rpx; |
|
display: flex; |
|
align-items: center; |
|
justify-content: space-between; |
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1); |
|
} |
|
|
|
.header-left { |
|
display: flex; |
|
align-items: center; |
|
gap: 16rpx; |
|
} |
|
|
|
.header-btn { |
|
width: 72rpx; |
|
height: 72rpx; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
border-radius: 50%; |
|
transition: all 0.2s; |
|
} |
|
|
|
.header-btn:active { |
|
background-color: rgba(255, 255, 255, 0.2); |
|
transform: scale(0.95); |
|
} |
|
|
|
.icon { |
|
color: #ffffff; |
|
font-size: 40rpx; |
|
font-weight: bold; |
|
} |
|
|
|
.header-title { |
|
color: #ffffff; |
|
font-size: 36rpx; |
|
font-weight: 600; |
|
} |
|
|
|
/* 滚动内容 */ |
|
.scroll-content { |
|
flex: 1; |
|
height: 100%; |
|
} |
|
|
|
.content { |
|
padding: 32rpx; |
|
padding-bottom: 60rpx; |
|
} |
|
|
|
/* 输入区域 */ |
|
.input-section { |
|
margin-bottom: 48rpx; |
|
} |
|
|
|
.label { |
|
color: #374151; |
|
font-size: 28rpx; |
|
font-weight: 500; |
|
display: block; |
|
margin-bottom: 24rpx; |
|
} |
|
|
|
.input-wrapper { |
|
position: relative; |
|
display: flex; |
|
align-items: center; |
|
} |
|
|
|
.input { |
|
width: 100%; |
|
padding: 24rpx 32rpx; |
|
/* padding-right: 100rpx; */ |
|
background-color: #ffffff; |
|
border: 2rpx solid #e5e7eb; |
|
border-radius: 16rpx; |
|
color: #374151; |
|
font-size: 28rpx; |
|
} |
|
|
|
.input::placeholder { |
|
color: #9ca3af; |
|
} |
|
|
|
.scan-btn { |
|
position: absolute; |
|
right: 16rpx; |
|
width: 72rpx; |
|
height: 72rpx; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
border-radius: 12rpx; |
|
transition: all 0.2s; |
|
} |
|
|
|
.scan-btn:active { |
|
background-color: #f3f4f6; |
|
transform: scale(0.95); |
|
} |
|
|
|
.scan-icon { |
|
color: #5b8ff9; |
|
font-size: 40rpx; |
|
} |
|
|
|
/* 空状态 */ |
|
.empty-state { |
|
text-align: center; |
|
padding: 120rpx 0; |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
} |
|
|
|
.empty-icon { |
|
width: 160rpx; |
|
height: 160rpx; |
|
background-color: #f3f4f6; |
|
border-radius: 32rpx; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
margin-bottom: 32rpx; |
|
} |
|
|
|
.icon-large { |
|
font-size: 80rpx; |
|
color: #9ca3af; |
|
} |
|
|
|
.empty-title { |
|
color: #6b7280; |
|
font-size: 28rpx; |
|
font-weight: 500; |
|
margin-bottom: 8rpx; |
|
} |
|
|
|
.empty-desc { |
|
color: #9ca3af; |
|
font-size: 24rpx; |
|
} |
|
|
|
/* 站点列表 */ |
|
.site-list { |
|
display: flex; |
|
flex-direction: column; |
|
gap: 24rpx; |
|
} |
|
|
|
.list-header { |
|
display: flex; |
|
align-items: center; |
|
justify-content: space-between; |
|
margin-bottom: 16rpx; |
|
} |
|
|
|
.list-title { |
|
color: #374151; |
|
font-size: 26rpx; |
|
font-weight: 600; |
|
} |
|
|
|
.site-count { |
|
background-color: #f3f4f6; |
|
color: #6b7280; |
|
font-size: 22rpx; |
|
padding: 6rpx 20rpx; |
|
border-radius: 999rpx; |
|
} |
|
|
|
/* 站点卡片 */ |
|
.site-card { |
|
background-color: #ffffff; |
|
border-radius: 20rpx; |
|
padding: 24rpx; |
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05); |
|
border: 2rpx solid #e5e7eb; |
|
} |
|
|
|
.card-header { |
|
display: flex; |
|
align-items: flex-start; |
|
justify-content: space-between; |
|
margin-bottom: 20rpx; |
|
padding-bottom: 20rpx; |
|
border-bottom: 2rpx solid #f3f4f6; |
|
} |
|
|
|
.card-info { |
|
flex: 1; |
|
padding-right: 20rpx; |
|
display: flex; |
|
flex-direction: column; |
|
} |
|
|
|
.site-name { |
|
color: #111827; |
|
font-size: 30rpx; |
|
font-weight: bold; |
|
margin-bottom: 6rpx; |
|
} |
|
|
|
.site-code { |
|
color: #6b7280; |
|
font-size: 22rpx; |
|
} |
|
|
|
.status-btn { |
|
padding: 10rpx 24rpx; |
|
border-radius: 10rpx; |
|
font-size: 22rpx; |
|
font-weight: 600; |
|
color: #ffffff; |
|
white-space: nowrap; |
|
transition: all 0.2s; |
|
background-color: #5b8ff9; |
|
box-shadow: 0 4rpx 12rpx rgba(91, 143, 249, 0.3); |
|
} |
|
|
|
.status-btn:active { |
|
transform: scale(0.95); |
|
} |
|
|
|
.btn-green { |
|
background-color: #10b981; |
|
} |
|
|
|
.btn-red { |
|
background-color: #ef4444; |
|
} |
|
|
|
/* 卡片详情 */ |
|
.card-details { |
|
display: flex; |
|
flex-direction: column; |
|
gap: 16rpx; |
|
} |
|
|
|
.detail-row { |
|
display: flex; |
|
align-items: center; |
|
justify-content: space-between; |
|
font-size: 26rpx; |
|
} |
|
|
|
.detail-label { |
|
color: #6b7280; |
|
} |
|
|
|
.detail-value { |
|
color: #111827; |
|
font-weight: 500; |
|
} |
|
|
|
/* 状态徽章 */ |
|
.status-badge { |
|
display: flex; |
|
align-items: center; |
|
gap: 10rpx; |
|
padding: 8rpx 16rpx; |
|
border-radius: 999rpx; |
|
} |
|
|
|
.status-occupied { |
|
background-color: #fef2f2; |
|
} |
|
|
|
.status-available { |
|
background-color: #f0fdf4; |
|
} |
|
|
|
.status-dot { |
|
position: relative; |
|
width: 12rpx; |
|
height: 12rpx; |
|
} |
|
|
|
.dot-ping { |
|
position: absolute; |
|
width: 100%; |
|
height: 100%; |
|
border-radius: 50%; |
|
animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite; |
|
opacity: 0.75; |
|
} |
|
|
|
.dot-core { |
|
position: relative; |
|
width: 100%; |
|
height: 100%; |
|
border-radius: 50%; |
|
} |
|
|
|
.status-occupied .dot-ping, |
|
.status-occupied .dot-core { |
|
background-color: #ef4444; |
|
} |
|
|
|
.status-available .dot-ping, |
|
.status-available .dot-core { |
|
background-color: #10b981; |
|
} |
|
|
|
.status-text { |
|
font-size: 24rpx; |
|
font-weight: 600; |
|
} |
|
|
|
.status-occupied .status-text { |
|
color: #b91c1c; |
|
} |
|
|
|
.status-available .status-text { |
|
color: #15803d; |
|
} |
|
|
|
@keyframes ping { |
|
75%, |
|
100% { |
|
transform: scale(2); |
|
opacity: 0; |
|
} |
|
} |
|
|
|
/* 加载中 */ |
|
.loading { |
|
text-align: center; |
|
padding: 32rpx 0; |
|
} |
|
|
|
.loading-text { |
|
color: #6b7280; |
|
font-size: 28rpx; |
|
font-weight: 500; |
|
} |
|
|
|
/* 模态框 */ |
|
.modal-overlay { |
|
position: fixed; |
|
top: 0; |
|
left: 0; |
|
right: 0; |
|
bottom: 0; |
|
background-color: rgba(0, 0, 0, 0.5); |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
z-index: 9999; |
|
padding: 32rpx; |
|
} |
|
|
|
.modal-content { |
|
background-color: #ffffff; |
|
border-radius: 32rpx; |
|
padding: 48rpx; |
|
width: 100%; |
|
max-width: 600rpx; |
|
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3); |
|
animation: modal-in 0.2s ease-out; |
|
} |
|
|
|
@keyframes modal-in { |
|
from { |
|
opacity: 0; |
|
transform: scale(0.9); |
|
} |
|
to { |
|
opacity: 1; |
|
transform: scale(1); |
|
} |
|
} |
|
|
|
.modal-title { |
|
color: #111827; |
|
font-size: 36rpx; |
|
font-weight: bold; |
|
margin-bottom: 24rpx; |
|
display: block; |
|
} |
|
|
|
.modal-desc { |
|
color: #6b7280; |
|
font-size: 28rpx; |
|
line-height: 1.6; |
|
margin-bottom: 48rpx; |
|
display: block; |
|
} |
|
|
|
.bold { |
|
font-weight: bold; |
|
color: #111827; |
|
} |
|
|
|
.text-red { |
|
color: #dc2626 !important; |
|
} |
|
|
|
.text-green { |
|
color: #16a34a !important; |
|
} |
|
|
|
.modal-actions { |
|
display: flex; |
|
align-items: center; |
|
gap: 24rpx; |
|
} |
|
|
|
.modal-btn { |
|
flex: 1; |
|
padding: 24rpx 32rpx; |
|
border-radius: 12rpx; |
|
font-size: 28rpx; |
|
font-weight: 600; |
|
text-align: center; |
|
transition: all 0.2s; |
|
} |
|
|
|
.modal-btn:active { |
|
transform: scale(0.95); |
|
} |
|
|
|
.btn-cancel { |
|
background-color: #f3f4f6; |
|
color: #374151; |
|
} |
|
|
|
.btn-confirm { |
|
background-color: #5b8ff9; |
|
color: #ffffff; |
|
box-shadow: 0 4rpx 12rpx rgba(91, 143, 249, 0.3); |
|
} |
|
</style> |