<template>
|
<div class="app-container">
|
<div class="search_form">
|
<el-form :model="searchForm" :inline="true">
|
<el-form-item label="产品大类:">
|
<el-input
|
v-model="searchForm.productCategory"
|
placeholder="请输入产品大类"
|
clearable
|
style="width: 200px"
|
/>
|
</el-form-item>
|
<el-form-item label="规格型号:">
|
<el-input
|
v-model="searchForm.specificationModel"
|
placeholder="请输入规格型号"
|
clearable
|
style="width: 200px"
|
/>
|
</el-form-item>
|
<el-form-item label="预警级别:">
|
<el-select
|
v-model="searchForm.warningLevel"
|
placeholder="请选择预警级别"
|
clearable
|
style="width: 150px"
|
>
|
<el-option label="紧急" value="紧急" />
|
<el-option label="重要" value="重要" />
|
<el-option label="一般" value="一般" />
|
</el-select>
|
</el-form-item>
|
<el-form-item label="预警状态:">
|
<el-select
|
v-model="searchForm.warningStatus"
|
placeholder="请选择预警状态"
|
clearable
|
style="width: 150px"
|
>
|
<el-option label="已预警" value="已预警" />
|
<el-option label="正常" value="正常" />
|
</el-select>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" @click="handleQuery">搜索</el-button>
|
<el-button @click="resetQuery">重置</el-button>
|
</el-form-item>
|
</el-form>
|
</div>
|
|
<div class="table_list">
|
<div class="actions"></div>
|
<el-table
|
:data="tableData"
|
border
|
v-loading="tableLoading"
|
style="width: 100%"
|
height="calc(100vh - 280px)"
|
>
|
<el-table-column align="center" label="序号" type="index" width="60" />
|
<el-table-column label="批次号" prop="code" width="130" show-overflow-tooltip />
|
<el-table-column label="产品大类" prop="productCategory" show-overflow-tooltip />
|
<el-table-column label="规格型号" prop="specificationModel" show-overflow-tooltip />
|
<el-table-column label="当前库存" prop="currentStock" width="120" show-overflow-tooltip>
|
<template #default="scope">
|
<span :class="getStockClass(scope.row)">{{ scope.row.currentStock || 0 }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="最低库存" prop="warnNum" width="120" show-overflow-tooltip />
|
<el-table-column label="预警级别" prop="warningLevel" width="100" show-overflow-tooltip>
|
<template #default="scope">
|
<el-tag :type="getWarningLevelTag(scope.row.warningLevel)">
|
{{ scope.row.warningLevel || '-' }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="预警状态" prop="warningStatus" width="100" show-overflow-tooltip>
|
<template #default="scope">
|
<el-tag :type="scope.row.warningStatus === '已预警' ? 'danger' : 'success'">
|
{{ scope.row.warningStatus || '正常' }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="预警时间" prop="warningTime" width="150" show-overflow-tooltip />
|
<el-table-column label="预计缺货时间" prop="expectedShortageTime" width="150" show-overflow-tooltip>
|
<template #default="scope">
|
<div v-if="scope.row.expectedShortageTime">
|
<div v-if="getCountdown(scope.row.expectedShortageTime).isExpired" class="countdown-expired">
|
<el-tag type="danger">已缺货</el-tag>
|
</div>
|
<div v-else class="countdown-timer">
|
<span :class="getCountdownClass(scope.row.expectedShortageTime)">
|
{{ getCountdown(scope.row.expectedShortageTime).text }}
|
</span>
|
</div>
|
</div>
|
<span v-else>-</span>
|
</template>
|
</el-table-column>
|
</el-table>
|
<pagination
|
v-show="total > 0"
|
:total="total"
|
layout="total, sizes, prev, pager, next, jumper"
|
:page="page.current"
|
:limit="page.size"
|
@pagination="paginationChange"
|
/>
|
</div>
|
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, onMounted } from 'vue'
|
import { ElMessage } from 'element-plus'
|
import pagination from '@/components/PIMTable/Pagination.vue'
|
import {
|
getStockWarningLedgerPage
|
} from '@/api/inventoryManagement/stockWarningLedger.js'
|
|
// 响应式数据
|
const tableData = ref([])
|
const tableLoading = ref(false)
|
const total = ref(0)
|
|
// 分页参数
|
const page = reactive({
|
current: 1,
|
size: 100
|
})
|
|
// 搜索表单
|
const searchForm = reactive({
|
productCategory: '',
|
specificationModel: '',
|
warningLevel: '',
|
warningStatus: ''
|
})
|
|
// 获取列表数据
|
const getList = () => {
|
tableLoading.value = true
|
const params = {
|
...page,
|
...searchForm
|
}
|
getStockWarningLedgerPage(params)
|
.then(res => {
|
tableLoading.value = false
|
if (res.code === 200) {
|
tableData.value = res.data.records || []
|
total.value = res.data.total || 0
|
|
// 计算预警级别和状态
|
tableData.value = tableData.value.map(item => {
|
const currentStock = parseFloat(item.inboundNum0 || item.currentStock || 0)
|
const warnNum = parseFloat(item.warnNum || 0)
|
const safetyStock = parseFloat(item.safetyStock || warnNum * 1.2)
|
|
// 计算预警级别
|
if (currentStock <= 0) {
|
item.warningLevel = '紧急'
|
item.warningStatus = '已预警'
|
} else if (currentStock < warnNum) {
|
item.warningLevel = '重要'
|
item.warningStatus = '已预警'
|
} else if (currentStock < safetyStock) {
|
item.warningLevel = '一般'
|
item.warningStatus = '已预警'
|
} else {
|
item.warningLevel = ''
|
item.warningStatus = '正常'
|
}
|
|
// 计算预计缺货时间(基于日均消耗量,这里简化处理)
|
if (item.warningStatus === '已预警' && currentStock > 0 && warnNum > 0) {
|
const dailyConsumption = warnNum / 30 // 假设30天消耗完最低库存
|
const daysRemaining = Math.floor(currentStock / dailyConsumption)
|
if (daysRemaining > 0) {
|
const date = new Date()
|
date.setDate(date.getDate() + daysRemaining)
|
item.expectedShortageTime = date.toISOString().split('T')[0]
|
}
|
}
|
|
item.currentStock = currentStock
|
item.safetyStock = safetyStock
|
|
return item
|
})
|
}
|
})
|
.catch(err => {
|
tableLoading.value = false
|
ElMessage.error(err.msg || '获取数据失败')
|
})
|
}
|
|
// 搜索
|
const handleQuery = () => {
|
page.current = 1
|
getList()
|
}
|
|
// 重置搜索
|
const resetQuery = () => {
|
Object.keys(searchForm).forEach(key => {
|
searchForm[key] = ''
|
})
|
handleQuery()
|
}
|
|
// 分页变化
|
const paginationChange = (obj) => {
|
page.current = obj.page
|
page.size = obj.limit
|
getList()
|
}
|
|
// 获取库存样式类
|
const getStockClass = (row) => {
|
const currentStock = parseFloat(row.currentStock || row.inboundNum0 || 0)
|
const warnNum = parseFloat(row.warnNum || 0)
|
|
if (currentStock <= 0) {
|
return 'text-danger'
|
} else if (currentStock < warnNum) {
|
return 'text-warning'
|
}
|
return 'text-success'
|
}
|
|
// 获取预警级别标签样式
|
const getWarningLevelTag = (level) => {
|
const levelMap = {
|
'紧急': 'danger',
|
'重要': 'warning',
|
'一般': 'info'
|
}
|
return levelMap[level] || 'info'
|
}
|
|
// 获取倒计时信息
|
const getCountdown = (expectedTime) => {
|
if (!expectedTime) return { text: '-', isExpired: false }
|
|
const now = new Date().getTime()
|
const expected = new Date(expectedTime).getTime()
|
const diff = expected - now
|
|
if (diff <= 0) {
|
return { text: '已缺货', isExpired: true }
|
}
|
|
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
|
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
|
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60))
|
|
if (days > 0) {
|
return { text: `${days}天${hours}小时`, isExpired: false }
|
} else if (hours > 0) {
|
return { text: `${hours}小时${minutes}分钟`, isExpired: false }
|
} else {
|
return { text: `${minutes}分钟`, isExpired: false }
|
}
|
}
|
|
// 获取倒计时样式类
|
const getCountdownClass = (expectedTime) => {
|
if (!expectedTime) return ''
|
|
const now = new Date().getTime()
|
const expected = new Date(expectedTime).getTime()
|
const diff = expected - now
|
|
if (diff <= 0) {
|
return 'countdown-expired'
|
} else if (diff <= 24 * 60 * 60 * 1000) { // 24小时内
|
return 'countdown-urgent'
|
} else if (diff <= 7 * 24 * 60 * 60 * 1000) { // 7天内
|
return 'countdown-warning'
|
} else {
|
return 'countdown-normal'
|
}
|
}
|
|
// 页面加载
|
onMounted(() => {
|
getList()
|
})
|
</script>
|
|
<style scoped lang="scss">
|
.app-container {
|
padding: 20px;
|
|
.search_form {
|
background: #fff;
|
padding: 20px;
|
border-radius: 4px;
|
margin-bottom: 20px;
|
}
|
|
.table_list {
|
background: #fff;
|
border-radius: 4px;
|
padding: 20px;
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
.actions {
|
display: flex;
|
justify-content: space-between;
|
margin-bottom: 20px;
|
}
|
}
|
|
.text-danger {
|
color: #f56c6c;
|
font-weight: bold;
|
}
|
|
.text-warning {
|
color: #e6a23c;
|
font-weight: bold;
|
}
|
|
.text-success {
|
color: #67c23a;
|
font-weight: bold;
|
}
|
|
.countdown-timer {
|
font-weight: bold;
|
}
|
|
.countdown-normal {
|
color: #67c23a;
|
}
|
|
.countdown-warning {
|
color: #e6a23c;
|
}
|
|
.countdown-urgent {
|
color: #f56c6c;
|
animation: blink 1s infinite;
|
}
|
|
.countdown-expired {
|
color: #f56c6c;
|
font-weight: bold;
|
}
|
|
@keyframes blink {
|
0%, 50% { opacity: 1; }
|
51%, 100% { opacity: 0.5; }
|
}
|
}
|
</style>
|