feat:甘特图菜单页-添加排产看板
parent
5653fa98c0
commit
1d8a70de9b
@ -0,0 +1,418 @@
|
|||||||
|
<template>
|
||||||
|
<div class="schedule-card-view" :class="{ 'fullscreen-mode': isFullscreen }">
|
||||||
|
<!-- 工具栏 -->
|
||||||
|
<div class="schedule-toolbar">
|
||||||
|
<div class="schedule-legend">
|
||||||
|
<div class="legend-item">
|
||||||
|
<div class="legend-color" style="background: #5dade2"></div>
|
||||||
|
<span>已排产</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend-item">
|
||||||
|
<div class="legend-color" style="background: #52c41a"></div>
|
||||||
|
<span>已并工</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend-item">
|
||||||
|
<div class="legend-color" style="background: #faad14"></div>
|
||||||
|
<span>暂停</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend-item">
|
||||||
|
<div class="legend-color" style="background: #ff7875"></div>
|
||||||
|
<span>待入库</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend-item">
|
||||||
|
<div class="legend-color" style="background: #b37feb"></div>
|
||||||
|
<span>已入库</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar-actions">
|
||||||
|
<el-button
|
||||||
|
circle
|
||||||
|
@click="toggleFullscreen"
|
||||||
|
>
|
||||||
|
<el-icon><FullScreen /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 设备卡片列表 -->
|
||||||
|
<div class="device-cards-container">
|
||||||
|
<div v-for="device in scheduleList" :key="device.deviceId" class="device-card">
|
||||||
|
<!-- 设备信息头 -->
|
||||||
|
<div class="device-header">
|
||||||
|
<div class="device-info">
|
||||||
|
<div class="device-name">{{ device.deviceName }}</div>
|
||||||
|
<div class="device-code">{{ device.deviceCode }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="device-stats">
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-label">计划数</span>
|
||||||
|
<span class="stat-value">{{ device.plans?.length || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="stat-label">产能</span>
|
||||||
|
<span class="stat-value">{{ device.ratedCapacity || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 计划卡片列表 -->
|
||||||
|
<div class="plans-container">
|
||||||
|
<div
|
||||||
|
v-for="plan in device.plans"
|
||||||
|
:key="plan.planId"
|
||||||
|
class="plan-card"
|
||||||
|
:style="{ borderLeftColor: getPlanStatusColor(plan.planStatus) }"
|
||||||
|
>
|
||||||
|
<div class="plan-header">
|
||||||
|
<span class="plan-status-badge" :style="{ backgroundColor: getPlanStatusColor(plan.planStatus) }">
|
||||||
|
{{ getPlanStatusLabel(plan.planStatus) }}
|
||||||
|
</span>
|
||||||
|
<span class="plan-code">{{ plan.planCode }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="plan-content">
|
||||||
|
<div class="plan-row">
|
||||||
|
<span class="plan-label">计划编号:</span>
|
||||||
|
<span class="plan-value">{{ plan.taskCode }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="plan-row">
|
||||||
|
<span class="plan-label">产品:</span>
|
||||||
|
<span class="plan-value">{{ plan.productCode }} / {{ plan.productName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="plan-row">
|
||||||
|
<span class="plan-label">计划数量:</span>
|
||||||
|
<span class="plan-value">{{ plan.planNumber }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="plan-row">
|
||||||
|
<span class="plan-label">交货日期:</span>
|
||||||
|
<span class="plan-value">{{ plan.deliveryDateStr || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="plan-row">
|
||||||
|
<span class="plan-label">开始:</span>
|
||||||
|
<span class="plan-value">{{ plan.planStartTimeStr }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="plan-row">
|
||||||
|
<span class="plan-label">结束:</span>
|
||||||
|
<span class="plan-value">{{ plan.planEndTimeStr }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<div v-if="!device.plans || device.plans.length === 0" class="empty-state">
|
||||||
|
<el-empty description="暂无计划" :image-size="40" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { FullScreen } from '@element-plus/icons-vue'
|
||||||
|
defineOptions({ name: 'ScheduleCardView' })
|
||||||
|
|
||||||
|
type UnifiedPlan = {
|
||||||
|
planId: number
|
||||||
|
planCode: string
|
||||||
|
taskCode: string
|
||||||
|
productCode: string
|
||||||
|
productName: string
|
||||||
|
planNumber: number
|
||||||
|
planStartTimeStr: string
|
||||||
|
planEndTimeStr: string
|
||||||
|
deliveryDateStr?: string
|
||||||
|
planStatus: number
|
||||||
|
sourceType: 'HISTORY'
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnifiedDevice = {
|
||||||
|
deviceId: string | number
|
||||||
|
deviceName: string
|
||||||
|
deviceCode: string
|
||||||
|
ratedCapacity?: number | string
|
||||||
|
plans: UnifiedPlan[]
|
||||||
|
}
|
||||||
|
|
||||||
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
scheduleList: UnifiedDevice[]
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
scheduleList: () => []
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const isFullscreen = ref(false)
|
||||||
|
|
||||||
|
const toggleFullscreen = () => {
|
||||||
|
isFullscreen.value = !isFullscreen.value
|
||||||
|
if (isFullscreen.value) {
|
||||||
|
document.body.style.overflow = 'hidden'
|
||||||
|
} else {
|
||||||
|
document.body.style.overflow = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (isFullscreen.value) {
|
||||||
|
document.body.style.overflow = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计划状态映射
|
||||||
|
const planStatusMap: Record<number, { label: string; color: string }> = {
|
||||||
|
1: { label: '已排产', color: '#5dade2' },
|
||||||
|
3: { label: '已并工', color: '#52c41a' },
|
||||||
|
5: { label: '暂停', color: '#faad14' },
|
||||||
|
8: { label: '待入库', color: '#ff7875' },
|
||||||
|
9: { label: '已入库', color: '#b37feb' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPlanStatusLabel = (status: number) => {
|
||||||
|
return planStatusMap[status]?.label || '未知'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPlanStatusColor = (status: number) => {
|
||||||
|
return planStatusMap[status]?.color || '#999999'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.schedule-card-view {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule-card-view.fullscreen-mode {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 9999;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule-toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-bottom: 1px solid #e4e7eb;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule-legend {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-color {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-cards-container {
|
||||||
|
flex: 1;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: flex-start;
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 隐藏滚动条但保留滚动功能 */
|
||||||
|
.device-cards-container::-webkit-scrollbar {
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-cards-container::-webkit-scrollbar-track {
|
||||||
|
background: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-cards-container::-webkit-scrollbar-thumb {
|
||||||
|
background: #d9d9d9;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-cards-container::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #bfbfbf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e4e7eb;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 340px;
|
||||||
|
max-height: 100%;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-header {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid #e4e7eb;
|
||||||
|
background: #fafbfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-info {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-name {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-code {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.device-stats {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plans-container {
|
||||||
|
padding: 12px;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
max-height: calc(100vh - 200px);
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plans-container:hover {
|
||||||
|
scrollbar-color: rgba(144, 147, 153, 0.3) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plans-container::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plans-container::-webkit-scrollbar-thumb {
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plans-container:hover::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(144, 147, 153, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.plans-container::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e4e7eb;
|
||||||
|
border-left: 3px solid;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-card:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-status-badge {
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 2px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-code {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-label {
|
||||||
|
color: #999;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-value {
|
||||||
|
color: #1f2937;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue