You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

453 lines
12 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="inventory-table-container">
<div class="table-header">
<h3>实时库存表</h3>
<div class="table-actions">
<el-switch
v-model="showDifference"
active-text="显示库存变化"
inactive-text="显示实际库存"
style="margin-right: 15px;"
@change="handleToggleChange"
/>
<el-button @click="refreshData" type="primary" size="small">刷新数据</el-button>
</div>
</div>
<!-- 调试信息 -->
<div v-if="!loading" style="margin-bottom: 10px; font-size: 12px; color: #666;">
调试信息: 获取到 {{ records.length }} 条记录,表格共 {{ tableData.length }} 行
</div>
<div v-loading="loading" class="table-wrapper">
<el-table
:data="tableData"
style="width: 100%; min-width: max-content;"
border
:header-cell-style="{ backgroundColor: '#f5f7fa', color: '#606266' }"
empty-text="暂无数据"
:scrollbar-always-on="true"
@cell-dblclick="handleCellDblClick"
>
<!-- 规格列 -->
<el-table-column
prop="specification"
label="规格"
width="80"
fixed="left"
/>
<!-- 动态列model-wire_disc 组合 -->
<el-table-column
v-for="column in dynamicColumns"
:key="column.key"
:label="column.label"
:prop="column.prop"
min-width="120"
>
<template #default="scope">
<div class="cell-content">
<span v-if="!showDifference">
{{ scope.row[column.prop] || '-' }}
</span>
<span v-else>
{{ scope.row[column.prop] || '-' }}
<span
v-if="scope.row[column.prop + '_difference'] !== undefined && scope.row[column.prop + '_difference'] !== null"
:class="getDifferenceClass(scope.row[column.prop + '_difference'])"
style="margin-left: 5px; font-size: 12px;"
>
({{ formatDifference(scope.row[column.prop + '_difference']) }})
</span>
<span v-else class="no-data" style="margin-left: 5px; font-size: 12px;">
(-)
</span>
</span>
</div>
</template>
<template #header>
<div v-html="column.label"></div> <!-- 使用 v-html 渲染 HTML 标签,确保 <br> 生效 -->
</template>
</el-table-column>
<!-- 总计列 -->
<el-table-column
label="总计"
width="100"
fixed="right"
>
<template #default="scope">
{{ scope.row.total }}
</template>
</el-table-column>
</el-table>
</div>
<!-- WmsTable 详情对话框 -->
<el-dialog
v-model="wmsDialogVisible"
title="库存详情"
width="80%"
destroy-on-close
>
<WmsTable
v-if="wmsDialogVisible"
ref="wmsTableRef"
:model="currentWmsModel"
:specification="currentWmsSpecification"
:loading-height="200"
:show-action="true"
/>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue'
import { API_CONFIG } from "@/config/api"
import { ElMessage } from 'element-plus'
import WmsTable from '@/components/WmsTable/index.vue'
// 响应式数据
const loading = ref(false)
const records = ref([])
const differenceRecords = ref([])
// WMS对话框相关状态
const wmsDialogVisible = ref(false)
const currentWmsModel = ref('')
const currentWmsSpecification = ref('')
const wmsTableRef = ref(null)
// 显示控制
const showDifference = ref(true)
// 计算属性:动态列
const dynamicColumns = computed(() => {
if (!records.value.length) return [];
// 使用 '::' 作为分隔符
const combinations = [...new Set(records.value.map(item => {
if (item.wire_disc && item.wire_disc.trim() !== '') {
return `${item.model}::${item.wire_disc}`;
} else {
return item.model;
}
}))].sort(); // 可选:若需自定义排序,调整这里
return combinations.map(comb => {
// 将组合拆分为 model 和 wire_disc
const hasWireDisc = comb.includes('::');
const [model, wire_disc] = hasWireDisc ? comb.split('::') : [comb, ''];
return {
key: comb,
prop: comb,
label: `${model}<br>${wire_disc}` // 使用HTML换行符实现两行显示
};
});
});
// 计算属性:表格数据
const tableData = computed(() => {
console.log('计算表格数据, records.value:', records.value);
if (!records.value.length) {
console.log('records为空返回空数组');
return [];
}
// 获取所有唯一的规格并从小到大排序(可选数值排序)
const specifications = [...new Set(records.value.map(item => item.specification))]
.sort((a, b) => parseFloat(a) - parseFloat(b)); // 改为数值排序,避免 "0.800" 在前
console.log('获取到的规格列表:', specifications);
// 获取所有唯一的 model::wire_disc 组合
const combinations = [...new Set(records.value.map(item => {
if (item.wire_disc && item.wire_disc.trim() !== '') {
return `${item.model}::${item.wire_disc}`;
} else {
return item.model;
}
}))];
console.log('获取到的model::wire_disc组合:', combinations);
// 构建表格数据
const tableDataResult = specifications.map(specification => {
const row = { specification };
let total = 0;
combinations.forEach(comb => {
// 判断是否包含 wire_disc检查 '::'
const hasWireDisc = comb.includes('::');
let model, wire_disc;
if (hasWireDisc) {
[model, wire_disc] = comb.split('::'); // 使用 '::' 分割
} else {
model = comb;
wire_disc = null;
}
// 查找对应的记录
const record = records.value.find(r => {
const specMatch = r.specification === specification;
const modelMatch = r.model === model;
const wireDiscMatch = hasWireDisc
? (r.wire_disc === wire_disc)
: (!r.wire_disc || r.wire_disc.trim() === '');
return specMatch && modelMatch && wireDiscMatch;
});
// 设置实际库存数量
row[comb] = record ? record.total_number : null;
if (record) total += record.total_number;
// 如果启用了差值显示,查找差值记录
if (showDifference.value && differenceRecords.value.length > 0) {
const differenceRecord = differenceRecords.value.find(r => {
const specMatch = r.specification === specification;
const modelMatch = r.model === model;
const wireDiscMatch = hasWireDisc
? (r.wire_disc === wire_disc)
: (!r.wire_disc || r.wire_disc.trim() === '');
return specMatch && modelMatch && wireDiscMatch;
});
// 设置差值
row[comb + '_difference'] = differenceRecord ? differenceRecord.difference : null;
}
});
row.total = total;
return row;
});
console.log('生成的表格数据:', tableDataResult);
return tableDataResult;
});
// 方法:获取数据
const fetchData = async () => {
loading.value = true;
try {
console.log('正在请求API:', `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.WMS_TYPE_1_RECORDS}`);
const response = await fetch(`${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.WMS_TYPE_1_RECORDS}`);
const result = await response.json();
console.log('API返回结果:', result);
if (result.code === 200 && result.data && result.data.records) {
records.value = result.data.records;
console.log('设置records值:', records.value);
} else {
console.error('获取实时库存数据失败:', result.message);
}
} catch (error) {
console.error('API调用失败:', error);
} finally {
loading.value = false;
}
};
// 方法:获取差值数据
const fetchDifferenceData = async () => {
const today = new Date().toISOString().split('T')[0]; // 获取今天的日期 YYYY-MM-DD
const targetDate = today;
try {
console.log('正在请求差值API:', `/api/wms/difference/type1-records?target_date=${targetDate}`);
const response = await fetch(`${API_CONFIG.BASE_URL}/api/wms/difference/type1-records?target_date=${targetDate}`);
const result = await response.json();
console.log('差值API返回结果:', result);
if (result.code === 200 && result.data && result.data.records) {
differenceRecords.value = result.data.records;
console.log('设置差值records:', differenceRecords.value);
} else {
console.error('获取差值数据失败:', result.message);
differenceRecords.value = [];
}
} catch (error) {
console.error('差值API调用失败:', error);
differenceRecords.value = [];
}
};
// 方法:刷新数据
const refreshData = () => {
fetchData();
if (showDifference.value) {
fetchDifferenceData();
}
};
// 方法处理toggle变化
const handleToggleChange = (value) => {
if (value) {
// 启用差值显示时获取差值数据
fetchDifferenceData();
} else {
// 禁用差值显示时清空差值数据
differenceRecords.value = [];
}
};
// 方法:格式化差值显示
const formatDifference = (difference) => {
if (difference === 0) return '0';
if (difference > 0) return `+${difference}`;
return `${difference}`;
};
// 方法:获取差值样式类
const getDifferenceClass = (difference) => {
if (difference > 0) return 'difference-positive'; // 绿色,增加
if (difference < 0) return 'difference-negative'; // 红色,减少
return 'difference-zero'; // 黑色,无变化
};
// 方法:处理单元格双击事件
const handleCellDblClick = (row, column, cell, event) => {
const cellValue = row[column.property];
// 检查单元格是否有值
if (!cellValue || cellValue === '-' || cellValue === null || cellValue === undefined) {
ElMessage.warning('该单元格暂无数据');
return;
}
// 检查是否是规格列或总计列这些列不需要显示WMS详情
if (column.property === 'specification' || column.property === 'total') {
ElMessage.info('该列不支持查看详情');
return;
}
// 从列属性中提取model和wire_disc
const columnKey = column.property;
const hasWireDisc = columnKey.includes('::');
let model, wire_disc;
if (hasWireDisc) {
[model, wire_disc] = columnKey.split('::');
} else {
model = columnKey;
wire_disc = null;
}
// 设置当前WMS数据的参数
currentWmsModel.value = model;
currentWmsSpecification.value = row.specification;
// 显示WMS详情对话框
wmsDialogVisible.value = true;
};
// 组件挂载时获取数据
onMounted(() => {
fetchData();
// 如果默认显示库存变化,则同时获取差值数据
if (showDifference.value) {
fetchDifferenceData();
}
});
</script>
<style scoped>
.inventory-table-container {
padding: 20px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.table-header h3 {
margin: 0;
font-size: 18px;
color: #303133;
}
.table-actions {
display: flex;
gap: 10px;
}
.table-wrapper {
width: 100%;
overflow-x: auto;
/* 确保滚动条始终可见 */
scrollbar-width: thin; /* Firefox */
}
:deep(.el-table) {
font-size: 14px;
/* 确保表格最小宽度正确计算 */
width: max-content !important;
min-width: 100% !important;
}
:deep(.el-table th) {
background-color: #f5f7fa !important;
color: #606266;
font-weight: 600;
white-space: nowrap;
text-align: center; /* 可选:居中对齐列头 */
padding: 8px 0; /* 调整列头内间距,以适应两行显示 */
}
:deep(.el-table td) {
padding: 8px 0;
white-space: nowrap;
}
/* 库存变化样式 */
.cell-content {
text-align: center;
}
.difference-positive {
color: #67c23a;
font-weight: bold;
}
.difference-negative {
color: #f56c6c;
font-weight: bold;
}
.difference-zero {
color: #606266;
font-weight: normal;
}
.no-data {
color: #c0c4cc;
}
/* Webkit 浏览器的滚动条样式 */
.table-wrapper::-webkit-scrollbar {
height: 10px;
}
.table-wrapper::-webkit-scrollbar-track {
background: #f1f1f1;
}
.table-wrapper::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 5px;
}
.table-wrapper::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
</style>