398 lines
12 KiB
Vue
398 lines
12 KiB
Vue
<script setup lang="ts">
|
||
import { useRentingCabinetStore } from "@/pinia/stores/rentingCabinet"
|
||
import { storeToRefs } from "pinia"
|
||
import { computed, onMounted, ref } from "vue"
|
||
import { publicPath } from "@/common/utils/path";
|
||
import VanPopup from "vant/es/popup"
|
||
import Cart from "./cart.vue" // 假设 Cart 和 Detail 也在同级或components文件夹中
|
||
// 假设 RentingDetail 和 RentingCart 组件也在同级或components文件夹中
|
||
// 如果没有可以根据需求自行创建,或者简化逻辑
|
||
// import RentingDetail from "./RentingDetail.vue" // 如果需要格口详情
|
||
// import RentingCart from "./RentingCart.vue" // 如果需要单独的购物车详情弹窗
|
||
|
||
// 定义Props
|
||
const props = defineProps<{
|
||
shopId: number; // 接收店铺ID,用于获取数据
|
||
}>();
|
||
|
||
// 定义Emit事件
|
||
const emit = defineEmits<{
|
||
(e: 'backToShopList'): void;
|
||
(e: 'checkoutRenting'): void; // 用于租用结算
|
||
}>();
|
||
|
||
// 状态管理
|
||
const rentingCabinetStore = useRentingCabinetStore();
|
||
const { rentingCabinets, rentingCartItems, rentingCartTotalQuantity } = storeToRefs(rentingCabinetStore);
|
||
(window as any).rentingCartItems = rentingCartItems;
|
||
// 从props接收的数据
|
||
const activeCategory = ref(0); // 这里的分类可以根据 cabinetName 来划分,也可以简化为所有可租用格口
|
||
|
||
// 滚动容器引用
|
||
const scrollContainer = ref<HTMLElement>();
|
||
|
||
// 购物车弹窗控制(如果需要)
|
||
const showCartPopup = ref(false); // 如果有单独的购物车详情弹窗,则需要控制
|
||
|
||
const searchQuery = ref('');
|
||
|
||
// 点击分类导航(如果需要根据cabinetName分类)
|
||
function handleCategoryClick(index: number) {
|
||
activeCategory.value = index;
|
||
}
|
||
|
||
// 占位图片 URL
|
||
const PLACEHOLDER_IMAGE_URL = `${publicPath}` + 'img/product-image.svg'; // 请替换为你的实际占位图URL
|
||
|
||
// 添加到购物车
|
||
function handleAddToCart(cabinetCell: any) { // 类型应为 CabinetCellEntity
|
||
// 每个格口只能加入一个,且无法增减,直接确保数量为1
|
||
const existingItem = rentingCartItems.value.find(item => item.cabinetCell.cellId === cabinetCell.cellId);
|
||
if (!existingItem) {
|
||
rentingCabinetStore.addToRentingCart(cabinetCell, 1);
|
||
}
|
||
// 如果已存在,则不进行任何操作,因为不允许增减
|
||
}
|
||
|
||
// 从购物车移除
|
||
function handleRemoveFromCart(cellId: number) {
|
||
rentingCabinetStore.removeFromRentingCart(cellId, 1); // 移除数量为1
|
||
}
|
||
|
||
// 获取购物车中某个格口的数量(应为0或1)
|
||
function getRentingCartItemCount(cellId: number) {
|
||
const item = rentingCartItems.value.find(item => item.cabinetCell.cellId === cellId);
|
||
return item ? item.quantity : 0;
|
||
}
|
||
|
||
// 过滤格口列表,搜索框功能
|
||
const filteredRentingCells = computed(() => {
|
||
let cells: any[] = []; // 类型应为 CabinetCellEntity[]
|
||
// 如果有分类,则按分类过滤
|
||
if (rentingCabinets.value.length > 0) {
|
||
// Flattern all cells from all cabinets
|
||
cells = rentingCabinets.value.flatMap(cabinet => cabinet.cells);
|
||
}
|
||
|
||
// 过滤掉已租用 (isRented = 1) 和已占用 (usageStatus = 2) 的格口
|
||
const availableCells = cells.filter(cell => cell.isRented === 0 && cell.usageStatus === 1);
|
||
|
||
// 搜索过滤
|
||
if (!searchQuery.value) {
|
||
return availableCells;
|
||
}
|
||
// 假设搜索是针对格口号 (cellNo) 或其他标识信息
|
||
return availableCells.filter(cell =>
|
||
String(cell.cellNo).includes(searchQuery.value) ||
|
||
(cell.cabinetName && cell.cabinetName.toLowerCase().includes(searchQuery.value.toLowerCase())) // 如果 cell 中包含 cabinetName
|
||
);
|
||
});
|
||
|
||
|
||
// 组件挂载时获取数据
|
||
onMounted(() => {
|
||
rentingCabinetStore.fetchRentingCabinetDetail(props.shopId);
|
||
});
|
||
|
||
// 结算方法
|
||
function handleCheckout() {
|
||
emit('checkoutRenting');
|
||
}
|
||
|
||
</script>
|
||
|
||
<template>
|
||
<div class="product-container">
|
||
<!-- 左侧分类导航 (如果需要根据柜机名称进行分类,可以改造) -->
|
||
<div>
|
||
<van-button icon="revoke" type="default" class="showShopListBtn" @click="emit('backToShopList')">重选地址</van-button>
|
||
<!-- 简化分类,如果不需要按cabinetName分类,可以移除van-sidebar -->
|
||
<van-sidebar v-model="activeCategory" class="category-nav" @change="handleCategoryClick">
|
||
<van-sidebar-item v-for="(cabinet, index) in rentingCabinets" :key="cabinet.cabinetId" :title="cabinet.cabinetName" />
|
||
</van-sidebar>
|
||
<!-- 暂时保持一个简单的“所有可租用格口”的逻辑,你可以根据实际需求调整分类 -->
|
||
<!-- <van-sidebar v-model="activeCategory" class="category-nav">
|
||
<van-sidebar-item title="可租用格口" />
|
||
</van-sidebar> -->
|
||
</div>
|
||
|
||
<!-- 右侧可租用格口列表 -->
|
||
<div ref="scrollContainer" class="product-list">
|
||
<van-search v-model="searchQuery" placeholder="搜索格口号或柜机名称" shape="round" class="search-box" />
|
||
<div class="category-section">
|
||
<van-cell v-for="cell in filteredRentingCells" :key="cell.cellId" class="product-item">
|
||
<template #icon>
|
||
<van-image :src="PLACEHOLDER_IMAGE_URL" width="80" height="80" class="product-image">
|
||
<!-- 已被占用或故障的格口显示 '不可租用' -->
|
||
<div v-if="cell.isRented === 1 || cell.usageStatus === 2 || cell.availableStatus === 2" class="sold-out-overlay">
|
||
<span class="sold-out-text">不可租用</span>
|
||
</div>
|
||
<template #error>
|
||
<div class="custom-error">
|
||
图片加载失败
|
||
</div>
|
||
</template>
|
||
</van-image>
|
||
</template>
|
||
<div class="product-info">
|
||
<div class="product-name van-ellipsis">
|
||
格口号: {{ cell.cellNo }}
|
||
</div>
|
||
<div class="product-price">
|
||
<!-- 如果格口有价格属性,可以用 cell.cellPrice -->
|
||
<!-- 例如:租金:¥{{ (cell.cellPrice || 0).toFixed(2) }}/天 -->
|
||
¥{{ (cell.cellPrice || 0).toFixed(2) }}
|
||
</div>
|
||
<div class="action-row">
|
||
<!-- 这里的库存概念是格口是否可用,数量只有1 -->
|
||
<span v-if="cell.isRented === 0 && cell.usageStatus === 1 && cell.availableStatus === 1" class="stock-count">
|
||
可租用
|
||
</span>
|
||
<div class="cart-actions">
|
||
<!-- 数量减按钮:如果已在购物车,则显示 -->
|
||
<van-button v-if="getRentingCartItemCount(cell.cellId) > 0" size="mini" icon="minus"
|
||
@click.stop="handleRemoveFromCart(cell.cellId)" />
|
||
<!-- 数量显示:0或1 -->
|
||
<span v-if="getRentingCartItemCount(cell.cellId) > 0" class="cart-count">{{ getRentingCartItemCount(cell.cellId) }}</span>
|
||
<!-- 数量加按钮:如果未在购物车且可租用,则显示 -->
|
||
<van-button size="mini" type="primary" class="add-cart-btn" icon="plus"
|
||
:disabled="getRentingCartItemCount(cell.cellId) > 0 || cell.isRented === 1 || cell.usageStatus === 2 || cell.availableStatus === 2"
|
||
@click.stop="handleAddToCart(cell)"
|
||
:style="{ opacity: (getRentingCartItemCount(cell.cellId) > 0 || cell.isRented === 1 || cell.usageStatus === 2 || cell.availableStatus === 2) ? 0.6 : 1 }"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</van-cell>
|
||
</div>
|
||
</div>
|
||
<!-- 底部购物车栏 -->
|
||
<div v-if="rentingCartItems.length" class="shopping-cart-bar">
|
||
<div class="cart-info">
|
||
<van-badge :content="rentingCartTotalQuantity" @click.stop="showCartPopup = true">
|
||
<van-icon name="shopping-cart-o" size="24" />
|
||
</van-badge>
|
||
<div class="total-price">
|
||
已选格口数:{{ rentingCartTotalQuantity }}
|
||
</div>
|
||
</div>
|
||
<van-button type="primary" size="small" @click="handleCheckout" class="checkout-btn">
|
||
去结算
|
||
</van-button>
|
||
</div>
|
||
</div>
|
||
<VanPopup v-model:show="showCartPopup" position="bottom" :style="{ height: '80%' }" round>
|
||
<Cart class="detail-container" @cart-close="showCartPopup = false" />
|
||
</VanPopup>
|
||
<!-- 购物车弹窗(如果需要) -->
|
||
<!-- <VanPopup v-model:show="showCartPopup" position="bottom" :style="{ height: '80%' }" round>
|
||
<RentingCart class="detail-container" @cart-close="showCartPopup = false" />
|
||
</VanPopup> -->
|
||
</template>
|
||
|
||
<style scoped lang="scss">
|
||
/* 导入或复制 ProductContainer.vue 的样式 */
|
||
/* 注意:这里可以直接复制 ProductContainer.vue 中的 <style scoped lang="scss"> 的内容 */
|
||
/* 或者将公共样式抽离成CSS文件导入 */
|
||
|
||
.product-container {
|
||
display: flex;
|
||
height: 100vh;
|
||
background: #f7f8fa;
|
||
flex: 1;
|
||
min-height: 0;
|
||
position: relative;
|
||
border-top: 1px solid #e0e0e0;
|
||
/* 顶部边框 */
|
||
border-top-left-radius: 8px;
|
||
/* 左上圆角 */
|
||
border-top-right-radius: 8px;
|
||
/* 右上圆角 */
|
||
overflow: hidden;
|
||
/* 确保圆角生效 */
|
||
}
|
||
|
||
.category-nav {
|
||
width: 100px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.showShopListBtn {
|
||
width: 100%;
|
||
padding: 0;
|
||
border: 0;
|
||
}
|
||
|
||
.product-list {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 10px 16px 60px;
|
||
background: #ffffff;
|
||
}
|
||
|
||
.category-section {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.category-title {
|
||
margin: 5px;
|
||
padding: 0;
|
||
font-size: 16px;
|
||
color: #333;
|
||
font-weight: bold;
|
||
top: 0;
|
||
z-index: 1;
|
||
}
|
||
|
||
.product-item {
|
||
margin-bottom: 10px;
|
||
padding: min(2.667vw, 20px) 0;
|
||
}
|
||
|
||
.product-image {
|
||
margin-right: 12px;
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.product-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
height: 80px;
|
||
position: relative;
|
||
}
|
||
|
||
.product-name {
|
||
font-size: 14px;
|
||
color: #333;
|
||
line-height: 1.4;
|
||
text-align: left;
|
||
}
|
||
|
||
.product-price {
|
||
font-size: 16px;
|
||
color: #e95d5d;
|
||
font-weight: bold;
|
||
text-align: left;
|
||
}
|
||
|
||
.add-cart-btn {
|
||
align-self: flex-end;
|
||
margin-top: 0;
|
||
}
|
||
|
||
.van-sidebar-item--select:before {
|
||
background-color: #e95d5d;
|
||
}
|
||
|
||
.van-button--primary {
|
||
background-color: #e95d5d;
|
||
border: 0;
|
||
border-color: #e95d5d;
|
||
}
|
||
|
||
.sold-out-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(255, 255, 255, 0.8);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 2; /* 确保在图片上层 */
|
||
}
|
||
|
||
.sold-out-text {
|
||
color: #999;
|
||
font-size: 14px;
|
||
transform: rotate(-15deg);
|
||
border: 1px solid #eee;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.action-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-end;
|
||
margin-top: auto;
|
||
}
|
||
|
||
.stock-count {
|
||
font-size: 11px;
|
||
color: #bbbbbb;
|
||
margin-right: 8px;
|
||
display: flex;
|
||
align-items: flex-end;
|
||
height: 100%;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* 禁用状态样式 */
|
||
.van-button--disabled {
|
||
background-color: #ccc !important;
|
||
border-color: #ccc !important;
|
||
}
|
||
|
||
/* 商品项置灰 */
|
||
.product-item:has(.sold-out-overlay) {
|
||
opacity: 0.6;
|
||
}
|
||
|
||
.detail-container {
|
||
height: 100%;
|
||
}
|
||
|
||
/* 修改购物车栏样式 */
|
||
.shopping-cart-bar {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 50px;
|
||
background: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 16px;
|
||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
|
||
z-index: 100;
|
||
}
|
||
|
||
.cart-info {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.total-price {
|
||
font-size: 14px;
|
||
color: #333;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.checkout-btn {
|
||
background-color: #e95d5d;
|
||
border: none;
|
||
border-radius: 16px;
|
||
padding: 0 24px;
|
||
}
|
||
|
||
.cart-actions {
|
||
margin-left: auto;
|
||
align-self: flex-end;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
|
||
.cart-count {
|
||
font-size: 12px;
|
||
min-width: 20px;
|
||
text-align: center;
|
||
}
|
||
}
|
||
</style>
|