223 lines
5.1 KiB
Vue
223 lines
5.1 KiB
Vue
|
|
<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>
|