From 11e376f1eeb8c8f2fa9d0b052de6d05ce9f28716 Mon Sep 17 00:00:00 2001 From: houzhongjian <houzhongyi@126.com> Date: 星期四, 29 五月 2025 14:01:12 +0800 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- src/views/model/sche/suggest/suggestOperationRecord.vue | 142 ++++ src/views/model/sche/suggest/index.vue | 181 ++++++ src/api/model/sche/suggest/index.ts | 5 src/views/model/sche/snapshotConf/index.vue | 176 +++++ src/api/model/sche/suggest/snapshotConfigDet.ts | 26 src/views/model/sche/snapshotConf/det/configDetForm.vue | 191 ++++++ src/api/model/mcs/index.ts | 8 src/views/data/point/DaPointValue.vue | 13 src/api/model/sche/suggest/snapshotConfig.ts | 26 src/views/model/pre/repair/index.vue | 522 +++++++++++++++++ src/views/model/sche/snapshotConf/configForm.vue | 116 +++ src/utils/dict.ts | 1 src/views/model/sche/snapshotConf/det/index.vue | 192 ++++++ src/views/model/sche/suggest/suggestSnapshot.vue | 146 ++++ src/api/model/sche/suggest/suggestSnapshotRecord.ts | 12 src/api/model/sche/suggest/suggestOperationRecord.ts | 14 src/views/data/point/DaPointForm.vue | 2 17 files changed, 1,769 insertions(+), 4 deletions(-) diff --git a/src/api/model/mcs/index.ts b/src/api/model/mcs/index.ts index 4f55207..086116f 100644 --- a/src/api/model/mcs/index.ts +++ b/src/api/model/mcs/index.ts @@ -28,3 +28,11 @@ export const exportPredictValue = (params) => { return request.download({ url: '/model/api/mcs/predict-data/exportValue', params }) } + +export const getPreDataMissDataList = (data: PreDataBarLineReqVO) => { + return request.post({ url: '/model/api/mcs/predict-data/missDataList', data }) +} + +export const repair = (params) => { + return request.post({ url: '/model/api/mcs/predict-data/repair', params }) +} diff --git a/src/api/model/sche/suggest/index.ts b/src/api/model/sche/suggest/index.ts index 68a3c84..d54d748 100644 --- a/src/api/model/sche/suggest/index.ts +++ b/src/api/model/sche/suggest/index.ts @@ -8,6 +8,11 @@ outIds: [] } +// 查询ScheduleModel列表 +export const getScheduleSuggestPage = (params: ScheduleSuggestPageReqVO) => { + return request.get({ url: '/model/sche/suggest/page', params }) +} + export const getListByOut = (data: StScheduleSuggestPageReqVO) => { return request.post({ url: '/model/sche/suggest/list-out', data }) } diff --git a/src/api/model/sche/suggest/snapshotConfig.ts b/src/api/model/sche/suggest/snapshotConfig.ts new file mode 100644 index 0000000..6013522 --- /dev/null +++ b/src/api/model/sche/suggest/snapshotConfig.ts @@ -0,0 +1,26 @@ +// 查询列表 +import request from "@/config/axios"; + +export const getPage = (params) => { + return request.get({ url: '/model/suggest/snapshot/conf-main/page', params}) +} + +// 获得 +export const get = (id) => { + return request.get({ url: '/model/suggest/snapshot/conf-main/get?id=' + id }) +} + +// 新增 +export const create = (data) => { + return request.post({ url: '/model/suggest/snapshot/conf-main/create', data }) +} + +// 修改 +export const update = (data) => { + return request.put({ url: '/model/suggest/snapshot/conf-main/update', data }) +} + +// 删除 +export const del = (id) => { + return request.delete({ url: '/model/suggest/snapshot/conf-main/delete?id=' + id }) +} diff --git a/src/api/model/sche/suggest/snapshotConfigDet.ts b/src/api/model/sche/suggest/snapshotConfigDet.ts new file mode 100644 index 0000000..3a22435 --- /dev/null +++ b/src/api/model/sche/suggest/snapshotConfigDet.ts @@ -0,0 +1,26 @@ +// 查询列表 +import request from "@/config/axios"; + +export const getPage = (params) => { + return request.get({ url: '/model/suggest/snapshot/conf-det/page', params}) +} + +// 获得 +export const get = (id) => { + return request.get({ url: '/model/suggest/snapshot/conf-det/get?id=' + id }) +} + +// 新增 +export const create = (data) => { + return request.post({ url: '/model/suggest/snapshot/conf-det/create', data }) +} + +// 修改 +export const update = (data) => { + return request.put({ url: '/model/suggest/snapshot/conf-det/update', data }) +} + +// 删除 +export const del = (id) => { + return request.delete({ url: '/model/suggest/snapshot/conf-det/delete?id=' + id }) +} diff --git a/src/api/model/sche/suggest/suggestOperationRecord.ts b/src/api/model/sche/suggest/suggestOperationRecord.ts new file mode 100644 index 0000000..f53b5e9 --- /dev/null +++ b/src/api/model/sche/suggest/suggestOperationRecord.ts @@ -0,0 +1,14 @@ +import request from '@/config/axios' + +export interface SuggestOperationRecordPageReqVO extends PageParam { + modelId?: string; + modelName?: string; + scheduleTime?: string; + startTime?: string; + endTime?: string; + handler?: string; +} + +export const getSuggestOperationRecordPage = (params: SuggestOperationRecordPageReqVO) => { + return request.get({ url: '/model/suggest/operation/record/page', params }) +} diff --git a/src/api/model/sche/suggest/suggestSnapshotRecord.ts b/src/api/model/sche/suggest/suggestSnapshotRecord.ts new file mode 100644 index 0000000..382ba52 --- /dev/null +++ b/src/api/model/sche/suggest/suggestSnapshotRecord.ts @@ -0,0 +1,12 @@ +// 建议快照记录 +import request from "@/config/axios"; + +// 列表 +export const getList = (id) => { + return request.get({ url: '/model/suggest/snapshot/record/list?operationId=' + id }) +} + +// 图表 +export const getChartList = (data : selectedDataList) => { + return request.post({ url: '/model/suggest/snapshot/record/getChartData' , data}) +} diff --git a/src/utils/dict.ts b/src/utils/dict.ts index bfe5db5..13efa8f 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -168,6 +168,7 @@ RESULT_TYPE = 'result_type', MATLAB_PLATFORM= 'matlab_platform', MATLAB_VERSION= 'matlab_version', + SUGGEST_SNAPSHOT_DATA_TYPE= 'suggest_snapshot_data_type', // ========== DATA - 数据平台模块 ========== DATA_FIELD_TYPE = 'data_field_type', TAG_DATA_TYPE = 'tag_data_type', diff --git a/src/views/data/point/DaPointForm.vue b/src/views/data/point/DaPointForm.vue index 43cb4f2..0b2376e 100644 --- a/src/views/data/point/DaPointForm.vue +++ b/src/views/data/point/DaPointForm.vue @@ -429,7 +429,7 @@ pointNo: '' }]) const queryParams = reactive({ - pointTypes: "MEASURE,CONSTANT", + pointTypes: "MEASURE,CONSTANT,CUMULATE,EXTREMAL", }) const pointList2 = ref([{ pointName: '', diff --git a/src/views/data/point/DaPointValue.vue b/src/views/data/point/DaPointValue.vue index 671ede7..80f73af 100644 --- a/src/views/data/point/DaPointValue.vue +++ b/src/views/data/point/DaPointValue.vue @@ -8,7 +8,7 @@ <el-form :model="dataForm" v-loading="formLoading" - label-width="120px" + label-width="100px" > <el-row> <el-col :span="12"> @@ -23,12 +23,17 @@ </el-col> </el-row> <el-row> - <el-col :span="12"> + <el-col :span="8"> <el-form-item label="数据时间" prop="dataTime"> <el-input v-model="dataForm.dataTime" readonly/> </el-form-item> </el-col> - <el-col :span="12"> + <el-col :span="8"> + <el-form-item label="单位转换" prop="unittransfactor"> + <el-input v-model="dataForm.unittransfactor" readonly /> + </el-form-item> + </el-col> + <el-col :span="8"> <el-form-item label="数据值" prop="dataValue"> <el-input v-model="dataForm.dataValue" readonly> <template #append>{{ dataForm.unit }}</template> @@ -88,6 +93,7 @@ /** 打开弹窗 */ const open = async (row: object) => { + console.log(row) visible.value = true resetForm() dataForm.value.id = row.id; @@ -95,6 +101,7 @@ dataForm.value.pointName = row.pointName; dataForm.value.pointType = row.pointType; dataForm.value.unit = row.unit; + dataForm.value.unittransfactor = row.unittransfactor; getCurrentData() queryParams.pointNo = row.pointNo; if (dataForm.value.pointType === "CALCULATE") { diff --git a/src/views/model/pre/repair/index.vue b/src/views/model/pre/repair/index.vue new file mode 100644 index 0000000..1ba6129 --- /dev/null +++ b/src/views/model/pre/repair/index.vue @@ -0,0 +1,522 @@ +<template> + <el-card shadow="never" class="aui-card--fill"> + <div class="mod-his__index"> + <el-form :inline="true" :model="formData" label-width="80px"> + <el-form-item label="开始时间"> + <el-date-picker + v-model="formData.startTime" + type="datetime" + format="YYYY-MM-DD HH:mm:00" + value-format="YYYY-MM-DD HH:mm:00" + placeholder="选择日期时间"/> + </el-form-item> + <el-form-item label="结束时间"> + <el-date-picker + v-model="formData.endTime" + type="datetime" + format="YYYY-MM-DD HH:mm:00" + value-format="YYYY-MM-DD HH:mm:00" + placeholder="选择日期时间"/> + </el-form-item> + <el-form-item> + <el-button-group> + <el-button type="primary" plain :icon="ArrowLeft" + :loading="loading1" @click="leftSearchDataByRange()"/> + <el-button type="primary" plain :icon="Search" + :loading="loading1" @click="getList()">查询 + </el-button> + <el-button type="primary" plain :icon="ArrowRight" + :loading="loading1" @click="rightSearchDataByRange()"/> + </el-button-group> + </el-form-item> + + <div class="his-body"> + <div class="his-body-left"> + <div class="his-body-tree"> + <el-tree + :data="treeData" + show-checkbox + node-key="id" + ref="tree" + highlight-current + :props="defaultProps" + @check="onCheckTree"/> + </div> + </div> + <div class="his-body-middle"> + <div class="his-body-chart"> + <el-form :inline="true" :model="formData" label-width="100px"> + <el-row> + <el-col> + <el-form-item label="数据类型"> + <el-checkbox-group v-model="formData.chartCheck" @change="changeChartCheck"> + <el-checkbox v-for="item in formData.chartOptions" :label="item" + :key="item">{{ item }} + </el-checkbox> + </el-checkbox-group> + </el-form-item> + </el-col> + </el-row> + </el-form> + <div ref="dataAnalysisChart" style="height: 500px;"></div> + </div> + </div> + <div class="his-body-right"> + <ContentWrap> + <el-table v-loading="loading" :data="list"> + <el-table-column label="编号" align="center" min-width="100" prop="itemno"/> + <el-table-column label="名称" header-align="center" align="left" min-width="200" + prop="resultName"/> + <el-table-column label="开始时间" header-align="center" align="left" min-width="200" + prop="startTime"/> + <el-table-column label="结束时间" header-align="center" align="left" min-width="200" + prop="endTime"/> + <el-table-column label="丢失时间(min)" header-align="center" align="left" min-width="100" + prop="gap"/> + <el-table-column label="随机偏差" header-align="center" align="left" min-width="200" + prop="random"> + <el-input-number + v-model="scope.row.random" + :min="0" + placeholder="请输入随机偏差" + /> + </el-table-column> + <el-table-column label="操作" align="center" min-width="120" fixed="right"> + <template #default="scope"> + <el-button + link + type="primary" + @click="repair(scope.row)" + > + 修复 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + </div> + </div> + </el-form> + </div> + </el-card> +</template> +<script lang="ts" setup> + import {getYMDHMS} from "@/utils/dateUtil" + import * as McsApi from '@/api/model/mcs' + import * as echarts from "echarts"; + import {Search, ArrowLeft, ArrowRight,} from '@element-plus/icons-vue' + + defineOptions({name: 'RepairformData'}) + + const dataAnalysisChart = ref(null); + const loading1 = ref(false) // 列表的加载中 + const treeData = ref([]) + const itemDataObject = ref() + const timer = ref() + const list = ref() + let myChart = null; + const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + }) + + const formData = ref({ + startTime: '', + endTime: '', + predictTime: '', + chartCheck: ['T+L', '真实值'], + chartOptions: ['T+N', 'T+L', '真实值'], + checkedItemData: [], + isMultipleYRadio: '单坐标轴', + isMultipleY: false, + }) + + const repair = async (rows) => { + const params = reactive({ + outIds: rows.outIds, + startTime: rows.startTime, + endTime: rows.endTime, + random: rows.random, + }) + + const result = await McsApi.repair(params); + if (result.code === 0) { + this.$alert('数据修复成功', '提示', { + confirmButtonText: '确定', + callback: () => { + this.getList(); + } + }); + } else { + this.$message.error(`数据修复失败`); + } + } + /** 查询列表 */ + const getList = async () => { + loading1.value = true + try { + if (!formData.value.chartCheck) { + formData.value.chartCheck = ['真实值'] + } + let chartCheckArray = formData.value.chartCheck; + if (!formData.value.checkedItemData || formData.value.checkedItemData.length == 0) { + return; + } + let outIds = formData.value.checkedItemData.map(item => { + return item.id + }) + const params = reactive({ + outIds: outIds, + predictTime: formData.value.predictTime, + startTime: formData.value.startTime, + endTime: formData.value.endTime + }) + const data = await McsApi.getPreDataCharts(params) + const list = await McsApi.getPreDataMissDataList(params) + this.list = list + formData.value.predictTime = data.predictTime; + formData.value.startTime = data.startTime + formData.value.endTime = data.endTime + + let xAxisData = data.categories; + 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 = {} + 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; + 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 + if (chartCheckArray.indexOf('真实值') !== -1) { + let legendName = dataView.resultName + '(真实)'; + legendData.push(legendName); + seriesData.push({ + name: legendName, + data: dataView.realData || [], + type: 'line', + yAxisIndex: yAxisIndex, + showSymbol: false, + smooth: true, + lineStyle: { + width: 2 + } + }); + } + if (chartCheckArray.indexOf('T+N') !== -1) { + let legendName = dataView.resultName + '(T+N)'; + seriesData.push({ + name: legendName, + data: dataView.preDataN || [], + type: 'line', + yAxisIndex: yAxisIndex, + showSymbol: false, + smooth: true, + lineStyle: { + width: 2 + } + }); + } + if (chartCheckArray.indexOf('T+L') !== -1) { + let legendName = dataView.resultName + '(T+L)'; + legendData.push(legendName); + seriesData.push({ + name: legendName, + data: dataView.preDataL || [], + type: 'line', + showSymbol: false, + connectNulls: true, + yAxisIndex: yAxisIndex, + smooth: true, + lineStyle: { + width: 2 + } + }); + } + if (chartCheckArray.indexOf('当时') !== -1) { + let legendName = dataView.resultName + '(当时)'; + legendData.push(legendName); + seriesData.push({ + name: legendName, + data: dataView.curData || [], + type: 'line', + yAxisIndex: yAxisIndex, + showSymbol: false, + smooth: true, + lineStyle: { + width: 2 + } + }); + } + if (chartCheckArray.indexOf('调整值') !== -1) { + let legendName = dataView.resultName + '(调整值)'; + legendData.push(legendName); + seriesData.push({ + name: legendName, + data: dataView.adjData || [], + type: 'line', + yAxisIndex: yAxisIndex, + showSymbol: false, + connectNulls: true, + smooth: true, + 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; + } + } + } + myChart = echarts.init(dataAnalysisChart.value); + let option = { + title: { + text: '' + }, + tooltip: { + trigger: 'axis' + }, + legend: { + show: true, + data: legendData, + top: 10 + }, + grid: { + top: '20%', + left: '3%', + right: '6%', + bottom: '3%', + containLabel: true + }, + xAxis: { + type: 'category', + boundaryGap: false, + data: xAxisData + }, + yAxis: formData.value.isMultipleY ? yAxisData : { + type: 'value', + splitLine: {show: false}, + axisLine: {show: true} + }, + dataZoom: [ + { + type: 'inside', + start: 0, + end: 100 + }, + { + start: 0, + end: 10 + } + ], + series: seriesData + } + myChart.clear() + myChart.setOption(option) + } finally { + loading1.value = false + } + } + + onMounted(() => { + getPreItemTree() + }) + + async function getPreItemTree() { + treeData.value = await McsApi.getPredictItemTree() + } + + function leftSearchDataByRange() { + let mins = getRangeMins(); + let startTime = formData.value.startTime; + let endTime = formData.value.endTime; + let predictTime = formData.value.predictTime; + if (predictTime) { + predictTime = getYMDHMS(new Date(predictTime) - 1000 * 60 * mins); + formData.value.predictTime = predictTime; + } + if (startTime) { + startTime = getYMDHMS(new Date(startTime) - 1000 * 60 * mins); + formData.value.startTime = startTime; + } + if (endTime) { + endTime = getYMDHMS(new Date(endTime) - 1000 * 60 * mins); + formData.value.endTime = endTime; + } + getList(); + } + + function getRangeMins() { + let result: string | number = 0; + if (formData.value.startTime && formData.value.endTime) { + let startStamp = new Date(formData.value.startTime).getTime(); + let endStamp = new Date(formData.value.endTime).getTime(); + let queryStep = ((endStamp - startStamp) / (1000 * 60)).toFixed(0); + result = queryStep >= 0 ? queryStep : 0; + } + return result; + } + + function onCheckTree(data, checked, indeterminate) { + formData.value.checkedItemData = []; + if (checked.checkedNodes) { + formData.value.checkedItemData = [...checked.checkedNodes] + } + debounce(getList, 1000); + } + + function debounce(func, wait) { + let args = []; + if (timer.value) { + clearTimeout(timer.value); + } + timer.value = setTimeout(() => { + func.apply(this, args); + timer.value = null; + }, wait) + } + + function rightSearchDataByRange() { + let mins = getRangeMins(); + let startTime = formData.value.startTime; + let endTime = formData.value.endTime; + let predictTime = formData.value.predictTime; + if (predictTime) { + predictTime = getYMDHMS(new Date(predictTime) - 0 + 1000 * 60 * mins); + formData.value.predictTime = predictTime; + } + if (startTime) { + startTime = getYMDHMS(new Date(startTime) - 0 + 1000 * 60 * mins); + formData.value.startTime = startTime; + } + if (endTime) { + endTime = getYMDHMS(new Date(endTime) - 0 + 1000 * 60 * mins); + formData.value.endTime = endTime; + } + getList(); + } +</script> +<style scoped> + .el-form-item { + margin-bottom: 0 !important; + } + + .his-body-chart { + height: 100%; + border: 1px solid lightgray; + padding: 10px; + } + + .his-body-tree { + height: 100%; + border: 1px solid lightgray; + padding: 10px; + } + + .his-body-middle{ + width: 40%; + height: 100%; + padding: 10px 10px 0 0; + } + .his-body-right { + width: 40%; + height: 100%; + padding-top: 10px; + } + .his-body-left { + width: 20%; + height: 100%; + padding: 10px 10px 0 0; + } + + .his-body { + width: 100%; + height: calc(calc(100vh - 68px - 38px - 160px)); + display: flex; + flex-direction: row; + justify-content: flex-start; + align-content: flex-start; + } +</style> diff --git a/src/views/model/sche/snapshotConf/configForm.vue b/src/views/model/sche/snapshotConf/configForm.vue new file mode 100644 index 0000000..ce76930 --- /dev/null +++ b/src/views/model/sche/snapshotConf/configForm.vue @@ -0,0 +1,116 @@ +<template> + <Dialog v-model="dialogVisible" :title="dialogTitle"> + <el-form + ref="formRef" + v-loading="formLoading" + :model="formData" + :rules="formRules" + label-width="80px" + > + <el-row :gutter="20"> + <el-col :span="20"> + <el-form-item label="标题" prop="title"> + <el-input v-model="formData.title" placeholder=""/> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="20"> + <el-form-item label="模型id" prop="modelId"> + <el-input v-model="formData.modelId" placeholder=""/> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="20"> + <el-form-item label="调整对象" prop="scheduleObj"> + <el-input v-model="formData.scheduleObj" placeholder=""/> + </el-form-item> + </el-col> + </el-row> + </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 * as SnapshotConfigApi from '@/api/model/sche/suggest/snapshotConfig' + +defineOptions({ name: 'SnapshotConfigForm' }) + +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, + title: undefined, + modelId: undefined, + scheduleObj: undefined, +}) +const formRules = reactive({ + chartName: [{ required: true, message: '不能为空', trigger: 'blur' }], + chartCode: [{ required: true, message: '不能为空', trigger: 'blur' }], +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const open = async (type: string, id?: string) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await SnapshotConfigApi.get(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 SnapshotConfigApi.create(data) + message.success(t('common.createSuccess')) + } else { + await SnapshotConfigApi.update(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + title: undefined, + modelId: undefined, + scheduleObj: undefined, + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/model/sche/snapshotConf/det/configDetForm.vue b/src/views/model/sche/snapshotConf/det/configDetForm.vue new file mode 100644 index 0000000..4051e8c --- /dev/null +++ b/src/views/model/sche/snapshotConf/det/configDetForm.vue @@ -0,0 +1,191 @@ +<template> + <Dialog v-model="dialogVisible" :title="dialogTitle"> + <el-form + ref="formRef" + v-loading="formLoading" + :model="formData" + :rules="formRules" + label-width="80px" + > + <el-row :gutter="24"> + <el-col :span="12"> + <el-form-item label="数据类型" prop="dataType"> + <el-select v-model="formData.dataType" placeholder="请选择"> + <el-option + v-for="dict in getStrDictOptions(DICT_TYPE.SUGGEST_SNAPSHOT_DATA_TYPE)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="数据名称" prop="dataName"> + <el-input v-model="formData.dataName" placeholder=""/> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="数据编号" prop="dataNo"> + <el-input v-model="formData.dataNo" placeholder=""/> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="左侧长度" prop="leftLength"> + <el-input v-model="formData.leftLength" placeholder=""/> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="右侧长度" prop="rightLength"> + <el-input v-model="formData.rightLength" placeholder=""/> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="排序" prop="sort"> + <el-input v-model="formData.sort" placeholder=""/> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="拓展字段1" prop="ext1"> + <el-input v-model="formData.ext1" placeholder=""/> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="拓展字段2" prop="ext2"> + <el-input v-model="formData.ext2" placeholder=""/> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="拓展字段3" prop="ext3"> + <el-input v-model="formData.ext3" placeholder=""/> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="拓展字段4" prop="ext4"> + <el-input v-model="formData.ext4" placeholder=""/> + </el-form-item> + </el-col> + </el-row> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="拓展字段5" prop="ext5"> + <el-input v-model="formData.ext5" placeholder=""/> + </el-form-item> + </el-col> + </el-row> + </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 * as ConfigDetApi from '@/api/model/sche/suggest/snapshotConfigDet' + import {deleteIcon} from "@/api/model/mpk/icon"; + import {DICT_TYPE, getIntDictOptions, getStrDictOptions} from '@/utils/dict' + + defineOptions({name: 'ConfigDetForm'}) + 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, + confId: undefined, + dataType: undefined, + dataName: undefined, + dataNo: undefined, + leftLength: undefined, + rightLength: undefined, + sort: undefined, + ext1: undefined, + ext2: undefined, + ext3: undefined, + ext4: undefined, + ext5: undefined, + }) + const formRules = reactive({ + dataType: [{required: true, message: '不能为空', trigger: 'blur'}], + dataName: [{required: true, message: '不能为空', trigger: 'blur'}], + dataNo: [{required: true, message: '不能为空', trigger: 'blur'}], + leftLength: [{required: true, message: '不能为空', trigger: 'blur'}], + rightLength: [{required: true, message: '不能为空', trigger: 'blur'}], + }) + const formRef = ref() // 表单 Ref + + /** 打开弹窗 */ + const open = async (type: string, id?: string, confId?: string) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + formData.value.confId = confId + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await ConfigDetApi.get(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 ConfigDetApi.create(data) + message.success(t('common.createSuccess')) + } else { + await ConfigDetApi.update(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } + } + + /** 重置表单 */ + const resetForm = () => { + formData.value = { + id: undefined, + confId: undefined, + dataType: undefined, + dataNo: undefined, + leftLength: undefined, + rightLength: undefined, + sort: undefined, + ext1: undefined, + ext2: undefined, + ext3: undefined, + ext4: undefined, + ext5: undefined, + } + formRef.value?.resetFields() + } +</script> diff --git a/src/views/model/sche/snapshotConf/det/index.vue b/src/views/model/sche/snapshotConf/det/index.vue new file mode 100644 index 0000000..4d9516b --- /dev/null +++ b/src/views/model/sche/snapshotConf/det/index.vue @@ -0,0 +1,192 @@ +<template> + <el-drawer + v-model="drawer" + size="60%" + title="参数列表" + direction="rtl" + :before-close="handleClose" + > + <!-- 搜索工作栏 --> + <ContentWrap> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="数据编号" prop="dataNo"> + <el-input + v-model="queryParams.dataNo" + placeholder="请输入数据编号" + clearable + class="!w-240px" + /> + </el-form-item> + <!-- <el-form-item label="参数编码" prop="paramCode">--> + <!-- <el-input--> + <!-- v-model="queryParams.paramCode"--> + <!-- placeholder="请输入"--> + <!-- clearable--> + <!-- class="!w-240px"--> + <!-- />--> + <!-- </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')" + > + <Icon icon="ep:plus" class="mr-5px"/> + 新增 + </el-button> + + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table + v-loading="loading" + :data="list" + row-key="id" + > + <el-table-column prop="dataType" align="center" label="数据类型"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.SUGGEST_SNAPSHOT_DATA_TYPE" :value="scope.row.dataType"/> + </template> + </el-table-column> + <el-table-column prop="dataName" label="数据名称"/> + <el-table-column prop="dataNo" label="数据编号"/> + <el-table-column prop="leftLength" label="左侧长度(min)"/> + <el-table-column prop="rightLength" label="右侧侧长度(min)"/> + <el-table-column prop="sort" label="排序"/> + <el-table-column prop="ext1" label="拓展字段1"/> + <el-table-column prop="ext2" label="拓展字段2"/> + <el-table-column prop="ext3" label="拓展字段3"/> + <el-table-column prop="ext4" label="拓展字段4"/> + <el-table-column prop="ext5" label="拓展字段5"/> + <el-table-column label="操作" align="center" width="150px"> + <template #default="scope"> + <div class="flex items-center justify-center"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + > + 编辑 + </el-button> + <el-button link type="danger" @click="handleDelete(scope.row.id)"> + 删除 + </el-button> + </div> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + v-model:limit="queryParams.limit" + v-model:page="queryParams.page" + :total="total" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <ConfigDetForm ref="formRef" @success="getList"/> + </el-drawer> +</template> +<script lang="ts" setup> + import {dateFormatter} from '@/utils/formatTime' + import * as configDetApi from '@/api/model/sche/suggest/snapshotConfigDet' + import ConfigDetForm from './configDetForm.vue' + import type {DrawerProps} from "element-plus"; + import {DICT_TYPE, getIntDictOptions, getStrDictOptions} from '@/utils/dict' + + defineOptions({name: 'ConfigDet'}) + + const message = useMessage() // 消息弹窗 + const {t} = useI18n() // 国际化 + + const drawer = ref(false) + const loading = ref(true) // 列表的加载中 + const total = ref(0) // 列表的总页数 + const list = ref([]) // 字典表格数据 + const queryParams = reactive({ + page: 1, + limit: 10, + confId: '', + }) + const queryFormRef = ref() // 搜索的表单 + + const getList = async () => { + loading.value = true + try { + const data = await configDetApi.getPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } + } + + /** 搜索按钮操作 */ + const handleQuery = () => { + getList() + } + + /** 重置按钮操作 */ + const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() + } + + /** 添加/修改操作 */ + const formRef = ref() + const openForm = (type: string, id?: string) => { + formRef.value.open(type, id, queryParams.confId) + } + + /** 删除按钮操作 */ + const handleDelete = async (id: string) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await configDetApi.del(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch { + } + } + + /** 打开弹窗 */ + const open = async (confId?: string) => { + resetForm() + drawer.value = true + queryParams.confId = confId + if (confId) { + getList() + } + } + defineExpose({open}) // 提供 open 方法,用于打开弹窗 + + /** 重置表单 */ + const resetForm = () => { + queryParams.confId = '' + } + + const handleClose = (done: () => void) => { + drawer.value = false + } +</script> diff --git a/src/views/model/sche/snapshotConf/index.vue b/src/views/model/sche/snapshotConf/index.vue new file mode 100644 index 0000000..3bbb0b4 --- /dev/null +++ b/src/views/model/sche/snapshotConf/index.vue @@ -0,0 +1,176 @@ +<template> + <!-- 搜索工作栏 --> + <ContentWrap> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="标题" prop="snapshotConfigName"> + <el-input + v-model="queryParams.snapshotConfigName" + placeholder="请输入图表名称" + clearable + class="!w-240px" + /> + </el-form-item> + <el-form-item label="模型ID" prop="snapshotConfigCode"> + <el-input + v-model="queryParams.snapshotConfigCode" + placeholder="请输入模型ID" + clearable + class="!w-240px" + /> + </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="['suggest:snapshot:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> + 新增 + </el-button> + + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table + v-loading="loading" + :data="list" + row-key="id" + > + <el-table-column prop="title" label="标题"/> + <el-table-column prop="modelId" label="模型ID"/> + <el-table-column prop="scheduleObj" label="调整对象"/> + <el-table-column label="操作" align="center" width="200px"> + <template #default="scope"> + <div class="flex items-center justify-center"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['suggest:snapshot:update']" + > + 编辑 + </el-button> + <el-button + link + type="primary" + @click="openSnapshotConfigDet(scope.row.id)" + > + 参数 + </el-button> + <el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['suggest:snapshot:delete']"> + 删除 + </el-button> + </div> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + v-model:limit="queryParams.limit" + v-model:page="queryParams.page" + :total="total" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <SnapshotConfigForm ref="formRef" @success="getList" /> + + <!-- 分组列表 --> + <SnapshotConfigDet ref="SnapshotConfigDetRef" /> + +</template> +<script lang="ts" setup> +import {dateFormatter} from '@/utils/formatTime' +import * as SnapshotConfigApi from '@/api/model/sche/suggest/snapshotConfig' +import SnapshotConfigForm from './configForm.vue' +import SnapshotConfigDet from './det/index.vue' + + +defineOptions({name: 'SnapshotConfig'}) + +const message = useMessage() // 消息弹窗 +const {t} = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const total = ref(0) // 列表的总页数 +const list = ref([]) // 字典表格数据 +const queryParams = reactive({ + page: 1, + limit: 10, + modelId: '', + snapshotConfigCode: '' +}) +const queryFormRef = ref() // 搜索的表单 + +const getList = async () => { + loading.value = true + try { + const data = await SnapshotConfigApi.getPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: string) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: string) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await SnapshotConfigApi.del(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch { + } +} + +/** List操作 */ +const SnapshotConfigDetRef = ref() +const openSnapshotConfigDet = (id?: string) => { + SnapshotConfigDetRef.value.open(id) +} + +/** 初始化 **/ +onMounted(async () => { + await getList() +}) +</script> diff --git a/src/views/model/sche/suggest/index.vue b/src/views/model/sche/suggest/index.vue new file mode 100644 index 0000000..b653bde --- /dev/null +++ b/src/views/model/sche/suggest/index.vue @@ -0,0 +1,181 @@ +<template> + <!-- 搜索 --> + <ContentWrap> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="标题" prop="title"> + <el-input + v-model="queryParams.title" + placeholder="标题" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="模型ID" prop="schemeId"> + <el-input + v-model="queryParams.modelId" + placeholder="模型ID" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="调整对象" prop="scheduleObj"> + <el-input + v-model="queryParams.scheduleObj" + placeholder="调整对象" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="状态" prop="state"> + <el-select + v-model="queryParams.state" + placeholder="请选择状态" + clearable + class="!w-240px" + > + <el-option + v-for="item in stateOptions" + :key="item.value" + :label="item.label" + :value="item.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-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list"> + <el-table-column label="标题" align="center" prop="title" min-width="100"/> + <el-table-column label="内容" header-align="center" align="left" prop="content" min-width="100"/> + <el-table-column label="方案ID" header-align="center" align="left" prop="schemeId" min-width="100"/> + <el-table-column label="预警ID" align="center" prop="alarmId" min-width="100"/> + <el-table-column label="预测项ID" align="center" prop="itemId" min-width="100"/> + <el-table-column label="模型ID" header-align="center" align="center" prop="modelId" min-width="100" /> + <el-table-column label="调整对象" align="center" prop="scheduleObj" min-width="100"/> +<!-- <el-table-column label="调整类型" align="center" min-width="100" fixed="right">--> +<!-- <el-table-column label="调整策略" align="center" min-width="100" fixed="right">--> +<!-- <el-table-column label="调整方式" align="center" min-width="100" fixed="right">--> +<!-- <el-table-column label="调整值" align="center" min-width="100" fixed="right">--> +<!-- <el-table-column label="调整单位" align="center" min-width="100" fixed="right">--> +<!-- <el-table-column label="持续时长" align="center" min-width="100" fixed="right">--> +<!-- <el-table-column label="调整开始时间" align="center" min-width="100" fixed="right">--> +<!-- <el-table-column label="调整结束时间" align="center" min-width="100" fixed="right">--> + <el-table-column label="调度时间" align="center" prop="scheduleTime" min-width="100" fixed="right"/> + <el-table-column label="状态" align="center" prop="status" min-width="100"> + <template #default="scope"> + <span v-if="scope.row.status === 0">未处理</span> + <span v-else-if="scope.row.status === 1">已采纳</span> + <span v-else-if="scope.row.status === 2">已忽略</span> + <span v-else>{{ scope.row.status }}</span> + </template> + </el-table-column> +<!-- <el-table-column label="处理人" align="center" min-width="100" fixed="right">--> +<!-- <el-table-column label="处理时间" align="center" min-width="100" fixed="right">--> +<!-- <el-table-column label="创建时间" align="center" min-width="100" fixed="right">--> + <el-table-column label="详情" align="center" min-width="100" fixed="right"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm(scope.row.modelId ,scope.row.scheduleTime)" + > + 调度历史 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <SuggestOperationRecord ref="formRef" @success="getList" /> + +</template> +<script lang="ts" setup> + import * as ScheduleSuggestApi from '@/api/model/sche/suggest' + import SuggestOperationRecord from './suggestOperationRecord.vue' + + defineOptions({name: 'ScheduleSuggest'}) + + const message = useMessage() // 消息弹窗 + const {t} = useI18n() // 国际化 + + const loading = ref(true) // 列表的加载中 + const total = ref(0) // 列表的总页数 + const list = ref([]) // 列表的数据 + const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + title: undefined, + modelId: undefined + }) + const stateOptions = [ + { value: 0, label: '未处理' }, + { value: 1, label: '已采纳' }, + { value: 2, label: '已忽略' } + ]; + const queryFormRef = ref() // 搜索的表单 + const exportLoading = ref(false) // 导出的加载中 + + /** 查询列表 */ + const getList = async () => { + loading.value = true + try { + const page = await ScheduleSuggestApi.getScheduleSuggestPage(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 = (modelId?: string,scheduleTime?: string) => { + formRef.value.open(modelId, scheduleTime) + } + + /** 初始化 **/ + onMounted(async () => { + await getList() + }) +</script> diff --git a/src/views/model/sche/suggest/suggestOperationRecord.vue b/src/views/model/sche/suggest/suggestOperationRecord.vue new file mode 100644 index 0000000..f09ddea --- /dev/null +++ b/src/views/model/sche/suggest/suggestOperationRecord.vue @@ -0,0 +1,142 @@ +<template> + <el-drawer + v-model="drawer" + size="60%" + title="建议调度历史" + :direction="direction" + :before-close="handleClose" + > + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list"> + <el-table-column + prop="scheduleTime" + label="调度时间" + header-align="center" + align="left" + min-width="150" + /> + <el-table-column + prop="resultCode" + label="结果code" + header-align="center" + align="left" + min-width="150" + /> + <el-table-column + prop="resultData" + label="结果数据" + header-align="center" + align="center" + /> + <el-table-column + prop="operate" + label="操作" + header-align="center" + align="center" + min-width="150" + /> + <el-table-column + prop="handler" + label="处理人" + header-align="center" + align="center" + min-width="150" + /> + <el-table-column + prop="handleTime" + label="处理时间" + header-align="center" + align="center" + min-width="150" + /> + <el-table-column label="快照" align="center" min-width="100" fixed="right"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openSnapshot(scope.row.id)" + v-if="scope.row.operate=='采纳建议'" + > + 快照 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + </el-drawer> + <!-- 快照弹窗 --> + <SuggestSnapshot ref="suggestSnapshotRef" @success="getList" /> +</template> +<script lang="ts" setup> + import type {DrawerProps} from 'element-plus' + import { getSuggestOperationRecordPage } from '@/api/model/sche/suggest/suggestOperationRecord'; + import SuggestSnapshot from './suggestSnapshot.vue' + import {ref} from "vue"; + + defineOptions({name: 'SuggestOperationRecord'}) + + const message = useMessage() // 消息弹窗 + const {t} = useI18n() // 国际化 + + const drawer = ref(false) + const direction = ref<DrawerProps['direction']>('rtl') + const loading = ref(true) // 列表的加载中 + const total = ref(0) // 列表的总页数 + const list = ref([]) // 列表的数据 + const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + modelId: undefined, + scheduleTime: undefined + }) + const queryFormRef = ref() // 搜索的表单 + const exportLoading = ref(false) // 导出的加载中 + + /** 查询列表 */ + const getList = async () => { + loading.value = true + try { + const page = await getSuggestOperationRecordPage(queryParams); + list.value = page.list + total.value = page.total + } finally { + loading.value = false + } + } + + /** 搜索按钮操作 */ + const handleQuery = () => { + queryParams.pageNo = 1 + getList() + } + + /** 快照 */ + const suggestSnapshotRef = ref() + const openSnapshot = (id?: string) => { + suggestSnapshotRef.value.open(id) + } + + /** 打开弹窗 */ + const open = async (modelId?: string, scheduleTime?: string) => { + drawer.value = true + queryParams.modelId = modelId + queryParams.scheduleTime = scheduleTime + if (modelId) { + getList() + } + } + defineExpose({open}) // 提供 open 方法,用于打开弹窗 + + const handleClose = (done: () => void) => { + drawer.value = false + } + +</script> diff --git a/src/views/model/sche/suggest/suggestSnapshot.vue b/src/views/model/sche/suggest/suggestSnapshot.vue new file mode 100644 index 0000000..f64263c --- /dev/null +++ b/src/views/model/sche/suggest/suggestSnapshot.vue @@ -0,0 +1,146 @@ +<template> + <el-dialog + title="历史值" + :close-on-click-modal="false" + width="80%" + v-model="visible" + > + <el-checkbox-group v-model="selectedData" @change="refreshCharts"> + <el-checkbox + v-for="item in dataList" + :label="item.dataName" + :key="item.dataName" + > + {{ item.dataName }} + </el-checkbox> + </el-checkbox-group> + + <div + v-for="(chart, index) in charts" + :key="chart.id" + class="chart-container" + :ref="el => chartDoms[index] = el" + v-loading="loading" + ></div> + </el-dialog> +</template> + +<script lang="ts" setup> + import { ref, reactive, nextTick } from 'vue' + import * as echarts from 'echarts' + import * as suggestSnapshotApi from '@/api/model/sche/suggest/suggestSnapshotRecord'; + + const { message } = useMessage() + const visible = ref(false) + const dataList = ref([]) + const selectedData = ref([]) + const charts = ref([]) + const chartDoms = ref([]) + const chartInstances = ref([]) + const loading = ref(false) + + + const open = async (id: string) => { + visible.value = true + await getDataList(id) + } + + defineExpose({ open }) + + /** 获取数据列表 */ + const getDataList = async (id: string) => { + try { + const res = await suggestSnapshotApi.getList(id) + dataList.value = res + selectedData.value = [] // 清空已选项 + } catch (error) { + console.error(error) + message.error('获取数据列表失败') + } + } + + /** 刷新图表 */ + const refreshCharts = async () => { + if (selectedData.value.length === 0) { + destroyCharts() + charts.value = [] + return + } + + loading.value = true + try { + const selectedDataList = selectedData.value.map(code => + dataList.value.find(d => d.dataName === code) + ).filter(Boolean) // 过滤无效项 + const chartData = await suggestSnapshotApi.getChartList( + selectedDataList + ) + destroyCharts() + + // 生成图表配置数据 + charts.value = selectedData.value.map(code => { + const item = dataList.value.find(d => d.dataName === code) + return { + id: `chart-${code}`, + name: item?.dataName || code, + data: chartData.find((d: any) => d.dataName === code) + } + }) + + await nextTick() + renderCharts() + } catch (error) { + console.error(error) + message.error('获取图表数据失败') + } finally { + loading.value = false + } + } + + /** 销毁图表实例 */ + const destroyCharts = () => { + chartInstances.value.forEach(instance => instance?.dispose()) + chartInstances.value = [] + } + + /** 渲染图表 */ + const renderCharts = () => { + chartInstances.value = chartDoms.value.map((dom, index) => { + if (!dom) return null + const chart = echarts.init(dom) + const chartInfo = charts.value[index] + + if (!chartInfo) return chart + + const option = { + title: { + text: chartInfo.name, + textStyle: { fontSize: 14 } + }, + tooltip: { trigger: 'axis' }, + grid: { top: 30, left: '3%', right: '5%', bottom: 20 }, + xAxis: {type: 'category'}, + yAxis: { type: 'value' }, + dataZoom: [{ type: 'inside' }], + series: [{ + type: 'line', + data: chartInfo.data?.dataList || [], + lineStyle: { color: '#5B8FF9', width: 1 } + }] + } + + chart.setOption(option) + return chart + }).filter(Boolean) as echarts.ECharts[] + } +</script> + +<style scoped> + .chart-container { + height: 400px; + margin: 20px 0; + border: 1px solid #eee; + border-radius: 4px; + padding: 10px; + } +</style> -- Gitblit v1.9.3