yyb
19 小时以前 384fb98c2d78e3ef2d37af24eb9da56a4e8be8da
src/views/costAccounting/energyCosts/index.vue
@@ -112,7 +112,7 @@
            <div class="kpi-left">
              <div class="kpi-label">总能耗成本</div>
              <div class="kpi-value">
                ¥{{ formatMoney(animatedOverview.totalCost) }}
                ¥{{ formatMoney(animatedOverview.totalEnergyCost) }}
              </div>
              <div class="kpi-meta">
                <span
@@ -143,7 +143,7 @@
              <button
                class="kpi-action"
                type="button"
                @click="copyKpi('totalCost')"
                @click="copyKpi('totalEnergyCost')"
              >
                复制
              </button>
@@ -165,7 +165,7 @@
            <div class="kpi-left">
              <div class="kpi-label">生产能耗成本</div>
              <div class="kpi-value">
                ¥{{ formatMoney(animatedOverview.productionCost) }}
                ¥{{ formatMoney(animatedOverview.productEnergyCost) }}
              </div>
              <div class="kpi-meta">
                <span
@@ -196,7 +196,7 @@
              <button
                class="kpi-action"
                type="button"
                @click="copyKpi('productionCost')"
                @click="copyKpi('productEnergyCost')"
              >
                复制
              </button>
@@ -218,7 +218,7 @@
            <div class="kpi-left">
              <div class="kpi-label">办公能耗成本</div>
              <div class="kpi-value">
                ¥{{ formatMoney(animatedOverview.officeCost) }}
                ¥{{ formatMoney(animatedOverview.officeEnergyCost) }}
              </div>
              <div class="kpi-meta">
                <span
@@ -249,7 +249,7 @@
              <button
                class="kpi-action"
                type="button"
                @click="copyKpi('officeCost')"
                @click="copyKpi('officeEnergyCost')"
              >
                复制
              </button>
@@ -270,7 +270,7 @@
            <div class="kpi-left">
              <div class="kpi-label">平均成本</div>
              <div class="kpi-value">
                ¥{{ formatMoney(animatedOverview.avgCost) }}
                ¥{{ formatMoney(animatedOverview.averageEnergyCost) }}
                <span class="kpi-unit"
                  >/{{ statisticsType === "day" ? "日" : "月" }}</span
                >
@@ -286,7 +286,7 @@
              <button
                class="kpi-action"
                type="button"
                @click="copyKpi('avgCost')"
                @click="copyKpi('averageEnergyCost')"
              >
                复制
              </button>
@@ -474,14 +474,14 @@
                        <button
                          class="chart-tool"
                          type="button"
                          @click="downloadChart('consumption', '能耗用量对比')"
                          @click="downloadChart('unit', '能耗用量对比')"
                        >
                          下载
                        </button>
                        <button
                          class="chart-tool"
                          type="button"
                          @click="openBigChart('consumption', '能耗用量对比')"
                          @click="openBigChart('unit', '能耗用量对比')"
                        >
                          大图
                        </button>
@@ -489,13 +489,13 @@
                    </div>
                  </template>
                  <div
                    ref="consumptionChartWrap"
                    ref="unitChartWrap"
                    class="chart-wrap"
                    v-loading="tableLoading"
                  >
                    <div
                      ref="consumptionChart"
                      class="chart-content"
                      ref="unitChart"
                      class="chart-content"
                      v-show="hasTableData"
                    ></div>
                    <div class="chart-empty" v-show="!hasTableData">
@@ -574,7 +574,7 @@
          sortable="custom"
        />
        <el-table-column
          prop="energyType"
          prop="energyTyep"
          label="能耗类型"
          width="100"
          align="center"
@@ -583,8 +583,8 @@
          filter-placement="bottom-end"
        >
          <template #default="scope">
            <el-tag :type="getEnergyTypeType(scope.row.energyType)">
              {{ scope.row.energyType }}
            <el-tag :type="getEnergyTypeType(scope.row.energyTyep)">
              {{ scope.row.energyTyep }}
            </el-tag>
          </template>
        </el-table-column>
