zhangwencui
2 天以前 5245b68b9d99aaa614bb418c8c9b77a59aeb60de
一次对接
已添加5个文件
已修改4个文件
2942 ■■■■■ 文件已修改
src/api/basicData/newProduct.js 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/costAccounting/energyCosts.js 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/energyManagement/energyType.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/index.vue 488 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/xxx.js 615 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/costAccounting/energyCosts/index.vue 1135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/energyManagement/energyConsumptionStatistical/index.vue 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionPlan/productionPlan/index.vue 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/reportAnalysis/unitEnergyConsumption/index.vue 405 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/basicData/newProduct.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,126 @@
// äº§å“ç»´æŠ¤é¡µé¢æŽ¥å£
import request from '@/utils/request'
// äº§å“æ ‘查询
export function productTreeList(query) {
    return request({
        url: '/productMaterial/list',
        method: 'get',
        params: query
    })
}
// äº§å“å­ç±»æ–°å¢ž
export function addOrEditProduct(query) {
    return request({
        url: '/productMaterial/add',
        method: 'post',
        data: query
    })
}
// äº§å“å­ç±»ä¿®æ”¹
export function updateOrEditProduct(query) {
    return request({
        url: '/productMaterial/update',
        method: 'put',
        data: query
    })
}
// è§„格型号新增
export function addOrEditProductModel(query) {
    return request({
        url: '/productMaterialSku/add',
        method: 'post',
        data: query
    })
}
// è§„格型号修改
export function updateOrEditProductModel(query) {
    return request({
        url: '/productMaterialSku/update',
        method: 'post',
        data: query
    })
}
// äº§å“å­ç±»åˆ é™¤
export function delProduct(query) {
    return request({
        url: '/productMaterial/delete',
        method: 'delete',
        data: query
    })
}
// è§„格型号删除
export function delProductModel(query) {
    return request({
        url: '/productMaterialSku/delete',
        method: 'delete',
        data: query
    })
}
// è§„格型号查询
export function modelList(query) {
    return request({
        url: '/basic/product/modelList',
        method: 'get',
        params: query
    })
}
export function modelListPage(query) {
    return request({
        url: '/productMaterialSku/list',
        method: 'get',
        params: query
    })
}
//  ä¸‹è½½äº§å“å¯¼å…¥æ¨¡æ¿
export function downloadProductModelImportTemplate() {
    return request({
        url: '/basic/product/export',
        method: 'get',
        responseType: 'blob'
    })
}
// äº§å“å¤§ç±»æ–°å¢ž
export function addOrEditProductConfig(query) {
    return request({
        url: '/productMaterial/config/add',
        method: 'post',
        data: query
    })
}
// äº§å“å¤§ç±»ä¿®æ”¹
export function updateOrEditProductConfig(query) {
    return request({
        url: '/productMaterial/config/update',
        method: 'put',
        data: query
    })
}
// äº§å“å¤§ç±»åˆ é™¤
export function delProductConfig(query) {
    return request({
        url: '/productMaterial/config/delete',
        method: 'delete',
        data: query
    })
}
// äº§å“ç‰©æ–™ä¿¡æ¯-存货类别数据集合
export function getinventoryCategoryList(query) {
    return request({
        url: '/productMaterial/inventoryCategoryList',
        method: 'get',
        params: query
    })
}
// äº§å“ç‰©æ–™ä¿¡æ¯-物料类型数据集合
export function getmaterialTypeList(query) {
    return request({
        url: '/productMaterial/materialTypeList',
        method: 'get',
        params: query
    })
}
src/api/costAccounting/energyCosts.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
// èƒ½è€—成本核算接口
import request from "@/utils/request";
// èƒ½è€—成本统计
export function energyCostStatistics(query) {
  return request({
    url: "/energyCost/statistics",
    method: "get",
    params: query,
  });
}
// å¯¼å‡ºèƒ½è€—成本报表
export function exportEnergyCostReport(query) {
  return request({
    url: "/energyCost/export",
    method: "get",
    params: query,
    responseType: "blob",
  });
}
src/api/energyManagement/energyType.js
@@ -76,3 +76,12 @@
    method: 'delete',
  })
}
// èƒ½è€—抄表明细-统计查询
export function energyConsumptionDetailStatistics(query) {
  return request({
    url: "/energyConsumptionDetail/statistics",
    method: "get",
    params: query,
  });
}
src/views/basicData/product/index.vue
@@ -2,25 +2,19 @@
  <div class="app-container product-view">
    <div class="left">
      <div>
        <el-input
          v-model="search"
        <el-input v-model="search"
          style="width: 210px"
          placeholder="输入关键字进行搜索"
          @change="searchFilter"
          @clear="searchFilter"
          clearable
          prefix-icon="Search"
        />
        <el-button
          type="primary"
          @click="openProDia('addOne')"
          style="margin-left: 10px"
          >新增产品大类</el-button
        >
                  prefix-icon="Search" />
        <el-button type="primary"
                   @click="openProDia1('addOne')"
                   style="margin-left: 10px">新增产品大类</el-button>
      </div>
      <div ref="containerRef">
        <el-tree
          ref="tree"
        <el-tree ref="tree"
          v-loading="treeLoad"
          :data="list"
          @node-click="handleNodeClick"
@@ -31,8 +25,7 @@
          highlight-current
          node-key="id"
          class="product-tree-scroll"
          style="height: calc(100vh - 190px); overflow-y: auto"
        >
                 style="height: calc(100vh - 190px); overflow-y: auto">
          <template #default="{ node, data }">
            <div class="custom-tree-node">
              <span class="tree-node-content">
@@ -42,24 +35,43 @@
                </el-icon>
                <span class="tree-node-label">{{ data.label }}</span>
              </span>
              <div>
                <el-button
                  type="primary"
              <div v-if="!node.childNodes.length">
                <el-button type="primary"
                  link
                  @click="openProDia('edit', data)"
                >
                           @click="openProDia('edit', data)">
                  ç¼–辑
                </el-button>
                <el-button type="primary" link @click="openProDia('add', data)" :disabled="node.level >= 3">
                <el-button v-if="data.children && data.children.length > 0"
                           type="primary"
                           link
                           @click="openProDia('add', data)">
                  æ·»åŠ äº§å“
                </el-button>
                <el-button
                  v-if="!node.childNodes.length"
                <el-button v-if="!node.childNodes.length"
                  style="margin-left: 4px"
                  type="danger"
                  link
                  @click="remove(node, data)"
                >
                           @click="remove(node, data)">
                  åˆ é™¤
                </el-button>
              </div>
              <div v-else>
                <!-- å¤§ç±» -->
                <el-button type="primary"
                           link
                           @click="openProDia1('edit', data)">
                  ç¼–辑
                </el-button>
                <el-button v-if="data.children && data.children.length > 0"
                           type="primary"
                           link
                           @click="openProDia('add', data)">
                  æ·»åŠ äº§å“
                </el-button>
                <el-button style="margin-left: 4px"
                           type="danger"
                           link
                           @click="remove1(node, data)">
                  åˆ é™¤
                </el-button>
              </div>
