Эх сурвалжийг харах

先提交一下。增加了成本趋势分析

wukai 1 сар өмнө
parent
commit
8b88858086
1 өөрчлөгдсөн 288 нэмэгдсэн , 35 устгасан
  1. 288 35
      src/views/lean/cost/fs2.vue

+ 288 - 35
src/views/lean/cost/fs2.vue

@@ -48,7 +48,9 @@
             <div class="metrics-grid">
               <div class="metric-card primary">
                 <div class="metric-label">固定成本(a)</div>
-                <div class="metric-value">{{ costAnalysisResult.fixedCost.toFixed(2) }} <span class="unit">元</span></div>
+                <div class="metric-value">{{ costAnalysisResult.fixedCost.toFixed(2) }} <span class="unit">元</span>
+                  <div v-if="costAnalysisResult.fixedCostNote" class="note">{{ costAnalysisResult.fixedCostNote }}</div>
+                </div>
               </div>
               <div class="metric-card primary">
                 <div class="metric-label">单位变动成本(b)</div>
@@ -72,9 +74,11 @@
               </div>
               <div class="metric-card warning">
                 <div class="metric-label">显著性</div>
-                <div class="metric-value">{{ costAnalysisResult.significance }}</div>
-                <div class="p-value" v-if="costAnalysisResult.pValue !== undefined && costAnalysisResult.pValue !== null">
-                  (P={{ formatPValue(costAnalysisResult.pValue) }})
+                <div class="metric-value">
+                  {{ costAnalysisResult.significance }}
+                  <span class="p-value" v-if="costAnalysisResult.pValue !== undefined && costAnalysisResult.pValue !== null">
+                    (P={{ formatPValue(costAnalysisResult.pValue) }})
+                  </span>
                 </div>
               </div>
               <div class="metric-card warning">
@@ -95,7 +99,9 @@
             <div class="metrics-grid">
               <div class="metric-card primary">
                 <div class="metric-label">固定成本(a)</div>
-                <div class="metric-value">{{ waterElectricityAnalysisResult.fixedCost.toFixed(2) }} <span class="unit">元</span></div>
+                <div class="metric-value">{{ waterElectricityAnalysisResult.fixedCost.toFixed(2) }} <span class="unit">元</span>
+                  <div v-if="waterElectricityAnalysisResult.fixedCostNote" class="note">{{ waterElectricityAnalysisResult.fixedCostNote }}</div>
+                </div>
               </div>
               <div class="metric-card primary">
                 <div class="metric-label">单位变动成本(b)</div>
@@ -119,9 +125,11 @@
               </div>
               <div class="metric-card warning">
                 <div class="metric-label">显著性</div>
-                <div class="metric-value">{{ waterElectricityAnalysisResult.significance }}</div>
-                <div class="p-value" v-if="waterElectricityAnalysisResult.pValue !== undefined && waterElectricityAnalysisResult.pValue !== null">
-                  (P={{ formatPValue(waterElectricityAnalysisResult.pValue) }})
+                <div class="metric-value">
+                  {{ waterElectricityAnalysisResult.significance }}
+                  <span class="p-value" v-if="waterElectricityAnalysisResult.pValue !== undefined && waterElectricityAnalysisResult.pValue !== null">
+                    (P={{ formatPValue(waterElectricityAnalysisResult.pValue) }})
+                  </span>
                 </div>
               </div>
               <div class="metric-card warning">
@@ -134,6 +142,14 @@
       </div>
     </el-card>
 
+    <!-- 固定成本和单位变动成本趋势图 -->
+    <el-card class="chart-card" v-if="rollingCostData.length > 0">
+      <div slot="header" class="clearfix">
+        <span>成本趋势分析(12个月滚动计算)</span>
+      </div>
+      <div ref="costTrendChart" class="chart-container"></div>
+    </el-card>
+
     <el-card class="table-card">
       <div slot="header" class="clearfix">
         <span>成本数据明细</span>
@@ -237,6 +253,8 @@
 
 <script setup name="CostForecast">
 import {listCost} from "@/api/lean/cost";
+import * as echarts from 'echarts';
+import { parseTime } from "@/utils/ruoyi.js";
 
 const {proxy} = getCurrentInstance();
 const {all_ws} = proxy.useDict('all_ws');