@@ -598,28 +598,28 @@
          filter-placement="bottom-end"
        >
          <template #default="scope">
            <el-tag :type="scope.row.type === '生产' ? 'primary' : 'info'">
            <el-tag :type="scope.row.type === '生产' ? 'primary' : 'warning'">
              {{ scope.row.type }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="consumption" label="用量" align="right">
        <el-table-column prop="dosage" label="用量" align="right">
          <template #default="scope">
            <span class="consumption-value">{{
              formatNumber(scope.row.consumption, 2)
            <span class="unit-value">{{
              formatNumber(scope.row.dosage, 2)
            }}</span>
            <span class="consumption-unit">{{ scope.row.unit }}</span>
            <span class="unit-unit">{{ scope.row.unit }}</span>
          </template>
        </el-table-column>
        <el-table-column
          prop="price"
          prop="unitPrice"
          label="单价(元)"
          align="right"
          sortable="custom"
        >
          <template #default="scope">
            <span class="price-value">{{
              formatNumber(scope.row.price, 2)
              formatNumber(scope.row.unitPrice, 2)
            }}</span>
          </template>
        </el-table-column>
@@ -674,7 +674,7 @@
} from "@element-plus/icons-vue";
import * as echarts from "echarts";
// import { energyCostStatistics } from "@/api/costAccounting/energyCosts";
import { energyConsumptionDetailStatistics } from "@/api/energyManagement/energyType";
import { energyConsumptionDetailAccount } from "@/api/energyManagement/energyType";
// 统计维度:day-按日,month-按月
const statisticsType = ref("day");
@@ -705,18 +705,18 @@
// 统计概览
const overview = reactive({
  totalCost: "0.00",
  productionCost: "0.00",
  officeCost: "0.00",
  avgCost: "0.00",
  totalEnergyCost: "0.00",
  productEnergyCost: "0.00",
  officeEnergyCost: "0.00",
  averageEnergyCost: "0.00",
});
const selectedKpi = ref("all"); // all | production | office
const animatedOverview = reactive({
  totalCost: 0,
  productionCost: 0,
  officeCost: 0,
  avgCost: 0,
  totalEnergyCost: 0,
  productEnergyCost: 0,
  officeEnergyCost: 0,
  averageEnergyCost: 0,
});
const formatMoney = (v) => {
@@ -754,10 +754,10 @@
watch(
  () => ({ ...overview }),
  (val) => {
    animateNumber("totalCost", Number.parseFloat(val.totalCost));
    animateNumber("productionCost", Number.parseFloat(val.productionCost));
    animateNumber("officeCost", Number.parseFloat(val.officeCost));
    animateNumber("avgCost", Number.parseFloat(val.avgCost));
    animateNumber("totalEnergyCost", Number.parseFloat(val.totalEnergyCost));
    animateNumber("productEnergyCost", Number.parseFloat(val.productEnergyCost));
    animateNumber("officeEnergyCost", Number.parseFloat(val.officeEnergyCost));
    animateNumber("averageEnergyCost", Number.parseFloat(val.averageEnergyCost));
  },
  { deep: true, immediate: true }
);
@@ -846,10 +846,10 @@
const copyKpi = async (field) => {
  const map = {
    totalCost: animatedOverview.totalCost,
    productionCost: animatedOverview.productionCost,
    officeCost: animatedOverview.officeCost,
    avgCost: animatedOverview.avgCost,
    totalEnergyCost: animatedOverview.totalEnergyCost,
    productEnergyCost: animatedOverview.productEnergyCost,
    officeEnergyCost: animatedOverview.officeEnergyCost,
    averageEnergyCost: animatedOverview.averageEnergyCost,
  };
  const raw = map[field];
  const text = `¥${formatMoney(raw)}`;
@@ -875,13 +875,13 @@
  if (key === "cost") return costChartInstance;
  if (key === "type") return typeChartInstance;
  if (key === "purpose") return purposeChartInstance;
  if (key === "consumption") return consumptionChartInstance;
  if (key === "unit") return unitChartInstance;
  return null;
};
const ensurePanelForChart = (key) => {
  if (key === "cost" || key === "type") chartPanel.value = "core";
  if (key === "purpose" || key === "consumption") chartPanel.value = "advanced";
  if (key === "purpose" || key === "unit") chartPanel.value = "advanced";
};
const downloadChart = (key, title) => {
@@ -967,7 +967,7 @@
  const prop = sortState.prop;
  const direction = sortState.order === "ascending" ? 1 : -1;
  const numFields = new Set(["price", "cost", "consumption"]);
  const numFields = new Set(["price", "cost", "unit"]);
  return data.sort((a, b) => {
    const av = a?.[prop];
@@ -997,7 +997,7 @@
  { text: "办公", value: "办公" },
];
const filterEnergyType = (value, row) => row.energyType === value;
const filterEnergyType = (value, row) => row.energyTyep === value;
const filterEnergyPurpose = (value, row) => row.type === value;
// 分页
@@ -1011,12 +1011,12 @@
const costChart = ref(null);
const typeChart = ref(null);
const purposeChart = ref(null);
const consumptionChart = ref(null);
const unitChart = ref(null);
const costChartWrap = ref(null);
const typeChartWrap = ref(null);
const purposeChartWrap = ref(null);
const consumptionChartWrap = ref(null);
const unitChartWrap = ref(null);
const tableAnchor = ref(null);
@@ -1044,28 +1044,32 @@
let costChartInstance = null;
let typeChartInstance = null;
let purposeChartInstance = null;
let consumptionChartInstance = null;
let unitChartInstance = null;
// 图表区切换:core | advanced | none(点击当前选中可收起)
const chartPanel = ref("core");
const ensureChartsReady = (panel) => {
  if (panel === "core") {
    if (costChart.value && !costChartInstance)
    if (costChart.value && !costChartInstance) {
      costChartInstance = echarts.init(costChart.value);
    if (typeChart.value && !typeChartInstance)
      setTimeout(() => costChartInstance?.resize(), 50);
    }
    if (typeChart.value && !typeChartInstance) {
      typeChartInstance = echarts.init(typeChart.value);
    if (costChartInstance) updateCostChart();
    if (typeChartInstance) updateTypeChart();
      setTimeout(() => typeChartInstance?.resize(), 50);
    }
    return;
  }
  if (panel === "advanced") {
    if (purposeChart.value && !purposeChartInstance)
    if (purposeChart.value && !purposeChartInstance) {
      purposeChartInstance = echarts.init(purposeChart.value);
    if (consumptionChart.value && !consumptionChartInstance)
      consumptionChartInstance = echarts.init(consumptionChart.value);
    if (purposeChartInstance) updatePurposeChart();
    if (consumptionChartInstance) updateConsumptionChart();
      setTimeout(() => purposeChartInstance?.resize(), 50);
    }
    if (unitChart.value && !unitChartInstance) {
      unitChartInstance = echarts.init(unitChart.value);
      setTimeout(() => unitChartInstance?.resize(), 50);
    }
  }
};
@@ -1089,6 +1093,16 @@
watch(chartPanel, (val) => {
  if (val !== "none") resizeChartsAfterExpand();
});
// 监听表格数据变化,确保数据加载后图表正确渲染
watch(tableData, () => {
  nextTick(() => {
    updateCharts();
    nextTick(() => {
      handleResize();
    });
  });
}, { deep: true });
// 获取能耗类型标签类型
const getEnergyTypeType = (type) => {
@@ -1199,10 +1213,10 @@
  const typeCosts = {};
  data.forEach((item) => {
    if (!typeCosts[item.energyType]) {
      typeCosts[item.energyType] = 0;
    if (!typeCosts[item.energyTyep]) {
      typeCosts[item.energyTyep] = 0;
    }
    typeCosts[item.energyType] += parseFloat(item.cost);
    typeCosts[item.energyTyep] += parseFloat(item.cost);
  });
  const chartData = Object.entries(typeCosts).map(([name, value]) => ({
@@ -1334,28 +1348,28 @@
// 更新能耗用量对比图
const updateConsumptionChart = () => {
  const data = tableData.value;
  const consumptionData = {};
  const unitData = {};
  data.forEach((item) => {
    if (!consumptionData[item.energyType]) {
      consumptionData[item.energyType] = {
    if (!unitData[item.energyTyep]) {
      unitData[item.energyTyep] = {
        生产: 0,
        办公: 0,
      };
    }
    if (consumptionData[item.energyType].hasOwnProperty(item.type)) {
      consumptionData[item.energyType][item.type] = parseFloat(
        item.consumption
    if (unitData[item.energyTyep].hasOwnProperty(item.type)) {
      unitData[item.energyTyep][item.type] += parseFloat(
        item.dosage || 0
      );
    }
  });
  const energyTypes = Object.keys(consumptionData);
  const energyTypes = Object.keys(unitData);
  const productionConsumptions = energyTypes.map(
    (type) => consumptionData[type].生产
    (type) => unitData[type].生产
  );
  const officeConsumptions = energyTypes.map(
    (type) => consumptionData[type].办公
    (type) => unitData[type].办公
  );
  const option = {
@@ -1424,7 +1438,7 @@
      },
    ],
  };
  consumptionChartInstance.setOption(option);
  unitChartInstance.setOption(option);
};
// 统计维度切换
@@ -1453,6 +1467,7 @@
// 查询
const handleQuery = () => {
  queryPulse.value = true;
  window.setTimeout(() => {
    queryPulse.value = false;
@@ -1511,36 +1526,36 @@
  }
  // 调用接口获取数据
  energyConsumptionDetailStatistics(params)
  energyConsumptionDetailAccount(params)
    .then((res) => {
      if (res.code === 200) {
        const data = res.data;
        overview.totalCost = data.totalEnergyConsumption || "0";
        overview.productionCost = data.totalEnergyCost || "0";
        overview.avgCost = data.averageConsumption || "0";
        overview.officeCost = data.changeVite || 0;
        overview.totalEnergyCost = data.totalEnergyCost || "0";
        overview.productEnergyCost = data.productEnergyCost || "0";
        overview.officeEnergyCost = data.officeEnergyCost || "0";
        overview.averageEnergyCost = data.averageEnergyCost || "0";
        // 处理表格数据
        tableData.value = data.energyCostDtos || [];
        tableData.value = data.energyConsumptionDetailDtoList || [];
        page.total = tableData.value.length || 0;
      } else {
        ElMessage.error(res.message || "获取数据失败");
        tableData.value = [];
        page.total = 0;
        overview.totalCost = "0.00";
        overview.productionCost = "0.00";
        overview.officeCost = "0.00";
        overview.avgCost = "0.00";
        overview.totalEnergyCost = "0.00";
        overview.productEnergyCost = "0.00";
        overview.officeEnergyCost = "0.00";
        overview.averageEnergyCost = "0.00";
      }
    })
    .catch((err) => {
      ElMessage.error("获取数据异常");
      tableData.value = [];
      page.total = 0;
      overview.totalCost = "0.00";
      overview.productionCost = "0.00";
      overview.officeCost = "0.00";
      overview.avgCost = "0.00";
      overview.totalEnergyCost = "0.00";
      overview.productEnergyCost = "0.00";
      overview.officeEnergyCost = "0.00";
      overview.averageEnergyCost = "0.00";
    })
    .finally(() => {
      tableLoading.value = false;
@@ -1551,10 +1566,19 @@
// 更新所有图表
const updateCharts = () => {
  nextTick(() => {
    // 确保 core 面板的图表始终初始化(因为默认显示的是 core)
    ensureChartsReady("core");
    // 同时也初始化当前可见面板的图表
    if (chartPanel.value === "advanced") {
      ensureChartsReady("advanced");
    }
    // 更新所有已初始化的图表
    if (costChartInstance) updateCostChart();
    if (typeChartInstance) updateTypeChart();
    if (purposeChartInstance) updatePurposeChart();
    if (consumptionChartInstance) updateConsumptionChart();
    if (unitChartInstance) updateConsumptionChart();
  });
};
@@ -1606,7 +1630,7 @@
  costChartInstance && costChartInstance.resize();
  typeChartInstance && typeChartInstance.resize();
  purposeChartInstance && purposeChartInstance.resize();
  consumptionChartInstance && consumptionChartInstance.resize();
  unitChartInstance && unitChartInstance.resize();
};
onMounted(() => {
@@ -2613,12 +2637,12 @@
  width: 100%;
}
.consumption-value {
.unit-value {
  font-weight: bold;
  color: var(--lux-primary);
}
.consumption-unit {
.unit-unit {
  font-size: 12px;
  color: var(--lux-muted);
  margin-left: 2px;
@@ -2745,4 +2769,4 @@
  max-height: 600px;
  opacity: 1;
}
</style>
</style>