Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
Z
zhmes-agecal
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
耿迪迪
zhmes-agecal
Commits
fc64ceb4
Commit
fc64ceb4
authored
Jan 09, 2026
by
wanghao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
1 上料支持 手动 和 自动
parent
1e2e8666
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1628 additions
and
10 deletions
+1628
-10
RobotArmCommandController.java
...g/web/controller/equipment/RobotArmCommandController.java
+9
-1
IRobotArmCommandService.java
...va/com/zehong/system/service/IRobotArmCommandService.java
+16
-8
RobotArmCommandServiceImpl.java
...ehong/system/service/impl/RobotArmCommandServiceImpl.java
+50
-0
robotArmCommand.js
zhmes-agecal-web/src/api/robotArm/robotArmCommand.js
+8
-0
ManualAutoSwitchRoboticArm.vue
...rc/views/screen/components/ManualAutoSwitchRoboticArm.vue
+1544
-0
index.vue
zhmes-agecal-web/src/views/screen/index.vue
+1
-1
No files found.
zhmes-agecal-admin/src/main/java/com/zehong/web/controller/equipment/RobotArmCommandController.java
View file @
fc64ceb4
...
@@ -21,7 +21,7 @@ import com.zehong.common.core.page.TableDataInfo;
...
@@ -21,7 +21,7 @@ import com.zehong.common.core.page.TableDataInfo;
/**
/**
* 机械臂指令Controller
* 机械臂指令Controller
*
*
* @author zehong
* @author zehong
* @date 2025-08-04
* @date 2025-08-04
*/
*/
...
@@ -89,6 +89,14 @@ public class RobotArmCommandController extends BaseController
...
@@ -89,6 +89,14 @@ public class RobotArmCommandController extends BaseController
return
AjaxResult
.
success
(
"发送成功"
);
return
AjaxResult
.
success
(
"发送成功"
);
}
}
/**
* 发送指令
*/
@PostMapping
(
"/addManualCommand"
)
public
AjaxResult
addManualCommand
(
@RequestBody
RobotArmCommand
robotArmCommand
)
{
return
toAjax
(
robotArmCommandService
.
addManualCommand
(
robotArmCommand
));
}
/**
/**
* 新增机械臂指令
* 新增机械臂指令
*/
*/
...
...
zhmes-agecal-system/src/main/java/com/zehong/system/service/IRobotArmCommandService.java
View file @
fc64ceb4
...
@@ -7,15 +7,15 @@ import com.zehong.system.domain.RobotArmCommand;
...
@@ -7,15 +7,15 @@ import com.zehong.system.domain.RobotArmCommand;
/**
/**
* 机械臂指令Service接口
* 机械臂指令Service接口
*
*
* @author zehong
* @author zehong
* @date 2025-08-04
* @date 2025-08-04
*/
*/
public
interface
IRobotArmCommandService
public
interface
IRobotArmCommandService
{
{
/**
/**
* 查询机械臂指令
* 查询机械臂指令
*
*
* @param robotArmCommandId 机械臂指令ID
* @param robotArmCommandId 机械臂指令ID
* @return 机械臂指令
* @return 机械臂指令
*/
*/
...
@@ -23,7 +23,7 @@ public interface IRobotArmCommandService
...
@@ -23,7 +23,7 @@ public interface IRobotArmCommandService
/**
/**
* 查询机械臂指令列表
* 查询机械臂指令列表
*
*
* @param robotArmCommand 机械臂指令
* @param robotArmCommand 机械臂指令
* @return 机械臂指令集合
* @return 机械臂指令集合
*/
*/
...
@@ -37,12 +37,20 @@ public interface IRobotArmCommandService
...
@@ -37,12 +37,20 @@ public interface IRobotArmCommandService
/**
/**
* 新增机械臂指令
* 新增机械臂指令
*
*
* @param robotArmCommand 机械臂指令
* @param robotArmCommand 机械臂指令
* @return 结果
* @return 结果
*/
*/
public
int
insertRobotArmCommand
(
RobotArmCommand
robotArmCommand
);
public
int
insertRobotArmCommand
(
RobotArmCommand
robotArmCommand
);
/**
* 新增机械臂指令-手动模式
*
* @param robotArmCommand 机械臂指令
* @return 结果
*/
public
int
addManualCommand
(
RobotArmCommand
robotArmCommand
);
public
void
powerOnCommand
(
Long
commandId
);
public
void
powerOnCommand
(
Long
commandId
);
/**
/**
...
@@ -53,7 +61,7 @@ public interface IRobotArmCommandService
...
@@ -53,7 +61,7 @@ public interface IRobotArmCommandService
public
AjaxResult
sureCompletedCommand
(
Long
commandId
);
public
AjaxResult
sureCompletedCommand
(
Long
commandId
);
/**
/**
* 修改机械臂指令
* 修改机械臂指令
*
*
* @param robotArmCommand 机械臂指令
* @param robotArmCommand 机械臂指令
* @return 结果
* @return 结果
*/
*/
...
@@ -63,7 +71,7 @@ public interface IRobotArmCommandService
...
@@ -63,7 +71,7 @@ public interface IRobotArmCommandService
/**
/**
* 批量删除机械臂指令
* 批量删除机械臂指令
*
*
* @param robotArmCommandIds 需要删除的机械臂指令ID
* @param robotArmCommandIds 需要删除的机械臂指令ID
* @return 结果
* @return 结果
*/
*/
...
@@ -71,7 +79,7 @@ public interface IRobotArmCommandService
...
@@ -71,7 +79,7 @@ public interface IRobotArmCommandService
/**
/**
* 删除机械臂指令信息
* 删除机械臂指令信息
*
*
* @param robotArmCommandId 机械臂指令ID
* @param robotArmCommandId 机械臂指令ID
* @return 结果
* @return 结果
*/
*/
...
...
zhmes-agecal-system/src/main/java/com/zehong/system/service/impl/RobotArmCommandServiceImpl.java
View file @
fc64ceb4
...
@@ -446,6 +446,56 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
...
@@ -446,6 +446,56 @@ public class RobotArmCommandServiceImpl implements IRobotArmCommandService
return
robotArmCommandMapper
.
insertRobotArmCommand
(
robotArmCommand
);
return
robotArmCommandMapper
.
insertRobotArmCommand
(
robotArmCommand
);
}
}
@Override
@Transactional
public
int
addManualCommand
(
RobotArmCommand
robotArmCommand
)
{
robotArmCommand
.
setCreateTime
(
DateUtils
.
getNowDate
());
if
(
StringUtils
.
isBlank
(
robotArmCommand
.
getTrayCode
())
||
StringUtils
.
isBlank
(
robotArmCommand
.
getStoreyCode
()))
{
throw
new
RuntimeException
(
"托盘编号和层编号不能为空"
);
}
TTrayInfo
tTrayInfo
=
tTrayInfoMapper
.
selectTTrayInfoByCode
(
robotArmCommand
.
getTrayCode
());
if
(
tTrayInfo
==
null
)
{
throw
new
RuntimeException
(
"托盘不存在"
);
}
// 看看 此托盘绑定了数据了吗?
int
i1
=
palletDeviceBindingMapper
.
countByTrayId
(
tTrayInfo
.
getfTrayId
());
if
(
i1
==
0
)
{
throw
new
RuntimeException
(
"托盘未绑定设备"
);
}
// 20251104 上料 不需要看托盘状态了
// 20251210 上料 又需要看托盘状态了
if
(!
"4"
.
equals
(
tTrayInfo
.
getfStatus
()))
{
throw
new
RuntimeException
(
"托盘状态异常,请联系管理员"
);
}
TStoreyInfo
tStoreyInfo
=
storeyInfoMapper
.
selectTStoreyInfoByCode
(
robotArmCommand
.
getStoreyCode
());
if
(
tStoreyInfo
==
null
)
{
throw
new
RuntimeException
(
"层不存在"
);
}
robotArmCommand
.
setStatus
(
"1"
);
robotArmCommand
.
setStoreyCode
(
tStoreyInfo
.
getfStoreyCode
());
robotArmCommand
.
setCommand
(
tStoreyInfo
.
getFeedingCommand
());
tTrayInfo
.
setfStoreyCode
(
tStoreyInfo
.
getfStoreyCode
());
tTrayInfo
.
setfBindingTime
(
new
Date
());
tTrayInfoMapper
.
updateTTrayInfo
(
tTrayInfo
);
tStoreyInfo
.
setfStatus
(
"1"
);
storeyInfoMapper
.
updateStatusByCode
(
tStoreyInfo
);
// 20260108 新加的把绑定层编号设置到实时数据上
palletDeviceBindingMapper
.
updateStoreCodeByTrayId
(
tTrayInfo
.
getfTrayId
(),
tStoreyInfo
.
getfStoreyCode
());
int
i
=
robotArmCommandMapper
.
insertRobotArmCommand
(
robotArmCommand
);
notifyCommandsUpdate
();
return
i
;
}
/**
/**
* 新增机械臂指令
* 新增机械臂指令
*
*
...
...
zhmes-agecal-web/src/api/robotArm/robotArmCommand.js
View file @
fc64ceb4
...
@@ -26,6 +26,14 @@ export function addCommand(data) {
...
@@ -26,6 +26,14 @@ export function addCommand(data) {
})
})
}
}
export
function
addManualCommand
(
data
)
{
return
request
({
url
:
'/robotArm/command/addManualCommand'
,
method
:
'post'
,
data
:
data
})
}
// 新增机械臂指令
// 新增机械臂指令
export
function
insertBlankingRobotArmCommand
(
data
)
{
export
function
insertBlankingRobotArmCommand
(
data
)
{
return
request
({
return
request
({
...
...
zhmes-agecal-web/src/views/screen/components/ManualAutoSwitchRoboticArm.vue
0 → 100644
View file @
fc64ceb4
<
template
>
<div
class=
"robotic-arm-panel"
>
<!-- 标题区域 -->
<div
class=
"panel-title"
>
<!-- 左侧标题+状态指示灯组合 -->
<div
class=
"title-with-status"
>
<div
class=
"title-left"
>
<div
class=
"title-text"
>
机械臂
</div>
<div
class=
"title-line"
></div>
</div>
<!-- 状态指示灯:紧挨着标题文字右侧 -->
<div
class=
"status-indicator"
>
<div
class=
"status-light"
:class=
"statusClass"
></div>
<div
class=
"status-text"
>
{{
statusText
}}
</div>
</div>
</div>
<!-- 上料按钮 -->
<div
class=
"title-right"
>
<button
class=
"add-button"
@
click=
"openAddModeDialog"
>
<i
class=
"el-icon-plus"
></i>
上料
</button>
<button
class=
"home-button"
@
click=
"handleHome"
>
<i
class=
"el-icon-refresh"
></i>
回零
</button>
<button
class=
"stop-button"
@
click=
"handleStop"
>
<i
class=
"el-icon-circle-close"
></i>
停止
</button>
</div>
</div>
<!-- 上料模式选择对话框 -->
<div
class=
"dialog-mask"
v-if=
"showModeDialog"
@
click
.
self=
"closeModeDialog"
>
<div
class=
"dialog-container mode-dialog"
>
<div
class=
"dialog-header"
>
选择上料模式
</div>
<div
class=
"dialog-body"
>
<div
class=
"dialog-content"
>
<div
class=
"mode-selection"
>
<div
class=
"mode-option"
@
click=
"selectMode('auto')"
>
<div
class=
"mode-icon"
>
<i
class=
"el-icon-video-play"
></i>
</div>
<div
class=
"mode-title"
>
自动模式
</div>
<div
class=
"mode-desc"
>
系统自动分配位置
</div>
</div>
<div
class=
"mode-option"
@
click=
"selectMode('manual')"
>
<div
class=
"mode-icon"
>
<i
class=
"el-icon-s-operation"
></i>
</div>
<div
class=
"mode-title"
>
手动模式
</div>
<div
class=
"mode-desc"
>
手动指定层编号
</div>
</div>
</div>
</div>
</div>
<div
class=
"dialog-footer"
>
<button
class=
"cancel-button"
@
click=
"closeModeDialog"
>
取消
</button>
</div>
</div>
</div>
<!-- 自动模式上料对话框 -->
<div
class=
"dialog-mask"
v-if=
"showAutoAddDialog"
@
click
.
self=
"closeAutoDialog"
>
<div
class=
"dialog-container"
>
<div
class=
"dialog-header"
>
自动上料
</div>
<div
class=
"dialog-body"
>
<div
class=
"dialog-content"
>
<div
class=
"scan-prompt"
>
请扫描托盘二维码
</div>
<div
class=
"mode-indicator"
>
自动模式 - 系统自动分配位置
</div>
<div
class=
"scan-input"
>
<input
type=
"text"
v-model=
"trayCode"
placeholder=
"手动输入或扫码"
ref=
"trayInput"
@
keyup
.
enter=
"confirmAutoAdd"
>
</div>
</div>
</div>
<div
class=
"dialog-footer"
>
<button
class=
"cancel-button"
@
click=
"closeAutoDialog"
>
取消
</button>
<button
class=
"confirm-button"
@
click=
"confirmAutoAdd"
>
确定
</button>
</div>
</div>
</div>
<!-- 手动模式上料对话框 -->
<div
class=
"dialog-mask"
v-if=
"showManualAddDialog"
@
click
.
self=
"closeManualDialog"
>
<div
class=
"dialog-container"
>
<div
class=
"dialog-header"
>
手动上料
</div>
<div
class=
"dialog-body"
>
<div
class=
"dialog-content"
>
<div
class=
"scan-prompt"
>
请输入上料信息
</div>
<div
class=
"mode-indicator"
>
手动模式 - 需要指定层编号
</div>
<div
class=
"manual-inputs"
>
<div
class=
"input-group"
>
<label
for=
"trayCode"
>
托盘编号:
</label>
<input
id=
"trayCode"
type=
"text"
v-model=
"manualTrayCode"
placeholder=
"输入托盘编号"
ref=
"manualTrayInput"
>
</div>
<div
class=
"input-group"
>
<label
for=
"storeyCode"
>
层编号:
</label>
<input
id=
"storeyCode"
type=
"text"
v-model=
"storeyCode"
placeholder=
"输入层编号(如: A01, B02)"
>
</div>
</div>
</div>
</div>
<div
class=
"dialog-footer"
>
<button
class=
"cancel-button"
@
click=
"closeManualDialog"
>
取消
</button>
<button
class=
"confirm-button"
@
click=
"confirmManualAdd"
>
确定
</button>
</div>
</div>
</div>
<!-- 主内容区:指令区+机械臂 -->
<div
class=
"main-content"
>
<!-- 左侧:待上料指令 -->
<div
class=
"loading-command"
>
<div
class=
"command-title"
>
待上料指令
</div>
<div
class=
"command-list"
>
<div
v-for=
"(cmd, index) in loadingCommands"
:key=
"index"
class=
"command-item"
:class=
"getCommandStatusClass(cmd.status)"
@
click=
"handleCommandClick(cmd)"
>
<div
class=
"cmd-info"
>
<div
class=
"cmd-tray"
>
托盘:
{{
cmd
.
trayCode
}}
</div>
<div
class=
"cmd-position"
>
位置:
{{
cmd
.
position
}}
</div>
<div
class=
"cmd-status"
>
状态:
{{
getStatusText
(
cmd
.
status
)
}}
</div>
</div>
</div>
</div>
</div>
<!-- 中间:机械臂主体 -->
<div
class=
"arm-center-wrapper"
>
<!-- 优先级控制区域 -->
<div
class=
"priority-control"
>
<div
class=
"priority-display"
>
{{
priorityDisplayText
}}
</div>
<button
class=
"priority-toggle-btn"
@
click=
"togglePriority"
>
点击切换
</button>
</div>
<div
class=
"robotic-arm-container"
>
<div
class=
"robotic-arm"
>
<!-- 机械臂各部件 -->
<div
class=
"arm-base"
>
<div
class=
"base-top"
></div>
<div
class=
"base-bottom"
></div>
</div>
<div
class=
"arm-joint joint-1"
>
<div
class=
"joint-body"
></div>
</div>
<div
class=
"arm-segment segment-1"
>
<div
class=
"segment-body"
></div>
</div>
<div
class=
"arm-joint joint-2"
>
<div
class=
"joint-body"
></div>
</div>
<div
class=
"arm-segment segment-2"
>
<div
class=
"segment-body"
></div>
</div>
<div
class=
"arm-gripper"
>
<div
class=
"gripper-left"
></div>
<div
class=
"gripper-right"
></div>
</div>
</div>
</div>
</div>
<!-- 右侧:待下料指令 -->
<div
class=
"unloading-command"
>
<div
class=
"command-title"
>
待下料指令
</div>
<div
class=
"command-list"
>
<div
v-for=
"(cmd, index) in unloadingCommands"
:key=
"index"
class=
"command-item"
:class=
"getCommandStatusClass(cmd.status)"
@
click=
"handleCommandClick(cmd)"
>
<div
class=
"cmd-info"
>
<div
class=
"cmd-tray"
>
托盘:
{{
cmd
.
trayCode
}}
</div>
<div
class=
"cmd-position"
>
位置:
{{
cmd
.
position
}}
</div>
<div
class=
"cmd-status"
>
状态:
{{
getStatusText
(
cmd
.
status
)
}}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 上电对话框 -->
<div
class=
"dialog-mask"
v-if=
"showPowerOnDialog"
@
click
.
self=
"closePowerOnDialog"
>
<div
class=
"dialog-container"
>
<div
class=
"dialog-header"
>
上电操作
</div>
<div
class=
"dialog-body"
>
<div
class=
"dialog-content"
>
<div
class=
"scan-prompt"
>
确认对以下托盘执行上电操作?
</div>
<div
class=
"power-on-info"
>
<div><span
class=
"label"
>
托盘编号:
</span>
{{
selectedCommand
.
trayCode
}}
</div>
<div><span
class=
"label"
>
位置:
</span>
{{
selectedCommand
.
position
}}
</div>
</div>
</div>
</div>
<div
class=
"dialog-footer"
>
<button
class=
"cancel-button"
@
click=
"closePowerOnDialog"
>
取消
</button>
<button
class=
"confirm-button"
@
click=
"confirmPowerOn"
>
确认上电
</button>
</div>
</div>
</div>
<!-- 确认执行完成对话框 -->
<div
class=
"dialog-mask"
v-if=
"showSureCompleteDialog"
@
click
.
self=
"closePowerOnDialog"
>
<div
class=
"dialog-container"
>
<div
class=
"dialog-header"
>
确认执行完成
</div>
<div
class=
"dialog-body"
>
<div
class=
"dialog-content"
>
<div
class=
"scan-prompt"
>
确认此条指令执行完成?
</div>
<div
class=
"power-on-info"
>
<div><span
class=
"label"
>
托盘编号:
</span>
{{
selectedCommand
.
trayCode
}}
</div>
<div><span
class=
"label"
>
位置:
</span>
{{
selectedCommand
.
position
}}
</div>
</div>
</div>
</div>
<div
class=
"dialog-footer"
>
<button
class=
"cancel-button"
@
click=
"closeSureCompleteDialog"
>
取消
</button>
<button
class=
"confirm-button"
@
click=
"confirmSureComplete"
>
确认完成
</button>
</div>
</div>
</div>
</div>
</
template
>
<
script
>
import
{
addCommand
,
powerOnCommand
,
sendHomeCommand
,
sendStopCommand
,
sureCompletedCommand
,
updateInstructionExecutionPriority
,
addManualCommand
}
from
"@/api/robotArm/robotArmCommand"
export
default
{
name
:
'RoboticArm'
,
data
()
{
return
{
status
:
'idle'
,
// idle, running, error
// 对话框状态
showModeDialog
:
false
,
showAutoAddDialog
:
false
,
showManualAddDialog
:
false
,
// 自动模式数据
trayCode
:
''
,
// 手动模式数据
manualTrayCode
:
''
,
storeyCode
:
''
,
trayType
:
'0'
,
// 0: 上料托盘, 1: 下料托盘
loadingCommands
:
[],
unloadingCommands
:
[],
websocket
:
null
,
reconnectInterval
:
null
,
showPowerOnDialog
:
false
,
showSureCompleteDialog
:
false
,
selectedCommand
:
null
,
priority
:
'loading'
// loading: 上料优先, unloading: 下料优先
};
},
computed
:
{
statusClass
()
{
return
{
'idle'
:
this
.
status
===
'idle'
,
'running'
:
this
.
status
===
'running'
,
'error'
:
this
.
status
===
'error'
,
'unknown'
:
this
.
status
===
'unknown'
};
},
statusText
()
{
return
{
'idle'
:
'空闲中'
,
'running'
:
'运行中'
,
'error'
:
'故障'
,
'unknown'
:
'未知'
}[
this
.
status
];
},
priorityDisplayText
()
{
return
this
.
priority
===
'loading'
?
'上料优先中'
:
'下料优先中'
;
},
priorityToggleText
()
{
return
this
.
priority
===
'loading'
?
'下料优先'
:
'上料优先'
;
}
},
mounted
()
{
this
.
initWebSocket
();
},
beforeDestroy
()
{
this
.
disconnectWebSocket
();
},
watch
:
{
showAutoAddDialog
(
val
)
{
if
(
val
)
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
trayInput
.
focus
();
});
}
else
{
this
.
trayCode
=
''
;
}
},
showManualAddDialog
(
val
)
{
if
(
val
)
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
manualTrayInput
.
focus
();
});
}
else
{
this
.
manualTrayCode
=
''
;
this
.
storeyCode
=
''
;
this
.
trayType
=
'0'
;
}
}
},
methods
:
{
// 打开上料模式选择对话框
openAddModeDialog
()
{
this
.
showModeDialog
=
true
;
},
// 关闭模式选择对话框
closeModeDialog
()
{
this
.
showModeDialog
=
false
;
},
// 选择模式
selectMode
(
mode
)
{
this
.
showModeDialog
=
false
;
if
(
mode
===
'auto'
)
{
this
.
showAutoAddDialog
=
true
;
}
else
{
this
.
showManualAddDialog
=
true
;
}
},
// 关闭自动模式对话框
closeAutoDialog
()
{
this
.
showAutoAddDialog
=
false
;
this
.
trayCode
=
''
;
},
// 关闭手动模式对话框
closeManualDialog
()
{
this
.
showManualAddDialog
=
false
;
this
.
manualTrayCode
=
''
;
this
.
storeyCode
=
''
;
this
.
trayType
=
'0'
;
},
// 确认自动模式上料
confirmAutoAdd
()
{
if
(
!
this
.
trayCode
.
trim
())
{
this
.
$message
.
warning
(
'请输入托盘编号'
);
return
;
}
const
robotArmCommand
=
{
trayCode
:
this
.
trayCode
,
storeyCode
:
'待分配位置'
,
type
:
'0'
};
addCommand
(
robotArmCommand
).
then
(
res
=>
{
if
(
res
.
code
===
200
)
{
this
.
$message
.
success
(
"添加成功"
);
this
.
closeAutoDialog
();
}
else
{
this
.
$message
.
error
(
"添加失败"
);
}
}).
catch
(
err
=>
{
this
.
$message
.
error
(
"添加失败"
);
});
},
// 确认手动模式上料
confirmManualAdd
()
{
if
(
!
this
.
manualTrayCode
.
trim
())
{
this
.
$message
.
warning
(
'请输入托盘编号'
);
return
;
}
if
(
!
this
.
storeyCode
.
trim
())
{
this
.
$message
.
warning
(
'请输入层编号'
);
return
;
}
const
robotArmCommand
=
{
trayCode
:
this
.
manualTrayCode
,
storeyCode
:
this
.
storeyCode
};
addManualCommand
(
robotArmCommand
).
then
(
res
=>
{
if
(
res
.
code
===
200
)
{
this
.
$message
.
success
(
"手动添加成功"
);
this
.
closeManualDialog
();
}
})
},
initWebSocket
()
{
// 从环境变量获取基础URL,默认使用Nginx代理地址
const
backendUrl
=
process
.
env
.
VUE_APP_API_BASE_URL
||
'http://192.168.0.100:8087'
;
// 根据需要切换不同的WebSocket端点
const
wsPath
=
'/agecal/ws-robot-arm'
;
// 或 '/agecal/ws-aging-cabinet'
// 替换协议并添加完整路径
const
wsUrl
=
backendUrl
.
replace
(
'http'
,
'ws'
)
+
wsPath
;
try
{
this
.
websocket
=
new
WebSocket
(
wsUrl
);
this
.
websocket
.
onopen
=
()
=>
{
console
.
log
(
'机械臂指令WebSocket连接成功'
);
this
.
status
=
'idle'
;
this
.
sendWebSocketMessage
({
type
:
'request'
,
commands
:
[
'loading'
,
'unloading'
]
});
};
this
.
websocket
.
onmessage
=
(
event
)
=>
{
try
{
const
message
=
JSON
.
parse
(
event
.
data
);
if
(
message
.
type
===
'loading'
)
{
this
.
loadingCommands
=
message
.
data
.
map
(
cmd
=>
({
robotArmCommandId
:
cmd
.
robotArmCommandId
,
trayCode
:
cmd
.
trayCode
,
position
:
cmd
.
storeyCode
,
status
:
cmd
.
status
||
'0'
// 默认待执行状态
}));
}
else
if
(
message
.
type
===
'unloading'
)
{
this
.
unloadingCommands
=
message
.
data
.
map
(
cmd
=>
({
robotArmCommandId
:
cmd
.
robotArmCommandId
,
trayCode
:
cmd
.
trayCode
,
position
:
cmd
.
storeyCode
,
status
:
cmd
.
status
||
'0'
// 默认待执行状态
}));
}
// 新增:处理状态更新消息
else
if
(
message
.
type
===
'status'
)
{
this
.
status
=
message
.
data
;
// 更新机械臂状态
}
}
catch
(
e
)
{
console
.
error
(
'解析WebSocket消息失败:'
,
e
);
}
};
this
.
websocket
.
onerror
=
(
error
)
=>
{
console
.
error
(
'WebSocket错误:'
,
error
);
this
.
status
=
'error'
;
this
.
scheduleReconnect
();
};
this
.
websocket
.
onclose
=
()
=>
{
console
.
log
(
'WebSocket连接关闭'
);
this
.
scheduleReconnect
();
};
}
catch
(
e
)
{
console
.
error
(
'创建WebSocket失败:'
,
e
);
this
.
scheduleReconnect
();
}
},
sendWebSocketMessage
(
message
)
{
if
(
this
.
websocket
&&
this
.
websocket
.
readyState
===
WebSocket
.
OPEN
)
{
this
.
websocket
.
send
(
JSON
.
stringify
(
message
));
}
},
scheduleReconnect
()
{
if
(
this
.
reconnectInterval
)
clearInterval
(
this
.
reconnectInterval
);
this
.
reconnectInterval
=
setInterval
(()
=>
{
if
(
!
this
.
websocket
||
this
.
websocket
.
readyState
!==
WebSocket
.
OPEN
)
{
console
.
log
(
'尝试重新连接WebSocket...'
);
this
.
disconnectWebSocket
();
this
.
initWebSocket
();
}
},
5000
);
},
disconnectWebSocket
()
{
if
(
this
.
websocket
)
{
try
{
this
.
websocket
.
close
();
}
catch
(
e
)
{
console
.
error
(
'关闭WebSocket时出错:'
,
e
);
}
}
if
(
this
.
reconnectInterval
)
{
clearInterval
(
this
.
reconnectInterval
);
this
.
reconnectInterval
=
null
;
}
this
.
websocket
=
null
;
},
// 回零
handleHome
()
{
sendHomeCommand
().
then
(
response
=>
{
this
.
$message
.
success
(
"已发送"
);
})
},
// 停止
handleStop
()
{
sendStopCommand
().
then
(
response
=>
{
this
.
$message
.
success
(
"已发送"
);
})
},
// 更新状态文本映射
getStatusText
(
status
)
{
const
statusMap
=
{
'0'
:
'待分配位置'
,
'1'
:
'待执行'
,
'2'
:
'执行中'
,
'3'
:
'未上电'
,
'4'
:
'执行结束'
};
return
statusMap
[
status
]
||
'未知状态'
;
},
// 更新状态样式类映射
getCommandStatusClass
(
status
)
{
return
{
'status-0'
:
status
==
'0'
,
// 待分配位置
'status-1'
:
status
==
'1'
,
// 待执行
'status-2'
:
status
==
'2'
,
// 执行中
'status-3'
:
status
==
'3'
,
// 未上电
'status-4'
:
status
==
'4'
// 执行结束
};
},
handleCommandClick
(
cmd
)
{
// 只有未上电状态的指令才可点击
if
(
cmd
.
status
===
'3'
)
{
this
.
selectedCommand
=
cmd
;
this
.
showPowerOnDialog
=
true
;
}
else
if
(
cmd
.
status
===
'2'
)
{
this
.
selectedCommand
=
cmd
;
this
.
showSureCompleteDialog
=
true
;
}
},
closeSureCompleteDialog
()
{
this
.
showSureCompleteDialog
=
false
;
this
.
selectedCommand
=
null
;
},
closePowerOnDialog
()
{
this
.
showPowerOnDialog
=
false
;
this
.
selectedCommand
=
null
;
},
confirmSureComplete
()
{
if
(
!
this
.
selectedCommand
)
return
;
sureCompletedCommand
(
this
.
selectedCommand
.
robotArmCommandId
).
then
(
res
=>
{
if
(
res
.
code
===
200
)
{
this
.
$message
.
success
(
"确认完成成功"
);
this
.
closeSureCompleteDialog
();
}
else
{
this
.
$message
.
error
(
"确认完成失败"
);
}
}).
catch
(
err
=>
{
this
.
$message
.
error
(
"确认完成失败"
);
})
},
confirmPowerOn
()
{
if
(
!
this
.
selectedCommand
)
return
;
powerOnCommand
(
this
.
selectedCommand
.
robotArmCommandId
).
then
(
res
=>
{
if
(
res
.
code
===
200
)
{
this
.
$message
.
success
(
"上电操作成功"
);
this
.
closePowerOnDialog
();
}
else
{
this
.
$message
.
error
(
"上电操作失败"
);
}
}).
catch
(
err
=>
{
this
.
$message
.
error
(
"上电操作失败"
);
});
},
// 切换优先级
togglePriority
()
{
this
.
priority
=
this
.
priority
===
'loading'
?
'unloading'
:
'loading'
;
updateInstructionExecutionPriority
(
this
.
priority
).
then
(
res
=>
{
if
(
res
.
code
===
200
)
{
this
.
$message
.
success
(
"优先级设置成功"
);
}
else
{
this
.
$message
.
error
(
"优先级设置失败"
);
}
})
// 这里可以添加发送优先级设置到后端的逻辑
console
.
log
(
`优先级已切换为:
${
this
.
priorityDisplayText
}
: this.priority:
${
this
.
priority
}
`
);
},
// 消息提示方法
msgSuccess
(
msg
)
{
this
.
$message
.
success
(
msg
);
},
msgError
(
msg
)
{
this
.
$message
.
error
(
msg
);
}
}
};
</
script
>
<
style
scoped
>
/* 标题区域样式 */
.panel-title
{
position
:
absolute
;
top
:
20px
;
left
:
20px
;
right
:
20px
;
z-index
:
20
;
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
/* 标题组合居左,按钮居右 */
margin-bottom
:
25px
;
padding-bottom
:
15px
;
border-bottom
:
1px
solid
rgba
(
64
,
158
,
255
,
0.3
);
}
/* 标题+状态指示灯组合容器:水平排列 */
.title-with-status
{
display
:
flex
;
align-items
:
center
;
/* 垂直居中对齐 */
gap
:
5px
;
/* 标题与状态指示灯之间的间距 */
}
.title-left
{
display
:
flex
;
flex-direction
:
column
;
}
.title-text
{
font-size
:
20px
;
font-weight
:
bold
;
color
:
#ffffff
;
letter-spacing
:
1px
;
text-shadow
:
0
0
10px
rgba
(
64
,
158
,
255
,
0.5
);
}
.title-line
{
height
:
3px
;
width
:
70px
;
background
:
linear-gradient
(
to
right
,
#409EFF
,
transparent
);
margin-top
:
8px
;
border-radius
:
2px
;
}
/* 状态指示灯:紧挨着标题文字右侧 */
.status-indicator
{
display
:
flex
;
align-items
:
center
;
padding
:
6px
12px
;
background
:
rgba
(
0
,
30
,
60
,
0.4
);
border-radius
:
20px
;
border
:
1px
solid
rgba
(
64
,
158
,
255
,
0.3
);
margin
:
0
;
/* 清除原有margin */
}
.status-light
{
width
:
12px
;
height
:
12px
;
border-radius
:
50%
;
margin-right
:
8px
;
box-shadow
:
0
0
6px
currentColor
;
}
.status-light.idle
{
background-color
:
#64ffaa
;
color
:
#64ffaa
;
}
.status-light.running
{
background-color
:
#409EFF
;
color
:
#409EFF
;
animation
:
pulse
1.5s
infinite
;
}
.status-light.error
{
background-color
:
#ff4d4f
;
color
:
#ff4d4f
;
animation
:
blink
1s
infinite
;
}
/* 添加未知状态样式 */
.status-light.unknown
{
background-color
:
#a0a0a0
;
color
:
#a0a0a0
;
animation
:
blink-gray
1s
infinite
;
}
@keyframes
blink-gray
{
0
%,
100
%
{
opacity
:
0.6
;
}
50
%
{
opacity
:
1
;
}
}
.status-text
{
font-size
:
14px
;
font-weight
:
bold
;
color
:
#64c8ff
;
}
.title-right
{
display
:
flex
;
align-items
:
center
;
}
/* 回零按钮样式 */
.home-button
{
background
:
linear-gradient
(
to
right
,
#67C23A
,
#85ce61
);
color
:
white
;
border
:
none
;
padding
:
8px
16px
;
border-radius
:
4px
;
cursor
:
pointer
;
font-size
:
14px
;
font-weight
:
bold
;
display
:
flex
;
align-items
:
center
;
box-shadow
:
0
2px
6px
rgba
(
0
,
0
,
0
,
0.3
);
transition
:
all
0.3s
;
margin-left
:
10px
;
/* 与其他按钮保持间距 */
}
.home-button
:hover
{
background
:
linear-gradient
(
to
right
,
#85ce61
,
#67C23A
);
transform
:
translateY
(
-2px
);
box-shadow
:
0
4px
8px
rgba
(
0
,
0
,
0
,
0.4
);
}
.home-button
i
{
margin-right
:
5px
;
}
/* 停止按钮样式 */
.stop-button
{
background
:
linear-gradient
(
to
right
,
#F56C6C
,
#f78989
);
color
:
white
;
border
:
none
;
padding
:
8px
16px
;
border-radius
:
4px
;
cursor
:
pointer
;
font-size
:
14px
;
font-weight
:
bold
;
display
:
flex
;
align-items
:
center
;
box-shadow
:
0
2px
6px
rgba
(
0
,
0
,
0
,
0.3
);
transition
:
all
0.3s
;
margin-left
:
10px
;
/* 与其他按钮保持间距 */
}
.stop-button
:hover
{
background
:
linear-gradient
(
to
right
,
#f78989
,
#F56C6C
);
transform
:
translateY
(
-2px
);
box-shadow
:
0
4px
8px
rgba
(
0
,
0
,
0
,
0.4
);
}
.stop-button
i
{
margin-right
:
5px
;
}
.add-button
{
background
:
linear-gradient
(
to
right
,
#409EFF
,
#64c8ff
);
color
:
white
;
border
:
none
;
padding
:
8px
16px
;
border-radius
:
4px
;
cursor
:
pointer
;
font-size
:
14px
;
font-weight
:
bold
;
display
:
flex
;
align-items
:
center
;
box-shadow
:
0
2px
6px
rgba
(
0
,
0
,
0
,
0.3
);
transition
:
all
0.3s
;
}
.add-button
:hover
{
background
:
linear-gradient
(
to
right
,
#64c8ff
,
#409EFF
);
transform
:
translateY
(
-2px
);
box-shadow
:
0
4px
8px
rgba
(
0
,
0
,
0
,
0.4
);
}
.add-button
i
{
margin-right
:
5px
;
}
/* 模式选择对话框样式 */
.mode-dialog
{
width
:
450px
;
}
.mode-selection
{
display
:
flex
;
gap
:
20px
;
margin-top
:
10px
;
}
.mode-option
{
flex
:
1
;
background
:
rgba
(
0
,
40
,
80
,
0.3
);
border
:
1px
solid
rgba
(
64
,
158
,
255
,
0.3
);
border-radius
:
8px
;
padding
:
20px
;
text-align
:
center
;
cursor
:
pointer
;
transition
:
all
0.3s
;
}
.mode-option
:hover
{
background
:
rgba
(
0
,
60
,
120
,
0.4
);
border-color
:
rgba
(
64
,
158
,
255
,
0.6
);
transform
:
translateY
(
-3px
);
box-shadow
:
0
5px
15px
rgba
(
0
,
0
,
0
,
0.2
);
}
.mode-icon
{
font-size
:
32px
;
color
:
#64c8ff
;
margin-bottom
:
10px
;
}
.mode-title
{
font-size
:
16px
;
font-weight
:
bold
;
color
:
#ffffff
;
margin-bottom
:
5px
;
}
.mode-desc
{
font-size
:
12px
;
color
:
#a0d2ff
;
}
/* 对话框样式 */
.dialog-mask
{
position
:
fixed
;
top
:
0
;
left
:
0
;
right
:
0
;
bottom
:
0
;
background-color
:
rgba
(
0
,
0
,
0
,
0.6
);
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
z-index
:
1000
;
}
.dialog-container
{
width
:
400px
;
background
:
linear-gradient
(
145deg
,
#1a2a3a
,
#0f1c2a
);
border-radius
:
10px
;
border
:
1px
solid
rgba
(
64
,
158
,
255
,
0.4
);
box-shadow
:
0
5px
20px
rgba
(
0
,
0
,
0
,
0.7
);
overflow
:
hidden
;
}
.dialog-header
{
padding
:
16px
20px
;
background
:
rgba
(
0
,
30
,
60
,
0.8
);
border-bottom
:
1px
solid
rgba
(
64
,
158
,
255
,
0.3
);
font-size
:
18px
;
font-weight
:
bold
;
color
:
#64c8ff
;
text-align
:
center
;
}
.dialog-body
{
padding
:
30px
20px
;
}
.dialog-content
{
text-align
:
center
;
}
.scan-prompt
{
font-size
:
18px
;
color
:
#a0d2ff
;
margin-bottom
:
10px
;
}
.mode-indicator
{
font-size
:
14px
;
color
:
#409EFF
;
margin-bottom
:
20px
;
padding
:
5px
10px
;
background
:
rgba
(
64
,
158
,
255
,
0.1
);
border-radius
:
4px
;
display
:
inline-block
;
}
.scan-input
input
{
width
:
100%
;
padding
:
12px
15px
;
background
:
rgba
(
0
,
20
,
40
,
0.6
);
border
:
1px
solid
rgba
(
64
,
158
,
255
,
0.5
);
border-radius
:
6px
;
color
:
white
;
font-size
:
16px
;
outline
:
none
;
transition
:
all
0.3s
;
}
.scan-input
input
:focus
{
border-color
:
#409EFF
;
box-shadow
:
0
0
10px
rgba
(
64
,
158
,
255
,
0.5
);
}
/* 手动模式输入框组样式 */
.manual-inputs
{
margin-top
:
20px
;
}
.input-group
{
margin-bottom
:
15px
;
text-align
:
left
;
}
.input-group
label
{
display
:
block
;
color
:
#a0d2ff
;
margin-bottom
:
5px
;
font-size
:
14px
;
}
.input-group
input
,
.input-group
select
{
width
:
100%
;
padding
:
10px
15px
;
background
:
rgba
(
0
,
20
,
40
,
0.6
);
border
:
1px
solid
rgba
(
64
,
158
,
255
,
0.5
);
border-radius
:
6px
;
color
:
white
;
font-size
:
14px
;
outline
:
none
;
transition
:
all
0.3s
;
box-sizing
:
border-box
;
}
.input-group
input
:focus
,
.input-group
select
:focus
{
border-color
:
#409EFF
;
box-shadow
:
0
0
10px
rgba
(
64
,
158
,
255
,
0.5
);
}
.input-group
select
option
{
background
:
#1a2a3a
;
color
:
white
;
}
.dialog-footer
{
padding
:
15px
20px
;
display
:
flex
;
justify-content
:
flex-end
;
background
:
rgba
(
0
,
20
,
40
,
0.5
);
border-top
:
1px
solid
rgba
(
64
,
158
,
255
,
0.3
);
}
.cancel-button
,
.confirm-button
{
padding
:
8px
20px
;
border-radius
:
4px
;
font-size
:
14px
;
font-weight
:
bold
;
cursor
:
pointer
;
transition
:
all
0.3s
;
margin-left
:
10px
;
}
.cancel-button
{
background
:
rgba
(
100
,
100
,
100
,
0.4
);
color
:
#a0d2ff
;
border
:
1px
solid
rgba
(
100
,
150
,
255
,
0.3
);
}
.cancel-button
:hover
{
background
:
rgba
(
120
,
120
,
120
,
0.5
);
border-color
:
rgba
(
100
,
150
,
255
,
0.5
);
}
.confirm-button
{
background
:
linear-gradient
(
to
right
,
#409EFF
,
#64c8ff
);
color
:
white
;
border
:
none
;
}
.confirm-button
:hover
{
background
:
linear-gradient
(
to
right
,
#64c8ff
,
#409EFF
);
transform
:
translateY
(
-2px
);
box-shadow
:
0
4px
8px
rgba
(
0
,
0
,
0
,
0.3
);
}
/* 外层容器 */
.robotic-arm-panel
{
width
:
100%
;
height
:
100%
;
min-height
:
100%
;
background
:
rgba
(
10
,
20
,
40
,
0.85
);
border-radius
:
12px
;
padding
:
20px
;
display
:
flex
;
flex-direction
:
column
;
border
:
1px
solid
rgba
(
64
,
158
,
255
,
0.3
);
box-shadow
:
0
0
20px
rgba
(
0
,
50
,
120
,
0.3
);
box-sizing
:
border-box
;
overflow
:
hidden
;
position
:
relative
;
}
/* 主内容区 */
.main-content
{
display
:
flex
;
flex
:
1
;
gap
:
15px
;
min-height
:
0
;
overflow
:
hidden
;
margin-top
:
60px
;
/* 为标题区域留出空间 */
}
.arm-center-wrapper
{
flex
:
1
;
min-width
:
0
;
display
:
flex
;
flex-direction
:
column
;
justify-content
:
flex-start
;
align-items
:
center
;
padding
:
10px
0
;
}
/* 优先级控制区域样式 */
.priority-control
{
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
margin-bottom
:
20px
;
padding
:
12px
20px
;
min-width
:
200px
;
}
.priority-display
{
font-size
:
16px
;
font-weight
:
bold
;
color
:
#64c8ff
;
margin-bottom
:
8px
;
text-align
:
center
;
}
.priority-toggle-btn
{
background
:
linear-gradient
(
to
right
,
#409EFF
,
#64c8ff
);
color
:
white
;
border
:
none
;
padding
:
6px
16px
;
border-radius
:
4px
;
cursor
:
pointer
;
font-size
:
14px
;
font-weight
:
bold
;
transition
:
all
0.3s
;
box-shadow
:
0
2px
6px
rgba
(
0
,
0
,
0
,
0.3
);
}
.priority-toggle-btn
:hover
{
background
:
linear-gradient
(
to
right
,
#64c8ff
,
#409EFF
);
transform
:
translateY
(
-2px
);
box-shadow
:
0
4px
8px
rgba
(
0
,
0
,
0
,
0.4
);
}
.robotic-arm-container
{
width
:
100%
;
height
:
100%
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
min-height
:
300px
;
}
.robotic-arm
{
position
:
relative
;
width
:
200px
;
height
:
330px
;
transform
:
scale
(
0.9
);
}
/* 指令区域 */
.loading-command
,
.unloading-command
{
width
:
160px
;
min-width
:
160px
;
background
:
rgba
(
0
,
30
,
60
,
0.4
);
border-radius
:
8px
;
padding
:
12px
;
border
:
1px
solid
rgba
(
64
,
158
,
255
,
0.3
);
display
:
flex
;
flex-direction
:
column
;
height
:
100%
;
box-sizing
:
border-box
;
}
.command-title
{
font-size
:
15px
;
font-weight
:
bold
;
color
:
#64c8ff
;
margin-bottom
:
12px
;
padding-bottom
:
6px
;
border-bottom
:
1px
solid
rgba
(
100
,
180
,
255
,
0.2
);
}
.command-list
{
flex
:
1
;
overflow-y
:
auto
;
min-height
:
0
;
}
.command-list
::-webkit-scrollbar
{
width
:
6px
;
}
.command-list
::-webkit-scrollbar-thumb
{
background
:
rgba
(
64
,
158
,
255
,
0.5
);
border-radius
:
3px
;
}
.command-item
{
padding
:
8px
;
margin-bottom
:
8px
;
background
:
rgba
(
0
,
40
,
80
,
0.3
);
border-radius
:
6px
;
transition
:
all
0.3s
;
border
:
1px
solid
transparent
;
}
.command-item
:hover
{
border-color
:
rgba
(
100
,
200
,
255
,
0.5
);
transform
:
translateY
(
-2px
);
box-shadow
:
0
4px
8px
rgba
(
0
,
0
,
0
,
0.2
);
}
.cmd-info
{
width
:
100%
;
font-size
:
13px
;
}
.cmd-tray
,
.cmd-position
{
margin-bottom
:
4px
;
color
:
#a0d2ff
;
display
:
flex
;
justify-content
:
space-between
;
}
/* 机械臂各部件样式 */
.arm-base
{
position
:
absolute
;
bottom
:
0
;
left
:
50%
;
transform
:
translateX
(
-50%
);
width
:
80px
;
height
:
20px
;
z-index
:
10
;
}
.base-top
{
width
:
100%
;
height
:
10px
;
background
:
#3a506b
;
border-radius
:
5px
5px
0
0
;
box-shadow
:
0
0
10px
rgba
(
0
,
0
,
0
,
0.5
);
}
.base-bottom
{
width
:
120%
;
margin-left
:
-10%
;
height
:
10px
;
background
:
#2c3e50
;
border-radius
:
0
0
8px
8px
;
}
.arm-joint
{
position
:
absolute
;
width
:
40px
;
height
:
40px
;
border-radius
:
50%
;
background
:
linear-gradient
(
145deg
,
#1e2a3a
,
#2c3e50
);
box-shadow
:
0
0
10px
rgba
(
0
,
0
,
0
,
0.5
);
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
z-index
:
9
;
}
.joint-body
{
width
:
30px
;
height
:
30px
;
border-radius
:
50%
;
background
:
#3a506b
;
border
:
2px
solid
#409EFF
;
}
.joint-1
{
bottom
:
20px
;
left
:
50%
;
transform
:
translateX
(
-50%
);
animation
:
rotateJoint1
8s
infinite
linear
;
}
.arm-segment
{
position
:
absolute
;
background
:
#3a506b
;
border
:
1px
solid
rgba
(
64
,
158
,
255
,
0.5
);
box-shadow
:
0
0
10px
rgba
(
0
,
0
,
0
,
0.3
);
transform-origin
:
top
center
;
}
.segment-body
{
height
:
100%
;
background
:
linear-gradient
(
to
right
,
#2c3e50
,
#3a506b
,
#2c3e50
);
border-left
:
1px
solid
rgba
(
100
,
200
,
255
,
0.3
);
border-right
:
1px
solid
rgba
(
100
,
200
,
255
,
0.3
);
}
.segment-1
{
width
:
20px
;
height
:
120px
;
bottom
:
60px
;
left
:
50%
;
transform
:
translateX
(
-50%
);
z-index
:
8
;
animation
:
moveSegment1
8s
infinite
ease-in-out
;
}
.joint-2
{
bottom
:
180px
;
left
:
50%
;
transform
:
translateX
(
-50%
);
animation
:
rotateJoint2
8s
infinite
linear
reverse
;
}
.segment-2
{
width
:
15px
;
height
:
100px
;
bottom
:
220px
;
left
:
50%
;
transform
:
translateX
(
-50%
);
z-index
:
7
;
animation
:
moveSegment2
8s
infinite
ease-in-out
;
}
.arm-gripper
{
position
:
absolute
;
bottom
:
320px
;
left
:
50%
;
transform
:
translateX
(
-50%
);
width
:
40px
;
height
:
30px
;
display
:
flex
;
justify-content
:
space-between
;
z-index
:
6
;
animation
:
moveGripper
8s
infinite
ease-in-out
;
}
.gripper-left
,
.gripper-right
{
width
:
10px
;
height
:
30px
;
background
:
#3a506b
;
border
:
1px
solid
rgba
(
64
,
158
,
255
,
0.5
);
border-radius
:
5px
;
}
.gripper-left
{
transform
:
rotate
(
-15deg
);
}
.gripper-right
{
transform
:
rotate
(
15deg
);
}
/* 动画效果 */
@keyframes
rotateJoint1
{
0
%,
100
%
{
transform
:
translateX
(
-50%
)
rotate
(
0deg
);
}
25
%
{
transform
:
translateX
(
-50%
)
rotate
(
45deg
);
}
50
%
{
transform
:
translateX
(
-50%
)
rotate
(
0deg
);
}
75
%
{
transform
:
translateX
(
-50%
)
rotate
(
-45deg
);
}
}
@keyframes
rotateJoint2
{
0
%,
100
%
{
transform
:
translateX
(
-50%
)
rotate
(
0deg
);
}
25
%
{
transform
:
translateX
(
-50%
)
rotate
(
30deg
);
}
50
%
{
transform
:
translateX
(
-50%
)
rotate
(
0deg
);
}
75
%
{
transform
:
translateX
(
-50%
)
rotate
(
-30deg
);
}
}
@keyframes
moveSegment1
{
0
%,
100
%
{
height
:
120px
;
}
50
%
{
height
:
140px
;
}
}
@keyframes
moveSegment2
{
0
%,
100
%
{
height
:
100px
;
}
50
%
{
height
:
80px
;
}
}
@keyframes
moveGripper
{
0
%,
100
%
{
transform
:
translateX
(
-50%
);
}
25
%
{
transform
:
translateX
(
-60%
);
}
75
%
{
transform
:
translateX
(
-40%
);
}
}
@keyframes
pulse
{
0
%
{
opacity
:
0.6
;
}
50
%
{
opacity
:
1
;
}
100
%
{
opacity
:
0.6
;
}
}
@keyframes
blink
{
0
%,
100
%
{
opacity
:
1
;
}
50
%
{
opacity
:
0.3
;
}
}
/* 响应式调整 */
@media
(
max-width
:
800px
)
{
.main-content
{
flex-direction
:
column
;
align-items
:
center
;
gap
:
15px
;
overflow-y
:
auto
;
}
.arm-center-wrapper
{
width
:
100%
;
min-height
:
350px
;
order
:
-1
;
/* 在移动设备上将机械臂区域放在最上面 */
}
.loading-command
,
.unloading-command
{
width
:
90%
;
max-width
:
300px
;
height
:
auto
;
min-height
:
200px
;
}
/* 标题区域响应式调整 */
.title-with-status
{
flex-direction
:
column
;
/* 小屏幕下标题和状态指示灯垂直排列 */
align-items
:
flex-start
;
gap
:
8px
;
}
.title-text
{
font-size
:
20px
;
}
.panel-title
{
flex-wrap
:
wrap
;
}
.title-right
{
margin-top
:
10px
;
order
:
3
;
}
/* 模式选择对话框响应式 */
.mode-dialog
{
width
:
90%
;
}
.mode-selection
{
flex-direction
:
column
;
}
}
@media
(
max-width
:
500px
)
{
.robotic-arm
{
transform
:
scale
(
0.8
);
}
.command-title
{
font-size
:
14px
;
}
.cmd-info
{
font-size
:
12px
;
}
.title-text
{
font-size
:
18px
;
}
.title-line
{
width
:
80px
;
}
.dialog-container
{
width
:
90%
;
}
/* 小屏幕下优先级控制区域调整 */
.priority-control
{
padding
:
8px
15px
;
min-width
:
180px
;
}
.priority-display
{
font-size
:
14px
;
}
.priority-toggle-btn
{
padding
:
5px
12px
;
font-size
:
12px
;
}
}
@media
(
max-width
:
300px
)
{
.robotic-arm
{
transform
:
scale
(
0.7
);
}
.loading-command
,
.unloading-command
{
width
:
100%
;
min-width
:
auto
;
}
}
/* 指令状态样式 */
.command-item.status-0
{
/* 待分配位置:紫蓝色调 */
background
:
rgba
(
50
,
50
,
100
,
0.3
);
border
:
1px
solid
rgba
(
160
,
160
,
255
,
0.5
);
}
.command-item.status-1
{
/* 待执行:天蓝色调(保留原待执行样式基础) */
background
:
rgba
(
0
,
40
,
80
,
0.3
);
border
:
1px
solid
rgba
(
100
,
200
,
255
,
0.5
);
}
.command-item.status-2
{
/* 执行中:青绿色调,带动态效果 */
background
:
rgba
(
0
,
80
,
40
,
0.3
);
border
:
1px
solid
rgba
(
100
,
255
,
170
,
0.5
);
animation
:
pulse-green
1.5s
infinite
;
/* 保持原执行中动画 */
}
.command-item.status-3
{
/* 未上电:红色调,带提醒效果 */
background
:
rgba
(
80
,
0
,
0
,
0.3
);
border
:
1px
solid
rgba
(
255
,
100
,
100
,
0.5
);
cursor
:
pointer
;
animation
:
blink-red
1s
infinite
;
/* 保持原未上电动画 */
}
.command-item.status-4
{
/* 执行结束:灰色调,无动态效果 */
background
:
rgba
(
60
,
60
,
60
,
0.3
);
border
:
1px
solid
rgba
(
180
,
180
,
180
,
0.5
);
}
/* 状态文本颜色 */
.status-0
.cmd-status
{
color
:
#a0a0ff
;
/* 待分配位置文本色 */
}
.status-1
.cmd-status
{
color
:
#64c8ff
;
/* 待执行文本色 */
}
.status-2
.cmd-status
{
color
:
#64ffaa
;
/* 执行中文本色 */
}
.status-3
.cmd-status
{
color
:
#ff6464
;
/* 未上电文本色 */
}
.status-4
.cmd-status
{
color
:
#a0a0a0
;
/* 执行结束文本色 */
}
/* 状态文本样式 */
.cmd-status
{
font-size
:
12px
;
margin-top
:
4px
;
font-weight
:
bold
;
}
.status-executing
.cmd-status
{
color
:
#64ffaa
;
}
.status-poweroff
.cmd-status
{
color
:
#ff6464
;
}
.status-completed
.cmd-status
{
color
:
#a0a0a0
;
}
/* 上电对话框样式 */
.power-on-info
{
background
:
rgba
(
0
,
20
,
40
,
0.4
);
padding
:
15px
;
border-radius
:
6px
;
margin-top
:
15px
;
text-align
:
left
;
}
.power-on-info
div
{
margin-bottom
:
10px
;
}
.power-on-info
.label
{
display
:
inline-block
;
width
:
80px
;
color
:
#64c8ff
;
font-weight
:
bold
;
}
/* 新增动画 */
@keyframes
pulse-green
{
0
%
{
opacity
:
0.8
;
box-shadow
:
0
0
5px
rgba
(
100
,
255
,
180
,
0.5
);
}
50
%
{
opacity
:
1
;
box-shadow
:
0
0
15px
rgba
(
100
,
255
,
180
,
0.8
);
}
100
%
{
opacity
:
0.8
;
box-shadow
:
0
0
5px
rgba
(
100
,
255
,
180
,
0.5
);
}
}
@keyframes
blink-red
{
0
%,
100
%
{
opacity
:
0.8
;
}
50
%
{
opacity
:
0.5
;
}
}
/* 响应式调整 */
@media
(
max-width
:
500px
)
{
.cmd-status
{
font-size
:
11px
;
}
}
</
style
>
zhmes-agecal-web/src/views/screen/index.vue
View file @
fc64ceb4
...
@@ -65,7 +65,7 @@ import RealTimeData from './components/RealTimeData'
...
@@ -65,7 +65,7 @@ import RealTimeData from './components/RealTimeData'
import
TrayBinding
from
"@/views/screen/components/TrayBinding"
;
import
TrayBinding
from
"@/views/screen/components/TrayBinding"
;
import
TrayInformation
from
"@/views/screen/components/TrayInformation"
;
import
TrayInformation
from
"@/views/screen/components/TrayInformation"
;
import
RoboticArm
from
'./components/RoboticArm.vue'
;
import
RoboticArm
from
'./components/
ManualAutoSwitch
RoboticArm.vue'
;
export
default
{
export
default
{
components
:
{
components
:
{
AgingCabinetBoard
,
AgingCabinetBoard
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment