dongyukun
2025-04-24 5eda9aff304ddc16077cb6e48a9e89a1a695146d
Merge remote-tracking branch 'origin/master'
已修改3个文件
已添加4个文件
981 ■■■■■ 文件已修改
src/api/model/pre/influenceFactor/influenceFactorApi.ts 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/dict.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/analysis/index.vue 439 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/influenceFactor/MmPredictInfluenceFactorForm.vue 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/influenceFactor/MmPredictInfluenceFactorResult.vue 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/pre/influenceFactor/index.vue 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/model/sche/scheme/record/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/model/pre/influenceFactor/influenceFactorApi.ts
对比新文件
@@ -0,0 +1,33 @@
import request from '@/config/axios'
export const getPage = (params) => {
  return request.get({ url: '/model/pre/influence-factor/page', params })
}
export const getResultPage = (params) => {
  return request.get({ url: '/model/pre/influence-factor/getResultPage', params })
}
export const getResultList = (data) => {
  return request.post({ url: '/model/pre/influence-factor/getResultList', data })
}
export const getListByOutId = (outId) => {
  return request.get({ url: `/model/pre/influence-factor/getListByOutId?outId=${outId}`})
}
export const getInfo = (id) => {
  return request.get({ url: `/model/pre/influence-factor/get/${id}`})
}
export const create = (data) => {
  return request.post({ url: '/model/pre/influence-factor/create', data })
}
export const update = (data) => {
  return request.put({ url: '/model/pre/influence-factor/update', data })
}
export const deleteConfig = (id) => {
  return request.delete({ url: '/model/pre/influence-factor/delete?id=' + id })
}
src/utils/dict.ts
@@ -197,5 +197,6 @@
  MODEL_STATUS = 'model_status',
  ADJUST_DIRECTION = 'adjust_direction',
  ADJUST_TRIGGER_RULE = 'adjust_trigger_rule',
  ADJUST_VALUE_RULE = 'adjust_value_rule'
  ADJUST_VALUE_RULE = 'adjust_value_rule',
  INFLUENCE_FACTOR_PATTERN = 'influence_factor_pattern',
}
src/views/model/pre/analysis/index.vue
@@ -69,6 +69,7 @@
                node-key="id"
                ref="treeRef"
                highlight-current
                check-strictly
                :filter-node-method="filterNode"
                @check="onCheckTree"/>
            </div>
@@ -78,33 +79,23 @@
              <el-form :inline="true" :model="calRateForm" :rules="formRules" ref="calRateFormRef"
                       label-width="108px">
                <el-row>
                  <el-col :span="6">
                    <el-form-item label="预测项" prop="calItem" style="width: 90%">
                      <el-select size="small" v-model="calRateForm.calItem"
                                 @change="calItemBaseVale"
                                 placeholder="请选择">
                        <el-option
                          v-for="itemOut in formData.checkedItemData"
                          :key="itemOut.id"
                          :label="itemOut.label"
                          :value="itemOut.id"/>
                      </el-select>
                    </el-form-item>
                  </el-col>
                  <el-col :span="6">
<!--                  <el-col :span="6" style="display: flex;align-items: center;justify-content: center">-->
<!--                    <span>预测项:{{formData.checkedItemData?.label || ''}}</span>-->
<!--                  </el-col>-->
                  <el-col :span="8">
                    <el-form-item label="精准度偏差" prop="IN_DEVIATION">
                      <el-input-number size="small" v-model="calRateForm.IN_DEVIATION"
                                       controls-position="right" :min="0"/>
                    </el-form-item>
                  </el-col>
                  <el-col :span="6">
                  <el-col :span="8">
                    <el-form-item label="不可信率偏差" prop="OUT_DEVIATION">
                      <el-input-number size="small" v-model="calRateForm.OUT_DEVIATION"
                                       controls-position="right"
                                       :min="1"/>
                    </el-form-item>
                  </el-col>
                  <el-col :span="4">
                  <el-col :span="8">
                    <el-form-item>
                      <el-button size="small" type="primary" plain :loading="loading2"
                                 @click="calAccuracyRate">计算精准度
@@ -200,7 +191,37 @@
                  </el-col>
                </el-row>
              </el-form>
              <div ref="dataAnalysisChart" style="height: 500px;"></div>
              <div style="width: 100%;height: 700px;display: flex;flex-direction: row;">
                <div style="height: 100%;width: 80%">
                  <div ref="dataAnalysisChart" style="height: 50%;width: 100%"></div>
                  <div ref="influenceFactorChart" style="height: 50%;width: 100%"></div>
                </div>
                <div style="width: 20%;height: 100%;">
                  <div style="display: flex;flex-direction: row;align-items: center;margin-bottom: 4px;">
                    <div style="font-weight: bold;font-size: 14px">影响因素:</div>
                    <div style="width: calc(100% - 80px);">
                      <el-select v-model="influenceFactor" placeholder="请选择" size="small" @change="changeInfluenceFactor">
                        <el-option
                          v-for="(influenceFactor,index) in influenceFactorList"
                          :key="index"
                          :label="influenceFactor.factorOutputName"
                          :value="influenceFactor.factorOutputId"
                        />
                      </el-select>
                    </div>
                  </div>
                  <div class="chart-foot-table p-2" style="width: 100%;height: calc(100% - 20px);overflow-x: hidden;overflow-y: auto;">
                    <div style="display: flex;flex-direction: column;align-items: center;margin-bottom: 4px;">
                      <span>影响时间</span>
                      <span style="font-size: 16px;font-weight: bold">{{influenceFactorResultTime}}</span>
                    </div>
                    <div v-for="(result, index) in influenceFactorResult" :key="index" style="display: flex;flex-direction: row">
                      <span>{{result.factorOutputName}}:</span>
                      <span>{{result.value}}</span>
                    </div>
                  </div>
                </div>
              </div>
              <div class="chart-foot">
                <div class="chart-foot-content">
                  <h3 class="chart-foot-title">预警信息</h3>
@@ -249,18 +270,20 @@
  </el-card>
</template>
<script lang="ts" setup>
import {getYMDHMS} from "@/utils/dateUtil"
import {getYMDHMS,formatToDateTime} from "@/utils/dateUtil"
import * as McsApi from '@/api/model/mcs'
import * as influenceFactorApi from '@/api/model/pre/influenceFactor/influenceFactorApi'
import * as AlarmMessageApi from '@/api/model/pre/alarm/message'
import * as ScheSuggestApi from '@/api/model/sche/suggest'
import * as echarts from "echarts";
import {Search, DArrowLeft, DArrowRight, VideoPlay, VideoPause, CaretLeft, CaretRight} from '@element-plus/icons-vue'
import {lighten} from "@/utils/color";
defineOptions({name: 'AnalysisformData'})
const message = useMessage() // 消息弹窗
const {t} = useI18n() // 国际化
const dataAnalysisChart = ref(null);
const loading1 = ref(false) // 列表的加载中
const loading2 = ref(false) // 列表的加载中
const total = ref(0) // 列表的总页数
@@ -281,7 +304,7 @@
  predictStamp: '',
  chartCheck: ['T+L', '真实值'],
  chartOptions: ['T+N', 'T+L','T+L(未调整)', '当时', '真实值', '调整值', '预测累计', '真实累计'],
  checkedItemData: [],
  checkedItemData: undefined,
  backItem: '',
  backValue: 0,
  backCoe: 1,
