PLC设备管理
This commit is contained in:
1898
rc_autoplc_front/package-lock.json
generated
1898
rc_autoplc_front/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -14,11 +14,15 @@
|
||||
"type-check": "vue-tsc --build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||
"axios": "^1.13.2",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.12.0",
|
||||
"html2canvas": "^1.4.1",
|
||||
"pinia": "^3.0.4",
|
||||
"pinia-plugin-persistedstate": "^4.7.1",
|
||||
"vue": "^3.5.25",
|
||||
"vue-echarts": "^8.0.1",
|
||||
"vue-router": "^4.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -27,6 +31,7 @@
|
||||
"@vitejs/plugin-vue": "^6.0.2",
|
||||
"@vue/tsconfig": "^0.8.1",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"sass-embedded": "^1.97.3",
|
||||
"typescript": "~5.9.0",
|
||||
"vite": "^7.2.4",
|
||||
"vite-plugin-vue-devtools": "^8.0.5",
|
||||
|
||||
108
rc_autoplc_front/src/api/tb/plcdevicecontrol.ts
Normal file
108
rc_autoplc_front/src/api/tb/plcdevicecontrol.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 连接PLC设备
|
||||
export function plcConnect(data: any) {
|
||||
return request({
|
||||
url: '/plc/connect',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 调用PLC设备执行动作
|
||||
export function plcDoAction(data: any) {
|
||||
return request({
|
||||
url: '/plc/doAction',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取全部PLC连接对象
|
||||
export function plcGetAll() {
|
||||
return request({
|
||||
url: '/plc/getAll',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// PLC设备获取模式值
|
||||
export function plcGetModel(ipAddr: string) {
|
||||
return request({
|
||||
url: '/plc/getModel',
|
||||
method: 'get',
|
||||
params: { ipAddr }
|
||||
})
|
||||
}
|
||||
|
||||
// 根据IP地址查询PLC连接对象
|
||||
export function plcGetPlcByIp(ipAddr: string) {
|
||||
return request({
|
||||
url: '/plc/getPlcByIp',
|
||||
method: 'get',
|
||||
params: { ipAddr }
|
||||
})
|
||||
}
|
||||
|
||||
// 暂停运行PLC
|
||||
export function plcPause(ipAddr: string) {
|
||||
return request({
|
||||
url: '/plc/pause',
|
||||
method: 'post',
|
||||
params: { ipAddr }
|
||||
})
|
||||
}
|
||||
|
||||
// PLC设备获取状态值
|
||||
export function plcPlcStatus(ipAddr: string) {
|
||||
return request({
|
||||
url: '/plc/plcStatus',
|
||||
method: 'get',
|
||||
params: { ipAddr }
|
||||
})
|
||||
}
|
||||
|
||||
// PLC设备复位故障
|
||||
export function plcResetError(ipAddr: string) {
|
||||
return request({
|
||||
url: '/plc/resetError',
|
||||
method: 'post',
|
||||
params: { ipAddr }
|
||||
})
|
||||
}
|
||||
|
||||
// 运行PLC
|
||||
export function plcRun(ipAddr: string) {
|
||||
return request({
|
||||
url: '/plc/run',
|
||||
method: 'post',
|
||||
params: { ipAddr }
|
||||
})
|
||||
}
|
||||
|
||||
// 为样品执行国标
|
||||
export function plcRunFlow(data: any) {
|
||||
return request({
|
||||
url: '/plc/runFlow',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// PLC设备设置模式
|
||||
export function plcSetModel(data: any) {
|
||||
return request({
|
||||
url: '/plc/setModel',
|
||||
method: 'post',
|
||||
params: data
|
||||
})
|
||||
}
|
||||
|
||||
// 停止PLC
|
||||
export function plcStop(ipAddr: string) {
|
||||
return request({
|
||||
url: '/plc/stop',
|
||||
method: 'post',
|
||||
params: { ipAddr }
|
||||
})
|
||||
}
|
||||
@@ -85,6 +85,11 @@ const router = createRouter({
|
||||
name: 'step-info',
|
||||
component: () => import('../views/stepinfo/index.vue'),
|
||||
},
|
||||
{
|
||||
path: '/plc-device-control',
|
||||
name: 'plc-device-control',
|
||||
component: () => import('../views/plcdevicecontrol/index.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// import router from './index' // 导入你的路由实例(与index.ts对应)
|
||||
// import { ElMessage } from 'element-plus'
|
||||
// import { useAuthStore } from '@/stores/auth' // 你的Pinia auth store路径
|
||||
|
||||
// // 路由前置守卫:每次路由跳转前执行
|
||||
// router.beforeEach((to, from, next) => {
|
||||
// const authStore = useAuthStore() // 获取auth store实例
|
||||
// const token = authStore.getToken() // 调用你的getToken()方法获取token
|
||||
|
||||
// // 1. 如果访问登录页
|
||||
// if (to.path === '/login') {
|
||||
// // 已登录状态下访问登录页,自动跳转到首页/dashboard
|
||||
// if (token) {
|
||||
// next('/dashboard')
|
||||
// ElMessage.success('已登录,自动跳转')
|
||||
// } else {
|
||||
// // 未登录,正常进入登录页
|
||||
// next()
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
// // 2. 访问非登录页,但未登录(无token)
|
||||
// if (!token) {
|
||||
// ElMessage.warning('请先登录')
|
||||
// next('/login') // 强制跳转到登录页
|
||||
// return
|
||||
// }
|
||||
|
||||
// // 3. 访问根路径,自动跳转到dashboard
|
||||
// if (to.path === '/') {
|
||||
// next('/dashboard')
|
||||
// return
|
||||
// }
|
||||
|
||||
// // 4. 已登录且访问合法页面,正常放行
|
||||
// next()
|
||||
// })
|
||||
@@ -100,7 +100,25 @@ request.interceptors.response.use(
|
||||
|
||||
// 其他错误码
|
||||
if (data.code !== 0 && data.code !== undefined) {
|
||||
ElMessage.error(data.message || data.msg || '请求失败')
|
||||
// 检查是否是状态刷新相关的请求,如果是则静默处理某些错误
|
||||
const url = response.config?.url || ''
|
||||
const isStatusRefreshRequest = url.includes('/plc/plcStatus') || url.includes('/plc/getModel')
|
||||
const errorMsg = data.message || data.msg || ''
|
||||
|
||||
// 如果是状态刷新请求,且错误消息包含特定关键词,则静默处理
|
||||
if (isStatusRefreshRequest && (
|
||||
errorMsg.includes('停止失败') ||
|
||||
errorMsg.includes('未连接') ||
|
||||
errorMsg.includes('连接失败') ||
|
||||
errorMsg.includes('获取状态失败') ||
|
||||
errorMsg.includes('获取模式失败')
|
||||
)) {
|
||||
// 静默处理,不弹出错误提示
|
||||
console.debug('状态刷新请求错误(已静默处理):', errorMsg)
|
||||
return Promise.reject(data)
|
||||
}
|
||||
|
||||
ElMessage.error(errorMsg || '请求失败')
|
||||
return Promise.reject(data)
|
||||
}
|
||||
|
||||
@@ -114,12 +132,26 @@ request.interceptors.response.use(
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
// 检查是否是状态刷新相关的请求
|
||||
const url = error.config?.url || ''
|
||||
const isStatusRefreshRequest = url.includes('/plc/plcStatus') || url.includes('/plc/getModel')
|
||||
const errorMsg = error.response?.data?.message || error.response?.data?.msg || '请求失败'
|
||||
|
||||
// 如果是状态刷新请求,且错误消息包含特定关键词,则静默处理
|
||||
if (isStatusRefreshRequest && (
|
||||
errorMsg.includes('停止失败') ||
|
||||
errorMsg.includes('未连接') ||
|
||||
errorMsg.includes('连接失败') ||
|
||||
errorMsg.includes('获取状态失败') ||
|
||||
errorMsg.includes('获取模式失败')
|
||||
)) {
|
||||
// 静默处理,不弹出错误提示
|
||||
console.debug('状态刷新请求错误(已静默处理):', errorMsg)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
// 网络错误或其他错误
|
||||
ElMessage.error(
|
||||
error.response?.data?.message ||
|
||||
error.response?.data?.msg ||
|
||||
'请求失败'
|
||||
)
|
||||
ElMessage.error(errorMsg)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -88,6 +88,10 @@
|
||||
<el-icon><List /></el-icon>
|
||||
<span>流程管理</span>
|
||||
</template>
|
||||
<el-menu-item index="/plc-device-control">
|
||||
<el-icon><Connection /></el-icon>
|
||||
<span>PLC设备控制</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/goods-info">
|
||||
<el-icon><Filter /></el-icon>
|
||||
<span>样品管理</span>
|
||||
@@ -172,7 +176,7 @@ const handleCommand = (command: string) => {
|
||||
|
||||
<style scoped>
|
||||
.layout-container {
|
||||
height: 100vh;
|
||||
height: 98vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -550,59 +550,123 @@ const getIslandName = (islandId: number | null | undefined) => {
|
||||
return islandMap.value[islandId] || `功能岛${islandId}`
|
||||
}
|
||||
|
||||
// 获取设备列表
|
||||
// 获取设备列表(只查询非PLC设备,每页显示10条)
|
||||
const getDeviceList = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const params: any = {
|
||||
|
||||
// 构建查询参数,排除PLC设备
|
||||
const baseParams: any = {
|
||||
pageNum: pagination.pageNum,
|
||||
pageSize: pagination.pageSize,
|
||||
}
|
||||
|
||||
// 设备名称模糊查询
|
||||
if (queryForm.devName) {
|
||||
params.devName = queryForm.devName
|
||||
baseParams.devName = queryForm.devName
|
||||
}
|
||||
// 设备状态精确查询
|
||||
if (queryForm.status !== undefined && queryForm.status !== null) {
|
||||
params.status = queryForm.status
|
||||
baseParams.status = queryForm.status
|
||||
}
|
||||
// 所属功能岛精确查询
|
||||
if (queryForm.islandId !== undefined && queryForm.islandId !== null) {
|
||||
params.islandId = queryForm.islandId
|
||||
baseParams.islandId = queryForm.islandId
|
||||
}
|
||||
// 排除 devModel 为 PLC 的设备
|
||||
params.devModelNot = 'PLC'
|
||||
|
||||
const res: any = await devInfolist(params)
|
||||
// 尝试使用 devModelNot 参数排除PLC设备
|
||||
baseParams.devModelNot = 'PLC'
|
||||
|
||||
const data = res?.data ?? res ?? {}
|
||||
// 收集所有非PLC设备,直到凑够pageSize条
|
||||
let allFilteredRecords: any[] = []
|
||||
let currentPage = pagination.pageNum
|
||||
let backendTotal = 0
|
||||
let backendSupportsDevModelNot = false
|
||||
|
||||
// 兼容多种返回格式
|
||||
const recordsFromData =
|
||||
data.records ||
|
||||
data.list ||
|
||||
data.rows ||
|
||||
data.items ||
|
||||
(Array.isArray(data.data) ? data.data : undefined)
|
||||
while (allFilteredRecords.length < pagination.pageSize) {
|
||||
const params = {
|
||||
...baseParams,
|
||||
pageNum: currentPage,
|
||||
pageSize: pagination.pageSize,
|
||||
}
|
||||
|
||||
const res: any = await devInfolist(params)
|
||||
const data = res?.data ?? res ?? {}
|
||||
|
||||
// 兼容多种返回格式
|
||||
const recordsFromData =
|
||||
data.records ||
|
||||
data.list ||
|
||||
data.rows ||
|
||||
data.items ||
|
||||
(Array.isArray(data.data) ? data.data : undefined)
|
||||
|
||||
const records = Array.isArray(recordsFromData)
|
||||
? recordsFromData
|
||||
: Array.isArray(data)
|
||||
? data
|
||||
: []
|
||||
const records = Array.isArray(recordsFromData)
|
||||
? recordsFromData
|
||||
: Array.isArray(data)
|
||||
? data
|
||||
: []
|
||||
|
||||
// 过滤掉 devModel 为 PLC 的设备(前端过滤作为备用)
|
||||
const filteredRecords = records.filter((item: any) => {
|
||||
const devModel = item.devModel || ''
|
||||
return devModel.toUpperCase() !== 'PLC'
|
||||
})
|
||||
// 过滤掉 devModel 为 PLC 的设备
|
||||
const filteredRecords = records.filter((item: any) => {
|
||||
const devModel = item.devModel || ''
|
||||
return devModel.toUpperCase() !== 'PLC'
|
||||
})
|
||||
|
||||
const totalValue =
|
||||
data.total ?? data.count ?? data.totalCount ?? filteredRecords.length ?? 0
|
||||
// 第一次请求时,判断后端是否支持 devModelNot
|
||||
if (currentPage === pagination.pageNum) {
|
||||
backendTotal = data.total ?? data.count ?? data.totalCount ?? 0
|
||||
backendSupportsDevModelNot = records.length === filteredRecords.length
|
||||
}
|
||||
|
||||
deviceList.value = filteredRecords
|
||||
total.value = Number(totalValue) || 0
|
||||
// 添加到总列表
|
||||
allFilteredRecords = [...allFilteredRecords, ...filteredRecords]
|
||||
|
||||
// 如果已经获取了足够的数据,或者没有更多数据了,退出循环
|
||||
if (allFilteredRecords.length >= pagination.pageSize || records.length === 0) {
|
||||
break
|
||||
}
|
||||
|
||||
// 如果已经请求了所有数据,退出循环
|
||||
if (currentPage * pagination.pageSize >= backendTotal && backendTotal > 0) {
|
||||
break
|
||||
}
|
||||
|
||||
currentPage++
|
||||
}
|
||||
|
||||
// 只取pageSize条数据
|
||||
deviceList.value = allFilteredRecords.slice(0, pagination.pageSize)
|
||||
|
||||
// 计算total
|
||||
if (backendSupportsDevModelNot) {
|
||||
// 后端支持 devModelNot,直接使用后端返回的total
|
||||
total.value = Number(backendTotal) || allFilteredRecords.length
|
||||
} else {
|
||||
// 后端不支持 devModelNot,估算total
|
||||
// 如果第一次请求有数据,根据PLC比例估算
|
||||
if (allFilteredRecords.length > 0 && backendTotal > 0) {
|
||||
// 计算已请求的数据中PLC的比例
|
||||
const totalRequested = (currentPage - pagination.pageNum + 1) * pagination.pageSize
|
||||
const plcRatio = totalRequested > 0
|
||||
? (totalRequested - allFilteredRecords.length) / totalRequested
|
||||
: 0
|
||||
const estimatedNonPlcTotal = Math.max(
|
||||
Math.floor(backendTotal * (1 - plcRatio)),
|
||||
allFilteredRecords.length
|
||||
)
|
||||
total.value = estimatedNonPlcTotal
|
||||
} else {
|
||||
total.value = allFilteredRecords.length
|
||||
}
|
||||
}
|
||||
|
||||
// 如果当前页没有非PLC设备,且不是第一页,跳转到上一页
|
||||
if (deviceList.value.length === 0 && pagination.pageNum > 1) {
|
||||
pagination.pageNum = pagination.pageNum - 1
|
||||
await getDeviceList()
|
||||
return
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取设备列表失败:', error)
|
||||
ElMessage.error('获取设备列表失败')
|
||||
|
||||
@@ -17,18 +17,6 @@
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="设备状态">
|
||||
<el-select
|
||||
v-model="queryForm.status"
|
||||
placeholder="请选择设备状态"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option label="空闲" :value="0" />
|
||||
<el-option label="运行" :value="1" />
|
||||
<el-option label="故障" :value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">查询</el-button>
|
||||
<el-button @click="resetSearch">重置</el-button>
|
||||
@@ -154,7 +142,7 @@
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="设备型号">
|
||||
<el-form-item label="设备型号" prop="devModel">
|
||||
<el-input
|
||||
v-model="formData.devModel"
|
||||
disabled
|
||||
@@ -245,7 +233,6 @@ const total = ref(0)
|
||||
// 查询表单
|
||||
const queryForm = reactive({
|
||||
devName: '',
|
||||
status: undefined as number | undefined,
|
||||
})
|
||||
|
||||
// 分页
|
||||
@@ -272,13 +259,74 @@ const formData = reactive({
|
||||
devDesc: '',
|
||||
remark: '',
|
||||
status: 0, // 状态默认为0
|
||||
runModel: 0, // 运行模式默认为0
|
||||
})
|
||||
|
||||
// IP地址校验函数
|
||||
const validateIpAddr = (rule: any, value: any, callback: any) => {
|
||||
if (!value || String(value).trim() === '') {
|
||||
callback(new Error('请输入IP地址'))
|
||||
return
|
||||
}
|
||||
|
||||
const ipStr = String(value).trim().toLowerCase()
|
||||
|
||||
// 禁止本机IP格式
|
||||
if (ipStr === 'localhost' || ipStr === '127.0.0.1') {
|
||||
callback(new Error('不允许使用本机IP地址(localhost或127.0.0.1)'))
|
||||
return
|
||||
}
|
||||
|
||||
// IPv4地址格式校验:xxx.xxx.xxx.xxx,每段0-255
|
||||
const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/
|
||||
const match = ipStr.match(ipv4Regex)
|
||||
|
||||
if (!match) {
|
||||
callback(new Error('请输入有效的IP地址格式(如:192.168.1.1)'))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查每段数字是否在0-255范围内
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
const segment = match[i]
|
||||
if (!segment) {
|
||||
callback(new Error('IP地址格式错误'))
|
||||
return
|
||||
}
|
||||
const num = parseInt(segment, 10)
|
||||
if (num < 0 || num > 255) {
|
||||
callback(new Error('IP地址每段数字应在0-255范围内'))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 再次检查是否为127.0.0.1(防止用户输入其他格式的127.0.0.1)
|
||||
if (match[1] === '127' && match[2] === '0' && match[3] === '0' && match[4] === '1') {
|
||||
callback(new Error('不允许使用本机IP地址(127.0.0.1)'))
|
||||
return
|
||||
}
|
||||
|
||||
callback()
|
||||
}
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = {
|
||||
devName: [
|
||||
{ required: true, message: '请输入设备名称', trigger: 'blur' },
|
||||
],
|
||||
devModel: [
|
||||
{ required: true, message: '设备型号不能为空', trigger: 'blur' },
|
||||
],
|
||||
ipAddr: [
|
||||
{ required: true, validator: validateIpAddr, trigger: ['blur', 'change'] },
|
||||
],
|
||||
port: [
|
||||
{ required: true, message: '请输入端口', trigger: 'blur' },
|
||||
{ type: 'number', min: 1, max: 65535, message: '端口范围应在1-65535之间', trigger: 'blur' },
|
||||
],
|
||||
protocolType: [
|
||||
{ required: true, message: '请输入协议类型', trigger: 'blur' },
|
||||
],
|
||||
}
|
||||
|
||||
// 获取序号
|
||||
@@ -301,10 +349,6 @@ const getDeviceList = async () => {
|
||||
if (queryForm.devName) {
|
||||
params.devName = queryForm.devName
|
||||
}
|
||||
// 设备状态精确查询
|
||||
if (queryForm.status !== undefined && queryForm.status !== null) {
|
||||
params.status = queryForm.status
|
||||
}
|
||||
|
||||
const res: any = await devInfolist(params)
|
||||
|
||||
@@ -349,7 +393,6 @@ const handleSearch = () => {
|
||||
// 重置查询
|
||||
const resetSearch = () => {
|
||||
queryForm.devName = ''
|
||||
queryForm.status = undefined
|
||||
handleSearch()
|
||||
}
|
||||
|
||||
@@ -384,7 +427,8 @@ const resetForm = () => {
|
||||
formData.company = ''
|
||||
formData.devDesc = ''
|
||||
formData.remark = ''
|
||||
formData.status = 0
|
||||
formData.status = 0 // 状态默认为0
|
||||
formData.runModel = 0 // 运行模式默认为0
|
||||
formRef.value?.clearValidate()
|
||||
}
|
||||
|
||||
@@ -406,6 +450,7 @@ const handleEdit = async (item: any) => {
|
||||
formData.devDesc = data.devDesc ?? ''
|
||||
formData.remark = data.remark ?? ''
|
||||
formData.status = data.status ?? 0
|
||||
formData.runModel = data.runModel ?? 0
|
||||
|
||||
isEdit.value = true
|
||||
drawerTitle.value = '编辑PLC设备'
|
||||
@@ -458,23 +503,18 @@ const handleSubmit = async () => {
|
||||
await formRef.value.validate()
|
||||
submitting.value = true
|
||||
|
||||
// 构建提交数据,只包含非空字段(根据后端要求:非空则入库/更新)
|
||||
// 构建提交数据
|
||||
const submitData: any = {
|
||||
devName: formData.devName,
|
||||
devModel: 'PLC', // 设备型号固定为PLC
|
||||
status: 0, // 状态默认为0
|
||||
ipAddr: formData.ipAddr, // 必填项
|
||||
port: formData.port, // 必填项
|
||||
protocolType: formData.protocolType, // 必填项
|
||||
status: isEdit.value ? formData.status : 0, // 新增时状态为0,编辑时使用原值
|
||||
runModel: isEdit.value ? formData.runModel : 0, // 新增时运行模式为0,编辑时使用原值
|
||||
}
|
||||
|
||||
// 添加其他字段(如果非空)
|
||||
if (formData.ipAddr) {
|
||||
submitData.ipAddr = formData.ipAddr
|
||||
}
|
||||
if (formData.port !== undefined && formData.port !== null) {
|
||||
submitData.port = formData.port
|
||||
}
|
||||
if (formData.protocolType) {
|
||||
submitData.protocolType = formData.protocolType
|
||||
}
|
||||
// 添加其他可选字段(如果非空)
|
||||
if (formData.company) {
|
||||
submitData.company = formData.company
|
||||
}
|
||||
|
||||
1127
rc_autoplc_front/src/views/plcdevicecontrol/index.vue
Normal file
1127
rc_autoplc_front/src/views/plcdevicecontrol/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user