From e304c012ce20fb13b32dbf7e109806485630a059 Mon Sep 17 00:00:00 2001 From: Lxq <19852720163@163.com> Date: Sun, 8 Feb 2026 17:02:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E9=80=BB=E8=BE=91=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rc_autoplc_front/src/App.vue | 46 + rc_autoplc_front/src/api/tb/devparam.ts | 24 + rc_autoplc_front/src/router/index.ts | 5 + rc_autoplc_front/src/views/Layout.vue | 91 +- rc_autoplc_front/src/views/devinfo/index.vue | 1214 ++++++---------- rc_autoplc_front/src/views/devparam/index.vue | 1291 +++++++++++++++++ .../src/views/islandInfo/index.vue | 364 +---- rc_autoplc_front/src/views/stepinfo/index.vue | 1141 ++++++++++++--- 8 files changed, 2867 insertions(+), 1309 deletions(-) create mode 100644 rc_autoplc_front/src/views/devparam/index.vue diff --git a/rc_autoplc_front/src/App.vue b/rc_autoplc_front/src/App.vue index b7a6acd..09a0f74 100644 --- a/rc_autoplc_front/src/App.vue +++ b/rc_autoplc_front/src/App.vue @@ -1,5 +1,51 @@ + + + diff --git a/rc_autoplc_front/src/views/islandInfo/index.vue b/rc_autoplc_front/src/views/islandInfo/index.vue index 0147f9b..dc4322d 100644 --- a/rc_autoplc_front/src/views/islandInfo/index.vue +++ b/rc_autoplc_front/src/views/islandInfo/index.vue @@ -108,7 +108,7 @@ v-model="drawerVisible" :title="drawerTitle" direction="rtl" - size="500px" + size="600px" :before-close="handleDrawerClose" > - -
- - - 绑定设备 - -
- - - - - - - - - - - - + +
+ + + + + + + + + + + + +
+ 该功能岛暂无动作
-
暂无绑定设备
@@ -198,67 +196,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -307,13 +244,12 @@ import { islandInfolist, islandInfobyid, } from '@/api/tb/islandinfo' -import { devselect, devInfoupd } from '@/api/tb/devinfo' +import { devselect } from '@/api/tb/devinfo' // 加载状态 const loading = ref(false) const submitting = ref(false) const deviceLoading = ref(false) -const savingDeviceBind = ref(false) // 功能岛列表 const islandList = ref([]) @@ -344,12 +280,8 @@ const formData = reactive({ islandDesc: '', }) -// 绑定设备相关 -const bindDeviceDialogVisible = ref(false) -const deviceListForBind = ref([]) -const deviceTableRef = ref() -const selectedDevices = ref([]) -const boundDevices = ref([]) +// 动作列表相关 +const deviceList = ref([]) // 表单验证规则 const formRules = { @@ -426,16 +358,11 @@ const getStatusText = (status: number | null | undefined) => { return '未知' } -// 获取设备序号 +// 获取动作序号 const getDeviceIndex = (index: number) => { return index + 1 } -// 获取已绑定设备序号 -const getBoundDeviceIndex = (index: number) => { - return index + 1 -} - // 获取功能岛列表 const getIslandList = async () => { try { @@ -513,8 +440,7 @@ const resetForm = () => { formData.islandName = '' formData.islandCode = '' formData.islandDesc = '' - boundDevices.value = [] - selectedDevices.value = [] + deviceList.value = [] formRef.value?.clearValidate() } @@ -540,9 +466,9 @@ const handleEdit = async (item: any) => { // 优先使用后端的 desc 字段,兼容其他可能的字段名 formData.islandDesc = data.desc ?? data.islandDesc ?? data.description ?? '' - // 加载已绑定的设备 + // 加载该功能岛下的动作列表 if (formData.id) { - await loadBoundDevices(formData.id) + await loadDeviceList(formData.id) } isEdit.value = true @@ -559,39 +485,51 @@ const handleEdit = async (item: any) => { } } -// 加载已绑定的设备 -const loadBoundDevices = async (islandId: number | string) => { +// 加载该功能岛下的动作列表 +const loadDeviceList = async (islandId: number | string) => { try { + deviceLoading.value = true const res: any = await devselect({}) const allDevices = res?.data ?? res ?? [] if (Array.isArray(allDevices)) { - boundDevices.value = allDevices.filter((device: any) => - device.islandId && (device.islandId === islandId || device.islandId === String(islandId)) - ) + // 过滤出该功能岛下的动作,并排除PLC动作 + deviceList.value = allDevices.filter((device: any) => { + const devModel = device.devModel || '' + const isNotPlc = devModel.toUpperCase() !== 'PLC' + const isBelongToIsland = device.islandId && (device.islandId === islandId || device.islandId === String(islandId)) + return isNotPlc && isBelongToIsland + }) + } else { + deviceList.value = [] } } catch (error) { - console.error('加载已绑定设备失败:', error) - boundDevices.value = [] + console.error('加载动作列表失败:', error) + deviceList.value = [] + } finally { + deviceLoading.value = false } } // 删除功能岛 const handleDelete = async (item: any) => { try { - // 检查是否绑定了设备 + // 检查是否绑定了动作 const res: any = await devselect({}) const allDevices = res?.data ?? res ?? [] const boundDevicesList = Array.isArray(allDevices) - ? allDevices.filter((device: any) => - device.islandId && (device.islandId === item.id || device.islandId === String(item.id)) - ) + ? allDevices.filter((device: any) => { + const devModel = device.devModel || '' + const isNotPlc = devModel.toUpperCase() !== 'PLC' + const isBelongToIsland = device.islandId && (device.islandId === item.id || device.islandId === String(item.id)) + return isNotPlc && isBelongToIsland + }) : [] if (boundDevicesList.length > 0) { - const deviceNames = boundDevicesList.map((d: any) => d.devName || '未命名设备').join('、') + const deviceNames = boundDevicesList.map((d: any) => d.devName || '未命名动作').join('、') const islandName = item.islandName || item.name || '未命名' await ElMessageBox.alert( - `${islandName}已绑定${deviceNames},请先完成设备解绑`, + `${islandName}下还有${deviceNames}等${boundDevicesList.length}个动作,请先完成动作解绑`, '提示', { confirmButtonText: '确定', @@ -674,164 +612,6 @@ const handleSubmit = async () => { } } -// 打开绑定设备对话框 -const handleBindDevice = async () => { - if (!isEdit.value || !formData.id) { - // 新增功能岛不允许绑定设备(仅新增功能岛信息) - return - } - try { - deviceLoading.value = true - const res: any = await devselect({}) - const allDevices = res?.data ?? res ?? [] - - if (Array.isArray(allDevices)) { - // 编辑模式:显示未绑定的设备 + 已绑定该功能岛的设备,排除设备型号为PLC的设备 - deviceListForBind.value = allDevices.filter((device: any) => { - // 排除设备型号为PLC的设备 - if (device.devModel && String(device.devModel).toUpperCase() === 'PLC') { - return false - } - // 筛选条件:islandId为0或空的设备,或者已绑定该功能岛的设备 - return ( - !device.islandId || - device.islandId === formData.id || - device.islandId === String(formData.id) || - device.islandId === 0 || - device.islandId === '0' - ) - }) - } else { - deviceListForBind.value = [] - } - - bindDeviceDialogVisible.value = true - - // 等待DOM更新后设置已选中的设备 - await new Promise(resolve => setTimeout(resolve, 100)) - if (deviceTableRef.value && isEdit.value && formData.id) { - // 编辑模式:默认选中已绑定的设备 - const boundDeviceIds = boundDevices.value.map((d: any) => d.id) - deviceListForBind.value.forEach((device: any) => { - if (boundDeviceIds.includes(device.id)) { - deviceTableRef.value.toggleRowSelection(device, true) - } - }) - } - } catch (error) { - console.error('获取设备列表失败:', error) - ElMessage.error('获取设备列表失败') - } finally { - deviceLoading.value = false - } -} - -// 设备选择变化 -const handleDeviceSelectionChange = (selection: any[]) => { - selectedDevices.value = selection -} - -// 保存设备绑定 -const handleSaveDeviceBind = async () => { - try { - savingDeviceBind.value = true - - // 获取当前选中的设备ID列表 - const selectedDeviceIds = selectedDevices.value.map((d: any) => d.id) - - if (!isEdit.value || !formData.id) return - - // 编辑模式:立即保存绑定关系 - // 获取之前已绑定的设备ID列表 - const previousBoundDeviceIds = boundDevices.value.map((d: any) => d.id) - - // 需要绑定的设备(新增的) - const devicesToBind = selectedDeviceIds.filter((id: any) => !previousBoundDeviceIds.includes(id)) - - // 需要解绑的设备(取消选中的) - const devicesToUnbind = previousBoundDeviceIds.filter((id: any) => !selectedDeviceIds.includes(id)) - - // 执行绑定操作 - const bindPromises = devicesToBind.map((deviceId: any) => { - const device = deviceListForBind.value.find((d: any) => d.id === deviceId) - if (device) { - // 包含设备名称等必填字段 - return devInfoupd({ - id: deviceId, - devName: device.devName, // 必填字段 - islandId: formData.id, - }) - } - return Promise.resolve() - }) - - // 执行解绑操作 - const unbindPromises = devicesToUnbind.map((deviceId: any) => { - const device = - deviceListForBind.value.find((d: any) => d.id === deviceId) || - boundDevices.value.find((d: any) => d.id === deviceId) - if (device) { - // 包含设备名称等必填字段 - return devInfoupd({ - id: deviceId, - devName: device.devName, // 必填字段 - islandId: 0, // 解绑时设置为0 - }) - } - return Promise.resolve() - }) - - // 执行所有操作 - const allPromises = [...bindPromises, ...unbindPromises] - if (allPromises.length > 0) { - const results = await Promise.all(allPromises) - // 检查是否有失败的请求 - const failedResults = results.filter((res: any) => { - if (!res) return false - // 检查响应数据(可能是直接返回的数据对象) - const data = res.data || res - return data && (data.code !== '0' && data.code !== 0 && data.code !== undefined && !data.success) - }) - if (failedResults.length > 0) { - const firstFailed = failedResults[0] - const errorData = firstFailed?.data || firstFailed - throw new Error(errorData?.message || errorData?.msg || '部分设备绑定/解绑失败') - } - } - - // 更新已绑定设备列表 - await loadBoundDevices(formData.id) - - ElMessage.success('保存成功') - - bindDeviceDialogVisible.value = false - } catch (error: any) { - console.error('保存设备绑定失败:', error) - // 处理不同类型的错误 - let errorMessage = '保存设备绑定失败' - if (error?.message) { - errorMessage = error.message - } else if (error?.response?.data?.message) { - errorMessage = error.response.data.message - } else if (error?.response?.data?.msg) { - errorMessage = error.response.data.msg - } else if (typeof error === 'string') { - errorMessage = error - } - ElMessage.error(errorMessage) - } finally { - savingDeviceBind.value = false - } -} - -// 关闭绑定设备对话框 -const handleBindDialogClose = () => { - bindDeviceDialogVisible.value = false - selectedDevices.value = [] - if (deviceTableRef.value) { - deviceTableRef.value.clearSelection() - } -} // 关闭抽屉 const handleDrawerClose = () => { @@ -1048,15 +828,11 @@ onMounted(() => { gap: 10px; } -.bind-device-wrapper { +.device-list-wrapper { width: 100%; } -.bound-devices-table-wrapper { - width: 100%; -} - -.no-bound-devices { +.no-devices { margin-top: 12px; padding: 12px; text-align: center; @@ -1067,12 +843,6 @@ onMounted(() => { border: 1px dashed #dcdfe6; } -.dialog-footer { - display: flex; - justify-content: flex-end; - gap: 10px; -} - // 功能岛卡片动画:从下往上滑动 @keyframes slideUpIn { from { diff --git a/rc_autoplc_front/src/views/stepinfo/index.vue b/rc_autoplc_front/src/views/stepinfo/index.vue index 1af6443..cca7801 100644 --- a/rc_autoplc_front/src/views/stepinfo/index.vue +++ b/rc_autoplc_front/src/views/stepinfo/index.vue @@ -6,26 +6,38 @@ 流程创建
- +
-

