693 lines
23 KiB
Vue
693 lines
23 KiB
Vue
<template>
|
|
<div v-if="showShopList" class="shop-list">
|
|
<div class="shop-prompt">
|
|
<van-cell title="请选择机柜地址:" center />
|
|
</div>
|
|
<van-row :gutter="[10, 10]" class="shop-row" justify="start">
|
|
<van-col v-for="shop in shopList" :key="shop.shopId" span="12" class="shop-col">
|
|
<div class="shop-item" @click="handleShopSelect(shop.shopId)">
|
|
<van-image :src="shop.coverImg || `${publicPath}product-image.png`" class="shop-cover-img" fit="cover" />
|
|
<div class="shop-info">
|
|
<van-icon name="shop-o" size="20" class="shop-icon" />
|
|
<div class="shop-name van-ellipsis">{{ shop.shopName }}</div>
|
|
</div>
|
|
</div>
|
|
</van-col>
|
|
<van-col v-if="shopList.length % 2 === 0" span="12" class="shop-col"></van-col>
|
|
</van-row>
|
|
</div>
|
|
<div v-else class="cabinet-container van-safe-area-bottom">
|
|
<div class="left-container">
|
|
<van-button icon="revoke" type="default" class="showShopListBtn" @click="handleBackToShopList">重选地址</van-button>
|
|
<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>
|
|
|
|
<div class="product-list">
|
|
<van-cell v-for="locker in lockerList" :key="locker.lockerId" class="product-item">
|
|
<template #icon>
|
|
<div class="image-container">
|
|
<van-image width="80" height="80"
|
|
v-if="locker.coverImg"
|
|
:src="locker.coverImg ? locker.coverImg : `${publicPath}` + 'img/product-image.svg'"
|
|
fit="cover" radius="4" class="product-image"
|
|
:style="{ filter: locker.stock === 0 && locker.usageStatus === 1 ? 'grayscale(100%)' : 'none' }">
|
|
</van-image>
|
|
<svg v-if="!locker.coverImg && locker.usageStatus === 2" width="80" height="80"
|
|
viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"
|
|
:class="['cell-image', selectedShop?.mode === 5 ? 'cell-image-full' : '']">
|
|
<defs>
|
|
<linearGradient id="occupiedGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
<stop offset="0%" style="stop-color:#FFF3E0;stop-opacity:1" />
|
|
<stop offset="100%" style="stop-color:#FFE0B2;stop-opacity:1" />
|
|
</linearGradient>
|
|
<filter id="shadow" x="-50%" y="-50%" width="200%" height="200%">
|
|
<feDropShadow dx="1" dy="1" stdDeviation="1.5" flood-color="#000000" flood-opacity="0.1" />
|
|
</filter>
|
|
<filter id="innerShadow" x="-50%" y="-50%" width="200%" height="200%">
|
|
<feOffset dx="0" dy="0.5" />
|
|
<feGaussianBlur stdDeviation="0.8" result="blur" />
|
|
<feComposite in="SourceGraphic" in2="blur" operator="arithmetic" k2="-1" k3="1" />
|
|
</filter>
|
|
</defs>
|
|
<!-- 主卡片背景 -->
|
|
<rect x="5" y="5" width="90" height="90" rx="12" fill="url(#occupiedGradient)" stroke="#FF9800"
|
|
stroke-width="2.5" filter="url(#shadow)" />
|
|
<!-- 内阴影效果 -->
|
|
<rect x="5" y="5" width="90" height="90" rx="12" fill="none" stroke="rgba(255,152,0,0.3)"
|
|
stroke-width="1" filter="url(#innerShadow)" />
|
|
|
|
<!-- 锁定图标 -->
|
|
<g transform="translate(35, 12)">
|
|
<rect x="8" y="12" width="16" height="12" rx="2" fill="#FF6F00" stroke="#E65100"
|
|
stroke-width="1" />
|
|
<path d="M 12 12 L 12 8 C 12 5.79 13.79 4 16 4 L 16 4 C 18.21 4 20 5.79 20 8 L 20 12" fill="none"
|
|
stroke="#FF6F00" stroke-width="2" stroke-linecap="round" />
|
|
</g>
|
|
|
|
<!-- 状态文字 -->
|
|
<text x="50" y="54" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#E65100"
|
|
text-anchor="middle">占用</text>
|
|
|
|
<!-- 装饰性分割线 -->
|
|
<line x1="20" y1="62" x2="80" y2="62" stroke="#FF9800" stroke-width="1.5" stroke-dasharray="3,2"
|
|
opacity="0.7" />
|
|
|
|
<!-- 格口类型标签 -->
|
|
<g transform="translate(50, 78)">
|
|
<rect x="-18" y="-8" width="36" height="16" rx="8" fill="#FF9800" opacity="0.9" />
|
|
<text x="0" y="3" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="white"
|
|
text-anchor="middle">{{ switchCellType(locker.cellType) }}</text>
|
|
</g>
|
|
</svg>
|
|
<svg v-if="!locker.coverImg && locker.usageStatus === 1" width="80" height="80"
|
|
viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"
|
|
:class="['cell-image', selectedShop?.mode === 5 ? 'cell-image-full' : '']">
|
|
<defs>
|
|
<linearGradient id="freeGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
<stop offset="0%" style="stop-color:#E8F5E9;stop-opacity:1" />
|
|
<stop offset="100%" style="stop-color:#C8E6C9;stop-opacity:1" />
|
|
</linearGradient>
|
|
<filter id="shadow2" x="-50%" y="-50%" width="200%" height="200%">
|
|
<feDropShadow dx="1" dy="1" stdDeviation="1.5" flood-color="#000000" flood-opacity="0.1" />
|
|
</filter>
|
|
<filter id="innerShadow2" x="-50%" y="-50%" width="200%" height="200%">
|
|
<feOffset dx="0" dy="0.5" />
|
|
<feGaussianBlur stdDeviation="0.8" result="blur" />
|
|
<feComposite in="SourceGraphic" in2="blur" operator="arithmetic" k2="-1" k3="1" />
|
|
</filter>
|
|
</defs>
|
|
<!-- 主卡片背景 -->
|
|
<rect x="5" y="5" width="90" height="90" rx="12" fill="url(#freeGradient)" stroke="#4CAF50"
|
|
stroke-width="2.5" filter="url(#shadow2)" />
|
|
<!-- 内阴影效果 -->
|
|
<rect x="5" y="5" width="90" height="90" rx="12" fill="none" stroke="rgba(76,175,80,0.3)"
|
|
stroke-width="1" filter="url(#innerShadow2)" />
|
|
|
|
<!-- 开锁图标 -->
|
|
<g transform="translate(35, 12)">
|
|
<rect x="8" y="12" width="16" height="12" rx="2" fill="#4CAF50" stroke="#388E3C"
|
|
stroke-width="1" />
|
|
<path d="M 12 12 L 12 8 C 12 5.79 13.79 4 16 4 L 16 4 C 18.21 4 20 5.79 20 8 L 20 12" fill="none"
|
|
stroke="#4CAF50" stroke-width="2" stroke-linecap="round" />
|
|
<!-- 打开的锁舌 -->
|
|
<circle cx="16" cy="18" r="2" fill="#4CAF50" stroke="#388E3C" stroke-width="1" />
|
|
</g>
|
|
|
|
<!-- 状态文字 -->
|
|
<text x="50" y="54" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="#2E7D32"
|
|
text-anchor="middle">空闲</text>
|
|
|
|
<!-- 装饰性分割线 -->
|
|
<line x1="20" y1="62" x2="80" y2="62" stroke="#4CAF50" stroke-width="1.5" stroke-dasharray="3,2"
|
|
opacity="0.7" />
|
|
|
|
<!-- 格口类型标签 -->
|
|
<g transform="translate(50, 78)">
|
|
<rect x="-18" y="-8" width="36" height="16" rx="8" fill="#4CAF50" opacity="0.9" />
|
|
<text x="0" y="3" font-family="Arial, sans-serif" font-size="10" font-weight="bold" fill="white"
|
|
text-anchor="middle">{{ switchCellType(locker.cellType) }}</text>
|
|
</g>
|
|
</svg>
|
|
<div v-if="locker.stock >= 0 && selectedShop?.mode !==5" class="stock-overlay">
|
|
库存: {{ locker.stock }}
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<div class="product-info">
|
|
<div class="goods-info">
|
|
<div v-if="locker.goodsName">
|
|
<div class="info-row">
|
|
<div class="locker-number">格口 {{ locker.cellNo }}</div>
|
|
<div v-if="selectedShop?.mode !==5" 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.cellNo }}</div>
|
|
<div v-if="selectedShop?.mode !==5" class="goods-price">¥0.00</div>
|
|
</div>
|
|
<!-- <div class="goods-name">{{ locker.usageStatus === 1 ? '空闲' : '占用' }}</div> -->
|
|
</div>
|
|
</div>
|
|
<div class="button-group">
|
|
<van-button v-if="selectedShop?.mode !== 5" size="small" type="primary" class="detail-btn" @click="showBindGoods(locker)">
|
|
绑定商品
|
|
</van-button>
|
|
<van-button v-if="selectedShop?.mode === 5 && locker.password" size="small" type="primary" class="detail-btn" @click="handleClearPassword(locker)">
|
|
清除密码
|
|
</van-button>
|
|
<div v-else-if="selectedShop?.mode === 5 && !locker.password" class="detail-btn-placeholder"></div>
|
|
|
|
<van-button size="small" plain hairline :loading="openingLockerId === locker.lockerId"
|
|
@click="handleOpenLocker(locker)"
|
|
style="margin-left: auto;">
|
|
立即开启
|
|
</van-button>
|
|
</div>
|
|
</div>
|
|
</van-cell>
|
|
</div>
|
|
|
|
<VanPopup v-model:show="showBindGoodsPopup" position="bottom" :style="{ height: '95%' }" round>
|
|
<BindGoods
|
|
v-if="currentLocker"
|
|
:shop-id="shopId"
|
|
:locker-id="currentLocker.lockerId"
|
|
:visible="showBindGoodsPopup"
|
|
:current-stock="currentLocker.stock"
|
|
:current-goods-name="currentLocker.goodsName"
|
|
:mode="selectedShop?.mode"
|
|
@cancel="showBindGoodsPopup = false"
|
|
@success="handleBindSuccess"
|
|
@unbind="handleBindSuccess"
|
|
@update:currentStock="handleStockUpdate"
|
|
/>
|
|
</VanPopup>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { throttle } from 'lodash-es';
|
|
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
|
|
import { getShopListApi } from '@/common/apis/shop';
|
|
import { ShopEntity } from '@/common/apis/shop/type';
|
|
import { getCabinetDetailApi, openCabinet, changeGoodsCellsStock, clearGoodsCells, resetCellById } from '@/common/apis/cabinet';
|
|
import type { CabinetDetailDTO } from '@/common/apis/cabinet/type';
|
|
import { useWxStore, useWxStoreOutside } from '@/pinia/stores/wx';
|
|
import { publicPath } from "@/common/utils/path";
|
|
import BindGoods from './components/BindGoods.vue';
|
|
import VanPopup from 'vant/es/popup';
|
|
import { showDialog, showToast } from 'vant';
|
|
|
|
const wxStore = useWxStore();
|
|
const { userid: qyUserid, name: qyName } = storeToRefs(wxStore);
|
|
|
|
const activeCabinet = ref(0)
|
|
const cabinetList = ref<CabinetItem[]>([])
|
|
const lockerList = ref<LockerItem[]>([])
|
|
const openingLockerId = ref<number | null>(null)
|
|
const showBindGoodsPopup = ref(false)
|
|
const currentLocker = ref<LockerItem | null>(null)
|
|
const cabinetData = ref<CabinetDetailDTO[]>([]);
|
|
const scrollContainer = ref<HTMLElement>();
|
|
// 机柜地址选择相关状态
|
|
const showShopList = ref(true);
|
|
const shopList = ref<ShopEntity[]>([]);
|
|
const shopId = ref<number>(0);
|
|
const selectedShop = ref<ShopEntity | null>(null);
|
|
const headerHeight = ref(150);
|
|
let scrollListener: any[] = [];
|
|
|
|
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
|
|
password?: string
|
|
usageStatus?: number
|
|
cellType?: number
|
|
}
|
|
|
|
// 获取机柜列表
|
|
const loadCabinetDetail = async (selectedShopId?: number) => {
|
|
const targetShopId = selectedShopId || shopId.value;
|
|
if (!targetShopId) return;
|
|
try {
|
|
const { data } = await getCabinetDetailApi(targetShopId);
|
|
cabinetData.value = data;
|
|
cabinetList.value = data.map(cabinet => ({
|
|
cabinetId: cabinet.cabinetId,
|
|
cabinetName: cabinet.cabinetName,
|
|
lockControlNo: cabinet.lockControlNo
|
|
}))
|
|
|
|
// 根据当前选中柜机加载格口数据
|
|
if (data.length > 0 && data[activeCabinet.value]) {
|
|
updateLockerList(data[activeCabinet.value])
|
|
} else {
|
|
lockerList.value = [];
|
|
}
|
|
} catch (error) {
|
|
console.error('获取柜机详情失败:', error)
|
|
}
|
|
}
|
|
|
|
// 更新格口列表数据
|
|
const updateLockerList = (cabinet: CabinetDetailDTO) => {
|
|
lockerList.value = cabinet.cells.map(cell => ({
|
|
lockerId: cell.cellId,
|
|
cellNo: cell.cellNo,
|
|
lockerNumber: cell.pinNo,
|
|
stock: cell.stock,
|
|
status: cell.product ? 1 : 0,
|
|
statusClass: cell.product ? 'occupied' : 'available',
|
|
goodsName: cell.product?.goodsName,
|
|
price: cell.product?.price,
|
|
coverImg: cell.product?.coverImg,
|
|
password: cell.password,
|
|
usageStatus: cell.usageStatus,
|
|
cellType: cell.cellType,
|
|
}))
|
|
}
|
|
|
|
// 监听侧边栏切换事件
|
|
const onCabinetChange = (index: number) => {
|
|
activeCabinet.value = index
|
|
if (cabinetList.value.length > index) {
|
|
updateLockerList(cabinetData.value[index]);
|
|
}
|
|
}
|
|
|
|
function switchCellType(cellType: number | undefined) {
|
|
switch (cellType) {
|
|
case 1: return '小格';
|
|
case 2: return '中格';
|
|
case 3: return '大格';
|
|
case 4: return '超大格';
|
|
default: return '未知';
|
|
}
|
|
}
|
|
|
|
const showBindGoods = (locker: LockerItem) => {
|
|
currentLocker.value = locker;
|
|
showBindGoodsPopup.value = true;
|
|
}
|
|
|
|
const handleBindSuccess = () => {
|
|
showBindGoodsPopup.value = false;
|
|
loadCabinetDetail();
|
|
};
|
|
|
|
const handleOpenLocker = async (locker: LockerItem) => {
|
|
openingLockerId.value = locker.lockerId
|
|
try {
|
|
// 调用打开柜口接口
|
|
await openCabinet(cabinetList.value[activeCabinet.value].cabinetId, locker.lockerNumber, {
|
|
cellId: locker.lockerId,
|
|
userid: qyUserid.value,
|
|
isInternal: 2,
|
|
name: qyName.value,
|
|
mobile: '',
|
|
operationType: 2
|
|
})
|
|
} catch (error) {
|
|
console.error('打开柜口失败:', error)
|
|
} finally {
|
|
openingLockerId.value = null
|
|
}
|
|
}
|
|
|
|
// 初始化方法
|
|
const init = async () => {
|
|
if (showShopList.value) {
|
|
await getShopList();
|
|
} else if (shopId.value) {
|
|
await loadCabinetDetail();
|
|
}
|
|
};
|
|
|
|
// 获取商店列表
|
|
const getShopList = async () => {
|
|
try {
|
|
const res = await getShopListApi(wxStore.corpid, -1);
|
|
if (res?.code === 0 && res?.data?.length > 0) {
|
|
shopList.value = res.data;
|
|
}
|
|
} catch (error) {
|
|
console.error('获取商店列表失败:', error);
|
|
}
|
|
};
|
|
|
|
// 选择商店
|
|
const handleShopSelect = (selectedShopId: number) => {
|
|
const shop = shopList.value.find(s => s.shopId === selectedShopId);
|
|
if (shop) {
|
|
selectedShop.value = shop;
|
|
shopId.value = selectedShopId;
|
|
}
|
|
showShopList.value = false;
|
|
loadCabinetDetail(selectedShopId);
|
|
};
|
|
|
|
// 返回商店列表
|
|
const handleBackToShopList = () => {
|
|
showShopList.value = true;
|
|
shopId.value = 0;
|
|
};
|
|
|
|
// 监听滚动事件调整头部高度
|
|
const throttledScroll = throttle(() => {
|
|
if (!scrollContainer.value) return;
|
|
const scrollTop = scrollContainer.value.scrollTop;
|
|
headerHeight.value = Math.max(150 - scrollTop * 0.5, 60);
|
|
}, 100);
|
|
|
|
|
|
|
|
const handleStockUpdate = (newStock: number) => {
|
|
if (currentLocker.value) {
|
|
currentLocker.value.stock = newStock;
|
|
}
|
|
};
|
|
|
|
const handleClearPassword = async (locker: LockerItem) => {
|
|
try {
|
|
await resetCellById(locker.lockerId);
|
|
showToast('清除密码成功');
|
|
loadCabinetDetail();
|
|
} catch (error) {
|
|
console.error('清除密码失败:', error);
|
|
showToast('清除密码失败');
|
|
}
|
|
};
|
|
|
|
onMounted(() => {
|
|
init();
|
|
scrollListener.push(window.addEventListener('scroll', throttledScroll));
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
scrollListener.forEach(listener => window.removeEventListener('scroll', listener));
|
|
scrollListener = [];
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.image-container {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.stock-overlay {
|
|
position: absolute;
|
|
right: 4px;
|
|
bottom: 4px;
|
|
background-color: rgba(0, 0, 0, 0.6);
|
|
color: white;
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.cabinet-container {
|
|
display: flex;
|
|
height: calc(100vh - var(--van-tabbar-height));
|
|
}
|
|
|
|
.left-container {
|
|
width: 90px;
|
|
}
|
|
|
|
/* 机柜选择相关样式 */
|
|
.shop {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
}
|
|
|
|
.shop-header {
|
|
overflow: hidden;
|
|
transition: height 0.3s ease;
|
|
}
|
|
|
|
.shop-cover {
|
|
width: 100%;
|
|
transform: translateY(calc((150px - var(--header-height)) / 2));
|
|
scale: calc(1 + (150 - var(--header-height)) / 150);
|
|
}
|
|
|
|
.shop-list {
|
|
.shop-prompt {
|
|
margin: 8px;
|
|
height: 44px !important;
|
|
background: white;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
transition: transform 0.2s;
|
|
}
|
|
|
|
.shop-row {
|
|
margin: 8px 0;
|
|
padding: 0 8px;
|
|
}
|
|
|
|
.shop-col {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.shop-item {
|
|
height: auto !important;
|
|
padding: 0;
|
|
background: white;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
transition: transform 0.2s;
|
|
|
|
.shop-cover-img {
|
|
width: 100%;
|
|
height: 80px;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.shop-info {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: start;
|
|
padding: 12px;
|
|
}
|
|
|
|
.shop-name {
|
|
white-space: normal;
|
|
word-break: break-word;
|
|
font-size: 14px;
|
|
color: #333;
|
|
font-weight: 500;
|
|
margin: 2px 0 4px 4px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
}
|
|
}
|
|
|
|
.showShopListBtn {
|
|
width: 100%;
|
|
padding: 0;
|
|
border: 0;
|
|
}
|
|
|
|
.cabinet-sidebar {
|
|
width: 90px;
|
|
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;
|
|
}
|
|
|
|
.detail-btn-placeholder {
|
|
flex: 1;
|
|
}
|
|
|
|
: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%;
|
|
}
|
|
|
|
.adjust-btn {
|
|
background-color: #4e80ee;
|
|
border: none;
|
|
}
|
|
|
|
.unbind-btn {
|
|
color: #999;
|
|
border-color: #999;
|
|
}
|
|
</style> |