@@ -318,15 +341,20 @@
const treeData = ref([])
const itemDataObject = ref()
const timer = ref()
let myChart = null;
const isPlay = ref(false)
const alarmList = ref([])
const suggestList = ref([])
const loadingAlarm = ref(false)
const loadingAdjust = ref(false)
// 影响因素结果列表
const influenceFactorResultList = ref([])
// 影响因素列表
const influenceFactorList = ref([])
// 选中影响因素
const influenceFactor = ref()
const formRules = reactive({
  calItem: [{required: true, message: '预测项不能为空', trigger: 'blur'}],
  IN_DEVIATION: [{required: true, message: '精准度偏差不能为空', trigger: 'blur'}],
  OUT_DEVIATION: [{required: true, message: '不可信率偏差不能为空', trigger: 'blur'}],
})
@@ -342,6 +370,8 @@
  return data.label.includes(value)
}
let xAxisData = []
/** 查询列表 */
const getList = async (isClear = true) => {
  loading1.value = true
@@ -350,13 +380,11 @@
      formData.value.chartCheck = ['真实值']
    }
    let chartCheckArray = formData.value.chartCheck;
    if (!formData.value.checkedItemData || formData.value.checkedItemData.length == 0) {
    if (!formData.value.checkedItemData) {
      itemData.value.option = {};
      return;
    }
    let outIds = formData.value.checkedItemData.map(item => {
      return item.id
    })
    let outIds = [formData.value.checkedItemData.id]
    const params = reactive({
      outIds: outIds,
      predictTime: formData.value.predictTime,
@@ -364,10 +392,32 @@
      endTime: formData.value.endTime
    })
    const data = await McsApi.getPreDataCharts(params)
    formData.value.predictTime = data.predictTime;
    formData.value.startTime = data.startTime
    formData.value.endTime = data.endTime
    // 默认影响时间
    changeInfluenceFactorTime(data.predictTime);
    // 获取影响因素结果列表
    influenceFactorResultList.value = await influenceFactorApi.getResultList({
      outIds: outIds,
      startTime: data.startTime,
      endTime: data.endTime
    })
    // 获取影响因素结果列表
    influenceFactorList.value = await influenceFactorApi.getListByOutId(formData.value.checkedItemData.id)
    if (influenceFactorList.value && influenceFactorList.value.length > 0) {
      // 根据factorOutputId去重,因为不同的统计规则会有重复的影响因素
      influenceFactorList.value = Array.from(new Map(influenceFactorList.value.map(item => [item.factorOutputId, item])).values());
      // 默认选中第一个影响因素
      influenceFactor.value = influenceFactorList.value?.[0]?.factorOutputId
      getInfluenceFactorChart(influenceFactorList.value?.[0]?.factorOutputId)
    }
    const paramsAlarm = reactive({
      outIds: outIds,
@@ -382,7 +432,7 @@
    suggestList.value = await ScheSuggestApi.getListByOut(paramsAlarm)
    loadingAdjust.value = false
    let xAxisData = data.categories;
    xAxisData = data.categories;
    let defaultYAxis = [
      {
        type: 'value',
@@ -641,7 +691,6 @@
      }
    }
    myChart = echarts.init(dataAnalysisChart.value);
    let option = {
      title: {
        text: ''
@@ -662,8 +711,8 @@
      },
      grid: {
        top: '20%',
        left: '3%',
        right: '6%',
        left: '5%',
        right: '5%',
        bottom: '3%',
        containLabel: true
      },
@@ -689,7 +738,12 @@
    if (isClear) {
      myChart.clear()
    }
    myChart.setOption(option)
  } finally {
    loading1.value = false
  }
@@ -697,10 +751,304 @@
  calItemBaseVale()
}
// 查询影响因素chart
const getInfluenceFactorChart = async (outId) => {
  loading1.value = true
  try {
    let outIds = [outId]
    const params = reactive({
      outIds: outIds,
      predictTime: formData.value.predictTime,
      startTime: formData.value.startTime,
      endTime: formData.value.endTime
    })
    const data = await McsApi.getPreDataCharts(params)
    if (!data?.dataViewList || data.dataViewList.length === 0) {
      myInfluenceFactorChart.clear()
      return
    }
    xAxisData = data.categories;
    let defaultYAxis = [
      {
        type: 'value',
        name: "累计值",
        splitLine: {show: false},
        axisLine: {show: true},
        position: 'right'
      },
      {
        type: 'value',
        name: "",
        splitLine: {show: false},
        axisLine: {show: true},
        position: 'left'
      }
    ];
    let yAxisData = [];
    let offset = 0;
    let yAxisIndex = 0;
    let legendData = [];
    let yMaxArr = [];
    let seriesData = [];
    seriesData.push({
      name: '',
      data: [null],
      type: 'line',
      smooth: true,
      color: 'green',
      markLine: {
        silent: true,
        lineStyle: {
          color: '#32a487',
          width: 2
        },
        data: [{
          xAxis: formData.value.predictTime
        }],
        label: {
          normal: {
            formatter: formData.value.predictTime
          }
        },
        symbol: ['circle', 'none'],
      },
    });
    itemDataObject.value = {}
    yAxisData.push({
      type: 'value',
      name: "累计值",
      position: 'right',
      splitLine: {
        show: false
      },
      axisLine: {
        show: true,
        lineStyle: {}
      },
      axisLabel: {
        formatter: '{value}'
      }
    })
    for (let i = 0; i < data.dataViewList.length; i++) {
      let dataView = data.dataViewList[i]
      itemDataObject.value[dataView.outId] = dataView;
      let maxValue = dataView.maxValue;
      let minValue = dataView.minValue;
      yAxisIndex = (formData.value.isMultipleY ? i : 0) + 1;
      let yMax = maxValue;
      if (maxValue < 0) {
        maxValue = 1;
      } else if (maxValue < 10) {
        yMax = (Math.ceil(maxValue * 11) / 10).toFixed(1);
      } else if (maxValue < 100) {
        yMax = (Math.ceil(maxValue * 1.1 / 5) * 5);
      } else {
        yMax = (Math.ceil(maxValue * 1.1 / 10) * 10);
      }
      yMaxArr.push(yMax);
      let yMin = minValue;
      if (minValue >= 0) {
        yMin = 0;
      } else if (minValue > -10) {
        yMin = (Math.floor(minValue * 11) / 10).toFixed(1);
      } else if (minValue > -100) {
        yMin = (Math.floor(minValue * 1.1 / 5) * 5);
      } else {
        yMin = (Math.floor(minValue * 1.1 / 10) * 10);
      }
      yAxisData.push({
        type: 'value',
        name: "",
        min: yMin,
        max: yMax,
        position: 'left',
        offset: offset,
        splitLine: {
          show: false
        },
        axisLine: {
          show: true,
          lineStyle: {}
        },
        axisLabel: {
          formatter: '{value}'
        }
      })
      offset = offset + 40
      //真实值
      legendData.push(dataView.resultName + '(真实)');
      seriesData.push({
        name: dataView.resultName + '(真实)',
        data: dataView.realData || [],
        type: 'line',
        yAxisIndex: yAxisIndex,
        showSymbol: false,
        smooth: false,
        lineStyle: {
          width: 2
        }
      });
      //T+L
      legendData.push(dataView.resultName + '(T+L)');
      seriesData.push({
        name: dataView.resultName + '(T+L)',
        data: dataView.preDataL || [],
        type: 'line',
        showSymbol: false,
        connectNulls: true,
        yAxisIndex: yAxisIndex,
        smooth: false,
        lineStyle: {
          width: 2
        }
      });
      // 当时
      legendData.push(dataView.resultName + '(当时)');
      seriesData.push({
        name: dataView.resultName + '(当时)',
        data: dataView.curData || [],
        type: 'line',
        yAxisIndex: yAxisIndex,
        showSymbol: true,
        smooth: false,
        lineStyle: {
          width: 3
        }
      });
      //预测累计
      legendData.push(dataView.resultName + '(预测累计)');
      seriesData.push({
        name: dataView.resultName + '(预测累计)',
        data: dataView.cumulantPreData || [],
        type: 'line',
        yAxisIndex: 0,
        showSymbol: false,
        connectNulls: true,
        smooth: false,
        lineStyle: {
          width: 2,
          type: 'dashed'
        }
      });
      // 真实累计
      legendData.push(dataView.resultName + '(真实累计)');
      seriesData.push({
        name: dataView.resultName + '(真实累计)',
        data: dataView.cumulantRealData || [],
        type: 'line',
        yAxisIndex: 0,
        showSymbol: false,
        connectNulls: true,
        smooth: false,
        lineStyle: {
          width: 2,
          type: 'dashed'
        }
      });
    }
    //如果最大值相差不大,改成一致大小
    if (yMaxArr.length > 1) {
      let max = Math.max.apply(null, yMaxArr);
      let min = Math.min.apply(null, yMaxArr);
      if (Math.abs((max - min) / max) <= 0.2) {
        for (let i = 0; i < yAxisData.length; i++) {
          yAxisData[i].max = max;
        }
      }
    }
    let option = {
      title: {
        text: ''
      },
      tooltip: {
        trigger: 'axis'
      },
      toolbox: {
        show: true,
        feature: {
          saveAsImage: {}
        }
      },
      legend: {
        show: true,
        data: legendData,
        top: 10
      },
      grid: {
        top: '20%',
        left: '5%',
        right: '5%',
        bottom: '3%',
        containLabel: true
      },
      xAxis: {
        type: 'category',
        boundaryGap: false,
        data: xAxisData
      },
      yAxis: formData.value.isMultipleY ? yAxisData : defaultYAxis,
      dataZoom: [
        {
          type: 'inside',
          start: 0,
          end: 100
        },
        {
          start: 0,
          end: 10
        }
      ],
      series: seriesData
    }
    myInfluenceFactorChart.clear()
    myInfluenceFactorChart.setOption(option)
  } finally {
    loading1.value = false
  }
}
onMounted(() => {
  initChart()
  resetForm()
  getPreItemTree()
})
const dataAnalysisChart = ref();
const influenceFactorChart = ref();
let myChart = ref({});
let myInfluenceFactorChart = ref({});
function initChart() {
  myChart = echarts.init(dataAnalysisChart.value)
  myInfluenceFactorChart = echarts.init(influenceFactorChart.value)
  // 监听点击事件
  myChart.getZr().on('click', 'series.line',function (params) {
    var pointInPixel = [params.offsetX, params.offsetY];
    var pointInData = myChart.convertFromPixel('grid', pointInPixel);
    const time = xAxisData[pointInData[0]];
    changeInfluenceFactorTime(time)
  });
}
let influenceFactorResult = ref([])
let influenceFactorResultTime = ref('')
// 影响因素时间改变
function changeInfluenceFactorTime(time) {
  if (time && new Date(time)?.getTime()) {
    influenceFactorResultTime.value = time
    influenceFactorResult.value = influenceFactorResultList.value?.[formData.value.checkedItemData?.id]?.filter(e => e.time === new Date(time).getTime()).sort((a, b) => b.value - a.value) || [];
  }
}// 选择影响因素
function changeInfluenceFactor(value) {
  getInfluenceFactorChart(value)
}
async function getPreItemTree() {
  treeData.value = await McsApi.getPredictItemTree()
@@ -781,16 +1129,21 @@
}
function onCheckTree(data, checked, indeterminate) {
  formData.value.checkedItemData = [];
  if (checked.checkedNodes) {
    let cns = [...checked.checkedNodes]
    for (let i = 0; i < cns.length; i++) {
      if (cns[i].id.indexOf('-') !== -1) {
        continue
      }
      formData.value.checkedItemData.push(cns[i])
    }
  }
  // 单选
  treeRef.value.setCheckedKeys([])
  treeRef.value.setCheckedNodes([data])
  formData.value.checkedItemData = data
  calRateForm.value.calItem = data.id
  // if (checked.checkedNodes) {
  //   let cns = [...checked.checkedNodes]
  //   for (let i = 0; i < cns.length; i++) {
  //     if (cns[i].id.indexOf('-') !== -1) {
  //       continue
  //     }
  //     formData.value.checkedItemData.push(cns[i])
  //   }
  // }
  debounce(getList, 1000);
}
@@ -827,8 +1180,8 @@
    calRateForm.value.realCumulant = dataView.hisCumulant;
    calDeviation(dataView.realData,dataView.preDataL,'deviation')
    calDeviation(dataView.cumulantRealData,dataView.cumulantPreData,'deviationCumulant')
    calAccuracyRate()
  }
  calAccuracyRate()
}
function calAccuracyRate() {
@@ -939,7 +1292,7 @@
    predictStamp: '',
    chartCheck: ['T+L', '真实值'],
    chartOptions: ['T+N', 'T+L','T+L(未调整)', '当时', '真实值', '调整值', '预测累计', '真实累计'],
    checkedItemData: [],
    checkedItemData: undefined,
    backItem: '',
    backValue: 0,
    backCoe: 1,
@@ -996,6 +1349,8 @@
  height: 100%;
  border: 1px solid lightgray;
  padding: 10px;
  overflow-y: auto;
  overflow-x: hidden;
}
.his-body-tree {
src/views/model/pre/influenceFactor/MmPredictInfluenceFactorForm.vue
对比新文件
@@ -0,0 +1,200 @@
<template>
  <Dialog v-model="dialogVisible" :title="dialogTitle" width="50%">
    <el-form
      ref="formRef"
      v-loading="formLoading"
      :model="formData"
      :rules="formRules"
      label-width="130px"
    >
      <el-row>
        <el-col :span="12">
          <el-form-item label="预测项" prop="outputId">
            <el-select-v2
              v-model="formData.outputId"
              :options="normalItemList || []"
              placeholder="请选择预测项"
              :props="{value:'value',label:'label',options:'children'}"
              clearable
              filterable
              :fit-input-width="false"
            />
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="统计方式" prop="pattern">
            <el-select v-model="formData.pattern" placeholder="请选择统计方式">
              <el-option
                v-for="dict in getStrDictOptions(DICT_TYPE.INFLUENCE_FACTOR_PATTERN)"
                :key="dict.value"
                :label="dict.label"
                :value="dict.value"
              />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="6">
          <el-form-item label="是否启用" prop="isEnable">
            <el-select v-model="formData.isEnable" placeholder="请选择">
              <el-option
                v-for="dict in getIntDictOptions(DICT_TYPE.COM_IS_INT)"
                :key="dict.value"
                :label="dict.label"
                :value="dict.value"
              />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-divider content-position="left">影响因素
      </el-divider>
      <el-button
        @click="addInfluenceFactor()"
        type="primary"
        size="small">
        添加
      </el-button>
      <el-table
        :data="formData.influenceFactors"
        border
        style="width: 100%; margin-top: 5px;">
        <el-table-column prop="" label="因素预测项" align="center">
          <template #default="scope">
            <el-select-v2
              v-model="scope.row.factorOutputId"
              :options="normalItemList || []"
              placeholder="请选择"
              :props="{value:'value',label:'label',options:'children'}"
              clearable
              filterable
              :fit-input-width="false"
            />
          </template>
        </el-table-column>
        <el-table-column prop="" label="偏差值" align="center">
          <template #default="scope">
            <el-input-number v-model="scope.row.deviationValue" :min="0" clearable/>
          </template>
        </el-table-column>
        <el-table-column prop="" label="操作" width="80" align="center">
          <template #default="scope">
            <el-button
              @click="deleteInfluenceFactor(scope.$index, formData.influenceFactors)"
              type="text"
              size="small">
              删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-form>
    <template #footer>
      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
      <el-button @click="dialogVisible = false">取 消</el-button>
    </template>
  </Dialog>
</template>
<script lang="ts" setup>
import {DICT_TYPE, getIntDictOptions,getStrDictOptions} from "@/utils/dict";
import * as MmPredictItem from '@/api/model/pre/item'
import * as InfluenceFactorApi from '@/api/model/pre/influenceFactor/influenceFactorApi'
defineOptions({name: 'MmPredictInfluenceFactorForm'})
const {t} = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
  id: undefined,
  outputId: undefined,
  pattern: undefined,
  isEnable: 1,
  influenceFactors: []
})
const formRules = reactive({
  outputId: [{required: true, message: '预测项不能为空', trigger: 'blur'}],
  pattern: [{required: true, message: '统计方式不能为空', trigger: 'blur'}],
})
const formRef = ref() // 表单 Ref
const normalItemList = ref([])
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
  dialogVisible.value = true
  dialogTitle.value = t('action.' + type)
  formType.value = type
  resetForm()
  normalItemList.value = await MmPredictItem.getNormalItemList()
  // 修改时,设置数据
  if (id) {
    formLoading.value = true
    try {
      formData.value = await InfluenceFactorApi.getInfo(id)
    } finally {
      formLoading.value = false
    }
  }
}
defineExpose({open}) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
  // 校验表单
  if (!formRef) return
  const valid = await formRef.value.validate()
  if (!valid) return
  // 提交请求
  formLoading.value = true
  try {
    const data = formData.value
    if (formType.value === 'create') {
      await InfluenceFactorApi.create(data)
      message.success(t('common.createSuccess'))
    } else {
      await InfluenceFactorApi.update(data)
      message.success(t('common.updateSuccess'))
    }
    dialogVisible.value = false
    // 发送操作成功的事件
    emit('success')
  } finally {
    formLoading.value = false
  }
}
/** 重置表单 */
const resetForm = () => {
  formData.value = {
    id: undefined,
    outputId: undefined,
    pattern: undefined,
    isEnable: 1,
    influenceFactors: []
  }
  formRef.value?.resetFields()
}
function addInfluenceFactor() {
  if (!formData.value.influenceFactors) {
    formData.value.influenceFactors = []
  }
  formData.value.influenceFactors.push({})
}
function deleteInfluenceFactor(index: string, rows) {
  if (!rows || rows.length === 1) {
    message.error('不能全部删除!')
    return
  }
  rows.splice(index, 1)
}
</script>
src/views/model/pre/influenceFactor/MmPredictInfluenceFactorResult.vue
对比新文件
@@ -0,0 +1,108 @@
<template>
  <Dialog v-model="dialogVisible" :title="dialogTitle" width="50%">
    <!-- 搜索工作栏 -->
    <ContentWrap>
      <el-form
        class="-mb-15px"
        :model="queryParams"
        ref="queryFormRef"
        :inline="true"
        label-width="68px"
      >
        <el-form-item label="开始时间">
          <el-date-picker
            v-model="queryParams.startTime"
            format="YYYY-MM-DD HH:mm:ss"
            value-format="YYYY-MM-DD HH:mm:ss"
            type="datetime"
            clearable
            placeholder="选择日期时间"/>
        </el-form-item>
        <el-form-item label="结束时间">
          <el-date-picker
            v-model="queryParams.endTime"
            format="YYYY-MM-DD HH:mm:ss"
            value-format="YYYY-MM-DD HH:mm:ss"
            type="datetime"
            clearable
            placeholder="选择日期时间"/>
        </el-form-item>
        <el-form-item>
          <el-button @click="getList"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
        </el-form-item>
      </el-form>
    </ContentWrap>
    <!-- 列表 -->
    <ContentWrap>
      <el-table
        v-loading="loading"
        :data="list"
        row-key="id"
        border
      >
        <el-table-column prop="time" label="影响时间" :formatter="dateFormatter"/>
        <el-table-column prop="value" label="影响值"/>
      </el-table>
      <!-- 分页 -->
      <Pagination
        v-model:limit="queryParams.pageSize"
        v-model:page="queryParams.pageNo"
        :total="total"
        @pagination="getList"
      />
    </ContentWrap>
  </Dialog>
