PLC设备管理页面修改

This commit is contained in:
Lxq
2026-01-23 11:26:14 +08:00
parent 7e3ff083c7
commit 82764f06d1
17 changed files with 314 additions and 236 deletions

View File

@@ -16,6 +16,15 @@ const router = createRouter({
component: () => import('../views/Layout.vue'), component: () => import('../views/Layout.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
children: [ children: [
{
path: '',
redirect: '/home',
},
{
path: '/home',
name: 'home',
component: () => import('../views/Home.vue'),
},
{ {
path: '/user', path: '/user',
name: 'user', name: 'user',
@@ -90,7 +99,7 @@ router.beforeEach((to, from, next) => {
if (to.path === '/login') { if (to.path === '/login') {
// 已登录状态下访问登录页,自动跳转到首页 // 已登录状态下访问登录页,自动跳转到首页
if (token) { if (token) {
next('/') next('/home')
ElMessage.success('已登录,自动跳转') ElMessage.success('已登录,自动跳转')
} else { } else {
// 未登录,正常进入登录页 // 未登录,正常进入登录页
@@ -106,6 +115,12 @@ router.beforeEach((to, from, next) => {
return return
} }
// 如果访问根路径,重定向到首页
if (to.path === '/') {
next('/home')
return
}
// 已登录且访问合法页面,正常放行 // 已登录且访问合法页面,正常放行
next() next()
}) })

View File

@@ -43,6 +43,27 @@ const TokenManager = {
// 应用启动时初始化token // 应用启动时初始化token
TokenManager.initToken() TokenManager.initToken()
// 登录过期提示防重复标志
let isLoginExpiredShown = false
// 处理登录过期(防重复提示)
const handleLoginExpired = () => {
if (!isLoginExpiredShown) {
isLoginExpiredShown = true
// 清除token
TokenManager.removeToken()
// 显示提示消息
ElMessage.error('登录已过期,请重新登录')
// 跳转到登录页
router.replace('/login').finally(() => {
// 延迟重置标志位,确保跳转完成后再允许下次提示
setTimeout(() => {
isLoginExpiredShown = false
}, 1000)
})
}
}
// 请求拦截器 // 请求拦截器
request.interceptors.request.use( request.interceptors.request.use(
(config: InternalAxiosRequestConfig) => { (config: InternalAxiosRequestConfig) => {
@@ -72,10 +93,8 @@ request.interceptors.response.use(
// 处理业务错误码 // 处理业务错误码
if (data.code === 302) { if (data.code === 302) {
// 清除token // 处理登录过期(防重复提示)
TokenManager.removeToken() handleLoginExpired()
// 跳转到登录页
router.replace('/login')
return Promise.reject(data) return Promise.reject(data)
} }
@@ -90,11 +109,8 @@ request.interceptors.response.use(
(error) => { (error) => {
// 处理401未授权错误 // 处理401未授权错误
if (error.response?.status === 401) { if (error.response?.status === 401) {
// 清除token // 处理登录过期(防重复提示)
TokenManager.removeToken() handleLoginExpired()
// 跳转到登录页
ElMessage.error('登录已过期,请重新登录')
router.replace('/login')
return Promise.reject(error) return Promise.reject(error)
} }

View File

@@ -0,0 +1,56 @@
<template>
<div class="home-container">
<div class="welcome-content">
<h1 class="welcome-title">欢迎使用北京融创智能仪器管理系统</h1>
</div>
</div>
</template>
<script setup lang="ts">
// 首页组件,未来将展示数字孪生监控大屏
</script>
<style scoped>
.home-container {
width: 100%;
height: calc(92vh - 25px);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
}
.welcome-content {
text-align: center;
color: #080101;
padding: 40px;
max-width: 100%;
box-sizing: border-box;
}
.welcome-title {
font-size: 48px;
font-weight: 600;
margin: 0;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
letter-spacing: 2px;
line-height: 1.4;
}
.welcome-subtitle {
font-size: 24px;
margin: 0;
opacity: 0.9;
font-weight: 300;
letter-spacing: 1px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.welcome-title {
font-size: 32px;
letter-spacing: 1px;
}
}
</style>

View File

@@ -31,6 +31,10 @@
router router
:collapse="false" :collapse="false"
> >
<el-menu-item index="/home">
<el-icon><HomeFilled /></el-icon>
<span>首页</span>
</el-menu-item>
<el-sub-menu index="system"> <el-sub-menu index="system">
<template #title> <template #title>
<el-icon><Setting /></el-icon> <el-icon><Setting /></el-icon>
@@ -66,17 +70,17 @@
<el-icon><Grid /></el-icon> <el-icon><Grid /></el-icon>
<span>业务管理</span> <span>业务管理</span>
</template> </template>
<el-menu-item index="/island-info"> <el-menu-item index="/plc-devinfo">
<el-icon><Setting /></el-icon> <el-icon><Connection /></el-icon>
<span>功能岛管理</span> <span>PLC设备管理</span>
</el-menu-item> </el-menu-item>
<el-menu-item index="/devinfo"> <el-menu-item index="/devinfo">
<el-icon><Monitor /></el-icon> <el-icon><Monitor /></el-icon>
<span>设备管理</span> <span>设备管理</span>
</el-menu-item> </el-menu-item>
<el-menu-item index="/plc-devinfo"> <el-menu-item index="/island-info">
<el-icon><Connection /></el-icon> <el-icon><Setting /></el-icon>
<span>PLC设备管理</span> <span>功能岛管理</span>
</el-menu-item> </el-menu-item>
</el-sub-menu> </el-sub-menu>
<el-sub-menu index="flow"> <el-sub-menu index="flow">
@@ -112,7 +116,7 @@
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { User, Setting, Avatar, OfficeBuilding, Briefcase, Document, CaretBottom, UserFilled, Grid, Monitor, Connection, List, EditPen, Files, Tickets, Management, FolderOpened, Box, Filter } from '@element-plus/icons-vue' import { User, Setting, Avatar, OfficeBuilding, Briefcase, Document, CaretBottom, UserFilled, Grid, Monitor, Connection, List, EditPen, Files, Tickets, Management, FolderOpened, Box, Filter, HomeFilled } from '@element-plus/icons-vue'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
const router = useRouter() const router = useRouter()

View File

@@ -209,7 +209,7 @@ const handleLogin = async () => {
ElMessage.success('登录成功') ElMessage.success('登录成功')
// 跳转到首页 // 跳转到首页
router.push('/') router.push('/home')
} }
} catch (error: any) { } catch (error: any) {
// 错误提示已在全局响应拦截中处理,这里不重复弹出 // 错误提示已在全局响应拦截中处理,这里不重复弹出

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="dept-page"> <div class="dept-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px"> <el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>系统管理</el-breadcrumb-item> <el-breadcrumb-item>系统管理</el-breadcrumb-item>
<el-breadcrumb-item>部门管理</el-breadcrumb-item> <el-breadcrumb-item>部门管理</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="devinfo-page"> <div class="devinfo-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px"> <el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>业务管理</el-breadcrumb-item> <el-breadcrumb-item>业务管理</el-breadcrumb-item>
<el-breadcrumb-item>设备管理</el-breadcrumb-item> <el-breadcrumb-item>设备管理</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="plc-devinfo-page"> <div class="plc-devinfo-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px"> <el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>业务管理</el-breadcrumb-item> <el-breadcrumb-item>业务管理</el-breadcrumb-item>
<el-breadcrumb-item>PLC设备管理</el-breadcrumb-item> <el-breadcrumb-item>PLC设备管理</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>
@@ -82,13 +83,6 @@
{{ row.company || '暂无' }} {{ row.company || '暂无' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="status" label="状态" width="100" align="center">
<template #default="{ row }">
<el-tag :type="getStatusType(row.status)" size="small">
{{ getStatusText(row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="devDesc" label="描述" min-width="150" show-overflow-tooltip> <el-table-column prop="devDesc" label="描述" min-width="150" show-overflow-tooltip>
<template #default="{ row }"> <template #default="{ row }">
{{ row.devDesc || '暂无' }} {{ row.devDesc || '暂无' }}
@@ -277,7 +271,7 @@ const formData = reactive({
company: '', company: '',
devDesc: '', devDesc: '',
remark: '', remark: '',
status: 0, // 状态自动设置为0 status: 0, // 状态默认为0
}) })
// 表单验证规则 // 表单验证规则
@@ -292,21 +286,6 @@ const getIndex = (index: number) => {
return (pagination.pageNum - 1) * pagination.pageSize + index + 1 return (pagination.pageNum - 1) * pagination.pageSize + index + 1
} }
// 获取状态类型
const getStatusType = (status: number | null | undefined) => {
if (status === 0) return 'success' // 空闲 - 绿色
if (status === 1) return 'warning' // 运行 - 黄色
if (status === 4) return 'danger' // 故障 - 红色
return 'info'
}
// 获取状态文本
const getStatusText = (status: number | null | undefined) => {
if (status === 0) return '空闲'
if (status === 1) return '运行'
if (status === 4) return '故障'
return '未知'
}
// 获取设备列表只查询devModel为PLC的设备 // 获取设备列表只查询devModel为PLC的设备
const getDeviceList = async () => { const getDeviceList = async () => {
@@ -483,7 +462,7 @@ const handleSubmit = async () => {
const submitData: any = { const submitData: any = {
devName: formData.devName, devName: formData.devName,
devModel: 'PLC', // 设备型号固定为PLC devModel: 'PLC', // 设备型号固定为PLC
status: 0, // 状态自动设置为0 status: 0, // 状态默认为0
} }
// 添加其他字段(如果非空) // 添加其他字段(如果非空)

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="flowinfo-page"> <div class="flowinfo-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px"> <el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>流程管理</el-breadcrumb-item> <el-breadcrumb-item>流程管理</el-breadcrumb-item>
<el-breadcrumb-item>标准流程管理</el-breadcrumb-item> <el-breadcrumb-item>标准流程管理</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>

View File

@@ -1,8 +1,9 @@
<template> <template>
<div class="goodsinfo-page"> <div class="goodsinfo-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px"> <el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>流程管理</el-breadcrumb-item> <el-breadcrumb-item>流程管理</el-breadcrumb-item>
<el-breadcrumb-item>样品信息管理</el-breadcrumb-item> <el-breadcrumb-item>样品管理</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>
<!-- 搜索栏 --> <!-- 搜索栏 -->
<el-card class="search-card" shadow="never"> <el-card class="search-card" shadow="never">

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="island-info-page"> <div class="island-info-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px"> <el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>业务管理</el-breadcrumb-item> <el-breadcrumb-item>业务管理</el-breadcrumb-item>
<el-breadcrumb-item>功能岛管理</el-breadcrumb-item> <el-breadcrumb-item>功能岛管理</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>
@@ -92,8 +93,8 @@
<el-pagination <el-pagination
v-model:current-page="pagination.pageNum" v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize" v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]" :page-sizes="[9]"
layout="total, sizes, prev, pager, next, jumper" layout="total, prev, pager, next, jumper"
:total="total" :total="total"
background background
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
@@ -149,7 +150,7 @@
clearable clearable
/> />
</el-form-item> </el-form-item>
<el-form-item label="绑定设备"> <el-form-item v-if="isEdit" label="绑定设备">
<div class="bind-device-wrapper"> <div class="bind-device-wrapper">
<el-button type="primary" @click="handleBindDevice"> <el-button type="primary" @click="handleBindDevice">
<el-icon><Plus /></el-icon> <el-icon><Plus /></el-icon>
@@ -199,6 +200,7 @@
<!-- 绑定设备对话框 --> <!-- 绑定设备对话框 -->
<el-dialog <el-dialog
v-if="isEdit"
v-model="bindDeviceDialogVisible" v-model="bindDeviceDialogVisible"
:title="isEdit ? '绑定/解绑设备' : '绑定设备'" :title="isEdit ? '绑定/解绑设备' : '绑定设备'"
width="800px" width="800px"
@@ -286,6 +288,17 @@ import {
Monitor, Monitor,
Cpu, Cpu,
DataAnalysis, DataAnalysis,
Upload,
Download,
MagicStick,
TakeawayBox,
CollectionTag,
Position,
Dish,
Bowl,
HomeFilled,
Odometer,
Sunrise,
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
import { import {
islandInfoadd, islandInfoadd,
@@ -314,7 +327,7 @@ const queryForm = reactive({
// 分页 // 分页
const pagination = reactive({ const pagination = reactive({
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 9,
}) })
// 抽屉相关 // 抽屉相关
@@ -351,45 +364,45 @@ const formRules = {
// 获取功能岛图标(根据名称自动生成) // 获取功能岛图标(根据名称自动生成)
const getIslandIcon = (name: string) => { const getIslandIcon = (name: string) => {
if (!name) return Box if (!name) return Box
// 根据名称关键词匹配图标
const nameLower = name.toLowerCase() const nameLower = name.toLowerCase()
if (nameLower.includes('加液') || nameLower.includes('ph') || nameLower.includes('涡旋')) {
return Watermelon // 预设功能岛与唯一图标的映射(避免重复)
} else if (nameLower.includes('水浴') || nameLower.includes('恒温')) { const iconRules = [
return Sunny { keywords: ['涡旋'], icon: MagicStick },
} else if (nameLower.includes('震荡')) { { keywords: ['加液', '加样'], icon: Watermelon },
return Connection { keywords: ['进样'], icon: Upload },
} else if (nameLower.includes('超声')) { { keywords: ['分液'], icon: TakeawayBox },
return Histogram { keywords: ['浓缩'], icon: CollectionTag },
} else if (nameLower.includes('离心')) { { keywords: ['移上清'], icon: Position },
return RefreshRight { keywords: ['取液'], icon: Dish },
} else if (nameLower.includes('移液')) { { keywords: ['金属浴', '金属'], icon: Bowl },
return Aim { keywords: ['干燥', '硫酸钠'], icon: Sunrise },
} else if (nameLower.includes('萃取')) { { keywords: ['温室', '静置'], icon: HomeFilled },
return Goblet { keywords: ['ph', '酸碱'], icon: Odometer },
} else if (nameLower.includes('氮吹')) { { keywords: ['出样'], icon: Download },
return WindPower { keywords: ['水浴', '恒温'], icon: Sunny },
} else if (nameLower.includes('过膜') || nameLower.includes('过滤')) { { keywords: ['震荡'], icon: Connection },
return Filter { keywords: ['超声'], icon: Histogram },
} else if (nameLower.includes('人工')) { { keywords: ['离心'], icon: RefreshRight },
return User { keywords: ['移液'], icon: Aim },
} else if (nameLower.includes('系统') || nameLower.includes('管理')) { { keywords: ['萃取'], icon: Goblet },
return Setting { keywords: ['氮吹'], icon: WindPower },
} else if (nameLower.includes('工具')) { { keywords: ['过膜', '过滤'], icon: Filter },
return Tools { keywords: ['人工'], icon: User },
} else if (nameLower.includes('数据') || nameLower.includes('分析')) { { keywords: ['系统', '管理'], icon: Setting },
return DataAnalysis { keywords: ['工具'], icon: Tools },
} else if (nameLower.includes('监控') || nameLower.includes('显示')) { { keywords: ['数据', '分析'], icon: DataAnalysis },
return Monitor { keywords: ['监控', '显示'], icon: Monitor },
} else if (nameLower.includes('处理') || nameLower.includes('计算')) { { keywords: ['处理', '计算'], icon: Cpu },
return Cpu { keywords: ['网格', '布局'], icon: Grid },
} else if (nameLower.includes('网格') || nameLower.includes('布局')) { ]
return Grid
} const matched = iconRules.find(rule =>
rule.keywords.some(keyword => nameLower.includes(keyword.toLowerCase()))
// 默认图标 )
return Box
return matched ? matched.icon : Box
} }
// 处理名称输入 // 处理名称输入
@@ -632,7 +645,6 @@ const handleSubmit = async () => {
} }
let res: any let res: any
let newIslandId: any = null
if (isEdit.value && formData.id) { if (isEdit.value && formData.id) {
// 编辑 - 需要传入ID其他字段可选(非空则更新) // 编辑 - 需要传入ID其他字段可选(非空则更新)
@@ -640,42 +652,12 @@ const handleSubmit = async () => {
id: formData.id, id: formData.id,
...submitData, ...submitData,
}) })
newIslandId = formData.id
} else { } else {
// 新增 // 新增
res = await islandInfoadd(submitData) res = await islandInfoadd(submitData)
// 获取新增后的ID
if (res.code === '0' || res.code === 0) {
newIslandId = res.data?.id || res.id
}
} }
if (res.code === '0' || res.code === 0) { if (res.code === '0' || res.code === 0) {
// 如果是新增模式且有绑定的设备需要更新设备的islandId
if (!isEdit.value && newIslandId && boundDevices.value.length > 0) {
try {
const bindPromises = boundDevices.value.map((device: any) => {
return devInfoupd({
id: device.id,
devName: device.devName, // 必填字段
islandId: newIslandId,
})
})
const bindResults = await Promise.all(bindPromises)
// 检查是否有失败的请求
const failedResults = bindResults.filter((res: any) => {
return res && (res.code !== '0' && res.code !== 0 && !res.success)
})
if (failedResults.length > 0) {
console.warn('部分设备绑定失败:', failedResults)
// 不阻止功能岛创建成功,只提示警告
}
} catch (error) {
console.error('绑定设备失败:', error)
// 不阻止功能岛创建成功,只记录错误
}
}
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功') ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
drawerVisible.value = false drawerVisible.value = false
getIslandList() getIslandList()
@@ -694,43 +676,31 @@ const handleSubmit = async () => {
// 打开绑定设备对话框 // 打开绑定设备对话框
const handleBindDevice = async () => { const handleBindDevice = async () => {
if (!isEdit.value || !formData.id) {
// 新增功能岛不允许绑定设备(仅新增功能岛信息)
return
}
try { try {
deviceLoading.value = true deviceLoading.value = true
const res: any = await devselect({}) const res: any = await devselect({})
const allDevices = res?.data ?? res ?? [] const allDevices = res?.data ?? res ?? []
if (Array.isArray(allDevices)) { if (Array.isArray(allDevices)) {
if (isEdit.value && formData.id) { // 编辑模式:显示未绑定的设备 + 已绑定该功能岛的设备排除设备型号为PLC的设备
// 编辑模式:显示未绑定的设备 + 已绑定该功能岛的设备排除设备型号为PLC的设备 deviceListForBind.value = allDevices.filter((device: any) => {
deviceListForBind.value = allDevices.filter((device: any) => { // 排除设备型号为PLC的设备
// 排除设备型号为PLC的设备 if (device.devModel && String(device.devModel).toUpperCase() === 'PLC') {
if (device.devModel && String(device.devModel).toUpperCase() === 'PLC') { return false
return false }
} // 筛选条件islandId为0或空的设备或者已绑定该功能岛的设备
// 筛选条件islandId为0或空的设备或者已绑定该功能岛的设备 return (
return ( !device.islandId ||
!device.islandId || device.islandId === formData.id ||
device.islandId === formData.id || device.islandId === String(formData.id) ||
device.islandId === String(formData.id) || device.islandId === 0 ||
device.islandId === 0 || device.islandId === '0'
device.islandId === '0' )
) })
})
} else {
// 新增模式只显示未绑定的设备排除设备型号为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 === 0 ||
device.islandId === '0'
)
})
}
} else { } else {
deviceListForBind.value = [] deviceListForBind.value = []
} }
@@ -769,75 +739,70 @@ const handleSaveDeviceBind = async () => {
// 获取当前选中的设备ID列表 // 获取当前选中的设备ID列表
const selectedDeviceIds = selectedDevices.value.map((d: any) => d.id) const selectedDeviceIds = selectedDevices.value.map((d: any) => d.id)
if (isEdit.value && formData.id) { if (!isEdit.value || !formData.id) return
// 编辑模式:立即保存绑定关系
// 获取之前已绑定的设备ID列表 // 编辑模式:立即保存绑定关系
const previousBoundDeviceIds = boundDevices.value.map((d: any) => d.id) // 获取之前已绑定的设备ID列表
const previousBoundDeviceIds = boundDevices.value.map((d: any) => d.id)
// 需要绑定的设备(新增的)
const devicesToBind = selectedDeviceIds.filter((id: any) => !previousBoundDeviceIds.includes(id)) // 需要绑定的设备(新增的)
const devicesToBind = selectedDeviceIds.filter((id: any) => !previousBoundDeviceIds.includes(id))
// 需要解绑的设备(取消选中的)
const devicesToUnbind = previousBoundDeviceIds.filter((id: any) => !selectedDeviceIds.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) const bindPromises = devicesToBind.map((deviceId: any) => {
if (device) { const device = deviceListForBind.value.find((d: any) => d.id === deviceId)
// 包含设备名称等必填字段 if (device) {
return devInfoupd({ // 包含设备名称等必填字段
id: deviceId, return devInfoupd({
devName: device.devName, // 必填字段 id: deviceId,
islandId: formData.id, 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 || '部分设备绑定/解绑失败')
}
} }
return Promise.resolve()
// 更新已绑定设备列表 })
await loadBoundDevices(formData.id)
// 执行解绑操作
ElMessage.success('保存成功') const unbindPromises = devicesToUnbind.map((deviceId: any) => {
} else { const device =
// 新增模式:只更新本地列表,不调用接口(等创建功能岛后再绑定) deviceListForBind.value.find((d: any) => d.id === deviceId) ||
boundDevices.value = deviceListForBind.value.filter((device: any) => boundDevices.value.find((d: any) => d.id === deviceId)
selectedDeviceIds.includes(device.id) if (device) {
) // 包含设备名称等必填字段
ElMessage.success('已选择设备,将在创建功能岛后自动绑定') 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 bindDeviceDialogVisible.value = false
} catch (error: any) { } catch (error: any) {

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="manage-log-page"> <div class="manage-log-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px"> <el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>系统管理</el-breadcrumb-item> <el-breadcrumb-item>系统管理</el-breadcrumb-item>
<el-breadcrumb-item>操作日志管理</el-breadcrumb-item> <el-breadcrumb-item>操作日志管理</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="position-page"> <div class="position-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px"> <el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>系统管理</el-breadcrumb-item> <el-breadcrumb-item>系统管理</el-breadcrumb-item>
<el-breadcrumb-item>职位管理</el-breadcrumb-item> <el-breadcrumb-item>职位管理</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="role-page"> <div class="role-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px"> <el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>系统管理</el-breadcrumb-item> <el-breadcrumb-item>系统管理</el-breadcrumb-item>
<el-breadcrumb-item>角色管理</el-breadcrumb-item> <el-breadcrumb-item>角色管理</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="step-info-wrapper"> <div class="step-info-wrapper">
<el-breadcrumb separator="/" style="margin-bottom: 16px"> <el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>流程管理</el-breadcrumb-item> <el-breadcrumb-item>流程管理</el-breadcrumb-item>
<el-breadcrumb-item>流程创建</el-breadcrumb-item> <el-breadcrumb-item>流程创建</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>
@@ -221,7 +222,24 @@ import {
WindPower, WindPower,
Filter, Filter,
User, User,
Setting,
Tools,
Box, Box,
Grid,
Monitor,
Cpu,
DataAnalysis,
Upload,
Download,
MagicStick,
TakeawayBox,
CollectionTag,
Position,
Dish,
Bowl,
HomeFilled,
Odometer,
Sunrise,
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
import { islandInfolist } from '@/api/tb/islandinfo' import { islandInfolist } from '@/api/tb/islandinfo'
import { devselect } from '@/api/tb/devinfo' import { devselect } from '@/api/tb/devinfo'
@@ -416,32 +434,48 @@ const getIslandIconCached = (name: string) => {
return islandIconCache.get(key)! return islandIconCache.get(key)!
} }
// 获取功能岛图标 // 获取功能岛图标(根据名称自动生成)
const getIslandIcon = (name: string) => { const getIslandIcon = (name: string) => {
if (!name) return Box if (!name) return Box
const nameLower = name.toLowerCase() const nameLower = name.toLowerCase()
if (nameLower.includes('加液') || nameLower.includes('ph') || nameLower.includes('涡旋')) {
return Watermelon // 预设功能岛与唯一图标的映射(避免重复)
} else if (nameLower.includes('水浴') || nameLower.includes('恒温')) { const iconRules = [
return Sunny { keywords: ['涡旋'], icon: MagicStick },
} else if (nameLower.includes('震荡')) { { keywords: ['加液', '加样'], icon: Watermelon },
return Connection { keywords: ['进样'], icon: Upload },
} else if (nameLower.includes('超声')) { { keywords: ['分液'], icon: TakeawayBox },
return Histogram { keywords: ['浓缩'], icon: CollectionTag },
} else if (nameLower.includes('离心')) { { keywords: ['移上清'], icon: Position },
return RefreshRight { keywords: ['取液'], icon: Dish },
} else if (nameLower.includes('移液')) { { keywords: ['金属浴', '金属'], icon: Bowl },
return Aim { keywords: ['干燥', '硫酸钠'], icon: Sunrise },
} else if (nameLower.includes('萃取')) { { keywords: ['温室', '静置'], icon: HomeFilled },
return Goblet { keywords: ['ph', '酸碱'], icon: Odometer },
} else if (nameLower.includes('氮吹')) { { keywords: ['出样'], icon: Download },
return WindPower { keywords: ['水浴', '恒温'], icon: Sunny },
} else if (nameLower.includes('过膜') || nameLower.includes('过滤')) { { keywords: ['震荡'], icon: Connection },
return Filter { keywords: ['超声'], icon: Histogram },
} else if (nameLower.includes('人工')) { { keywords: ['离心'], icon: RefreshRight },
return User { keywords: ['移液'], icon: Aim },
} { keywords: ['萃取'], icon: Goblet },
return Box { keywords: ['氮吹'], icon: WindPower },
{ keywords: ['过膜', '过滤'], icon: Filter },
{ keywords: ['人工'], icon: User },
{ keywords: ['系统', '管理'], icon: Setting },
{ keywords: ['工具'], icon: Tools },
{ keywords: ['数据', '分析'], icon: DataAnalysis },
{ keywords: ['监控', '显示'], icon: Monitor },
{ keywords: ['处理', '计算'], icon: Cpu },
{ keywords: ['网格', '布局'], icon: Grid },
]
const matched = iconRules.find(rule =>
rule.keywords.some(keyword => nameLower.includes(keyword.toLowerCase()))
)
return matched ? matched.icon : Box
} }
// 获取功能岛描述关键词 // 获取功能岛描述关键词

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="user-role-page"> <div class="user-role-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px"> <el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>系统管理</el-breadcrumb-item> <el-breadcrumb-item>系统管理</el-breadcrumb-item>
<el-breadcrumb-item>用户角色管理</el-breadcrumb-item> <el-breadcrumb-item>用户角色管理</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>

View File

@@ -1,6 +1,7 @@
<template> <template>
<div class="user-page"> <div class="user-page">
<el-breadcrumb separator="/" style="margin-bottom: 16px"> <el-breadcrumb separator="/" style="margin-bottom: 16px">
<el-breadcrumb-item><router-link to="/home">首页</router-link></el-breadcrumb-item>
<el-breadcrumb-item>系统管理</el-breadcrumb-item> <el-breadcrumb-item>系统管理</el-breadcrumb-item>
<el-breadcrumb-item>用户管理</el-breadcrumb-item> <el-breadcrumb-item>用户管理</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>