zhangwencui
4 天以前 108a4fb5ce13ed06596f7e125e59632e76aafa58
能耗管理模块
已添加6个文件
已修改1个文件
3742 ■■■■■ 文件已修改
src/api/energyManagement/energyType.js 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionPlan/productionPlan.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/energyManagement/energyConsumptionStatistical/index.vue 874 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/energyManagement/energyType/index2.vue 697 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/energyManagement/officeEnergyConsumption/index.vue 696 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/energyManagement/productionEnergyConsumption/index.vue 696 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionPlan/productionPlan/index.vue 715 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/energyManagement/energyType.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
// èƒ½è€—类型
import request from "@/utils/request";
// èƒ½è€—类型-分页查询
export function energyTypeListPage(query) {
  return request({
    url: "/energy/page",
    method: "get",
    params: query,
  });
}
// èƒ½è€—类型-新增修改
export function energyTypeAdd(query) {
  return request({
    url: "/energy",
    method: "post",
    data: query,
  });
}
// èƒ½è€—类型-删除
export function energyTypeDelete(ids) {
  return request({
    url: "/energy/" + ids,
    method: "delete",
  });
}
// èƒ½è€—抄表明细
export function energyConsumptionDetailListPage(query) {
  return request({
    url: "/energyConsumptionDetail/page",
    method: "get",
    params: query,
  });
}
// èƒ½è€—抄表明细-新增修改
export function energyConsumptionDetailAdd(query) {
  return request({
    url: "/energyConsumptionDetail",
    method: "post",
    data: query,
  });
}
// èƒ½è€—抄表明细-删除
export function energyConsumptionDetailDelete(ids) {
  return request({
    url: "/energyConsumptionDetail/" + ids,
    method: "delete",
  });
}
src/api/productionPlan/productionPlan.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
// ç”Ÿäº§è®¢å•页面接口
import request from "@/utils/request";
export function productionPlanListPage(query) {
  return request({
    url: "/productionPlan/listPage",
    method: "get",
    params: query,
  });
}
src/views/energyManagement/energyConsumptionStatistical/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,874 @@
<template>
  <div class="app-container">
    <!-- æœç´¢åŒºåŸŸ -->
    <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="统计维度:">
          <el-radio-group v-model="statisticsType"
                          @change="handleTypeChange">
            <el-radio-button label="day">按日统计</el-radio-button>
            <el-radio-button label="month">按月统计</el-radio-button>
            <el-radio-button label="year">按年统计</el-radio-button>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="能耗用途:">
          <el-select v-model="searchForm.energyType"
                     placeholder="全部"
                     clearable
                     style="width: 120px;"
                     @change="handleQuery">
            <el-option label="全部"
                       value="全部" />
            <el-option label="办公"
                       value="办公" />
            <el-option label="生产"
                       value="生产" />
          </el-select>
        </el-form-item>
        <el-form-item label="时间范围:">
          <el-date-picker v-if="statisticsType === 'day'"
                          v-model="searchForm.dateRange"
                          type="daterange"
                          range-separator="至"
                          start-placeholder="开始日期"
                          end-placeholder="结束日期"
                          value-format="YYYY-MM-DD"
                          style="width: 240px;"
                          @change="handleQuery" />
          <el-date-picker v-else-if="statisticsType === 'month'"
                          v-model="searchForm.monthRange"
                          type="monthrange"
                          range-separator="至"
                          start-placeholder="开始月份"
                          end-placeholder="结束月份"
                          value-format="YYYY-MM"
                          style="width: 240px;"
                          @change="handleQuery" />
          <el-select v-else
                     v-model="searchForm.year"
                     placeholder="选择年份"
                     style="width: 140px;"
                     @change="handleQuery">
            <el-option v-for="year in yearOptions"
                       :key="year"
                       :label="year + 'å¹´'"
                       :value="year" />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery">查询</el-button>
          <el-button @click="handleReset">重置</el-button>
        </el-form-item>
      </el-form>
      <div>
        <el-button type="success"
                   @click="handleExport">导出报表</el-button>
      </div>
    </div>
    <!-- ç»Ÿè®¡æ¦‚览卡片 -->
    <div class="statistics-overview">
      <el-row :gutter="20">
        <el-col :span="6">
          <div class="overview-card total-consumption">
            <div class="overview-icon">
              <el-icon>
                <DataLine />
              </el-icon>
            </div>
            <div class="overview-info">
              <div class="overview-label">总能耗用量</div>
              <div class="overview-value">{{ overview.totalConsumption }} <span class="unit">吨/度/m³</span></div>
            </div>
          </div>
        </el-col>
        <el-col :span="6">
          <div class="overview-card total-amount">
            <div class="overview-icon">
              <el-icon>
                <Money />
              </el-icon>
            </div>
            <div class="overview-info">
              <div class="overview-label">总能耗费用</div>
              <div class="overview-value">Â¥{{ overview.totalAmount }}</div>
            </div>
          </div>
        </el-col>
        <el-col :span="6">
          <div class="overview-card avg-consumption">
            <div class="overview-icon">
              <el-icon>
                <TrendCharts />
              </el-icon>
            </div>
            <div class="overview-info">
              <div class="overview-label">平均用量</div>
              <div class="overview-value">{{ overview.avgConsumption }} <span class="unit">/{{ statisticsType === 'day' ? '日' : statisticsType === 'month' ? '月' : 'å¹´' }}</span></div>
            </div>
          </div>
        </el-col>
        <el-col :span="6">
          <div class="overview-card compare-last">
            <div class="overview-icon">
              <el-icon>
                <Histogram />
              </el-icon>
            </div>
            <div class="overview-info">
              <div class="overview-label">环比变化</div>
              <div class="overview-value"
                   :class="overview.compareRate >= 0 ? 'up' : 'down'">
                <el-icon v-if="overview.compareRate >= 0">
                  <ArrowUp />
                </el-icon>
                <el-icon v-else>
                  <ArrowDown />
                </el-icon>
                {{ Math.abs(overview.compareRate) }}%
              </div>
            </div>
          </div>
        </el-col>
      </el-row>
    </div>
    <!-- å›¾è¡¨åŒºåŸŸ -->
    <div class="charts-container">
      <el-row :gutter="20">
        <el-col :span="12">
          <div class="chart-card">
            <div class="chart-title">能耗用量趋势</div>
            <div ref="consumptionChart"
                 class="chart-content"></div>
          </div>
        </el-col>
        <el-col :span="12">
          <div class="chart-card">
            <div class="chart-title">能耗费用趋势</div>
            <div ref="amountChart"
                 class="chart-content"></div>
          </div>
        </el-col>
      </el-row>
      <el-row :gutter="20"
              style="margin-top: 20px;">
        <el-col :span="12">
          <div class="chart-card">
            <div class="chart-title">能耗类型占比</div>
            <div ref="typeChart"
                 class="chart-content"></div>
          </div>
        </el-col>
        <el-col :span="12">
          <div class="chart-card">
            <div class="chart-title">能耗类型费用占比</div>
            <div ref="amountTypeChart"
                 class="chart-content"></div>
          </div>
        </el-col>
      </el-row>
    </div>
    <!-- æ•°æ®è¡¨æ ¼ -->
    <div class="table-section">
      <div class="section-title">详细数据</div>
      <el-table :data="tableData"
                v-loading="tableLoading"
                border
                stripe>
        <el-table-column type="index"
                         label="序号"
                         width="60"
                         align="center" />
        <el-table-column prop="timePeriod"
                         :label="timeColumnLabel"
                         align="center" />
        <el-table-column prop="waterConsumption"
                         label="用水量(吨)"
                         align="right">
          <template #default="scope">
            <span class="consumption-value">{{ scope.row.waterConsumption }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="waterAmount"
                         label="æ°´è´¹(元)"
                         align="right">
          <template #default="scope">
            <span class="amount-value">{{ scope.row.waterAmount }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="electricityConsumption"
                         label="用电量(度)"
                         align="right">
          <template #default="scope">
            <span class="consumption-value">{{ scope.row.electricityConsumption }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="electricityAmount"
                         label="电费(元)"
                         align="right">
          <template #default="scope">
            <span class="amount-value">{{ scope.row.electricityAmount }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="gasConsumption"
                         label="用气量(m³)"
                         align="right">
          <template #default="scope">
            <span class="consumption-value">{{ scope.row.gasConsumption }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="gasAmount"
                         label="气费(元)"
                         align="right">
          <template #default="scope">
            <span class="amount-value">{{ scope.row.gasAmount }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="totalConsumption"
                         label="合计用量"
                         align="right">
          <template #default="scope">
            <span class="total-value">{{ scope.row.totalConsumption }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="totalAmount"
                         label="合计费用(元)"
                         align="right"
                         fixed="right">
          <template #default="scope">
            <span class="total-amount-value">Â¥{{ scope.row.totalAmount }}</span>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <!-- åˆ†é¡µ -->
    <div class="pagination-container">
      <el-pagination v-model:current-page="page.current"
                     v-model:page-size="page.size"
                     :page-sizes="[10, 20, 50, 100]"
                     :total="page.total"
                     layout="total, sizes, prev, pager, next, jumper"
                     @size-change="handleSizeChange"
                     @current-change="handleCurrentChange" />
    </div>
  </div>
</template>
<script setup>
  import { ref, reactive, onMounted, computed, nextTick } from "vue";
  import { ElMessage } from "element-plus";
  import {
    DataLine,
    Money,
    TrendCharts,
    Histogram,
    ArrowUp,
    ArrowDown,
  } from "@element-plus/icons-vue";
  import * as echarts from "echarts";
  // ç»Ÿè®¡ç»´åº¦ï¼šday-按日,month-按月,year-按年
  const statisticsType = ref("day");
  // æœç´¢è¡¨å•
  const searchForm = reactive({
    energyType: "",
    dateRange: [],
    monthRange: [],
    year: new Date().getFullYear(),
  });
  // å¹´ä»½é€‰é¡¹
  const yearOptions = computed(() => {
    const currentYear = new Date().getFullYear();
    const years = [];
    for (let i = currentYear - 5; i <= currentYear; i++) {
      years.push(i);
    }
    return years.reverse();
  });
  // æ—¶é—´åˆ—标签
  const timeColumnLabel = computed(() => {
    const labels = {
      day: "日期",
      month: "月份",
      year: "年份",
    };
    return labels[statisticsType.value];
  });
  // ç»Ÿè®¡æ¦‚览
  const overview = reactive({
    totalConsumption: "25,680.5",
    totalAmount: "45,280.60",
    avgConsumption: "856.0",
    compareRate: -5.2,
  });
  // è¡¨æ ¼æ•°æ®
  const tableData = ref([]);
  const tableLoading = ref(false);
  // åˆ†é¡µ
  const page = reactive({
    current: 1,
    size: 10,
    total: 0,
  });
  // å›¾è¡¨å¼•用
  const consumptionChart = ref(null);
  const amountChart = ref(null);
  const typeChart = ref(null);
  const amountTypeChart = ref(null);
  // å›¾è¡¨å®žä¾‹
  let consumptionChartInstance = null;
  let amountChartInstance = null;
  let typeChartInstance = null;
  let amountTypeChartInstance = null;
  // ç”Ÿæˆæ¨¡æ‹Ÿæ•°æ®
  const generateMockData = () => {
    const data = [];
    const count =
      statisticsType.value === "day"
        ? 30
        : statisticsType.value === "month"
        ? 12
        : 5;
    for (let i = 0; i < count; i++) {
      const waterConsumption = (Math.random() * 100 + 50).toFixed(2);
      const waterAmount = (waterConsumption * 2.5).toFixed(2);
      const electricityConsumption = (Math.random() * 500 + 200).toFixed(2);
      const electricityAmount = (electricityConsumption * 0.8).toFixed(2);
      const gasConsumption = (Math.random() * 200 + 100).toFixed(2);
      const gasAmount = (gasConsumption * 3.0).toFixed(2);
      let timePeriod;
      if (statisticsType.value === "day") {
        const date = new Date();
        date.setDate(date.getDate() - i);
        timePeriod = date.toISOString().split("T")[0];
      } else if (statisticsType.value === "month") {
        const date = new Date();
        date.setMonth(date.getMonth() - i);
        timePeriod = date.toISOString().slice(0, 7);
      } else {
        timePeriod = (new Date().getFullYear() - i).toString();
      }
      data.push({
        timePeriod,
        waterConsumption,
        waterAmount,
        electricityConsumption,
        electricityAmount,
        gasConsumption,
        gasAmount,
        totalConsumption: (
          parseFloat(waterConsumption) +
          parseFloat(electricityConsumption) +
          parseFloat(gasConsumption)
        ).toFixed(2),
        totalAmount: (
          parseFloat(waterAmount) +
          parseFloat(electricityAmount) +
          parseFloat(gasAmount)
        ).toFixed(2),
      });
    }
    return data.reverse();
  };
  // åˆå§‹åŒ–图表
  const initCharts = () => {
    nextTick(() => {
      // èƒ½è€—用量趋势图
      if (consumptionChart.value) {
        consumptionChartInstance = echarts.init(consumptionChart.value);
        updateConsumptionChart();
      }
      // èƒ½è€—费用趋势图
      if (amountChart.value) {
        amountChartInstance = echarts.init(amountChart.value);
        updateAmountChart();
      }
      // èƒ½è€—类型占比图
      if (typeChart.value) {
        typeChartInstance = echarts.init(typeChart.value);
        updateTypeChart();
      }
      // èƒ½è€—类型费用占比图
      if (amountTypeChart.value) {
        amountTypeChartInstance = echarts.init(amountTypeChart.value);
        updateAmountTypeChart();
      }
    });
  };
  // æ›´æ–°èƒ½è€—用量趋势图
  const updateConsumptionChart = () => {
    const data = tableData.value;
    const option = {
      tooltip: {
        trigger: "axis",
        axisPointer: { type: "cross" },
      },
      legend: {
        data: ["用水量", "用电量", "用气量"],
        bottom: 0,
      },
      grid: {
        left: "3%",
        right: "4%",
        bottom: "15%",
        containLabel: true,
      },
      xAxis: {
        type: "category",
        data: data.map(item => item.timePeriod),
        axisLabel: { rotate: statisticsType.value === "day" ? 45 : 0 },
      },
      yAxis: {
        type: "value",
        name: "用量",
      },
      series: [
        {
          name: "用水量",
          type: "bar",
          data: data.map(item => item.waterConsumption),
          itemStyle: { color: "#409EFF" },
        },
        {
          name: "用电量",
          type: "bar",
          data: data.map(item => item.electricityConsumption),
          itemStyle: { color: "#E6A23C" },
        },
        {
          name: "用气量",
          type: "bar",
          data: data.map(item => item.gasConsumption),
          itemStyle: { color: "#67C23A" },
        },
      ],
    };
    consumptionChartInstance.setOption(option);
  };
  // æ›´æ–°èƒ½è€—费用趋势图
  const updateAmountChart = () => {
    const data = tableData.value;
    const option = {
      tooltip: {
        trigger: "axis",
        axisPointer: { type: "cross" },
      },
      legend: {
        data: ["æ°´è´¹", "电费", "气费"],
        bottom: 0,
      },
      grid: {
        left: "3%",
        right: "4%",
        bottom: "15%",
        containLabel: true,
      },
      xAxis: {
        type: "category",
        data: data.map(item => item.timePeriod),
        axisLabel: { rotate: statisticsType.value === "day" ? 45 : 0 },
      },
      yAxis: {
        type: "value",
        name: "费用(元)",
      },
      series: [
        {
          name: "æ°´è´¹",
          type: "line",
          data: data.map(item => item.waterAmount),
          smooth: true,
          itemStyle: { color: "#409EFF" },
        },
        {
          name: "电费",
          type: "line",
          data: data.map(item => item.electricityAmount),
          smooth: true,
          itemStyle: { color: "#E6A23C" },
        },
        {
          name: "气费",
          type: "line",
          data: data.map(item => item.gasAmount),
          smooth: true,
          itemStyle: { color: "#67C23A" },
        },
      ],
    };
    amountChartInstance.setOption(option);
  };
  // æ›´æ–°èƒ½è€—类型占比图
  const updateTypeChart = () => {
    const data = tableData.value;
    const totalWater = data.reduce(
      (sum, item) => sum + parseFloat(item.waterConsumption),
      0
    );
    const totalElectricity = data.reduce(
      (sum, item) => sum + parseFloat(item.electricityConsumption),
      0
    );
    const totalGas = data.reduce(
      (sum, item) => sum + parseFloat(item.gasConsumption),
      0
    );
    const option = {
      tooltip: {
        trigger: "item",
        formatter: "{a} <br/>{b}: {c} ({d}%)",
      },
      legend: {
        orient: "vertical",
        left: "left",
      },
      series: [
        {
          name: "能耗用量",
          type: "pie",
          radius: ["40%", "70%"],
          avoidLabelOverlap: false,
          itemStyle: {
            borderRadius: 10,
            borderColor: "#fff",
            borderWidth: 2,
          },
          label: {
            show: true,
            formatter: "{b}: {d}%",
          },
          data: [
            {
              value: totalWater.toFixed(2),
              name: "æ°´",
              itemStyle: { color: "#409EFF" },
            },
            {
              value: totalElectricity.toFixed(2),
              name: "电",
              itemStyle: { color: "#E6A23C" },
            },
            {
              value: totalGas.toFixed(2),
              name: "气",
              itemStyle: { color: "#67C23A" },
            },
          ],
        },
      ],
    };
    typeChartInstance.setOption(option);
  };
  // æ›´æ–°èƒ½è€—类型费用占比图
  const updateAmountTypeChart = () => {
    const data = tableData.value;
    const totalWaterAmount = data.reduce(
      (sum, item) => sum + parseFloat(item.waterAmount),
      0
    );
    const totalElectricityAmount = data.reduce(
      (sum, item) => sum + parseFloat(item.electricityAmount),
      0
    );
    const totalGasAmount = data.reduce(
      (sum, item) => sum + parseFloat(item.gasAmount),
      0
    );
    const option = {
      tooltip: {
        trigger: "item",
        formatter: "{a} <br/>{b}: Â¥{c} ({d}%)",
      },
      legend: {
        orient: "vertical",
        left: "left",
      },
      series: [
        {
          name: "能耗费用",
          type: "pie",
          radius: "60%",
          data: [
            {
              value: totalWaterAmount.toFixed(2),
              name: "æ°´è´¹",
              itemStyle: { color: "#409EFF" },
            },
            {
              value: totalElectricityAmount.toFixed(2),
              name: "电费",
              itemStyle: { color: "#E6A23C" },
            },
            {
              value: totalGasAmount.toFixed(2),
              name: "气费",
              itemStyle: { color: "#67C23A" },
            },
          ],
          emphasis: {
            itemStyle: {
              shadowBlur: 10,
              shadowOffsetX: 0,
              shadowColor: "rgba(0, 0, 0, 0.5)",
            },
          },
        },
      ],
    };
    amountTypeChartInstance.setOption(option);
  };
  // ç»Ÿè®¡ç»´åº¦åˆ‡æ¢
  const handleTypeChange = () => {
    // é‡ç½®æ—¶é—´èŒƒå›´
    searchForm.dateRange = [];
    searchForm.monthRange = [];
    searchForm.year = new Date().getFullYear();
    page.current = 1;
    handleQuery();
  };
  // æŸ¥è¯¢
  const handleQuery = () => {
    tableLoading.value = true;
    setTimeout(() => {
      const data = generateMockData();
      tableData.value = data;
      page.total = data.length;
      tableLoading.value = false;
      updateCharts();
    }, 300);
  };
  // æ›´æ–°æ‰€æœ‰å›¾è¡¨
  const updateCharts = () => {
    nextTick(() => {
      if (consumptionChartInstance) updateConsumptionChart();
      if (amountChartInstance) updateAmountChart();
      if (typeChartInstance) updateTypeChart();
      if (amountTypeChartInstance) updateAmountTypeChart();
    });
  };
  // é‡ç½®
  const handleReset = () => {
    searchForm.energyType = "";
    searchForm.dateRange = [];
    searchForm.monthRange = [];
    searchForm.year = new Date().getFullYear();
    page.current = 1;
    handleQuery();
  };
  // å¯¼å‡º
  const handleExport = () => {
    ElMessage.success("报表导出成功");
  };
  // åˆ†é¡µå¤§å°å˜åŒ–
  const handleSizeChange = val => {
    page.size = val;
  };
  // é¡µç å˜åŒ–
  const handleCurrentChange = val => {
    page.current = val;
  };
  // çª—口大小变化时重新渲染图表
  const handleResize = () => {
    consumptionChartInstance && consumptionChartInstance.resize();
    amountChartInstance && amountChartInstance.resize();
    typeChartInstance && typeChartInstance.resize();
    amountTypeChartInstance && amountTypeChartInstance.resize();
  };
  onMounted(() => {
    handleQuery();
    initCharts();
    window.addEventListener("resize", handleResize);
  });
</script>
<style scoped lang="scss">
  .app-container {
    padding: 20px;
  }
  .search_form {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    padding: 15px;
    background-color: #f5f7fa;
    border-radius: 8px;
  }
  .statistics-overview {
    margin-bottom: 20px;
  }
  .overview-card {
    display: flex;
    align-items: center;
    padding: 20px;
    border-radius: 8px;
    background: #fff;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    &.total-consumption {
      border-left: 4px solid #409eff;
    }
    &.total-amount {
      border-left: 4px solid #67c23a;
    }
    &.avg-consumption {
      border-left: 4px solid #e6a23c;
    }
    &.compare-last {
      border-left: 4px solid #909399;
    }
    .overview-icon {
      width: 50px;
      height: 50px;
      display: flex;
      align-items: center;
      justify-content: center;
      background: #f5f7fa;
      border-radius: 50%;
      margin-right: 15px;
      .el-icon {
        font-size: 24px;
        color: #409eff;
      }
    }
    .overview-info {
      flex: 1;
      .overview-label {
        font-size: 14px;
        color: #909399;
        margin-bottom: 5px;
      }
      .overview-value {
        font-size: 24px;
        font-weight: bold;
        color: #303133;
        .unit {
          font-size: 14px;
          font-weight: normal;
          color: #909399;
        }
        &.up {
          color: #67c23a;
        }
        &.down {
          color: #f56c6c;
        }
        .el-icon {
          font-size: 16px;
          vertical-align: middle;
        }
      }
    }
  }
  .charts-container {
    margin-bottom: 20px;
  }
  .chart-card {
    background: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    padding: 20px;
    .chart-title {
      font-size: 16px;
      font-weight: bold;
      color: #303133;
      margin-bottom: 15px;
      padding-bottom: 10px;
      border-bottom: 1px solid #ebeef5;
    }
    .chart-content {
      height: 300px;
    }
  }
  .table-section {
    background: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    padding: 20px;
    margin-bottom: 20px;
    .section-title {
      font-size: 16px;
      font-weight: bold;
      color: #303133;
      margin-bottom: 15px;
      padding-bottom: 10px;
      border-bottom: 1px solid #ebeef5;
    }
  }
  .consumption-value {
    font-weight: bold;
    color: #409eff;
  }
  .amount-value {
    font-weight: bold;
    color: #67c23a;
  }
  .total-value {
    font-weight: bold;
    color: #e6a23c;
  }
  .total-amount-value {
    font-weight: bold;
    color: #f56c6c;
    font-size: 14px;
  }
  .pagination-container {
    display: flex;
    justify-content: flex-end;
  }
</style>
src/views/energyManagement/energyType/index2.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,697 @@
<template>
  <div class="app-container">
    <!-- æœç´¢åŒºåŸŸ -->
    <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="能源类型:">
          <el-select v-model="searchForm.energyTyep"
                     placeholder="全部"
                     clearable
                     style="width: 120px;"
                     @change="handleQuery">
            <el-option label="气"
                       value="gas" />
            <el-option label="电"
                       value="electricity" />
            <el-option label="æ°´"
                       value="water" />
          </el-select>
        </el-form-item>
        <el-form-item label="能源名称:">
          <el-input v-model="searchForm.energyName"
                    placeholder="请输入"
                    clearable
                    style="width: 160px;"
                    @keyup.enter="handleQuery" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery">查询</el-button>
          <el-button @click="handleReset">重置</el-button>
        </el-form-item>
      </el-form>
      <div>
        <el-button type="primary"
                   @click="handleAdd">新增</el-button>
        <!-- <el-button type="success"
                   @click="handleImport">导入</el-button> -->
        <el-button type="warning"
                   @click="handleExport">导出</el-button>
      </div>
    </div>
    <!-- æ•°æ®è¡¨æ ¼ -->
    <div class="table_list">
      <el-table :data="tableData"
                v-loading="tableLoading"
                border
                height="calc(100vh - 350px)"
                stripe>
        <el-table-column type="selection"
                         width="55"
                         align="center" />
        <el-table-column prop="energyTyep"
                         label="能源类型"
                         width="100"
                         align="center">
          <template #default="scope">
            <el-tag :type="getEnergyTypeType(scope.row.energyTyep)">
              {{ scope.row.energyTyep }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="energyName"
                         label="能源名称"
                         align="center" />
        <el-table-column prop="unitPrice"
                         label="单价"
                         sortable
                         align="right">
          <template #default="scope">
            <span>{{ scope.row.unitPrice }}</span>
          </template>
        </el-table-column>
        <el-table-column label="备注"
                         prop="remark"
                         align="center" />
        <el-table-column prop="unit"
                         label="单位"
                         align="center" />
        <el-table-column prop="createUserName"
                         label="创建人"
                         align="center" />
        <el-table-column prop="createTime"
                         label="创建时间"
                         sortable
                         align="center" />
        <el-table-column label="操作"
                         align="center"
                         fixed="right">
          <template #default="scope">
            <el-button type="primary"
                       link
                       @click="handleDetail(scope.row)">详情</el-button>
            <el-button type="danger"
                       link
                       @click="handleDelete(scope.row)">删除</el-button>
            <el-button type="primary"
                       link
                       @click="handleEdit(scope.row)">编辑</el-button>
          </template>
        </el-table-column>
      </el-table>
      <!-- åˆ†é¡µ -->
      <div class="pagination-container">
        <el-pagination v-model:current-page="page.current"
                       v-model:page-size="page.size"
                       :page-sizes="[10, 20, 50, 100]"
                       :total="page.total"
                       layout="total, sizes, prev, pager, next, jumper"
                       @size-change="handleSizeChange"
                       @current-change="handleCurrentChange" />
      </div>
    </div>
    <!-- æ–°å¢ž/编辑弹窗 -->
    <el-dialog v-model="dialogVisible"
               :title="dialogTitle"
               width="600px">
      <el-form :model="form"
               :rules="rules"
               ref="formRef"
               label-width="100px">
        <el-form-item label="能源名称"
                      prop="energyName">
          <el-input v-model="form.energyName"
                    placeholder="请输入" />
        </el-form-item>
        <el-form-item label="能源类型"
                      prop="energyTyep">
          <el-select v-model="form.energyTyep"
                     placeholder="请选择"
                     style="width: 100%;">
            <el-option label="气"
                       value="气" />
            <el-option label="电"
                       value="电" />
            <el-option label="æ°´"
                       value="æ°´" />
          </el-select>
        </el-form-item>
        <el-form-item label="备注"
                      prop="remark">
          <el-input v-model="form.remark"
                    type="textarea"
                    rows="3" />
        </el-form-item>
        <el-form-item label="单位"
                      prop="unit">
          <el-input v-model="form.unit"
                    placeholder="请输入" />
        </el-form-item>
        <el-form-item label="单价"
                      prop="unitPrice">
          <el-input-number v-model="form.unitPrice"
                           :precision="2"
                           :min="0"
                           style="width: 100%;" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="handleSubmit">确定</el-button>
          <el-button @click="dialogVisible = false">取消</el-button>
        </span>
      </template>
    </el-dialog>
    <!-- è¯¦æƒ…弹窗 -->
    <el-dialog v-model="detailDialogVisible"
               title="能源类型详情"
               width="600px">
      <el-form :model="detailForm"
               label-width="100px"
               disabled>
        <el-form-item label="能源类型">
          <el-tag :type="getEnergyTypeType(detailForm.energyTyep)">
            {{ detailForm.energyTyep }}
          </el-tag>
        </el-form-item>
        <el-form-item label="能源名称">
          <el-input v-model="detailForm.energyName" />
        </el-form-item>
        <el-form-item label="单价">
          <el-input v-model="detailForm.unitPrice" />
        </el-form-item>
        <el-form-item label="单位">
          <el-input v-model="detailForm.unit" />
        </el-form-item>
        <el-form-item label="备注">
          <el-input v-model="detailForm.remark"
                    type="textarea"
                    rows="3" />
        </el-form-item>
        <el-form-item label="创建人">
          <el-input v-model="detailForm.createUserName" />
        </el-form-item>
        <el-form-item label="创建时间">
          <el-input v-model="detailForm.createTime" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="detailDialogVisible = false">关闭</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
  import { ref, reactive, onMounted, getCurrentInstance } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import {
    energyTypeListPage,
    energyTypeAdd,
    energyTypeDelete,
  } from "@/api/energyManagement/energyType";
  import { More } from "@element-plus/icons-vue";
  // æœç´¢è¡¨å•
  const searchForm = reactive({
    energyTyep: "",
    energyName: "",
  });
  // è¡¨æ ¼æ•°æ®
  const tableData = ref([]);
  const tableLoading = ref(false);
  // åˆ†é¡µ
  const page = reactive({
    current: 1,
    size: 10,
    total: 0,
  });
  // å¼¹çª—控制
  const dialogVisible = ref(false);
  const dialogTitle = ref("新增能源类型");
  const formRef = ref(null);
  const isEdit = ref(false);
  const currentId = ref(null);
  // è¡¨å•数据
  const form = reactive({
    energyName: "",
    energyTyep: "",
    id: "",
    remark: "",
    unit: "",
    unitPrice: 0,
  });
  const { proxy } = getCurrentInstance();
  // è¡¨å•校验规则
  const rules = {
    energyTyep: [
      { required: true, message: "请选择能源类型", trigger: "change" },
    ],
    energyName: [{ required: true, message: "请输入能源名称", trigger: "blur" }],
    unitPrice: [{ required: true, message: "请输入单价", trigger: "blur" }],
    unit: [{ required: true, message: "请输入单位", trigger: "blur" }],
  };
  // è¯¦æƒ…弹窗
  const detailDialogVisible = ref(false);
  const detailForm = reactive({
    energyTyep: "",
    energyName: "",
    unitPrice: "",
    unit: "",
    remark: "",
    createUserName: "",
    createUserNameOrganization: "",
    createTime: "",
    updateTime: "",
  });
  // èŽ·å–èƒ½æºç±»åž‹æ ·å¼
  const getEnergyTypeType = type => {
    const typeMap = {
      æ°”: "success",
      ç”µ: "warning",
      æ°´: "primary",
    };
    return typeMap[type] || "info";
  };
  // ç”Ÿæˆæ¨¡æ‹Ÿæ•°æ®
  const generateMockData = () => {
    const data = [
      {
        id: 1,
        energyTyep: "气",
        energyName: "蒸汽",
        unitPrice: "0.00",
        unit: "T",
        createUserName: "白洛已痴画",
        createUserNameOrganization: "宁夏中银绒业实业集团有限公司",
        createTime: "2022-11-09 14:14:05",
        updateTime: "2022-11-10 18:14:48",
        remark: "",
      },
      {
        id: 2,
        energyTyep: "电",
        energyName: "电量",
        unitPrice: "0.00",
        unit: "度",
        createUserName: "王琪已痴画",
        createUserNameOrganization: "宁夏中银绒业实业集团有限公司",
        createTime: "2022-10-08 17:27:27",
        updateTime: "2022-11-10 18:14:40",
        remark: "",
      },
      {
        id: 3,
        energyTyep: "æ°´",
        energyName: "中水",
        unitPrice: "0.00",
        unit: "m³",
        createUserName: "王琪已痴画",
        createUserNameOrganization: "宁夏中银绒业实业集团有限公司",
        createTime: "2022-10-08 10:03:03",
        updateTime: "2022-11-10 18:10:31",
        remark: "",
      },
    ];
    return data;
  };
  // æŸ¥è¯¢
  const handleQuery = () => {
    tableLoading.value = true;
    let params = {
      current: page.current,
      size: page.size,
      energyTyep: searchForm.energyTyep,
      energyName: searchForm.energyName,
    };
    energyTypeListPage(params)
      .then(res => {
        if (res.code === 200) {
          tableData.value = res.data.records;
          page.total = res.data.total;
          tableLoading.value = false;
        } else {
          ElMessage.error(res.message || "查询失败");
          tableLoading.value = false;
        }
      })
      .catch(error => {
        ElMessage.error("网络错误,请稍后重试");
        tableLoading.value = false;
      });
  };
  // é‡ç½®
  const handleReset = () => {
    searchForm.energyTyep = "";
    searchForm.energyName = "";
    page.current = 1;
    handleQuery();
  };
  // æ–°å¢ž
  const handleAdd = () => {
    isEdit.value = false;
    currentId.value = null;
    dialogTitle.value = "新增能源类型";
    resetForm();
    dialogVisible.value = true;
  };
  // ç¼–辑
  const handleEdit = row => {
    isEdit.value = true;
    currentId.value = row.id;
    dialogTitle.value = "编辑能源类型";
    Object.assign(form, row);
    dialogVisible.value = true;
  };
  // è¯¦æƒ…
  const handleDetail = row => {
    Object.assign(detailForm, row);
    detailDialogVisible.value = true;
  };
  // åˆ é™¤
  const handleDelete = row => {
    ElMessageBox.confirm("确定要删除该能源类型吗?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      tableLoading.value = true;
      energyTypeDelete([row.id])
        .then(res => {
          if (res.code === 200) {
            ElMessage.success("删除成功");
            handleQuery();
          } else {
            ElMessage.error(res.message || "删除失败");
          }
        })
        .catch(error => {
          ElMessage.error("网络错误,请稍后重试");
        })
        .finally(() => {
          tableLoading.value = false;
        });
    });
  };
  // å¤åˆ¶
  const handleCopy = row => {
    isEdit.value = false;
    currentId.value = null;
    dialogTitle.value = "复制能源类型";
    Object.assign(form, row);
    delete form.id;
    dialogVisible.value = true;
    ElMessage.success("已复制到表单");
  };
  // é‡ç½®è¡¨å•
  const resetForm = () => {
    form.energyName = "";
    form.energyTyep = "";
    form.id = "";
    form.remark = "";
    form.unit = "";
    form.unitPrice = 0;
  };
  // æäº¤è¡¨å•
  const handleSubmit = () => {
    formRef.value.validate(valid => {
      if (valid) {
        tableLoading.value = true;
        energyTypeAdd(form)
          .then(res => {
            if (res.code === 200) {
              ElMessage.success(isEdit.value ? "编辑成功" : "新增成功");
              dialogVisible.value = false;
              handleQuery();
            } else {
              ElMessage.error(res.message || "操作失败");
            }
          })
          .catch(error => {
            ElMessage.error("网络错误,请稍后重试");
          })
          .finally(() => {
            tableLoading.value = false;
          });
      }
    });
  };
  // å¯¼å…¥
  const handleImport = () => {
    ElMessage.success("导入成功");
  };
  // å¯¼å‡º
  const handleExport = () => {
    // proxy.download("/energy/export", { ...searchForm.value }, "能耗类型.xlsx");
    proxy.download("/energy/export", {}, "能耗类型.xlsx");
  };
  // åˆ†é¡µå¤§å°å˜åŒ–
  const handleSizeChange = val => {
    page.size = val;
    handleQuery();
  };
  // é¡µç å˜åŒ–
  const handleCurrentChange = val => {
    page.current = val;
    handleQuery();
  };
  onMounted(() => {
    handleQuery();
  });
</script>
<style scoped lang="scss">
  .app-container {
    padding: 24px;
    background-color: #f0f2f5;
    min-height: calc(100vh - 48px);
  }
  .search_form {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 24px;
    padding: 20px;
    background-color: #ffffff;
    border-radius: 6px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
    transition: all 0.3s ease;
    &:hover {
      box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
    }
  }
  .table_list {
    // margin-bottom: 24px;
    background-color: #ffffff;
    border-radius: 6px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
    overflow: hidden;
    height: calc(100vh - 250px);
  }
  :deep(.el-table) {
    border: none;
    border-radius: 6px;
    overflow: hidden;
    .el-table__header-wrapper {
      background-color: #fafafa;
      th {
        background-color: #fafafa;
        font-weight: 600;
        color: #303133;
        border-bottom: 1px solid #ebeef5;
        padding: 14px 0;
      }
    }
    .el-table__body-wrapper {
      tr {
        transition: all 0.3s ease;
        &:hover {
          background-color: #f5f7fa;
        }
        td {
          border-bottom: 1px solid #ebeef5;
          padding: 12px 0;
        }
      }
      tr.current-row {
        background-color: #ecf5ff;
      }
    }
    .el-table__empty-block {
      padding: 40px 0;
    }
  }
  .pagination-container {
    display: flex;
    justify-content: flex-end;
    padding: 16px 20px;
    background-color: #ffffff;
    border-top: 1px solid #ebeef5;
    border-radius: 0 0 12px 12px;
  }
  :deep(.el-button) {
    transition: all 0.3s ease;
    &:hover {
      transform: translateY(-1px);
    }
  }
  :deep(.el-dialog) {
    border-radius: 6px;
    overflow: hidden;
    .el-dialog__header {
      background-color: #fafafa;
      border-bottom: 1px solid #ebeef5;
      padding: 20px 24px;
      .el-dialog__title {
        font-size: 16px;
        font-weight: 600;
        color: #303133;
      }
    }
    .el-dialog__body {
      padding: 24px;
    }
    .el-dialog__footer {
      padding: 16px 24px;
      border-top: 1px solid #ebeef5;
      background-color: #fafafa;
    }
  }
  :deep(.el-form) {
    .el-form-item {
      margin-bottom: 20px;
      .el-form-item__label {
        font-weight: 500;
        color: #303133;
      }
      .el-input,
      .el-select,
      .el-date-picker,
      .el-input-number {
        width: 100%;
        // .el-input__inner {
        //   border-radius: 6px;
        //   border: 1px solid #dcdfe6;
        //   transition: all 0.3s ease;
        //   &:focus {
        //     border-color: #409eff;
        //     box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
        //   }
        // }
      }
    }
  }
  :deep(.el-tag) {
    border-radius: 4px;
    padding: 2px 8px;
    font-size: 12px;
  }
  @media (max-width: 768px) {
    .app-container {
      padding: 16px;
    }
    .search_form {
      flex-direction: column;
      align-items: flex-start;
      gap: 12px;
      .el-form {
        width: 100%;
        .el-form-item {
          width: 100%;
        }
      }
      > div {
        width: 100%;
        display: flex;
        gap: 12px;
        .el-button {
          flex: 1;
        }
      }
    }
    :deep(.el-table) {
      th,
      td {
        padding: 10px 0;
        font-size: 12px;
      }
    }
    :deep(.el-dialog) {
      width: 90% !important;
      margin: 20px auto !important;
    }
  }
  .consumption-value {
    font-weight: bold;
    color: #409eff;
  }
  .consumption-unit {
    font-size: 12px;
    color: #909399;
    margin-left: 4px;
  }
  .search_form {
    :deep(.el-form-item) {
      margin-bottom: 0px !important;
    }
  }
</style>
src/views/energyManagement/officeEnergyConsumption/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,696 @@
<template>
  <div class="app-container">
    <!-- æœç´¢åŒºåŸŸ -->
    <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="能耗类型:">
          <el-select v-model="searchForm.energyType"
                     placeholder="请选择"
                     clearable
                     style="width: 140px;"
                     @change="handleQuery">
            <el-option label="æ°´"
                       value="water" />
            <el-option label="电"
                       value="electricity" />
            <el-option label="气"
                       value="gas" />
          </el-select>
        </el-form-item>
        <!-- <el-form-item label="日期范围:">
          <el-date-picker v-model="searchForm.dateRange"
                          type="daterange"
                          range-separator="至"
                          start-placeholder="开始日期"
                          end-placeholder="结束日期"
                          value-format="YYYY-MM-DD"
                          style="width: 240px;"
                          @change="handleQuery" />
        </el-form-item> -->
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery">搜索</el-button>
          <el-button @click="handleReset">重置</el-button>
        </el-form-item>
      </el-form>
      <div>
        <!-- <el-button type="primary"
                   @click="handleAdd">新增</el-button> -->
        <el-button type="success"
                   @click="handleExport">导出</el-button>
      </div>
    </div>
    <!-- æ•°æ®è¡¨æ ¼ -->
    <div class="table_list">
      <el-table :data="tableData"
                v-loading="tableLoading"
                border
                height="calc(100vh - 350px)"
                stripe>
        <el-table-column type="index"
                         label="序号"
                         width="60"
                         align="center" />
        <el-table-column prop="meterReadingDate"
                         label="抄表日期"
                         width="120"
                         sortable
                         align="center" />
        <el-table-column prop="energyType"
                         label="能源类型"
                         width="100"
                         align="center">
          <template #default="scope">
            <el-tag :type="getEnergyTypeType(scope.row.energyTyep)">
              {{ scope.row.energyTyep }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="energyName"
                         label="能源名称"
                         align="center" />
        <el-table-column prop="meterReadingLocation"
                         label="抄表位置"
                         align="center" />
        <el-table-column prop="startCode"
                         label="起码"
                         sortable
                         align="right" />
        <el-table-column prop="stopCode"
                         label="止码"
                         sortable
                         align="right" />
        <el-table-column prop="dosage"
                         label="用量"
                         sortable
                         align="right">
          <template #default="scope">
            <span class="consumption-value">{{ scope.row.dosage }}</span>
            <span class="consumption-unit">{{ scope.row.unit }}</span>
          </template>
        </el-table-column>
        <!-- <el-table-column prop="unit"
                         label="单位"
                         width="80"
                         align="center" /> -->
        <!-- <el-table-column prop="energyId"
                         label="能源类型ID"
                         width="120"
                         align="center" /> -->
        <el-table-column prop="remark"
                         label="备注"
                         min-width="150"
                         show-overflow-tooltip />
        <!-- <el-table-column label="操作"
                         width="180"
                         align="center"
                         fixed="right">
          <template #default="scope">
            <el-button type="primary"
                       link
                       @click="handleEdit(scope.row)">编辑</el-button>
            <el-button type="danger"
                       link
                       @click="handleDelete(scope.row)">删除</el-button>
          </template>
        </el-table-column> -->
      </el-table>
      <div class="pagination-container">
        <el-pagination v-model:current-page="page.current"
                       v-model:page-size="page.size"
                       :page-sizes="[10, 20, 50, 100]"
                       :total="page.total"
                       layout="total, sizes, prev, pager, next, jumper"
                       @size-change="handleSizeChange"
                       @current-change="handleCurrentChange" />
      </div>
    </div>
    <!-- åˆ†é¡µ -->
    <!-- æ–°å¢ž/编辑弹窗 -->
    <el-dialog v-model="dialogVisible"
               :title="dialogTitle"
               width="600px">
      <el-form :model="form"
               :rules="rules"
               ref="formRef"
               label-width="120px">
        <el-form-item label="抄表日期"
                      prop="meterReadingDate">
          <el-date-picker v-model="form.meterReadingDate"
                          type="date"
                          placeholder="请选择"
                          value-format="YYYY-MM-DD"
                          style="width: 100%;" />
        </el-form-item>
        <el-form-item label="能源类型"
                      prop="energyType">
          <el-select v-model="form.energyType"
                     placeholder="请选择"
                     style="width: 100%;"
                     @change="handleEnergyTypeChange">
            <el-option v-for="item in energyTypeList"
                       :key="item.id"
                       :label="item.energyName"
                       :value="item.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="抄表位置"
                      prop="meterReadingLocation">
          <el-input v-model="form.meterReadingLocation"
                    placeholder="请输入" />
        </el-form-item>
        <el-form-item label="起码"
                      prop="startCode">
          <el-input-number v-model="form.startCode"
                           :min="0"
                           style="width: 100%;" />
        </el-form-item>
        <el-form-item label="止码"
                      prop="stopCode">
          <el-input-number v-model="form.stopCode"
                           :min="0"
                           style="width: 100%;" />
        </el-form-item>
        <el-form-item label="用量"
                      prop="dosage">
          <el-input-number v-model="form.dosage"
                           :min="0"
                           style="width: 100%;" />
        </el-form-item>
        <el-form-item label="单位"
                      prop="unit">
          <el-input v-model="form.unit"
                    disabled
                    placeholder="请输入" />
        </el-form-item>
        <el-form-item label="备注"
                      prop="remark">
          <el-input v-model="form.remark"
                    type="textarea"
                    rows="3" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary"
                     @click="handleSubmit">确定</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
  import { ref, reactive, onMounted, getCurrentInstance } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import { Watermelon, Lightning } from "@element-plus/icons-vue";
  import {
    energyConsumptionDetailListPage,
    energyConsumptionDetailAdd,
    energyConsumptionDetailDelete,
    energyTypeListPage,
  } from "@/api/energyManagement/energyType";
  // æœç´¢è¡¨å•
  const searchForm = reactive({
    energyType: "",
    dateRange: [],
  });
  const { proxy } = getCurrentInstance();
  // ç»Ÿè®¡æ•°æ®
  const statistics = reactive({
    waterTotal: 1256.8,
    waterAmount: "3,142.00",
    electricityTotal: 8520.5,
    electricityAmount: "6,816.40",
    gasTotal: 3200.6,
    gasAmount: "9,601.80",
  });
  // è¡¨æ ¼æ•°æ®
  const tableData = ref([]);
  const tableLoading = ref(false);
  // åˆ†é¡µ
  const page = reactive({
    current: 1,
    size: 10,
    total: 0,
  });
  // å¼¹çª—控制
  const dialogVisible = ref(false);
  const dialogTitle = ref("新增能耗记录");
  const formRef = ref(null);
  const isEdit = ref(false);
  const currentId = ref(null);
  // è¡¨å•数据
  const form = reactive({
    meterReadingDate: "",
    energyType: "",
    energyName: "",
    energyId: "",
    meterReadingLocation: "",
    startCode: 0,
    stopCode: 0,
    dosage: 0,
    unit: "",
    remark: "",
    type: "办公",
  });
  // è¡¨å•校验规则
  const rules = {
    meterReadingDate: [
      { required: true, message: "请选择抄表日期", trigger: "change" },
    ],
    energyType: [
      { required: true, message: "请选择能源类型", trigger: "change" },
    ],
    energyName: [
      { required: true, message: "请选择能源名称", trigger: "change" },
    ],
    energyId: [{ required: true, message: "请选择能源类型", trigger: "change" }],
    meterReadingLocation: [
      { required: true, message: "请输入抄表位置", trigger: "blur" },
    ],
    startCode: [{ required: true, message: "请输入起码", trigger: "blur" }],
    stopCode: [{ required: true, message: "请输入止码", trigger: "blur" }],
    dosage: [{ required: true, message: "请输入用量", trigger: "blur" }],
    unit: [{ required: true, message: "请输入单位", trigger: "blur" }],
  };
  // èŽ·å–èƒ½è€—ç±»åž‹æ ·å¼
  const getEnergyTypeType = type => {
    const typeMap = {
      æ°´: "primary",
      ç”µ: "warning",
      æ°”: "success",
      water: "primary",
      electricity: "warning",
      gas: "success",
    };
    return typeMap[type] || "info";
  };
  // èŽ·å–èƒ½è€—ç±»åž‹æ–‡æœ¬
  const getEnergyTypeText = type => {
    const textMap = {
      water: "æ°´",
      electricity: "电",
      gas: "气",
    };
    return textMap[type] || type;
  };
  // æŸ¥è¯¢
  const handleQuery = () => {
    tableLoading.value = true;
    const params = {
      current: page.current,
      size: page.size,
      type: "办公",
      // energyType: searchForm.energyType,
      // startDate:
      //   searchForm.dateRange && searchForm.dateRange.length === 2
      //     ? searchForm.dateRange[0]
      //     : null,
      // endDate:
      //   searchForm.dateRange && searchForm.dateRange.length === 2
      //     ? searchForm.dateRange[1]
      //     : null,
    };
    energyConsumptionDetailListPage(params)
      .then(res => {
        if (res.code === 200) {
          tableData.value = res.data.records;
          page.total = res.data.total;
        } else {
          ElMessage.error(res.message || "查询失败");
        }
      })
      .catch(error => {
        ElMessage.error("网络错误,请稍后重试");
      })
      .finally(() => {
        tableLoading.value = false;
      });
  };
  const energyTypeList = ref([]);
  const getEnergyTypeList = () => {
    energyTypeListPage({ current: -1, size: -1 })
      .then(res => {
        if (res.code === 200) {
          energyTypeList.value = res.data.records;
        } else {
          ElMessage.error(res.message || "查询失败");
        }
      })
      .catch(error => {
        ElMessage.error("网络错误,请稍后重试");
      });
  };
  // é‡ç½®
  const handleReset = () => {
    searchForm.energyType = "";
    searchForm.dateRange = [];
    page.current = 1;
    handleQuery();
  };
  // å¯¼å‡º
  const handleExport = () => {
    proxy.download(
      "/energyConsumptionDetail/export",
      { type: "办公" },
      "能耗明细.xlsx"
    );
  };
  // åˆ†é¡µå¤§å°å˜åŒ–
  const handleSizeChange = val => {
    page.size = val;
    handleQuery();
  };
  // é¡µç å˜åŒ–
  const handleCurrentChange = val => {
    page.current = val;
    handleQuery();
  };
  // å¤„理能源类型变化
  const handleEnergyTypeChange = value => {
    const selectedType = energyTypeList.value.find(item => item.id === value);
    if (selectedType) {
      form.energyName = selectedType.energyName;
      form.energyId = selectedType.id;
      form.unit = selectedType.unit;
    }
  };
  // æ–°å¢ž
  const handleAdd = () => {
    isEdit.value = false;
    currentId.value = null;
    dialogTitle.value = "新增能耗记录";
    resetForm();
    dialogVisible.value = true;
  };
  // ç¼–辑
  const handleEdit = row => {
    isEdit.value = true;
    currentId.value = row.id;
    dialogTitle.value = "编辑能耗记录";
    Object.assign(form, row);
    dialogVisible.value = true;
  };
  // åˆ é™¤
  const handleDelete = row => {
    ElMessageBox.confirm("确定要删除该能耗记录吗?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      tableLoading.value = true;
      energyConsumptionDetailDelete([row.id])
        .then(res => {
          if (res.code === 200) {
            ElMessage.success("删除成功");
            handleQuery();
          } else {
            ElMessage.error(res.message || "删除失败");
          }
        })
        .catch(error => {
          ElMessage.error("网络错误,请稍后重试");
        })
        .finally(() => {
          tableLoading.value = false;
        });
    });
  };
  // é‡ç½®è¡¨å•
  const resetForm = () => {
    form.meterReadingDate = "";
    form.energyType = "";
    form.energyName = "";
    form.energyId = "";
    form.meterReadingLocation = "";
    form.startCode = 0;
    form.stopCode = 0;
    form.dosage = 0;
    form.unit = "";
    form.remark = "";
    form.type = "办公";
  };
  // æäº¤è¡¨å•
  const handleSubmit = () => {
    formRef.value.validate(valid => {
      if (valid) {
        tableLoading.value = true;
        energyConsumptionDetailAdd(form)
          .then(res => {
            if (res.code === 200) {
              ElMessage.success(isEdit.value ? "编辑成功" : "新增成功");
              dialogVisible.value = false;
              handleQuery();
            } else {
              ElMessage.error(res.message || "操作失败");
            }
          })
          .catch(error => {
            ElMessage.error("网络错误,请稍后重试");
          })
          .finally(() => {
            tableLoading.value = false;
          });
      }
    });
  };
  onMounted(() => {
    getEnergyTypeList();
    handleQuery();
  });
</script>
<style scoped lang="scss">
  .app-container {
    padding: 24px;
    background-color: #f0f2f5;
    min-height: calc(100vh - 48px);
  }
  .search_form {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 24px;
    padding: 20px;
    background-color: #ffffff;
    border-radius: 6px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
    transition: all 0.3s ease;
    &:hover {
      box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
    }
  }
  .table_list {
    // margin-bottom: 24px;
    background-color: #ffffff;
    border-radius: 6px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
    overflow: hidden;
    height: calc(100vh - 250px);
  }
  :deep(.el-table) {
    border: none;
    border-radius: 6px;
    overflow: hidden;
    .el-table__header-wrapper {
      background-color: #fafafa;
      th {
        background-color: #fafafa;
        font-weight: 600;
        color: #303133;
        border-bottom: 1px solid #ebeef5;
        padding: 14px 0;
      }
    }
    .el-table__body-wrapper {
      tr {
        transition: all 0.3s ease;
        &:hover {
          background-color: #f5f7fa;
        }
        td {
          border-bottom: 1px solid #ebeef5;
          padding: 12px 0;
        }
      }
      tr.current-row {
        background-color: #ecf5ff;
      }
    }
    .el-table__empty-block {
      padding: 40px 0;
    }
  }
  .pagination-container {
    display: flex;
    justify-content: flex-end;
    padding: 16px 20px;
    background-color: #ffffff;
    border-top: 1px solid #ebeef5;
    border-radius: 0 0 12px 12px;
  }
  :deep(.el-button) {
    transition: all 0.3s ease;
    &:hover {
      transform: translateY(-1px);
    }
  }
  :deep(.el-dialog) {
    border-radius: 6px;
    overflow: hidden;
    .el-dialog__header {
      background-color: #fafafa;
      border-bottom: 1px solid #ebeef5;
      padding: 20px 24px;
      .el-dialog__title {
        font-size: 16px;
        font-weight: 600;
        color: #303133;
      }
    }
    .el-dialog__body {
      padding: 24px;
    }
    .el-dialog__footer {
      padding: 16px 24px;
      border-top: 1px solid #ebeef5;
      background-color: #fafafa;
    }
  }
  :deep(.el-form) {
    .el-form-item {
      margin-bottom: 20px;
      .el-form-item__label {
        font-weight: 500;
        color: #303133;
      }
      .el-input,
      .el-select,
      .el-date-picker,
      .el-input-number {
        width: 100%;
        // .el-input__inner {
        //   border-radius: 6px;
        //   border: 1px solid #dcdfe6;
        //   transition: all 0.3s ease;
        //   &:focus {
        //     border-color: #409eff;
        //     box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
        //   }
        // }
      }
    }
  }
  :deep(.el-tag) {
    border-radius: 4px;
    padding: 2px 8px;
    font-size: 12px;
  }
  @media (max-width: 768px) {
    .app-container {
      padding: 16px;
    }
    .search_form {
      flex-direction: column;
      align-items: flex-start;
      gap: 12px;
      .el-form {
        width: 100%;
        .el-form-item {
          width: 100%;
        }
      }
      > div {
        width: 100%;
        display: flex;
        gap: 12px;
        .el-button {
          flex: 1;
        }
      }
    }
    :deep(.el-table) {
      th,
      td {
        padding: 10px 0;
        font-size: 12px;
      }
    }
    :deep(.el-dialog) {
      width: 90% !important;
      margin: 20px auto !important;
    }
  }
  .consumption-value {
    font-weight: bold;
    color: #409eff;
  }
  .consumption-unit {
    font-size: 12px;
    color: #909399;
    margin-left: 4px;
  }
  .search_form {
    :deep(.el-form-item) {
      margin-bottom: 0px !important;
    }
  }
</style>
src/views/energyManagement/productionEnergyConsumption/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,696 @@
<template>
  <div class="app-container">
    <!-- æœç´¢åŒºåŸŸ -->
    <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="能耗类型:">
          <el-select v-model="searchForm.energyType"
                     placeholder="请选择"
                     clearable
                     style="width: 140px;"
                     @change="handleQuery">
            <el-option label="æ°´"
                       value="water" />
            <el-option label="电"
                       value="electricity" />
            <el-option label="气"
                       value="gas" />
          </el-select>
        </el-form-item>
        <!-- <el-form-item label="日期范围:">
          <el-date-picker v-model="searchForm.dateRange"
                          type="daterange"
                          range-separator="至"
                          start-placeholder="开始日期"
                          end-placeholder="结束日期"
                          value-format="YYYY-MM-DD"
                          style="width: 240px;"
                          @change="handleQuery" />
        </el-form-item> -->
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery">搜索</el-button>
          <el-button @click="handleReset">重置</el-button>
        </el-form-item>
      </el-form>
      <div>
        <el-button type="primary"
                   @click="handleAdd">新增</el-button>
        <el-button type="success"
                   @click="handleExport">导出</el-button>
      </div>
    </div>
    <!-- æ•°æ®è¡¨æ ¼ -->
    <div class="table_list">
      <el-table :data="tableData"
                v-loading="tableLoading"
                border
                height="calc(100vh - 350px)"
                stripe>
        <el-table-column type="index"
                         label="序号"
                         width="60"
                         align="center" />
        <el-table-column prop="meterReadingDate"
                         label="抄表日期"
                         width="120"
                         sortable
                         align="center" />
        <el-table-column prop="energyType"
                         label="能源类型"
                         width="100"
                         align="center">
          <template #default="scope">
            <el-tag :type="getEnergyTypeType(scope.row.energyTyep)">
              {{ scope.row.energyTyep }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="energyName"
                         label="能源名称"
                         align="center" />
        <el-table-column prop="meterReadingLocation"
                         label="抄表位置"
                         align="center" />
        <el-table-column prop="startCode"
                         label="起码"
                         sortable
                         align="right" />
        <el-table-column prop="stopCode"
                         label="止码"
                         sortable
                         align="right" />
        <el-table-column prop="dosage"
                         label="用量"
                         sortable
                         align="right">
          <template #default="scope">
            <span class="consumption-value">{{ scope.row.dosage }}</span>
            <span class="consumption-unit">{{ scope.row.unit }}</span>
          </template>
        </el-table-column>
        <!-- <el-table-column prop="unit"
                         label="单位"
                         width="80"
                         align="center" /> -->
        <!-- <el-table-column prop="energyId"
                         label="能源类型ID"
                         width="120"
                         align="center" /> -->
        <el-table-column prop="remark"
                         label="备注"
                         min-width="150"
                         show-overflow-tooltip />
        <el-table-column label="操作"
                         width="180"
                         align="center"
                         fixed="right">
          <template #default="scope">
            <el-button type="primary"
                       link
                       @click="handleEdit(scope.row)">编辑</el-button>
            <el-button type="danger"
                       link
                       @click="handleDelete(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <div class="pagination-container">
        <el-pagination v-model:current-page="page.current"
                       v-model:page-size="page.size"
                       :page-sizes="[10, 20, 50, 100]"
                       :total="page.total"
                       layout="total, sizes, prev, pager, next, jumper"
                       @size-change="handleSizeChange"
                       @current-change="handleCurrentChange" />
      </div>
    </div>
    <!-- åˆ†é¡µ -->
    <!-- æ–°å¢ž/编辑弹窗 -->
    <el-dialog v-model="dialogVisible"
               :title="dialogTitle"
               width="600px">
      <el-form :model="form"
               :rules="rules"
               ref="formRef"
               label-width="120px">
        <el-form-item label="抄表日期"
                      prop="meterReadingDate">
          <el-date-picker v-model="form.meterReadingDate"
                          type="date"
                          placeholder="请选择"
                          value-format="YYYY-MM-DD"
                          style="width: 100%;" />
        </el-form-item>
        <el-form-item label="能源类型"
                      prop="energyType">
          <el-select v-model="form.energyType"
                     placeholder="请选择"
                     style="width: 100%;"
                     @change="handleEnergyTypeChange">
            <el-option v-for="item in energyTypeList"
                       :key="item.id"
                       :label="item.energyName"
                       :value="item.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="抄表位置"
                      prop="meterReadingLocation">
          <el-input v-model="form.meterReadingLocation"
                    placeholder="请输入" />
        </el-form-item>
        <el-form-item label="起码"
                      prop="startCode">
          <el-input-number v-model="form.startCode"
                           :min="0"
                           style="width: 100%;" />
        </el-form-item>
        <el-form-item label="止码"
                      prop="stopCode">
          <el-input-number v-model="form.stopCode"
                           :min="0"
                           style="width: 100%;" />
        </el-form-item>
        <el-form-item label="用量"
                      prop="dosage">
          <el-input-number v-model="form.dosage"
                           :min="0"
                           style="width: 100%;" />
        </el-form-item>
        <el-form-item label="单位"
                      prop="unit">
          <el-input v-model="form.unit"
                    disabled
                    placeholder="请输入" />
        </el-form-item>
        <el-form-item label="备注"
                      prop="remark">
          <el-input v-model="form.remark"
                    type="textarea"
                    rows="3" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="dialogVisible = false">取消</el-button>
          <el-button type="primary"
                     @click="handleSubmit">确定</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
  import { ref, reactive, onMounted, getCurrentInstance } from "vue";
  import { ElMessage, ElMessageBox } from "element-plus";
  import { Watermelon, Lightning } from "@element-plus/icons-vue";
  import {
    energyConsumptionDetailListPage,
    energyConsumptionDetailAdd,
    energyConsumptionDetailDelete,
    energyTypeListPage,
  } from "@/api/energyManagement/energyType";
  // æœç´¢è¡¨å•
  const searchForm = reactive({
    energyType: "",
    dateRange: [],
  });
  const { proxy } = getCurrentInstance();
  // ç»Ÿè®¡æ•°æ®
  const statistics = reactive({
    waterTotal: 1256.8,
    waterAmount: "3,142.00",
    electricityTotal: 8520.5,
    electricityAmount: "6,816.40",
    gasTotal: 3200.6,
    gasAmount: "9,601.80",
  });
  // è¡¨æ ¼æ•°æ®
  const tableData = ref([]);
  const tableLoading = ref(false);
  // åˆ†é¡µ
  const page = reactive({
    current: 1,
    size: 10,
    total: 0,
  });
  // å¼¹çª—控制
  const dialogVisible = ref(false);
  const dialogTitle = ref("新增能耗记录");
  const formRef = ref(null);
  const isEdit = ref(false);
  const currentId = ref(null);
  // è¡¨å•数据
  const form = reactive({
    meterReadingDate: "",
    energyType: "",
    energyName: "",
    energyId: "",
    meterReadingLocation: "",
    startCode: 0,
    stopCode: 0,
    dosage: 0,
    unit: "",
    remark: "",
    type: "生产",
  });
  // è¡¨å•校验规则
  const rules = {
    meterReadingDate: [
      { required: true, message: "请选择抄表日期", trigger: "change" },
    ],
    energyType: [
      { required: true, message: "请选择能源类型", trigger: "change" },
    ],
    energyName: [
      { required: true, message: "请选择能源名称", trigger: "change" },
    ],
    energyId: [{ required: true, message: "请选择能源类型", trigger: "change" }],
    meterReadingLocation: [
      { required: true, message: "请输入抄表位置", trigger: "blur" },
    ],
    startCode: [{ required: true, message: "请输入起码", trigger: "blur" }],
    stopCode: [{ required: true, message: "请输入止码", trigger: "blur" }],
    dosage: [{ required: true, message: "请输入用量", trigger: "blur" }],
    unit: [{ required: true, message: "请输入单位", trigger: "blur" }],
  };
  // èŽ·å–èƒ½è€—ç±»åž‹æ ·å¼
  const getEnergyTypeType = type => {
    const typeMap = {
      æ°´: "primary",
      ç”µ: "warning",
      æ°”: "success",
      water: "primary",
      electricity: "warning",
      gas: "success",
    };
    return typeMap[type] || "info";
  };
  // èŽ·å–èƒ½è€—ç±»åž‹æ–‡æœ¬
  const getEnergyTypeText = type => {
    const textMap = {
      water: "æ°´",
      electricity: "电",
      gas: "气",
    };
    return textMap[type] || type;
  };
  // æŸ¥è¯¢
  const handleQuery = () => {
    tableLoading.value = true;
    const params = {
      current: page.current,
      size: page.size,
      type: "生产",
      // energyType: searchForm.energyType,
      // startDate:
      //   searchForm.dateRange && searchForm.dateRange.length === 2
      //     ? searchForm.dateRange[0]
      //     : null,
      // endDate:
      //   searchForm.dateRange && searchForm.dateRange.length === 2
      //     ? searchForm.dateRange[1]
      //     : null,
    };
    energyConsumptionDetailListPage(params)
      .then(res => {
        if (res.code === 200) {
          tableData.value = res.data.records;
          page.total = res.data.total;
        } else {
          ElMessage.error(res.message || "查询失败");
        }
      })
      .catch(error => {
        ElMessage.error("网络错误,请稍后重试");
      })
      .finally(() => {
        tableLoading.value = false;
      });
  };
  const energyTypeList = ref([]);
  const getEnergyTypeList = () => {
    energyTypeListPage({ current: -1, size: -1 })
      .then(res => {
        if (res.code === 200) {
          energyTypeList.value = res.data.records;
        } else {
          ElMessage.error(res.message || "查询失败");
        }
      })
      .catch(error => {
        ElMessage.error("网络错误,请稍后重试");
      });
  };
  // é‡ç½®
  const handleReset = () => {
    searchForm.energyType = "";
    searchForm.dateRange = [];
    page.current = 1;
    handleQuery();
  };
  // å¯¼å‡º
  const handleExport = () => {
    proxy.download(
      "/energyConsumptionDetail/export",
      { type: "生产" },
      "能耗明细.xlsx"
    );
  };
  // åˆ†é¡µå¤§å°å˜åŒ–
  const handleSizeChange = val => {
    page.size = val;
    handleQuery();
  };
  // é¡µç å˜åŒ–
  const handleCurrentChange = val => {
    page.current = val;
    handleQuery();
  };
  // å¤„理能源类型变化
  const handleEnergyTypeChange = value => {
    const selectedType = energyTypeList.value.find(item => item.id === value);
    if (selectedType) {
      form.energyName = selectedType.energyName;
      form.energyId = selectedType.id;
      form.unit = selectedType.unit;
    }
  };
  // æ–°å¢ž
  const handleAdd = () => {
    isEdit.value = false;
    currentId.value = null;
    dialogTitle.value = "新增能耗记录";
    resetForm();
    dialogVisible.value = true;
  };
  // ç¼–辑
  const handleEdit = row => {
    isEdit.value = true;
    currentId.value = row.id;
    dialogTitle.value = "编辑能耗记录";
    Object.assign(form, row);
    dialogVisible.value = true;
  };
  // åˆ é™¤
  const handleDelete = row => {
    ElMessageBox.confirm("确定要删除该能耗记录吗?", "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }).then(() => {
      tableLoading.value = true;
      energyConsumptionDetailDelete([row.id])
        .then(res => {
          if (res.code === 200) {
            ElMessage.success("删除成功");
            handleQuery();
          } else {
            ElMessage.error(res.message || "删除失败");
          }
        })
        .catch(error => {
          ElMessage.error("网络错误,请稍后重试");
        })
        .finally(() => {
          tableLoading.value = false;
        });
    });
  };
  // é‡ç½®è¡¨å•
  const resetForm = () => {
    form.meterReadingDate = "";
    form.energyType = "";
    form.energyName = "";
    form.energyId = "";
    form.meterReadingLocation = "";
    form.startCode = 0;
    form.stopCode = 0;
    form.dosage = 0;
    form.unit = "";
    form.remark = "";
    form.type = "生产";
  };
  // æäº¤è¡¨å•
  const handleSubmit = () => {
    formRef.value.validate(valid => {
      if (valid) {
        tableLoading.value = true;
        energyConsumptionDetailAdd(form)
          .then(res => {
            if (res.code === 200) {
              ElMessage.success(isEdit.value ? "编辑成功" : "新增成功");
              dialogVisible.value = false;
              handleQuery();
            } else {
              ElMessage.error(res.message || "操作失败");
            }
          })
          .catch(error => {
            ElMessage.error("网络错误,请稍后重试");
          })
          .finally(() => {
            tableLoading.value = false;
          });
      }
    });
  };
  onMounted(() => {
    getEnergyTypeList();
    handleQuery();
  });
</script>
<style scoped lang="scss">
  .app-container {
    padding: 24px;
    background-color: #f0f2f5;
    min-height: calc(100vh - 48px);
  }
  .search_form {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 24px;
    padding: 20px;
    background-color: #ffffff;
    border-radius: 6px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
    transition: all 0.3s ease;
    &:hover {
      box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
    }
  }
  .table_list {
    // margin-bottom: 24px;
    background-color: #ffffff;
    border-radius: 6px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
    overflow: hidden;
    height: calc(100vh - 250px);
  }
  :deep(.el-table) {
    border: none;
    border-radius: 6px;
    overflow: hidden;
    .el-table__header-wrapper {
      background-color: #fafafa;
      th {
        background-color: #fafafa;
        font-weight: 600;
        color: #303133;
        border-bottom: 1px solid #ebeef5;
        padding: 14px 0;
      }
    }
    .el-table__body-wrapper {
      tr {
        transition: all 0.3s ease;
        &:hover {
          background-color: #f5f7fa;
        }
        td {
          border-bottom: 1px solid #ebeef5;
          padding: 12px 0;
        }
      }
      tr.current-row {
        background-color: #ecf5ff;
      }
    }
    .el-table__empty-block {
      padding: 40px 0;
    }
  }
  .pagination-container {
    display: flex;
    justify-content: flex-end;
    padding: 16px 20px;
    background-color: #ffffff;
    border-top: 1px solid #ebeef5;
    border-radius: 0 0 12px 12px;
  }
  :deep(.el-button) {
    transition: all 0.3s ease;
    &:hover {
      transform: translateY(-1px);
    }
  }
  :deep(.el-dialog) {
    border-radius: 6px;
    overflow: hidden;
    .el-dialog__header {
      background-color: #fafafa;
      border-bottom: 1px solid #ebeef5;
      padding: 20px 24px;
      .el-dialog__title {
        font-size: 16px;
        font-weight: 600;
        color: #303133;
      }
    }
    .el-dialog__body {
      padding: 24px;
    }
    .el-dialog__footer {
      padding: 16px 24px;
      border-top: 1px solid #ebeef5;
      background-color: #fafafa;
    }
  }
  :deep(.el-form) {
    .el-form-item {
      margin-bottom: 20px;
      .el-form-item__label {
        font-weight: 500;
        color: #303133;
      }
      .el-input,
      .el-select,
      .el-date-picker,
      .el-input-number {
        width: 100%;
        // .el-input__inner {
        //   border-radius: 6px;
        //   border: 1px solid #dcdfe6;
        //   transition: all 0.3s ease;
        //   &:focus {
        //     border-color: #409eff;
        //     box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
        //   }
        // }
      }
    }
  }
  :deep(.el-tag) {
    border-radius: 4px;
    padding: 2px 8px;
    font-size: 12px;
  }
  @media (max-width: 768px) {
    .app-container {
      padding: 16px;
    }
    .search_form {
      flex-direction: column;
      align-items: flex-start;
      gap: 12px;
      .el-form {
        width: 100%;
        .el-form-item {
          width: 100%;
        }
      }
      > div {
        width: 100%;
        display: flex;
        gap: 12px;
        .el-button {
          flex: 1;
        }
      }
    }
    :deep(.el-table) {
      th,
      td {
        padding: 10px 0;
        font-size: 12px;
      }
    }
    :deep(.el-dialog) {
      width: 90% !important;
      margin: 20px auto !important;
    }
  }
  .consumption-value {
    font-weight: bold;
    color: #409eff;
  }
  .consumption-unit {
    font-size: 12px;
    color: #909399;
    margin-left: 4px;
  }
  .search_form {
    :deep(.el-form-item) {
      margin-bottom: 0px !important;
    }
  }
</style>
src/views/productionPlan/productionPlan/index.vue
@@ -50,6 +50,7 @@
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                height="calc(100vh - 350px)"
                :tableLoading="tableLoading"
                :isSelection="true"
                :selectable="isSelectable"
@@ -63,12 +64,12 @@
               width="500px">
      <el-form :model="mergeForm"
               label-width="120px">
        <el-form-item label="生产计划号">
          <el-input v-model="mergeForm.productionPlanNo"
        <el-form-item label="序列号">
          <el-input v-model="mergeForm.serialNo"
                    disabled />
        </el-form-item>
        <el-form-item label="生产计划数量">
          <el-input-number v-model="mergeForm.totalManufactureQuantity"
          <el-input-number v-model="mergeForm.totalquantity"
                           :min="1"
                           :step="1"
                           style="width: 100%" />
@@ -93,17 +94,83 @@
      <el-table :data="categorySummary"
                border
                style="width: 100%">
        <el-table-column prop="productCategory"
        <el-table-column prop="materialCategory"
                         label="产品类别"
                         align="center"
                         width="150" />
        <el-table-column prop="totalManufactureQuantity"
        <el-table-column prop="totalquantity"
                         label="总制造数量"
                         align="center" />
      </el-table>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="showCategorySummaryDialog = false">关闭</el-button>
        </span>
      </template>
    </el-dialog>
    <!-- è¿½è¸ªè¿›åº¦å¼¹çª— -->
    <el-dialog v-model="showTrackProgressDialog"
               :title="`追踪进度 - ${trackProgressForm.serialNo || ''}`"
               width="600px">
      <el-form :model="trackProgressForm"
               label-width="120px">
        <el-form-item label="序列号">
          <el-input v-model="trackProgressForm.serialNo"
                    disabled />
        </el-form-item>
        <el-form-item label="当前状态">
          <el-select v-model="trackProgressForm.currentStatus"
                     placeholder="请选择状态">
            <el-option label="待处理"
                       value="pending" />
            <el-option label="进行中"
                       value="processing" />
            <el-option label="已完成"
                       value="completed" />
          </el-select>
        </el-form-item>
        <el-form-item label="完成进度">
          <el-progress :percentage="trackProgressForm.completionRate"
                       :status="trackProgressForm.completionRate === 100 ? 'success' : ''" />
        </el-form-item>
        <el-form-item label="进度详情">
          <el-table :data="trackProgressForm.progressDetails"
                    border
                    style="width: 100%">
            <el-table-column prop="step"
                             label="步骤"
                             align="center"
                             width="100" />
            <el-table-column prop="status"
                             label="状态"
                             align="center"
                             width="100">
              <template #default="scope">
                <el-tag :type="scope.row.status === 'completed' ? 'success' : scope.row.status === 'processing' ? 'warning' : 'info'">
                  {{ scope.row.status === 'completed' ? '已完成' : scope.row.status === 'processing' ? '进行中' : '待开始' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="startTime"
                             label="开始时间"
                             align="center"
                             width="180" />
            <el-table-column prop="endTime"
                             label="结束时间"
                             align="center"
                             width="180" />
          </el-table>
        </el-form-item>
        <el-form-item label="备注">
          <el-input v-model="trackProgressForm.remark"
                    type="textarea" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="showTrackProgressDialog = false">关闭</el-button>
          <el-button type="primary"
                     @click="handleUpdateProgress">更新进度</el-button>
        </span>
      </template>
    </el-dialog>
@@ -114,7 +181,7 @@
  import { onMounted, ref } from "vue";
  import { ElMessage } from "element-plus";
  import dayjs from "dayjs";
  import { productOrderListPage } from "@/api/productionManagement/productionOrder.js";
  import { productionPlanListPage } from "@/api/productionPlan/productionPlan.js";
  import PIMTable from "./components/PIMTable.vue";
  const tableColumn = ref([
@@ -139,8 +206,8 @@
      width: "120px",
    },
    {
      label: "生产计划号",
      prop: "productionPlanNo",
      label: "序列号",
      prop: "serialNo",
      width: "140px",
    },
    {
@@ -155,7 +222,7 @@
    },
    {
      label: "产品类别",
      prop: "productCategory",
      prop: "materialCategory",
      width: "100px",
    },
    {
@@ -171,7 +238,7 @@
    },
    {
      label: "制造数量",
      prop: "manufactureQuantity",
      prop: "quantity",
      width: "100px",
      align: "right",
    },
@@ -237,8 +304,8 @@
          clickFun: row => {
            // å•独下发操作
            // è®¾ç½®è¡¨å•数据
            mergeForm.productionPlanNo = row.productionPlanNo;
            mergeForm.totalManufactureQuantity = row.manufactureQuantity;
            mergeForm.serialNo = row.serialNo;
            mergeForm.totalquantity = row.quantity;
            mergeForm.remark = "";
            // æ‰“开弹窗
@@ -249,8 +316,7 @@
          name: "追踪进度",
          type: "text",
          clickFun: row => {
            // è¿½è¸ªè¿›åº¦æ“ä½œ
            ElMessage.warning("追踪进度功能待开发");
            handleTrackProgress(row);
          },
        },
      ],
@@ -274,17 +340,118 @@
  const isShowNewModal = ref(false);
  // åˆå¹¶ä¸‹å‘表单数据
  const mergeForm = reactive({
    productionPlanNo: "",
    totalManufactureQuantity: 0,
    serialNo: "",
    totalquantity: 0,
    remark: "",
  });
  // è¿½è¸ªè¿›åº¦å¼¹çª—控制
  const showTrackProgressDialog = ref(false);
  // è¿½è¸ªè¿›åº¦è¡¨å•数据
  const trackProgressForm = reactive({
    serialNo: "",
    currentStatus: "",
    completionRate: 0,
    progressDetails: [],
    remark: "",
  });
  // å¤„理追踪进度按钮点击
  const handleTrackProgress = row => {
    // è®¾ç½®è¡¨å•数据
    trackProgressForm.serialNo = row.serialNo;
    trackProgressForm.currentStatus = row.status;
    // ç”Ÿæˆæ¨¡æ‹Ÿè¿›åº¦æ•°æ®
    trackProgressForm.progressDetails = generateProgressDetails(row.status);
    // è®¡ç®—完成率
    trackProgressForm.completionRate = calculateCompletionRate(
      trackProgressForm.progressDetails
    );
    trackProgressForm.remark = "";
    // æ‰“开弹窗
    showTrackProgressDialog.value = true;
  };
  // ç”Ÿæˆæ¨¡æ‹Ÿè¿›åº¦è¯¦æƒ…数据
  const generateProgressDetails = status => {
    const details = [
      {
        step: "计划确认",
        status: "completed",
        startTime: "2026-03-01 09:00:00",
        endTime: "2026-03-01 10:00:00",
      },
      {
        step: "物料准备",
        status:
          status === "completed"
            ? "completed"
            : status === "processing"
            ? "completed"
            : "pending",
        startTime:
          status === "completed" || status === "processing"
            ? "2026-03-01 10:30:00"
            : "",
        endTime:
          status === "completed" || status === "processing"
            ? "2026-03-02 16:00:00"
            : "",
      },
      {
        step: "生产加工",
        status:
          status === "completed"
            ? "completed"
            : status === "processing"
            ? "processing"
            : "pending",
        startTime:
          status === "completed" || status === "processing"
            ? "2026-03-03 08:00:00"
            : "",
        endTime: status === "completed" ? "2026-03-08 17:00:00" : "",
      },
      {
        step: "质量检验",
        status: status === "completed" ? "completed" : "pending",
        startTime: status === "completed" ? "2026-03-09 09:00:00" : "",
        endTime: status === "completed" ? "2026-03-09 15:00:00" : "",
      },
      {
        step: "入库",
        status: status === "completed" ? "completed" : "pending",
        startTime: status === "completed" ? "2026-03-10 10:00:00" : "",
        endTime: status === "completed" ? "2026-03-10 11:00:00" : "",
      },
    ];
    return details;
  };
  // è®¡ç®—完成率
  const calculateCompletionRate = details => {
    const completedSteps = details.filter(
      step => step.status === "completed"
    ).length;
    return Math.round((completedSteps / details.length) * 100);
  };
  // å¤„理进度更新
  const handleUpdateProgress = () => {
    // è¿™é‡Œå¯ä»¥æ·»åŠ æ›´æ–°è¿›åº¦çš„é€»è¾‘
    ElMessage.success("进度更新成功");
    showTrackProgressDialog.value = false;
  };
  const data = reactive({
    searchForm: {
      customerName: "",
      salesContractNo: "",
      projectName: "",
      productCategory: "",
      materialCategory: "",
      specificationModel: "",
    },
  });
@@ -307,14 +474,14 @@
    // éåŽ†è¡¨æ ¼æ•°æ®ï¼ŒæŒ‰äº§å“ç±»åˆ«æ±‡æ€»
    tableData.value.forEach(row => {
      const category = row.productCategory;
      const category = row.materialCategory;
      if (!summary[category]) {
        summary[category] = {
          productCategory: category,
          totalManufactureQuantity: 0,
          materialCategory: category,
          totalquantity: 0,
        };
      }
      summary[category].totalManufactureQuantity += row.manufactureQuantity;
      summary[category].totalquantity += row.quantity;
    });
    // è½¬æ¢ä¸ºæ•°ç»„格式
@@ -326,164 +493,164 @@
    // æž„造一个新的对象,不包含entryDate字段
    const params = { ...searchForm.value, ...page };
    params.entryDate = undefined;
    tableData.value = [
      {
        id: 1,
        source: "销售订单",
        status: "待处理",
        auditStatus: "已审核",
        orderNo: "SO20260301001",
        productionPlanNo: "PP20260301001",
        partNo: "P001",
        partName: "零件A",
        productCategory: "类别1",
        processFileNo: "PF20260301001",
        salesQuantity: 100,
        manufactureQuantity: 105,
        partUnit: "个",
        mainPlanDemandDate: "2026-03-15",
        commitmentDate: "2026-03-10",
        manufactureProperty: "常规",
        remark: "",
        updateTime: "2026-03-01",
        updateBy: "admin",
        createTime: "2026-03-01",
        createBy: "admin",
      },
      {
        id: 2,
        source: "销售订单",
        status: "待处理",
        auditStatus: "已审核",
        orderNo: "SO20260301002",
        productionPlanNo: "PP20260301001",
        partNo: "P002",
        partName: "零件B",
        productCategory: "类别1",
        processFileNo: "PF20260301002",
        salesQuantity: 200,
        manufactureQuantity: 210,
        partUnit: "个",
        mainPlanDemandDate: "2026-03-15",
        commitmentDate: "2026-03-10",
        manufactureProperty: "常规",
        remark: "",
        updateTime: "2026-03-01",
        updateBy: "admin",
        createTime: "2026-03-01",
        createBy: "admin",
      },
      {
        id: 3,
        source: "销售订单",
        status: "进行中",
        auditStatus: "已审核",
        orderNo: "SO20260301003",
        productionPlanNo: "PP20260301002",
        partNo: "P003",
        partName: "零件C",
        productCategory: "类别2",
        processFileNo: "PF20260301003",
        salesQuantity: 150,
        manufactureQuantity: 155,
        partUnit: "个",
        mainPlanDemandDate: "2026-03-20",
        commitmentDate: "2026-03-15",
        manufactureProperty: "常规",
        remark: "",
        updateTime: "2026-03-01",
        updateBy: "admin",
        createTime: "2026-03-01",
        createBy: "admin",
      },
      {
        id: 4,
        source: "销售订单",
        status: "进行中",
        auditStatus: "已审核",
        orderNo: "SO20260301004",
        productionPlanNo: "PP20260301002",
        partNo: "P004",
        partName: "零件D",
        productCategory: "类别2",
        processFileNo: "PF20260301004",
        salesQuantity: 300,
        manufactureQuantity: 315,
        partUnit: "个",
        mainPlanDemandDate: "2026-03-20",
        commitmentDate: "2026-03-15",
        manufactureProperty: "常规",
        remark: "",
        updateTime: "2026-03-01",
        updateBy: "admin",
        createTime: "2026-03-01",
        createBy: "admin",
      },
      {
        id: 5,
        source: "销售订单",
        status: "已完成",
        auditStatus: "已审核",
        orderNo: "SO20260301005",
        productionPlanNo: "PP20260301003",
        partNo: "P005",
        partName: "零件E",
        productCategory: "类别3",
        processFileNo: "PF20260301005",
        salesQuantity: 250,
        manufactureQuantity: 260,
        partUnit: "个",
        mainPlanDemandDate: "2026-03-10",
        commitmentDate: "2026-03-05",
        manufactureProperty: "常规",
        remark: "",
        updateTime: "2026-03-01",
        updateBy: "admin",
        createTime: "2026-03-01",
        createBy: "admin",
      },
    ];
    tableLoading.value = false;
    page.total = tableData.value.length;
    // è®¡ç®—产品类别汇总统计
    calculateCategorySummary();
    // productOrderListPage(params)
    //   .then(res => {
    // tableData.value = [
    //   {
    //     id: 1,
    //     source: "销售订单",
    //     status: "待处理",
    //     auditStatus: "已审核",
    //     orderNo: "SO20260301001",
    //     serialNo: "PP20260301001",
    //     partNo: "P001",
    //     partName: "零件A",
    //     materialCategory: "类别1",
    //     processFileNo: "PF20260301001",
    //     salesQuantity: 100,
    //     quantity: 105,
    //     partUnit: "个",
    //     mainPlanDemandDate: "2026-03-15",
    //     commitmentDate: "2026-03-10",
    //     manufactureProperty: "常规",
    //     remark: "",
    //     updateTime: "2026-03-01",
    //     updateBy: "admin",
    //     createTime: "2026-03-01",
    //     createBy: "admin",
    //   },
    //   {
    //     id: 2,
    //     source: "销售订单",
    //     status: "待处理",
    //     auditStatus: "已审核",
    //     orderNo: "SO20260301002",
    //     serialNo: "PP20260301001",
    //     partNo: "P002",
    //     partName: "零件B",
    //     materialCategory: "类别1",
    //     processFileNo: "PF20260301002",
    //     salesQuantity: 200,
    //     quantity: 210,
    //     partUnit: "个",
    //     mainPlanDemandDate: "2026-03-15",
    //     commitmentDate: "2026-03-10",
    //     manufactureProperty: "常规",
    //     remark: "",
    //     updateTime: "2026-03-01",
    //     updateBy: "admin",
    //     createTime: "2026-03-01",
    //     createBy: "admin",
    //   },
    //   {
    //     id: 3,
    //     source: "销售订单",
    //     status: "进行中",
    //     auditStatus: "已审核",
    //     orderNo: "SO20260301003",
    //     serialNo: "PP20260301002",
    //     partNo: "P003",
    //     partName: "零件C",
    //     materialCategory: "类别2",
    //     processFileNo: "PF20260301003",
    //     salesQuantity: 150,
    //     quantity: 155,
    //     partUnit: "个",
    //     mainPlanDemandDate: "2026-03-20",
    //     commitmentDate: "2026-03-15",
    //     manufactureProperty: "常规",
    //     remark: "",
    //     updateTime: "2026-03-01",
    //     updateBy: "admin",
    //     createTime: "2026-03-01",
    //     createBy: "admin",
    //   },
    //   {
    //     id: 4,
    //     source: "销售订单",
    //     status: "进行中",
    //     auditStatus: "已审核",
    //     orderNo: "SO20260301004",
    //     serialNo: "PP20260301002",
    //     partNo: "P004",
    //     partName: "零件D",
    //     materialCategory: "类别2",
    //     processFileNo: "PF20260301004",
    //     salesQuantity: 300,
    //     quantity: 315,
    //     partUnit: "个",
    //     mainPlanDemandDate: "2026-03-20",
    //     commitmentDate: "2026-03-15",
    //     manufactureProperty: "常规",
    //     remark: "",
    //     updateTime: "2026-03-01",
    //     updateBy: "admin",
    //     createTime: "2026-03-01",
    //     createBy: "admin",
    //   },
    //   {
    //     id: 5,
    //     source: "销售订单",
    //     status: "已完成",
    //     auditStatus: "已审核",
    //     orderNo: "SO20260301005",
    //     serialNo: "PP20260301003",
    //     partNo: "P005",
    //     partName: "零件E",
    //     materialCategory: "类别3",
    //     processFileNo: "PF20260301005",
    //     salesQuantity: 250,
    //     quantity: 260,
    //     partUnit: "个",
    //     mainPlanDemandDate: "2026-03-10",
    //     commitmentDate: "2026-03-05",
    //     manufactureProperty: "常规",
    //     remark: "",
    //     updateTime: "2026-03-01",
    //     updateBy: "admin",
    //     createTime: "2026-03-01",
    //     createBy: "admin",
    //   },
    // ];
    //     tableLoading.value = false;
    //     tableData.value = res.data.records;
    //     page.total = res.data.total;
    // page.total = tableData.value.length;
    //     // è®¡ç®—产品类别汇总统计
    //     calculateCategorySummary();
    //   })
    //   .catch(() => {
    //     tableLoading.value = false;
    //   });
    productionPlanListPage(params)
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        page.total = res.data.total;
        // è®¡ç®—产品类别汇总统计
        calculateCategorySummary();
      })
      .catch(() => {
        tableLoading.value = false;
      });
  };
  // é€‰ä¸­çš„生产计划号
  const selectedProductionPlanNo = ref("");
  // é€‰ä¸­çš„序列号
  const selectedserialNo = ref("");
  // è¡¨æ ¼é€‰æ‹©æ•°æ®
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
    // å¦‚果有选中的行,记录第一个选中行的生产计划号
    // å¦‚果有选中的行,记录第一个选中行的序列号
    if (selection.length > 0) {
      selectedProductionPlanNo.value = selection[0].productionPlanNo;
      selectedserialNo.value = selection[0].serialNo;
    } else {
      // å¦‚果没有选中的行,清空生产计划号
      selectedProductionPlanNo.value = "";
      // å¦‚果没有选中的行,清空序列号
      selectedserialNo.value = "";
    }
  };
  // åˆ¤æ–­è¡Œæ˜¯å¦å¯é€‰æ‹©
  const isSelectable = row => {
    // å¦‚果没有选中的行,所有行都可选择
    if (!selectedProductionPlanNo.value) {
    if (!selectedserialNo.value) {
      return true;
    }
    // å¦‚果有选中的行,只有生产计划号相同的行才可选择
    return row.productionPlanNo === selectedProductionPlanNo.value;
    // å¦‚果有选中的行,只有序列号相同的行才可选择
    return row.serialNo === selectedserialNo.value;
  };
  // å¤„理合并下发按钮点击
@@ -495,12 +662,12 @@
    // è®¡ç®—总制造数量
    const totalQuantity = selectedRows.value.reduce((sum, row) => {
      return sum + row.manufactureQuantity;
      return sum + row.quantity;
    }, 0);
    // è®¾ç½®è¡¨å•数据
    mergeForm.productionPlanNo = selectedProductionPlanNo.value;
    mergeForm.totalManufactureQuantity = totalQuantity;
    mergeForm.serialNo = selectedserialNo.value;
    mergeForm.totalquantity = totalQuantity;
    mergeForm.remark = "";
    // æ‰“开弹窗
@@ -521,56 +688,212 @@
</script>
<style scoped lang="scss">
  .app-container {
    padding: 24px;
    background-color: #f0f2f5;
    min-height: calc(100vh - 48px);
  }
  .search_form {
    align-items: start;
  }
  .summary-section {
    margin-bottom: 16px;
  }
  .horizontal-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    padding: 10px 0;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 24px;
    padding: 20px;
    background-color: #ffffff;
    border-radius: 6px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
    transition: all 0.3s ease;
    &:hover {
      box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
    }
  }
  .summary-item {
    flex: 1;
    min-width: 120px;
    text-align: center;
    padding: 10px;
  .table_list {
    // margin-bottom: 24px;
    background-color: #ffffff;
    border-radius: 6px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
    overflow: hidden;
    height: calc(100vh - 250px);
  }
  :deep(.el-table) {
    border: none;
    border-radius: 6px;
    overflow: hidden;
    .el-table__header-wrapper {
      background-color: #fafafa;
      th {
        background-color: #fafafa;
        font-weight: 600;
        color: #303133;
        border-bottom: 1px solid #ebeef5;
        padding: 14px 0;
      }
    }
    .el-table__body-wrapper {
      tr {
        transition: all 0.3s ease;
        &:hover {
    background-color: #f5f7fa;
    border-radius: 4px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
  }
  .summary-label {
    font-size: 14px;
    color: #606266;
    margin-bottom: 5px;
        td {
          border-bottom: 1px solid #ebeef5;
          padding: 12px 0;
        }
  }
  .summary-value {
    font-size: 18px;
      tr.current-row {
        background-color: #ecf5ff;
      }
    }
    .el-table__empty-block {
      padding: 40px 0;
    }
  }
  .pagination-container {
    display: flex;
    justify-content: flex-end;
    padding: 16px 20px;
    background-color: #ffffff;
    border-top: 1px solid #ebeef5;
    border-radius: 0 0 12px 12px;
  }
  :deep(.el-button) {
    transition: all 0.3s ease;
    &:hover {
      transform: translateY(-1px);
    }
  }
  :deep(.el-dialog) {
    border-radius: 6px;
    overflow: hidden;
    .el-dialog__header {
      background-color: #fafafa;
      border-bottom: 1px solid #ebeef5;
      padding: 20px 24px;
      .el-dialog__title {
        font-size: 16px;
    font-weight: 600;
    color: #303133;
  }
  ::v-deep .yellow {
    background-color: #faf0de;
  }
  ::v-deep .pink {
    background-color: #fae1de;
    .el-dialog__body {
      padding: 24px;
  }
  ::v-deep .red {
    background-color: #f80202;
    .el-dialog__footer {
      padding: 16px 24px;
      border-top: 1px solid #ebeef5;
      background-color: #fafafa;
    }
  }
  ::v-deep .purple {
    background-color: #f4defa;
  :deep(.el-form) {
    .el-form-item {
      margin-bottom: 20px;
      .el-form-item__label {
        font-weight: 500;
        color: #303133;
      }
      .el-input,
      .el-select,
      .el-date-picker,
      .el-input-number {
        width: 100%;
        // .el-input__inner {
        //   border-radius: 6px;
        //   border: 1px solid #dcdfe6;
        //   transition: all 0.3s ease;
        //   &:focus {
        //     border-color: #409eff;
        //     box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
        //   }
        // }
      }
    }
  }
  :deep(.el-tag) {
    border-radius: 4px;
    padding: 2px 8px;
    font-size: 12px;
  }
  @media (max-width: 768px) {
    .app-container {
      padding: 16px;
    }
    .search_form {
      flex-direction: column;
      align-items: flex-start;
      gap: 12px;
      .el-form {
        width: 100%;
        .el-form-item {
          width: 100%;
        }
      }
      > div {
        width: 100%;
        display: flex;
        gap: 12px;
        .el-button {
          flex: 1;
        }
      }
    }
    :deep(.el-table) {
      th,
      td {
        padding: 10px 0;
        font-size: 12px;
      }
    }
    :deep(.el-dialog) {
      width: 90% !important;
      margin: 20px auto !important;
    }
  }
  .consumption-value {
    font-weight: bold;
    color: #409eff;
  }
  .consumption-unit {
    font-size: 12px;
    color: #909399;
    margin-left: 4px;
  }
  .search_form {
    :deep(.el-form-item) {
      margin-bottom: 0px !important;
    }
  }
</style>