|
@@ -10,10 +10,10 @@
|
|
|
<i class="fas fa-home" />
|
|
<i class="fas fa-home" />
|
|
|
<span>主页</span>
|
|
<span>主页</span>
|
|
|
</button>
|
|
</button>
|
|
|
- <h1 class="page-title">还料入库</h1>
|
|
|
|
|
|
|
+ <h1 class="page-title">入库</h1>
|
|
|
<!-- 右侧操作按钮 -->
|
|
<!-- 右侧操作按钮 -->
|
|
|
<div class="header-actions">
|
|
<div class="header-actions">
|
|
|
- <button class="action-btn refresh-btn" @click="verify">
|
|
|
|
|
|
|
+ <button class="action-btn refresh-btn" @click="resetView">
|
|
|
<i class="fas fa-sync-alt" />
|
|
<i class="fas fa-sync-alt" />
|
|
|
<span>重新校验</span>
|
|
<span>重新校验</span>
|
|
|
</button>
|
|
</button>
|
|
@@ -24,9 +24,8 @@
|
|
|
<main class="main-content">
|
|
<main class="main-content">
|
|
|
<!-- 统计信息 -->
|
|
<!-- 统计信息 -->
|
|
|
<div v-if="materialList.length > 0" class="stats-section">
|
|
<div v-if="materialList.length > 0" class="stats-section">
|
|
|
- <span class="stats-text">共 {{ materialList.length }} 条数据</span>
|
|
|
|
|
|
|
+ <span class="stats-text">共 {{ materialList.length }} 条数据,不识别 {{ unrecognizedCount }} 条</span>
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
<!-- 卡片网格区域 -->
|
|
<!-- 卡片网格区域 -->
|
|
|
<div class="card-grid-wrapper">
|
|
<div class="card-grid-wrapper">
|
|
|
<!-- 空状态 -->
|
|
<!-- 空状态 -->
|
|
@@ -37,54 +36,61 @@
|
|
|
|
|
|
|
|
<!-- 卡片网格 -->
|
|
<!-- 卡片网格 -->
|
|
|
<div v-else class="card-grid">
|
|
<div v-else class="card-grid">
|
|
|
- <div
|
|
|
|
|
- v-for="(item, index) in materialList"
|
|
|
|
|
- :key="item.id || index"
|
|
|
|
|
- class="inventory-card"
|
|
|
|
|
- :class="getCardClass(item.remarks)"
|
|
|
|
|
- >
|
|
|
|
|
- <!-- 卡片序号 -->
|
|
|
|
|
- <div class="card-index" :class="getCardClass(item.remarks)">{{ index + 1 }}</div>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 图片区域 -->
|
|
|
|
|
- <div class="card-image">
|
|
|
|
|
- <div class="image-placeholder" :class="getCardClass(item.remarks)">
|
|
|
|
|
- <i :class="getInventoryIcon(item.inventoryType)" />
|
|
|
|
|
|
|
+ <template v-for="(item, index) in materialList" :key="item.id || index">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-if="item.inventoryId != null"
|
|
|
|
|
+ class="inventory-card"
|
|
|
|
|
+ :class="getCardClass(item.remarks)"
|
|
|
|
|
+ @click="handleSelect(item)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <!-- 卡片序号 -->
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-if="item.selected" class="card-index"
|
|
|
|
|
+ style="background: linear-gradient(135deg, #a8f63b 0%, #60faee 100%);"
|
|
|
|
|
+ :class="getCardClass(item.remarks)"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 图片区域 -->
|
|
|
|
|
+ <div class="card-image">
|
|
|
|
|
+ <div class="image-placeholder" :class="getCardClass(item.remarks)">
|
|
|
|
|
+ <i :class="getInventoryIcon(item.inventoryType)" />
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
|
|
|
|
- <!-- 信息区域 -->
|
|
|
|
|
- <div class="card-info">
|
|
|
|
|
- <div class="info-row">
|
|
|
|
|
- <span class="info-label">名称:</span>
|
|
|
|
|
- <span class="info-value">{{ item.inventoryName || '-' }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="info-row">
|
|
|
|
|
- <span class="info-label">编号:</span>
|
|
|
|
|
- <span class="info-value">{{ item.inventoryNo || '-' }}</span>
|
|
|
|
|
|
|
+ <!-- 信息区域 -->
|
|
|
|
|
+ <div class="card-info">
|
|
|
|
|
+ <div class="info-row">
|
|
|
|
|
+ <span class="info-label">名称:</span>
|
|
|
|
|
+ <span class="info-value">{{ item.inventoryName || '-' }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="info-row">
|
|
|
|
|
+ <span class="info-label">编号:</span>
|
|
|
|
|
+ <span class="info-value">{{ item.inventoryNo || '-' }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="info-row location-row">
|
|
|
|
|
+ <i class="fas fa-map-marker-alt location-icon" />
|
|
|
|
|
+ <span class="info-value">{{ item.positionName || '-' }} / {{ item.warehouseName || '-' }}</span>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="info-row location-row">
|
|
|
|
|
- <i class="fas fa-map-marker-alt location-icon" />
|
|
|
|
|
- <span class="info-value">{{ item.positionName || '-' }} / {{ item.warehouseName || '-' }}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
|
|
|
- <!-- 状态区域(固定高度) -->
|
|
|
|
|
- <div class="card-status-section">
|
|
|
|
|
- <div class="status-badge" :class="getCardClass(item.remarks)">
|
|
|
|
|
- <i class="fas fa-exclamation-circle" />
|
|
|
|
|
- {{ item.remarks || '-' }}
|
|
|
|
|
|
|
+ <!-- 状态区域(固定高度) -->
|
|
|
|
|
+ <div class="card-status-section">
|
|
|
|
|
+ <div class="status-badge" :class="getCardClass(item.remarks)">
|
|
|
|
|
+ <i class="fas fa-exclamation-circle" />
|
|
|
|
|
+ {{ item.remarks || '-' }}
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
|
+ </template>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</main>
|
|
</main>
|
|
|
|
|
|
|
|
<!-- 底部操作按钮 -->
|
|
<!-- 底部操作按钮 -->
|
|
|
<div class="bottom-actions">
|
|
<div class="bottom-actions">
|
|
|
- <button class="submit-btn" :disabled="notInStockCount === 0" @click="handleComplete">
|
|
|
|
|
- 还料入库
|
|
|
|
|
|
|
+ <button class="submit-btn" @click="handleComplete">
|
|
|
|
|
+ 确认入库
|
|
|
|
|
+ <span v-if="validStockCount != 0">{{ validStockCount }}</span>
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -98,6 +104,29 @@
|
|
|
<span class="loading-text">加载中...</span>
|
|
<span class="loading-text">加载中...</span>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 直接进入仓库提示 -->
|
|
|
|
|
+ <div v-if="enterConfirmVisible" class="tech-modal-overlay" @click.self="enterConfirmVisible = false">
|
|
|
|
|
+ <div class="tech-modal">
|
|
|
|
|
+ <div class="modal-content-row">
|
|
|
|
|
+ <div class="modal-text">
|
|
|
|
|
+ <h3>请确认您要直接进入仓库</h3>
|
|
|
|
|
+ <p>系统检测到您未携带物料,或者未勾选任何物料,是否确认直接进入仓库?</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="modal-icon">
|
|
|
|
|
+ <div class="icon-box">
|
|
|
|
|
+ <i class="fas fa-clipboard-check" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="modal-footer">
|
|
|
|
|
+ <button class="modal-btn cancel-btn" @click="enterConfirm">直接进入仓库</button>
|
|
|
|
|
+ <button class="modal-btn confirm-btn" @click="enterConfirmVisible = false">取消</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
@@ -124,8 +153,6 @@ const loading = ref(false);
|
|
|
// 物料列表数据
|
|
// 物料列表数据
|
|
|
const materialList = ref([]);
|
|
const materialList = ref([]);
|
|
|
const epcs = ref([]);
|
|
const epcs = ref([]);
|
|
|
-// 临时收集epc数据的数组
|
|
|
|
|
-const tempEpcs = ref([]);
|
|
|
|
|
|
|
|
|
|
// 分页配置
|
|
// 分页配置
|
|
|
const pagination = reactive({
|
|
const pagination = reactive({
|
|
@@ -135,8 +162,8 @@ const pagination = reactive({
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// 计算不在库的数量
|
|
// 计算不在库的数量
|
|
|
-const notInStockCount = computed(() => {
|
|
|
|
|
- return materialList.value.filter(item => item.remarks === '不在库').length;
|
|
|
|
|
|
|
+const validStockCount = computed(() => {
|
|
|
|
|
+ return materialList.value.filter(item => item.remarks === '不在库' && item.selected === true).length;
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// 根据 remarks 返回卡片样式类
|
|
// 根据 remarks 返回卡片样式类
|
|
@@ -172,16 +199,16 @@ const getInventoryIcon = type => {
|
|
|
// 还料入库
|
|
// 还料入库
|
|
|
const handleComplete = async () => {
|
|
const handleComplete = async () => {
|
|
|
// 只处理不在库的数据
|
|
// 只处理不在库的数据
|
|
|
- const notInStockItems = materialList.value.filter(item => item.remarks === '不在库');
|
|
|
|
|
|
|
+ const notInStockItems = materialList.value.filter(item => item.selected === true);
|
|
|
|
|
|
|
|
if (notInStockItems.length === 0) {
|
|
if (notInStockItems.length === 0) {
|
|
|
- showNotify({ type: 'danger', message: '没有需要入库的物料!' });
|
|
|
|
|
|
|
+ enterConfirmVisible.value = true;
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const params = [];
|
|
const params = [];
|
|
|
- notInStockItems.forEach(item => {
|
|
|
|
|
- if (item.remarks === '不在库') {
|
|
|
|
|
|
|
+ materialList.value.forEach(item => {
|
|
|
|
|
+ if (item.selected === true) {
|
|
|
params.push({
|
|
params.push({
|
|
|
inventoryId: item.inventoryId,
|
|
inventoryId: item.inventoryId,
|
|
|
});
|
|
});
|
|
@@ -193,24 +220,53 @@ const handleComplete = async () => {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 校验
|
|
// 校验
|
|
|
-const verify = () => {
|
|
|
|
|
- getList();
|
|
|
|
|
|
|
+const resetView = () => {
|
|
|
|
|
+ epcs.value = [];
|
|
|
|
|
+ materialList.value = [];
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 获取物料列表(外侧校验)
|
|
// 获取物料列表(外侧校验)
|
|
|
const getList = async () => {
|
|
const getList = async () => {
|
|
|
// loading.value = true;
|
|
// loading.value = true;
|
|
|
- try {
|
|
|
|
|
|
|
|
|
|
- const params = {
|
|
|
|
|
- epcList: epcs.value,
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ const params = {
|
|
|
|
|
+ epcList: [],
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
|
|
+ const tempMaterialList = [];
|
|
|
|
|
+
|
|
|
|
|
+ for(let i = 0; i < materialList.value.length; i++) {
|
|
|
|
|
+ if(materialList.value[i].queryStatus === 0) {
|
|
|
|
|
+ params.epcList.push(materialList.value[i].epc);
|
|
|
|
|
+ materialList.value[i].queryStatus = 1;
|
|
|
|
|
+ tempMaterialList.push(materialList.value[i]);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(params.epcList.length == 0){
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
const res = await cfStockIn(params);
|
|
const res = await cfStockIn(params);
|
|
|
|
|
|
|
|
if (res.errorCode === 0) {
|
|
if (res.errorCode === 0) {
|
|
|
if (res.datas && res.datas.length > 0) {
|
|
if (res.datas && res.datas.length > 0) {
|
|
|
- materialList.value = res.datas;
|
|
|
|
|
|
|
+ res.datas.forEach(i => {
|
|
|
|
|
+ materialList.value.forEach(j => {
|
|
|
|
|
+ if (j.epc === i.epc) {
|
|
|
|
|
+ j.inventoryName = i.inventoryName;
|
|
|
|
|
+ j.inventoryNo = i.inventoryNo;
|
|
|
|
|
+ j.inventoryType = i.inventoryType;
|
|
|
|
|
+ j.warehouseName = i.warehouseName;
|
|
|
|
|
+ j.positionName = i.positionName;
|
|
|
|
|
+ j.inventoryId = i.inventoryId;
|
|
|
|
|
+ j.queryStatus = 2;
|
|
|
|
|
+ j.selected = false;
|
|
|
|
|
+ j.remarks = i.remarks;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
pagination.total = materialList.value.length;
|
|
pagination.total = materialList.value.length;
|
|
|
} else {
|
|
} else {
|
|
|
materialList.value = [];
|
|
materialList.value = [];
|
|
@@ -222,6 +278,10 @@ const getList = async () => {
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('获取物料列表API调用失败:', error);
|
|
console.error('获取物料列表API调用失败:', error);
|
|
|
showNotify({ type: 'danger', message: '获取物料列表API调用失败' });
|
|
showNotify({ type: 'danger', message: '获取物料列表API调用失败' });
|
|
|
|
|
+ // 处理查询失败的物料
|
|
|
|
|
+ tempMaterialList.forEach(item => {
|
|
|
|
|
+ item.queryStatus = 0;
|
|
|
|
|
+ });
|
|
|
} finally {
|
|
} finally {
|
|
|
loading.value = false;
|
|
loading.value = false;
|
|
|
}
|
|
}
|
|
@@ -240,7 +300,7 @@ const generateCFStockIn = async params => {
|
|
|
|
|
|
|
|
showNotify({ type: 'success', message: '入库申请已完成,还料任务已创建!' });
|
|
showNotify({ type: 'success', message: '入库申请已完成,还料任务已创建!' });
|
|
|
// 跳转回主页
|
|
// 跳转回主页
|
|
|
- router.push('/home');
|
|
|
|
|
|
|
+ router.push('/');
|
|
|
} else {
|
|
} else {
|
|
|
showNotify({ type: 'danger', message: res.errorMessage });
|
|
showNotify({ type: 'danger', message: res.errorMessage });
|
|
|
}
|
|
}
|
|
@@ -259,8 +319,25 @@ const addEpc = data => {
|
|
|
const newEpcs = data.map(item => item.epc);
|
|
const newEpcs = data.map(item => item.epc);
|
|
|
|
|
|
|
|
// 将新epc添加到临时数组中(使用Set去重)
|
|
// 将新epc添加到临时数组中(使用Set去重)
|
|
|
- const uniqueEpcs = [...new Set([...tempEpcs.value, ...newEpcs])];
|
|
|
|
|
- tempEpcs.value = uniqueEpcs;
|
|
|
|
|
|
|
+ const uniqueEpcs = new Set([...newEpcs]);
|
|
|
|
|
+
|
|
|
|
|
+ if(newEpcs != null && newEpcs.length > 0) {
|
|
|
|
|
+ newEpcs.forEach(item => {
|
|
|
|
|
+ let exist = false;
|
|
|
|
|
+ for(let i =0; i < materialList.value.length; i++) {
|
|
|
|
|
+ if(materialList.value[i].epc === item) {
|
|
|
|
|
+ exist = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if(exist == false) {
|
|
|
|
|
+ materialList.value.push({
|
|
|
|
|
+ epc: item,
|
|
|
|
|
+ queryStatus: 0, // 0: 未查询, 1: 查询中, 2: 已查询, 3: 未查询到
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
@@ -277,12 +354,15 @@ onMounted(() => {
|
|
|
timer = setInterval(() => {
|
|
timer = setInterval(() => {
|
|
|
// 只有当临时数组有数据时才执行getList
|
|
// 只有当临时数组有数据时才执行getList
|
|
|
// 将临时数组的数据赋值给epcs
|
|
// 将临时数组的数据赋值给epcs
|
|
|
- epcs.value = [...tempEpcs.value];
|
|
|
|
|
// 执行获取列表
|
|
// 执行获取列表
|
|
|
|
|
+
|
|
|
|
|
+ //addEpc([{
|
|
|
|
|
+ // 'epc': 'FFF000000000000000000001',
|
|
|
|
|
+ // 'epc': 'AAA000000000000000000002',
|
|
|
|
|
+ //}]);
|
|
|
|
|
+
|
|
|
getList();
|
|
getList();
|
|
|
- // 清空临时数组
|
|
|
|
|
- tempEpcs.value = [];
|
|
|
|
|
- }, 3000);
|
|
|
|
|
|
|
+ }, 1000);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
onUnmounted(() => {
|
|
@@ -293,6 +373,30 @@ onUnmounted(() => {
|
|
|
timer = null;
|
|
timer = null;
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+// 不识别的数据统计
|
|
|
|
|
+const unrecognizedCount = computed(() => {
|
|
|
|
|
+ return materialList.value.filter(item => item.inventoryId == null).length;
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+const handleSelect = item => {
|
|
|
|
|
+ if(item.remarks === '不在库'){
|
|
|
|
|
+ item.selected = !item.selected;
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const enterConfirmVisible = ref(false);
|
|
|
|
|
+
|
|
|
|
|
+const enterConfirm = () => {
|
|
|
|
|
+ enterConfirmVisible.value = false;
|
|
|
|
|
+
|
|
|
|
|
+ // 调用开门操作
|
|
|
|
|
+ gateController('SHOTOPEN');
|
|
|
|
|
+
|
|
|
|
|
+ // 跳转回主页
|
|
|
|
|
+ router.push('/');
|
|
|
|
|
+};
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
@@ -429,7 +533,8 @@ onUnmounted(() => {
|
|
|
left: 10px;
|
|
left: 10px;
|
|
|
width: 36px;
|
|
width: 36px;
|
|
|
height: 36px;
|
|
height: 36px;
|
|
|
- background: linear-gradient(135deg, #1e90ff 0%, #00bfff 100%);
|
|
|
|
|
|
|
+ /* background: linear-gradient(135deg, #1e90ff 0%, #00bfff 100%); */
|
|
|
|
|
+ background: linear-gradient(135deg, rgb(86, 236, 17) 0%, #28ea0a 100%);
|
|
|
border-radius: 50%;
|
|
border-radius: 50%;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|