shop-wx/src/components/storage-cells-summary/index.vue

223 lines
5.1 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import { availableStorageCells } from '@/api/cabinet/index'
import type { AvailableStorageCellDTO } from '@/api/cabinet/types'
import { ref, computed, onMounted } from 'vue'
// 格口类型映射
const CELL_TYPE_MAP = {
1: '小格',
2: '中格',
3: '大格',
4: '超大格'
} as const
// 组件 Props
interface Props {
/** 店铺ID用于获取可用格口列表 */
shopId: number
/** 是否自动加载数据 */
autoLoad?: boolean
/** 是否显示操作按钮 */
showButtons?: boolean
}
const props = withDefaults(defineProps<Props>(), {
autoLoad: true,
showButtons: true
})
// 组件 Emits
const emit = defineEmits<{
(e: 'deposit'): void
(e: 'retrieve'): void
(e: 'refresh'): void
(e: 'error', error: Error): void
}>()
// 状态变量
const loading = ref(false)
const error = ref<Error | null>(null)
const cellsData = ref<AvailableStorageCellDTO[]>([])
const selectedCellType = ref<number>(1)
// 统计计算属性
const availableCells = computed(() =>
cellsData.value.filter(cell => !cell.hasPassword)
)
const cellTypeStats = computed(() => {
const stats = new Map<number, number>()
availableCells.value.forEach(cell => {
const type = cell.cellType
stats.set(type, (stats.get(type) || 0) + 1)
})
// 确保所有四种类型都包含即使数量为0
const allTypes = [1, 2, 3, 4] as const
return allTypes.map(type => ({
type,
name: CELL_TYPE_MAP[type as keyof typeof CELL_TYPE_MAP] || '未知',
count: stats.get(type) || 0
}))
})
const hasAvailableCells = computed(() =>
availableCells.value.length > 0
)
// 数据获取逻辑
async function refresh() {
loading.value = true
error.value = null
try {
const response = await availableStorageCells(props.shopId)
cellsData.value = response.data || []
emit('refresh')
} catch (err) {
error.value = err as Error
emit('error', err as Error)
uni.showToast({
title: '数据加载失败',
icon: 'error'
})
} finally {
loading.value = false
}
}
// 事件处理函数
function handleDeposit() {
emit('deposit')
}
function handleRetrieve() {
emit('retrieve')
}
// 生命周期钩子
onMounted(() => {
if (props.autoLoad !== false) {
refresh()
}
})
</script>
<template>
<view class="storage-cells-summary">
<!-- 标题区域 -->
<view class="summary-header">
<text class="header-title">本区域空余暂存柜子</text>
<wd-button v-if="!loading" size="small" type="primary" plain @click="refresh">
刷新
</wd-button>
</view>
<!-- 加载状态 -->
<view v-if="loading" class="loading-state">
<wd-loading type="ring" />
<text class="loading-text">正在加载格口数据...</text>
</view>
<!-- 错误状态 -->
<view v-else-if="error" class="error-state">
<wd-icon name="warning" size="48px" color="#F56C6C" />
<text class="error-text">数据加载失败</text>
<wd-button size="small" type="primary" @click="refresh">重试</wd-button>
</view>
<!-- 空状态 -->
<view v-else-if="!hasAvailableCells" class="empty-state">
<wd-icon name="box" size="48px" color="#C0C4CC" />
<text class="empty-text">暂无可用格口</text>
</view>
<!-- 格口类型选择区域 -->
<view v-else class="cell-type-selection">
<wd-radio-group v-model="selectedCellType" shape="button" size="large" inline>
<wd-radio
v-for="stat in cellTypeStats"
:key="stat.type"
:value="stat.type"
:disabled="stat.count === 0"
>
{{ stat.name }} ({{ stat.count }})
</wd-radio>
</wd-radio-group>
</view>
<!-- 操作按钮区域 -->
<view v-if="props.showButtons && hasAvailableCells && !loading" class="action-buttons">
<wd-row :gutter="16">
<wd-col :span="12">
<wd-button type="primary" block @click="handleDeposit">物品暂存</wd-button>
</wd-col>
<wd-col :span="12">
<wd-button type="success" block @click="handleRetrieve">物品取出</wd-button>
</wd-col>
</wd-row>
</view>
</view>
</template>
<style scoped lang="scss">
.storage-cells-summary {
padding: 32rpx;
background: #fff;
border-radius: 16rpx;
box-shadow: 0 4rpx 24rpx rgba(0, 0, 0, 0.08);
.summary-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 32rpx;
.header-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
}
.cell-type-selection {
margin-bottom: 48rpx;
.wd-radio {
margin-bottom: 16rpx;
}
}
.action-buttons {
margin-top: 32rpx;
}
// 状态样式
.loading-state,
.error-state,
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 64rpx 0;
text-align: center;
.loading-text,
.error-text,
.empty-text {
margin-top: 24rpx;
font-size: 28rpx;
color: #666;
}
}
.error-state {
.error-text {
color: #F56C6C;
margin-bottom: 24rpx;
}
}
}
</style>