Files
pqs-9100_client/frontend/src/views/machine/controlSource/index.vue
2026-07-10 08:42:38 +08:00

568 lines
15 KiB
Vue

<template>
<div>
<el-card style="margin-bottom: 10px" class="cardTop">
<el-form :inline="true" label-width="auto" ref="dialogFormRef" class="form-four">
<el-form-item label="检测源" prop="sourceId">
<el-select
v-model="controlContent.sourceId"
collapse-tags
placeholder="请选择检测源"
:disabled="!isSourceSwitchEnabled"
>
<el-option
v-for="(option, index) in pqSourceArray"
:key="index"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item>
<el-form-item label="检测脚本" prop="scriptId">
<el-select
v-model="controlContent.scriptId"
collapse-tags
placeholder="请选择检测脚本"
@change="handleScriptChange"
>
<el-option
v-for="(option, index) in scriptArray"
:key="index"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
:icon="Connection"
@click="start"
:disabled="!isConnectButtonEnabled"
>
源通讯
</el-button>
<el-button type="text" :style="{ color: connectionStatusColor }" disabled>
<span v-if="connectionState.status === 'connecting'" class="connection-loading-spinner"></span>
{{ connectionStatusText }}
</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card v-if="show" style="margin-bottom: 10px">
<div class="connection-status-row">
<div class="connection-status-left">
<span class="connection-status-dot" :class="`is-${connectionState.status}`"></span>
<span class="connection-status-text">{{ connectionStatusText }}</span>
</div>
<el-divider direction="vertical" />
<div class="running-script-status" :title="runningScriptStatusText">{{ runningScriptStatusText }}</div>
</div>
</el-card>
<el-card v-if="show">
<ControlSourceDetail
v-if="secondLevelOptions.length > 0"
ref="controlSourceDetailRef"
:options="secondLevelOptions"
:formContent="formContent"
:formControl="controlContent"
@update:activeName="handleActiveNameChange"
@update:active-index="handleActiveIndexChange"
v-model:startDisabeld="startDisabeld"
@update:startDisabeld="startDisabeld = $event"
v-model:pauseDisabled="pauseDisabled"
@update:pauseDisabled="pauseDisabled = $event"
@update:runningScriptName="runningScriptName = $event"
/>
</el-card>
</div>
</template>
<script setup lang="ts">
import { computed, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, reactive, ref, watch } from "vue";
import { useDictStore } from "@/stores/modules/dict";
import ControlSourceDetail from "@/views/machine/controlSource/components/controlSourceDetail.vue";
import { type TestScript } from "@/api/device/interface/testScript";
import type { Dict } from "@/api/system/dictionary/interface";
import { getDictTreeByCode } from "@/api/system/dictionary/dictTree";
import { Connection } from "@element-plus/icons-vue";
import { getPqScriptList, getTestSourceList } from "@/api/plan/plan.ts";
import { TestSource } from "@/api/device/interface/testSource";
import { CascaderOption, ElMessage } from "element-plus";
import { useRouter } from "vue-router";
import { useModeStore } from "@/stores/modules/mode";
import socketClient from "@/utils/webSocketClient";
import { checkSimulate } from "@/api/device/controlSource/index.ts";
import { controlSource } from "@/api/device/interface/controlSource";
import { JwtUtil } from "@/utils/jwtUtil";
import mittBus, { TAB_CLOSED_EVENT } from "@/utils/mittBus";
import {
applyConnectionTerminalMessage,
createDisconnectedState,
getConnectionStatusText,
isConnectButtonEnabled as canConnect,
isSourceSelectable,
resolveConnectionFailureReason,
shouldHandleConnectionTerminalMessage,
} from "./connectionState";
defineOptions({
name: "controlSource",
});
const show = ref(false);
const router = useRouter();
const modeId = ref();
const controlSourcePath = "/machine/controlSource";
const secondLevelOptions: any[] = [];
const dialogFormRef = ref();
const dictStore = useDictStore();
const pqSourceList = ref<TestSource.ResTestSource[]>([]);
const modeStore = useModeStore();
const pqSourceArray = ref<{ label: string; value: string }[]>([]);
const scriptArray = reactive<{ label: string; value: string }[]>([]);
const formContent = ref<TestScript.ResTestScript>({
id: "",
name: "",
type: "",
valueType: "",
pattern: modeId.value,
standardName: "",
standardTime: "",
state: 1,
});
const connectionState = ref(createDisconnectedState());
const startDisabeld = ref(true);
const pauseDisabled = ref(true);
const runningScriptName = ref("");
const controlSourceDetailRef = ref<InstanceType<typeof ControlSourceDetail>>();
const controlContent = ref<controlSource.ResControl>({
userPageId: "",
scriptId: "",
scriptIndex: 0,
sourceId: "",
});
const dataSocket = reactive({
socketServe: socketClient.Instance,
});
const webMsgSend = ref<any>();
const handleSocketMessage = (res: { code?: number; message?: string }) => {
if (res.code === 20000) {
ElMessage.error(res.message || "请求失败");
return;
}
webMsgSend.value = res;
};
const bindSocket = () => {
const socket = socketClient.Instance;
dataSocket.socketServe = socket;
socket.connect();
socket.registerCallBack("aaa", handleSocketMessage);
};
const unbindSocket = () => {
dataSocket.socketServe?.unRegisterCallBack?.("aaa");
};
const closeSocket = () => {
unbindSocket();
if (dataSocket.socketServe?.connected) {
dataSocket.socketServe.closeWs();
}
};
const isSourceSwitchEnabled = computed(() => isSourceSelectable(connectionState.value));
const isConnectButtonEnabled = computed(() => canConnect(connectionState.value));
const connectionStatusText = computed(() => getConnectionStatusText(connectionState.value));
const runningScriptStatusText = computed(() =>
runningScriptName.value ? `当前正在加量的脚本:${runningScriptName.value}` : "当前无正在加量脚本"
);
const connectionStatusColor = computed(() => {
switch (connectionState.value.status) {
case "connecting":
return "#e6a23c";
case "connected":
return "#67c23a";
case "failed":
return "#f56c6c";
case "disconnected":
default:
return "#303133";
}
});
const resetConnectionState = () => {
connectionState.value = createDisconnectedState();
};
const markConnectionFailed = (message: {
data?: unknown;
desc?: string;
requestId?: string;
operateCode?: string;
}) => {
connectionState.value = {
status: "failed",
reason: resolveConnectionFailureReason(message),
};
};
onMounted(async () => {
if (!socketClient.Instance) {
console.error("WebSocket 客户端实例不存在");
return;
}
bindSocket();
const pqSourceResult = await getTestSourceList({
pattern: dictStore.getDictData("Pattern").find(item => item.name === modeStore.currentMode)?.id,
datasourceIds: "",
sourceIds: "",
planId: "",
scriptName: "",
errorSysName: "",
sourceName: "",
devIds: [],
id: "",
name: "",
dataSourceId: "",
scriptId: "",
errorSysId: "",
timeCheck: 0,
testState: 0,
reportState: 0,
result: 0,
code: 0,
state: 1,
});
pqSourceList.value = pqSourceResult.data as TestSource.ResTestSource[];
const sourceArray = Array.isArray(pqSourceList.value) ? pqSourceList.value : [];
pqSourceArray.value = sourceArray.map(item => ({
label: item.name || "",
value: item.id,
}));
if (pqSourceArray.value.length > 0) {
controlContent.value.sourceId = pqSourceArray.value[0].value;
}
const patternId = dictStore.getDictData("Pattern").find(item => item.name === modeStore.currentMode)?.id;
const { data } = await getPqScriptList({ pattern: patternId });
scriptArray.push(...data.map(item => ({ label: item.name, value: item.id })));
if (scriptArray.length > 0) {
controlContent.value.scriptId = scriptArray[0].value;
}
nextTick(async () => {
await treeInfo(modeStore.currentMode);
formContent.value.pattern = modeId.value;
formContent.value.id = controlContent.value.scriptId;
show.value = true;
});
});
onActivated(() => {
bindSocket();
});
onDeactivated(() => {
unbindSocket();
});
onBeforeUnmount(() => {
unbindSocket();
mittBus.off(TAB_CLOSED_EVENT, handleTabClosed);
});
const handleTabClosed = (closedPath: string) => {
if (closedPath !== controlSourcePath) {
return;
}
closeSocket();
};
mittBus.on(TAB_CLOSED_EVENT, handleTabClosed);
watch(webMsgSend, newValue => {
if (!newValue) {
return;
}
if (newValue.requestId?.includes("formal_real&&") && newValue.operateCode === "OPER_GATHER") {
if (newValue.code === 10200) {
ElMessage.success("启动成功!");
startDisabeld.value = false;
pauseDisabled.value = false;
controlSourceDetailRef.value?.startTimeCount();
} else if (newValue.code !== 10201) {
ElMessage.error("启动失败!");
startDisabeld.value = false;
pauseDisabled.value = true;
}
}
if (newValue.requestId?.includes("close_source") && newValue.operateCode === "CLOSE_GATHER") {
if (connectionState.value.status !== "connected" || newValue.code !== 10200) {
return;
}
setTimeout(() => {
ElMessage.success("停止成功!");
resetConnectionState();
startDisabeld.value = true;
pauseDisabled.value = true;
controlSourceDetailRef.value?.stopTimeCount();
}, 5000);
}
if (!shouldHandleConnectionTerminalMessage(newValue)) {
return;
}
switch (newValue.requestId) {
case "yjc_ytxjy":
if (newValue.operateCode === "INIT_GATHER") {
connectionState.value = applyConnectionTerminalMessage(connectionState.value, newValue);
if (newValue.code === 10200) {
ElMessage.success("源连接成功!");
startDisabeld.value = false;
pauseDisabled.value = false;
} else {
ElMessage.error(connectionStatusText.value);
startDisabeld.value = true;
pauseDisabled.value = true;
}
}
break;
case "connect":
if (newValue.operateCode === "Source") {
markConnectionFailed(newValue);
ElMessage.error(resolveConnectionFailureReason(newValue));
startDisabeld.value = true;
pauseDisabled.value = true;
}
// else if (newValue.operateCode === "Dev") {
// ElMessage.error(resolveConnectionFailureReason(newValue));
// startDisabeld.value = true;
// pauseDisabled.value = true;
// }
break;
case "server_error":
if (newValue.operateCode === "server_error") {
markConnectionFailed(newValue);
ElMessage.error(connectionStatusText.value);
startDisabeld.value = true;
pauseDisabled.value = true;
}
break;
}
});
watch(
() => controlContent.value.sourceId,
(newValue, oldValue) => {
if (!oldValue || newValue === oldValue) {
return;
}
resetConnectionState();
startDisabeld.value = true;
pauseDisabled.value = true;
runningScriptName.value = "";
controlSourceDetailRef.value?.stopTimeCount();
}
);
const treeInfo = async (currentMode: string) => {
const data: Dict.ResDictTree = {
name: "",
id: "",
pid: "",
pids: "",
code: "Script_Indicator_Items",
sort: 0,
};
const result = await getDictTreeByCode(data);
const result1 = (await getDictTreeByCode({ ...data, code: "Script_Error" })).data[0].children;
const allOptions = convertToOptions(result.data as Dict.ResDictTree[]);
const setAllTree = await setTree(allOptions[0]?.children || [], result1);
secondLevelOptions.push(...setAllTree);
modeId.value = dictStore.getDictData("Pattern").find(item => item.name === currentMode)?.id;
};
const setTree = async (data: any[], data1: any[]) => {
data.forEach(item => {
data1.forEach(item1 => {
if (item.label.replace(/准确度|检测/g, "") === item1.name) {
item.value = item1.id;
}
});
});
return data;
};
const convertToOptions = (dictTree: Dict.ResDictTree[]): CascaderOption[] => {
return dictTree.map(item => ({
value: item.id,
code: item.code.split("-")[1] || item.code.split("-")[0],
label: item.name,
children: item.children ? convertToOptions(item.children) : undefined,
}));
};
const scriptId = ref("");
const scriptIndex = ref(0);
const handleActiveNameChange = (newActiveName: string) => {
scriptId.value = newActiveName;
};
const handleActiveIndexChange = (newActiveIndex: number) => {
scriptIndex.value = newActiveIndex;
};
const handleScriptChange = () => {
router.push({
path: "/machine/controlSource",
state: { title: "新增检测脚本", row: "", mode: modeStore.currentMode },
});
};
const start = async () => {
if (!isConnectButtonEnabled.value) {
return;
}
connectionState.value = {
status: "connecting",
reason: "",
};
startDisabeld.value = true;
pauseDisabled.value = true;
controlContent.value.userPageId = JwtUtil.getLoginName();
controlContent.value.scriptIndex = scriptIndex.value;
try {
await checkSimulate(controlContent.value);
} catch (error: any) {
connectionState.value = {
status: "failed",
reason: error?.message || "请求失败",
};
ElMessage.error(connectionStatusText.value);
}
};
</script>
<style lang="scss" scoped>
:deep(.cardTop) {
.el-card__body {
padding: 20px 0 0 20px;
}
}
.form-four {
display: flex;
flex-wrap: wrap;
.el-form-item {
display: flex;
width: 24%;
.el-form-item__content {
flex: 1;
.el-select,
.el-cascader,
.el-input__inner,
.el-date-editor {
width: 100%;
}
}
}
}
.connection-status-row {
display: flex;
align-items: center;
gap: 8px;
color: var(--el-text-color-regular);
min-width: 0;
white-space: nowrap;
overflow: hidden;
}
.connection-status-left {
flex: 0 0 10%;
min-width: 0;
display: flex;
align-items: center;
gap: 8px;
overflow: hidden;
}
.connection-status-text {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.running-script-status {
flex: 0 0 85%;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.connection-status-dot {
flex: 0 0 auto;
width: 8px;
height: 8px;
border-radius: 50%;
background: #909399;
}
.connection-status-dot.is-connecting {
background: #e6a23c;
}
.connection-status-dot.is-connected {
background: #67c23a;
}
.connection-status-dot.is-failed {
background: #f56c6c;
}
.connection-loading-spinner {
display: inline-block;
width: 12px;
height: 12px;
margin-right: 6px;
border: 2px solid rgba(230, 162, 60, 0.25);
border-top-color: #e6a23c;
border-radius: 50%;
vertical-align: -1px;
animation: control-source-spin 0.8s linear infinite;
}
@keyframes control-source-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
</style>