</template>
<script lang="ts" setup>
  import * as influenceFactorApi from '@/api/model/pre/influenceFactor/influenceFactorApi'
  import {dateFormatter} from '@/utils/formatTime'
  const { t } = useI18n() // 国际化
  const message = useMessage() // 消息弹窗
  const dialogVisible = ref(false) // 弹窗的是否展示
  const dialogTitle = ref('历史结果') // 弹窗的标题
  const loading = ref(true) // 列表的加载中
  const total = ref(0) // 列表的总页数
  const list = ref([]) // 字典表格数据
  const queryParams = reactive({
    pageNo: 1,
    pageSize: 10,
    factorId: undefined,
    startTime: undefined,
    endTime: undefined,
  })
  /** 打开弹窗 */
  const open = async (id) => {
    dialogVisible.value = true
    resetForm()
    queryParams.factorId = id;
    getList()
  }
  defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  const getList = async () => {
    loading.value = true
    try {
      let data = await influenceFactorApi.getResultPage(queryParams)
      list.value = data.list
      total.value = data.total
    } finally {
      loading.value = false
    }
  }
  /** 重置表单 */
  const resetForm = () => {
    queryParams.pageNo = 1;
    queryParams.pageSize = 10;
    queryParams.factorId = undefined;
    queryParams.startTime = undefined;
    queryParams.endTime = undefined;
  }
