修改历史模拟分析页面

This commit is contained in:
limengnan 2026-01-19 17:31:35 +08:00
parent 41fb70bb78
commit dee3a3e051
8 changed files with 726 additions and 254 deletions

View File

@ -6,3 +6,4 @@ NODE_ENV='development'
VITE_APP_TITLE = '临界事故情景分析模拟系统'
VITE_APP_PORT = 3000
VITE_APP_BASE_API = '/dev-api'
VITE_APP_BASE_HTTP = 'http://localhost:3000'

View File

@ -4,3 +4,4 @@ NODE_ENV='production'
VITE_APP_TITLE = 'NewFrameWork2023-WEB'
VITE_APP_PORT = 3000
VITE_APP_BASE_API = '/prod-api'
VITE_APP_BASE_HTTP = 'http://localhost:3000'

View File

@ -1,13 +1,13 @@
import request from '@/utils/request';
// //获取所有项目列表
// export function searchScenariosLsit(queryParams:any){
// return request({
// url: '/events/by-project' ,
// method: 'get',
// params:queryParams
// });
// }
export function searchEventsByScenarioId(queryParams:any){
return request({
url: '/events/by-scenario' ,
method: 'get',
params:queryParams
});
}

View File

@ -0,0 +1,218 @@
<!-- 线 + 柱混合图 -->
<template>
<div id="chartContainer" :class="className" style="height: calc(100vh - 200px); width: 1360px;" />
</template>
<script setup lang="ts">
import {
ref,
nextTick,
onActivated,
onBeforeUnmount,
onDeactivated,
onMounted,
} from 'vue';
import { init, EChartsOption } from 'echarts';
import { getByScenario } from "@/api/business/scenario";
import * as echarts from 'echarts';
import resize from '@/utils/resize';
const props = defineProps({
deviceId: {
required: false,
type: String,
default: ''
},
scenarioId: {
required: false,
type: String,
default: ''
},
className: {
type: String,
default: '',
}
});
const { mounted, chart, beforeDestroy, activated, deactivated } = resize();
function getName(code:any) {
let name = ''
switch (code) {
case 'width':
return name = "宽度cm";
break;
case 'height':
return name = "高度cm";
break;
case 'length':
return name = "长度cm";
break;
case 'diameter':
return name = "外径cm";
break;
case 'volume':
return name = "体积单位L";
break;
case 'flow_rate':
return name = "流量单位m3/h";
break;
case 'pulse_velocity':
return name = "脉冲速度单位Hz";
break;
case 'u_concentration':
return name = "铀浓度g/L";
break;
case 'uo2_density':
return name = "氧化铀密度g/cm3";
break;
case 'u_enrichment':
return name = "铀富集度(%";
break;
case 'pu_concentration':
return name = "钚浓度g/L";
break;
case 'puo2_density':
return name = "氧化钚密度g/cm3";
break;
case 'pu_isotope':
return name = "钚同位素比例PU-240占比%";
break;
case 'hno3_acidity':
return name = "硝酸酸度mol/L";
break;
case 'h2c2o4_concentration':
return name = "草酸浓度mol/L";
break;
case 'organic_ratio':
return name = "有机相比例%";
break;
case 'moisture_content':
return name = "含水率%";
break;
default:
return name = "";
}
return name
}
const option = ref<any>({
title: {
text: ''
},
tooltip: {
trigger: 'axis'
},
legend: {
data: []
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: []
},
yAxis: {
type: 'value'
},
series: [
]
});
function getScenarioResults(){
getByScenario({
scenarioId: props.scenarioId,
deviceId: props.deviceId,
pageNum:1,
pageSize:999
}).then((res:any) => {
option.value.xAxis.data = []
option.value.series = []
option.value.legend.data = []
let tempData = ['keff']
for(let i = 0;i<res.data.records.length;i++){
let tempAttrState = []
if(res.data.records[i].attrState != null){
if(i === 0){
option.value.series.push({
name: 'keff',
type: 'line',
data:[]
})
}
tempAttrState.push({
name: 'keff',
value: res.data.records[i].keffValue,
})
let attrState = JSON.parse(res.data.records[i].attrState)
for (const key in attrState) {
if (!Object.hasOwn(attrState, key)) continue;
const element = attrState[key];
res.data.records[i][key] = element
if(i === 0){
tempData.push(getName(key) )
option.value.series.push({
name: getName(key),
type: 'line',
data:[]
})
}
tempAttrState.push({
name: getName(key),
value: element,
})
}
if(i === 0 ){
option.value.legend.data = tempData
}
}
option.value.xAxis.data.push(res.data.records[i].step)
for(let j = 0;j<tempAttrState.length;j++){
option.value.series[j].data.push(tempAttrState[j].value)
}
}
initChart(option.value);
})
}
function initChart(option:any) {
const barChart = init(document.getElementById('chartContainer') as HTMLDivElement);
barChart.setOption(option);
chart.value = barChart;
}
onBeforeUnmount(() => {
beforeDestroy();
});
onActivated(() => {
activated();
});
onDeactivated(() => {
deactivated();
});
onMounted(() => {
mounted();
nextTick(() => {
getScenarioResults()
});
});
</script>

View File

