feat(shop): 重构店铺列表页面,支持按模式分类筛选

- 新增模式映射常量文件
- 扩展店铺列表接口参数类型
- 重构店铺列表页面布局为左右结构
- 添加模式分类导航功能
- 优化店铺列表筛选逻辑
This commit is contained in:
dzq 2025-12-30 17:27:44 +08:00
parent 48ece1eeae
commit fa46750951
4 changed files with 231 additions and 115 deletions

View File

@ -2,6 +2,7 @@ import { http } from "@/http/http";
import type {
GetBalanceResponse,
GetOrdersByOpenIdDTO,
GetShopListParams,
OpenCabinetApiData,
QyLoginDTO,
QyLoginRequestParams,
@ -70,16 +71,15 @@ export async function getUserBalance(corpid: string, ab98UserId: number) {
return await http.get<GetBalanceResponse>("payment/getUserBalance", { corpid, ab98UserId });
}
export async function getShopListApi(corpid: string, mode?: number) {
const params: any = {
corpid
};
if (typeof mode !== 'undefined') {
params.mode = mode;
}
export async function getShopListApi(params: GetShopListParams) {
return await http.get<ShopEntity[]>("shop/list", params);
}
export async function getCorpidById(cid?: number) {
return await http.get<string>("qy/getCorpidById", { id: cid ? cid : 0 });
}
/** 获取模式列表 */
export function getModeListApi() {
return http.get<number[]>("shop/mode/list")
}

View File

@ -253,4 +253,15 @@ export interface SearchGoodsDO extends ShopGoodsEntity {
cellNoStr?: string;
/** 已分配库存 */
totalStock?: number;
}
export interface GetShopListParams {
/** 企业微信id */
corpid: string;
/** 不包含的运行模式 */
mode?: number;
/** 只查询该运行模式0-支付模式 1-审批模式 2-借还模式 3-会员模式 4-耗材模式 5-暂存模式) */
eqMode?: number;
/** 运行模式列表(逗号分隔) */
modeList?: string;
}

View File

@ -16,6 +16,8 @@ import { useAb98UserStore } from '@/pinia/stores/ab98-user'
import { storeToRefs } from 'pinia'
import { useWxParamsStore } from '@/pinia/stores/wx-params'
import { useStorageCabinetStore } from '@/pinia/stores/storageCabinet'
import { getModeListApi } from '@/api/shop'
import { MODE_MAP } from '@/utils/maps/mode'
definePage({
style: {
@ -36,24 +38,35 @@ const showShopList = ref<boolean>(true)
const shopList = ref<ShopEntity[]>([])
const shopId = ref<number>(0)
// tabs
const activeTab = ref<number>(0)
const tabs = [
{ title: '借还柜', value: 0 },
{ title: '暂存柜', value: 1 }
]
// mode
const modeList = ref<number[]>([])
const activeModeCategory = ref<number>(0)
// activeTab
// const filteredShopList = computed(() => {
// if (!shopList.value.length) return []
// if (activeTab.value === 0) {
// // mode5shop
// return shopList.value.filter(shop => shop.mode !== 5)
// } else {
// // mode5shop
// return shopList.value.filter(shop => shop.mode === 5)
// }
// })
// ""
const categoryModes = computed(() => ['全部', ...modeList.value])
// mode
const filteredShopList = computed(() => {
if (!shopList.value.length) return []
if (activeTab.value === 0) {
// mode5shop
return shopList.value.filter(shop => shop.mode !== 5)
} else {
// mode5shop
return shopList.value.filter(shop => shop.mode === 5)
// ""
if (activeModeCategory.value === 0 || !modeList.value.length) {
return shopList.value
}
// mode
const selectedMode = modeList.value[activeModeCategory.value - 1]
return shopList.value.filter(shop => shop.mode === selectedMode)
})
//
@ -74,6 +87,33 @@ const isStorageMode = computed(() => selectedShopMode.value === 5)
onMounted(async () => {
})
// mode
function handleModeCategoryClick(index: number) {
activeModeCategory.value = index
//
fetchShopListByMode()
}
// mode
async function fetchShopListByMode() {
try {
await wxStore.waitForHandleWxCallbackComplete()
const eqMode = activeModeCategory.value === 0
? undefined
: modeList.value[activeModeCategory.value - 1]
const res = await getShopListApi({ corpid: wxStore.corpid || '', eqMode })
console.log('获取店铺列表:', res)
if (res?.code === 0 && res?.data?.length > 0) {
shopList.value = res.data
} else {
shopList.value = []
}
} catch (error) {
console.error('获取店铺列表失败:', error)
shopList.value = []
}
}
//
function handleShopSelect(selectedShopId: number) {
shopId.value = selectedShopId
@ -187,86 +227,104 @@ onLoad(async (query) => {
console.error('用户登录处理失败:', error);
}
// mode
try {
const modeRes = await getModeListApi();
if (modeRes?.code === 0 && modeRes?.data?.length > 0) {
modeList.value = modeRes.data;
}
} catch (error) {
console.error('获取mode列表失败:', error);
}
//
if (showShopList.value) {
try {
await wxStore.waitForHandleWxCallbackComplete();
const res = await getShopListApi(wxStore.corpid || '');
console.log('获取店铺列表:', res);
if (res?.code === 0 && res?.data?.length > 0) {
shopList.value = res.data;
}
} catch (error) {
console.error('获取店铺列表失败:', error);
}
await fetchShopListByMode();
}
});
onShow(async () => {
//
if (showShopList.value) {
try {
await wxStore.waitForHandleWxCallbackComplete();
const res = await getShopListApi(wxStore.corpid || '');
console.log('获取店铺列表:', res);
if (res?.code === 0 && res?.data?.length > 0) {
shopList.value = res.data;
// mode
if (modeList.value.length === 0) {
try {
const modeRes = await getModeListApi();
if (modeRes?.code === 0 && modeRes?.data?.length > 0) {
modeList.value = modeRes.data;
}
} catch (error) {
console.error('获取mode列表失败:', error);
}
} catch (error) {
console.error('获取店铺列表失败:', error);
}
await fetchShopListByMode();
}
})
</script>
<template>
<!-- 直接使用 shop-list 合并 shop-container 的样式 -->
<view class="shop-list">
<view class="shop-header">
<!-- 容器使用左右布局 -->
<view :class="['container', { 'container-full': !showShopList }]">
<!-- 页面顶部header -->
<view v-if="showShopList" class="page-header">
<wd-img
:src="`/static/cover.jpg`"
width="100%"
height="150"
height="120"
mode="aspectFill"
></wd-img>
</view>
<!-- 店铺选择列表内容 -->
<view v-if="showShopList" class="shop-list-content">
<!-- tabs切换 -->
<wd-tabs v-model="activeTab" class="shop-tabs">
<wd-tab v-for="tab in tabs" :key="tab.value" :title="tab.title" :name="tab.value"></wd-tab>
</wd-tabs>
<!-- 左右布局区域 -->
<view v-if="showShopList" class="content-wrapper">
<!-- 左侧分类导航 -->
<view class="left-nav">
<!-- <view class="back-btn" @click="backToShopList">
<wd-icon name="arrow-left" size="16px"></wd-icon>
<text>选择机柜</text>
</view> -->
<scroll-view class="category-nav" scroll-y>
<view
v-for="(mode, index) in categoryModes"
:key="index"
:class="['category-item', { active: activeModeCategory === index }]"
@click="handleModeCategoryClick(index)"
>
{{ mode === '全部' ? '全部' : (MODE_MAP[mode] || `模式${mode}`) }}
</view>
</scroll-view>
</view>
<!-- <view class="shop-prompt">
<view class="prompt-text">请选择机柜地址</view>
</view> -->
<view class="shop-row">
<template v-if="filteredShopList.length > 0">
<view v-for="shop in filteredShopList" :key="shop.shopId" class="shop-col" @click="handleShopSelect(shop.shopId)">
<view class="shop-item">
<wd-img
:src="toHttpsUrl(shop.coverImg) || '/static/product-image.png'"
width="100%"
height="80"
mode="aspectFill"
></wd-img>
<view class="shop-info">
<view class="shop-name">
<wd-icon name="shop" size="16px" color="#666"></wd-icon>
<text>{{ shop.shopName }}</text>
<!-- 右侧店铺列表 -->
<view class="shop-list">
<view class="shop-list-content">
<view class="shop-row">
<template v-if="filteredShopList.length > 0">
<view v-for="shop in filteredShopList" :key="shop.shopId" class="shop-col" @click="handleShopSelect(shop.shopId)">
<view class="shop-item">
<wd-img
:src="toHttpsUrl(shop.coverImg) || '/static/product-image.png'"
width="100%"
height="80"
mode="aspectFill"
></wd-img>
<view class="shop-info">
<view class="shop-name">
<wd-icon name="shop" size="16px" color="#666"></wd-icon>
<text>{{ shop.shopName }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<template v-else>
<view class="empty-state">
<wd-icon name="info" size="48px" color="#ccc"></wd-icon>
<text class="empty-text">暂无店铺</text>
</view>
</template>
</view>
</template>
<template v-else>
<view class="empty-state">
<wd-icon name="info" size="48px" color="#ccc"></wd-icon>
<text class="empty-text">暂无店铺</text>
</view>
</template>
</view>
</view>
</view>
@ -296,61 +354,100 @@ onShow(async () => {
@checkout="handleCheckout"
/>
<wd-gap safe-area-bottom height="20"></wd-gap>
<!-- <wd-gap safe-area-bottom height="20"></wd-gap> -->
</view>
</template>
<style scoped lang="scss">
.shop-list {
.container {
display: flex;
flex-direction: column;
// height: calc(100vh - 94px - env(safe-area-inset-top) - env(safe-area-inset-bottom));
height: 100vh;
background: #f7f8fa;
overflow: hidden;
&.container-full {
display: block;
}
}
.page-header {
width: 100%;
height: 120px;
overflow: hidden;
flex-shrink: 0;
}
.content-wrapper {
display: flex;
flex: 1;
overflow: hidden;
}
.left-nav {
width: 100px;
flex-shrink: 0;
background: #fff;
border-right: 1px solid #e0e0e0;
display: flex;
flex-direction: column;
.back-btn {
display: flex;
align-items: center;
padding: 12px 8px;
background: #fff;
border-bottom: 1px solid #e0e0e0;
font-size: 14px;
color: #666;
text {
margin-left: 4px;
}
}
.category-nav {
flex: 1;
height: 100%;
overflow-y: auto;
}
.category-item {
padding: 16px 8px;
text-align: center;
font-size: 14px;
color: #666;
border-left: 3px solid transparent;
background: #fff;
&.active {
background: #f7f8fa;
color: #F56C6C;
border-left-color: #F56C6C;
}
}
}
.shop-list {
flex: 1;
height: 100%;
overflow: hidden;
.shop-list-content {
height: 100%;
overflow-y: auto;
overflow-x: hidden;
}
.shop-header {
width: 100%;
height: 150px;
height: 120px;
overflow: hidden;
flex-shrink: 0;
}
.shop-list-content {
flex: 1;
height: calc(100vh - 150px);
}
.shop-tabs {
margin: 8px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.shop-prompt {
margin: 8px;
padding: 12px 16px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
.prompt-text {
font-size: 14px;
color: #333;
font-weight: 500;
}
}
.shop-row {
overflow-y: auto;
overflow-x: hidden;
padding: 10px 8px 0 8px;
display: flex;
flex-wrap: wrap;
gap: 8px;
flex: 1;
height: calc(100vh - 150px - 52px);
padding: 10px;
.empty-state {
display: flex;
@ -369,8 +466,8 @@ onShow(async () => {
}
.shop-col {
width: calc(50% - 4px);
margin-bottom: 8px;
width: 100%;
margin-bottom: 10px;
}
.shop-item {

8
src/utils/maps/mode.ts Normal file
View File

@ -0,0 +1,8 @@
export const MODE_MAP: Record<number, string> = {
0: '支付柜',
1: '审批柜',
2: '借还柜',
3: '会员柜',
4: '耗材柜',
5: '暂存柜',
}