feat(智能柜): 实现物品存取流程及密码验证功能
添加格口分配、密码验证和状态重置的API接口 重构storage-cells-summary组件,实现完整的存入取出流程 添加密码生成、验证和格口操作的用户交互
This commit is contained in:
parent
ad13dce989
commit
8239ed657f
|
|
@ -1,5 +1,5 @@
|
|||
import { http } from "@/http/http";
|
||||
import type { AvailableStorageCellDTO, CabinetDetailDTO, RentingCabinetDetailDTO } from './types';
|
||||
import type { AvailableStorageCellDTO, CabinetCellEntity, CabinetDetailDTO, RentingCabinetDetailDTO, StoreItemToCellCommand, OpenCellByPasswordCommand, ResetCellByPasswordCommand } from './types';
|
||||
import type { OpenCabinetApiData } from '@/api/shop/types';
|
||||
|
||||
/** 获取智能柜详情接口 */
|
||||
|
|
@ -37,3 +37,18 @@ export async function changeGoodsCellsStock(cellId: number, stock: number) {
|
|||
export async function clearGoodsCells(cellId: number) {
|
||||
return await http.put<void>(`/cabinet/clearGoodsCells/${cellId}`);
|
||||
}
|
||||
|
||||
/** 存入物品分配格口 */
|
||||
export async function storeItemApi(command: StoreItemToCellCommand) {
|
||||
return await http.post<CabinetCellEntity>('cabinet/storeItem', command);
|
||||
}
|
||||
|
||||
/** 根据密码开启格口接口 */
|
||||
export async function openByPassword(command: OpenCellByPasswordCommand) {
|
||||
return await http.post<void>("cabinet/openByPassword", command);
|
||||
}
|
||||
|
||||
/** 重置格口状态接口 */
|
||||
export async function resetByPassword(command: ResetCellByPasswordCommand) {
|
||||
return await http.post<void>("cabinet/resetByPassword", command);
|
||||
}
|
||||
|
|
@ -104,3 +104,27 @@ export interface AvailableStorageCellDTO {
|
|||
/** 封面图URL */
|
||||
coverImg: string
|
||||
}
|
||||
|
||||
/** 存入物品分配格口请求参数 */
|
||||
export interface StoreItemToCellCommand {
|
||||
/** 店铺ID */
|
||||
shopId: number
|
||||
/** 格口类型(1小格 2中格 3大格 4超大格) */
|
||||
cellType: number
|
||||
}
|
||||
|
||||
/** 根据密码开启格口请求参数 */
|
||||
export interface OpenCellByPasswordCommand {
|
||||
/** 店铺ID */
|
||||
shopId: number;
|
||||
/** 格口密码 */
|
||||
password: string;
|
||||
}
|
||||
|
||||
/** 重置格口状态请求参数 */
|
||||
export interface ResetCellByPasswordCommand {
|
||||
/** 店铺ID */
|
||||
shopId: number;
|
||||
/** 格口密码 */
|
||||
password: string;
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import { availableStorageCells } from '@/api/cabinet/index'
|
||||
import { availableStorageCells, storeItemApi, openByPassword, resetByPassword } from '@/api/cabinet/index'
|
||||
import type { AvailableStorageCellDTO } from '@/api/cabinet/types'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useMessage } from 'wot-design-uni'
|
||||
|
||||
// 格口类型映射
|
||||
const CELL_TYPE_MAP = {
|
||||
|
|
@ -32,6 +33,7 @@ const emit = defineEmits<{
|
|||
(e: 'retrieve'): void
|
||||
(e: 'refresh'): void
|
||||
(e: 'error', error: Error): void
|
||||
(e: 'backToAddressSelect'): void
|
||||
}>()
|
||||
|
||||
// 状态变量
|
||||
|
|
@ -40,6 +42,11 @@ const error = ref<Error | null>(null)
|
|||
const cellsData = ref<AvailableStorageCellDTO[]>([])
|
||||
const selectedCellType = ref<number>(1)
|
||||
|
||||
const message = useMessage()
|
||||
const depositLoading = ref(false)
|
||||
const retrieveLoading = ref(false)
|
||||
const generatedPassword = ref('')
|
||||
|
||||
// 统计计算属性
|
||||
const availableCells = computed(() =>
|
||||
cellsData.value.filter(cell => !cell.hasPassword)
|
||||
|
|
@ -89,11 +96,173 @@ async function refresh() {
|
|||
|
||||
// 事件处理函数
|
||||
function handleDeposit() {
|
||||
// 保持事件发射以向后兼容
|
||||
emit('deposit')
|
||||
// 调用新的存入流程函数
|
||||
handleDepositFlow()
|
||||
}
|
||||
|
||||
function handleRetrieve() {
|
||||
// 保持事件发射以向后兼容
|
||||
emit('retrieve')
|
||||
// 调用新的取出流程函数
|
||||
handleRetrieveFlow()
|
||||
}
|
||||
|
||||
function handleBackToAddressSelect() {
|
||||
emit('backToAddressSelect')
|
||||
}
|
||||
|
||||
// 存入流程处理函数
|
||||
async function handleDepositFlow() {
|
||||
try {
|
||||
// 1. 调用 storeItemApi 分配格口
|
||||
depositLoading.value = true
|
||||
const response = await storeItemApi({
|
||||
shopId: props.shopId,
|
||||
cellType: selectedCellType.value
|
||||
})
|
||||
|
||||
// 保存生成的密码(根据用户确认,接口返回包含 password 字段)
|
||||
generatedPassword.value = response.data?.password || ''
|
||||
|
||||
// 检查密码是否为空
|
||||
if (!generatedPassword.value) {
|
||||
throw new Error('格口分配失败,未获取到密码')
|
||||
}
|
||||
|
||||
// 2. 显示密码弹窗让用户记住
|
||||
await message.alert({
|
||||
title: '密码已生成',
|
||||
msg: `请牢记你的暂存密码为:${generatedPassword.value}\n请确认已打开的柜子,放置物品后将柜子关闭。`,
|
||||
confirmButtonText: '已记住',
|
||||
closeOnClickModal: false
|
||||
})
|
||||
|
||||
// 3. 密码验证弹窗
|
||||
const { value: inputPassword } = await message.prompt({
|
||||
title: '密码验证',
|
||||
msg: '请输入刚才显示的密码进行验证',
|
||||
inputPlaceholder: '请输入密码',
|
||||
closeOnClickModal: false,
|
||||
inputValidate: ((value: string) => {
|
||||
if (!value) return '请输入密码'
|
||||
if (value !== generatedPassword.value) return '密码不正确'
|
||||
return true
|
||||
}) as any
|
||||
})
|
||||
|
||||
// 4. 打开格口
|
||||
await openByPassword({
|
||||
shopId: props.shopId,
|
||||
password: String(inputPassword)
|
||||
})
|
||||
|
||||
// 5. 成功提示
|
||||
uni.showToast({
|
||||
title: '格口已打开',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
// 6. 刷新数据
|
||||
await refresh()
|
||||
|
||||
} catch (error) {
|
||||
// 处理不同类型的错误
|
||||
// 用户取消操作(message.prompt/confirm/alert 返回 'cancel' 字符串或包含 cancel 的错误)
|
||||
if (error === 'cancel' || (error as any)?.message?.includes?.('cancel')) {
|
||||
return
|
||||
}
|
||||
|
||||
// API 错误处理
|
||||
const errorMessage = (error as any)?.message || '操作失败'
|
||||
console.error('存入流程失败:', error)
|
||||
|
||||
// 显示具体的错误信息
|
||||
uni.showToast({
|
||||
title: errorMessage.length > 20 ? '操作失败' : errorMessage,
|
||||
icon: 'error',
|
||||
duration: 3000
|
||||
})
|
||||
} finally {
|
||||
depositLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 取出流程处理函数
|
||||
async function handleRetrieveFlow() {
|
||||
try {
|
||||
// 1. 确认取出弹窗
|
||||
// 注释掉确认弹窗,直接执行取出操作
|
||||
/* await message.confirm({
|
||||
title: '物品取出',
|
||||
msg: '确认要取出物品吗?',
|
||||
closeOnClickModal: false
|
||||
}) */
|
||||
|
||||
// 2. 密码输入弹窗
|
||||
const { value: password } = await message.prompt({
|
||||
title: '输入密码',
|
||||
msg: '请输入格口密码',
|
||||
inputPlaceholder: '请输入密码',
|
||||
closeOnClickModal: false,
|
||||
inputType: ('password') as any
|
||||
})
|
||||
|
||||
// 3. 打开格口
|
||||
retrieveLoading.value = true
|
||||
await openByPassword({
|
||||
shopId: props.shopId,
|
||||
password: String(password)
|
||||
})
|
||||
|
||||
// 4. 成功提示并询问是否清空
|
||||
await message.alert({
|
||||
title: '格口已打开',
|
||||
msg: '柜子已开,请取物品!如不再使用柜子请点击 “清空” 。',
|
||||
confirmButtonText: '清空',
|
||||
closeOnClickModal: false
|
||||
})
|
||||
|
||||
// 5. 确认清空弹窗
|
||||
await message.confirm({
|
||||
title: '确认清空',
|
||||
msg: '清空后密码将不能再次使用,确认清空?',
|
||||
closeOnClickModal: false
|
||||
})
|
||||
|
||||
// 6. 重置格口状态
|
||||
await resetByPassword({
|
||||
shopId: props.shopId,
|
||||
password: String(password)
|
||||
})
|
||||
|
||||
// 7. 重置成功提示
|
||||
uni.showToast({
|
||||
title: '格口已清空',
|
||||
icon: 'success'
|
||||
})
|
||||
} catch (error) {
|
||||
// 错误处理
|
||||
// 用户取消操作(message.prompt/confirm/alert 返回 'cancel' 字符串或包含 cancel 的错误)
|
||||
if (error === 'cancel' || (error as any)?.message?.includes?.('cancel')) {
|
||||
return
|
||||
}
|
||||
|
||||
// API 错误处理
|
||||
const errorMessage = (error as any)?.message || '操作失败'
|
||||
console.error('取出流程失败:', error)
|
||||
|
||||
// 显示具体的错误信息
|
||||
uni.showToast({
|
||||
title: errorMessage.length > 20 ? '操作失败' : errorMessage,
|
||||
icon: 'error',
|
||||
duration: 3000
|
||||
})
|
||||
} finally {
|
||||
retrieveLoading.value = false
|
||||
await refresh()
|
||||
}
|
||||
}
|
||||
|
||||
// 生命周期钩子
|
||||
|
|
@ -109,10 +278,16 @@ onMounted(() => {
|
|||
<!-- 标题区域 -->
|
||||
<view class="summary-header">
|
||||
<text class="header-title">本区域空余暂存柜子</text>
|
||||
<view class="header-actions">
|
||||
<view class="back-btn" @click="handleBackToAddressSelect">
|
||||
<wd-icon name="arrow-left" size="12px"></wd-icon>
|
||||
<text style="margin-left: 4px">重选地址</text>
|
||||
</view>
|
||||
<wd-button v-if="!loading" size="small" type="primary" plain @click="refresh">
|
||||
刷新
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<view v-if="loading" class="loading-state">
|
||||
|
|
@ -151,13 +326,16 @@ onMounted(() => {
|
|||
<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-button type="primary" block @click="handleDeposit" :loading="depositLoading" :disabled="depositLoading || retrieveLoading">物品暂存</wd-button>
|
||||
</wd-col>
|
||||
<wd-col :span="12">
|
||||
<wd-button type="success" block @click="handleRetrieve">物品取出</wd-button>
|
||||
<wd-button type="success" block @click="handleRetrieve" :loading="retrieveLoading" :disabled="depositLoading || retrieveLoading">物品取出</wd-button>
|
||||
</wd-col>
|
||||
</wd-row>
|
||||
</view>
|
||||
|
||||
<!-- MessageBox 组件 -->
|
||||
<wd-message-box />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
|
@ -179,6 +357,28 @@ onMounted(() => {
|
|||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
|
||||
.back-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8rpx 12rpx;
|
||||
background: #fff;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
white-space: nowrap;
|
||||
|
||||
&:active {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cell-type-selection {
|
||||
|
|
@ -219,5 +419,10 @@ onMounted(() => {
|
|||
margin-bottom: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-message-box {
|
||||
/* 保留空白,自动换行 */
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -121,15 +121,23 @@ function handleCheckoutRenting() {
|
|||
|
||||
// storage-cells-summary 事件处理方法
|
||||
function handleDeposit() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/storage/deposit?shopId=${shopId.value}`
|
||||
})
|
||||
// 原有的跳转逻辑已删除
|
||||
// uni.navigateTo({
|
||||
// url: `/pages/storage/deposit?shopId=${shopId.value}`
|
||||
// })
|
||||
|
||||
// 可以保留简单的事件处理
|
||||
console.log('存入操作触发')
|
||||
}
|
||||
|
||||
function handleRetrieve() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/storage/retrieve?shopId=${shopId.value}`
|
||||
})
|
||||
// 原有的跳转逻辑已删除
|
||||
// uni.navigateTo({
|
||||
// url: `/pages/storage/retrieve?shopId=${shopId.value}`
|
||||
// })
|
||||
|
||||
// 可以保留简单的事件处理
|
||||
console.log('取出操作触发')
|
||||
}
|
||||
|
||||
function handleRefresh() {
|
||||
|
|
@ -279,6 +287,7 @@ onShow(async () => {
|
|||
@retrieve="handleRetrieve"
|
||||
@refresh="handleRefresh"
|
||||
@error="handleError"
|
||||
@back-to-address-select="backToShopList"
|
||||
/>
|
||||
<!-- 商品列表 -->
|
||||
<ProductContainer
|
||||
|
|
|
|||
Loading…
Reference in New Issue