@@ -250,6 +268,9 @@ const originalRows = ref({}); // 用于保存原始数据,便于取消编辑
 const selectedQueryYear = ref(new Date().getFullYear().toString()); // 查询条件中的年份,默认当年,转为字符串
 const costAnalysisResult = ref(null); // 成本性态分析结果
 const waterElectricityAnalysisResult = ref(null); // 水电费分析结果
+const rollingCostData = ref([]); // 用于存储滚动计算的成本数据
+const costTrendChart = ref(null); // 图表实例
+let chartInstance = null; // 图表实例引用
 
 // 获取车间名称
 function getWsName(wsValue) {
@@ -419,16 +440,30 @@ function performCostAnalysis(data, property = 'totalCost') {
   // 计算 P 值 (使用近似计算)
   const pValue = calculatePValue(fStatistic, 1, n - 2);
 
+  // 检查计算结果是否合理
+  const isResultValid = !isNaN(a) && !isNaN(b) && isFinite(a) && isFinite(b);
+  
+  if (!isResultValid) {
+    return null; // 返回空结果,避免显示无效数据
+  }
+
+  // 添加注释说明固定成本为负数的情况
+  let fixedCostNote = "";
+  if (a < 0) {
+    fixedCostNote = " (负数,可能数据分布特殊)";
+  }
+
   return {
-    fixedCost: a,               // 固定成本
-    variableCostPerUnit: b,     // 单位变动成本(元/米)
-    correlationCoefficient: r,  // 相关系数
-    rSquared: rSquared,         // R方 (决定系数)
-    fStatistic: fStatistic,     // F统计量
-    pValue: pValue,             // P 值
+    fixedCost: parseFloat(a.toFixed(2)),               // 固定成本,保留2位小数
+    variableCostPerUnit: parseFloat(b.toFixed(2)),     // 单位变动成本(元/米),保留2位小数
+    correlationCoefficient: parseFloat(r.toFixed(4)),  // 相关系数,保留4位小数
+    rSquared: rSquared !== null ? parseFloat(rSquared.toFixed(4)) : null,         // R方 (决定系数),保留4位小数
+    fStatistic: fStatistic !== null ? parseFloat(fStatistic.toFixed(2)) : null,     // F统计量,保留2位小数
+    pValue: pValue !== null ? parseFloat(pValue.toFixed(6)) : null,             // P 值,保留6位小数
     significance: pValue !== null ? (pValue < 0.05 ? "显著" : "不显著") : "无法计算", // 显著性
-    standardError: standardError, // 标准误差
-    dataPoints: validData.length // 有效数据点数量
+    standardError: standardError !== null ? parseFloat(standardError.toFixed(2)) : null, // 标准误差,保留2位小数
+    dataPoints: validData.length, // 有效数据点数量
+    fixedCostNote: fixedCostNote // 固定成本注释
   };
 }
 
@@ -622,6 +657,170 @@ function performCostForecast() {
   });
 }
 
