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

View File

@@ -43,6 +43,27 @@ const TokenManager = {
// 应用启动时初始化token
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(
(config: InternalAxiosRequestConfig) => {
@@ -72,10 +93,8 @@ request.interceptors.response.use(
// 处理业务错误码
if (data.code === 302) {
// 清除token
TokenManager.removeToken()
// 跳转到登录页
router.replace('/login')
// 处理登录过期(防重复提示)
handleLoginExpired()
return Promise.reject(data)
}
@@ -90,11 +109,8 @@ request.interceptors.response.use(
(error) => {
// 处理401未授权错误
if (error.response?.status === 401) {
// 清除token
TokenManager.removeToken()
// 跳转到登录页
ElMessage.error('登录已过期,请重新登录')
router.replace('/login')
// 处理登录过期(防重复提示)
handleLoginExpired()
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
:collapse="false"
>
<el-menu-item index="/home">
<el-icon><HomeFilled /></el-icon>
<span>首页</span>
</el-menu-item>
<el-sub-menu index="system">
<template #title>
<el-icon><Setting /></el-icon>
@@ -66,17 +70,17 @@
<el-icon><Grid /></el-icon>
<span>业务管理</span>
</template>
<el-menu-item index="/island-info">
<el-icon><Setting /></el-icon>
<span>功能岛管理</span>
<el-menu-item index="/plc-devinfo">
<el-icon><Connection /></el-icon>
<span>PLC设备管理</span>
</el-menu-item>
<el-menu-item index="/devinfo">
<el-icon><Monitor /></el-icon>
<span>设备管理</span>
</el-menu-item>
<el-menu-item index="/plc-devinfo">
<el-icon><Connection /></el-icon>
<span>PLC设备管理</span>
<el-menu-item index="/island-info">
<el-icon><Setting /></el-icon>
<span>功能岛管理</span>
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="flow">
@@ -112,7 +116,7 @@
import { ref, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
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'
const router = useRouter()

View File

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

View File

@@ -1,6 +1,7 @@
<template>
<div class="dept-page">
<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>

View File

@@ -1,6 +1,7 @@
<template>
<div class="devinfo-page">
<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>

View File

@@ -1,6 +1,7 @@
<template>
<div class="plc-devinfo-page">
<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>PLC设备管理</el-breadcrumb-item>
</el-breadcrumb>
@@ -82,13 +83,6 @@
{{ row.company || '暂无' }}
</template>
</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>
<template #default="{ row }">
{{ row.devDesc || '暂无' }}
@@ -277,7 +271,7 @@ const formData = reactive({
company: '',
devDesc: '',
remark: '',
status: 0, // 状态自动设置为0
status: 0, // 状态默认为0
})
// 表单验证规则
@@ -292,21 +286,6 @@ const getIndex = (index: number) => {
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的设备
const getDeviceList = async () => {
@@ -483,7 +462,7 @@ const handleSubmit = async () => {
const submitData: any = {
devName: formData.devName,
devModel: 'PLC', // 设备型号固定为PLC
status: 0, // 状态自动设置为0
status: 0, // 状态默认为0
}
// 添加其他字段(如果非空)

View File

@@ -1,6 +1,7 @@
<template>
<div class="flowinfo-page">
<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>

View File

@@ -1,8 +1,9 @@
<template>
<div class="goodsinfo-page">
<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>
<!-- 搜索栏 -->
<el-card class="search-card" shadow="never">

View File

@@ -1,6 +1,7 @@
<template>
<div class="island-info-page">
<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>
@@ -92,8 +93,8 @@
<el-pagination
v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[9]"
layout="total, prev, pager, next, jumper"
:total="total"
background
@current-change="handleCurrentChange"
@@ -149,7 +150,7 @@
clearable
/>
</el-form-item>
<el-form-item label="绑定设备">
<el-form-item v-if="isEdit" label="绑定设备">
<div class="bind-device-wrapper">
<el-button type="primary" @click="handleBindDevice">
<el-icon><Plus /></el-icon>
@@ -199,6 +200,7 @@
<!-- 绑定设备对话框 -->
<el-dialog
v-if="isEdit"
v-model="bindDeviceDialogVisible"
:title="isEdit ? '绑定/解绑设备' : '绑定设备'"
width="800px"
@@ -286,6 +288,17 @@ import {
Monitor,
Cpu,
DataAnalysis,
Upload,
Download,
MagicStick,
TakeawayBox,
CollectionTag,
Position,
Dish,
Bowl,
HomeFilled,
Odometer,
Sunrise,
} from '@element-plus/icons-vue'
import {
islandInfoadd,
@@ -314,7 +327,7 @@ const queryForm = reactive({
// 分页
const pagination = reactive({
pageNum: 1,
pageSize: 10,
pageSize: 9,
})
// 抽屉相关
@@ -351,45 +364,45 @@ const formRules = {
// 获取功能岛图标(根据名称自动生成)
const getIslandIcon = (name: string) => {
if (!name) return Box
// 根据名称关键词匹配图标
const nameLower = name.toLowerCase()
if (nameLower.includes('加液') || nameLower.includes('ph') || nameLower.includes('涡旋')) {
return Watermelon
} else if (nameLower.includes('水浴') || nameLower.includes('恒温')) {
return Sunny
} else if (nameLower.includes('震荡')) {
return Connection
} else if (nameLower.includes('超声')) {
return Histogram
} else if (nameLower.includes('离心')) {
return RefreshRight
} else if (nameLower.includes('移液')) {
return Aim
} else if (nameLower.includes('萃取')) {
return Goblet
} else if (nameLower.includes('氮吹')) {
return WindPower
} else if (nameLower.includes('过膜') || nameLower.includes('过滤')) {
return Filter
} else if (nameLower.includes('人工')) {
return User
} else if (nameLower.includes('系统') || nameLower.includes('管理')) {
return Setting
} else if (nameLower.includes('工具')) {
return Tools
} else if (nameLower.includes('数据') || nameLower.includes('分析')) {
return DataAnalysis
} else if (nameLower.includes('监控') || nameLower.includes('显示')) {
return Monitor
} else if (nameLower.includes('处理') || nameLower.includes('计算')) {
return Cpu
} else if (nameLower.includes('网格') || nameLower.includes('布局')) {
return Grid
}
// 默认图标
return Box
// 预设功能岛与唯一图标的映射(避免重复)
const iconRules = [
{ keywords: ['涡旋'], icon: MagicStick },
{ keywords: ['加液', '加样'], icon: Watermelon },
{ keywords: ['进样'], icon: Upload },
{ keywords: ['分液'], icon: TakeawayBox },
{ keywords: ['浓缩'], icon: CollectionTag },
{ keywords: ['移上清'], icon: Position },
{ keywords: ['取液'], icon: Dish },
{ keywords: ['金属浴', '金属'], icon: Bowl },
{ keywords: ['干燥', '硫酸钠'], icon: Sunrise },
{ keywords: ['温室', '静置'], icon: HomeFilled },
{ keywords: ['ph', '酸碱'], icon: Odometer },
{ keywords: ['出样'], icon: Download },
{ keywords: ['水浴', '恒温'], icon: Sunny },
{ keywords: ['震荡'], icon: Connection },
{ keywords: ['超声'], icon: Histogram },
{ keywords: ['离心'], icon: RefreshRight },
{ keywords: ['移液'], icon: Aim },
{ keywords: ['萃取'], icon: Goblet },
{ 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
}
// 处理名称输入
@@ -632,7 +645,6 @@ const handleSubmit = async () => {
}
let res: any
let newIslandId: any = null
if (isEdit.value && formData.id) {
// 编辑 - 需要传入ID其他字段可选(非空则更新)
@@ -640,42 +652,12 @@ const handleSubmit = async () => {
id: formData.id,
...submitData,
})
newIslandId = formData.id
} else {
// 新增
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) {
// 如果是新增模式且有绑定的设备需要更新设备的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 ? '编辑成功' : '新增成功')
drawerVisible.value = false
getIslandList()
@@ -694,43 +676,31 @@ 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)) {
if (isEdit.value && formData.id) {
// 编辑模式:显示未绑定的设备 + 已绑定该功能岛的设备排除设备型号为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 {
// 新增模式只显示未绑定的设备排除设备型号为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'
)
})
}
// 编辑模式:显示未绑定的设备 + 已绑定该功能岛的设备排除设备型号为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 = []
}
@@ -769,75 +739,70 @@ const handleSaveDeviceBind = async () => {
// 获取当前选中的设备ID列表
const selectedDeviceIds = selectedDevices.value.map((d: any) => d.id)
if (isEdit.value && formData.id) {
// 编辑模式:立即保存绑定关系
// 获取之前已绑定的设备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 (!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,
})
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('保存成功')
} else {
// 新增模式:只更新本地列表,不调用接口(等创建功能岛后再绑定)
boundDevices.value = deviceListForBind.value.filter((device: any) =>
selectedDeviceIds.includes(device.id)
)
ElMessage.success('已选择设备,将在创建功能岛后自动绑定')
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) {

View File

@@ -1,6 +1,7 @@
<template>
<div class="manage-log-page">
<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>

View File

@@ -1,6 +1,7 @@
<template>
<div class="position-page">
<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>

View File

@@ -1,6 +1,7 @@
<template>
<div class="role-page">
<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>

View File

@@ -1,6 +1,7 @@
<template>
<div class="step-info-wrapper">
<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>
@@ -221,7 +222,24 @@ import {
WindPower,
Filter,
User,
Setting,
Tools,
Box,
Grid,
Monitor,
Cpu,
DataAnalysis,
Upload,
Download,
MagicStick,
TakeawayBox,
CollectionTag,
Position,
Dish,
Bowl,
HomeFilled,
Odometer,
Sunrise,
} from '@element-plus/icons-vue'
import { islandInfolist } from '@/api/tb/islandinfo'
import { devselect } from '@/api/tb/devinfo'
@@ -416,32 +434,48 @@ const getIslandIconCached = (name: string) => {
return islandIconCache.get(key)!
}
// 获取功能岛图标
// 获取功能岛图标(根据名称自动生成)
const getIslandIcon = (name: string) => {
if (!name) return Box
const nameLower = name.toLowerCase()
if (nameLower.includes('加液') || nameLower.includes('ph') || nameLower.includes('涡旋')) {
return Watermelon
} else if (nameLower.includes('水浴') || nameLower.includes('恒温')) {
return Sunny
} else if (nameLower.includes('震荡')) {
return Connection
} else if (nameLower.includes('超声')) {
return Histogram
} else if (nameLower.includes('离心')) {
return RefreshRight
} else if (nameLower.includes('移液')) {
return Aim
} else if (nameLower.includes('萃取')) {
return Goblet
} else if (nameLower.includes('氮吹')) {
return WindPower
} else if (nameLower.includes('过膜') || nameLower.includes('过滤')) {
return Filter
} else if (nameLower.includes('人工')) {
return User
}
return Box
// 预设功能岛与唯一图标的映射(避免重复)
const iconRules = [
{ keywords: ['涡旋'], icon: MagicStick },
{ keywords: ['加液', '加样'], icon: Watermelon },
{ keywords: ['进样'], icon: Upload },
{ keywords: ['分液'], icon: TakeawayBox },
{ keywords: ['浓缩'], icon: CollectionTag },
{ keywords: ['移上清'], icon: Position },
{ keywords: ['取液'], icon: Dish },
{ keywords: ['金属浴', '金属'], icon: Bowl },
{ keywords: ['干燥', '硫酸钠'], icon: Sunrise },
{ keywords: ['温室', '静置'], icon: HomeFilled },
{ keywords: ['ph', '酸碱'], icon: Odometer },
{ keywords: ['出样'], icon: Download },
{ keywords: ['水浴', '恒温'], icon: Sunny },
{ keywords: ['震荡'], icon: Connection },
{ keywords: ['超声'], icon: Histogram },
{ keywords: ['离心'], icon: RefreshRight },
{ keywords: ['移液'], icon: Aim },
{ keywords: ['萃取'], icon: Goblet },
{ 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>
<div class="user-role-page">
<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>

View File

@@ -1,6 +1,7 @@
<template>
<div class="user-page">
<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>