feat(机柜页面): 增加商店选择功能并优化机柜展示

- 在机柜页面添加商店列表选择功能,用户需先选择商店才能查看对应机柜
- 修改getCabinetDetailApi和getShopListApi接口,支持传入shopId和mode参数
- 添加返回商店列表按钮,优化页面交互流程
- 实现滚动时头部高度动态调整效果
- 新增相关样式和状态管理逻辑
This commit is contained in:
dzq 2025-06-13 15:40:18 +08:00
parent 87574aebf6
commit 2132e0e1e5
3 changed files with 184 additions and 19 deletions

View File

@ -3,10 +3,13 @@ import type { CabinetDetailResponse } from './type'
import { OpenCabinetApiData } from '../shop/type'
/** 获取智能柜详情接口 */
export function getCabinetDetailApi() {
export function getCabinetDetailApi(shopId: number) {
return request<CabinetDetailResponse>({
url: 'cabinet/detail',
method: 'get'
method: 'get',
params: {
shopId
}
})
}

View File

@ -74,10 +74,16 @@ export function getBalanceByQyUserid(corpid: string, userid: string) {
})
}
export function getShopListApi(corpid: string) {
export function getShopListApi(corpid: string, mode?: number) {
const params: any = {
corpid
};
if (typeof mode !== 'undefined') {
params.mode = mode;
}
return request<ApiResponseData<ShopEntity[]>>({
url: "shop/list",
method: "get",
params: { corpid }
params
})
}

View File

@ -1,16 +1,36 @@
<template>
<div class="cabinet-container van-safe-area-bottom">
<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)">
<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>
<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"
:src="locker.coverImg ? locker.coverImg : `${publicPath}` + 'img/product-image.svg'" fit="cover"
radius="4" class="product-image" :style="{ filter: locker.stock === 0 ? 'grayscale(100%)' : 'none' }">
:src="locker.coverImg ? locker.coverImg : `${publicPath}` + 'img/product-image.svg'"
fit="cover" radius="4" class="product-image"
:style="{ filter: locker.stock === 0 ? 'grayscale(100%)' : 'none' }">
</van-image>
<div v-if="locker.stock >= 0" class="stock-overlay">
库存: {{ locker.stock }}
@ -50,11 +70,14 @@
</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 { useWxStore, useWxStoreOutside } from '@/pinia/stores/wx'
import { publicPath } from "@/common/utils/path"
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 } 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";
const wxStore = useWxStore();
const { userid: qyUserid, name: qyName } = storeToRefs(wxStore);
@ -64,6 +87,13 @@ const cabinetList = ref<CabinetItem[]>([])
const lockerList = ref<LockerItem[]>([])
const openingLockerId = ref<number | null>(null)
const cabinetData = ref<CabinetDetailDTO[]>([]);
const scrollContainer = ref<HTMLElement>();
//
const showShopList = ref(true);
const shopList = ref<ShopEntity[]>([]);
const shopId = ref<number>(0);
const headerHeight = ref(150);
let scrollListener: any[] = [];
interface CabinetItem {
cabinetId: number
@ -83,10 +113,12 @@ interface LockerItem {
coverImg?: string
}
//
const loadCabinetDetail = async () => {
//
const loadCabinetDetail = async (selectedShopId?: number) => {
const targetShopId = selectedShopId || shopId.value;
if (!targetShopId) return;
try {
const { data } = await getCabinetDetailApi();
const { data } = await getCabinetDetailApi(targetShopId);
cabinetData.value = data;
cabinetList.value = data.map(cabinet => ({
cabinetId: cabinet.cabinetId,
@ -149,7 +181,56 @@ const handleOpenLocker = async (locker: LockerItem) => {
}
}
loadCabinetDetail()
//
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) => {
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);
onMounted(() => {
init();
scrollListener.push(window.addEventListener('scroll', throttledScroll));
});
onBeforeUnmount(() => {
scrollListener.forEach(listener => window.removeEventListener('scroll', listener));
scrollListener = [];
});
</script>
<style lang="scss" scoped>
@ -176,6 +257,81 @@ loadCabinetDetail()
height: calc(100vh - var(--van-tabbar-height));
}
/* 机柜选择相关样式 */
.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: 60px !important;
padding: 12px;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: transform 0.2s;
.shop-info {
display: flex;
align-items: center;
justify-content: start;
height: 100%;
}
.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;
height: 100%;
}
}
}
.showShopListBtn {
width: 100%;
padding: 0;
border: 0;
}
.cabinet-sidebar {
width: 120px;
height: 100%;