|
@@ -19,8 +19,13 @@
|
|
|
</el-header>
|
|
|
<splitpanes class="default-theme diysplitpanes">
|
|
|
<pane min-size="10" size="20" max-size="50" class="custom-aside">
|
|
|
- <el-tabs type="border-card" class="full-height-tabs">
|
|
|
- <el-tab-pane label="Liberal">
|
|
|
+ <el-tabs
|
|
|
+ v-model="activeName"
|
|
|
+ type="border-card"
|
|
|
+ class="full-height-tabs"
|
|
|
+ @tab-click="handleTabclick"
|
|
|
+ >
|
|
|
+ <el-tab-pane label="Liberal" name="Liberal">
|
|
|
<el-collapse v-model="colactiveNames">
|
|
|
<el-collapse-item
|
|
|
v-for="item in collapseData"
|
|
@@ -48,180 +53,147 @@
|
|
|
</el-collapse>
|
|
|
</el-tab-pane>
|
|
|
|
|
|
- <el-tab-pane label="Project"></el-tab-pane>
|
|
|
- <el-tab-pane label="Result"></el-tab-pane>
|
|
|
+ <el-tab-pane label="Project" name="Project"></el-tab-pane>
|
|
|
+ <el-tab-pane label="Result" name="Result">
|
|
|
+ <el-empty v-if="activities.length === 0" description="暂无数据" />
|
|
|
+ <el-timeline v-else>
|
|
|
+ <el-timeline-item
|
|
|
+ v-for="(activity, index) in activities"
|
|
|
+ center
|
|
|
+ :key="index"
|
|
|
+ :color="selectedIndex === index ? '#67C23A' : ''"
|
|
|
+ size="normal"
|
|
|
+ :timestamp="activity.timestamp"
|
|
|
+ @click="handleTimelineItemClick(index)"
|
|
|
+ >
|
|
|
+ {{ activity.content }}
|
|
|
+ </el-timeline-item>
|
|
|
+ </el-timeline>
|
|
|
+ </el-tab-pane>
|
|
|
</el-tabs>
|
|
|
</pane>
|
|
|
<pane class="custom-main">
|
|
|
- <div class="main-header">
|
|
|
- <el-tabs type="border-card">
|
|
|
- <el-tab-pane label="Topology">
|
|
|
- <el-space :size="spacesize" class="spaceclass">
|
|
|
- <!-- 放大 -->
|
|
|
- <el-button
|
|
|
- title="放大"
|
|
|
- @click="zoomIn"
|
|
|
- class="custom-icon-button"
|
|
|
- >
|
|
|
- <el-icon><ZoomIn /></el-icon>
|
|
|
- </el-button>
|
|
|
-
|
|
|
- <!-- 缩小 -->
|
|
|
- <el-button
|
|
|
- title="缩小"
|
|
|
- @click="zoomOut"
|
|
|
- class="custom-icon-button"
|
|
|
- >
|
|
|
- <el-icon><ZoomOut /></el-icon>
|
|
|
- </el-button>
|
|
|
-
|
|
|
- <!-- 自适应 -->
|
|
|
- <el-button
|
|
|
- title="自适应"
|
|
|
- @click="fitView"
|
|
|
- class="custom-icon-button"
|
|
|
- >
|
|
|
- <el-icon><FullScreen /></el-icon>
|
|
|
- </el-button>
|
|
|
-
|
|
|
- <el-tooltip content="重置" placement="top">
|
|
|
- <el-button
|
|
|
- title="重置"
|
|
|
- @click="resetTransform"
|
|
|
- class="custom-icon-button"
|
|
|
- >
|
|
|
- <changebak name="reset" />
|
|
|
- </el-button>
|
|
|
- </el-tooltip>
|
|
|
-
|
|
|
- <el-tooltip content="背景切换" placement="top">
|
|
|
- <el-button
|
|
|
- title="背景切换"
|
|
|
- @click="toggleDarkMode"
|
|
|
- class="custom-icon-button"
|
|
|
- >
|
|
|
- <changebak v-if="dark" name="sun" />
|
|
|
- <changebak v-else name="moon" />
|
|
|
- </el-button>
|
|
|
- </el-tooltip>
|
|
|
-
|
|
|
- <el-tooltip content="保存" placement="top">
|
|
|
- <el-button @click="saveproject" class="custom-icon-button">
|
|
|
- <el-icon class="btn-icon" :color="iconcolor"
|
|
|
- ><UploadFilled
|
|
|
- /></el-icon>
|
|
|
- </el-button>
|
|
|
- </el-tooltip>
|
|
|
-
|
|
|
- <el-tooltip content="删除节点" placement="top">
|
|
|
- <el-button @click="removeNode" class="custom-icon-button">
|
|
|
- <el-icon class="btn-icon" :color="iconcolor"
|
|
|
- ><DocumentDelete
|
|
|
- /></el-icon>
|
|
|
- </el-button>
|
|
|
- </el-tooltip>
|
|
|
-
|
|
|
- <el-tooltip content="删除线" placement="top">
|
|
|
- <el-button @click="removeEdge" class="custom-icon-button">
|
|
|
- <el-icon class="btn-icon" :color="iconcolor"
|
|
|
- ><Crop
|
|
|
- /></el-icon>
|
|
|
- </el-button>
|
|
|
- </el-tooltip>
|
|
|
-
|
|
|
- <el-tooltip content="清空全部" placement="top">
|
|
|
- <el-button @click="confirmDelete" class="custom-icon-button">
|
|
|
- <el-icon class="btn-icon" :color="iconcolor"
|
|
|
- ><DeleteFilled
|
|
|
- /></el-icon>
|
|
|
- </el-button>
|
|
|
- </el-tooltip>
|
|
|
- </el-space>
|
|
|
- </el-tab-pane>
|
|
|
- </el-tabs>
|
|
|
- </div>
|
|
|
- <div class="flow-content">
|
|
|
- <vueflow ref="vueflowref" />
|
|
|
- </div>
|
|
|
+ <splitpanes class="default-theme" horizontal>
|
|
|
+ <pane min-size="50" size="75" max-size="100" class="flow-pane">
|
|
|
+ <div class="main-header">
|
|
|
+ <el-tabs type="border-card">
|
|
|
+ <el-tab-pane label="Topology">
|
|
|
+ <el-space :size="spacesize" class="spaceclass">
|
|
|
+ <!-- 放大 -->
|
|
|
+ <el-button
|
|
|
+ title="放大"
|
|
|
+ @click="zoomIn"
|
|
|
+ class="custom-icon-button"
|
|
|
+ >
|
|
|
+ <el-icon><ZoomIn /></el-icon>
|
|
|
+ </el-button>
|
|
|
+
|
|
|
+ <!-- 缩小 -->
|
|
|
+ <el-button
|
|
|
+ title="缩小"
|
|
|
+ @click="zoomOut"
|
|
|
+ class="custom-icon-button"
|
|
|
+ >
|
|
|
+ <el-icon><ZoomOut /></el-icon>
|
|
|
+ </el-button>
|
|
|
+
|
|
|
+ <!-- 自适应 -->
|
|
|
+ <el-button
|
|
|
+ title="自适应"
|
|
|
+ @click="fitView"
|
|
|
+ class="custom-icon-button"
|
|
|
+ >
|
|
|
+ <el-icon><FullScreen /></el-icon>
|
|
|
+ </el-button>
|
|
|
+
|
|
|
+ <el-tooltip content="重置" placement="top">
|
|
|
+ <el-button
|
|
|
+ title="重置"
|
|
|
+ @click="resetTransform"
|
|
|
+ class="custom-icon-button"
|
|
|
+ >
|
|
|
+ <changebak name="reset" />
|
|
|
+ </el-button>
|
|
|
+ </el-tooltip>
|
|
|
+
|
|
|
+ <el-tooltip content="背景切换" placement="top">
|
|
|
+ <el-button
|
|
|
+ title="背景切换"
|
|
|
+ @click="toggleDarkMode"
|
|
|
+ class="custom-icon-button"
|
|
|
+ >
|
|
|
+ <changebak v-if="dark" name="sun" />
|
|
|
+ <changebak v-else name="moon" />
|
|
|
+ </el-button>
|
|
|
+ </el-tooltip>
|
|
|
+
|
|
|
+ <el-tooltip content="保存" placement="top">
|
|
|
+ <el-button
|
|
|
+ @click="saveproject"
|
|
|
+ class="custom-icon-button"
|
|
|
+ >
|
|
|
+ <el-icon class="btn-icon" :color="iconcolor"
|
|
|
+ ><UploadFilled
|
|
|
+ /></el-icon>
|
|
|
+ </el-button>
|
|
|
+ </el-tooltip>
|
|
|
+
|
|
|
+ <el-tooltip content="删除节点" placement="top">
|
|
|
+ <el-button @click="removeNode" class="custom-icon-button">
|
|
|
+ <el-icon class="btn-icon" :color="iconcolor"
|
|
|
+ ><DocumentDelete
|
|
|
+ /></el-icon>
|
|
|
+ </el-button>
|
|
|
+ </el-tooltip>
|
|
|
+
|
|
|
+ <el-tooltip content="删除线" placement="top">
|
|
|
+ <el-button @click="removeEdge" class="custom-icon-button">
|
|
|
+ <el-icon class="btn-icon" :color="iconcolor"
|
|
|
+ ><Crop
|
|
|
+ /></el-icon>
|
|
|
+ </el-button>
|
|
|
+ </el-tooltip>
|
|
|
+
|
|
|
+ <el-tooltip content="清空全部" placement="top">
|
|
|
+ <el-button
|
|
|
+ @click="confirmDelete"
|
|
|
+ class="custom-icon-button"
|
|
|
+ >
|
|
|
+ <el-icon class="btn-icon" :color="iconcolor"
|
|
|
+ ><DeleteFilled
|
|
|
+ /></el-icon>
|
|
|
+ </el-button>
|
|
|
+ </el-tooltip>
|
|
|
+ </el-space>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </div>
|
|
|
+ <div class="flow-content">
|
|
|
+ <vueflow ref="vueflowref" :jobId="jobId" />
|
|
|
+ </div>
|
|
|
+ </pane>
|
|
|
+ <pane min-size="0" size="25" max-size="50">
|
|
|
+ <el-tabs type="border-card" style="height: 100%">
|
|
|
+ <el-tab-pane label="日志" style="height: 100%">
|
|
|
+ <el-input
|
|
|
+ v-model="logContent"
|
|
|
+ type="textarea"
|
|
|
+ id="textarea_id"
|
|
|
+ spellcheck="false"
|
|
|
+ style="height: 100%;font-size: 12px;"
|
|
|
+ :autosize="false"
|
|
|
+ resize="none"
|
|
|
+ />
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </pane>
|
|
|
+ </splitpanes>
|
|
|
</pane>
|
|
|
</splitpanes>
|
|
|
|
|
|
- <el-dialog
|
|
|
- v-model="SLdatadialog"
|
|
|
- align-center
|
|
|
- :append-to-body="true"
|
|
|
- width="500"
|
|
|
- class="dialog_class"
|
|
|
- draggable
|
|
|
- >
|
|
|
- <template #header="{ titleId, titleClass }">
|
|
|
- <div class="my-header">
|
|
|
- <!-- <el-image :src="icon" fit="contain"></el-image> -->
|
|
|
- <h4 :id="titleId" :class="titleClass">模拟数据</h4>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <el-table :data="tableSLData" border height="300" style="width: 100%">
|
|
|
- <el-table-column type="index" width="40" label="" />
|
|
|
- <el-table-column prop="name" label="属性"> </el-table-column>
|
|
|
- <el-table-column prop="value" label="值">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-select
|
|
|
- v-if="row.valueType === 1"
|
|
|
- v-model="row.value"
|
|
|
- placeholder="请选择"
|
|
|
- class="full-width-select"
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="option in row.options"
|
|
|
- :key="option.val"
|
|
|
- :label="option.tag"
|
|
|
- :value="option.val"
|
|
|
- >
|
|
|
- </el-option>
|
|
|
- </el-select>
|
|
|
- <el-input
|
|
|
- v-else-if="row.valueType === 2"
|
|
|
- v-model="row.value"
|
|
|
- class="full-width-input"
|
|
|
- />
|
|
|
- <el-button
|
|
|
- v-else-if="row.valueType === 3"
|
|
|
- ></el-button>
|
|
|
- <div v-else>{{ row.valueDef }}</div>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="unit" label="单位" width="100">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-select
|
|
|
- v-if="row.unitType !== '无'"
|
|
|
- v-model="row.unit"
|
|
|
- placeholder="请选择"
|
|
|
- class="full-width-select"
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="option in row.unitoptions"
|
|
|
- :key="option.val"
|
|
|
- :label="option.tag"
|
|
|
- :value="option.val"
|
|
|
- >
|
|
|
- </el-option>
|
|
|
- </el-select>
|
|
|
- <div v-else>{{ row.unitType }}</div>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
-
|
|
|
- <template #footer>
|
|
|
- <span class="lastbtn">
|
|
|
- <el-button @click="SLdatadialog = false">{{
|
|
|
- $t("dialog.cancel")
|
|
|
- }}</el-button>
|
|
|
- <el-button type="primary" @click="saveSLTabelDialog">
|
|
|
- {{ $t("dialog.ok") }}
|
|
|
- </el-button>
|
|
|
- </span>
|
|
|
- </template>
|
|
|
- </el-dialog>
|
|
|
+ <!-- 模拟数据弹窗 -->
|
|
|
+ <SLDataDialog ref="SLdatadialogref" @selectRunType="handleSelectRunType" />
|
|
|
+
|
|
|
</el-container>
|
|
|
</template>
|
|
|
|
|
@@ -249,6 +221,7 @@ import {
|
|
|
Close
|
|
|
} from "@element-plus/icons-vue"
|
|
|
import changebak from "./vueflow/changebak.vue"
|
|
|
+import SLDataDialog from "./dialog/SLDataDialog.vue"
|
|
|
import { useVueFlow } from "@vue-flow/core"
|
|
|
|
|
|
const { zoomIn, zoomOut, fitView } = useVueFlow()
|
|
@@ -272,8 +245,14 @@ const vueflowref = ref()
|
|
|
const dark = ref(false)
|
|
|
let iconcolor = ref("#000")
|
|
|
|
|
|
-const SLdatadialog = ref(false)
|
|
|
-const tableSLData = ref([])
|
|
|
+const activeName = ref("Liberal")
|
|
|
+
|
|
|
+const selectedIndex = ref(null)
|
|
|
+const activities = ref([])
|
|
|
+
|
|
|
+const jobId = ref()
|
|
|
+
|
|
|
+const SLdatadialogref = ref(null)
|
|
|
|
|
|
const headerbuttons = ref([
|
|
|
{ type: "button", img: "newproject.png", name: "temp" },
|
|
@@ -315,6 +294,20 @@ let mainbuttons = ref([
|
|
|
{ img: "temp.png" }
|
|
|
])
|
|
|
|
|
|
+const logContent = ref("")
|
|
|
+const arrobj = ref([])
|
|
|
+let websock = ref(null);
|
|
|
+let times = ref({
|
|
|
+ lockReconnect: false, //是否真正建立连接
|
|
|
+ timeout: 28 * 1000, //30秒一次心跳
|
|
|
+ timeoutObj: null, //心跳倒计时
|
|
|
+ serverTimeoutObj: null, //
|
|
|
+ timeoutnum: null, //断开重连倒计时
|
|
|
+})
|
|
|
+
|
|
|
+// 用于分开不同求解运行
|
|
|
+const runtype = ref('');
|
|
|
+
|
|
|
const getComponent = () => {
|
|
|
const params = {
|
|
|
transCode: "ES1001",
|
|
@@ -367,10 +360,27 @@ const confirmDelete = () => {
|
|
|
|
|
|
const btnfunc = (name) => {
|
|
|
if (name === "run") {
|
|
|
+ if(runtype.value === '') {
|
|
|
+ ElMessage.error("请先设置模拟类型")
|
|
|
+ return
|
|
|
+ }else if(runtype.value === 'Incompressible Transient'){
|
|
|
+ // 处理不可压缩瞬态的逻辑
|
|
|
+
|
|
|
+ }else if(runtype.value === 'Incompressible Steady State'){
|
|
|
+ // 处理不可压缩稳态的逻辑
|
|
|
+ }else if(runtype.value === 'Compressible Transient'){
|
|
|
+ // 处理可压缩瞬态的逻辑
|
|
|
+ }else if(runtype.value === 'Compressible Steady State'){
|
|
|
+ // 处理可压缩稳态的逻辑
|
|
|
+ }else{
|
|
|
+ ElMessage.error("未知的模拟类型")
|
|
|
+ return
|
|
|
+ }
|
|
|
runProject()
|
|
|
- }else if(name === "SLdata"){
|
|
|
- getSLData();
|
|
|
- SLdatadialog.value = true;
|
|
|
+ } else if (name === "SLdata") {
|
|
|
+ nextTick(() => {
|
|
|
+ SLdatadialogref.value?.openDialog?.(pid.value)
|
|
|
+ })
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -388,122 +398,200 @@ const runProject = () => {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-const getSLData = async () => {
|
|
|
+const handleSelectRunType = (type) => {
|
|
|
+ runtype.value = type
|
|
|
+}
|
|
|
+
|
|
|
+// 获取结果列表
|
|
|
+const getresultlist = () => {
|
|
|
const params = {
|
|
|
- transCode: "ES0012",
|
|
|
+ transCode: "ES0014",
|
|
|
pid: pid.value
|
|
|
}
|
|
|
+ request(params)
|
|
|
+ .then((res) => {
|
|
|
+ console.log("获取结果列表成功", res)
|
|
|
+ // 处理结果列表数据
|
|
|
+ activities.value = res.rows.map((item) => ({
|
|
|
+ jobId: item.jobId,
|
|
|
+ content: item.ser,
|
|
|
+ timestamp: item.startTime
|
|
|
+ }))
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.error("获取结果列表失败", err)
|
|
|
+ ElMessage.error("获取结果列表失败")
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+const handleTabclick = (tab, event) => {
|
|
|
+ console.log("tab clicked:", tab.props.name)
|
|
|
+ if (tab.props.name === "Result") {
|
|
|
+ getresultlist()
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- try {
|
|
|
- const res = await request(params) // 使用 await 等待请求完成
|
|
|
- tableSLData.value = res.rows.map((item) => ({
|
|
|
- ...item,
|
|
|
- isVisible: true // 默认所有行都可见
|
|
|
- }))
|
|
|
- console.log("tableSLData.value", tableSLData.value)
|
|
|
-
|
|
|
- // 使用 for...of 循环以便使用 await
|
|
|
- for (const item of tableSLData.value) {
|
|
|
- if (!item.pcaId) continue
|
|
|
-
|
|
|
- // 并行处理 value 和 unit 的初始化
|
|
|
- const promises = []
|
|
|
-
|
|
|
- if (item.valueType === 1) {
|
|
|
- promises.push(getlistopt(item, "value"))
|
|
|
- }
|
|
|
-
|
|
|
- if (item.unitType !== "无") {
|
|
|
- promises.push(getlistopt(item, "unit"))
|
|
|
- } else {
|
|
|
- item.unit = "无"
|
|
|
- }
|
|
|
-
|
|
|
- // 等待当前 item 的所有初始化完成
|
|
|
- await Promise.all(promises)
|
|
|
- }
|
|
|
+const handleTimelineItemClick = (index) => {
|
|
|
+ selectedIndex.value = index
|
|
|
+ console.log("Timeline item clicked:", activities.value[index])
|
|
|
+ jobId.value = activities.value[index].jobId
|
|
|
+ vueflowref.value?.asideDataref?.getresultData(jobId.value)
|
|
|
+}
|
|
|
|
|
|
- console.log("所有数据初始化完成")
|
|
|
|
|
|
- } catch (err) {
|
|
|
- console.error("初始化失败:", err)
|
|
|
- ElMessage.error("初始化失败")
|
|
|
- }
|
|
|
+//websockct的连接
|
|
|
+function initWebSocket() {
|
|
|
+ //初始化weosocket
|
|
|
+ const wsurl = import.meta.env.VITE_WEBSOCKET_URL + pid.value
|
|
|
+ websock = new WebSocket(wsurl)
|
|
|
+ websock.onopen = websocketonopen
|
|
|
+ websock.onmessage = websocketonmessage
|
|
|
+ websock.onerror = websocketonerror
|
|
|
+ websock.onclose = websocketclose
|
|
|
}
|
|
|
|
|
|
-const getlistopt = async (item, gettype) => {
|
|
|
- let params = {}
|
|
|
- if (gettype === "value") {
|
|
|
- params = {
|
|
|
- transCode: "BES001",
|
|
|
- type: item.valueDef
|
|
|
- }
|
|
|
+// Websoket连接成功事件
|
|
|
+const websocketonopen = (res) => {
|
|
|
+ console.log("WebSocket连接成功", res)
|
|
|
+ start()
|
|
|
+}
|
|
|
+
|
|
|
+// Websoket接收消息事件
|
|
|
+const websocketonmessage = (res) => {
|
|
|
+ console.log('websocket接受消息:',res.data)
|
|
|
+ arrobj.value = []
|
|
|
+ if (res.data.indexOf("{") !== -1) {
|
|
|
+ // console.log("websocket接受消息:", res.data)
|
|
|
+ // 解析 WebSocket 接收到的消息数据
|
|
|
+
|
|
|
+
|
|
|
+ // 新增:记录结构化 JSON 数据到日志(仅打印 step, vars, vals)
|
|
|
+ const lines = res.data.split("\n")
|
|
|
+ const filteredLogs = lines
|
|
|
+ .filter((line) => line.trim() !== "")
|
|
|
+ .map((line) => {
|
|
|
+ try {
|
|
|
+ const json = JSON.parse(line)
|
|
|
+ const { step, vars, vals } = json
|
|
|
+ return JSON.stringify({ step, vars, vals })
|
|
|
+ } catch (e) {
|
|
|
+ // 如果不是合法 JSON,就原样返回
|
|
|
+ return line
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .join("\n")
|
|
|
+
|
|
|
+ logContent.value = logContent.value + "\n" + filteredLogs
|
|
|
+
|
|
|
+ // 自动滚动日志到底部
|
|
|
+ let textarea = document.getElementById("textarea_id")
|
|
|
+ textarea.scrollTop = textarea.scrollHeight
|
|
|
} else {
|
|
|
- params = {
|
|
|
- transCode: "BES001",
|
|
|
- type: item.unitType
|
|
|
+ if (res.data.indexOf("——成功") !== -1) {
|
|
|
+ const timer = setTimeout(function () {
|
|
|
+ console.log("关闭定时器")
|
|
|
+ }, 10000)
|
|
|
+ }
|
|
|
+ if (res.data.indexOf("msg=heartChec") == -1) {
|
|
|
+ // 去除空行
|
|
|
+ const cleanedLog = res.data
|
|
|
+ .split("\n")
|
|
|
+ .filter((line) => line.trim() !== "")
|
|
|
+ .join("\n")
|
|
|
+ logContent.value = logContent.value + "\n" + cleanedLog
|
|
|
+ let textarea = document.getElementById("textarea_id")
|
|
|
+ textarea.scrollTop = textarea.scrollHeight
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- try {
|
|
|
- const res = await request(params)
|
|
|
- // console.log("选项获取成功", res)
|
|
|
-
|
|
|
- if (gettype === "value") {
|
|
|
- if (
|
|
|
- item.value === undefined ||
|
|
|
- item.value === null ||
|
|
|
- item.value === ""
|
|
|
- ) {
|
|
|
- item.value = res.rows?.[0]?.val ?? ""
|
|
|
- }
|
|
|
- item.options = res.rows || []
|
|
|
- } else if (gettype === "unit") {
|
|
|
- if (item.unit === undefined || item.unit === null || item.unit === "") {
|
|
|
- item.unit = res.rows?.[0]?.val ?? ""
|
|
|
- }
|
|
|
- item.unitoptions = res.rows || []
|
|
|
+ reset()
|
|
|
+}
|
|
|
+
|
|
|
+// Websoket连接错误事件
|
|
|
+const websocketonerror = (res) => {
|
|
|
+ console.log("连接错误", res);
|
|
|
+ websock.close();
|
|
|
+ reconnect();
|
|
|
+};
|
|
|
+// Websoket断开事件
|
|
|
+const websocketclose = (res) => {
|
|
|
+ console.log("断开连接", res);
|
|
|
+};
|
|
|
+
|
|
|
+// 心跳包
|
|
|
+const reconnect = () => {
|
|
|
+ if (times.value.lockReconnect) return;
|
|
|
+ times.value.lockReconnect = true;
|
|
|
+ //没连接上会一直重连,设置延迟避免请求过多
|
|
|
+ times.value.timeoutnum && clearTimeout(times.value.timeoutnum);
|
|
|
+ times.value.timeoutnum = setTimeout(function () {
|
|
|
+ //新连接
|
|
|
+ initWebSocket();
|
|
|
+ times.value.lockReconnect = false;
|
|
|
+ }, 10000);
|
|
|
+}
|
|
|
+const reset = () => {
|
|
|
+ //重置心跳
|
|
|
+ clearTimeout(times.value.timeoutObj);
|
|
|
+ clearTimeout(times.value.serverTimeoutObj);
|
|
|
+ start();
|
|
|
+}
|
|
|
+const start = () => {
|
|
|
+ //开启心跳
|
|
|
+ times.value.timeoutObj && clearTimeout(times.value.timeoutObj);
|
|
|
+ times.value.serverTimeoutObj && clearTimeout(times.value.serverTimeoutObj);
|
|
|
+ times.value.timeoutObj = setTimeout(function () {
|
|
|
+ //这里发送一个心跳,后端收到后,返回一个心跳消息
|
|
|
+ if (websock.readyState == 1) {
|
|
|
+ //如果连接正常
|
|
|
+ websock.send("heartCheck");
|
|
|
+ } else {
|
|
|
+ //否则重连
|
|
|
+ reconnect();
|
|
|
}
|
|
|
- } catch (err) {
|
|
|
- console.error("err", err)
|
|
|
- ElMessage.error("选项初始化失败")
|
|
|
- }
|
|
|
+ times.value.serverTimeoutObj = setTimeout(function () {
|
|
|
+ // 超时关闭
|
|
|
+ websock.close(); //如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
|
|
|
+ }, times.value.timeout);
|
|
|
+ }, times.value.timeout);
|
|
|
}
|
|
|
|
|
|
-const saveSLTabelDialog = () => {
|
|
|
- SLdatadialog.value = false
|
|
|
- console.log("tableSLData:", tableSLData.value)
|
|
|
- // dataType 为 -1 的数据
|
|
|
- const validItems = tableSLData.value.filter((item) => item.dataType === -1)
|
|
|
- const pcavals = validItems
|
|
|
- .map((item) => {
|
|
|
- const pcaId = item.pcaId ?? ""
|
|
|
- const value = item.value ?? ""
|
|
|
- const unit = item.unit ?? ""
|
|
|
- return `${pcaId},${value},${unit}`
|
|
|
- })
|
|
|
- .join(";")
|
|
|
+const getlogs = () => {
|
|
|
const params = {
|
|
|
- transCode: "ES0008",
|
|
|
- pcavals: pcavals
|
|
|
+ transCode: "ES0017",
|
|
|
+ pid: pid.value
|
|
|
}
|
|
|
request(params)
|
|
|
.then((res) => {
|
|
|
- ElMessage.success("保存成功")
|
|
|
+ // console.log("获取日志成功", res)
|
|
|
+ logContent.value = res.logs.split('\n').filter(line => line.trim() !== '').join('\n');
|
|
|
+ // 自动滚动日志到底部
|
|
|
+ let textarea = document.getElementById("textarea_id")
|
|
|
+ textarea.scrollTop = textarea.scrollHeight
|
|
|
})
|
|
|
.catch((err) => {
|
|
|
- console.error("err", err)
|
|
|
- ElMessage.error("保存失败")
|
|
|
+ console.error("获取日志失败", err)
|
|
|
+ ElMessage.error("获取日志失败")
|
|
|
})
|
|
|
}
|
|
|
|
|
|
-
|
|
|
onMounted(() => {
|
|
|
getComponent()
|
|
|
+
|
|
|
+ setTimeout(function () {
|
|
|
+ initWebSocket();
|
|
|
+ getlogs();
|
|
|
+ }, 1500);
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
+.flow-pane {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
.main-header {
|
|
|
background: #eeeeee;
|
|
|
border-radius: 0px 0px 0px 0px;
|