<template>
|
<div class="intelligent-scheduling">
|
<!-- 搜索表单 -->
|
<el-card class="search-form-card" shadow="never">
|
<el-form :inline="true" :model="searchParams" class="search-form">
|
<el-form-item label="时间范围">
|
<el-date-picker
|
v-model="searchParams.dateRange"
|
type="daterange"
|
range-separator="至"
|
start-placeholder="开始日期"
|
end-placeholder="结束日期"
|
:shortcuts="dateShortcuts"
|
/>
|
</el-form-item>
|
<el-form-item label="煤种">
|
<el-input v-model="searchParams.coalType" placeholder="请输入煤种" clearable />
|
</el-form-item>
|
<el-form-item label="排产状态">
|
<el-select v-model="searchParams.status" placeholder="请选择排产状态" clearable style="width: 200px">
|
<el-option label="全部" value="" />
|
<el-option label="待排产" value="pending" />
|
<el-option label="排产中" value="processing" />
|
<el-option label="已完成" value="completed" />
|
</el-select>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" @click="handleSearch" :loading="loading">查询</el-button>
|
<el-button @click="handleReset">重置</el-button>
|
<el-button type="success" @click="handleRandomScheduling">随机排产</el-button>
|
</el-form-item>
|
</el-form>
|
</el-card>
|
|
<!-- 统计卡片 -->
|
<div class="stats-cards">
|
<div class="stat-card">
|
<div class="card-icon">
|
<i class="el-icon-s-order" />
|
</div>
|
<div class="card-content">
|
<div class="card-title">总排产量</div>
|
<div class="card-value">{{ totalScheduledQuantity.toFixed(2) }} 吨</div>
|
</div>
|
</div>
|
|
<div class="stat-card">
|
<div class="card-icon">
|
<i class="el-icon-check" />
|
</div>
|
<div class="card-content">
|
<div class="card-title">已完成排产</div>
|
<div class="card-value">{{ completedScheduledQuantity.toFixed(2) }} 吨</div>
|
</div>
|
</div>
|
|
<div class="stat-card">
|
<div class="card-icon">
|
<i class="el-icon-time" />
|
</div>
|
<div class="card-content">
|
<div class="card-title">待排产</div>
|
<div class="card-value">{{ pendingScheduledQuantity.toFixed(2) }} 吨</div>
|
</div>
|
</div>
|
|
<div class="stat-card">
|
<div class="card-icon">
|
<i class="el-icon-warning" />
|
</div>
|
<div class="card-content">
|
<div class="card-title">库存预警</div>
|
<div class="card-value">{{ stockWarningCount }} 项</div>
|
</div>
|
</div>
|
</div>
|
|
<!-- 库存原料列表 -->
|
<el-card class="stock-materials-card" shadow="never">
|
<template #header>
|
<div class="card-header">
|
<span>库存原料</span>
|
</div>
|
</template>
|
<el-table v-loading="loading" :data="stockMaterials" style="width: 100%">
|
<el-table-column prop="id" label="序号" width="80" type="index" />
|
<el-table-column prop="coalType" label="煤种" width="120" />
|
<el-table-column prop="origin" label="产地" width="120" />
|
<el-table-column prop="calorificValue" label="热值" width="120" />
|
<el-table-column prop="currentStock" label="当前库存(吨)" width="150" align="right">
|
<template #default="scope">
|
<span :class="{ 'stock-warning': scope.row.currentStock < scope.row.minStock }">
|
{{ scope.row.currentStock.toFixed(2) }}
|
</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="minStock" label="最低库存(吨)" width="150" align="right">
|
<template #default="scope">{{ scope.row.minStock.toFixed(2) }}</template>
|
</el-table-column>
|
<el-table-column prop="unit" label="单位" width="80" />
|
<el-table-column prop="updateTime" label="更新时间" width="180" />
|
</el-table>
|
</el-card>
|
|
<!-- 排产结果列表 -->
|
<el-card class="scheduling-results-card" shadow="never">
|
<template #header>
|
<div class="card-header">
|
<span>排产结果</span>
|
</div>
|
</template>
|
<el-table v-loading="loading" :data="schedulingResults" style="width: 100%">
|
<el-table-column prop="code" label="排产编码" width="180" />
|
<el-table-column prop="productionLine" label="生产线" width="120" />
|
<el-table-column prop="coalType" label="煤种" width="120" />
|
<el-table-column prop="quantity" label="排产数量(吨)" width="150" align="right">
|
<template #default="scope">{{ scope.row.quantity.toFixed(2) }}</template>
|
</el-table-column>
|
<el-table-column prop="scheduleTime" label="排产时间" width="180" />
|
<el-table-column prop="status" label="状态" width="100">
|
<template #default="scope">
|
<el-tag
|
:type="scope.row.status === 'completed' ? 'success' : scope.row.status === 'processing' ? 'warning' : 'info'"
|
size="small"
|
>
|
{{ scope.row.status === 'completed' ? '已完成' : scope.row.status === 'processing' ? '排产中' : '待排产' }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="remark" label="备注" width="200" />
|
</el-table>
|
</el-card>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, onMounted } from 'vue'
|
import { ElMessage } from 'element-plus'
|
|
// 搜索参数
|
const searchParams = ref({
|
dateRange: [],
|
coalType: '',
|
status: ''
|
})
|
|
// 日期快捷选项
|
const dateShortcuts = [
|
{
|
text: '今天',
|
value: () => {
|
const end = new Date()
|
const start = new Date()
|
return [start, end]
|
}
|
},
|
{
|
text: '昨天',
|
value: () => {
|
const end = new Date()
|
const start = new Date()
|
start.setTime(start.getTime() - 3600 * 1000 * 24)
|
return [start, end]
|
}
|
},
|
{
|
text: '近7天',
|
value: () => {
|
const end = new Date()
|
const start = new Date()
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
|
return [start, end]
|
}
|
},
|
{
|
text: '近30天',
|
value: () => {
|
const end = new Date()
|
const start = new Date()
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
|
return [start, end]
|
}
|
},
|
{
|
text: '本月',
|
value: () => {
|
const end = new Date()
|
const start = new Date(end.getFullYear(), end.getMonth(), 1)
|
return [start, end]
|
}
|
}
|
]
|
|
// 统计数据
|
const totalScheduledQuantity = ref(0)
|
const completedScheduledQuantity = ref(0)
|
const pendingScheduledQuantity = ref(0)
|
const stockWarningCount = ref(0)
|
|
// 库存原料数据
|
const stockMaterials = ref([
|
{ id: 1, coalType: '烟煤', origin: '山西', calorificValue: '5300大卡', currentStock: 1200.5, minStock: 500, unit: '吨', updateTime: '2023-05-07 09:30:15' },
|
{ id: 2, coalType: '无烟煤', origin: '内蒙古', calorificValue: '5800大卡', currentStock: 800.2, minStock: 400, unit: '吨', updateTime: '2023-05-07 10:15:30' },
|
{ id: 3, coalType: '褐煤', origin: '新疆', calorificValue: '4200大卡', currentStock: 350.8, minStock: 400, unit: '吨', updateTime: '2023-05-07 11:05:45' },
|
{ id: 4, coalType: '贫煤', origin: '陕西', calorificValue: '5100大卡', currentStock: 900.0, minStock: 300, unit: '吨', updateTime: '2023-05-07 13:20:00' },
|
{ id: 5, coalType: '瘦煤', origin: '贵州', calorificValue: '5400大卡', currentStock: 650.5, minStock: 350, unit: '吨', updateTime: '2023-05-07 14:45:15' }
|
])
|
|
// 排产结果数据
|
const schedulingResults = ref([
|
{ code: 'PS20230507001', productionLine: '生产线1', coalType: '烟煤', quantity: 200.5, scheduleTime: '2023-05-07 09:30:15', status: 'completed', remark: '按计划完成' },
|
{ code: 'PS20230507002', productionLine: '生产线2', coalType: '无烟煤', quantity: 150.2, scheduleTime: '2023-05-07 10:15:30', status: 'processing', remark: '正在进行中' },
|
{ code: 'PS20230507003', productionLine: '生产线3', coalType: '贫煤', quantity: 180.0, scheduleTime: '2023-05-07 11:05:45', status: 'pending', remark: '等待排产' }
|
])
|
|
// 加载状态
|
const loading = ref(false)
|
|
// 计算统计数据
|
const calculateStats = () => {
|
// 计算总排产量
|
totalScheduledQuantity.value = schedulingResults.value.reduce((sum, item) => sum + item.quantity, 0)
|
|
// 计算已完成排产
|
completedScheduledQuantity.value = schedulingResults.value
|
.filter(item => item.status === 'completed')
|
.reduce((sum, item) => sum + item.quantity, 0)
|
|
// 计算待排产
|
pendingScheduledQuantity.value = schedulingResults.value
|
.filter(item => item.status === 'pending')
|
.reduce((sum, item) => sum + item.quantity, 0)
|
|
// 计算库存预警数量
|
stockWarningCount.value = stockMaterials.value.filter(item => item.currentStock < item.minStock).length
|
}
|
|
// 生成排产编码
|
const generateSchedulingCode = () => {
|
const date = new Date()
|
const year = date.getFullYear()
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
const day = String(date.getDate()).padStart(2, '0')
|
const random = Math.floor(Math.random() * 1000).toString().padStart(3, '0')
|
return `PS${year}${month}${day}${random}`
|
}
|
|
// 随机排产
|
const handleRandomScheduling = async () => {
|
try {
|
loading.value = true
|
|
// 模拟API请求延迟
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
// 获取有库存的原料
|
const availableMaterials = stockMaterials.value.filter(item => item.currentStock > 0)
|
|
if (availableMaterials.length === 0) {
|
ElMessage.warning('没有可用的库存原料')
|
return
|
}
|
|
// 随机选择原料
|
const randomMaterial = availableMaterials[Math.floor(Math.random() * availableMaterials.length)]
|
|
// 随机生成排产数量(不超过库存的50%)
|
const maxQuantity = randomMaterial.currentStock * 0.5
|
const quantity = Math.max(50, Math.random() * maxQuantity) // 至少排产50吨
|
|
// 随机选择生产线
|
const productionLines = ['生产线1', '生产线2', '生产线3']
|
const randomLine = productionLines[Math.floor(Math.random() * productionLines.length)]
|
|
// 生成新的排产计划
|
const newScheduling = {
|
code: generateSchedulingCode(),
|
productionLine: randomLine,
|
coalType: randomMaterial.coalType,
|
quantity: quantity,
|
scheduleTime: new Date().toLocaleString('zh-CN'),
|
status: 'pending',
|
remark: '随机排产生成'
|
}
|
|
// 添加到排产结果列表
|
schedulingResults.value.unshift(newScheduling)
|
|
// 更新库存数量
|
randomMaterial.currentStock -= quantity
|
|
// 更新统计数据
|
calculateStats()
|
|
ElMessage.success('随机排产成功')
|
} catch (error) {
|
console.error('随机排产失败:', error)
|
ElMessage.error('随机排产失败,请稍后重试')
|
} finally {
|
loading.value = false
|
}
|
}
|
|
// 查询数据
|
const handleSearch = async () => {
|
try {
|
loading.value = true
|
|
// 模拟API请求延迟
|
await new Promise(resolve => setTimeout(resolve, 800))
|
|
// 这里可以根据搜索条件过滤数据
|
// 实际应用中应该调用API获取数据
|
|
ElMessage.success('查询成功')
|
} catch (error) {
|
console.error('查询失败:', error)
|
ElMessage.error('查询失败,请稍后重试')
|
} finally {
|
loading.value = false
|
}
|
}
|
|
// 重置搜索参数
|
const handleReset = () => {
|
searchParams.value = {
|
dateRange: [],
|
coalType: '',
|
status: ''
|
}
|
|
// 重置后重新查询
|
handleSearch()
|
}
|
|
// 初始化页面
|
onMounted(() => {
|
// 默认查询近7天数据
|
const end = new Date()
|
const start = new Date()
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
|
searchParams.value.dateRange = [start, end]
|
|
// 计算统计数据
|
calculateStats()
|
|
// 初始加载数据
|
handleSearch()
|
})
|
</script>
|
|
<style scoped>
|
.intelligent-scheduling {
|
padding: 20px;
|
}
|
|
.page-header {
|
margin-bottom: 20px;
|
}
|
|
.page-header h2 {
|
font-size: 18px;
|
font-weight: 600;
|
color: #303133;
|
}
|
|
.search-form-card {
|
margin-bottom: 20px;
|
}
|
|
.search-form {
|
display: flex;
|
align-items: center;
|
flex-wrap: wrap;
|
}
|
|
.stats-cards {
|
display: grid;
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
gap: 16px;
|
margin-bottom: 24px;
|
}
|
|
.stat-card {
|
display: flex;
|
padding: 20px;
|
background: #fff;
|
border-radius: 8px;
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
transition: transform 0.3s, box-shadow 0.3s;
|
}
|
|
.stat-card:hover {
|
transform: translateY(-2px);
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
}
|
|
.card-icon {
|
width: 48px;
|
height: 48px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
border-radius: 50%;
|
font-size: 24px;
|
margin-right: 16px;
|
}
|
|
.stat-card:nth-child(1) .card-icon {
|
background: #ECF5FF;
|
color: #409EFF;
|
}
|
|
.stat-card:nth-child(2) .card-icon {
|
background: #F6FFED;
|
color: #52C41A;
|
}
|
|
.stat-card:nth-child(3) .card-icon {
|
background: #FFF7E6;
|
color: #FAAD14;
|
}
|
|
.stat-card:nth-child(4) .card-icon {
|
background: #FFF1F0;
|
color: #F5222D;
|
}
|
|
.card-content {
|
flex: 1;
|
}
|
|
.card-title {
|
font-size: 14px;
|
color: #606266;
|
margin-bottom: 8px;
|
}
|
|
.card-value {
|
font-size: 24px;
|
font-weight: 600;
|
color: #303133;
|
}
|
|
.stock-materials-card,
|
.scheduling-results-card {
|
margin-bottom: 24px;
|
}
|
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 16px;
|
border-bottom: 1px solid #EBEEF5;
|
}
|
|
.card-header span {
|
font-weight: 600;
|
color: #303133;
|
}
|
|
.stock-warning {
|
color: #F5222D;
|
font-weight: 500;
|
}
|
|
/* 响应式布局 */
|
@media (max-width: 768px) {
|
.intelligent-scheduling {
|
padding: 10px;
|
}
|
|
.stats-cards {
|
grid-template-columns: 1fr;
|
}
|
|
.search-form {
|
flex-direction: column;
|
align-items: stretch;
|
}
|
|
.search-form .el-form-item {
|
margin-right: 0;
|
margin-bottom: 10px;
|
}
|
}
|
</style>
|