</script>
src/views/model/pre/influenceFactor/index.vue
对比新文件
@@ -0,0 +1,196 @@
<template>
  <!-- 搜索 -->
  <ContentWrap>
    <el-form
      class="-mb-15px"
      :model="queryParams"
      ref="queryFormRef"
      :inline="true"
      label-width="85px"
    >
      <el-form-item label="预测项名称" prop="outputName">
        <el-input
          v-model="queryParams.outputName"
          placeholder="请输入名称"
          clearable
          @keyup.enter="handleQuery"
          class="!w-240px"
        />
      </el-form-item>
      <el-form-item label="统计方式" prop="pattern">
        <el-select v-model="queryParams.pattern" placeholder="请选择统计方式" clearable @keyup.enter="handleQuery" class="!w-240px">
          <el-option
            v-for="dict in getStrDictOptions(DICT_TYPE.INFLUENCE_FACTOR_PATTERN)"
            :key="dict.value"
            :label="dict.label"
            :value="dict.value"
          />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button @click="handleQuery">
          <Icon icon="ep:search" class="mr-5px" />
          搜索
        </el-button>
        <el-button @click="resetQuery">
          <Icon icon="ep:refresh" class="mr-5px" />
          重置
        </el-button>
        <el-button
          type="primary"
          plain
          @click="openForm('create')"
          v-hasPermi="['pre:influence-factor:create']"
        >
          <Icon icon="ep:plus" class="mr-5px" />
          新增
        </el-button>
      </el-form-item>
    </el-form>
  </ContentWrap>
  <!-- 列表 -->
  <ContentWrap>
    <el-table v-loading="loading" :data="list">
      <el-table-column label="预测项名称" header-align="center" align="center" prop="outputName" fixed/>
      <el-table-column label="统计方式" align="center" prop="pattern">
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.INFLUENCE_FACTOR_PATTERN" :value="scope.row.pattern" />
        </template>
      </el-table-column>
      <el-table-column label="是否启用" header-align="center" align="center" min-width="100" prop="isEnable" >
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.COM_IS_INT" :value="scope.row.isEnable" />
        </template>
      </el-table-column>
      <el-table-column prop="createTime" align="center" label="创建时间" :formatter="dateFormatter"/>
      <el-table-column label="影响因素" type="expand" width="100px">
        <template #default="props">
          <el-table :data="props.row.influenceFactors">
            <el-table-column align="center" label="因素名称" prop="factorOutputName" />
            <el-table-column align="center" label="偏差值" prop="deviationValue" />
            <el-table-column prop="" label="操作" align="center">
              <template #default="scope">
                <el-button
                  @click="showResult(scope.row.id)"
                  type="text">
                  查看结果
                </el-button>
              </template>
            </el-table-column>
          </el-table>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" fixed="right">
        <template #default="scope">
          <el-button
            link
            type="primary"
            @click="openForm('update', scope.row.id)"
            v-hasPermi="['pre:influence-factor:update']"
          >
            编辑
          </el-button>
          <el-button
            link
            type="danger"
            @click="handleDelete(scope.row.id)"
            v-hasPermi="['pre:influence-factor:delete']"
          >
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <Pagination
      :total="total"
      v-model:page="queryParams.pageNo"
      v-model:limit="queryParams.pageSize"
      @pagination="getList"
    />
  </ContentWrap>
  <!-- 表单弹窗:添加/修改 -->
  <MmPredictInfluenceFactorForm ref="formRef" @success="getList" />
  <!-- 历史结果 -->
  <MmPredictInfluenceFactorResult ref="resultRef" />
