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.

1835 lines
56 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="equipment-status-container">
<!-- 完成度筛选器 -->
<div class="filter-container">
<span class="filter-label">完成度筛选:</span>
<el-select v-model="selectedCompletionLevel" @change="onCompletionLevelChange" placeholder="请选择完成度" style="width: 150px">
<el-option label="全部显示" value="all" />
<el-option label="0% - 25%" value="low" />
<el-option label="25% - 75%" value="medium" />
<el-option label="75% - 100%" value="high" />
<el-option label="100%" value="full" />
</el-select>
</div>
<div class="equipment-grid">
<div v-for="equipment in sortedEquipmentData" :key="equipment.equipment_code" class="equipment-item">
<div class="equipment-header">
<span class="equipment-name">{{ equipment.equipment_code }}</span>
<span class="equipment-health" v-if="equipment.status_records && equipment.status_records.length > 0">
健康度: {{ equipment.status_records[0].health_score }}%
</span>
</div>
<div class="equipment-body">
<div class="axle-container">
<div class="axle-side left">
<div v-for="record in getLeftAxles(equipment)" :key="`${record.axle_number}-${record.specification || 'no-spec'}-${record.model || 'no-model'}-${record.update_time || 'no-data'}`"
v-show="shouldShowAxle(record.degree_of_completion)"
class="axle-item"
:class="[getCompletionClass(record.degree_of_completion), { 'not-running': !record.update_time }]"
@dblclick="showAxleDetails(record, equipment.equipment_code)">
<div class="axle-number">{{ record.axle_number }} <span v-if="!record.update_time" class="not-running-label">(不在运行)</span></div>
<div class="axle-spec" v-if="record.update_time">规格: {{ record.specification }}</div>
<div class="axle-model" v-if="record.update_time">型号: {{ record.model }}</div>
<div class="axle-disc" v-if="record.update_time">线盘: {{ record.raw_wire_disc || '无' }}</div>
<div class="axle-progress" v-if="record.update_time">完成度: {{ (record.degree_of_completion * 100).toFixed(0) }}%</div>
<div class="axle-stock" v-if="record.update_time && (record.total_number || record.total_net_weight || record.total_gross_weight)">
<div class="stock-item" v-if="record.total_number">总箱数: {{ record.total_number }}</div>
<div class="stock-item" v-if="record.total_net_weight">总净重: {{ record.total_net_weight }}kg</div>
<div class="stock-item" v-if="record.total_gross_weight">总毛重: {{ record.total_gross_weight }}kg</div>
</div>
</div>
</div>
<div class="axle-side right">
<div v-for="record in getRightAxles(equipment)" :key="`${record.axle_number}-${record.specification || 'no-spec'}-${record.model || 'no-model'}-${record.update_time || 'no-data'}`"
v-show="shouldShowAxle(record.degree_of_completion)"
class="axle-item"
:class="[getCompletionClass(record.degree_of_completion), { 'not-running': !record.update_time }]"
@dblclick="showAxleDetails(record, equipment.equipment_code)">
<div class="axle-number">{{ record.axle_number }} <span v-if="!record.update_time" class="not-running-label">(不在运行)</span></div>
<div class="axle-spec" v-if="record.update_time">规格: {{ record.specification }}</div>
<div class="axle-model" v-if="record.update_time">型号: {{ record.model }}</div>
<div class="axle-disc" v-if="record.update_time">线盘: {{ record.raw_wire_disc || '无' }}</div>
<div class="axle-progress" v-if="record.update_time">完成度: {{ (record.degree_of_completion * 100).toFixed(0) }}%</div>
<div class="axle-stock" v-if="record.update_time && (record.total_number || record.total_net_weight || record.total_gross_weight)">
<div class="stock-item" v-if="record.total_number">总箱数: {{ record.total_number }}</div>
<div class="stock-item" v-if="record.total_net_weight">总净重: {{ record.total_net_weight }}kg</div>
<div class="stock-item" v-if="record.total_gross_weight">总毛重: {{ record.total_gross_weight }}kg</div>
</div>
</div>
</div>
</div>
<div v-if="!isEquipmentRunning(equipment)" class="no-records">
设备当前未运行
</div>
</div>
</div>
</div>
<!-- 轴详情对话框 -->
<el-dialog
v-model="dialogVisible"
title="轴详细信息"
width="80%"
top="5vh"
:before-close="handleClose">
<div v-if="selectedAxle" class="axle-details-container">
<!-- 左侧:轴基本信息 -->
<div class="axle-info-panel">
<div class="detail-title">基本信息</div>
<div class="detail-row">
<span class="detail-label">设备编号:</span>
<span class="detail-value">{{ selectedEquipmentCode }}</span>
</div>
<div class="detail-row">
<span class="detail-label">轴号:</span>
<span class="detail-value">{{ selectedAxle.axle_number }}</span>
</div>
<div class="detail-row">
<span class="detail-label">运行状态:</span>
<span class="detail-value" :class="{ 'status-running': selectedAxle.update_time, 'status-stopped': !selectedAxle.update_time }">
{{ selectedAxle.update_time ? '运行中' : '不在运行' }}
</span>
</div>
<div v-if="selectedAxle.update_time" class="detail-row specification-row">
<span class="detail-label">规格:</span>
<span class="detail-value spec-item">{{ selectedAxle.specification || '无' }}</span>
<span class="detail-label model-label">型号:</span>
<span class="detail-value model-item">{{ selectedAxle.model || '无' }}</span>
<span class="detail-label disc-label">线盘:</span>
<span class="detail-value disc-item">{{ selectedAxle.raw_wire_disc || '无' }}</span>
</div>
<div v-if="selectedAxle.update_time" class="detail-row">
<span class="detail-label">完成度:</span>
<span class="detail-value">{{ (selectedAxle.degree_of_completion * 100).toFixed(1) }}%</span>
</div>
<div v-if="selectedAxle.update_time && (selectedAxle.total_number || selectedAxle.total_net_weight || selectedAxle.total_gross_weight)" class="stock-details">
<div class="detail-title">库存信息</div>
<div v-if="selectedAxle.total_number || selectedAxle.total_net_weight || selectedAxle.total_gross_weight" class="detail-row stock-row">
<span v-if="selectedAxle.total_number" class="detail-label stock-label">总箱数:</span>
<span v-if="selectedAxle.total_number" class="detail-value stock-item-number">{{ selectedAxle.total_number }}</span>
<span v-if="selectedAxle.total_net_weight" class="detail-label stock-label">总净重:</span>
<span v-if="selectedAxle.total_net_weight" class="detail-value stock-item-weight">{{ selectedAxle.total_net_weight }}kg</span>
<span v-if="selectedAxle.total_gross_weight" class="detail-label stock-label">总毛重:</span>
<span v-if="selectedAxle.total_gross_weight" class="detail-value stock-item-weight">{{ selectedAxle.total_gross_weight }}kg</span>
</div>
</div>
<div v-if="productionScheduleLoading" v-loading="true" class="production-schedule-loading"></div>
<div v-else-if="productionScheduleData" class="production-schedule-details">
<div class="detail-title">排产信息</div>
<div v-if="productionScheduleData.axle_final_average_speed" class="detail-row">
<span class="detail-label">产速:</span>
<span class="detail-value">{{ productionScheduleData.axle_final_average_speed }} kg/小时</span>
</div>
<div v-if="productionScheduleData.axle_final_average_weight" class="detail-row">
<span class="detail-label">每轴均重:</span>
<span class="detail-value">{{ productionScheduleData.axle_final_average_weight }}kg</span>
</div>
</div>
<div v-if="selectedAxle.update_time" class="current-order-details">
<div class="detail-title">当前订单</div>
<div class="detail-row">
<span class="detail-label">设备轴号:</span>
<span class="detail-value">{{ selectedAxle.axle_number }}</span>
</div>
<div v-if="selectedAxle.total_quantity" class="detail-row">
<span class="detail-label">总轴数:</span>
<el-input-number
v-model="editableTotalQuantity"
:min="1"
:precision="0"
size="small"
@change="onTotalQuantityChange"
class="editable-input"
></el-input-number>
</div>
</div>
<div v-if="selectedAxle.update_time && selectedAxle.total_quantity && productionScheduleData && productionScheduleData.axle_final_average_speed && productionScheduleData.axle_final_average_weight" class="calculation-details">
<div class="detail-title">生产计算</div>
<div class="detail-row">
<span class="detail-label">小时轴数:</span>
<span class="detail-value">{{ calculateAxlesPerHour() }} 轴</span>
</div>
<div v-if="selectedAxle.update_time && selectedAxle.total_quantity && productionScheduleData && productionScheduleData.axle_final_average_speed && productionScheduleData.axle_final_average_weight" class="detail-row stock-row">
<span class="detail-label stock-label">每天重量:</span>
<span class="detail-value stock-item-weight">{{ calculateDailyWeight() }} kg</span>
<span class="detail-label stock-label">每天轴数:</span>
<span class="detail-value stock-item-number">{{ calculateAxlesPerDay() }} 轴</span>
<span class="detail-label stock-label">每天箱数:</span>
<span class="detail-value stock-item-number">{{ calculateBoxesPerDay() }} 箱</span>
</div>
</div>
<!-- 转移概率信息 -->
<div v-if="transitionProbabilitiesLoading" v-loading="true" class="transition-probabilities-loading"></div>
<div v-else-if="transitionProbabilitiesData && transitionProbabilitiesData.current_specification_transitions" class="transition-probabilities-details">
<div class="detail-title">当前规格的转移概率</div>
<div class="transition-probabilities-content">
<div v-for="(info, spec) in transitionProbabilitiesData.current_specification_transitions" :key="spec" class="transition-probability-item">
<span class="detail-label">到 {{ spec }}:</span>
<span class="detail-value">概率 {{ (info.probability * 100).toFixed(2) }}%, 转移次数 {{ info.count }}</span>
</div>
</div>
</div>
</div>
<!-- 右侧:历史数据图表 -->
<div class="history-chart-panel">
<div class="chart-controls">
<div class="detail-title">历史数据分析</div>
<el-select v-model="selectedTimeRange" @change="onTimeRangeChange" style="width: 120px">
<el-option label="近1天" value="近1天" />
<el-option label="近3天" value="近3天" />
<el-option label="近1周" value="近1周" />
<el-option label="近1月" value="近1月" />
</el-select>
</div>
<div v-if="historyDataLoading || salesDataLoading" v-loading="true" class="chart-loading"></div>
<div v-else-if="!hasHistoryData && !hasSalesData" class="no-history-data">
暂无数据
</div>
<div v-else class="charts-container">
<!-- 图表选项卡 -->
<el-tabs v-model="activeChartTab" type="border-card" @tab-click="handleTabClick">
<el-tab-pane label="总箱数变化" name="totalNumber">
<div ref="totalNumberChartRef" class="chart"></div>
</el-tab-pane>
<el-tab-pane label="总净重变化" name="totalNetWeight">
<div ref="totalNetWeightChartRef" class="chart"></div>
</el-tab-pane>
<el-tab-pane label="总毛重变化" name="totalGrossWeight">
<div ref="totalGrossWeightChartRef" class="chart"></div>
</el-tab-pane>
<el-tab-pane label="销量变化" name="salesData">
<div ref="salesChartRef" class="chart"></div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false"></el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { defineProps, computed, ref, onMounted, nextTick, watch } from 'vue';
import { ElDialog, ElButton, ElSelect, ElOption, ElTabs, ElTabPane, ElInputNumber } from 'element-plus';
import { getDateRangeByTimeRange } from '@/utils/dateFormat';
import * as echarts from 'echarts';
import { API_CONFIG } from "@/config/api";
const props = defineProps({
equipmentData: {
type: Array,
required: true
}
});
// 设备代码与轴号的映射表
const equipmentAxlesMap = {
'QB001': ['左边', '右边'],
'QB002': ['左边', '右边'],
'QB003': ['左边', '右边'],
'QB004': ['左1', '左2', '左3', '左4', '左5', '右1', '右2', '右3', '右4', '右5'],
'QB0042': ['左1', '左2', '左3', '左4', '左5', '右1', '右2', '右3', '右4', '右5'],
'QB005': ['左1', '左2', '左3', '左4', '左5', '左6', '右1', '右2', '右3', '右4', '右5', '右6'],
'QB007': ['左边', '右边'],
'QB008': ['左边', '右边'],
'QB009': ['左边', '右边'],
'QB010': ['左边', '右边'],
'QB011': ['左边', '右边'],
'QB012': ['左边', '右边'],
'QB013': ['左边', '右边'],
'QB014': ['左边', '右边'],
'QB015': ['左边', '右边'],
'QB016': ['左边', '右边'],
'QB017': ['左边', '右边'],
'QB018': ['左1', '左2', '右1', '右2'],
'QB019': ['左1', '左2', '右1', '右2'],
'QB020': ['左1', '左2', '右1', '右2'],
'QB024': ['左1', '左2', '左3', '左4', '右1', '右2', '右3', '右4'],
'QB025': ['左边', '右边'],
'QB026': ['左边', '右边'],
'QB027': ['左边', '右边'],
'QB028': ['左1', '左2', '右1', '右2'],
'QB029': ['左边', '右边'],
'QB030': ['左边', '右边'],
'QB031': ['左边', '右边'],
'QB032': ['左边', '右边'],
'QB033': ['左边', '右边'],
'QB034': ['左边', '右边']
};
// 对设备数据进行排序,将不在生产中的设备移到最后
const sortedEquipmentData = computed(() => {
// 创建一个副本以避免修改原始数组
const equipmentCopy = [...props.equipmentData];
return equipmentCopy.sort((a, b) => {
// 使用新的判断函数检查设备是否在运行
const aIsRunning = isEquipmentRunning(a);
const bIsRunning = isEquipmentRunning(b);
// 如果a在运行而b不在运行a排在前面
if (aIsRunning && !bIsRunning) return -1;
// 如果a不在运行而b在运行b排在前面
if (!aIsRunning && bIsRunning) return 1;
// 如果都在运行或都不在运行,按设备编号排序
return a.equipment_code.localeCompare(b.equipment_code);
});
});
// 获取左侧轴信息
const getLeftAxles = (equipment) => {
if (!equipment.status_records || equipment.status_records.length === 0) {
// 如果没有运行记录,返回所有左侧轴的未运行状态
const validAxles = equipmentAxlesMap[equipment.equipment_code] || [];
return validAxles.filter(axle => axle.includes('左') || axle.includes('left')).map(axleNumber => ({
axle_number: axleNumber,
specification: '',
model: '',
raw_wire_disc: '',
degree_of_completion: 0,
total_number: null,
total_net_weight: null,
total_gross_weight: null,
isRunning: false
}));
}
// 获取设备对应的预期轴号列表
const validAxles = equipmentAxlesMap[equipment.equipment_code] || [];
const validLeftAxles = validAxles.filter(axle =>
axle.includes('左') || axle.includes('left')
);
// 存储所有的轴记录,包括重复的轴号
const resultAxles = [];
const processedAxles = new Set(); // 用于跟踪已处理的轴号
// 处理运行中的记录
for (const record of equipment.status_records) {
if (!record.axle_number) continue;
// 检查记录中包含哪些左侧轴
for (const axleNumber of validLeftAxles) {
if (record.axle_number.includes(axleNumber)) {
// 对于复合轴号(如"左1,左2")需要特殊处理
if (record.axle_number.includes(',')) {
// 分割复合轴号,检查是否包含当前轴号
const axleNumbers = record.axle_number.split(',');
if (axleNumbers.includes(axleNumber)) {
resultAxles.push({
...record,
axle_number: axleNumber, // 使用单个轴号
axle_count: axleNumbers.length // 更新轴数为实际数量
});
processedAxles.add(axleNumber);
}
} else {
// 单个轴号
resultAxles.push({
...record,
axle_number: axleNumber // 使用实际的轴号
});
processedAxles.add(axleNumber);
}
}
}
}
// 添加未运行的左侧轴
for (const axleNumber of validLeftAxles) {
if (!processedAxles.has(axleNumber)) {
resultAxles.push({
axle_number: axleNumber,
specification: '',
model: '',
raw_wire_disc: '',
degree_of_completion: 0,
total_number: null,
total_net_weight: null,
total_gross_weight: null,
isRunning: false
});
}
}
// 按照有效轴号列表的顺序排序,相同轴号的记录会排在一起
return resultAxles.sort((a, b) => {
const aIndex = validLeftAxles.indexOf(a.axle_number);
const bIndex = validLeftAxles.indexOf(b.axle_number);
if (aIndex !== bIndex) {
return aIndex - bIndex;
}
// 如果轴号相同,按更新时间排序(最新的在前)
if (a.update_time && b.update_time) {
return new Date(b.update_time) - new Date(a.update_time);
}
// 如果一个有更新时间一个没有,有更新时间的在前
return a.update_time ? -1 : 1;
});
};
// 获取右侧轴信息
const getRightAxles = (equipment) => {
if (!equipment.status_records || equipment.status_records.length === 0) {
// 如果没有运行记录,返回所有右侧轴的未运行状态
const validAxles = equipmentAxlesMap[equipment.equipment_code] || [];
return validAxles.filter(axle => axle.includes('右') || axle.includes('right')).map(axleNumber => ({
axle_number: axleNumber,
specification: '',
model: '',
raw_wire_disc: '',
degree_of_completion: 0,
total_number: null,
total_net_weight: null,
total_gross_weight: null,
isRunning: false
}));
}
// 获取设备对应的预期轴号列表
const validAxles = equipmentAxlesMap[equipment.equipment_code] || [];
const validRightAxles = validAxles.filter(axle =>
axle.includes('右') || axle.includes('right')
);
// 存储所有的轴记录,包括重复的轴号
const resultAxles = [];
const processedAxles = new Set(); // 用于跟踪已处理的轴号
// 处理运行中的记录
for (const record of equipment.status_records) {
if (!record.axle_number) continue;
// 检查记录中包含哪些右侧轴
for (const axleNumber of validRightAxles) {
if (record.axle_number.includes(axleNumber)) {
// 对于复合轴号(如"右1,右2")需要特殊处理
if (record.axle_number.includes(',')) {
// 分割复合轴号,检查是否包含当前轴号
const axleNumbers = record.axle_number.split(',');
if (axleNumbers.includes(axleNumber)) {
resultAxles.push({
...record,
axle_number: axleNumber, // 使用单个轴号
axle_count: axleNumbers.length // 更新轴数为实际数量
});
processedAxles.add(axleNumber);
}
} else {
// 单个轴号
resultAxles.push({
...record,
axle_number: axleNumber // 使用实际的轴号
});
processedAxles.add(axleNumber);
}
}
}
}
// 添加未运行的右侧轴
for (const axleNumber of validRightAxles) {
if (!processedAxles.has(axleNumber)) {
resultAxles.push({
axle_number: axleNumber,
specification: '',
model: '',
raw_wire_disc: '',
degree_of_completion: 0,
total_number: null,
total_net_weight: null,
total_gross_weight: null,
isRunning: false
});
}
}
// 按照有效轴号列表的顺序排序,相同轴号的记录会排在一起
return resultAxles.sort((a, b) => {
const aIndex = validRightAxles.indexOf(a.axle_number);
const bIndex = validRightAxles.indexOf(b.axle_number);
if (aIndex !== bIndex) {
return aIndex - bIndex;
}
// 如果轴号相同,按更新时间排序(最新的在前)
if (a.update_time && b.update_time) {
return new Date(b.update_time) - new Date(a.update_time);
}
// 如果一个有更新时间一个没有,有更新时间的在前
return a.update_time ? -1 : 1;
});
};
// 检查设备是否在运行
const isEquipmentRunning = (equipment) => {
if (!equipment.status_records || equipment.status_records.length === 0) return false;
// 获取设备对应的预期轴号列表
const validAxles = equipmentAxlesMap[equipment.equipment_code] || [];
// 检查是否有至少一个有效轴号在status_records中
return equipment.status_records.some(record =>
record.axle_number && validAxles.some(axle => record.axle_number.includes(axle))
);
};
// 提取轴号中的数字
const extractAxleNumber = (axleStr) => {
if (!axleStr) return Infinity;
// 使用正则表达式提取数字,如从"左1"中提取出1
const match = axleStr.match(/(\d+)/);
if (match) {
return parseInt(match[0], 10);
}
// 如果没有数字,例如只有"左边"或"右边",则使用默认值
if (axleStr.includes('左') || axleStr.includes('left')) return 0;
if (axleStr.includes('右') || axleStr.includes('right')) return 0;
return Infinity;
};
// 根据完成度返回对应的CSS类名
const getCompletionClass = (degree) => {
const percentage = degree * 100;
if (percentage < 25) return 'completion-low';
if (percentage < 75) return 'completion-medium';
if (percentage < 100) return 'completion-high';
return 'completion-full';
};
// 对话框相关状态
const dialogVisible = ref(false);
const selectedAxle = ref(null);
const selectedEquipmentCode = ref('');
// 历史数据相关状态
const historyDataLoading = ref(false);
const historyData = ref([]);
const selectedTimeRange = ref('近1月'); // 默认时间范围
const activeChartTab = ref('totalNumber');
const hasHistoryData = ref(false);
// 销量数据相关状态
const salesDataLoading = ref(false);
const salesData = ref([]);
const hasSalesData = ref(false);
// 完成度筛选相关状态
const selectedCompletionLevel = ref('all'); // 默认显示全部
// 排产信息相关状态
const productionScheduleData = ref(null);
const productionScheduleLoading = ref(false);
// 转移概率相关状态
const transitionProbabilitiesData = ref(null);
const transitionProbabilitiesLoading = ref(false);
// 可编辑的总轴数
const editableTotalQuantity = ref(0);
// 图表引用
const totalNumberChartRef = ref(null);
const totalNetWeightChartRef = ref(null);
const totalGrossWeightChartRef = ref(null);
const salesChartRef = ref(null);
// 图表实例
let totalNumberChart = null;
let totalNetWeightChart = null;
let totalGrossWeightChart = null;
let salesChart = null;
// 显示轴详情对话框
const showAxleDetails = (record, equipmentCode) => {
selectedAxle.value = { ...record };
selectedEquipmentCode.value = equipmentCode;
dialogVisible.value = true;
// 重置排产信息
productionScheduleData.value = null;
// 如果轴在运行且有规格和线盘信息,则获取历史数据和排产信息
if (record.update_time && record.model && record.specification && record.wire_disc) {
fetchHistoryData(record.model, record.specification, record.wire_disc);
fetchProductionScheduleData(record.model, record.specification, record.wire_disc, equipmentCode, record.axle_number);
fetchSalesData(record.model, record.specification);
fetchTransitionProbabilitiesData(equipmentCode, record.axle_number, record.model, record.specification, record.wire_disc);
} else {
hasHistoryData.value = false;
}
// 在对话框显示后初始化图表
nextTick(() => {
initCharts();
});
};
// 关闭对话框
const handleClose = () => {
dialogVisible.value = false;
// 重置转移概率数据
transitionProbabilitiesData.value = null;
// 销毁图表实例
if (totalNumberChart) {
totalNumberChart.dispose();
totalNumberChart = null;
}
if (totalNetWeightChart) {
totalNetWeightChart.dispose();
totalNetWeightChart = null;
}
if (totalGrossWeightChart) {
totalGrossWeightChart.dispose();
totalGrossWeightChart = null;
}
if (salesChart) {
salesChart.dispose();
salesChart = null;
}
};
// 监听对话框打开状态
watch(dialogVisible, (newValue) => {
if (newValue) {
// 对话框打开后,确保图表容器已渲染
setTimeout(() => {
initCharts();
// 如果已有历史数据,则更新图表
if (hasHistoryData.value && historyData.value.length > 0) {
// 再次延迟以确保图表容器完全初始化
setTimeout(() => {
updateCharts();
}, 300);
}
// 如果已有销量数据,则更新图表
if (hasSalesData.value && salesData.value.length > 0) {
// 再次延迟以确保图表容器完全初始化
setTimeout(() => {
updateCharts();
}, 300);
}
}, 300);
}
});
// 时间范围改变时获取新的历史数据
const onTimeRangeChange = () => {
if (selectedAxle.value && selectedAxle.value.update_time &&
selectedAxle.value.specification && selectedAxle.value.wire_disc) {
fetchHistoryData(
selectedAxle.value.model,
selectedAxle.value.specification,
selectedAxle.value.wire_disc
);
fetchSalesData(
selectedAxle.value.model,
selectedAxle.value.specification
);
}
};
// 选项卡切换处理函数
const handleTabClick = (tab) => {
// 使用setTimeout确保DOM完全渲染后再操作图表
setTimeout(() => {
if (tab.props.name === 'totalNumber' && !totalNumberChart && totalNumberChartRef.value) {
totalNumberChart = echarts.init(totalNumberChartRef.value);
updateCharts();
} else if (tab.props.name === 'totalNetWeight' && !totalNetWeightChart && totalNetWeightChartRef.value) {
totalNetWeightChart = echarts.init(totalNetWeightChartRef.value);
updateCharts();
} else if (tab.props.name === 'totalGrossWeight' && !totalGrossWeightChart && totalGrossWeightChartRef.value) {
totalGrossWeightChart = echarts.init(totalGrossWeightChartRef.value);
updateCharts();
} else if (tab.props.name === 'salesData' && !salesChart && salesChartRef.value) {
salesChart = echarts.init(salesChartRef.value);
updateCharts();
}
// 强制图表重新渲染
if (tab.props.name === 'totalNumber' && totalNumberChart) {
totalNumberChart.resize();
} else if (tab.props.name === 'totalNetWeight' && totalNetWeightChart) {
totalNetWeightChart.resize();
} else if (tab.props.name === 'totalGrossWeight' && totalGrossWeightChart) {
totalGrossWeightChart.resize();
} else if (tab.props.name === 'salesData' && salesChart) {
salesChart.resize();
}
}, 100);
};
// 获取历史数据
const fetchHistoryData = (model, specification, wireDisc) => {
historyDataLoading.value = true;
// 获取日期范围
const dateRange = getDateRangeByTimeRange(selectedTimeRange.value);
// 构建API URL
let apiUrl = `${API_CONFIG.BASE_URL}/api/plan/wms/history?model=${model}&specification=${specification}&wire_disc=${wireDisc}&start_date=${dateRange.start_date}&end_date=${dateRange.end_date}`;
fetch(apiUrl)
.then(response => response.json())
.then(data => {
if (data.code === 200 && data.data && data.data.records) {
historyData.value = data.data.records;
hasHistoryData.value = historyData.value.length > 0;
if (hasHistoryData.value) {
// 使用setTimeout确保DOM完全渲染后再初始化图表
setTimeout(() => {
initCharts();
// 再次延迟以确保图表容器完全初始化
setTimeout(() => {
updateCharts();
}, 300);
}, 300);
}
} else {
historyData.value = [];
hasHistoryData.value = false;
}
})
.catch(error => {
console.error('历史数据API调用失败:', error);
historyData.value = [];
hasHistoryData.value = false;
})
.finally(() => {
historyDataLoading.value = false;
});
};
// 获取销量数据
const fetchSalesData = (model, specification) => {
salesDataLoading.value = true;
// 获取日期范围
const dateRange = getDateRangeByTimeRange(selectedTimeRange.value);
// 构建API URL
let apiUrl = `${API_CONFIG.BASE_URL}/api/sale/records?model=${model}&specification=${specification}&start_date=${dateRange.start_date}&end_date=${dateRange.end_date}`;
fetch(apiUrl)
.then(response => response.json())
.then(data => {
if (data.code === 200 && data.data) {
salesData.value = data.data;
hasSalesData.value = salesData.value.length > 0;
if (hasSalesData.value) {
// 使用setTimeout确保DOM完全渲染后再初始化图表
setTimeout(() => {
initCharts();
// 再次延迟以确保图表容器完全初始化
setTimeout(() => {
updateCharts();
}, 300);
}, 300);
}
} else {
salesData.value = [];
hasSalesData.value = false;
}
})
.catch(error => {
console.error('销量数据API调用失败:', error);
salesData.value = [];
hasSalesData.value = false;
})
.finally(() => {
salesDataLoading.value = false;
});
};
// 获取排产信息数据
const fetchProductionScheduleData = (model, specification, wireDisc, equipmentCode, axleNumber) => {
productionScheduleLoading.value = true;
// 构建API URL
let apiUrl = `${API_CONFIG.BASE_URL}/api/plan/qb_machine_unit_stats?model=${model}&specification=${specification}&wire_disc=${wireDisc}`;
fetch(apiUrl)
.then(response => response.json())
.then(data => {
if (data.code === 200 && data.data && data.data.qb_machine_unit_stats && data.data.qb_machine_unit_stats.data) {
// 构建匹配键 "equipment_code-axle_number"
const matchKey = `${equipmentCode}-${axleNumber}`;
// 查找匹配的数据
if (data.data.qb_machine_unit_stats.data[matchKey]) {
productionScheduleData.value = data.data.qb_machine_unit_stats.data[matchKey];
} else {
console.log(`未找到匹配的排产数据: ${matchKey}`);
productionScheduleData.value = null;
}
} else {
console.log('排产信息API返回格式不正确或无数据');
productionScheduleData.value = null;
}
})
.catch(error => {
console.error('排产信息API调用失败:', error);
productionScheduleData.value = null;
})
.finally(() => {
productionScheduleLoading.value = false;
});
};
// 获取转移概率数据
const fetchTransitionProbabilitiesData = (equipmentCode, axleNumber, model, specification, wireDisc) => {
transitionProbabilitiesLoading.value = true;
// 使用URL编码处理参数特别是中文和特殊字符
const params = new URLSearchParams({
equipment_code: equipmentCode,
axle_number: axleNumber,
model: model,
specification: specification,
wire_disc: wireDisc
});
// 构建API URL
let apiUrl = `${API_CONFIG.BASE_URL}/api/plan/transition/probabilities?${params.toString()}`;
console.log('转移概率API URL:', apiUrl);
fetch(apiUrl)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('转移概率API响应:', data);
if (data.code === 200 && data.data) {
transitionProbabilitiesData.value = data.data;
} else {
transitionProbabilitiesData.value = null;
}
})
.catch(error => {
console.error('转移概率API调用失败:', error);
transitionProbabilitiesData.value = null;
})
.finally(() => {
transitionProbabilitiesLoading.value = false;
});
};
// 计算小时轴数 = 总轴数 * 产速 / 每轴均重
const calculateAxlesPerHour = () => {
if (!selectedAxle.value || !editableTotalQuantity.value ||
!productionScheduleData.value || !productionScheduleData.value.axle_final_average_speed ||
!productionScheduleData.value.axle_final_average_weight) {
return 0;
}
const totalQuantity = editableTotalQuantity.value;
const speed = productionScheduleData.value.axle_final_average_speed;
const weightPerAxle = productionScheduleData.value.axle_final_average_weight;
// 公式:总轴数 * 产速 / 每轴均重
const result = (totalQuantity * speed / weightPerAxle);
return result.toFixed(2);
};
// 计算每天轴数 = 小时轴数 * 24
const calculateAxlesPerDay = () => {
const axlesPerHour = calculateAxlesPerHour();
return (axlesPerHour * 24).toFixed(2);
};
// 计算每天重量 = 产速 * 24小时 * 总轴数
const calculateDailyWeight = () => {
if (!productionScheduleData.value || !productionScheduleData.value.axle_final_average_speed || !editableTotalQuantity.value) {
return 0;
}
const speed = productionScheduleData.value.axle_final_average_speed;
const totalQuantity = editableTotalQuantity.value;
// 公式:产速 * 24小时 * 总轴数
const result = speed * 24 * totalQuantity;
return result.toFixed(2);
};
// 计算每天箱数根据wire_disc进行判断
const calculateBoxesPerDay = () => {
const axlesPerDay = calculateAxlesPerDay();
// 如果没有选中轴或者没有线盘信息返回0
if (!selectedAxle.value || !selectedAxle.value.raw_wire_disc) {
return 0;
}
// 确保axlesPerDay是数字类型
const axlesPerDayNum = parseFloat(axlesPerDay);
if (isNaN(axlesPerDayNum)) {
return 0;
}
// 根据wire_disc判断计算方法
if (selectedAxle.value.wire_disc === 'PT-4') {
// 如果是'PT-4',则每天轴数/4
return (axlesPerDayNum / 4).toFixed(2);
} else {
// 其他情况,每天轴数/1
return axlesPerDayNum.toFixed(2);
}
};
// 总轴数变更处理函数
const onTotalQuantityChange = (newValue) => {
// 更新计算结果
// Vue的响应式系统会自动更新无需手动触发
};
// 监听选中轴的变化,初始化可编辑总轴数
watch(selectedAxle, (newVal) => {
if (newVal && newVal.total_quantity) {
editableTotalQuantity.value = newVal.total_quantity;
}
}, { immediate: true });
// 完成度筛选变化处理函数
const onCompletionLevelChange = () => {
// 筛选逻辑通过shouldShowAxle函数处理这里不需要额外操作
};
// 判断是否应该显示轴
const shouldShowAxle = (degree) => {
if (!degree) return false;
const percentage = degree * 100;
switch (selectedCompletionLevel.value) {
case 'all':
return true;
case 'low':
return percentage >= 0 && percentage < 25;
case 'medium':
return percentage >= 25 && percentage < 75;
case 'high':
return percentage >= 75 && percentage < 100;
case 'full':
return percentage >= 100;
default:
return true;
}
};
// 初始化图表
const initCharts = () => {
// 销毁已有图表实例
if (totalNumberChart) {
totalNumberChart.dispose();
totalNumberChart = null;
}
if (totalNetWeightChart) {
totalNetWeightChart.dispose();
totalNetWeightChart = null;
}
if (totalGrossWeightChart) {
totalGrossWeightChart.dispose();
totalGrossWeightChart = null;
}
if (salesChart) {
salesChart.dispose();
salesChart = null;
}
// 初始化新图表实例
if (totalNumberChartRef.value) {
totalNumberChart = echarts.init(totalNumberChartRef.value);
}
if (totalNetWeightChartRef.value) {
totalNetWeightChart = echarts.init(totalNetWeightChartRef.value);
}
if (totalGrossWeightChartRef.value) {
totalGrossWeightChart = echarts.init(totalGrossWeightChartRef.value);
}
if (salesChartRef.value) {
salesChart = echarts.init(salesChartRef.value);
}
};
// 更新图表数据
const updateCharts = () => {
// 确保图表容器已经渲染完成
nextTick(() => {
// 初始化或重新初始化图表实例
if (!totalNumberChart && totalNumberChartRef.value) {
totalNumberChart = echarts.init(totalNumberChartRef.value);
}
if (!totalNetWeightChart && totalNetWeightChartRef.value) {
totalNetWeightChart = echarts.init(totalNetWeightChartRef.value);
}
if (!totalGrossWeightChart && totalGrossWeightChartRef.value) {
totalGrossWeightChart = echarts.init(totalGrossWeightChartRef.value);
}
if (!salesChart && salesChartRef.value) {
salesChart = echarts.init(salesChartRef.value);
}
// 更新总箱数图表
if (totalNumberChart && historyData.value && historyData.value.length > 0) {
const timeData = historyData.value.map(item => {
const date = new Date(item.create_time);
return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
});
const totalNumberData = historyData.value.map(item => item.total_number);
// 准备销量数据,按日期聚合
let salesBoxCountData = [];
if (salesData.value && salesData.value.length > 0) {
// 创建一个映射,键为日期,值为当天总箱数
const salesBoxCountMap = {};
salesData.value.forEach(item => {
const date = new Date(item.date);
const dateStr = `${date.getMonth() + 1}/${date.getDate()}`;
if (!salesBoxCountMap[dateStr]) {
salesBoxCountMap[dateStr] = 0;
}
salesBoxCountMap[dateStr] += item.box_count;
});
// 将销量数据映射到时间轴上
salesBoxCountData = timeData.map(timeStr => {
// 从时间字符串中提取日期部分(去掉时间部分)
const dateStr = timeStr.split(' ')[0];
return salesBoxCountMap[dateStr] || 0;
});
}
const totalNumberOption = {
title: {
text: '总箱数变化趋势',
left: 'center',
textStyle: {
fontSize: 16
}
},
tooltip: {
trigger: 'axis',
formatter: function(params) {
let result = params[0].name + '<br/>';
params.forEach(param => {
result += `${param.seriesName}: ${param.value} 箱<br/>`;
});
return result;
}
},
legend: {
data: ['生产箱数', '销量箱数'],
top: 30
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: 60,
containLabel: true
},
xAxis: {
type: 'category',
data: timeData,
axisLabel: {
rotate: 45
}
},
yAxis: {
type: 'value',
name: '箱数'
},
series: [
{
name: '生产箱数',
data: totalNumberData,
type: 'line',
smooth: true,
itemStyle: {
color: '#409EFF'
},
areaStyle: {
opacity: 0.3
}
},
{
name: '销量箱数',
data: salesBoxCountData,
type: 'line',
smooth: true,
itemStyle: {
color: '#E6A23C'
}
}
]
};
totalNumberChart.setOption(totalNumberOption, true); // true表示不合并选项
}
// 更新总净重图表
if (totalNetWeightChart && historyData.value && historyData.value.length > 0) {
const timeData = historyData.value.map(item => {
const date = new Date(item.create_time);
return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
});
const totalNetWeightData = historyData.value.map(item => item.total_net_weight);
// 准备销量数据,按日期聚合
let salesWeightData = [];
if (salesData.value && salesData.value.length > 0) {
// 创建一个映射,键为日期,值为当天总重量
const salesWeightMap = {};
salesData.value.forEach(item => {
const date = new Date(item.date);
const dateStr = `${date.getMonth() + 1}/${date.getDate()}`;
if (!salesWeightMap[dateStr]) {
salesWeightMap[dateStr] = 0;
}
salesWeightMap[dateStr] += item.weight;
});
// 将销量数据映射到时间轴上
salesWeightData = timeData.map(timeStr => {
// 从时间字符串中提取日期部分(去掉时间部分)
const dateStr = timeStr.split(' ')[0];
return salesWeightMap[dateStr] || 0;
});
}
const totalNetWeightOption = {
title: {
text: '总净重变化趋势',
left: 'center',
textStyle: {
fontSize: 16
}
},
tooltip: {
trigger: 'axis',
formatter: function(params) {
let result = params[0].name + '<br/>';
params.forEach(param => {
result += `${param.seriesName}: ${param.value} kg<br/>`;
});
return result;
}
},
legend: {
data: ['生产净重', '销量净重'],
top: 30
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: 60,
containLabel: true
},
xAxis: {
type: 'category',
data: timeData,
axisLabel: {
rotate: 45
}
},
yAxis: {
type: 'value',
name: '重量(kg)'
},
series: [
{
name: '生产净重',
data: totalNetWeightData,
type: 'line',
smooth: true,
itemStyle: {
color: '#67C23A'
},
areaStyle: {
opacity: 0.3
}
},
{
name: '销量净重',
data: salesWeightData,
type: 'line',
smooth: true,
itemStyle: {
color: '#F56C6C'
}
}
]
};
totalNetWeightChart.setOption(totalNetWeightOption, true); // true表示不合并选项
}
// 更新总毛重图表
if (totalGrossWeightChart && historyData.value && historyData.value.length > 0) {
const timeData = historyData.value.map(item => {
const date = new Date(item.create_time);
return `${date.getMonth() + 1}/${date.getDate()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`;
});
const totalGrossWeightData = historyData.value.map(item => item.total_gross_weight);
// 准备销量数据,按日期聚合
let salesWeightData = [];
if (salesData.value && salesData.value.length > 0) {
// 创建一个映射,键为日期,值为当天总重量
const salesWeightMap = {};
salesData.value.forEach(item => {
const date = new Date(item.date);
const dateStr = `${date.getMonth() + 1}/${date.getDate()}`;
if (!salesWeightMap[dateStr]) {
salesWeightMap[dateStr] = 0;
}
salesWeightMap[dateStr] += item.weight;
});
// 将销量数据映射到时间轴上
salesWeightData = timeData.map(timeStr => {
// 从时间字符串中提取日期部分(去掉时间部分)
const dateStr = timeStr.split(' ')[0];
return salesWeightMap[dateStr] || 0;
});
}
const totalGrossWeightOption = {
title: {
text: '总毛重变化趋势',
left: 'center',
textStyle: {
fontSize: 16
}
},
tooltip: {
trigger: 'axis',
formatter: function(params) {
let result = params[0].name + '<br/>';
params.forEach(param => {
result += `${param.seriesName}: ${param.value} kg<br/>`;
});
return result;
}
},
legend: {
data: ['生产毛重', '销量重量'],
top: 30
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: 60,
containLabel: true
},
xAxis: {
type: 'category',
data: timeData,
axisLabel: {
rotate: 45
}
},
yAxis: {
type: 'value',
name: '重量(kg)'
},
series: [
{
name: '生产毛重',
data: totalGrossWeightData,
type: 'line',
smooth: true,
itemStyle: {
color: '#E6A23C'
},
areaStyle: {
opacity: 0.3
}
},
{
name: '销量重量',
data: salesWeightData,
type: 'line',
smooth: true,
itemStyle: {
color: '#F56C6C'
}
}
]
};
totalGrossWeightChart.setOption(totalGrossWeightOption, true); // true表示不合并选项
}
// 更新销量图表
if (salesChart && salesData.value && salesData.value.length > 0) {
// 准备销量数据
const salesTimeData = salesData.value.map(item => {
const date = new Date(item.date);
return `${date.getMonth() + 1}/${date.getDate()}`;
});
const boxCountData = salesData.value.map(item => item.box_count);
const weightData = salesData.value.map(item => item.weight);
const salesOption = {
title: {
text: '销量变化趋势',
left: 'center',
textStyle: {
fontSize: 16
}
},
tooltip: {
trigger: 'axis',
formatter: function(params) {
let result = params[0].name + '<br/>';
params.forEach(param => {
if (param.seriesName === '箱数') {
result += `${param.seriesName}: ${param.value} 箱<br/>`;
} else if (param.seriesName === '重量') {
result += `${param.seriesName}: ${param.value} kg<br/>`;
}
});
return result;
}
},
legend: {
data: ['箱数', '重量'],
top: 30
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: 60,
containLabel: true
},
xAxis: {
type: 'category',
data: salesTimeData,
axisLabel: {
rotate: 45
}
},
yAxis: [
{
type: 'value',
name: '箱数',
position: 'left'
},
{
type: 'value',
name: '重量(kg)',
position: 'right'
}
],
series: [
{
name: '箱数',
type: 'bar',
data: boxCountData,
itemStyle: {
color: '#409EFF'
}
},
{
name: '重量',
type: 'line',
yAxisIndex: 1,
data: weightData,
smooth: true,
itemStyle: {
color: '#67C23A'
},
areaStyle: {
opacity: 0.3
}
}
]
};
salesChart.setOption(salesOption, true); // true表示不合并选项
}
});
};
</script>
<style scoped>
.equipment-status-container {
padding: 20px;
}
.equipment-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
gap: 20px;
}
.equipment-item {
border: 1px solid #dcdfe6;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
overflow: hidden;
}
.equipment-header {
background-color: #f5f7fa;
padding: 10px 15px;
font-size: 16px;
border-bottom: 1px solid #dcdfe6;
display: flex;
justify-content: space-between;
align-items: center;
}
.equipment-name {
font-weight: bold;
}
.equipment-health {
font-size: 14px;
color: #67c23a;
}
.equipment-body {
padding: 15px;
}
.axle-container {
display: flex;
justify-content: space-between;
min-height: 200px;
}
.axle-side {
width: 48%;
display: flex;
flex-direction: column;
gap: 8px;
}
.axle-item {
padding: 8px;
border-radius: 4px;
background-color: #f5f7fa;
border: 2px solid #e4e7ed;
position: relative;
}
/* 0% - 25% 完成度 - 浅绿色边框 */
.axle-item.completion-low {
border-color: #b3e19d;
}
.axle-item.completion-low::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background-color: #b3e19d;
border-radius: 4px 0 0 4px;
}
/* 25% - 75% 完成度 - 中等绿色边框 */
.axle-item.completion-medium {
border-color: #67c23a;
}
.axle-item.completion-medium::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background-color: #67c23a;
border-radius: 4px 0 0 4px;
}
/* 75% - 100% 完成度 - 深绿色边框 */
.axle-item.completion-high {
border-color: #529b2e;
}
.axle-item.completion-high::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background-color: #529b2e;
border-radius: 4px 0 0 4px;
}
/* 100%及以上完成度 - 最深绿色边框 */
.axle-item.completion-full {
border-color: #387c2a;
}
.axle-item.completion-full::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background-color: #387c2a;
border-radius: 4px 0 0 4px;
}
.axle-number {
font-weight: bold;
margin-bottom: 4px;
}
.axle-spec {
font-size: 12px;
color: #606266;
margin-bottom: 2px;
background-color: rgba(64, 158, 255, 0.1);
padding: 2px 4px;
border-radius: 3px;
display: inline-block;
}
.axle-model {
font-size: 12px;
color: #909399;
margin-bottom: 2px;
background-color: rgba(103, 194, 58, 0.1);
padding: 2px 4px;
border-radius: 3px;
display: inline-block;
}
.axle-disc {
font-size: 12px;
color: #606266;
margin-bottom: 2px;
background-color: rgba(230, 162, 60, 0.1);
padding: 2px 4px;
border-radius: 3px;
display: inline-block;
}
.axle-progress {
font-size: 12px;
color: #409eff;
font-weight: bold;
}
.axle-stock {
margin-top: 6px;
padding-top: 6px;
border-top: 1px dashed #e4e7ed;
}
.stock-item {
font-size: 11px;
color: #606266;
margin-bottom: 2px;
}
.no-records {
text-align: center;
color: #909399;
padding: 20px 0;
}
/* 不在运行的轴样式 */
.axle-item.not-running {
background-color: #f5f7fa;
opacity: 0.7;
border-color: #c0c4cc;
}
.axle-item.not-running::before {
background-color: #c0c4cc;
}
.not-running-label {
font-size: 12px;
color: #f56c6c;
font-weight: normal;
}
/* 筛选器样式 */
.filter-container {
margin-bottom: 20px;
display: flex;
align-items: center;
}
.filter-label {
margin-right: 10px;
font-weight: bold;
color: #606266;
}
/* 对话框样式 */
.axle-details-container {
display: flex;
min-height: 650px;
}
/* 左侧面板样式 */
.axle-info-panel {
flex: 1;
padding: 10px 20px 10px 10px;
border-right: 1px solid #EBEEF5;
max-height: 650px;
overflow-y: auto;
}
/* 右侧面板样式 */
.history-chart-panel {
flex: 1;
padding: 10px;
display: flex;
flex-direction: column;
}
.detail-row {
display: flex;
margin-bottom: 12px;
line-height: 1.5;
}
/* 规格、型号、线盘在同一行显示的特殊样式 */
.specification-row {
flex-wrap: wrap;
}
.spec-item {
background-color: rgba(64, 158, 255, 0.1);
padding: 2px 6px;
border-radius: 3px;
margin-right: 10px;
}
.model-label {
min-width: auto;
margin-left: 5px;
}
.model-item {
background-color: rgba(103, 194, 58, 0.1);
padding: 2px 6px;
border-radius: 3px;
margin-right: 10px;
}
.disc-label {
min-width: auto;
margin-left: 5px;
}
.disc-item {
background-color: rgba(230, 162, 60, 0.1);
padding: 2px 6px;
border-radius: 3px;
}
/* 库存信息在同一行显示的特殊样式 */
.stock-row {
flex-wrap: wrap;
}
.stock-label {
min-width: auto;
margin-left: 5px;
}
.stock-item-number {
background-color: rgba(64, 158, 255, 0.1);
padding: 2px 6px;
border-radius: 3px;
margin-right: 10px;
}
.stock-item-weight {
background-color: rgba(144, 147, 153, 0.1);
padding: 2px 6px;
border-radius: 3px;
margin-right: 10px;
}
/* 转移概率样式 */
.transition-probabilities-loading {
height: 80px;
position: relative;
}
.transition-probabilities-details {
margin-top: 15px;
border-top: 1px dashed #e4e7ed;
padding-top: 10px;
}
.transition-probabilities-content {
background-color: #f8f9fa;
border-radius: 4px;
padding: 8px;
max-height: 150px;
overflow-y: auto;
}
.transition-probability-item {
display: flex;
justify-content: space-between;
margin-bottom: 4px;
font-size: 12px;
line-height: 1.4;
}
.detail-label {
min-width: 100px;
font-weight: bold;
color: #606266;
}
.detail-value {
flex: 1;
color: #303133;
}
.detail-title {
font-weight: bold;
color: #303133;
margin: 15px 0 10px;
padding-bottom: 5px;
border-bottom: 1px solid #e4e7ed;
}
.status-running {
color: #67c23a;
}
.status-stopped {
color: #f56c6c;
}
/* 图表控制区域 */
.chart-controls {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
/* 图表容器样式 */
.charts-container {
flex: 1;
display: flex;
flex-direction: column;
}
.chart {
height: 500px;
width: 100%;
}
/* 加载状态 */
.chart-loading {
height: 500px;
display: flex;
align-items: center;
justify-content: center;
}
/* 无数据状态 */
.no-history-data {
height: 500px;
display: flex;
align-items: center;
justify-content: center;
color: #909399;
font-size: 16px;
}
/* 排产信息加载状态 */
.production-schedule-loading {
height: 60px;
display: flex;
align-items: center;
justify-content: center;
}
/* 排产信息面板样式 */
.production-schedule-details {
margin-top: 10px;
}
/* 当前订单面板样式 */
.current-order-details {
margin-top: 10px;
}
/* 生产计算面板样式 */
.calculation-details {
margin-top: 10px;
}
/* 每天生产数据在同一行显示的特殊样式 */
.daily-production-row {
flex-wrap: wrap;
}
.daily-production-item {
min-width: 30%;
margin-right: 10px;
display: flex;
}
.daily-production-item .detail-label {
margin-right: 5px;
}
/* 可编辑输入框样式 */
.editable-input {
width: 100px;
}
/* 添加悬停效果,提示用户可以双击 */
.axle-item {
cursor: pointer;
}
.axle-item:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
</style>