314 lines
8.0 KiB
Vue
314 lines
8.0 KiB
Vue
<template>
|
|
<div class="cabinet-container van-safe-area-bottom">
|
|
<van-sidebar v-model="activeCabinet" class="cabinet-sidebar" @change="onCabinetChange">
|
|
<van-sidebar-item v-for="cabinet in cabinetList" :key="cabinet.cabinetId" :title="cabinet.cabinetName" />
|
|
</van-sidebar>
|
|
|
|
<div class="product-list">
|
|
<van-cell v-for="locker in lockerList" :key="locker.lockerId" class="product-item">
|
|
<template #icon>
|
|
<van-image width="80" height="80"
|
|
:src="locker.coverImg ? locker.coverImg : `${publicPath}` + 'img/product-image.svg'" fit="cover"
|
|
radius="4" class="product-image">
|
|
</van-image>
|
|
<!-- <div v-else class="status-overlay">
|
|
<svg width="80" height="80" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
|
<rect x="5" y="5" width="90" height="90" rx="10" fill="#E8F5E9" stroke="#81C784"
|
|
stroke-width="2" />
|
|
<text x="50" y="60" font-family="Arial, sans-serif" font-size="24" font-weight="bold"
|
|
fill="#2E7D32" text-anchor="middle">空闲</text>
|
|
</svg>
|
|
</div> -->
|
|
</template>
|
|
<div class="product-info">
|
|
<div class="goods-info">
|
|
<div v-if="locker.goodsName">
|
|
<div class="info-row">
|
|
<div class="locker-number">格口 {{ locker.lockerNumber }}</div>
|
|
<div class="goods-price">¥{{ (locker.price || 0).toFixed(2) }}</div>
|
|
</div>
|
|
<div class="goods-name">{{ locker.goodsName }}</div>
|
|
</div>
|
|
<div v-else>
|
|
<div class="info-row">
|
|
<div class="locker-number">格口 {{ locker.lockerNumber }}</div>
|
|
<div class="goods-price">¥0.00</div>
|
|
</div>
|
|
<div class="goods-name">空闲</div>
|
|
</div>
|
|
</div>
|
|
<div class="button-group">
|
|
<van-button size="small" type="primary" class="detail-btn" @click="showLockerDetail(locker)">
|
|
详情
|
|
</van-button>
|
|
<van-button size="small" plain hairline :loading="openingLockerId === locker.lockerId"
|
|
@click="handleOpenLocker(locker)">
|
|
立即开启
|
|
</van-button>
|
|
</div>
|
|
</div>
|
|
</van-cell>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref } from 'vue'
|
|
import { getCabinetDetailApi, openCabinet } from '@/common/apis/cabinet'
|
|
import type { CabinetDetailDTO } from '@/common/apis/cabinet/type'
|
|
import { useWxStoreOutside } from '@/pinia/stores/wx'
|
|
import { publicPath } from "@/common/utils/path"
|
|
|
|
const wxStore = useWxStoreOutside()
|
|
|
|
const activeCabinet = ref(0)
|
|
const cabinetList = ref<CabinetItem[]>([])
|
|
const lockerList = ref<LockerItem[]>([])
|
|
const openingLockerId = ref<number | null>(null)
|
|
const cabinetData = ref<CabinetDetailDTO[]>([]);
|
|
|
|
interface CabinetItem {
|
|
cabinetId: number
|
|
cabinetName: string
|
|
lockControlNo: number
|
|
}
|
|
|
|
interface LockerItem {
|
|
lockerId: number
|
|
lockerNumber: number
|
|
status: 0 | 1
|
|
statusClass: 'available' | 'occupied'
|
|
goodsName?: string
|
|
price?: number
|
|
coverImg?: string
|
|
}
|
|
|
|
// 获取柜机列表
|
|
const loadCabinetDetail = async () => {
|
|
try {
|
|
const { data } = await getCabinetDetailApi();
|
|
cabinetData.value = data;
|
|
cabinetList.value = data.map(cabinet => ({
|
|
cabinetId: cabinet.cabinetId,
|
|
cabinetName: cabinet.cabinetName,
|
|
lockControlNo: cabinet.lockControlNo
|
|
}))
|
|
|
|
// 根据当前选中柜机加载格口数据
|
|
if (data.length > 0) {
|
|
updateLockerList(data[activeCabinet.value])
|
|
}
|
|
} catch (error) {
|
|
console.error('获取柜机详情失败:', error)
|
|
}
|
|
}
|
|
|
|
// 更新格口列表数据
|
|
const updateLockerList = (cabinet: CabinetDetailDTO) => {
|
|
lockerList.value = cabinet.cells.map(cell => ({
|
|
lockerId: cell.cellId,
|
|
lockerNumber: cell.pinNo,
|
|
status: cell.product ? 1 : 0,
|
|
statusClass: cell.product ? 'occupied' : 'available',
|
|
goodsName: cell.product?.goodsName,
|
|
price: cell.product?.price,
|
|
coverImg: cell.product?.coverImg
|
|
}))
|
|
}
|
|
|
|
// 监听侧边栏切换事件
|
|
const onCabinetChange = (index: number) => {
|
|
activeCabinet.value = index
|
|
if (cabinetList.value.length > index) {
|
|
updateLockerList(cabinetData.value[index]);
|
|
}
|
|
}
|
|
|
|
const showLockerDetail = (locker: LockerItem) => {
|
|
// 实现详情弹窗逻辑
|
|
}
|
|
|
|
const handleOpenLocker = async (locker: LockerItem) => {
|
|
openingLockerId.value = locker.lockerId
|
|
try {
|
|
// 调用打开柜口接口
|
|
await openCabinet(cabinetList.value[activeCabinet.value].lockControlNo, locker.lockerNumber)
|
|
} catch (error) {
|
|
console.error('打开柜口失败:', error)
|
|
} finally {
|
|
openingLockerId.value = null
|
|
}
|
|
}
|
|
|
|
loadCabinetDetail()
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.cabinet-container {
|
|
display: flex;
|
|
height: calc(100vh - var(--van-tabbar-height));
|
|
}
|
|
|
|
.cabinet-sidebar {
|
|
width: 120px;
|
|
height: 100%;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.locker-grid {
|
|
flex: 1;
|
|
width: 100%;
|
|
padding: 12px;
|
|
overflow-y: auto;
|
|
|
|
:deep(.van-grid) {
|
|
width: 100%;
|
|
max-width: 100%;
|
|
padding: 0;
|
|
|
|
.van-grid-item {
|
|
padding: 8px;
|
|
|
|
.van-grid-item__content {
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.status-overlay {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
.status-text {
|
|
color: white;
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
|
|
.locker-number {
|
|
font-size: 14px;
|
|
color: #666;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.goods-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
width: 100%;
|
|
|
|
.goods-name {
|
|
white-space: normal;
|
|
word-wrap: break-word;
|
|
font-size: 12px;
|
|
color: #666;
|
|
line-height: 1.2;
|
|
width: 70%;
|
|
}
|
|
|
|
.goods-price {
|
|
font-size: 14px;
|
|
color: #e95d5d;
|
|
font-weight: bold;
|
|
margin-top: 4px;
|
|
}
|
|
}
|
|
|
|
.button-group {
|
|
display: flex;
|
|
gap: 8px;
|
|
margin-top: 0;
|
|
|
|
.detail-btn {
|
|
flex: 1;
|
|
background-color: #e95d5d;
|
|
border: none;
|
|
}
|
|
|
|
:deep(.van-button--default) {
|
|
color: #e95d5d;
|
|
border-color: #e95d5d;
|
|
}
|
|
}
|
|
|
|
.product-list {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 10px 16px 60px;
|
|
background: #ffffff;
|
|
}
|
|
|
|
.product-item {
|
|
margin-bottom: 10px;
|
|
padding: min(2.667vw, 20px) 0;
|
|
}
|
|
|
|
.product-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: space-between;
|
|
position: relative;
|
|
padding-left: 3px;
|
|
}
|
|
|
|
.goods-name {
|
|
text-align: left;
|
|
}
|
|
|
|
.button-group {
|
|
padding: 4px;
|
|
}
|
|
|
|
.button-group .van-button {
|
|
font-size: 12px;
|
|
height: 24px;
|
|
line-height: 22px;
|
|
}
|
|
|
|
:deep(.van-button) {
|
|
--van-button-mini-height: 24px;
|
|
--van-button-mini-padding: 0 8px;
|
|
}
|
|
|
|
.locker-number {
|
|
font-size: 14px;
|
|
color: #333;
|
|
line-height: 1.4;
|
|
text-align: left;
|
|
}
|
|
|
|
.goods-price {
|
|
font-size: 16px;
|
|
color: #e95d5d;
|
|
font-weight: bold;
|
|
text-align: left;
|
|
}
|
|
|
|
.button-group {
|
|
display: flex;
|
|
gap: 8px;
|
|
margin-top: 12px;
|
|
}
|
|
|
|
.detail-btn {
|
|
flex: 1;
|
|
background-color: #e95d5d;
|
|
border: none;
|
|
}
|
|
|
|
:deep(.van-button--default) {
|
|
color: #e95d5d;
|
|
border-color: #e95d5d;
|
|
}
|
|
|
|
.info-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
width: 100%;
|
|
}
|
|
</style> |