</template>
<script lang="ts" setup>
import MmPredictInfluenceFactorForm from './MmPredictInfluenceFactorForm.vue'
import MmPredictInfluenceFactorResult from './MmPredictInfluenceFactorResult.vue'
import * as influenceFactorApi from '@/api/model/pre/influenceFactor/influenceFactorApi'
import {dateFormatter} from '@/utils/formatTime'
import {DICT_TYPE, getIntDictOptions,getStrDictOptions} from "@/utils/dict";
defineOptions({name: 'influenceFactor'})
  const message = useMessage() // 消息弹窗
  const {t} = useI18n() // 国际化
  const loading = ref(true) // 列表的加载中
  const total = ref(0) // 列表的总页数
  const list = ref([]) // 列表的数据
  const queryParams = reactive({
    pageNo: 1,
    pageSize: 10,
    outputName: undefined,
    pattern: undefined,
  })
  const queryFormRef = ref() // 搜索的表单
  /** 查询列表 */
  const getList = async () => {
    loading.value = true
    try {
      const page = await influenceFactorApi.getPage(queryParams)
      list.value = page.list
      total.value = page.total
    } finally {
      loading.value = false
    }
  }
  /** 搜索按钮操作 */
  const handleQuery = () => {
    queryParams.pageNo = 1
    getList()
  }
  /** 重置按钮操作 */
  const resetQuery = () => {
    queryFormRef.value.resetFields()
    handleQuery()
  }
  /** 添加/修改操作 */
  const formRef = ref()
  const openForm = (type: string, id?: number) => {
    formRef.value.open(type, id)
  }
  /** 删除按钮操作 */
  const handleDelete = async (id: number) => {
    try {
      // 删除的二次确认
      await message.delConfirm()
      // 发起删除
      await influenceFactorApi.deleteConfig(id)
      message.success(t('common.delSuccess'))
      // 刷新列表
      await getList()
    } catch {
    }
  }
  /** 展示影响结果 */
  const resultRef = ref()
  const showResult = (id) => {
    resultRef.value.open(id)
  }
  /** 初始化 **/
  onMounted(async () => {
    await getList()
  })
</script>
src/views/model/sche/scheme/record/index.vue
@@ -47,7 +47,7 @@
    </ContentWrap>
    <!-- 列表 -->
    <ContentWrap>
      <el-table v-loading="loading" :data="list" max-height="300px">
      <el-table v-loading="loading" :data="list" min-height="300px">
        <el-table-column
          prop="scheduleTime"
          label="调度时间"