@@ -69,103 +81,219 @@
      </div>
    </div>
    <div class="right">
      <div style="margin-bottom: 10px" v-if="isShowButton">
        <el-button type="primary" @click="openModelDia('add')">
      <div style="margin-bottom: 10px"
           v-if="isShowButton">
        <el-button type="primary"
                   @click="openModelDia('add')">
          æ–°å¢žè§„格型号
        </el-button>
        <ImportExcel :product-id="currentId" @uploadSuccess="getModelList" />
        <el-button
          type="danger"
        <ImportExcel :product-id="currentId"
                     @uploadSuccess="getModelList" />
        <el-button type="danger"
          @click="handleDelete"
          style="margin-left: 10px"
          plain
        >
                   plain>
          åˆ é™¤
        </el-button>
      </div>
      <PIMTable
        rowKey="id"
      <PIMTable rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        :tableLoading="tableLoading"
        @pagination="pagination"
      ></PIMTable>
                @pagination="pagination"></PIMTable>
    </div>
    <el-dialog v-model="productDia" title="产品" width="400px" @keydown.enter.prevent>
      <el-form
        :model="form"
    <el-dialog v-model="productDia"
               title="产品"
               width="400px"
               @keydown.enter.prevent>
      <el-form :model="form"
        label-width="140px"
        label-position="top"
        :rules="rules"
        ref="formRef"
      >
               ref="formRef">
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="产品名称:" prop="productName">
              <el-input
                v-model="form.productName"
            <el-form-item label="产品名称:"
                          prop="materialName">
              <el-input v-model="form.materialName"
                placeholder="请输入产品名称"
                maxlength="20"
                show-word-limit
                clearable
                @keydown.enter.prevent
              />
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="存货类别:"
                          prop="inventoryCategoryId">
              <el-select v-model="form.inventoryCategoryId"
                         placeholder="请选择存货类别"
                         clearable
                         style="width: 100%">
                <el-option v-for="item in inventoryCategoryList"
                           :key="item.id"
                           :label="item.configName"
                           :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="物料类型:"
                          prop="materialTypeId">
              <el-select v-model="form.materialTypeId"
                         placeholder="请选择物料类型"
                         clearable
                         style="width: 100%">
                <el-option v-for="item in materialTypeList"
                           :key="item.id"
                           :label="item.configName"
                           :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="基本单位:"
                          prop="baseUnit">
              <el-input v-model="form.baseUnit"
                        placeholder="请输入基本单位"
                        clearable
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="备注:">
              <el-input v-model="form.remark"
                        placeholder="请输入备注"
                        type="textarea"
                        :rows="3"
                        clearable
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button type="primary"
                     @click="submitForm">确认</el-button>
          <el-button @click="closeProDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <el-dialog
      v-model="modelDia"
    <el-dialog v-model="modelDia"
      title="规格型号"
      width="400px"
      @close="closeModelDia"
      @keydown.enter.prevent
    >
      <el-form
        :model="modelForm"
               @keydown.enter.prevent>
      <el-form :model="modelForm"
        label-width="140px"
        label-position="top"
        :rules="modelRules"
        ref="modelFormRef"
      >
               ref="modelFormRef">
        <el-row>
          <el-col :span="24">
            <el-form-item label="规格型号:" prop="model">
              <el-input
                v-model="modelForm.model"
            <el-form-item label="规格型号:"
                          prop="model">
              <el-input v-model="modelForm.model"
                placeholder="请输入规格型号"
                clearable
                @keydown.enter.prevent
              />
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="单位:" prop="unit">
              <el-input
                v-model="modelForm.unit"
            <el-form-item label="单位:"
                          prop="unit">
              <el-input v-model="modelForm.unit"
                placeholder="请输入单位"
                clearable
                @keydown.enter.prevent
              />
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="标识符代码:"
                          prop="identifierCode">
              <el-input v-model="modelForm.identifierCode"
                        placeholder="请输入标识符代码"
                        clearable
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="物料代码:"
                          prop="materialCode">
              <el-input v-model="modelForm.materialCode"
                        placeholder="请输入物料代码"
                        clearable
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="规格:"
                          prop="specification">
              <el-input v-model="modelForm.specification"
                        placeholder="请输入规格"
                        clearable
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="供应类型:"
                          prop="supplyType">
              <el-input v-model="modelForm.supplyType"
                        placeholder="请输入供应类型"
                        clearable
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="创始人姓名:"
                          prop="originatorName">
              <el-input v-model="modelForm.originatorName"
                        placeholder="请输入创始人姓名"
                        clearable
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="创始人组织:"
                          prop="originatorOrg">
              <el-input v-model="modelForm.originatorOrg"
                        placeholder="请输入创始人组织"
                        clearable
                        @keydown.enter.prevent />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitModelForm">确认</el-button>
          <el-button type="primary"
                     @click="submitModelForm">确认</el-button>
          <el-button @click="closeModelDia">取消</el-button>
        </div>
      </template>
@@ -178,12 +306,19 @@
import { ElMessageBox } from "element-plus";
import {
  addOrEditProduct,
    updateOrEditProduct,
    getinventoryCategoryList,
    getmaterialTypeList,
  addOrEditProductModel,
    updateOrEditProductModel,
  delProduct,
  delProductModel,
  modelListPage,
  productTreeList,
} from "@/api/basicData/product.js";
    addOrEditProductConfig,
    updateOrEditProductConfig,
    delProductConfig,
  } from "@/api/basicData/newProduct.js";
import ImportExcel from "./ImportExcel/index.vue";
const { proxy } = getCurrentInstance();
@@ -200,14 +335,48 @@
const treeLoad = ref(false);
const list = ref([]);
const expandedKeys = ref([]);
  const inventoryCategoryList = ref([]);
  const materialTypeList = ref([]);
  // èŽ·å–å­˜è´§ç±»åˆ«åˆ—è¡¨
  const getInventoryCategoryList = () => {
    getinventoryCategoryList()
      .then(res => {
        inventoryCategoryList.value = res.data.map(item => ({
          id: item.id,
          configName: item.configName,
        }));
      })
      .catch(err => {
        console.error("获取存货类别失败:", err);
      });
  };
  // èŽ·å–ç‰©æ–™ç±»åž‹åˆ—è¡¨
  const getMaterialTypeList = () => {
    getmaterialTypeList()
      .then(res => {
        materialTypeList.value = res.data.map(item => ({
          id: item.id,
          configName: item.configName,
        }));
      })
      .catch(err => {
        console.error("获取物料类型失败:", err);
      });
  };
