2025-11-05 09:34:04 +08:00
|
|
|
<template>
|
|
|
|
|
<view class="cabinet-container">
|
|
|
|
|
<view class="left-container">
|
|
|
|
|
<scroll-view
|
|
|
|
|
class="cabinet-sidebar"
|
|
|
|
|
scroll-y
|
|
|
|
|
>
|
|
|
|
|
<view
|
|
|
|
|
v-for="(cabinet, index) in cabinetList"
|
|
|
|
|
:key="cabinet.cabinetId"
|
|
|
|
|
class="cabinet-sidebar-item"
|
|
|
|
|
:class="{ active: activeCabinet === index }"
|
|
|
|
|
@tap="onCabinetChange(index)"
|
|
|
|
|
>
|
|
|
|
|
{{ cabinet.cabinetName }}
|
|
|
|
|
</view>
|
|
|
|
|
</scroll-view>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<scroll-view class="product-list" scroll-y>
|
|
|
|
|
<view
|
|
|
|
|
v-for="locker in lockerList"
|
|
|
|
|
:key="locker.lockerId"
|
|
|
|
|
class="product-item"
|
|
|
|
|
>
|
|
|
|
|
<view class="product-info">
|
|
|
|
|
<view class="image-container">
|
|
|
|
|
<image
|
|
|
|
|
class="product-image"
|
|
|
|
|
:src="locker.coverImg || defaultImage"
|
|
|
|
|
mode="aspectFill"
|
|
|
|
|
:style="{ filter: locker.stock === 0 ? 'grayscale(100%)' : 'none' }"
|
|
|
|
|
/>
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<view class="goods-info">
|
|
|
|
|
<view class="info-row">
|
|
|
|
|
<view class="locker-number">格口 {{ locker.cellNo }}</view>
|
|
|
|
|
<view class="goods-price">¥{{ (locker.price || 0).toFixed(2) }}</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="goods-name">{{ locker.goodsName || '暂无商品信息' }}</view>
|
|
|
|
|
|
|
|
|
|
<view class="button-group">
|
|
|
|
|
<button
|
|
|
|
|
class="custom-btn"
|
|
|
|
|
:loading="openingLockerId === locker.lockerId"
|
|
|
|
|
@tap="handleRefund(locker.orderId, locker.orderGoodsId)"
|
|
|
|
|
>
|
|
|
|
|
退还格口
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
class="custom-btn primary"
|
|
|
|
|
:loading="openingLockerId === locker.lockerId"
|
|
|
|
|
@tap="handleOpenLocker(locker)"
|
|
|
|
|
>
|
|
|
|
|
开启格口
|
|
|
|
|
</button>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</scroll-view>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { ref, onMounted } from 'vue';
|
|
|
|
|
import { storeToRefs } from 'pinia';
|
|
|
|
|
import { getUserRentedCabinetListApi, openCabinet } from '@/api/cabinet';
|
|
|
|
|
import type { RentingCabinetDetailDTO, RetingCellEntity } from '@/api/cabinet/types';
|
|
|
|
|
import { useWxStore } from '@/pinia/stores/wx';
|
|
|
|
|
import { useAb98UserStore } from '@/pinia/stores/ab98-user';
|
|
|
|
|
|
|
|
|
|
definePage({
|
|
|
|
|
style: {
|
|
|
|
|
navigationBarTitleText: '我的柜子',
|
|
|
|
|
},
|
|
|
|
|
enablePullDownRefresh: true,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const wxStore = useWxStore();
|
|
|
|
|
const { corpid, ab98User } = storeToRefs(wxStore);
|
|
|
|
|
|
|
|
|
|
// 默认图片
|
|
|
|
|
const defaultImage = '/static/img/product-image.svg'
|
|
|
|
|
|
|
|
|
|
// 响应式数据
|
|
|
|
|
const activeCabinet = ref(0)
|
|
|
|
|
const cabinetList = ref<CabinetItem[]>([])
|
|
|
|
|
const lockerList = ref<LockerItem[]>([])
|
|
|
|
|
const openingLockerId = ref<number | null>(null)
|
|
|
|
|
const cabinetData = ref<RentingCabinetDetailDTO[]>([])
|
|
|
|
|
|
|
|
|
|
// 类型定义
|
|
|
|
|
interface CabinetItem {
|
|
|
|
|
cabinetId: number
|
|
|
|
|
cabinetName: string
|
|
|
|
|
lockControlNo: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface LockerItem {
|
|
|
|
|
lockerId: number
|
|
|
|
|
cellNo: number
|
|
|
|
|
lockerNumber: number
|
|
|
|
|
stock: number
|
|
|
|
|
status: 0 | 1
|
|
|
|
|
statusClass: 'available' | 'occupied'
|
|
|
|
|
goodsName?: string
|
|
|
|
|
price?: number
|
|
|
|
|
coverImg?: string
|
|
|
|
|
orderId: number
|
|
|
|
|
orderGoodsId: number
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取用户租用的机柜列表
|
|
|
|
|
*/
|
|
|
|
|
const loadUserRentedCabinetDetail = async () => {
|
|
|
|
|
if (!ab98User?.value?.ab98UserId) {
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '用户信息不完整',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const res = await getUserRentedCabinetListApi(corpid.value, ab98User.value.ab98UserId);
|
|
|
|
|
|
|
|
|
|
if (res.code !== 0) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cabinetData.value = res.data || [];
|
|
|
|
|
|
|
|
|
|
// 转换机柜列表
|
|
|
|
|
cabinetList.value = cabinetData.value.map(cabinet => ({
|
|
|
|
|
cabinetId: cabinet.cabinetId,
|
|
|
|
|
cabinetName: cabinet.cabinetName,
|
|
|
|
|
lockControlNo: cabinet.lockControlNo
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
// 根据当前选中柜机加载格口数据
|
|
|
|
|
if (cabinetData.value.length > 0) {
|
|
|
|
|
updateLockerList(cabinetData.value[activeCabinet.value])
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取用户租用的柜机详情失败:', error)
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '获取柜机详情失败',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 更新格口列表数据
|
|
|
|
|
*/
|
|
|
|
|
const updateLockerList = (cabinet: RentingCabinetDetailDTO) => {
|
|
|
|
|
lockerList.value = cabinet.cells.map(cell => ({
|
|
|
|
|
lockerId: cell.cellId,
|
|
|
|
|
cellNo: cell.cellNo,
|
|
|
|
|
lockerNumber: cell.pinNo,
|
|
|
|
|
stock: cell.stock,
|
|
|
|
|
status: cell.isRented ? 1 : 0,
|
|
|
|
|
statusClass: cell.isRented ? 'occupied' : 'available',
|
|
|
|
|
goodsName: '',
|
|
|
|
|
price: cell.cellPrice,
|
|
|
|
|
coverImg: defaultImage,
|
|
|
|
|
orderId: cell.orderId,
|
|
|
|
|
orderGoodsId: cell.orderGoodsId,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 监听侧边栏切换事件
|
|
|
|
|
*/
|
|
|
|
|
const onCabinetChange = (index: number) => {
|
|
|
|
|
activeCabinet.value = index
|
|
|
|
|
if (cabinetList.value.length > index && cabinetData.value[index]) {
|
|
|
|
|
updateLockerList(cabinetData.value[index])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 开启格口
|
|
|
|
|
*/
|
|
|
|
|
const handleOpenLocker = async (locker: LockerItem) => {
|
|
|
|
|
openingLockerId.value = locker.lockerId
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 调用打开柜口接口
|
|
|
|
|
await openCabinet(cabinetList.value[activeCabinet.value].cabinetId, locker.lockerNumber, {
|
|
|
|
|
cellId: locker.lockerId,
|
|
|
|
|
userid: wxStore.userid,
|
|
|
|
|
isInternal: 2,
|
|
|
|
|
name: wxStore.name,
|
|
|
|
|
mobile: '',
|
|
|
|
|
operationType: 2
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '格口开启成功',
|
|
|
|
|
icon: 'success'
|
|
|
|
|
})
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('打开柜口失败:', error)
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '打开柜口失败',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
})
|
|
|
|
|
} finally {
|
|
|
|
|
openingLockerId.value = null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 退还格口
|
|
|
|
|
*/
|
|
|
|
|
const handleRefund = (orderId: number, orderGoodsId: number) => {
|
|
|
|
|
uni.navigateTo({
|
2025-12-13 11:07:56 +08:00
|
|
|
url: `/pages/approval/submit?orderGoodsId=${orderGoodsId}&orderId=${orderId}`,
|
2025-11-05 09:34:04 +08:00
|
|
|
fail: (err) => {
|
|
|
|
|
console.error('页面跳转失败:', err)
|
|
|
|
|
uni.showToast({
|
|
|
|
|
title: '页面跳转失败',
|
|
|
|
|
icon: 'none'
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 初始化
|
|
|
|
|
*/
|
|
|
|
|
const init = async () => {
|
|
|
|
|
await loadUserRentedCabinetDetail()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
init()
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.image-container {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cabinet-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
height: 100vh;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.left-container {
|
|
|
|
|
width: 180rpx;
|
|
|
|
|
background-color: #f5f5f5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cabinet-sidebar {
|
|
|
|
|
height: 100%;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.cabinet-sidebar-item {
|
|
|
|
|
padding: 30rpx 20rpx;
|
|
|
|
|
text-align: center;
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #666;
|
|
|
|
|
border-bottom: 1rpx solid #e5e5e5;
|
|
|
|
|
|
|
|
|
|
&.active {
|
|
|
|
|
background-color: #ffffff;
|
|
|
|
|
color: #e95d5d;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.product-list {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
padding: 20rpx 32rpx 120rpx;
|
|
|
|
|
background-color: #ffffff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.product-item {
|
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
padding: 20rpx 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.product-info {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 20rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.product-image {
|
|
|
|
|
width: 160rpx;
|
|
|
|
|
height: 160rpx;
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.goods-info {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 16rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info-row {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
align-items: center;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.locker-number {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #666;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.goods-name {
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
color: #666;
|
|
|
|
|
line-height: 1.4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.goods-price {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
color: #e95d5d;
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.button-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 16rpx;
|
|
|
|
|
margin-top: 8rpx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.custom-btn {
|
|
|
|
|
flex: 1;
|
|
|
|
|
height: 48rpx;
|
|
|
|
|
line-height: 48rpx;
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
border: 1rpx solid #e95d5d;
|
|
|
|
|
color: #e95d5d;
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
|
|
|
|
|
&::after {
|
|
|
|
|
border: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.primary {
|
|
|
|
|
background-color: #e95d5d;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
opacity: 0.8;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|