Files
klp-mono/apps/hand-factory/components/klp-warehouse-picker/klp-warehouse-picker.vue
砂糖 5bae1f405b feat: 更新版本号至1.3.23并新增真实库区选择器组件
refactor(warehouse-picker): 重构逻辑库区选择器组件
feat(actual-warehouse-picker): 新增真实库区选择器组件
fix(easycode.vue): 调整钢卷质量状态校验逻辑
style(search.vue): 优化表单样式和字段显示
2026-01-13 15:49:43 +08:00

433 lines
9.9 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view>
<!-- 选择器触发按钮 - 核心互斥未选中=箭头 / 选中=清除按钮二选一 -->
<view
class="picker-input"
@click="handleOpen"
:class="{ 'picker-input-disabled': disabled }"
>
<text class="picker-text" :class="{ 'picker-placeholder': !selectedName }">
{{ selectedName || placeholder }}
</text>
<!-- 互斥核心选中库区 清除按钮 | 未选中 下拉箭头永不共存 -->
<text
class="picker-clear"
v-if="selectedName && !disabled"
@click.stop.prevent="handleClear"
></text>
<text class="picker-arrow" v-else-if="!disabled"></text>
</view>
<!-- 弹窗内容 - 固定逻辑库区标题无任何真实库区相关内容 -->
<uni-popup ref="popup" type="bottom" @close="handlePopupClose">
<view class="warehouse-popup">
<!-- 弹窗头部 - 固定标题选择逻辑库区 -->
<view class="popup-header">
<text class="popup-title">选择逻辑库区</text>
<text class="popup-close" @click="handleClose"></text>
</view>
<!-- 加载状态 -->
<view class="loading-tip" v-if="loading">
<uni-loading type="circle" size="24"></uni-loading>
<text class="loading-text">加载库区中...</text>
</view>
<!-- 搜索框 -->
<view class="popup-search" v-if="!loading">
<input
v-model="searchKeyword"
@input="handleSearch"
placeholder="搜索库区名称"
class="search-input"
/>
</view>
<!-- 库区列表 -->
<scroll-view class="popup-body" scroll-y v-if="!loading">
<view
class="warehouse-item"
v-for="item in filteredList"
:key="item.warehouseId"
@click="handleSelect(item)"
>
<text class="warehouse-name">{{ item.warehouseName }}</text>
<text class="warehouse-check" v-if="item.warehouseId === selectedId"></text>
</view>
<view class="empty-tip" v-if="filteredList.length === 0">
<text>未找到匹配的库区</text>
</view>
</scroll-view>
<!-- 错误提示 -->
<view class="error-tip" v-if="error">
<text class="error-icon"></text>
<text class="error-text">{{ errorMsg || '加载库区失败' }}</text>
<button class="retry-btn" @click="loadWarehouses">重试</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
// ✅ 只保留逻辑库区接口,彻底删除真实库区接口引入
import { listWarehouse } from '@/api/wms/warehouse.js'
export default {
name: 'WarehousePicker',
props: {
// 已选中的逻辑库区ID用于回显
value: {
type: [String, Number],
default: ''
},
// 禁用状态
disabled: {
type: Boolean,
default: false
},
// 按钮占位文本
placeholder: {
type: String,
default: '请选择库区'
},
// 弹窗标题(保留该属性,如需自定义可传,默认固定为选择逻辑库区)
title: {
type: String,
default: ''
}
},
data() {
return {
// 弹窗显示状态
showPopup: false,
// 所有逻辑库区列表
dataList: [],
// 过滤后的库区列表
filteredList: [],
// 搜索关键词
searchKeyword: '',
// 加载状态
loading: false,
// 错误状态
error: false,
// 错误信息
errorMsg: '',
// 选中的库区名称(用于显示)
selectedName: ''
}
},
computed: {
// 选中的库区ID双向绑定
selectedId: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
}
}
},
watch: {
// 监听选中值变化,更新显示名称
value: {
immediate: true,
handler(val) {
if (val && this.dataList.length) {
const matched = this.dataList.find(item => item.warehouseId === val)
this.selectedName = matched ? matched.warehouseName : ''
}
}
}
},
mounted() {
this.loadWarehouses()
},
methods: {
// 加载逻辑库区列表 ✅ 精简后:只保留逻辑库区接口请求,无任何类型判断
async loadWarehouses() {
if (this.loading) return
this.loading = true
this.error = false
this.errorMsg = ''
try {
const res = await listWarehouse({ pageNum: 1, pageSize: 1000 })
if (res.code === 200) {
this.dataList = res.data || []
this.filteredList = [...this.dataList]
this.updateSelectedName()
} else {
throw new Error(res.msg || '逻辑库区数据加载失败')
}
} catch (err) {
this.error = true
this.errorMsg = err.message
console.error('逻辑库区加载失败:', err)
} finally {
this.loading = false
}
},
// 更新选中的库区名称
updateSelectedName() {
if (!this.selectedId) return
const matched = this.dataList.find(item => item.warehouseId === this.selectedId)
this.selectedName = matched ? matched.warehouseName : ''
},
// 搜索过滤:只匹配逻辑库区名称
handleSearch() {
const keyword = this.searchKeyword.trim().toLowerCase()
if (!keyword) {
this.filteredList = [...this.dataList]
} else {
this.filteredList = this.dataList.filter(item =>
item.warehouseName.toLowerCase().includes(keyword)
)
}
},
// 选择逻辑库区:保留禁用库区过滤逻辑
handleSelect(item) {
if (item.isEnabled == 0) {
uni.showToast({
title: '该库区已禁用',
icon: 'none'
});
return;
}
this.selectedId = item.warehouseId
this.selectedName = item.warehouseName
this.$emit('change', item)
this.handleClose()
},
// 打开弹窗
handleOpen() {
if (this.disabled) return
this.searchKeyword = ''
this.filteredList = [...this.dataList]
this.showPopup = true
this.$refs.popup.open()
},
// 关闭弹窗
handleClose() {
this.showPopup = false
this.$refs.popup.close()
},
// 弹窗关闭回调
handlePopupClose() {
this.showPopup = false
},
// ✅ 清空选中的库区数据清空ID+名称,双向绑定同步,触发空回调
handleClear() {
this.selectedId = '';
this.selectedName = '';
this.$emit('change', {});
}
}
}
</script>
<style scoped lang="scss">
/* 保持原有样式不变,样式适配互斥显示,无需任何修改 */
.picker-input {
width: 100%;
height: 88rpx;
padding: 0 24rpx;
padding-right: 60rpx; // 预留按钮/箭头空间,防止文本遮挡
background: #f8f9fa;
border: 2rpx solid #e8e8e8;
border-radius: 12rpx;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
transition: all 0.3s;
&:active {
background: #fff;
border-color: #007aff;
}
&.picker-input-disabled {
background: #f5f5f5;
color: #999;
cursor: not-allowed;
}
.picker-text {
flex: 1;
font-size: 28rpx;
color: #333;
&.picker-placeholder {
color: #999;
}
}
// 下拉箭头样式
.picker-arrow {
font-size: 24rpx;
color: #999;
margin-left: 10rpx;
}
// 清除按钮样式 (和箭头位置一致,样式协调)
.picker-clear {
font-size: 28rpx;
color: #999;
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: center;
margin-left: 8rpx;
border-radius: 50%;
&:active {
background-color: #e8e8e8;
color: #666;
}
}
}
.warehouse-popup {
background: #fff;
border-radius: 24rpx 24rpx 0 0;
max-height: 70vh;
}
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
.popup-title {
font-size: 32rpx;
color: #333;
font-weight: 600;
}
.popup-close {
font-size: 40rpx;
color: #999;
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
.loading-tip {
display: flex;
align-items: center;
justify-content: center;
padding: 60rpx 0;
gap: 16rpx;
.loading-text {
font-size: 28rpx;
color: #666;
}
}
.error-tip {
padding: 40rpx 30rpx;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
gap: 20rpx;
.error-icon {
font-size: 48rpx;
color: #ff4d4f;
}
.error-text {
font-size: 28rpx;
color: #ff4d4f;
max-width: 80%;
}
.retry-btn {
margin-top: 10rpx;
padding: 10rpx 30rpx;
background: #007aff;
color: #fff;
border-radius: 20rpx;
font-size: 26rpx;
}
}
.popup-search {
padding: 20rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
.search-input {
width: 100%;
height: 80rpx;
padding: 0 20rpx;
background: #f8f9fa;
border: 2rpx solid #e8e8e8;
border-radius: 10rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
&:focus {
background: #fff;
border-color: #007aff;
}
}
}
.popup-body {
max-height: 400rpx;
.warehouse-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
&:active {
background: #f0f7ff;
}
.warehouse-name {
flex: 1;
font-size: 28rpx;
color: #333;
}
.warehouse-check {
font-size: 32rpx;
color: #007aff;
font-weight: bold;
}
}
.empty-tip {
text-align: center;
padding: 60rpx 0;
color: #999;
font-size: 28rpx;
}
}
</style>