const tableColumn = ref([
  {
    label: "规格型号",
    prop: "model",
      prop: "materialName",
    },
    {
      label: "规格",
      prop: "specification",
  },
  {
    label: "单位",
    prop: "unit",
      prop: "baseUnit",
  },
  {
    dataType: "action",
@@ -217,7 +386,7 @@
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          clickFun: row => {
          openModelDia("edit", row);
        },
      },
@@ -235,21 +404,41 @@
});
const data = reactive({
  form: {
    productName: "",
      materialTypeId: null,
      inventoryCategoryId: null,
      materialName: "",
      baseUnit: "",
      remark: "",
  },
  rules: {
    productName: [
      materialName: [
      { required: true, message: "请输入", trigger: "blur" },
      { max: 20, message: "产品名称不能超过20个字符", trigger: "blur" },
    ],
      inventoryCategoryId: [
        { required: true, message: "请选择", trigger: "change" },
      ],
      baseUnit: [{ required: true, message: "请输入", trigger: "blur" }],
  },
  modelForm: {
    model: "",
    unit: "",
      identifierCode: "",
      materialCode: "",
      specification: "",
      supplyType: "",
      originatorName: "",
      originatorOrg: "",
  },
  modelRules: {
    model: [{ required: true, message: "请输入", trigger: "blur" }],
    unit: [{ required: true, message: "请输入", trigger: "blur" }],
      identifierCode: [{ required: true, message: "请输入", trigger: "blur" }],
      materialCode: [{ required: true, message: "请输入", trigger: "blur" }],
      specification: [{ required: true, message: "请输入", trigger: "blur" }],
      supplyType: [{ required: true, message: "请输入", trigger: "blur" }],
      originatorName: [{ required: true, message: "请输入", trigger: "blur" }],
      originatorOrg: [{ required: true, message: "请输入", trigger: "blur" }],
  },
});
const { form, rules, modelForm, modelRules } = toRefs(data);
@@ -257,14 +446,31 @@
const getProductTreeList = () => {
  treeLoad.value = true;
  productTreeList()
    .then((res) => {
      list.value = res;
      list.value.forEach((a) => {
        expandedKeys.value.push(a.label);
      });
      .then(res => {
        // è½¬æ¢æ–°çš„æ•°æ®æ ¼å¼
        const newList = [];
        expandedKeys.value = [];
        for (const category of res.data) {
          // æ·»åŠ åˆ†ç±»èŠ‚ç‚¹
          const categoryNode = {
            label: category.configName,
            id: category.configId,
            children: category.materialList.map(item => ({
              id: item.id,
              label: item.materialName,
              inventoryCategoryId: item.inventoryCategoryId,
              materialTypeId: item.materialTypeId,
              remark: item.remark,
              baseUnit: item.baseUnit,
            })),
          };
          newList.push(categoryNode);
          expandedKeys.value.push(category.configName);
        }
        list.value = newList;
      treeLoad.value = false;
    })
    .catch((err) => {
      .catch(err => {
      treeLoad.value = false;
    });
};
@@ -276,47 +482,94 @@
const openProDia = (type, data) => {
  operationType.value = type;
  productDia.value = true;
  form.value.productName = "";
  if (type === "edit") {
    form.value.productName = data.productName;
    // é‡ç½®è¡¨å•
    form.value = {
      materialName: "",
      inventoryCategoryId: null,
      baseUnit: "",
      remark: "",
      materialTypeId: null,
    };
    console.log(data);
    if (type === "edit" && data) {
      // ç¼–辑模式,回填数据
      form.value.materialName = data.label || "";
      form.value.inventoryCategoryId = data.inventoryCategoryId || null;
      form.value.baseUnit = data.baseUnit || "";
      form.value.remark = data.remark || "";
      form.value.materialTypeId = data.materialTypeId || null;
      form.value.id = data.id || null;
    } else {
      form.value.materialTypeId = data.id || null;
  }
};
// æ‰“开规格型号弹框
const openModelDia = (type, data) => {
  modelOperationType.value = type;
  modelDia.value = true;
    // é‡ç½®æ‰€æœ‰å­—段
  modelForm.value.model = "";
  modelForm.value.model = "";
    modelForm.value.unit = "";
    modelForm.value.identifierCode = "";
    modelForm.value.materialCode = "";
    modelForm.value.specification = "";
    modelForm.value.supplyType = "";
    modelForm.value.originatorName = "";
    modelForm.value.originatorOrg = "";
  modelForm.value.id = "";
  if (type === "edit") {
    if (type === "edit" && data) {
      // ç¼–辑模式,回填数据
    modelForm.value = { ...data };
  }
};
// æäº¤äº§å“åç§°ä¿®æ”¹
const submitForm = () => {
  proxy.$refs.formRef.validate((valid) => {
    proxy.$refs.formRef.validate(valid => {
    if (valid) {
      if (operationType.value === "add") {
        form.value.parentId = currentId.value;
        form.value.id = "";
      } else if (operationType.value === "addOne") {
        form.value.id = "";
        form.value.parentId = "";
      } else {
        form.value.id = currentId.value;
        form.value.parentId = "";
      }
      addOrEditProduct(form.value).then((res) => {
        // æž„建提交参数
        // const params = {
        //   materialTypeId: null,
        //   inventoryCategoryId: form.value.inventoryCategoryId,
        //   materialName: form.value.materialName,
        //   baseUnit: form.value.baseUnit,
        //   remark: form.value.remark,
        // };
        // if (operationType.value === "add") {
        //   // æ·»åŠ å­çº§ï¼Œä¼ çˆ¶çº§çš„id作为 materialTypeId
        //   params.materialTypeId = currentId.value;
        // } else if (operationType.value === "addOne") {
        //   // æ·»åŠ ä¸€çº§ï¼ŒmaterialTypeId ä¸º null
        //   params.materialTypeId = null;
        // } else {
        //   // ç¼–辑,传当前id作为 materialTypeId
        //   params.materialTypeId = currentId.value;
        // }
        console.log(form.value);
        console.log(operationType.value);
        if (operationType.value != "edit") {
          addOrEditProduct(form.value).then(res => {
        proxy.$modal.msgSuccess("提交成功");
        closeProDia();
        getProductTreeList();
      });
        } else {
          updateOrEditProduct(form.value).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeProDia();
            getProductTreeList();
          });
        }
    }
  });
};
// å…³é—­äº§å“å¼¹æ¡†
const closeProDia = () => {
  proxy.$refs.formRef.resetFields();
    // æ‰‹åŠ¨é‡ç½®éžè¡¨å•å­—æ®µ
    form.value.materialTypeId = null;
    form.value.remark = "";
  productDia.value = false;
};
@@ -332,7 +585,7 @@
    .then(() => {
      tableLoading.value = true;
      delProduct(ids)
        .then((res) => {
          .then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getProductTreeList();
        })
@@ -356,14 +609,33 @@
// æäº¤è§„格型号修改
const submitModelForm = () => {
  proxy.$refs.modelFormRef.validate((valid) => {
    proxy.$refs.modelFormRef.validate(valid => {
    if (valid) {
      modelForm.value.productId = currentId.value;
      addOrEditProductModel(modelForm.value).then((res) => {
        // æž„建提交参数
        const params = {
          materialId: currentId.value,
          identifierCode: modelForm.value.identifierCode,
          materialCode: modelForm.value.materialCode,
          specification: modelForm.value.specification,
          supplyType: modelForm.value.supplyType,
          originatorName: modelForm.value.originatorName,
          originatorOrg: modelForm.value.originatorOrg,
        };
        if (modelOperationType.value === "add") {
          // æ·»åŠ è§„æ ¼åž‹å·
          addOrEditProductModel(params).then(res => {
        proxy.$modal.msgSuccess("提交成功");
        closeModelDia();
        getModelList();
      });
        } else if (modelOperationType.value === "edit") {
          // ä¿®æ”¹è§„格型号
          updateOrEditProductModel(params).then(res => {
            proxy.$modal.msgSuccess("提交成功");
            closeModelDia();
            getModelList();
          });
        }
    }
  });
};
@@ -373,25 +645,28 @@
  modelDia.value = false;
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  const handleSelectionChange = selection => {
  selectedRows.value = selection;
};
// æŸ¥è¯¢è§„格型号
const pagination = (obj) => {
  const pagination = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getModelList();
};
const getModelList = () => {
    if (!currentId.value) {
      return;
    }
  tableLoading.value = true;
  modelListPage({
    id: currentId.value,
      materialId: currentId.value,
    current: page.current,
    size: page.size,
  }).then((res) => {
    }).then(res => {
    console.log("res", res);
    tableData.value = res.records;
      tableData.value = res.data;
    page.total = res.total;
    tableLoading.value = false;
  });
@@ -400,7 +675,7 @@
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
      ids = selectedRows.value.map(item => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
@@ -413,7 +688,7 @@
    .then(() => {
      tableLoading.value = true;
      delProductModel(ids)
        .then((res) => {
          .then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getModelList();
        })
@@ -461,6 +736,9 @@
  // æ²¡åŒ¹é…åˆ°è¿”回false
  return false;
};
  // é¡µé¢åŠ è½½æ—¶èŽ·å–æ•°æ®
  getInventoryCategoryList();
  getMaterialTypeList();
getProductTreeList();
</script>
src/views/basicData/product/xxx.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,615 @@
{
    "msg": "操作成功",
    "code": 200,
    "data": [
        {
            "configId": 19,
            "configName": "成品",
            "materialList": [
                {
                    "id": 11,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "板材",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 13,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "MU20烧结煤矸石实心砖",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 14,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "水泥标砖",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 15,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "烧结煤矸石页岩实心砖",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 16,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "水泥三角砖",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 17,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "水泥方砖",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 18,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "烧结煤矸石页岩空心砖",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 19,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "烧结煤矸石页岩砖",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 20,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "标砖",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 23,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "加气板",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                }
            ]
        },
        {
            "configId": 27,
            "configName": "生产物耗",
            "materialList": [
                {
                    "id": 43,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "防冻液",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 60,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "醇酸调和漆",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 64,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "清洗剂",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 106,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "灰斗车",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 116,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "打包带(手工带)",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 117,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "稀释剂",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 118,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "木托盘",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 310,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "三乙醇胺",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 313,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "乙炔",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 314,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "透平油",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 315,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "电解粉",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 316,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "打包带",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 317,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "钢球",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 318,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "对讲机",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 319,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "钢锻",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 320,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "抗磨液压油",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 321,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "黄油",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 322,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "缝纫机油",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 323,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "机油",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 324,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "柴油",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 325,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "昆仑原包M320开齿轮油原",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 326,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "昆仑CKD320中负荷齿轮油原",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 327,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "丙烷",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 328,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "昆仑CKC320中负荷齿轮油原",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 329,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "昆仑CKC150工业闭式齿轮油",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 330,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "氧气",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 331,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "昆仑46#抗磨液压油",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 332,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "石蜡",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 335,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "脱模剂",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                }
            ]
        },
        {
            "configId": 30,
            "configName": "沙加砌块",
            "materialList": [
                {
                    "id": 139,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "沙加砌块",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                }
            ]
        },
        {
            "configId": 31,
            "configName": "砌块",
            "materialList": [
                {
                    "id": 12,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "砌块",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                }
            ]
        },
        {
            "configId": 35,
            "configName": "辅助材料",
            "materialList": [
                {
                    "id": 22,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "氧化镁",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 486,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "卡扣",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 487,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "防腐剂",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 489,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "冷拔丝",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                }
            ]
        },
        {
            "configId": 36,
            "configName": "原材料",
            "materialList": [
                {
                    "id": 41,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "生石灰",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 42,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "æ°´",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 488,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "硅砂",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 490,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "æ°´æ³¥",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 491,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "铝粉",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 492,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "粉煤灰",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                },
                {
                    "id": 493,
                    "tenantId": null,
                    "materialTypeId": null,
                    "inventoryCategoryId": null,
                    "materialName": "石膏",
                    "baseUnit": null,
                    "remark": null,
                    "createTime": null,
                    "updateTime": null
                }
            ]
        }
    ]
}
src/views/costAccounting/energyCosts/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1135 @@
<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-group>
        </el-form-item>
        <el-form-item label="能耗类型:">
          <el-select v-model="searchForm.energyType"
                     placeholder="全部"
                     clearable
                     style="width: 140px;"
                     @change="handleQuery">
            <el-option label="全部"
                       value="全部" />
            <el-option label="æ°´"
                       value="æ°´" />
            <el-option label="电"
                       value="电" />
            <el-option label="气"
                       value="气" />
          </el-select>
        </el-form-item>
        <el-form-item label="能耗用途:">
          <el-select v-model="searchForm.energyPurpose"
                     placeholder="全部"
                     clearable
                     style="width: 140px;"
                     @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
                          v-model="searchForm.monthRange"
                          type="monthrange"
                          range-separator="至"
                          start-placeholder="开始月份"
                          end-placeholder="结束月份"
                          value-format="YYYY-MM"
                          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="success"
                   @click="handleExport">导出报表</el-button>
      </div>
    </div>
    <!-- ç»Ÿè®¡æ¦‚览卡片 -->
    <div class="statistics-overview">
      <h2 class="section-header">
        <el-icon class="header-icon">
          <DataLine />
        </el-icon>
        èƒ½è€—成本概览
      </h2>
      <el-row :gutter="20">
        <el-col :span="6">
          <div class="overview-card blue-card">
            <div class="overview-icon blue-icon">
              <el-icon>
                <Money />
              </el-icon>
            </div>
            <div class="overview-info">
              <div class="overview-label">总能耗成本</div>
              <div class="overview-value">Â¥{{ overview.totalCost }}</div>
            </div>
          </div>
        </el-col>
        <el-col :span="6">
          <div class="overview-card green-card">
            <div class="overview-icon green-icon">
              <el-icon>
                <DataLine />
              </el-icon>
            </div>
            <div class="overview-info">
              <div class="overview-label">生产能耗成本</div>
              <div class="overview-value">Â¥{{ overview.productionCost }}</div>
            </div>
          </div>
        </el-col>
        <el-col :span="6">
          <div class="overview-card purple-card">
            <div class="overview-icon purple-icon">
              <el-icon>
                <TrendCharts />
              </el-icon>
            </div>
            <div class="overview-info">
              <div class="overview-label">办公能耗成本</div>
              <div class="overview-value">Â¥{{ overview.officeCost }}</div>
            </div>
          </div>
        </el-col>
        <el-col :span="6">
          <div class="overview-card gray-card">
            <div class="overview-icon gray-icon">
              <el-icon>
                <Histogram />
              </el-icon>
            </div>
            <div class="overview-info">
              <div class="overview-label">平均能耗成本</div>
              <div class="overview-value">Â¥{{ overview.avgCost }} <span class="unit">/{{ statisticsType === 'day' ? '日' : '月' }}</span></div>
            </div>
          </div>
        </el-col>
      </el-row>
    </div>
    <!-- å›¾è¡¨åŒºåŸŸ -->
    <div class="charts-container">
      <h2 class="section-header">
        <el-icon class="header-icon">
          <Histogram />
        </el-icon>
        èƒ½è€—成本分析
      </h2>
      <el-row :gutter="20">
        <el-col :span="12">
          <div class="chart-card">
            <div class="chart-title">能耗成本趋势</div>
            <div ref="costChart"
                 class="chart-content"></div>
          </div>
        </el-col>
        <el-col :span="12">
          <div class="chart-card">
            <div class="chart-title">能耗类型成本占比</div>
            <div ref="typeChart"
                 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="purposeChart"
                 class="chart-content"></div>
          </div>
        </el-col>
        <el-col :span="12">
          <div class="chart-card">
            <div class="chart-title">能耗单价对比</div>
            <div ref="priceChart"
                 class="chart-content"></div>
          </div>
        </el-col>
      </el-row>
    </div>
    <!-- æ•°æ®è¡¨æ ¼ -->
    <div class="table-section">
      <h2 class="section-header">
        <el-icon class="header-icon">
          <List />
        </el-icon>
        è¯¦ç»†æ•°æ®
      </h2>
      <el-table :data="tableData"
                v-loading="tableLoading"
                border>
        <el-table-column type="index"
                         label="序号"
                         width="60"
                         align="center" />
        <el-table-column prop="timePeriod"
                         :label="timeColumnLabel"
                         align="center" />
        <el-table-column prop="energyType"
                         label="能耗类型"
                         width="100"
                         align="center">
          <template #default="scope">
            <el-tag :type="getEnergyTypeType(scope.row.energyType)">
              {{ scope.row.energyType }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="energyPurpose"
                         label="能耗用途"
                         width="100"
                         align="center">
          <template #default="scope">
            <el-tag :type="scope.row.energyPurpose === '生产' ? 'primary' : 'info'">
              {{ scope.row.energyPurpose }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="consumption"
                         label="用量"
                         align="right">
          <template #default="scope">
            <span class="consumption-value">{{ scope.row.consumption }}</span>
            <span class="consumption-unit">{{ scope.row.unit }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="price"
                         label="单价(元)"
                         align="right">
          <template #default="scope">
            <span class="price-value">{{ scope.row.price }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="cost"
                         label="成本(元)"
                         align="right"
                         fixed="right">
          <template #default="scope">
            <span class="cost-value">Â¥{{ scope.row.cost }}</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 {
    Money,
    DataLine,
    TrendCharts,
    Histogram,
    List,
  } from "@element-plus/icons-vue";
  import * as echarts from "echarts";
  import { energyCostStatistics } from "@/api/costAccounting/energyCosts";
  // ç»Ÿè®¡ç»´åº¦ï¼šday-按日,month-按月
  const statisticsType = ref("day");
  // æœç´¢è¡¨å•
  const searchForm = reactive({
    energyType: "",
    energyPurpose: "",
    dateRange: (() => {
      // é»˜è®¤æœ€è¿‘7天
      const end = new Date();
      const start = new Date();
      start.setDate(start.getDate() - 6);
      return [start.toISOString().split("T")[0], end.toISOString().split("T")[0]];
    })(),
    monthRange: (() => {
      // é»˜è®¤æœ€è¿‘3个月
      const end = new Date();
      const start = new Date();
      start.setMonth(start.getMonth() - 2);
      return [start.toISOString().slice(0, 7), end.toISOString().slice(0, 7)];
    })(),
  });
  // æ—¶é—´åˆ—标签
  const timeColumnLabel = computed(() => {
    return statisticsType.value === "day" ? "日期" : "月份";
  });
  // ç»Ÿè®¡æ¦‚览
  const overview = reactive({
    totalCost: "0.00",
    productionCost: "0.00",
    officeCost: "0.00",
    avgCost: "0.00",
  });
  // è¡¨æ ¼æ•°æ®
  const tableData = ref([]);
  const tableLoading = ref(false);
  // åˆ†é¡µ
  const page = reactive({
    current: 1,
    size: 10,
    total: 0,
  });
  // å›¾è¡¨å¼•用
  const costChart = ref(null);
  const typeChart = ref(null);
  const purposeChart = ref(null);
  const priceChart = ref(null);
  // å›¾è¡¨å®žä¾‹
  let costChartInstance = null;
  let typeChartInstance = null;
  let purposeChartInstance = null;
  let priceChartInstance = null;
  // èŽ·å–èƒ½è€—ç±»åž‹æ ‡ç­¾ç±»åž‹
  const getEnergyTypeType = type => {
    const typeMap = {
      æ°´: "primary",
      ç”µ: "warning",
      æ°”: "success",
    };
    return typeMap[type] || "info";
  };
  // åˆå§‹åŒ–图表
  const initCharts = () => {
    nextTick(() => {
      // èƒ½è€—成本趋势图
      if (costChart.value) {
        costChartInstance = echarts.init(costChart.value);
        updateCostChart();
      }
      // èƒ½è€—类型成本占比图
      if (typeChart.value) {
        typeChartInstance = echarts.init(typeChart.value);
        updateTypeChart();
      }
      // èƒ½è€—用途成本占比图
      if (purposeChart.value) {
        purposeChartInstance = echarts.init(purposeChart.value);
        updatePurposeChart();
      }
      // èƒ½è€—单价对比图
      if (priceChart.value) {
        priceChartInstance = echarts.init(priceChart.value);
        updatePriceChart();
      }
    });
  };
  // æ›´æ–°èƒ½è€—成本趋势图
  const updateCostChart = () => {
    const data = tableData.value;
    const option = {
      tooltip: {
        trigger: "axis",
        axisPointer: { type: "shadow" },
        backgroundColor: "rgba(255, 255, 255, 0.95)",
        borderColor: "#409EFF",
        borderWidth: 1,
        textStyle: { color: "#303133" },
      },
      legend: {
        data: ["生产能耗成本", "办公能耗成本"],
        top: 0,
        right: 10,
        textStyle: { color: "#606266" },
      },
      grid: {
        left: "3%",
        right: "4%",
        bottom: "10%",
        top: "15%",
        containLabel: true,
      },
      xAxis: {
        type: "category",
        data: data.map(item => item.timePeriod),
        axisLabel: {
          rotate: statisticsType.value === "day" ? 45 : 0,
          color: "#606266",
        },
        axisLine: { lineStyle: { color: "#ebeef5" } },
        splitLine: { show: false },
      },
      yAxis: {
        type: "value",
        name: "成本(元)",
        nameTextStyle: { color: "#606266" },
        axisLabel: { color: "#606266" },
        axisLine: { show: false },
        splitLine: { lineStyle: { color: "#f0f2f5" } },
      },
      series: [
        {
          name: "生产能耗成本",
          type: "bar",
          data: data.map(item => (item.energyPurpose === "生产" ? item.cost : 0)),
          itemStyle: {
            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
              { offset: 0, color: "#409EFF" },
              { offset: 1, color: "#66b1ff" },
            ]),
            borderRadius: [4, 4, 0, 0],
          },
          animationDelay: function (idx) {
            return idx * 100;
          },
        },
        {
          name: "办公能耗成本",
          type: "bar",
          data: data.map(item => (item.energyPurpose === "办公" ? item.cost : 0)),
          itemStyle: {
            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
              { offset: 0, color: "#67C23A" },
              { offset: 1, color: "#85ce61" },
            ]),
            borderRadius: [4, 4, 0, 0],
          },
          animationDelay: function (idx) {
            return idx * 100 + 100;
          },
        },
      ],
      animationEasing: "elasticOut",
      animationDelayUpdate: function (idx) {
        return idx * 5;
      },
    };
    costChartInstance.setOption(option);
  };
  // æ›´æ–°èƒ½è€—类型成本占比图
  const updateTypeChart = () => {
    const data = tableData.value;
    const typeCosts = {};
    data.forEach(item => {
      if (!typeCosts[item.energyType]) {
        typeCosts[item.energyType] = 0;
      }
      typeCosts[item.energyType] += parseFloat(item.cost);
    });
    const chartData = Object.entries(typeCosts).map(([name, value]) => ({
      name,
      value: value.toFixed(2),
    }));
    const option = {
      tooltip: {
        trigger: "item",
        formatter: "{a} <br/>{b}: Â¥{c} ({d}%)",
        backgroundColor: "rgba(255, 255, 255, 0.95)",
        borderColor: "#409EFF",
        borderWidth: 1,
        textStyle: { color: "#303133" },
      },
      legend: {
        orient: "horizontal",
        bottom: 0,
        textStyle: { color: "#606266" },
      },
      series: [
        {
          name: "能耗类型成本",
          type: "pie",
          radius: ["40%", "70%"],
          center: ["50%", "40%"],
          avoidLabelOverlap: false,
          itemStyle: {
            borderRadius: 4,
            borderColor: "#fff",
            borderWidth: 2,
          },
          label: {
            show: false,
            position: "center",
          },
          emphasis: {
            label: {
              show: true,
              fontSize: "18",
              fontWeight: "bold",
              color: "#303133",
            },
            itemStyle: {
              shadowBlur: 10,
              shadowOffsetX: 0,
              shadowColor: "rgba(0, 0, 0, 0.3)",
            },
          },
          labelLine: {
            show: false,
          },
          data: chartData,
        },
      ],
      color: ["#409EFF", "#67C23A", "#E6A23C"],
    };
    typeChartInstance.setOption(option);
  };
  // æ›´æ–°èƒ½è€—用途成本占比图
  const updatePurposeChart = () => {
    const data = tableData.value;
    const purposeCosts = {
      ç”Ÿäº§: 0,
      åŠžå…¬: 0,
    };
    data.forEach(item => {
      if (purposeCosts.hasOwnProperty(item.energyPurpose)) {
        purposeCosts[item.energyPurpose] += parseFloat(item.cost);
      }
    });
    const chartData = Object.entries(purposeCosts).map(([name, value]) => ({
      name,
      value: value.toFixed(2),
    }));
    const option = {
      tooltip: {
        trigger: "item",
        formatter: "{a} <br/>{b}: Â¥{c} ({d}%)",
        backgroundColor: "rgba(255, 255, 255, 0.95)",
        borderColor: "#409EFF",
        borderWidth: 1,
        textStyle: { color: "#303133" },
      },
      legend: {
        orient: "horizontal",
        bottom: 0,
        textStyle: { color: "#606266" },
      },
      series: [
        {
          name: "能耗用途成本",
          type: "pie",
          radius: "60%",
          center: ["50%", "40%"],
          data: chartData,
          emphasis: {
            itemStyle: {
              shadowBlur: 10,
              shadowOffsetX: 0,
              shadowColor: "rgba(0, 0, 0, 0.3)",
            },
          },
          label: {
            show: true,
            formatter: "{b}: {d}%",
            color: "#606266",
          },
          labelLine: {
            show: true,
            lineStyle: { color: "#dcdfe6" },
          },
        },
      ],
      color: ["#409EFF", "#67C23A"],
    };
    purposeChartInstance.setOption(option);
  };
  // æ›´æ–°èƒ½è€—单价对比图
  const updatePriceChart = () => {
    const data = tableData.value;
    const priceData = {};
    data.forEach(item => {
      if (!priceData[item.energyType]) {
        priceData[item.energyType] = {
          ç”Ÿäº§: 0,
          åŠžå…¬: 0,
        };
      }
      if (priceData[item.energyType].hasOwnProperty(item.energyPurpose)) {
        priceData[item.energyType][item.energyPurpose] = parseFloat(item.price);
      }
    });
    const energyTypes = Object.keys(priceData);
    const productionPrices = energyTypes.map(type => priceData[type].生产);
    const officePrices = energyTypes.map(type => priceData[type].办公);
    const option = {
      tooltip: {
        trigger: "axis",
        axisPointer: { type: "shadow" },
        backgroundColor: "rgba(255, 255, 255, 0.95)",
        borderColor: "#409EFF",
        borderWidth: 1,
        textStyle: { color: "#303133" },
      },
      legend: {
        data: ["生产能耗单价", "办公能耗单价"],
        top: 0,
        right: 10,
        textStyle: { color: "#606266" },
      },
      grid: {
        left: "3%",
        right: "4%",
        bottom: "10%",
        top: "15%",
        containLabel: true,
      },
      xAxis: {
        type: "category",
        data: energyTypes,
        axisLabel: { color: "#606266" },
        axisLine: { lineStyle: { color: "#ebeef5" } },
        splitLine: { show: false },
      },
      yAxis: {
        type: "value",
        name: "单价(元)",
        nameTextStyle: { color: "#606266" },
        axisLabel: { color: "#606266" },
        axisLine: { show: false },
        splitLine: { lineStyle: { color: "#f0f2f5" } },
      },
      series: [
        {
          name: "生产能耗单价",
          type: "bar",
          data: productionPrices,
          itemStyle: {
            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
              { offset: 0, color: "#409EFF" },
              { offset: 1, color: "#66b1ff" },
            ]),
            borderRadius: [4, 4, 0, 0],
          },
        },
        {
          name: "办公能耗单价",
          type: "bar",
          data: officePrices,
          itemStyle: {
            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
              { offset: 0, color: "#67C23A" },
              { offset: 1, color: "#85ce61" },
            ]),
            borderRadius: [4, 4, 0, 0],
          },
        },
      ],
    };
    priceChartInstance.setOption(option);
  };
  // ç»Ÿè®¡ç»´åº¦åˆ‡æ¢
  const handleTypeChange = () => {
    // é‡ç½®æ—¶é—´èŒƒå›´
    if (statisticsType.value === "day") {
      const end = new Date();
      const start = new Date();
      start.setDate(start.getDate() - 6);
      searchForm.dateRange = [
        start.toISOString().split("T")[0],
        end.toISOString().split("T")[0],
      ];
    } else {
      const end = new Date();
      const start = new Date();
      start.setMonth(start.getMonth() - 2);
      searchForm.monthRange = [
        start.toISOString().slice(0, 7),
        end.toISOString().slice(0, 7),
      ];
    }
    page.current = 1;
    handleQuery();
  };
  // æŸ¥è¯¢
  const handleQuery = () => {
    tableLoading.value = true;
    // æž„造请求参数
    const params = {
      type: statisticsType.value,
      energyType: searchForm.energyType || undefined,
      energyPurpose: searchForm.energyPurpose || undefined,
    };
    if (statisticsType.value === "day") {
      if (searchForm.dateRange && searchForm.dateRange.length === 2) {
        params.startDate = searchForm.dateRange[0];
        params.endDate = searchForm.dateRange[1];
      }
    } else {
      if (searchForm.monthRange && searchForm.monthRange.length === 2) {
        params.startDate = searchForm.monthRange[0] + "-01";
        params.endDate = searchForm.monthRange[1] + "-01";
      }
    }
    // è°ƒç”¨æŽ¥å£èŽ·å–æ•°æ®
    energyCostStatistics(params)
      .then(res => {
        if (res.code === 200) {
          tableData.value = res.data.records || [];
          page.total = res.data.total || 0;
          // æ›´æ–°ç»Ÿè®¡æ¦‚览数据
          if (res.data.overview) {
            overview.totalCost = res.data.overview.totalCost || "0.00";
            overview.productionCost = res.data.overview.productionCost || "0.00";
            overview.officeCost = res.data.overview.officeCost || "0.00";
            overview.avgCost = res.data.overview.avgCost || "0.00";
          }
        } else {
          ElMessage.error(res.message || "获取数据失败");
          tableData.value = [];
          page.total = 0;
        }
      })
      .catch(err => {
        console.error("获取数据异常:", err);
        // ç”Ÿæˆå‡æ•°æ®
        generateMockData();
      })
      .finally(() => {
        tableLoading.value = false;
        updateCharts();
      });
  };
  // ç”Ÿæˆå‡æ•°æ®
  const generateMockData = () => {
    if (statisticsType.value === "day") {
      // ç”Ÿæˆæœ€è¿‘7天的假数据
      const mockData = [];
      const today = new Date();
      for (let i = 6; i >= 0; i--) {
        const date = new Date(today);
        date.setDate(date.getDate() - i);
        const dateStr = date.toISOString().split("T")[0];
        // ç”Ÿäº§èƒ½è€—数据
        mockData.push({
          timePeriod: dateStr,
          energyType: "电",
          energyPurpose: "生产",
          consumption: (Math.random() * 1000 + 500).toFixed(2),
          unit: "kWh",
          price: "0.85",
          cost: (Math.random() * 850 + 425).toFixed(2),
        });
        mockData.push({
          timePeriod: dateStr,
          energyType: "æ°´",
          energyPurpose: "生产",
          consumption: (Math.random() * 500 + 200).toFixed(2),
          unit: "m³",
          price: "3.50",
          cost: (Math.random() * 1750 + 700).toFixed(2),
        });
        mockData.push({
          timePeriod: dateStr,
          energyType: "气",
          energyPurpose: "生产",
          consumption: (Math.random() * 300 + 100).toFixed(2),
          unit: "m³",
          price: "2.80",
          cost: (Math.random() * 840 + 280).toFixed(2),
        });
        // åŠžå…¬èƒ½è€—æ•°æ®
        mockData.push({
          timePeriod: dateStr,
          energyType: "电",
          energyPurpose: "办公",
          consumption: (Math.random() * 200 + 100).toFixed(2),
          unit: "kWh",
          price: "0.85",
          cost: (Math.random() * 170 + 85).toFixed(2),
        });
        mockData.push({
          timePeriod: dateStr,
          energyType: "æ°´",
          energyPurpose: "办公",
          consumption: (Math.random() * 50 + 20).toFixed(2),
          unit: "m³",
          price: "3.50",
          cost: (Math.random() * 175 + 70).toFixed(2),
        });
      }
      tableData.value = mockData;
      page.total = mockData.length;
    } else {
      // ç”Ÿæˆæœ€è¿‘3个月的假数据
      const mockData = [];
      const today = new Date();
      for (let i = 2; i >= 0; i--) {
        const date = new Date(today);
        date.setMonth(date.getMonth() - i);
        const monthStr = date.toISOString().slice(0, 7);
        // ç”Ÿäº§èƒ½è€—数据
        mockData.push({
          timePeriod: monthStr,
          energyType: "电",
          energyPurpose: "生产",
          consumption: (Math.random() * 30000 + 15000).toFixed(2),
          unit: "kWh",
          price: "0.85",
          cost: (Math.random() * 25500 + 12750).toFixed(2),
        });
        mockData.push({
          timePeriod: monthStr,
          energyType: "æ°´",
          energyPurpose: "生产",
          consumption: (Math.random() * 15000 + 6000).toFixed(2),
          unit: "m³",
          price: "3.50",
          cost: (Math.random() * 52500 + 21000).toFixed(2),
        });
        mockData.push({
          timePeriod: monthStr,
          energyType: "气",
          energyPurpose: "生产",
          consumption: (Math.random() * 9000 + 3000).toFixed(2),
          unit: "m³",
          price: "2.80",
          cost: (Math.random() * 25200 + 8400).toFixed(2),
        });
        // åŠžå…¬èƒ½è€—æ•°æ®
        mockData.push({
          timePeriod: monthStr,
          energyType: "电",
          energyPurpose: "办公",
          consumption: (Math.random() * 6000 + 3000).toFixed(2),
          unit: "kWh",
          price: "0.85",
          cost: (Math.random() * 5100 + 2550).toFixed(2),
        });
        mockData.push({
          timePeriod: monthStr,
          energyType: "æ°´",
          energyPurpose: "办公",
          consumption: (Math.random() * 1500 + 600).toFixed(2),
          unit: "m³",
          price: "3.50",
          cost: (Math.random() * 5250 + 2100).toFixed(2),
        });
      }
      tableData.value = mockData;
      page.total = mockData.length;
    }
    // æ›´æ–°ç»Ÿè®¡æ¦‚览数据
    calculateOverview();
  };
  // è®¡ç®—统计概览数据
  const calculateOverview = () => {
    let totalCost = 0;
    let productionCost = 0;
    let officeCost = 0;
    tableData.value.forEach(item => {
      const cost = parseFloat(item.cost);
      totalCost += cost;
      if (item.energyPurpose === "生产") {
        productionCost += cost;
      } else if (item.energyPurpose === "办公") {
        officeCost += cost;
      }
    });
    overview.totalCost = totalCost.toFixed(2);
    overview.productionCost = productionCost.toFixed(2);
    overview.officeCost = officeCost.toFixed(2);
    overview.avgCost = (totalCost / tableData.value.length).toFixed(2);
  };
  // æ›´æ–°æ‰€æœ‰å›¾è¡¨
  const updateCharts = () => {
    nextTick(() => {
      if (costChartInstance) updateCostChart();
      if (typeChartInstance) updateTypeChart();
      if (purposeChartInstance) updatePurposeChart();
      if (priceChartInstance) updatePriceChart();
    });
  };
  // é‡ç½®
  const handleReset = () => {
    searchForm.energyType = "";
    searchForm.energyPurpose = "";
    if (statisticsType.value === "day") {
      const end = new Date();
      const start = new Date();
      start.setDate(start.getDate() - 6);
      searchForm.dateRange = [
        start.toISOString().split("T")[0],
        end.toISOString().split("T")[0],
      ];
    } else {
      const end = new Date();
      const start = new Date();
      start.setMonth(start.getMonth() - 2);
      searchForm.monthRange = [
        start.toISOString().slice(0, 7),
        end.toISOString().slice(0, 7),
      ];
    }
    page.current = 1;
    handleQuery();
  };
  // å¯¼å‡º
  const handleExport = () => {
    ElMessage.success("报表导出成功");
  };
  // åˆ†é¡µå¤§å°å˜åŒ–
  const handleSizeChange = val => {
    page.size = val;
  };
  // é¡µç å˜åŒ–
  const handleCurrentChange = val => {
    page.current = val;
  };
  // çª—口大小变化时重新渲染图表
  const handleResize = () => {
    costChartInstance && costChartInstance.resize();
    typeChartInstance && typeChartInstance.resize();
    purposeChartInstance && purposeChartInstance.resize();
    priceChartInstance && priceChartInstance.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: 30px;
  }
  .charts-container {
    margin-bottom: 30px;
  }
  .table-section {
    margin-bottom: 20px;
  }
  .section-header {
    display: flex;
    align-items: center;
    font-size: 18px;
    font-weight: bold;
    color: #303133;
    margin-bottom: 15px;
    padding-left: 10px;
    border-left: 3px solid #409eff;
    .header-icon {
      margin-right: 8px;
      color: #409eff;
    }
  }
  .overview-card {
    display: flex;
    align-items: center;
    padding: 20px;
    border-radius: 4px;
    background: #fff;
    border: 1px solid #ebeef5;
    &.blue-card {
      background-color: #ecf5ff;
    }
    &.green-card {
      background-color: #f0f9eb;
    }
    &.purple-card {
      background-color: #f3f0ff;
    }
    &.gray-card {
      background-color: #f5f7fa;
    }
    .overview-icon {
      width: 40px;
      height: 40px;
      display: flex;
      align-items: center;
      justify-content: center;
      border-radius: 50%;
      margin-right: 15px;
      &.blue-icon {
        background-color: #409eff;
        color: #fff;
      }
      &.green-icon {
        background-color: #67c23a;
        color: #fff;
      }
      &.purple-icon {
        background-color: #909399;
        color: #fff;
      }
      &.gray-icon {
        background-color: #909399;
        color: #fff;
      }
      .el-icon {
        font-size: 20px;
      }
    }
    .overview-info {
      flex: 1;
      .overview-label {
        font-size: 14px;
        color: #606266;
        margin-bottom: 5px;
      }
      .overview-value {
        font-size: 20px;
        font-weight: bold;
        color: #303133;
        .unit {
          font-size: 12px;
          font-weight: normal;
          color: #909399;
        }
      }
    }
  }
  .chart-card {
    background: #fff;
    border-radius: 4px;
    border: 1px solid #ebeef5;
    padding: 20px;
    .chart-title {
      font-size: 14px;
      font-weight: bold;
      color: #303133;
      margin-bottom: 15px;
      padding-bottom: 10px;
      border-bottom: 1px solid #ebeef5;
    }
    .chart-content {
      height: 300px;
    }
  }
  .consumption-value {
    font-weight: bold;
    color: #409eff;
  }
  .consumption-unit {
    font-size: 12px;
    color: #909399;
    margin-left: 2px;
  }
  .price-value {
    font-weight: bold;
    color: #67c23a;
  }
  .cost-value {
    font-weight: bold;
    color: #f56c6c;
  }
  .pagination-container {
    display: flex;
    justify-content: flex-end;
  }
</style>
src/views/energyManagement/energyConsumptionStatistical/index.vue
@@ -267,15 +267,28 @@
    ArrowDown,
  } from "@element-plus/icons-vue";
  import * as echarts from "echarts";
  import { energyConsumptionDetailStatistics } from "@/api/energyManagement/energyType";
  // ç»Ÿè®¡ç»´åº¦ï¼šday-按日,month-按月,year-按年
  const statisticsType = ref("day");
  // æœç´¢è¡¨å•
  const searchForm = reactive({
    energyType: "",
    dateRange: [],
    monthRange: [],
    energyType: "全部",
    dateRange: (() => {
      // é»˜è®¤æœ€è¿‘7天
      const end = new Date();
      const start = new Date();
      start.setDate(start.getDate() - 6);
      return [start.toISOString().split("T")[0], end.toISOString().split("T")[0]];
    })(),
    monthRange: (() => {
      // é»˜è®¤æœ€è¿‘3个月
      const end = new Date();
      const start = new Date();
      start.setMonth(start.getMonth() - 2);
      return [start.toISOString().slice(0, 7), end.toISOString().slice(0, 7)];
    })(),
    year: new Date().getFullYear(),
  });
@@ -651,13 +664,73 @@
  // æŸ¥è¯¢
  const handleQuery = () => {
    tableLoading.value = true;
    setTimeout(() => {
      const data = generateMockData();
      tableData.value = data;
      page.total = data.length;
    const params = {
      type: "",
    };
    // æž„造请求参数
    if (searchForm.energyType != "全部") {
      params.type = searchForm.energyType;
    }
    if (statisticsType.value === "day") {
      if (searchForm.dateRange && searchForm.dateRange.length === 2) {
        params.startDate = searchForm.dateRange[0];
        params.endDate = searchForm.dateRange[1];
        // è®¡ç®—天数
        const start = new Date(searchForm.dateRange[0]);
        const end = new Date(searchForm.dateRange[1]);
        params.days = Math.ceil((end - start) / (1000 * 60 * 60 * 24)) + 1;
      }
    } else if (statisticsType.value === "month") {
      if (searchForm.monthRange && searchForm.monthRange.length === 2) {
        params.startDate = searchForm.monthRange[0] + "-01";
        params.endDate = searchForm.monthRange[1] + "-01";
        // è®¡ç®—月数
        const start = new Date(searchForm.monthRange[0] + "-01");
        const end = new Date(searchForm.monthRange[1] + "-01");
        params.days =
          (end.getFullYear() - start.getFullYear()) * 12 +
          (end.getMonth() - start.getMonth()) +
          1;
      }
    } else if (statisticsType.value === "year") {
      params.startDate = searchForm.year + "-01-01";
      params.endDate = searchForm.year + "-12-31";
      params.days = 365;
    }
    // è°ƒç”¨æŽ¥å£èŽ·å–æ•°æ®
    energyConsumptionDetailStatistics(params)
      .then(res => {
        if (res.code === 200) {
          const data = res.data;
          // æ›´æ–°ç»Ÿè®¡æ¦‚览数据
          overview.totalConsumption = data.totalEnergyConsumption || "0";
          overview.totalAmount = data.totalEnergyCost || "0";
          overview.avgConsumption = data.averageConsumption || "0";
          overview.compareRate = data.changeVite || 0;
          // å¤„理表格数据
          tableData.value = data.energyCostDtos || [];
          page.total = tableData.value.length || 0;
        } else {
          ElMessage.error(res.message || "获取数据失败");
          tableData.value = [];
          page.total = 0;
        }
      })
      .catch(err => {
        console.error("获取数据异常:", err);
        ElMessage.error("系统异常,获取数据失败");
        tableData.value = [];
        page.total = 0;
      })
      .finally(() => {
      tableLoading.value = false;
      updateCharts();
    }, 300);
      });
  };
  // æ›´æ–°æ‰€æœ‰å›¾è¡¨
@@ -672,10 +745,28 @@
  // é‡ç½®
  const handleReset = () => {
    searchForm.energyType = "";
    searchForm.dateRange = [];
    searchForm.monthRange = [];
    searchForm.energyType = "全部";
    // é‡ç½®ä¸ºé»˜è®¤æ—¶é—´èŒƒå›´
    const end = new Date();
    const start = new Date();
    // é»˜è®¤æœ€è¿‘7天
    start.setDate(start.getDate() - 6);
    searchForm.dateRange = [
      start.toISOString().split("T")[0],
      end.toISOString().split("T")[0],
    ];
    // é»˜è®¤æœ€è¿‘3个月
    start.setMonth(start.getMonth() - 2);
    searchForm.monthRange = [
      start.toISOString().slice(0, 7),
      end.toISOString().slice(0, 7),
    ];
    // é»˜è®¤å½“前年份
    searchForm.year = new Date().getFullYear();
    page.current = 1;
    handleQuery();
  };
src/views/productionPlan/productionPlan/index.vue
@@ -18,7 +18,7 @@
                    @keyup.enter="handleQuery" />
        </el-form-item>
        <el-form-item label="产品规格:">
          <el-input v-model="searchForm.productSpec"
          <el-input v-model="searchForm.specification"
                    placeholder="请输入"
                    clearable
                    style="width: 160px;"
@@ -102,7 +102,7 @@
        <el-row :gutter="20">
          <el-col :span="10">
            <el-form-item label="产品规格">
              <div class="info-display">{{ mergeForm.productSpec || '-' }}</div>
              <div class="info-display">{{ mergeForm.specification || '-' }}</div>
            </el-form-item>
          </el-col>
          <el-col :span="10">
@@ -239,8 +239,8 @@
                    placeholder="请输入产品名称" />
        </el-form-item>
        <el-form-item label="产品规格"
                      prop="productSpec">
          <el-input v-model="form.productSpec"
                      prop="specification">
          <el-input v-model="form.specification"
                    placeholder="请输入产品规格" />
        </el-form-item>
        <el-form-item label="物料编码"
@@ -365,7 +365,7 @@
    },
    {
      label: "产品规格",
      prop: "productSpec",
      prop: "specification",
      width: "150px",
      className: "spec-cell",
    },
@@ -484,7 +484,7 @@
            // è®¾ç½®è¡¨å•数据
            mergeForm.materialCode = row.materialCode;
            mergeForm.productName = row.productName || "";
            mergeForm.productSpec = row.productSpec || "";
            mergeForm.specification = row.specification || "";
            mergeForm.length = row.length || 0;
            mergeForm.width = row.width || 0;
            mergeForm.height = row.height || 0;
@@ -526,7 +526,7 @@
  const mergeForm = reactive({
    materialCode: "",
    productName: "",
    productSpec: "",
    specification: "",
    length: 0,
    width: 0,
    height: 0,
@@ -563,7 +563,7 @@
    applyNo: "",
    customerName: "",
    productName: "",
    productSpec: "",
    specification: "",
    materialCode: "",
    quantity: 0,
    volume: 0,
@@ -582,7 +582,9 @@
      { required: true, message: "请输入客户名称", trigger: "blur" },
    ],
    productName: [{ required: true, message: "请输入产品名称", trigger: "blur" }],
    productSpec: [{ required: true, message: "请输入产品规格", trigger: "blur" }],
    specification: [
      { required: true, message: "请输入产品规格", trigger: "blur" },
    ],
    materialCode: [
      { required: true, message: "请输入物料编码", trigger: "blur" },
    ],
@@ -686,7 +688,7 @@
    searchForm: {
      customerName: "",
      productName: "",
      productSpec: "",
      specification: "",
      materialCode: "",
      applyNo: "",
      dateRange: [],
@@ -706,7 +708,7 @@
    Object.assign(searchForm.value, {
      customerName: "",
      productName: "",
      productSpec: "",
      specification: "",
      materialCode: "",
      applyNo: "",
      dateRange: [],
@@ -820,7 +822,7 @@
    const firstRow = selectedRows.value[0];
    mergeForm.materialCode = selectedserialNo.value;
    mergeForm.productName = firstRow.productName || "";
    mergeForm.productSpec = firstRow.productSpec || "";
    mergeForm.specification = firstRow.specification || "";
    mergeForm.length = firstRow.length || 0;
    mergeForm.width = firstRow.width || 0;
    mergeForm.height = firstRow.height || 0;
@@ -948,7 +950,7 @@
      applyNo: "",
      customerName: "",
      productName: "",
      productSpec: "",
      specification: "",
      materialCode: "",
      quantity: 0,
      volume: 0,
@@ -972,7 +974,7 @@
      applyNo: row.applyNo || "",
      customerName: row.customerName || "",
      productName: row.productName || "",
      productSpec: row.productSpec || "",
      specification: row.specification || "",
      materialCode: row.materialCode || "",
      quantity: row.quantity || 0,
      volume: row.volume || 0,
src/views/reportAnalysis/unitEnergyConsumption/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,405 @@
<template>
  <div class="app-container">
    <!-- æœç´¢åŒºåŸŸ -->
    <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="年份:">
          <el-select v-model="searchForm.year"
                     placeholder="请选择年份"
                     style="width: 120px;"
                     @change="handleQuery">
            <el-option v-for="year in years"
                       :key="year"
                       :label="year + 'å¹´'"
                       :value="year" />
          </el-select>
        </el-form-item>
        <el-form-item label="能耗类型:">
          <el-select v-model="searchForm.energyType"
                     placeholder="全部"
                     clearable
                     style="width: 140px;"
                     @change="handleQuery">
            <el-option label="全部"
                       value="全部" />
            <el-option label="æ°´"
                       value="æ°´" />
            <el-option label="电"
                       value="电" />
            <el-option label="蒸汽"
                       value="蒸汽" />
          </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="chart-section">
      <h2 class="section-header">
        <el-icon class="header-icon">
          <TrendCharts />
        </el-icon>
        èƒ½è€—单耗趋势
      </h2>
      <div class="chart-card">
        <div ref="consumptionChart"
             class="chart-content"></div>
      </div>
    </div>
    <!-- æ•°æ®è¡¨æ ¼ -->
    <div class="table-section">
      <h2 class="section-header">
        <el-icon class="header-icon">
          <List />
        </el-icon>
        èƒ½è€—单耗数据
      </h2>
      <el-table :data="tableData"
                v-loading="tableLoading"
                border>
        <el-table-column prop="energyType"
                         label="能耗"
                         width="100"
                         align="center">
          <template #default="scope">
            <el-tag :type="getEnergyTypeType(scope.row.energyType)">
              {{ scope.row.energyType }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="unit"
                         label="单位"
                         width="120"
                         align="center" />
        <el-table-column label="月度数据">
          <el-table-column prop="monthlyUnitConsumption"
                           label="月度累计单耗"
                           align="right">
            <template #default="scope">
              <span class="data-value">{{ scope.row.monthlyUnitConsumption }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="monthlyConsumption"
                           label="月度累计用量/月度累计产量"
                           align="right">
            <template #default="scope">
              <span class="data-value">{{ scope.row.monthlyConsumption }}/{{ scope.row.monthlyProduction }}</span>
            </template>
          </el-table-column>
        </el-table-column>
        <el-table-column label="年度数据">
          <el-table-column prop="annualUnitConsumption"
                           label="年度累计单耗"
                           align="right">
            <template #default="scope">
              <span class="data-value">{{ scope.row.annualUnitConsumption }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="annualConsumption"
                           label="年度累计用量/年度累计产量"
                           align="right">
            <template #default="scope">
              <span class="data-value">{{ scope.row.annualConsumption }}/{{ scope.row.annualProduction }}</span>
            </template>
          </el-table-column>
        </el-table-column>
      </el-table>
    </div>
  </div>
</template>
<script setup>
  import { ref, reactive, onMounted, nextTick } from "vue";
  import { ElMessage } from "element-plus";
  import { TrendCharts, List } from "@element-plus/icons-vue";
  import * as echarts from "echarts";
  // æœç´¢è¡¨å•
  const searchForm = reactive({
    year: new Date().getFullYear(),
    energyType: "",
  });
  // ç”Ÿæˆå¹´ä»½é€‰é¡¹
  const years = [];
  const currentYear = new Date().getFullYear();
  for (let i = currentYear - 5; i <= currentYear; i++) {
    years.push(i);
  }
  // è¡¨æ ¼æ•°æ®
  const tableData = ref([]);
  const tableLoading = ref(false);
  // å›¾è¡¨å¼•用
  const consumptionChart = ref(null);
  let consumptionChartInstance = null;
  // èŽ·å–èƒ½è€—ç±»åž‹æ ‡ç­¾ç±»åž‹
  const getEnergyTypeType = type => {
    const typeMap = {
      æ°´: "primary",
      ç”µ: "warning",
      è’¸æ±½: "success",
    };
    return typeMap[type] || "info";
  };
  // åˆå§‹åŒ–图表
  const initChart = () => {
    nextTick(() => {
      if (consumptionChart.value) {
        consumptionChartInstance = echarts.init(consumptionChart.value);
        updateChart();
      }
    });
  };
  // æ›´æ–°å›¾è¡¨
  const updateChart = () => {
    const data = tableData.value;
    const months = [
      "1月",
      "2月",
      "3月",
      "4月",
      "5月",
      "6月",
      "7月",
      "8月",
      "9月",
      "10月",
      "11月",
      "12月",
    ];
    // å‡†å¤‡å›¾è¡¨æ•°æ®
    const series = [];
    const energyTypes = ["æ°´", "电", "蒸汽"];
    energyTypes.forEach(type => {
      const typeData = data.find(item => item.energyType === type);
      if (typeData && typeData.monthlyData) {
        series.push({
          name: type,
          type: "line",
          data: typeData.monthlyData.map(item => item.unitConsumption),
          smooth: true,
          symbol: "circle",
          symbolSize: 8,
          lineStyle: {
            width: 3,
          },
          itemStyle: {
            color:
              getEnergyTypeType(type) === "primary"
                ? "#409EFF"
                : getEnergyTypeType(type) === "warning"
                ? "#E6A23C"
                : "#67C23A",
          },
        });
      }
    });
    const option = {
      tooltip: {
        trigger: "axis",
        backgroundColor: "rgba(255, 255, 255, 0.95)",
        borderColor: "#409EFF",
        borderWidth: 1,
        textStyle: { color: "#303133" },
      },
      legend: {
        data: energyTypes,
        top: 0,
        right: 10,
        textStyle: { color: "#606266" },
      },
      grid: {
        left: "3%",
        right: "4%",
        bottom: "10%",
        top: "15%",
        containLabel: true,
      },
      xAxis: {
        type: "category",
        data: months,
        axisLabel: { color: "#606266" },
        axisLine: { lineStyle: { color: "#ebeef5" } },
        splitLine: { show: false },
      },
      yAxis: {
        type: "value",
        name: "单耗",
        nameTextStyle: { color: "#606266" },
        axisLabel: { color: "#606266" },
        axisLine: { show: false },
        splitLine: { lineStyle: { color: "#f0f2f5" } },
      },
      series: series,
    };
    consumptionChartInstance.setOption(option);
  };
  // æŸ¥è¯¢
  const handleQuery = () => {
    tableLoading.value = true;
    // æ¨¡æ‹ŸæŽ¥å£è°ƒç”¨
    setTimeout(() => {
      generateMockData();
      tableLoading.value = false;
      updateChart();
    }, 500);
  };
  // é‡ç½®
  const handleReset = () => {
    searchForm.year = new Date().getFullYear();
    searchForm.energyType = "";
    handleQuery();
  };
  // å¯¼å‡º
  const handleExport = () => {
    ElMessage.success("报表导出成功");
  };
  // ç”Ÿæˆå‡æ•°æ®
  const generateMockData = () => {
    const energyTypes = [
      {
        energyType: "æ°´",
        unit: "吨/立方米",
        monthlyUnitConsumption: (Math.random() * 0.5 + 0.8).toFixed(4),
        monthlyConsumption: Math.floor(Math.random() * 5000 + 10000),
        monthlyProduction: Math.floor(Math.random() * 10000 + 20000),
        annualUnitConsumption: (Math.random() * 0.3 + 0.9).toFixed(4),
        annualConsumption: Math.floor(Math.random() * 60000 + 120000),
        annualProduction: Math.floor(Math.random() * 120000 + 240000),
        monthlyData: generateMonthlyData(0.8, 1.3),
      },
      {
        energyType: "电",
        unit: "度/立方米",
        monthlyUnitConsumption: (Math.random() * 2 + 5).toFixed(4),
        monthlyConsumption: Math.floor(Math.random() * 50000 + 100000),
        monthlyProduction: Math.floor(Math.random() * 10000 + 20000),
        annualUnitConsumption: (Math.random() * 1.5 + 5.5).toFixed(4),
        annualConsumption: Math.floor(Math.random() * 600000 + 1200000),
        annualProduction: Math.floor(Math.random() * 120000 + 240000),
        monthlyData: generateMonthlyData(5, 7),
      },
      {
        energyType: "蒸汽",
        unit: "吨/立方米",
        monthlyUnitConsumption: (Math.random() * 0.3 + 0.5).toFixed(4),
        monthlyConsumption: Math.floor(Math.random() * 3000 + 6000),
        monthlyProduction: Math.floor(Math.random() * 10000 + 20000),
        annualUnitConsumption: (Math.random() * 0.2 + 0.55).toFixed(4),
        annualConsumption: Math.floor(Math.random() * 36000 + 72000),
        annualProduction: Math.floor(Math.random() * 120000 + 240000),
        monthlyData: generateMonthlyData(0.5, 0.8),
      },
    ];
    if (searchForm.energyType && searchForm.energyType !== "全部") {
      tableData.value = energyTypes.filter(
        item => item.energyType === searchForm.energyType
      );
    } else {
      tableData.value = energyTypes;
    }
  };
  // ç”Ÿæˆæœˆåº¦æ•°æ®
  const generateMonthlyData = (min, max) => {
    const data = [];
    for (let i = 1; i <= 12; i++) {
      data.push({
        month: i,
        unitConsumption: (Math.random() * (max - min) + min).toFixed(4),
      });
    }
    return data;
  };
  // çª—口大小变化时重新渲染图表
  const handleResize = () => {
    consumptionChartInstance && consumptionChartInstance.resize();
  };
  onMounted(() => {
    handleQuery();
    initChart();
    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;
  }
  .chart-section {
    margin-bottom: 30px;
  }
  .table-section {
    margin-bottom: 20px;
  }
  .section-header {
    display: flex;
    align-items: center;
    font-size: 18px;
    font-weight: bold;
    color: #303133;
    margin-bottom: 15px;
    padding-left: 10px;
    border-left: 3px solid #409eff;
    .header-icon {
      margin-right: 8px;
      color: #409eff;
    }
  }
  .chart-card {
    background: #fff;
    border-radius: 4px;
    border: 1px solid #ebeef5;
    padding: 20px;
    .chart-content {
      height: 400px;
    }
  }
  .data-value {
    font-weight: bold;
    color: #409eff;
  }
</style>