功能岛

+

动作列表

-
- - - -
{{ index + 1 }}. {{ item.islandName || item.name }}
-
+
+
+ {{ group.islandName }} +
+
+ + + +
{{ item.devName || '未命名动作' }}
+
+
@@ -39,15 +51,49 @@ >
- - - +
+
+ 标准流程序号 + + +
+
+ 标准流程名称 + + +
+
+   + + 确定 + +
+
编辑流程 @@ -55,7 +101,7 @@
- 拖拽左侧功能岛到此处创建流程步骤 + 拖拽左侧动作到此处创建流程步骤
@@ -76,9 +122,9 @@
- + -
{{ item.islandName || item.name }}
+
{{ item.devName || '未命名动作' }}
@@ -96,7 +142,7 @@
-
{{ item.description }}
+
{{ item.description || `第${chineseNumbers[item.sort] || item.sort + 1}次[${item.devName || '未命名动作'}]` }}
@@ -112,6 +158,31 @@
+ + +
+
+
+ {{ selectedWorkflowItem?.devName || '动作' }} 参数预览 +
+ 收起 +
+
+
+ {{ p.paramName || `参数${i + 1}` }}: + {{ formatPreviewValue(p) }} +
+
+
+ 暂无参数(请先点击“编辑”配置参数并保存) +
+
+
+
退出 清空 @@ -204,7 +275,7 @@ @@ -1342,6 +1907,23 @@ onMounted(() => { padding: 5px; } + .island-group { + margin-bottom: 16px; + + .island-group-header { + font-size: 14px; + font-weight: 600; + color: #303133; + padding: 8px 12px; + background-color: #e6f7ff; + border-left: 3px solid #409eff; + margin-bottom: 8px; + border-radius: 2px; + animation: slideUpIn 0.4s ease-out forwards; + opacity: 0; + } + } + .component-item { display: flex; flex-direction: row; @@ -1412,6 +1994,36 @@ onMounted(() => { display: flex; gap: 6px; align-items: center; + flex: 1; + } + + .flow-input-group { + display: flex; + gap: 20px; + align-items: center; + flex: 1; + } + + .flow-input-item { + display: flex; + align-items: center; + gap: 8px; + } + + .flow-input-label { + font-size: 14px; + color: #303133; + white-space: nowrap; + + &::before { + content: '*'; + color: #f56c6c; + margin-right: 4px; + } + } + + .flow-input { + width: 200px; } .canvas-status { @@ -1610,9 +2222,100 @@ onMounted(() => { box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05); z-index: 2; } + + // 底部参数预览抽屉 + .param-preview-panel { + margin-top: 12px; + padding: 12px 14px; + background: #ffffff; + border-top: 1px solid #e4e7ed; + box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.06); + border-radius: 8px; + z-index: 1; + user-select: text; + } + + .param-preview-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 10px; + } + + .param-preview-title { + font-size: 14px; + font-weight: 600; + color: #303133; + } + + .param-preview-list { + display: flex; + flex-wrap: wrap; + gap: 10px 18px; + max-height: 120px; + overflow: auto; + padding-right: 6px; + } + + .param-preview-item { + display: inline-flex; + align-items: baseline; + gap: 6px; + padding: 6px 10px; + border-radius: 6px; + background: #f5f7fa; + border: 1px solid #ebeef5; + color: #303133; + max-width: 100%; + } + + .param-preview-name { + color: #606266; + white-space: nowrap; + } + + .param-preview-value { + color: #303133; + word-break: break-all; + } + + .param-preview-empty { + color: #909399; + font-size: 13px; + padding: 8px 0; + } } } +// 抽屉滑入动画(从下往上) +.param-preview-slide-enter-active, +.param-preview-slide-leave-active { + transition: all 0.18s ease; +} +.param-preview-slide-enter-from, +.param-preview-slide-leave-to { + opacity: 0; + transform: translateY(10px); +} + +.flow-readonly { + display: inline-flex; + align-items: center; + height: 32px; + padding: 0 10px; + border: 1px solid #dcdfe6; + border-radius: 4px; + background: #f5f7fa; + color: #303133; + min-width: 180px; + box-sizing: border-box; +} + +.flow-confirm-item { + display: flex; + align-items: flex-end; +} + // 抽屉样式 .drawer-content { padding: 20px;