odf_new/odf-uniapp/pages/rack/index.vue
zpc 5b0de386f1
All checks were successful
continuous-integration/drone/push Build is passing
feat(odf-v1021): Add optical box rack type support with dual-column layout
- Add migration script to introduce rack_type field (0=ODF, 1=optical box)
- Add RackType, LeftPortsCount, RightPortsCount properties to OdfRacks model and DTOs
- Add rack type selector and port count inputs to OdfRackForm component
- Display rack type labels in OdfRacks management table
- Add rack type badges to uni-app rack list cards
- Implement dual-column layout for optical box type in rack detail page with left/right port sections
- Add optical box port naming format (A-1, A-2, etc.) with row-based labeling
- Add visual distinction with background colors (left: #E3F2FD, right: #FFF3E0) and center divider
- Update import/export DTOs to support rack type and optical box port naming
- Mark all v1.0.2.1 tasks as completed
2026-04-02 16:40:09 +08:00

232 lines
4.7 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>
<view class="rack-page">
<image class="bg-image" src="/static/images/home_bg.png" mode="aspectFill" />
<view class="content">
<!-- 顶部导航栏 -->
<view class="nav-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-bar-inner">
<image
class="nav-icon"
src="/static/images/ic_back.png"
mode="aspectFit"
@click="goBack"
/>
<text class="nav-title">机房详情</text>
<view class="checkin-btn" @click="goCheckin">
<text class="checkin-btn-text">签到</text>
</view>
</view>
</view>
<!-- 搜索入口栏 -->
<view class="search-bar" @click="goSearch">
<image class="search-icon" src="/static/images/ic_search.png" mode="aspectFit" />
<text class="search-placeholder">请输入要搜索的备注内容</text>
</view>
<!-- 机架列表 -->
<view class="rack-list">
<view
class="rack-card"
:class="{ 'rack-card-optical': item.rackType === 1 }"
v-for="item in rackList"
:key="item.id"
@click="goDetail(item)"
>
<text class="rack-name">{{ item.rackName }}</text>
<text class="rack-type">类型{{ item.rackType === 1 ? '光交箱' : 'ODF' }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
import { getRackList } from '@/services/machine'
const statusBarHeight = uni.getSystemInfoSync().statusBarHeight || 0
const rackList = ref([])
const roomIdRef = ref('')
const roomName = ref('')
const pageNum = ref(1)
const pageSize = 20
const totalPage = ref(0)
const loading = ref(false)
async function loadRackList(isLoadMore = false) {
if (loading.value) return
loading.value = true
try {
const res = await getRackList(pageNum.value, pageSize, roomIdRef.value)
if (res.code === 200 && res.data) {
totalPage.value = res.data.totalPage || 0
if (isLoadMore) {
rackList.value = [...rackList.value, ...(res.data.result || [])]
} else {
rackList.value = res.data.result || []
}
}
} finally {
loading.value = false
}
}
function goBack() {
uni.navigateBack()
}
function goCheckin() {
uni.navigateTo({
url: '/pages/checkin/index?roomId=' + roomIdRef.value
})
}
function goSearch() {
uni.navigateTo({
url: '/pages/search/index?roomId=' + roomIdRef.value
})
}
function goDetail(item) {
uni.navigateTo({
url: '/pages/rack-detail/index?rackId=' + item.id
+ '&rackName=' + encodeURIComponent(item.rackName)
+ '&roomName=' + encodeURIComponent(roomName.value)
+ '&rackType=' + (item.rackType || 0)
})
}
onLoad((options) => {
if (options.roomId) {
roomIdRef.value = options.roomId
}
if (options.roomName) {
roomName.value = decodeURIComponent(options.roomName)
}
loadRackList()
})
onPullDownRefresh(() => {
pageNum.value = 1
loadRackList().finally(() => {
uni.stopPullDownRefresh()
})
})
onReachBottom(() => {
if (pageNum.value >= totalPage.value) return
pageNum.value++
loadRackList(true)
})
</script>
<style scoped>
.rack-page {
position: relative;
min-height: 100vh;
background-color: #F5F5F5;
}
.bg-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 500rpx;
z-index: 0;
}
.content {
position: relative;
z-index: 1;
}
.nav-bar {
width: 100%;
}
.nav-bar-inner {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
padding: 0 24rpx;
}
.nav-icon {
width: 44rpx;
height: 44rpx;
}
.checkin-btn {
background-color: #1A73EC;
border-radius: 8rpx;
padding: 8rpx 24rpx;
}
.checkin-btn-text {
color: #fff;
font-size: 26rpx;
}
.search-bar {
display: flex;
align-items: center;
margin: 16rpx 24rpx;
padding: 0 24rpx;
height: 72rpx;
background-color: rgba(255, 255, 255, 0.9);
border-radius: 36rpx;
}
.search-icon {
width: 32rpx;
height: 32rpx;
margin-right: 16rpx;
}
.search-placeholder {
font-size: 26rpx;
color: #999;
}
.nav-title {
font-size: 34rpx;
font-weight: 600;
color: #fff;
}
.rack-list {
padding: 16rpx 24rpx;
}
.rack-card {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx 24rpx;
margin-bottom: 20rpx;
background-color: #fff;
border-radius: 12rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
}
.rack-card-optical {
border-left: 6rpx solid #1A73EC;
}
.rack-name {
font-size: 30rpx;
font-weight: 500;
color: #333;
}
.rack-type {
font-size: 26rpx;
color: #666;
}
</style>