yaowanxin
2025-08-08 c8ba396b0b879d2c4bd78eaa4240525205d4d81d
用电时段界面
已修改2个文件
821 ■■■■ 文件已修改
src/api/energyManagement/index.js 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/energyManagement/energyPeriodTime/index.vue 813 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/energyManagement/index.js
@@ -28,7 +28,7 @@
// 时间周期-分页查询
export function periodListPage(query) {
  return request({
    url: '/electricityConsumptionPeriod/listPage',
    url: '/energyPeriod/listPage',
    method: 'get',
    params: query,
  })
@@ -53,7 +53,7 @@
// 时间周期-删除
export function periodDelete(query) {
  return request({
    url: '/electricityConsumptionPeriod/delete',
    url: '/energyPeriod/delete',
    method: 'delete',
    data: query,
  })
@@ -80,7 +80,7 @@
// 时间周期-新增
export function periodAdd(query) {
  return request({
    url: '/electricityConsumptionPeriod/add',
    url: '/energyPeriod/add',
    method: 'post',
    data: query,
  })
@@ -104,7 +104,7 @@
// 时间周期-修改
export function periodUpdate(query) {
  return request({
    url: '/electricityConsumptionPeriod/update',
    url: '/energyPeriod/update',
    method: 'post',
    data: query,
  })
src/views/energyManagement/energyPeriodTime/index.vue
@@ -1,480 +1,407 @@
<template>
  <div class="app-container">
    <!-- 搜索栏 -->
    <el-form :model="searchForm" :inline="true" class="search-form">
      <el-form-item label="时段名称">
        <el-input
          v-model="searchForm.periodName"
          placeholder="请输入时段名称"
          clearable
          prefix-icon="Search"
          @change="handleQuery"
    <div class="search_form">
      <div>
        <span class="search_title">时间范围:</span>
        <el-time-picker
            style="width: 240px;margin-right: 10px"
            v-model="searchForm.startTime"
            value-format="HH:mm:ss"
            format="HH:mm:ss"
            type="time"
            placeholder="请选择开始时间"
            clearable
        />
        <el-time-picker
            style="width: 240px;margin-right: 10px"
            v-model="searchForm.endTime"
            value-format="HH:mm:ss"
            format="HH:mm:ss"
            type="time"
            placeholder="请选择结束时间"
            clearable
        />
        <!-- <el-time-picker
            v-model="searchForm.timeRange"
            is-range
            arrow-control
            range-separator="To"
            start-placeholder="选择结束时间"
            end-placeholder="选择结束时间"
        /> -->
        <span class="search_title">电价(元/度):</span>
        <el-input
            v-model="searchForm.price"
            style="width: 240px"
            placeholder="请输入电价"
            @change="handleQuery"
            clearable
            :prefix-icon="Search"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="handleQuery">搜索</el-button>
        <el-button @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>
    <!-- 操作按钮 -->
    <div class="table-actions">
      <el-button
        type="primary"
        icon="Plus"
        @click="handleAdd"
        v-hasPermi="['energy:period:add']"
      >新增时段</el-button>
        <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
        >搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </div>
      <div>
        <el-button type="primary" @click="openForm('add')">新增</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
    </div>
    <!-- 时段列表表格 -->
    <el-table
      v-loading="loading"
      :data="periodList"
      @selection-change="handleSelectionChange"
      border
    <div class="table_list">
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :page="page"
          :isSelection="true"
          @selection-change="handleSelectionChange"
          :tableLoading="tableLoading"
          @pagination="pagination"
      ></PIMTable>
    </div>
    <el-dialog
        v-model="dialogFormVisible"
        title="用电时段管理"
        width="70%"
        @close="closeDia"
    >
      <el-table-column type="selection" width="55" align="center" />
      <el-table-column label="序号" type="index" width="60" align="center" />
      <el-table-column
        label="时段名称"
        prop="periodName"
        align="center"
        :show-overflow-tooltip="true"
      />
      <el-table-column
        label="时段单价(元/度)"
        prop="price"
        align="center"
        :formatter="formatPrice"
      />
      <el-table-column
        label="状态"
        prop="status"
        align="center"
        width="100"
      <el-form
          :model="form"
          label-width="140px"
          label-position="top"
          :rules="rules"
          ref="formRef"
      >
        <template #default="scope">
          <dict-tag :options="statusOptions" :value="scope.row.status" />
        </template>
      </el-table-column>
      <el-table-column
        label="创建时间"
        prop="createTime"
        align="center"
        width="180"
      />
      <el-table-column label="操作" align="center" width="200">
        <template #default="scope">
          <el-button
            link
            type="primary"
            icon="Edit"
            @click="handleUpdate(scope.row)"
            v-hasPermi="['energy:period:edit']"
          >修改</el-button>
          <el-button
            link
            type="primary"
            icon="Delete"
            @click="handleDelete(scope.row)"
            v-hasPermi="['energy:period:remove']"
          >删除</el-button>
          <el-button
            link
            type="primary"
            icon="Setting"
            @click="handleTimeConfig(scope.row)"
            v-hasPermi="['energy:period:config']"
          >时间配置</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页控件 -->
    <pagination
      v-show="total > 0"
      :total="total"
      v-model:page="queryParams.pageNum"
      v-model:limit="queryParams.pageSize"
      @pagination="getList"
    />
    <!-- 新增/编辑时段对话框 -->
    <el-dialog
      :title="title"
      v-model="open"
      width="500px"
      append-to-body
    >
      <el-form
        ref="formRef"
        :model="form"
        :rules="rules"
        label-width="100px"
      >
        <el-form-item label="时段名称" prop="periodName">
          <el-input v-model="form.periodName" placeholder="请输入时段名称" />
        </el-form-item>
        <el-form-item label="时段单价(元/度)" prop="price">
          <el-input
            v-model="form.price"
            type="number"
            placeholder="请输入时段单价"
            step="0.01"
            min="0"
          />
        </el-form-item>
        <el-form-item label="状态" prop="status">
          <el-radio-group v-model="form.status">
            <el-radio :label="1">启用</el-radio>
            <el-radio :label="0">禁用</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input
            v-model="form.remark"
            type="textarea"
            placeholder="请输入备注信息"
            rows="3"
          />
        </el-form-item>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="开始时间:" prop="startTime">
              <el-time-picker
                  style="width: 100%"
                  v-model="form.startTime"
                  value-format="HH:mm:ss"
                  format="HH:mm:ss"
                  type="time"
                  placeholder="请选择开始时间"
                  clearable
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="结束时间:" prop="endTime">
              <el-time-picker
                  style="width: 100%"
                  v-model="form.endTime"
                  value-format="HH:mm:ss"
                  format="HH:mm:ss"
                  type="time"
                  placeholder="请选择结束时间"
                  clearable
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
<!--          <el-col :span="12">-->
<!--            <el-form-item label="时段名称:" prop="timeName">-->
<!--              <el-input-->
<!--                  v-model="form.timeName"-->
<!--                  placeholder="请输入时段名称"-->
<!--                  clearable-->
<!--                  :disabled="operationType !== 'add'"-->
<!--              />-->
<!--            </el-form-item>-->
<!--          </el-col>-->
          <el-col :span="12">
            <el-form-item label="电价(元/度):" prop="price">
              <el-input
                  v-model="form.price"
                  placeholder="请输入电价"
                  clearable
                  type="number"
                  step="0.01"
                  min="0"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="cancel">取消</el-button>
          <el-button type="primary" @click="submitForm">确定</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 时间段配置对话框 -->
    <el-dialog
      title="峰平谷尖时间配置"
      v-model="timeConfigOpen"
      width="800px"
      append-to-body
    >
      <el-form ref="timeFormRef" :model="timeForm" label-width="100px">
        <el-form-item label="时段名称">
          <el-input v-model="timeForm.periodName" disabled />
        </el-form-item>
        <el-table
          :data="timeForm.timeSegments"
          border
          style="width: 100%"
          @selection-change="handleTimeSelectionChange"
        >
          <el-table-column type="selection" width="55" align="center" />
          <el-table-column label="序号" type="index" width="60" align="center" />
          <el-table-column label="类型" align="center" prop="segmentType" width="100">
            <template #default="scope">
              <el-select v-model="scope.row.segmentType" placeholder="请选择">
                <el-option label="峰" value="peak" />
                <el-option label="平" value="flat" />
                <el-option label="谷" value="valley" />
                <el-option label="尖" value="sharp" />
              </el-select>
            </template>
          </el-table-column>
          <el-table-column label="开始时间" align="center" prop="startTime" width="140">
            <template #default="scope">
              <el-time-picker
                v-model="scope.row.startTime"
                format="HH:mm"
                value-format="HH:mm"
                placeholder="选择开始时间"
              />
            </template>
          </el-table-column>
          <el-table-column label="结束时间" align="center" prop="endTime" width="140">
            <template #default="scope">
              <el-time-picker
                v-model="scope.row.endTime"
                format="HH:mm"
                value-format="HH:mm"
                placeholder="选择结束时间"
              />
            </template>
          </el-table-column>
          <el-table-column label="操作" align="center" width="100">
            <template #default="scope">
              <el-button
                link
                type="primary"
                icon="Delete"
                @click="handleTimeDelete(scope.row)"
              >删除</el-button>
            </template>
          </el-table-column>
        </el-table>
        <div style="margin-top: 15px; text-align: right;">
          <el-button type="primary" @click="handleAddTimeSegment">新增时间段</el-button>
        </div>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="cancelTimeConfig">取消</el-button>
          <el-button type="primary" @click="submitTimeConfig">保存配置</el-button>
          <el-button type="primary" @click="submitForm">确认</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup name="EnergyPeriodTime">
import { ref, reactive, toRefs, getCurrentInstance, onMounted } from 'vue';
import { listPeriod, getPeriod, delPeriod, addPeriod, updatePeriod, getTimeSegments, saveTimeSegments } from "@/api/energyManagement/index.js";
import { useDict } from '@/hooks/useDict';
const { proxy } = getCurrentInstance();
const { sys_normal_disable } = useDict('sys_normal_disable');
<script setup>
import {Search} from "@element-plus/icons-vue";
import {onMounted, ref} from "vue";
import {ElMessageBox} from "element-plus";
import {getToken} from "@/utils/auth.js";
import {periodListPage,periodDelete,periodAdd,periodUpdate} from "@/api/energyManagement/index.js";
const { proxy } = getCurrentInstance();
// 状态定义
const state = reactive({
  loading: false,
  open: false,
  timeConfigOpen: false,
  title: "",
  total: 0,
  periodList: [],
  timeSegments: [],
  selectedRows: [],
  timeSelectedRows: [],
  statusOptions: sys_normal_disable,
  // 搜索表单
  searchForm: {
    periodName: ""
  },
  // 查询参数
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    periodName: undefined
  },
  // 表单参数
  form: {
    periodId: undefined,
    periodName: "",
    price: 0,
    status: 1,
    remark: ""
  },
  // 时间配置表单
  timeForm: {
    periodId: undefined,
    periodName: "",
    timeSegments: []
  },
  // 表单校验
  rules: {
    periodName: [
      { required: true, message: "请输入时段名称", trigger: "blur" }
    ],
    price: [
      { required: true, message: "请输入时段单价", trigger: "blur" },
      { type: "number", message: "请输入有效的数字", trigger: "blur" },
      { min: 0, message: "单价不能小于0", trigger: "blur" }
    ]
  }
const data = reactive({
    searchForm: {
        startTime: "",
        endTime: "",
        price: ""
    },
    form: {
        // timeName: "",
        startTime: "",
        endTime: "",
        price: ""
    }
});
const {
  loading, open, timeConfigOpen, title, total, periodList,
  selectedRows, timeSelectedRows, statusOptions, searchForm,
  queryParams, form, timeForm, rules
} = toRefs(state);
const formRef = ref(null);
const timeFormRef = ref(null);
// 生命周期 - 挂载完成
onMounted(() => {
  getList();
const { searchForm,form } = toRefs(data);
const page = ref({
  current: 1,
  size: 10,
  total: 0
});
const dialogFormVisible = ref(false);
const selectedRows = ref([]);
const operationType = ref('');
const tableData = ref([]);
const emit = defineEmits(['close'])
const tableLoading = ref(false);
const tableColumn = ref([
    // {
    //     label: "时段名称",
    //     prop: "timeName",
    //     width: 200,
    // },
    {
        label: "开始时间",
        prop: "startTime",
    },
    {
        label: "结束时间",
        prop: "endTime",
    },
  {
    label: "电价(元/度)",
    prop: "price",
    width: 200,
  },
    {
        dataType: "action",
        label: "操作",
        align: "center",
        fixed: 'right',
        operation: [
            {
                name: "编辑",
                type: "text",
                clickFun: (row) => {
                    openForm("edit", row);
                },
            },
        ],
    },
]);
// 获取时段列表
const getList = async () => {
  state.loading = true;
  try {
    const response = await listPeriod(state.queryParams);
    state.periodList = response.rows;
    state.total = response.total;
  } catch (error) {
    proxy.$modal.msgError("获取时段列表失败");
  } finally {
    state.loading = false;
  }
};
// 搜索按钮点击事件
const handleQuery = () => {
  state.queryParams.pageNum = 1;
  state.queryParams.periodName = state.searchForm.periodName;
  getList();
};
// 重置按钮点击事件
const resetQuery = () => {
  state.searchForm = {
    periodName: ""
  };
  state.queryParams = {
    pageNum: 1,
    pageSize: 10,
    periodName: undefined
  };
  getList();
};
// 新增按钮点击事件
const handleAdd = () => {
  state.form = {
    periodId: undefined,
    periodName: "",
    price: 0,
    status: 1,
    remark: ""
  };
  state.open = true;
  state.title = "新增时段";
};
// 修改按钮点击事件
const handleUpdate = async (row) => {
  state.form = { ...row };
  state.open = true;
  state.title = "修改时段";
};
// 删除按钮点击事件
const handleDelete = async (row) => {
  await proxy.$modal.confirm("是否确认删除该时段?");
  try {
    await delPeriod(row.periodId);
    getList();
    proxy.$modal.msgSuccess("删除成功");
  } catch (error) {
    proxy.$modal.msgError("删除失败");
  }
};
// 时间段配置按钮点击事件
const handleTimeConfig = async (row) => {
  state.timeForm.periodId = row.periodId;
  state.timeForm.periodName = row.periodName;
  try {
    const response = await getTimeSegments(row.periodId);
    state.timeForm.timeSegments = response.data || [];
  } catch (error) {
    state.timeForm.timeSegments = [];
  }
  state.timeConfigOpen = true;
};
// 新增时间段
const handleAddTimeSegment = () => {
  state.timeForm.timeSegments.push({
    segmentId: undefined,
    periodId: state.timeForm.periodId,
    segmentType: "flat",
    startTime: "00:00",
    endTime: "00:00"
  });
};
// 删除时间段
const handleTimeDelete = (row) => {
  const index = state.timeForm.timeSegments.indexOf(row);
  if (index !== -1) {
    state.timeForm.timeSegments.splice(index, 1);
  }
};
// 提交时段表单
const submitForm = async () => {
  await proxy.$refs[formRef.value].validate();
  try {
    if (state.form.periodId) {
      await updatePeriod(state.form);
      proxy.$modal.msgSuccess("修改成功");
    } else {
      await addPeriod(state.form);
      proxy.$modal.msgSuccess("新增成功");
    }
    state.open = false;
    getList();
  } catch (error) {
    proxy.$modal.msgError(state.form.periodId ? "修改失败" : "新增失败");
  }
};
// 提交时间段配置
const submitTimeConfig = async () => {
  try {
    await saveTimeSegments(state.timeForm.timeSegments);
    proxy.$modal.msgSuccess("时间配置保存成功");
    state.timeConfigOpen = false;
  } catch (error) {
    proxy.$modal.msgError("时间配置保存失败");
  }
};
// 取消按钮点击事件
const cancel = () => {
  state.open = false;
  proxy.$refs[formRef.value].resetFields();
};
// 取消时间配置按钮点击事件
const cancelTimeConfig = () => {
  state.timeConfigOpen = false;
};
// 选择项变化事件
// 表格选择数据
const handleSelectionChange = (selection) => {
  state.selectedRows = selection;
    selectedRows.value = selection;
};
const formDia = ref()
const upload = reactive({
    // 是否显示弹出层(客户导入)
    open: false,
    // 弹出层标题(客户导入)
    title: "",
    // 是否禁用上传
    isUploading: false,
    // 设置上传的请求头部
    headers: { Authorization: "Bearer " + getToken() },
    // 上传的地址
    url: import.meta.env.VITE_APP_BASE_API + "/equipmentEnergyConsumption/importData",
    // 文件上传前的回调
    beforeUpload: (file) => {
        console.log('文件即将上传', file);
        // 可以在此处做文件类型或大小校验
        const isValid = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
        if (!isValid) {
            proxy.$modal.msgError("只能上传 Excel 文件");
        }
        return isValid;
    },
    // 文件状态改变时的回调
    onChange: (file, fileList) => {
        console.log('文件状态改变', file, fileList);
    },
    // 文件上传成功时的回调
    onSuccess: (response, file, fileList) => {
        console.log('上传成功', response, file, fileList);
        if(response.code === 200){
            proxy.$modal.msgSuccess("文件上传成功");
        }else if(response.code === 500){
            ElMessageBox.error(response.msg);
        }else{
            ElMessageBox.warning(response.msg);
        }
    },
    // 文件上传失败时的回调
    onError: (error, file, fileList) => {
        console.error('上传失败', error, file, fileList);
        ElMessageBox.error("文件上传失败");
    },
    // 文件上传进度回调
    onProgress: (event, file, fileList) => {
        console.log('上传中...', event.percent);
    }
});
// 时间段选择项变化事件
const handleTimeSelectionChange = (selection) => {
  state.timeSelectedRows = selection;
// 查询列表
/** 搜索按钮操作 */
const handleQuery = () => {
    page.current = 1;
    getList();
};
//重置
const resetFilters = () => {
    searchForm.value = {
    startTime: "",
    endTime: "",
    price: ""
  };
  getList();
// 格式化价格显示
const formatPrice = (row, column, cellValue) => {
  return cellValue ? cellValue.toFixed(2) : "0.00";
};
const pagination = (obj) => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
};
const getList = () => {
    tableLoading.value = true;
    periodListPage({ ...searchForm.value, ...page.value }).then((res) => {
            tableLoading.value = false;
            if (res && res.data) {
                tableData.value = res.data.records || [];
                page.total = res.data.total || 0;
            } else {
                tableData.value = [];
                page.total = 0;
                ElMessageBox.warning('未获取到数据');
            }
        })
        .catch((err) => {
            tableLoading.value = false;
            console.error('数据加载失败:', err);
            ElMessageBox.error('数据加载失败,请重试');
        });
};
// 打开弹框
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
    // form.value.maintainer = userStore.nickName;
    // form.value.maintenanceTime = getCurrentDate();
    form.value = {}
    proxy.resetForm("formRef");
    periodListPage().then((res) => {
        codeList.value = res.data;
    });
    if (type === "edit") {
        form.value = {...row}
    }
}
// 打开弹框
const openForm = (type, row) => {
    openDialog(type, row)
};
// 提交表单
const submitForm = () => {
    proxy.$refs["formRef"].validate(valid => {
        if (valid) {
            if (operationType.value === "add") {
                periodAdd(form.value).then(response => {
                    proxy.$modal.msgSuccess("新增成功")
                    closeDia()
                    getList()
                })
            } else {
                periodUpdate(form.value).then(response => {
                    proxy.$modal.msgSuccess("修改成功")
                    closeDia()
                    getList()
                })
            }
        }
    })
}
// 关闭弹框
const closeDia = () => {
    proxy.resetForm("formRef");
    dialogFormVisible.value = false;
    emit('close')
};
/** 导入按钮操作 */
function handleImport() {
    upload.title = "设备能耗";
    upload.open = true;
    // 清空上次上传的文件列表
    nextTick(() => {
        proxy.$refs["uploadRef"]?.clearFiles();
    });
}
function importTemplate() {
    proxy.download(
        "/equipmentEnergyConsumption/export",
        {},
        '设备能耗导入模版.xlsx'
    );
}
/** 提交上传文件 */
function submitFileForm() {
    proxy.$refs["uploadRef"].submit();
}
// 导出方法
defineExpose({
  getList
/** 弹框关闭时清空文件列表 */
function handleDialogClose() {
    nextTick(() => {
        proxy.$refs["uploadRef"]?.clearFiles();
    });
}
const handleDelete = () => {
    let ids = [];
    if (selectedRows.value.length > 0) {
        ids = selectedRows.value.map((item) => item.id);
    } else {
        proxy.$modal.msgWarning("请选择数据");
        return;
    }
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", {
        confirmButtonText: "确认",
        cancelButtonText: "取消",
        type: "warning",
    })
    .then(() => {
        tableLoading.value = true;
        periodDelete(ids)
            .then((res) => {
                proxy.$modal.msgSuccess("删除成功");
                getList();
            })
            .finally(() => {
                tableLoading.value = false;
            });
    })
    .catch(() => {
        proxy.$modal.msg("已取消");
    });
};
onMounted(() => {
    getList();
});
</script>
<style scoped>
.search-form {
  margin-bottom: 16px;
}
.table-actions {
  margin-bottom: 16px;
  display: flex;
  justify-content: flex-start;
}
</style>