@ -6,7 +6,8 @@ export default {
<script setup lang="ts">
import { onMounted, ref, nextTick } from "vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus";
import { ElForm, ElMessage, ElMessageBox, selectEmits } from "element-plus";
import Page from '@/components/Pagination/page.vue'
import { getByScenario } from "@/api/business/scenario";
const emit = defineEmits([ 'closeEditdevice']);
@ -23,16 +24,47 @@ const props = defineProps({
},
})
const queryParams = ref({
current: 1,
size: 10,
})
const total = ref(0)
const scenarioResultData:any = ref([])
const selectData = ref<any>([])
function getIf(list:any,code:any){
for(let i = 0;i<list.length;i++){
if(list[i] == code){
return true
}
}
}
function getScenarioResults(){
scenarioResultData.value = []
getByScenario({
scenarioId: props.scenarioId,
deviceId: props.deviceId,
pageNum:1,
pageSize:999
pageNum:queryParams.value.current,
pageSize:queryParams.value.size
}).then((res:any) => {
for(let i = 0;i<res.data.records.length;i++){
if(res.data.records[i].attrState != null){
let attrState = JSON.parse(res.data.records[i].attrState)
for (const key in attrState) {
if (!Object.hasOwn(attrState, key)) continue;
const element = attrState[key];
res.data.records[i][key] = element
if(i == 0){
selectData.value.push(key)
}
}
}
}
scenarioResultData.value = res.data.records
total.value = res.data.total
})
}
onMounted(() => {
@ -44,86 +76,41 @@ onMounted(() => {
<template>
<div class="editdevice-box">
<el-table :data="scenarioResultData" style="width: 100%; height: calc(100vh - 260px);margin-bottom: 10px;" border
:header-cell-style="{ background: 'rgb(250 250 250)', color: '#383838', height: '50px' }">
<el-table-column type="selection" width="50" align="center"></el-table-column>
<el-table-column prop="step" label="时间" width="100"></el-table-column>
<el-table-column prop="keffValue" label="keff" min-width="100"></el-table-column>
<el-table-column v-if="getIf(selectData,'diameter')" prop="diameter" label="直径cm" min-width="100"></el-table-column>
<el-table-column v-if="getIf(selectData,'height')" prop="height" label="高度cm" min-width="100"></el-table-column>
<el-table-column v-if="getIf(selectData,'width')" prop="width" label="宽度cm" min-width="100"></el-table-column>
<el-table-column v-if="getIf(selectData,'length')" prop="length" label="长度cm" min-width="100"></el-table-column>
<el-table-column v-if="getIf(selectData,'volume')" prop="volume" label="体积单位L" min-width="160"></el-table-column>
<el-table-column v-if="getIf(selectData,'flow_rate')" prop="flow_rate" label="流量单位m3/h" min-width="160"></el-table-column>
<el-table-column v-if="getIf(selectData,'pulse_velocity')" prop="pulse_velocity" label="脉冲速度单位Hz" min-width="160"></el-table-column>
<el-table-column v-if="getIf(selectData,'u_concentration')" prop="u_concentration" label="铀浓度g/L" min-width="160"></el-table-column>
<el-table-column v-if="getIf(selectData,'uo2_density')" prop="uo2_density" label="氧化铀密度g/cm3" min-width="160"></el-table-column>
<el-table-column v-if="getIf(selectData,'u_enrichment')" prop="u_enrichment" label="铀富集度(%" min-width="160"></el-table-column>
<el-table-column v-if="getIf(selectData,'pu_concentration')" prop="pu_concentration" label="钚浓度g/L" min-width="160"></el-table-column>
<el-table-column v-if="getIf(selectData,'puo2_density')" prop="puo2_density" label="氧化钚密度g/cm3" min-width="160"></el-table-column>
<el-table-column v-if="getIf(selectData,'pu_isotope')" prop="pu_isotope" label="钚同位素比例PU-240占比%" min-width="160"></el-table-column>
<el-table-column v-if="getIf(selectData,'hno3_acidity')" prop="hno3_acidity" label="硝酸酸度mol/L" min-width="160"></el-table-column>
<el-table-column v-if="getIf(selectData,'h2c2o4_concentration')" prop="h2c2o4_concentration" label="草酸浓度mol/L" min-width="160"></el-table-column>
<el-table-column v-if="getIf(selectData,'organic_ratio')" prop="organic_ratio" label="有机相比例%" min-width="160"></el-table-column>
<el-table-column v-if="getIf(selectData,'moisture_content')" prop="moisture_content" label="含水率%" min-width="160"></el-table-column>
</el-table>
<div style="display: flex; justify-content: flex-end;">
<Page :total="total" v-model:size="queryParams.size" v-model:current="queryParams.current" @pagination="getScenarioResults" ></Page>
</div>
</div>
</template>
<style scoped>
.editdevice-box{
width: 100%;
height: calc(100vh - 200px);
}
.editdevice_navigation_left{
width: 110px;
height: 32px;
line-height: 32px;
text-align: center;
cursor: pointer;
font-family: 'Arial Normal', 'Arial';
font-weight: 400;
font-style: normal;
font-size: 14px;
color: #363636;
background-image: url('@/assets/x6/navleft.png');
}
.editdevice_navigation_left:hover{
color: #266fff ;
}
.editdevice_navigation_activeleft{
background-image: url('@/assets/x6/navleft_active.png');
color: #fff !important;
}
.editdevice_navigation_right{
width: 110px;
height: 32px;
line-height: 32px;
text-align: center;
cursor: pointer;
font-family: 'Arial Normal', 'Arial';
font-weight: 400;
font-style: normal;
font-size: 14px;
color: #363636;
background-image: url('@/assets/x6/navright.png');
}
.editdevice_navigation_right:hover{
color: #266fff ;
}
.editdevice_navigation_activeright{
background-image: url('@/assets/x6/navright_active.png');
color: #fff !important;
}
</style>
<style>
.el-table .el-table--enable-row-hover .el-table__body tr:hover > td{
background-color: #194764 !important;
}
.editdevice-box .el-table .el-table__row.current-row td{
background-color: #f3faff !important;
border-top:1px solid #14aaff;
border-bottom:1px solid #14aaff
}
.editdevice-box .el-table .el-table__row.current-row td:last-child{
border-right:1px solid #14aaff;
}
.editdevice-box .el-table .el-table__row.current-row td:first-child{
border-left:1px solid #14aaff;
}
.editdevice-box .el-table .el-table__row.current-row:last-child td{
border-bottom:1px solid #14aaff
}
.el-table__inner-wrapper:before{
background-color: transparent !important;
border-top: 1px solid var(--el-table-border-color);
}
.el-table--border:before{
background-color: transparent !important;
border-top: 1px solid var(--el-table-border-color);
}
</style>

View File

@ -19,15 +19,10 @@ import { updateProjects,projectsById} from "@/api/business/project";
import { getByScenario } from "@/api/business/scenario";
import textimg from '@/assets/x6/text.png'
import chartsimg from '@/assets/x6/charts.png'
import echartsimg from '@/assets/x6/charts.png'
import Createscenario from '@/views/component/scenario/createscenario.vue'
import ScenarioModel from '@/views/component/scenario/index.vue'
import TableModels from './tableModel.vue';
import EditdeviceModel from './editdevice.vue';
import MaterialModels from './materialmodel.vue';
import ChangesettingsModels from './changesettings.vue';
import EchartsModels from './echartsModel.vue';
const route = useRoute();
const router = useRouter()
@ -70,7 +65,6 @@ function getScenarioResults(){
for(let i = 0;i<customImageData.value.length;i++){
if(key == customImageData.value[i].id){
customImageData.value[i].scenarioResults = element
console.log(customImageData.value[i])
addAttrText(customImageData.value[i])
}
}
@ -78,11 +72,30 @@ function getScenarioResults(){
})
}
function addAttrText(item:any){
item.attrs.text = item.deviceName
// item.attrs.text = item.deviceInfo.name
graph.addNode({
x: item.position.x ,
y: item.position.y + 150,
width: 100,
height: 40,
label: item.deviceInfo.name,
attrs: {
body: {
stroke: 'transparent',
fill: 'transparent',
strokeWidth: 1,
},
text: {
text: '',
fill: '#363636',
fontSize: 16,
},
},
})
graph.addNode({
x: item.position.x ,
y: item.position.y + 160,
y: item.position.y + 180,
width: 100,
height: 30,
label: 'keff' + item.scenarioResults[item.scenarioResults.length - 1].keffValue,
@ -94,7 +107,7 @@ function addAttrText(item:any){
},
text: {
text: '',
fill: item.scenarioResults[item.scenarioResults.length - 1].keffValue > 0.98 ? '#ff4d4f' : '#363636', //
fill: item.scenarioResults[item.scenarioResults.length - 1].keffValue > 0.98 ? '#ff4d4f' : '#363636',
fontSize: 14,
},
},
@ -102,16 +115,16 @@ function addAttrText(item:any){
graph.addNode({
shape: 'image-node',
x: item.position.x + 135 ,
y: item.position.y + 167,
y: item.position.y + 186,
width: 14,
height: 14,
correlationId: item.id,
type:'charts',
type:'echarts',
attrs: {
img: {
width: 14,
height: 14,
'xlink:href': chartsimg,
'xlink:href': echartsimg,
},
label: {
text: '',
@ -121,7 +134,7 @@ function addAttrText(item:any){
graph.addNode({
shape: 'image-node',
x: item.position.x + 110 ,
y: item.position.y + 167,
y: item.position.y + 186,
width: 14,
height: 14,
correlationId: item.id,
@ -137,7 +150,118 @@ function addAttrText(item:any){
},
},
})
let tempData:any = []
if(item.scenarioResults[item.scenarioResults.length - 1].attrState){
let attrState = JSON.parse(item.scenarioResults[item.scenarioResults.length - 1].attrState)
for (const key in attrState) {
if (!Object.hasOwn(attrState, key)) continue;
const element = attrState[key];
tempData.push({
name: key,
value: element
})
}
}
appendAttrText(item,tempData)
}
function getName(code:any) {
let name = ''
switch (code) {
case 'width':
return name = "宽度cm";
break;
case 'height':
return name = "高度cm";
break;
case 'length':
return name = "长度cm";
break;
case 'diameter':
return name = "外径cm";
break;
case 'volume':
return name = "体积单位L";
break;
case 'flow_rate':
return name = "流量单位m3/h";
break;
case 'pulse_velocity':
return name = "脉冲速度单位Hz";
break;
case 'u_concentration':
return name = "铀浓度g/L";
break;
case 'uo2_density':
return name = "氧化铀密度g/cm3";
break;
case 'u_enrichment':
return name = "铀富集度(%";
break;
case 'pu_concentration':
return name = "钚浓度g/L";
break;
case 'puo2_density':
return name = "氧化钚密度g/cm3";
break;
case 'pu_isotope':
return name = "钚同位素比例PU-240占比%";
break;
case 'hno3_acidity':
return name = "硝酸酸度mol/L";
break;
case 'h2c2o4_concentration':
return name = "草酸浓度mol/L";
break;
case 'organic_ratio':
return name = "有机相比例%";
break;
case 'moisture_content':
return name = "含水率%";
break;
default:
return name = "";
}
return name
}
function appendAttrText(item:any,data:any){
if(isDisplay.value == false){
return
}
for(let i = 0;i<data.length;i++){
graph.addNode({
x: item.position.x,
y: item.position.y + 203 + i * 25,
width: 260,
height: 30,
// label: data[i].name + '' + data[i].value,
attrs: {
body: {
stroke: 'transparent',
fill: 'transparent',
strokeWidth: 1,
},
label: {
textAnchor: 'left',
refX: 0,
text: getName(data[i].name) + '' + data[i].value,
textWrap: {
width: 250,
height: 30,
ellipsis: true,
},
},
text: {
text: '',
fill: '#363636', //
fontSize: 12,
},
},
})
}
}
function groupByDeviceId(data:any) { // id
//
if (!Array.isArray(data)) {
@ -397,12 +521,15 @@ onMounted(() => {
textAnchor: 'middle',
dy: 10,
},
'.': {
class: 'custom-image-node',
},
},
},
true,
)
// Graph.registerNode(
// 'image-charts',
// 'image-echarts',
// {
// inherit: 'rect',
// width: 120,
@ -470,40 +597,18 @@ onMounted(() => {
})
// #endregion
// #region 使
// graph
// .use(
// new Transform({
// resizing: false, //
// rotating: false, //
// // scaling Transform
// }),
// )
// .use(
// new Selection({
// rubberband: true,
// showNodeSelectionBox: true,
// }),
// )
// .use(new Snapline())
// .use(new Keyboard())
// .use(new Clipboard())
// .use(new History())
// #endregion
graph.on('node:click', ({ e, node }) => {
if(node?.store?.data?.type == 'charts'){
deviceId.value = node?.store?.data?.correlationId
graph.on('node:click', ( e:any ) => {
const node = e.node
if(node == null)return
if(node.store == null)return
if(node.store.data == null)return
if(node.store.data.type == 'echarts'){
deviceId.value = node.store.data.correlationId
isEchartsModel.value = true
return
}
if(node?.store?.data?.type == 'text'){
deviceId.value = node?.store?.data?.correlationId
if(node.store.data.type == 'text'){
deviceId.value = node.store.data.correlationId
isTableModel.value = true
return
}
@ -516,11 +621,6 @@ onMounted(() => {
const left = ref(0)
const top = ref(0)
const isMenuShow = ref(false)
const selectedNode:any = ref(null)
function closeAntvx6() {
router.push('/business/project/index')
}
function bigClick(){
graph.zoom(0.1)
@ -531,13 +631,39 @@ function smallClick(){
const isTableModel = ref(false)
const isTableModel = ref(false) //
const isEchartsModel = ref(false) //
function dialogTableModel(){ //
isTableModel.value = false;
}
function dialogEchartsModel(){ //
isEchartsModel.value = false;
}
function isDisplayClick(){
if(isDisplay.value == true){
isDisplay.value = false
let tempGraph:any = graph.toJSON().cells
for(let i = 0;i<tempGraph.length;i++){
if(tempGraph[i].shape == 'rect' || tempGraph[i].shape == 'image-node'){
graph.removeCell(tempGraph[i].id)
}
}
}else{
isDisplay.value = true
for (const key in scenarioResults.value) {
if (!Object.hasOwn(scenarioResults.value, key)) continue;
for(let i = 0;i<customImageData.value.length;i++){
if(key == customImageData.value[i].id){
addAttrText(customImageData.value[i])
}
}
}
}
}
</script>
@ -545,9 +671,10 @@ function dialogTableModel(){ // 关闭变动设置弹窗
<div class="app-layout" @click="isMenuShow = false">
<div class="antvx6-header">
<div class="header-left-box">
<div class="return-icon-box" @click="closeAntvx6" title="返回工作台">
<!-- <div class="return-icon-box" @click="closeAntvx6" title="返回工作台">
<img src="@/assets/x6/return.png" alt="图标" style="cursor: pointer;">
</div>
-->
<div class="project-name">{{ projectInfo.name }}</div>
<!-- <div class="return-icon-box" @click="analysisAdd" title="新增模拟分析">
<img src="@/assets/x6/add.png" alt="图标" style="cursor: pointer;">
@ -565,7 +692,7 @@ function dialogTableModel(){ // 关闭变动设置弹窗
<img src="@/assets/x6/reduce.png">
<div class="operation-icon-text">缩小</div>
</div>
<div class="operation-icon-box" @click="isDisplay = !isDisplay">
<div class="operation-icon-box" @click="isDisplayClick">
<img v-if="isDisplay" src="@/assets/x6/display.png">
<img v-else src="@/assets/x6/hide.png">
<div class="operation-icon-text">显示</div>
@ -582,23 +709,13 @@ function dialogTableModel(){ // 关闭变动设置弹窗
<img src="@/assets/x6/del.png" alt="图标" title="删除" style="cursor: pointer;"
@click="deleteNode"> -->
</div>
<div class="line-style-box">
<div class="expansionandcontraction-box" v-if="isExpansionandcontraction == false" @click="isExpansionandcontraction = true">
<img src="@/assets/x6/expansionandcontraction-left.png">
</div>
<div v-if="isExpansionandcontraction == true" style="display: flex;align-items: center;">
<div class="expansionandcontraction-box" @click="isExpansionandcontraction = false">
<img src="@/assets/x6/expansionandcontraction-right.png">
</div>
<div class="style-content-box">
<div class="style-content-box-title">样式</div>
</div>
</div>
</div>
</div>
<el-dialog v-model="isTableModel" :close-on-click-modal="false" :modal="false" draggable :before-close="dialogTableModel" title="设备分析列表" append-to-body width="1430px">
<TableModels v-id="isTableModel" :deviceId="deviceId" :scenarioId="scenarioId"/>
<TableModels v-if="isTableModel" :deviceId="deviceId" :scenarioId="scenarioId"/>
</el-dialog>
<el-dialog v-model="isEchartsModel" :close-on-click-modal="false" :modal="false" draggable :before-close="dialogEchartsModel" title="设备分析图像" append-to-body width="1430px">
<EchartsModels v-if="isEchartsModel" :deviceId="deviceId" :scenarioId="scenarioId"/>
</el-dialog>
</div>
</template>
@ -878,3 +995,39 @@ function dialogTableModel(){ // 关闭变动设置弹窗
padding-left: 15px;
}
</style>
<style>
.el-dialog {
padding: 0 !important;
border-radius: 10px !important;
border: 1px solid #363636 !important;
}
.el-dialog .el-dialog__header {
display: flex;
display: -webkit-flex;
justify-content: flex-start;
-webkit-justify-content: flex-start;
align-items: center;
-webkit-align-items: center;
padding: 10px 20px;
background-color: #f1f3f8 !important;
font-family: 'Arial Negreta', 'Arial Normal', 'Arial', sans-serif;
font-weight: 700;
font-style: normal;
font-size: 16px;
color: #1B1B1B;
text-align: left;
border-radius: 10px 10px 0 0;
height: 50px;
}
.el-dialog .el-dialog__close {
font-size: 22px;
color: rgb(80, 80, 80);
}
</style>
<style >
.custom-image-node {
cursor: pointer;
}
</style>

View File

@ -11,6 +11,7 @@ import Page from '@/components/Pagination/page.vue'
import { getDictItemById } from '@/api/dict';
import { topologyDevicesLsit } from '@/api/business/project';
import { searchEventsByScenarioId } from '@/api/business/event';
const conditionData:any = ref([{ //
@ -30,12 +31,18 @@ const props = defineProps({ // 接收父组件传递的项目信息
type: String,
default: ''
},
isEdit: {
required: true,
type: Boolean,
default: false
},
});
const isEdit = ref(props.isEdit)
function submitClick() {
debugger
return menuList.value
}
function initDeviceData(){
console.log(props.projectInfo)
topologyDevicesLsit({
id: props.projectInfo.projectId
}).then((res:any) => {
@ -44,9 +51,34 @@ function initDeviceData(){
}
})
}
function initConditionData(){
searchEventsByScenarioId({
scenarioId: props.scenarioId
}).then((res:any) => {
if(res.length > 0){
menuList.value = []
}
for(let i = 0; i < res.length; i++){
if(res[i].attrChanges != null && res[i].attrChanges != ''){
let attrChanges = JSON.parse(res[i].attrChanges)
menuList.value.push({
...attrChanges,
eventId: res[i].eventId,
})
getTimelineList()
}
}
if(res.length > 0){
getDeviceInfo(menuList.value[0].device)
}
})
}
onMounted(() => {
DicInit()
initDeviceData()
initDeviceData()
initConditionData()
});
const deviceData:any = ref([]) //
const attributeData:any = ref([]) //
@ -94,6 +126,7 @@ function addAttr(){ // 添加自定义属性
function menuClick(index:any){ //
menuIndex.value = index;
segmentationIndex.value = 0;
getTimelineList()
}
function addMenu(){ //
menuList.value.push({
@ -124,6 +157,9 @@ function DicInit() {
getDictItemById(params).then((result: any) => {
attributeData.value = result.data.records;
}).catch((err: any) => {
if(menuList.value[menuIndex.value].target.entityType == 'device'){
getDeviceInfo(menuList.value[menuIndex.value].device)
}
});
let paramss = {
dictId: '9d87f873bc80e79c6d399131cbe01016',
@ -132,6 +168,9 @@ function DicInit() {
}
getDictItemById(paramss).then((result: any) => {
materialData.value = result.data.records;
if(menuList.value[menuIndex.value].target.entityType == 'device'){
getDeviceInfo(menuList.value[menuIndex.value].device)
}
}).catch((err: any) => {
});
}
@ -173,6 +212,25 @@ function changeMaterial(e:any){ // 设备属性改变时,清空变化物料
getDeviceInfo(e)
}
//
// async function DicInit() {
// let params = {
// dictId: 'dc7419e5f8a655966e6bb90b0cb5c0c2',
// size:99,
// current:1
// }
// const attributeResult = await getDictItemById(params)
// attributeData.value = attributeResult.data.records;
// let paramss = {
// dictId: '9d87f873bc80e79c6d399131cbe01016',
// size:99,
// current:1
// }
// const materialResult = await getDictItemById(paramss)
// materialData.value = materialResult.data.records;
// initConditionData()
// }
const devicename = ref('') //
@ -181,13 +239,21 @@ const timelineList:any = ref([]) // 时间线数据
function getTimelineList(){
timelineList.value = []
menuList.value.forEach((item:any) => {
// menuList.value.forEach((item:any) => {
// item.segments.forEach((segment:any) => {
// segment.timeline.forEach((timeline:any) => {
// console.log(timeline)
// timelineList.value.push(timeline)
// })
// })
// })
let item = menuList.value[menuIndex.value]
item.segments.forEach((segment:any) => {
segment.timeline.forEach((timeline:any) => {
console.log(timeline)
timelineList.value.push(timeline)
})
})
})
}
function getDeviceInfo(e:any){ //
devicename.value = ""
@ -227,25 +293,26 @@ function getDeviceInfo(e:any){ // 获取设备信息
:class="{'condition-item-active': index == menuIndex}" @click="menuClick(index)">
{{ item.label }}
</div>
<img src="@/assets/add.png" alt="" class="condition-img" @click="addMenu">
<img src="@/assets/add.png" v-if="isEdit == true" alt="" class="condition-img" @click="addMenu">
</div>
<el-form ref="infoForm" label-width="120px">
<el-form-item label="始发设备(物料)" prop="code">
<el-select v-model="menuList[menuIndex].device" placeholder="请选择" style="width:100%;" clearable @change="getDeviceInfo">
<el-select v-model="menuList[menuIndex].device" placeholder="请选择" style="width:100%;" clearable @change="getDeviceInfo"
:disabled="isEdit == false">
<el-option v-for="item in deviceData" :key="item.deviceName" :label="item.deviceName +'('+item.materialName+')'" :value="item.deviceName" />
</el-select>
</el-form-item>
<div style="display: flex; width: 100%;justify-content: space-between;">
<el-form-item label="设备属性" prop="name" style="width: 49%;">
<el-select v-model="menuList[menuIndex].attribute" placeholder="请选择" style="width:100%;" clearable
:disabled="menuList[menuIndex].material!='' && menuList[menuIndex].material!=null" @change="changeAttribute">
:disabled="(menuList[menuIndex].material!='' && menuList[menuIndex].material!=null) || isEdit == false" @change="changeAttribute">
<el-option v-for="item in attributeData" :key="item.itemCode" :label="item.dictName" :value="item.itemCode" />
</el-select>
</el-form-item>
<el-form-item label="变化物料" style="width: 49%;">
<el-select v-model="menuList[menuIndex].material" placeholder="请选择" style="width:100%;" clearable
:disabled="menuList[menuIndex].attribute!='' && menuList[menuIndex].attribute!=null" @change="changeMaterial">
:disabled="(menuList[menuIndex].attribute!='' && menuList[menuIndex].attribute!=null)|| isEdit == true" @change="changeMaterial">
<el-option v-for="item in materialData" :key="item.itemCode" :label="item.dictName" :value="item.itemCode" />
</el-select>
</el-form-item>
@ -254,9 +321,9 @@ function getDeviceInfo(e:any){ // 获取设备信息
<div v-for="(item, index) in menuList[menuIndex].segments" :key="index" class="segmentation-item"
:class="{'segmentation-item-active': index == segmentationIndex}" @click="segmentationIndex = index">
<span>{{ item.segmentId }}</span>
<svg t="1766969938271" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4998" width="14" height="14"><path d="M571.01312 523.776l311.3472-311.35232c15.7184-15.71328 15.7184-41.6256 0-57.344l-1.69472-1.69984c-15.7184-15.71328-41.6256-15.71328-57.34912 0l-311.3472 311.77728-311.35232-311.77728c-15.7184-15.71328-41.63072-15.71328-57.344 0l-1.69984 1.69984a40.0128 40.0128 0 0 0 0 57.344L452.92544 523.776l-311.35232 311.35744c-15.71328 15.71328-15.71328 41.63072 0 57.33888l1.69984 1.69984c15.71328 15.7184 41.6256 15.7184 57.344 0l311.35232-311.35232 311.3472 311.35232c15.72352 15.7184 41.63072 15.7184 57.34912 0l1.69472-1.69984c15.7184-15.70816 15.7184-41.6256 0-57.33888l-311.3472-311.35744z" p-id="4999" fill="currentColor"></path></svg>
<svg v-if="isEdit == true" t="1766969938271" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4998" width="14" height="14"><path d="M571.01312 523.776l311.3472-311.35232c15.7184-15.71328 15.7184-41.6256 0-57.344l-1.69472-1.69984c-15.7184-15.71328-41.6256-15.71328-57.34912 0l-311.3472 311.77728-311.35232-311.77728c-15.7184-15.71328-41.63072-15.71328-57.344 0l-1.69984 1.69984a40.0128 40.0128 0 0 0 0 57.344L452.92544 523.776l-311.35232 311.35744c-15.71328 15.71328-15.71328 41.63072 0 57.33888l1.69984 1.69984c15.71328 15.7184 41.6256 15.7184 57.344 0l311.35232-311.35232 311.3472 311.35232c15.72352 15.7184 41.63072 15.7184 57.34912 0l1.69472-1.69984c15.7184-15.70816 15.7184-41.6256 0-57.33888l-311.3472-311.35744z" p-id="4999" fill="currentColor"></path></svg>
</div>
<div class="segmentation-item-add" @click="addSegmentation">
<div class="segmentation-item-add" @click="addSegmentation" v-if="isEdit == true">
+ 新增分段
</div>
</div>
@ -291,12 +358,12 @@ function getDeviceInfo(e:any){ // 获取设备信息
<el-input v-model="item.value" style="width: 100%" placeholder=""></el-input>
</div>
<div class="segmentation_custom_attrs_content4" @click="removeAttr(indexs)">
删除
<div class="segmentation_custom_attrs_content4" >
<span @click="removeAttr(indexs)" v-if="isEdit == true">删除</span>
</div>
</div>
</div>
<div class="addAttrBox" @click="addAttr">
<div class="addAttrBox" @click="addAttr" v-if="isEdit == true">
<svg t="1766543724217" style="margin-right: 5px;" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6518" width="16" height="16"><path d="M105 480a8 8 0 0 1 8-8h799a8 8 0 0 1 8 8v64a8 8 0 0 1-8 8H113a8 8 0 0 1-8-8v-64z" fill="#266FFF" p-id="6519"></path><path d="M480 920a8 8 0 0 1-8-8V112a8 8 0 0 1 8-8h64a8 8 0 0 1 8 8v800a8 8 0 0 1-8 8h-64z" fill="#266FFF" p-id="6520"></path></svg>
添加一行
</div>

View File

@ -8,14 +8,14 @@ export default {
import { onMounted, ref, nextTick } from "vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus";
import { searchScenariosLsit,addScenarios,updateScenarios,deleteScenarios,deleteBatchScenarios} from "@/api/business/scenario";
import { getActiveAlgorithms } from "@/api/business/scenario";
import ConditionModel from '@/views/component/scenario/condition.vue'
import { simulationInit,simulationRun } from "@/api/business/project";
import Page from '@/components/Pagination/page.vue'
import { getDictItemById } from '@/api/dict';
// import ConditionModel from '@/views/component/scenario/condition.vue'
import Createscenario from '@/views/component/scenario/createscenario.vue'
import { eventsBatchSave } from '@/api/business/event';
const webUrl = import.meta.env.VITE_APP_BASE_HTTP; //
const algorithmTypeData: any = ref([]); //
const stepsActive = ref(0); //
const props = defineProps({ //
@ -25,7 +25,6 @@ const props = defineProps({ // 接收父组件传递的项目信息
default: {}
}
});
//
const queryParams = ref({
current: 1,
@ -72,7 +71,8 @@ const info: any = ref({
description: "",
});
const scenarioId = ref(""); // id
const dialogVisible = ref(false);
const dialogVisible = ref(false); //
const dialogEditVisible = ref(false); //
function addClick() {
title.value = "新增事故情景";
info.value = {
@ -142,25 +142,37 @@ function confirmClick(formEl: any) {
//-
function handleClose() {
dialogVisible.value = false;
dialogEditVisible.value = false;
if (infoForm.value != null) infoForm.value.resetFields();
}
function handleScenarioClose(){
dialogVisible.value = false;
gettableData();
if (infoForm.value != null) infoForm.value.resetFields();
}
//
const rules = ref({
name: [{ required: true, message: "请输入事故情景名称", trigger: "blur" }],
algorithmType: [{ required: true, message: "请选择算法类型", trigger: "change" }],
});
//
function editClick(row: any) {
title.value = "修改事故情景";
function editClick(row: any,type: string) {
title.value =type + "事故情景";
info.value = JSON.parse(JSON.stringify(row));
dialogVisible.value = true;
dialogEditVisible.value = true;
}
const isEdit = ref(false);
function departureClick(row: any,type: string) {
title.value =type;
if(type == "初始条件设置"){
isEdit.value = true;
} else {
isEdit.value = false;
}
info.value = JSON.parse(JSON.stringify(row));
scenarioId.value = row.scenarioId;
isShowCondition.value = true;
}
function editClick1(row: any) {
info.value = JSON.parse(JSON.stringify(row));
dialogEditVisible.value = true;
}
//
@ -226,38 +238,27 @@ function dateFormat(row: any) {
}
const getAlgorithmType = async () => {
try {
let params = {
dictId: '595502b91e7741b5033b53622d8731a3',
size:99,
current:1
}
const result:any = await getDictItemById(params);
if (result.code == '0') {
algorithmTypeData.value = result.data.records
}
const result:any = await getActiveAlgorithms();
algorithmTypeData.value = result
} catch (error) {
}
}
const conditionModel = ref()
function submitClick(){
const tempData = conditionModel.value?.submitClick()
let data:any = []
tempData.forEach((item: any) => {
data.push({
triggerTime:'0',
attrChanges:JSON.stringify(item) ,
scenarioId: scenarioId.value
})
})
eventsBatchSave(data).then((res:any) => {
if (res.code == '0') {
// ElMessage({
// type: "success",
// message: "",
// });
dialogVisible.value = false;
let data = {
scenarioId: info.value.scenarioId,
algorithm_type: info.value.algorithmType,
name: info.value.name,
description: info.value.description,
}
updateScenarios(data).then((res:any) => {
if (res == true) {
ElMessage({
type: "success",
message: "保存成功",
});
dialogEditVisible.value = false;
gettableData();
}
})
@ -288,8 +289,24 @@ function confirmationAnalysis(row: any) {
let data = {
data: res.data
}
simulationRun(row.projectId, row.scenarioId, data || {}).then((res) => {
simulationRun(row.projectId, row.scenarioId, data || {}).then((res:any) => {
if(res.code == '0'){
ElMessage({
type: "success",
message: "模拟计算成功",
});
gettableData();
setTimeout(() => {
window.open(webUrl + '/#/viewanalysis?projectId='+ row.projectId +'&scenarioId='+ row.scenarioId, '_blank');
}, 1000);
}else{
ElMessage({
type: "error",
message: "模拟运行失败,请检查后端服务是否正常",
});
}{}
}).catch((error) => {
console.error('模拟运行失败:', error);
ElMessage({
@ -303,15 +320,39 @@ function confirmationAnalysis(row: any) {
message: "模拟初始化失败",
});
}
});
})
}
const isShowCondition = ref<boolean>(false); //
const conditionModelRef = ref<any>() //
function handleScenarioClose(){
isShowCondition.value = false;
}
function submitConditionClick(){
const tempData = conditionModelRef.value?.submitClick()
let data:any = []
tempData.forEach((item: any) => {
data.push({
triggerTime:'0',
eventId: item.eventId ==null? '' : item.eventId,
attrChanges:JSON.stringify(item) ,
scenarioId: scenarioId.value
})
})
eventsBatchSave(data).then((res:any) => {
if (res.code == '0') {
isShowCondition.value = false;
gettableData();
ElMessage({
type: "success",
message: "修改成功",
});
}
})
}
</script>
@ -358,25 +399,24 @@ function confirmationAnalysis(row: any) {
style="display: flex;display: -webkit-flex; justify-content: space-around;-webkit-justify-content: space-around; ">
<img src="@/assets/table/edit.png" alt="" title="修改" v-if="scope.row.status == 0"
@click="editClick(scope.row)" style="cursor: pointer; ">
@click="editClick(scope.row,'修改')" style="cursor: pointer; ">
<img src="@/assets/table/view.png" alt="" title="查看" v-if="scope.row.status != 0"
@click="editClick(scope.row)" style="cursor: pointer; ">
@click="editClick(scope.row,'查看')" style="cursor: pointer; ">
<img src="@/assets/table/shifa.png" alt="" title="始发事件" v-if="scope.row.status == 0"
@click="editClick(scope.row)" style="cursor: pointer; ">
@click="departureClick(scope.row,'初始条件设置')" style="cursor: pointer; ">
<img src="@/assets/table/see.png" alt="" title="查看始发事件" v-if="scope.row.status != 0"
@click="editClick(scope.row)" style="cursor: pointer; ">
@click="departureClick(scope.row,'查看初始条件')" style="cursor: pointer; ">
<img src="@/assets/table/moni.png" alt="" title="模拟计算" v-if="scope.row.status == 0"
@click="confirmationAnalysis(scope.row)" style="cursor: pointer; ">
<img src="@/assets/table/moni_disabled.png" alt="" title="查看始发事件" v-if="scope.row.status != 0">
<img src="@/assets/table/result.png" alt="" title="计算结果" v-if="scope.row.status == 2"
@click="editClick(scope.row)" style="cursor: pointer; ">
@click="editClick1(scope.row)" style="cursor: pointer; ">
<img src="@/assets/table/result_disabled.png" alt="" title="计算结果" v-if="scope.row.status != 2">
<img src="@/assets/table/del.png" alt="" title="删除"
@click="delAloneClick(scope.row)" style="cursor: pointer; ">
</span>
@ -387,38 +427,43 @@ function confirmationAnalysis(row: any) {
</div>
<Createscenario v-if="dialogVisible" :projectInfo="projectInfo" @closeCreatescenario ="closeCreatescenario"/>
<!-- <el-dialog v-model="dialogVisible" :close-on-click-modal="false"
:modal="false" draggable :before-close="handleScenarioClose" title="新增事故情景"
append-to-body width="1000px">
<div class="scenario-setting-box">
<el-steps style="width: 500px;margin:0 auto " :active="stepsActive" align-center>
<el-step title="事故情景设置"/>
<el-step title="初始条件设置" />
</el-steps>
</div>
<div class="scenario-setting-bottombox">
<el-form ref="infoForm" :model="info" :rules="rules" label-width="140px" style="width:600px"
v-if="stepsActive == 0">
<el-dialog v-model="dialogEditVisible" :close-on-click-modal="false"
:modal="false" draggable :before-close="handleClose" title="新增事故情景"
append-to-body width="590px">
<el-form ref="infoForm" :model="info" :rules="rules" label-width="140px" style="width:500px">
<el-form-item label="事故情景名称:" prop="name">
<el-input v-model="info.name" style="width: 100%" placeholder="输入事故情景名称" ></el-input>
<el-input v-model="info.name" style="width: 100%" placeholder="输入事故情景名称" :disabled="title == '查看事故情景'"></el-input>
</el-form-item>
<el-form-item label="算法类型:" prop="algorithmType">
<el-select v-model="info.algorithmType" placeholder="请选择" style="width:100%;" clearable>
<el-option v-for="item in algorithmTypeData" :key="item.id" :label="item.dictName" :value="item.itemCode" />
<el-form-item label="算法类型:" prop="algorithmType" :disabled="title == '查看事故情景'">
<el-select v-model="info.algorithmType" placeholder="请选择" style="width:100%;" clearable :disabled="title == '查看事故情景'">
<el-option v-for="item in algorithmTypeData" :key="item.algorithmType" :label="item.name" :value="item.algorithmType" />
</el-select>
</el-form-item>
<el-form-item label="事故情景描述:">
<el-input type="textarea" v-model="info.description" :rows="6" style="width: 100%" placeholder="请输入事故情景描述"></el-input>
<el-form-item label="事故情景描述:" :disabled="title == '查看事故情景'">
<el-input type="textarea" v-model="info.description" :rows="6" style="width: 100%" placeholder="请输入事故情景描述" :disabled="title == '查看事故情景'"></el-input>
</el-form-item>
</el-form>
<ConditionModel v-if="stepsActive == 1" ref="conditionModel" :projectInfo="projectInfo" :scenarioId="scenarioId" />
<div style="text-align: right;">
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="submitClick" v-if="title == '修改事故情景'">确定</el-button>
</div>
</el-dialog>
<el-dialog v-model="isShowCondition" :close-on-click-modal="false"
:modal="false" draggable :before-close="handleScenarioClose" :title="title"
append-to-body width="1000px">
<ConditionModel v-if="isShowCondition" ref="conditionModelRef" :projectInfo="projectInfo" :scenarioId="scenarioId"
:isEdit="isEdit"
/>
<div style="text-align: center;">
<el-button v-if="stepsActive == 0" type="primary" @click="confirmClick(infoForm)">下一步</el-button>
<el-button v-else type="primary" @click="submitClick">下一步</el-button>
<el-button @click="handleScenarioClose">取消</el-button>
<el-button type="primary" @click="submitConditionClick" v-if="title == '初始条件设置'">确定</el-button>
</div>
</el-dialog> -->
</el-dialog>
</div>
</template>