+// 计算滚动12个月的成本数据
+function calculateRollingCostData(data) {
+  const rollingData = [];
+  
+  // 按时间排序
+  const sortedData = [...data].sort((a, b) => new Date(a.costTime) - new Date(b.costTime));
+  
+  // 对每个月计算前12个月的滚动平均值
+  for (let i = 0; i < sortedData.length; i++) {
+    // 当前月份
+    const currentDate = new Date(sortedData[i].costTime);
+    
+    // 计算12个月时间窗口的开始和结束时间
+    // 开始时间:当前月份的前11个月
+    const startDate = new Date(currentDate);
+    startDate.setMonth(startDate.getMonth() - 11);
+    
+    // 结束时间:当前月份
+    const endDate = new Date(currentDate);
+    
+    // 筛选出在这个12个月时间窗口内的数据
+    const windowData = sortedData.filter(item => {
+      const itemDate = new Date(item.costTime);
+      return itemDate >= startDate && itemDate <= endDate;
+    });
+    
+    // 过滤有效数据用于总成本分析
+    const validTotalCostData = windowData.filter(item => 
+      item.length !== null && 
+      item.totalCost !== null &&
+      !isNaN(parseFloat(item.length)) && 
+      !isNaN(parseFloat(item.totalCost))
+    );
+    
+    // 过滤有效数据用于水电费分析
+    const validWaterElectricityData = windowData.filter(item => 
+      item.length !== null && 
+      item.waterElectricity !== null &&
+      !isNaN(parseFloat(item.length)) && 
+      !isNaN(parseFloat(item.waterElectricity))
+    );
+    
+    // 计算总成本的固定成本和单位变动成本
+    let totalCostAnalysis = null;
+    if (validTotalCostData.length >= 2) {
+      totalCostAnalysis = performCostAnalysis(validTotalCostData, 'totalCost');
+    }
+    
+    // 计算水电费的固定成本和单位变动成本
+    let waterElectricityAnalysis = null;
+    if (validWaterElectricityData.length >= 2) {
+      waterElectricityAnalysis = performCostAnalysis(validWaterElectricityData, 'waterElectricity');
+    }
+    
+    // 添加到结果数组
+    rollingData.push({
+      month: parseTime(sortedData[i].costTime, '{y}-{m}'),
+      costTime: sortedData[i].costTime,
+      totalCostFixed: totalCostAnalysis ? totalCostAnalysis.fixedCost : null,
+      totalCostVariable: totalCostAnalysis ? totalCostAnalysis.variableCostPerUnit : null,
+      waterElectricityFixed: waterElectricityAnalysis ? waterElectricityAnalysis.fixedCost : null,
+      waterElectricityVariable: waterElectricityAnalysis ? waterElectricityAnalysis.variableCostPerUnit : null
+    });
+  }
+  
+  return rollingData;
+}
+
+// 绘制成本趋势图
+function drawCostTrendChart() {
+  if (!costTrendChart.value) return;
+  
+  // 销毁之前的图表实例
+  if (chartInstance) {
+    chartInstance.dispose();
+  }
+  
+  // 初始化图表
+  chartInstance = echarts.init(costTrendChart.value);
+  
+  // 准备图表数据
+  const months = rollingCostData.value.map(item => item.month);
+  const totalCostFixedData = rollingCostData.value.map(item => item.totalCostFixed);
+  const totalCostVariableData = rollingCostData.value.map(item => item.totalCostVariable);
+  const waterElectricityFixedData = rollingCostData.value.map(item => item.waterElectricityFixed);
+  const waterElectricityVariableData = rollingCostData.value.map(item => item.waterElectricityVariable);
+  
+  // 图表配置
+  const option = {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'cross'
+      }
+    },
+    legend: {
+      data: ['总成本固定部分', '总成本单位变动', '水电费固定部分', '水电费单位变动']
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      containLabel: true
+    },
+    xAxis: [
+      {
+        type: 'category',
+        boundaryGap: false,
+        data: months
+      }
+    ],
+    yAxis: [
+      {
+        type: 'value',
+        name: '固定成本(元)',
+        position: 'left'
+      },
+      {
+        type: 'value',
+        name: '单位变动成本(元/米)',
+        position: 'right'
+      }
+    ],
+    series: [
+      {
+        name: '总成本固定部分',
+        type: 'line',
+        yAxisIndex: 0,
+        data: totalCostFixedData,
+        smooth: true
+      },
+      {
+        name: '总成本单位变动',
+        type: 'line',
+        yAxisIndex: 1,
+        data: totalCostVariableData,
+        smooth: true
+      },
+      {
+        name: '水电费固定部分',
+        type: 'line',
+        yAxisIndex: 0,
+        data: waterElectricityFixedData,
+        smooth: true
+      },
+      {
+        name: '水电费单位变动',
+        type: 'line',
+        yAxisIndex: 1,
+        data: waterElectricityVariableData,
+        smooth: true
+      }
+    ]
+  };
+  
+  // 设置图表选项
+  chartInstance.setOption(option);
+  
+  // 监听窗口大小变化,自适应图表
+  window.addEventListener('resize', () => {
+    chartInstance.resize();
+  });
+}
+
 const data = reactive({
   form: {},
   queryParams: {
@@ -664,16 +863,24 @@ function getList() {
     selectedQueryYear.value = new Date().getFullYear().toString();
   }
   queryParams.value.params = {};
-  // 如果选择了年份,则构造该年份的开始和结束时间
-  if (selectedQueryYear.value) {
-    queryParams.value.params["year"] = selectedQueryYear.value;
-  }
+  // 不传递年份参数,获取该车间下所有数据
+  // 年份过滤在前端进行
 
   // 构造查询条件
   const queryParam = {...queryParams.value};
 
   listCost(queryParam).then(response => {
-    // 生成完整的12个月数据
+    // 先按时间排序
+    const sortedData = [...response.rows].sort((a, b) => new Date(a.costTime) - new Date(b.costTime));
+    
+    // 过滤出选定年份及前一年的数据用于12个月滚动计算
+    const filteredData = sortedData.filter(item => {
+      const itemYear = new Date(item.costTime).getFullYear();
+      // 保留选定年份和前一年的数据用于滚动计算
+      return itemYear >= (parseInt(selectedQueryYear.value) - 1) && itemYear <= parseInt(selectedQueryYear.value);
+    });
+    
+    // 生成选定年份的完整12个月数据用于表格显示
     const fullYearData = generateFullYearData(response.rows, selectedQueryYear.value);
     
     costList.value = fullYearData.map(item => ({
@@ -706,6 +913,22 @@ function getList() {
       !isNaN(parseFloat(item.waterElectricity))
     ), 'waterElectricity');
 
+    // 计算滚动成本数据,使用包含前一年数据的完整数据集
+    rollingCostData.value = calculateRollingCostData(filteredData);
+    
+    // 过滤滚动数据,只显示选定年份的数据用于趋势图显示
+    const chartFilteredData = rollingCostData.value.filter(item => {
+      const itemYear = new Date(item.costTime).getFullYear();
+      return itemYear == parseInt(selectedQueryYear.value);
+    });
+    
+    rollingCostData.value = chartFilteredData;
+    
+    // 绘制趋势图
+    nextTick(() => {
+      drawCostTrendChart();
+    });
+
     // 执行成本预测
     if (costAnalysisResult.value) {
       performCostForecast();
@@ -769,6 +992,13 @@ onMounted(() => {
   }
   handleQuery();
 });
+
+// 组件卸载时销毁图表
+onUnmounted(() => {
+  if (chartInstance) {
+    chartInstance.dispose();
+  }
+});
 </script>
 
 <style scoped>
@@ -780,6 +1010,10 @@ onMounted(() => {
   margin-bottom: 20px;
 }
 
+.chart-card {
+  margin-bottom: 20px;
+}
+
 .table-card {
   margin-bottom: 20px;
 }
@@ -806,34 +1040,39 @@ onMounted(() => {
   margin: 0 20px;
 }
 
+.analysis-section {
+  padding: 10px;
+}
+
 .section-title {
-  font-size: 18px;
+  font-size: 16px;
   font-weight: bold;
   color: #333;
-  margin-bottom: 15px;
+  margin-bottom: 10px;
   text-align: center;
-  padding-bottom: 10px;
+  padding-bottom: 8px;
   border-bottom: 1px solid #ebeef5;
 }
 
 .metrics-grid {
   display: grid;
   grid-template-columns: repeat(4, 1fr);
-  gap: 15px;
-  margin-top: 15px;
+  gap: 6px;
+  margin-top: 6px;
 }
 
 .metric-card {
   background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
-  border-radius: 8px;
-  padding: 15px 10px;
+  border-radius: 6px;
+  padding: 5px 4px;
   text-align: center;
   transition: all 0.3s ease;
-  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
   border: 1px solid #e9ecef;
   display: flex;
   flex-direction: column;
   justify-content: center;
+  min-height: 45px;
 }
 
 .metric-card:hover {
@@ -863,19 +1102,20 @@ onMounted(() => {
 }
 
 .metric-label {
-  font-size: 12px;
+  font-size: 11px;
   color: #495057;
-  margin-bottom: 8px;
+  margin-bottom: 2px;
   font-weight: 500;
 }
 
 .metric-value {
-  font-size: 16px;
+  font-size: 13px;
   font-weight: bold;
   color: #212529;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
+  line-height: 1.2;
 }
 
 .unit {
@@ -884,9 +1124,17 @@ onMounted(() => {
 }
 
 .p-value {
-  font-size: 11px;
+  font-size: 10px;
   color: #666;
-  margin-top: 3px;
+  margin-top: 1px;
+  display: inline-block;
+  line-height: 1;
+}
+
+.note {
+  font-size: 10px;
+  color: #ff9900;
+  margin-top: 2px;
 }
 
 .model-display {
@@ -932,6 +1180,11 @@ onMounted(() => {
   font-weight: bold;
 }
 
+.chart-container {
+  width: 100%;
+  height: 240px;
+}
+
 @media (max-width: 1200px) {
   .metrics-grid {
     grid-template-columns: repeat(2, 1fr);
@@ -953,4 +1206,4 @@ onMounted(() => {
     grid-template-columns: repeat(2, 1fr);
   }
 }
-</style>
+</style>