feat(cabinet): 添加格口备注功能
- 在类型定义中为 CabinetCellEntity、CellInfoDTO 和 AvailableStorageCellDTO 添加 remark 字段 - 新增 UpdateCellRemarkDTO 类型用于更新格口备注 - 添加 updateCellRemark API 方法 - 在格口列表页添加备注显示 - 实现格口点击弹窗功能,包含备注编辑和保存 - 添加相关样式和状态管理
This commit is contained in:
parent
7eb6521dfd
commit
3d99b20754
|
|
@ -6,7 +6,8 @@ import type {
|
|||
StoreItemToCellCommand,
|
||||
OpenCellByPasswordCommand,
|
||||
ResetCellByPasswordCommand,
|
||||
CabinetCellEntity
|
||||
CabinetCellEntity,
|
||||
UpdateCellRemarkDTO
|
||||
} from './type'
|
||||
import { OpenCabinetApiData } from '../shop/type'
|
||||
|
||||
|
|
@ -54,32 +55,41 @@ export function openCabinet(cabinetId: number, pinNo: number, data: OpenCabinetA
|
|||
|
||||
export const configureGoodsCellsStock = (cellId: number, goodsId: number, stock: number) => {
|
||||
return request<ApiResponseData<void>>({
|
||||
url: `/cabinet/configureGoodsCellsStock/${cellId}/${goodsId}/${stock}`,
|
||||
url: `cabinet/configureGoodsCellsStock/${cellId}/${goodsId}/${stock}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
export const changeGoodsCellsStock = (cellId: number, stock: number) => {
|
||||
return request<ApiResponseData<void>>({
|
||||
url: `/cabinet/changeGoodsCellsStock/${cellId}/${stock}`,
|
||||
url: `cabinet/changeGoodsCellsStock/${cellId}/${stock}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
export const clearGoodsCells = (cellId: number) => {
|
||||
return request<ApiResponseData<void>>({
|
||||
url: `/cabinet/clearGoodsCells/${cellId}`,
|
||||
url: `cabinet/clearGoodsCells/${cellId}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
export const resetCellById = (cellId: number) => {
|
||||
return request<ApiResponseData<void>>({
|
||||
url: `/cabinet/reset/${cellId}`,
|
||||
url: `cabinet/reset/${cellId}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
/** 临时:更新格口备注 */
|
||||
export const updateCellRemark = (data: UpdateCellRemarkDTO) => {
|
||||
return request<ApiResponseData<void>>({
|
||||
url: `cabinet/cell`,
|
||||
method: 'put',
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
/** 获取可用暂存柜格口列表 */
|
||||
export function availableStorageCells(shopId: number) {
|
||||
return request<ApiResponseData<AvailableStorageCellDTO[]>>({
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ export interface CabinetCellEntity {
|
|||
goodsId?: number
|
||||
/** 密码 */
|
||||
password?: string
|
||||
/** 备注 */
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export interface CellInfoDTO {
|
||||
|
|
@ -75,6 +77,8 @@ export interface CellInfoDTO {
|
|||
usageStatus: number
|
||||
/** 格口类型(1小格 2中格 3大格 4超大格) */
|
||||
cellType: number
|
||||
/** 备注 */
|
||||
remark?: string
|
||||
}
|
||||
|
||||
export interface ProductInfoDTO {
|
||||
|
|
@ -122,6 +126,8 @@ export interface AvailableStorageCellDTO {
|
|||
price: number
|
||||
/** 封面图URL */
|
||||
coverImg: string
|
||||
/** 备注 */
|
||||
remark?: string
|
||||
}
|
||||
|
||||
/** 存入物品分配格口请求参数 */
|
||||
|
|
@ -146,4 +152,36 @@ export interface ResetCellByPasswordCommand {
|
|||
shopId: number
|
||||
/** 格口密码 */
|
||||
password: string
|
||||
}
|
||||
|
||||
/** 更新格口备注请求参数(cellId 必需,其他字段可选) */
|
||||
export interface UpdateCellRemarkDTO {
|
||||
/** 格口唯一ID */
|
||||
cellId: number
|
||||
/** 关联柜机ID */
|
||||
cabinetId?: number
|
||||
/** 主板ID */
|
||||
mainboardId?: number
|
||||
/** 格口号 */
|
||||
cellNo?: number
|
||||
/** 针脚序号 */
|
||||
pinNo?: number
|
||||
/** 库存数量 */
|
||||
stock?: number
|
||||
/** 格口价格 */
|
||||
cellPrice?: number
|
||||
/** 是否已租用:0-未租用,1-已租用 */
|
||||
isRented?: number
|
||||
/** 格口类型(1小格 2中格 3大格 4超大格) */
|
||||
cellType?: number
|
||||
/** 使用状态:1空闲 2已占用 */
|
||||
usageStatus?: number
|
||||
/** 可用状态:1正常 2故障 */
|
||||
availableStatus?: number
|
||||
/** 商品ID */
|
||||
goodsId?: number
|
||||
/** 密码 */
|
||||
password?: string
|
||||
/** 备注 */
|
||||
remark?: string
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
一键全开
|
||||
</van-button>
|
||||
</div>
|
||||
<van-cell v-for="locker in lockerList" :key="locker.lockerId" class="product-item">
|
||||
<van-cell v-for="locker in lockerList" :key="locker.lockerId" class="product-item" @click="handleLockerClick(locker)">
|
||||
<template #icon>
|
||||
<div class="image-container">
|
||||
<van-image width="80" height="80" v-if="locker.coverImg"
|
||||
|
|
@ -183,22 +183,8 @@
|
|||
<!-- <div class="goods-name">{{ locker.usageStatus === 1 ? '空闲' : '占用' }}</div> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<van-button v-if="selectedShop?.mode !== 5" size="small" type="primary" class="detail-btn"
|
||||
@click="showBindGoods(locker)">
|
||||
绑定商品
|
||||
</van-button>
|
||||
<van-button v-if="selectedShop?.mode === 5 && locker.password" size="small" type="primary"
|
||||
class="detail-btn" @click="handleClearPassword(locker)">
|
||||
清除密码
|
||||
</van-button>
|
||||
<div v-else-if="selectedShop?.mode === 5 && !locker.password" class="detail-btn-placeholder">
|
||||
</div>
|
||||
|
||||
<van-button size="small" plain hairline :loading="openingLockerId === locker.lockerId"
|
||||
@click="handleOpenLocker(locker)" style="margin-left: auto;">
|
||||
立即开启
|
||||
</van-button>
|
||||
<div class="remark-info">
|
||||
备注: {{ locker.remark || '-' }}
|
||||
</div>
|
||||
</div>
|
||||
</van-cell>
|
||||
|
|
@ -211,6 +197,48 @@
|
|||
@cancel="showBindGoodsPopup = false" @success="handleBindSuccess" @unbind="handleBindSuccess"
|
||||
@update:currentStock="handleStockUpdate" />
|
||||
</VanPopup>
|
||||
|
||||
<!-- 格口控制弹窗 -->
|
||||
<VanPopup v-model:show="showLockerPopup" position="bottom" :style="{ height: '45%' }" round>
|
||||
<div class="locker-popup-content" v-if="selectedLocker">
|
||||
<div class="popup-header">
|
||||
<span class="popup-title">格口 {{ selectedLocker.cellNo }}</span>
|
||||
<van-icon name="cross" @click="showLockerPopup = false" />
|
||||
</div>
|
||||
|
||||
<div class="locker-info">
|
||||
<div class="info-row">
|
||||
<span>状态: {{ selectedLocker.usageStatus === 1 ? '空闲' : '占用' }}</span>
|
||||
<span v-if="selectedLocker.goodsName">商品: {{ selectedLocker.goodsName }}</span>
|
||||
</div>
|
||||
<div class="info-row" v-if="selectedShop?.mode !== 5">
|
||||
<span>价格: ¥{{ (selectedLocker.price || 0).toFixed(2) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<van-field v-model="editingRemark" label="备注" placeholder="请输入备注" rows="2" type="textarea"
|
||||
class="remark-field" />
|
||||
|
||||
<div class="popup-actions">
|
||||
<van-button type="primary" :loading="openingLockerId === selectedLocker.lockerId"
|
||||
@click="handleOpenLocker(selectedLocker)" style="flex: 1;">
|
||||
立即开启
|
||||
</van-button>
|
||||
<van-button v-if="selectedShop?.mode === 5 && selectedLocker.password" plain
|
||||
@click="handleClearPassword(selectedLocker)" style="flex: 1;">
|
||||
清除密码
|
||||
</van-button>
|
||||
<van-button v-else-if="selectedShop?.mode !== 5" plain
|
||||
@click="handleBindFromLocker(selectedLocker)" style="flex: 1;">
|
||||
绑定商品
|
||||
</van-button>
|
||||
</div>
|
||||
|
||||
<van-button @click="handleSaveRemark" :loading="isSavingRemark" style="margin-top: 8px;">
|
||||
保存备注
|
||||
</van-button>
|
||||
</div>
|
||||
</VanPopup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -219,7 +247,7 @@ import { throttle } from 'lodash-es';
|
|||
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue';
|
||||
import { getShopListApi, getModeListApi } from '@/common/apis/shop';
|
||||
import { ShopEntity } from '@/common/apis/shop/type';
|
||||
import { getCabinetDetailApi, openCabinet, changeGoodsCellsStock, clearGoodsCells, resetCellById } from '@/common/apis/cabinet';
|
||||
import { getCabinetDetailApi, openCabinet, changeGoodsCellsStock, clearGoodsCells, resetCellById, updateCellRemark } from '@/common/apis/cabinet';
|
||||
import type { CabinetDetailDTO } from '@/common/apis/cabinet/type';
|
||||
import { useWxStore, useWxStoreOutside } from '@/pinia/stores/wx';
|
||||
import { useRouterStore } from '@/pinia/stores/router';
|
||||
|
|
@ -249,6 +277,11 @@ const shopList = ref<ShopEntity[]>([]);
|
|||
const shopId = ref<number>(0);
|
||||
const selectedShop = ref<ShopEntity | null>(null);
|
||||
const headerHeight = ref(150);
|
||||
// 弹窗控制相关状态
|
||||
const showLockerPopup = ref(false)
|
||||
const selectedLocker = ref<LockerItem | null>(null)
|
||||
const editingRemark = ref('')
|
||||
const isSavingRemark = ref(false)
|
||||
// 分类选择相关状态
|
||||
const activeModeIndex = ref(0); // 默认选中第一个(全部)
|
||||
const modeList = ref<number[]>([]);
|
||||
|
|
@ -279,6 +312,7 @@ interface LockerItem {
|
|||
password?: string
|
||||
usageStatus?: number
|
||||
cellType?: number
|
||||
remark?: string
|
||||
}
|
||||
|
||||
// 获取机柜列表
|
||||
|
|
@ -320,6 +354,7 @@ const updateLockerList = (cabinet: CabinetDetailDTO) => {
|
|||
password: cell.password,
|
||||
usageStatus: cell.usageStatus,
|
||||
cellType: cell.cellType,
|
||||
remark: cell.remark,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -509,6 +544,42 @@ const handleClearPassword = async (locker: LockerItem) => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleBindFromLocker = (locker: LockerItem) => {
|
||||
currentLocker.value = locker;
|
||||
showLockerPopup.value = false;
|
||||
showBindGoodsPopup.value = true;
|
||||
};
|
||||
|
||||
// 点击格口打开弹窗
|
||||
const handleLockerClick = (locker: LockerItem) => {
|
||||
selectedLocker.value = locker
|
||||
editingRemark.value = locker.remark || ''
|
||||
showLockerPopup.value = true
|
||||
}
|
||||
|
||||
// 保存备注
|
||||
const handleSaveRemark = async () => {
|
||||
if (!selectedLocker.value) return
|
||||
isSavingRemark.value = true
|
||||
try {
|
||||
await updateCellRemark({
|
||||
cellId: selectedLocker.value.lockerId,
|
||||
remark: editingRemark.value
|
||||
})
|
||||
showToast('保存成功')
|
||||
// 更新本地数据
|
||||
if (selectedLocker.value) {
|
||||
selectedLocker.value.remark = editingRemark.value
|
||||
}
|
||||
loadCabinetDetail()
|
||||
showLockerPopup.value = false
|
||||
} catch (error) {
|
||||
console.error('保存备注失败:', error)
|
||||
} finally {
|
||||
isSavingRemark.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
scrollListener.push(window.addEventListener('scroll', throttledScroll));
|
||||
|
|
@ -656,6 +727,13 @@ onBeforeUnmount(() => {
|
|||
}
|
||||
}
|
||||
|
||||
.remark-info {
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
|
@ -944,4 +1022,53 @@ onBeforeUnmount(() => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 格口控制弹窗样式 */
|
||||
.locker-popup-content {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.popup-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #ebedf0;
|
||||
|
||||
.popup-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.van-icon {
|
||||
font-size: 20px;
|
||||
color: #999;
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.locker-info {
|
||||
.info-row {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.remark-field {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.popup-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue