#
Junjie
1 天以前 3eab4409e562a873d9bd7b047b3d0590901a6654
#
1个文件已添加
21个文件已修改
1879 ■■■■ 已修改文件
src/main/resources/i18n/en-US/legacy.properties 370 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/config/config.js 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/static/js/wrkMast/wrkMast.js 319 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/ai/diagnosis.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/ai/llm_config.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/basOutStationArea/basOutStationArea.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/basStationDevice/basStationDevice.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/config/config.html 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/debugParam/debugParam.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/detail.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/deviceLogs/deviceLogs.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/index.html 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/locMap/locMap.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/login.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/notifyReport/notifyReport.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/password.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/role/role_power_detail.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/watch/console.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/watch/console_html.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/watch/console_pixijs.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/watch/stationColorConfig.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/webapp/views/wrkMast/wrkMast.html 830 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/i18n/en-US/legacy.properties
@@ -181,8 +181,8 @@
不可修改=Not Editable
重要!一般用于后台登入=Important: usually used for admin login
当前角色不可更改为其它角色=Current role cannot be changed
手机号:=Phone:
用户名:=Username:
手机号\:=Phone:
用户名\:=Username:
输入手机号=Enter phone number
输入用户名=Enter username
重置密码=Reset Password
@@ -525,8 +525,8 @@
ConveyorCommand分段长度=Conveyor Command Segment Length
站点点最大任务数量上限=Max Tasks Per Station
Stations点最大Task数量上限=Max Tasks Per Station
冷却到: -=Cooldown Until: -
最近错误: -=Latest Error: -
冷却到\:-=Cooldown Until: -
最近错误\:-=Latest Error: -
故障切换开启=Failover Enabled
Failover开启=Failover Enabled
额度切换开启=Quota Switch Enabled
@@ -539,10 +539,10 @@
Success 0 / Failed 0 / 连续Failed 0=Success 0 / Failed 0 / Consecutive Failures 0
Success 7 / Failed 2 / 连续Failed 0=Success 7 / Failed 2 / Consecutive Failures 0
Success 8 / Failed 0 / 连续Failed 0=Success 8 / Failed 0 / Consecutive Failures 0
必填,例如: https://dashscope.aliyuncs.com/compatible-mode/v1=Required, for example: https://dashscope.aliyuncs.com/compatible-mode/v1
Required,例如: https://dashscope.aliyuncs.com/compatible-mode/v1=Required, for example: https://dashscope.aliyuncs.com/compatible-mode/v1
最近错误:=Latest Error:
冷却到:=Cooldown Until:
必填,例如\: https\://dashscope.aliyuncs.com/compatible-mode/v1=Required, for example: https://dashscope.aliyuncs.com/compatible-mode/v1
Required,例如\: https\://dashscope.aliyuncs.com/compatible-mode/v1=Required, for example: https://dashscope.aliyuncs.com/compatible-mode/v1
最近错误\:=Latest Error:
冷却到\:=Cooldown Until:
D.空桶/空栈板=D. Empty Tote/Pallet
E.出入专用轨道=E. Dedicated IO Rail
W.穿梭车母轨道=W. Shuttle Main Rail
@@ -705,3 +705,357 @@
阿里百炼-kimi-k2.5=Alibaba Bailian - kimi-k2.5
阿里百炼-glm-5=Alibaba Bailian - glm-5
硅基流动=SiliconFlow
提示=Prompt
尝试=Attempt
错误=Error
空闲=Idle
无物=Unloaded
工作类型=Work Type
能出=Outbound Enabled
能入=Inbound Enabled
文件数=File Count
类型\:=Type:
设备编号\:=Device No.:
文件数\:=File Count:
日志详情 -=Log Details -
日志可视化 -=Device Logs -
已删除=Deleted
老项目仍可继续使用这份硬件信息 JSON 申请许可证。=Legacy projects can still use this hardware JSON to apply for a license.
请求码中已包含项目名称,直接发给许可证服务端即可。=The request code already contains the project name and can be sent directly to the license service.
暂无路由配置=No route configuration available
点击右上角“新增路由”创建第一条配置=Click "Add Route" in the upper-right corner to create the first route.
保存成功=Saved successfully
保存失败=Save failed
测试成功=Test succeeded
测试失败=Test failed
测试中...=Testing...
导出成功=Export succeeded
导出失败=Export failed
导入确认=Import Confirmation
导入失败=Import failed
覆盖导入=Overwrite Import
合并导入=Merge Import
读取文件失败=Failed to read file
JSON 格式不正确=Invalid JSON format
未找到可导入的 routes=No importable routes found
请选择导入方式:覆盖导入会先清空现有路由;点击“合并导入”则按ID更新或新增。=Choose an import mode: overwrite import clears existing routes first; merge import updates by ID or adds new routes.
当前是未保存配置,测试通过后仍需先保存才会生效=This configuration is not saved yet. It still needs to be saved after a successful test to take effect.
复制失败,请手动复制=Copy failed. Please copy it manually.
没有可复制内容=Nothing to copy
已复制=Copied
API Key 为空=API key is empty
API Key 已复制=API key copied
日志加载失败=Failed to load logs
删除成功=Deleted successfully
删除失败=Delete failed
已清除冷却=Cooldown cleared
已清空=Cleared
清空日志=Clear Logs
清空失败=Clear failed
确定清空全部LLM调用日志吗?=Are you sure you want to clear all LLM call logs?
确定删除该路由吗?=Are you sure you want to delete this route?
确定删除该日志吗?=Are you sure you want to delete this log?
导入完成:新增=Import completed: added
导入异常明细(最多20条)=Import exception details (up to 20 items)
状态码=Status Code
场景=Scene
模型=Model
路由=Route
结果=Result
耗时(ms)=Latency (ms)
耗时\:=Latency:
结果\:=Result:
模型\:=Model:
路由\:=Route:
场景\:=Scene:
时间\:=Time:
状态码\:=Status Code:
请求\:=Request:
响应\:=Response:
返回片段\:=Response Snippet:
最近错误\:=Latest Error:
冷却到\:=Cooldown Until:
regex\:^成功\s*(\d+)\s*\/\s*失败\s*(\d+)\s*\/\s*连续失败\s*(\d+)$=Success $1 / Failed $2 / Consecutive Failures $3
regex\:^时间\:\s*(.+)$=Time: $1
regex\:^状态码\:\s*(.+)$=Status Code: $1
regex\:^耗时\:\s*(.+)$=Latency: $1
regex\:^结果\:\s*(.+)$=Result: $1
regex\:^路由\:\s*(.+)$=Route: $1
regex\:^模型\:\s*(.+)$=Model: $1
regex\:^返回片段\:\s*(.+)$=Response Snippet: $1
regex\:^请求\:\s*(.+)$=Request: $1
regex\:^响应\:\s*(.+)$=Response: $1
regex\:^最近错误\:\s*(.+)$=Latest Error: $1
regex\:^冷却到\:\s*(.+)$=Cooldown Until: $1
WCS AI 助手=WCS AI Assistant
AI 深度思考=AI Deep Thinking
系统巡检、异常问答、历史会话=System inspection, anomaly Q&A, and session history
连接中=Connecting
一键巡检=One-click Inspection
巡检当前系统=Inspect Current System
新会话=New Session
选择历史会话=Select Session History
向 AI 助手提问=Ask the AI Assistant
支持连续追问、历史会话切换,以及 AI 思考过程折叠展示。=Supports follow-up questions, session switching, and collapsible AI reasoning.
未绑定历史会话=No history session bound
新建会话,等待首条消息=Create a new session and wait for the first message
输入问题,或先执行一次巡检=Enter a question, or run an inspection first
AI 正在生成回复...=AI is generating a response...
AI 助手=AI Assistant
用户=User
WCS 诊断回复=WCS Diagnostic Reply
问题输入=Question Input
定位堆垛机异常=Locate Crane Anomalies
分析堵塞与积压=Analyze Blockages and Backlogs
追问最近告警=Ask About Recent Alarms
让 AI 主动梳理设备、任务和日志,给出一轮完整巡检。=Let AI review devices, tasks, and logs proactively and provide a full inspection.
让 AI 优先关注工位堵塞、任务堆积和节拍异常。=Let AI focus on station blockages, task backlogs, and rhythm anomalies first.
把最近异常事件压缩成可执行排查建议。=Condense recent anomalies into actionable troubleshooting steps.
帮我定位当前堆垛机相关的异常风险,按可能性从高到低列出。=Identify current crane-related risks and list them by likelihood.
帮我总结最近最值得关注的异常,并给出下一步排查动作。=Summarize the most important recent anomalies and suggest next troubleshooting steps.
结合近期日志与任务状态,判断是否存在堆垛机链路异常。=Determine whether there is any crane workflow anomaly based on recent logs and task status.
例如:最近哪个设备最值得优先排查?异常是否和堆垛机任务、工位堵塞或日志波动有关?=For example: which device should be checked first? Are the anomalies related to crane tasks, station blockage, or log fluctuation?
请重点分析当前是否存在工位堵塞、任务积压或节拍异常。=Please focus on whether there are station blockages, task backlogs, or rhythm anomalies.
删除会话失败=Failed to delete session
加载 AI 会话列表失败=Failed to load AI session list
加载会话历史失败=Failed to load session history
诊断中=Diagnosing
未命名会话=Untitled Session
最近更新=Updated Recently
刚刚创建=Just created
刚刚更新=Just updated
会话已绑定=Session Bound
临时会话=Temporary Session
Enter 发送,Shift+Enter 换行=Press Enter to send, Shift+Enter for a new line
regex\:^(\d+)\s*个会话$=$1 sessions
regex\:^(\d+)\s*分钟前$=$1 minute(s) ago
regex\:^(\d+)\s*小时前$=$1 hour(s) ago
补发结果=Retry Result
补发失败=Retry failed
当前筛选条件下没有通知日志=No notification logs match the current filters
获取通知队列失败=Failed to load notification queue
获取通知概览失败=Failed to load notification overview
获取通知日志失败=Failed to load notification logs
请求失败=Request failed
请选择要补发的队列通知=Please select queue notifications to retry
请选择要补发的通知日志=Please select notification logs to retry
确定按该日志重新补发通知吗?=Retry this notification based on the selected log?
确定补发该队列通知吗?=Retry this queued notification?
确定批量补发选中的队列通知吗?=Retry the selected queued notifications in batch?
确定批量补发选中的通知日志吗?=Retry the selected notification logs in batch?
确定执行手动补发吗?=Are you sure you want to retry manually?
上级任务号=Parent Task No.
接口响应=API Response
通知报文=Notification Payload
待发送=Pending
发送日志=Send Logs
regex\:^已选\s*(\d+)\s*条$=Selected $1 item(s)
regex\:^当前页签:\s*(.+)$=Current Tab: $1
调色盘=Palette
恢复默认=Restore Default
保存后,新打开的监控地图会直接读取 Redis 配置;已打开页面刷新后即可生效。=After saving, newly opened monitoring maps will read the Redis configuration directly. Refresh already opened pages to apply it.
regex\:^默认值:\s*(.+)$=Default: $1
操作区域=Action Area
regex\:^层:\s*(.+)$=Level: $1
regex\:^排:\s*(.+)$=Row: $1
regex\:^列:\s*(.+)$=Bay: $1
regex\:^库位号:\s*(.+)$=Location No.: $1
regex\:^库位状态:\s*(.+)$=Location Status: $1
regex\:^站点:\s*(.+)$=Station: $1
regex\:^工作号:\s*(.+)$=Work No.: $1
regex\:^工作类型:\s*(.+)$=Work Type: $1
regex\:^工作状态:\s*(.+)$=Work Status: $1
regex\:^源站:\s*(.+)$=Source Station: $1
regex\:^目标站:\s*(.+)$=Target Station: $1
regex\:^源库位:\s*(.+)$=Source Location: $1
regex\:^目标库位:\s*(.+)$=Target Location: $1
regex\:^自动:\s*(.+)$=Auto: $1
regex\:^有物:\s*(.+)$=Loaded: $1
regex\:^能入:\s*(.+)$=Inbound Enabled: $1
regex\:^能出:\s*(.+)$=Outbound Enabled: $1
地图加载失败=Failed to load map
楼层信息加载失败=Failed to load floor information
站点详情加载失败=Failed to load station details
双工位=Dual Station
regex\:^设备\s*(.+)$=Device $1
regex\:^区域\s*(.+)$=Area $1
regex\:^出库站点\s*\((\d+)\)$=Outbound Stations ($1)
共=Total
跳转时间=Jump Time
选择时间=Select Time
压缩生成进度=Compression Progress
下载接收进度=Download Receive Progress
日期选择=Date Selection
选中日期=Selected Date
设备类型=Device Type
起始序号=Start Offset
最大文件=Max Files
设备列表=Device List
暂无数据,请先选择日期=No data. Please select a date first.
播放=Play
暂停=Pause
重置=Reset
跳转=Jump
倍速=Speed
regex\:^共\s*(\d+)\s*个设备$=Total $1 devices
regex\:^下载\((.+)\)$=Download ($1)
regex\:^可视化\((.+)\)$=Visualize ($1)
regex\:^未知设备类型\:\s*(.+)$=Unknown device type: $1
不在定位=Off Position
在定位=At Position
初始化失败=Initialization failed
加载日期失败=Failed to load dates
加载设备失败=Failed to load devices
加载数据中...=Loading data...
没有找到日志数据=No log data found
目标时间超出日志范围,已跳转至结束时间=Target time exceeds the log range. Moved to the end time.
请输入设备编号=Please enter the device number
请选择设备类型=Please select a device type
数据已全部加载=All data loaded
无任务=No Task
下载失败或未找到日志=Download failed or logs not found
已到达日志末尾,无法到达目标时间=Reached the end of logs and cannot jump to the target time
已跳转至目标时间=Jumped to the target time
正在跳转至目标时间 (加载中)...=Jumping to the target time (loading)...
保存请求异常=Save request exception
加载请求异常=Load request exception
regex\:^保存失败\:\s*(.+)$=Save failed: $1
regex\:^加载数据失败\:\s*(.+)$=Failed to load data: $1
该关联已存在=This mapping already exists
确定删除该关联吗?=Are you sure you want to delete this mapping?
regex\:^已建立关联\:\s*站点\s*(.+)$=Mapping created: Station $1
该绑定已存在=This binding already exists
请输入区域编码和名称=Please enter the area code and name
区域已存在=Area already exists
regex\:^已建立绑定\:\s*站点\s*(.+)$=Binding created: Station $1
保存站点颜色配置失败=Failed to save station color configuration
加载站点颜色配置失败=Failed to load station color configuration
颜色格式已自动修正为十六进制=Color format has been normalized to hexadecimal automatically
已恢复默认颜色=Default colors restored
站点颜色配置已保存=Station color configuration saved
登录失败=Login failed
确定执行一键激活吗?=Are you sure you want to activate now?
系统登录=System Login
系统配置信息=System Configuration
对接WMS、设备与业务规则=Integrates WMS, devices, and business rules
统一编排现场执行任务=Unified orchestration of on-site execution tasks
请输入账号和密码进入系统。=Please enter your account and password to access the system.
将许可证服务端返回的 license 字段完整粘贴到这里。=Paste the full license field returned by the license service here.
许可证 Base64=License Base64
自动化立体仓库与智能物流系统解决方案=Automated AS/RS and intelligent logistics system solutions
作业、设备、日志全链路留痕=End-to-end traceability across jobs, devices, and logs
WCS系统让设备调度、任务执行与现场监控保持在同一套业务链路中。=The WCS keeps device scheduling, task execution, and on-site monitoring within the same business workflow.
角色管理=Role Management
权限管理=Permission Management
菜单列表=Menu List
菜单等级=Menu Level
父级菜单=Parent Menu
请输入编码=Please enter code
请输入名称=Please enter name
请选择上级=Please select parent
请输入菜单编码=Please enter menu code
请输入菜单名称=Please enter menu name
请输入排序=Please enter sort order
请选择类型=Please select type
请选择上级菜单=Please select parent menu
请选择状态=Please select status
请输入接口地址=Please enter API URL
请输入权限名称=Please enter permission name
请选择所属菜单=Please select menu
菜单查询失败=Menu query failed
菜单详情加载失败=Failed to load menu details
权限加载失败=Failed to load permissions
权限树加载失败=Failed to load permission tree
权限回显失败=Failed to load assigned permissions
权限保存失败=Failed to save permissions
角色查询失败=Role query failed
角色加载失败=Failed to load roles
确定删除选中角色吗?=Delete selected roles?
确定删除选中凭证吗?=Delete selected credentials?
# Playwright audit supplement
列设置=Column Settings
创建者=Creator
主要=Primary
编 号=ID
*编 号=*ID
异 常 码=Error Code
异 常=Error
状 态=Status
工 作 号=Work No.
备 注=Remarks
作 业=Operation
命 令=Command
目 标 站=Target Station
源 站=Source Station
源 站 点=Source Station
源 库 位=Source Location
条 码=Barcode
基准排=Base Row
基准排-code=Base Row Code
基准列=Base Bay
基准列-code=Base Bay Code
实 现 类=Implementation Class
日志ID=Log ID
平台密钥=Platform Key
Time戳=Timestamp
Exception内容=Exception Content
Menu列表=Menu List
展开All=Expand All
Permissions管理=Permission Management
凭证记录=Credential Records
标 识=Identifier
字 典 值=Dictionary Value
字典文本=Dictionary Text
是否Delete=Deleted
租 户=Tenant
账 号=Account
密 码=Password
昵 称=Nickname
头 像=Avatar
工 号=Employee No.
性 别=Gender
手 机 号=Mobile No.
邮 箱=Email
Email验证=Email Verified
所属部门=Department
真实姓名=Real Name
身份证号=ID Card No.
出生日期=Birth Date
个人简介=Biography
所属机构=Organization
*角 色=*Role
已设置Password=Password Set
Virtual Device初始化Status=Virtual Device Initialization Status
Please enter电梯中转点=Please enter the elevator transfer point
Role管理=Role Management
展开面板=Expand Panel
收起Actions=Collapse Actions
信息=Information
OKExportExcel吗=Export to Excel?
全选=Select All
至=to
regex\:^Success\\s*(\\d+)\\s*/\\s*Failed\\s*(\\d+)\\s*/\\s*连续Failed\\s*(\\d+)$=Success $1 / Failed $2 / Consecutive Failures $3
regex\:^(\\d{1,2})日$=Day $1
regex\:^\\*[\\s ]*编[\\s ]*号$=*ID
regex\:^编[\\s ]*号$=ID
regex\:^异[\\s ]*常$=Error
regex\:^状[\\s ]*态$=Status
regex\:^工[\\s ]*作[\\s ]*号$=Work No.
regex\:^备[\\s ]*注$=Remarks
regex\:^作[\\s ]*业$=Operation
regex\:^命[\\s ]*令$=Command
regex\:^目[\\s ]*标[\\s ]*站$=Target Station
regex\:^源[\\s ]*站[\\s ]*点$=Source Station
regex\:^源[\\s ]*站$=Source Station
regex\:^源[\\s ]*库[\\s ]*位$=Source Location
regex\:^条[\\s ]*码$=Barcode
regex\:^账[\\s ]*号$=Account
regex\:^密[\\s ]*码$=Password
regex\:^昵[\\s ]*称$=Nickname
regex\:^头[\\s ]*像$=Avatar
regex\:^工[\\s ]*号$=Employee No.
regex\:^性[\\s ]*别$=Gender
regex\:^邮[\\s ]*箱$=Email
regex\:^标[\\s ]*识$=Identifier
regex\:^租[\\s ]*户$=Tenant
regex\:^\\*[\\s ]*角[\\s ]*色$=*Role
src/main/webapp/static/js/config/config.js
@@ -850,6 +850,154 @@
        return result;
    }
    fieldMeta = [
        {
            field: 'id',
            columnName: 'id',
            label: 'ID',
            tableProp: 'id',
            exportField: 'id',
            kind: 'text',
            valueType: 'number',
            required: true,
            primaryKey: true,
            searchable: false,
            sortable: true,
            textarea: false,
            minWidth: 90,
            enumOptions: [],
            foreignQuery: '',
            checkboxActiveRaw: '1',
            checkboxInactiveRaw: '0'
        },
        {
            field: 'name',
            columnName: 'name',
            label: '名称',
            tableProp: 'name',
            exportField: 'name',
            kind: 'text',
            valueType: 'string',
            required: true,
            primaryKey: false,
            searchable: false,
            sortable: false,
            textarea: false,
            minWidth: 180,
            dialogSpan: 12,
            enumOptions: [],
            foreignQuery: '',
            checkboxActiveRaw: 'Y',
            checkboxInactiveRaw: 'N'
        },
        {
            field: 'code',
            columnName: 'code',
            label: '编码',
            tableProp: 'code',
            exportField: 'code',
            kind: 'text',
            valueType: 'string',
            required: true,
            primaryKey: false,
            searchable: true,
            sortable: false,
            textarea: false,
            minWidth: 180,
            dialogSpan: 12,
            enumOptions: [],
            foreignQuery: '',
            checkboxActiveRaw: 'Y',
            checkboxInactiveRaw: 'N'
        },
        {
            field: 'value',
            columnName: 'value',
            label: '对应值',
            tableProp: 'value',
            exportField: 'value',
            kind: 'text',
            valueType: 'string',
            required: true,
            primaryKey: false,
            searchable: false,
            sortable: false,
            textarea: false,
            minWidth: 220,
            dialogSpan: 24,
            enumOptions: [],
            foreignQuery: '',
            checkboxActiveRaw: 'Y',
            checkboxInactiveRaw: 'N'
        },
        {
            field: 'type',
            columnName: 'type',
            label: '类型',
            tableProp: 'type$',
            exportField: 'type$',
            kind: 'enum',
            valueType: 'number',
            required: true,
            primaryKey: false,
            searchable: false,
            sortable: false,
            textarea: false,
            minWidth: 120,
            dialogSpan: 12,
            enumOptions: [
                { rawValue: '1', label: 'String' },
                { rawValue: '2', label: 'JSON' }
            ],
            foreignQuery: '',
            checkboxActiveRaw: '1',
            checkboxInactiveRaw: '0'
        },
        {
            field: 'status',
            columnName: 'status',
            label: '状态',
            tableProp: 'status$',
            exportField: 'status$',
            kind: 'enum',
            valueType: 'number',
            required: true,
            primaryKey: false,
            searchable: false,
            sortable: false,
            textarea: false,
            minWidth: 120,
            dialogSpan: 12,
            enumOptions: [
                { rawValue: '1', label: '正常' },
                { rawValue: '0', label: '禁用' }
            ],
            foreignQuery: '',
            checkboxActiveRaw: '1',
            checkboxInactiveRaw: '0'
        },
        {
            field: 'selectType',
            columnName: 'select_type',
            label: '筛选类型',
            tableProp: 'selectType',
            exportField: 'selectType',
            kind: 'text',
            valueType: 'string',
            required: false,
            primaryKey: false,
            searchable: true,
            sortable: false,
            textarea: false,
            minWidth: 140,
            dialogSpan: 24,
            enumOptions: [],
            foreignQuery: '',
            checkboxActiveRaw: 'Y',
            checkboxInactiveRaw: 'N'
        }
    ];
    function isEmptyValue(value) {
        return value === null || value === undefined || value === '';
    }
@@ -877,11 +1025,17 @@
    }
    function isSearchableField(field) {
        return !!field && field.kind !== 'image' && !field.textarea;
        return !!field && field.searchable !== false && field.kind !== 'image' && !field.textarea;
    }
    function isSortableField(field) {
        if (!field) {
            return false;
        }
        if (field.sortable === true) {
            return true;
        }
        if (field.sortable === false) {
            return false;
        }
        if (field.primaryKey) {
@@ -1217,6 +1371,7 @@
                        mode: 'create',
                        submitting: false
                    },
                    tableHeight: 420,
                    layoutTimer: null,
                    tableResizeHandler: null,
                    dialogForm: createFormDefaults(),
@@ -1270,16 +1425,14 @@
                        };
                    });
                },
                tableHeight: function () {
                    return this.advancedFiltersVisible && this.hasAdvancedFilters
                        ? 'calc(100vh - 390px)'
                        : 'calc(100vh - 300px)';
                },
                formTarget: function () {
                    return this.dialogForm;
                },
                displayTarget: function () {
                    return this.dialogDisplay;
                },
                isDialogReadonly: function () {
                    return this.dialog.mode === 'detail';
                }
            },
            created: function () {
@@ -1304,12 +1457,26 @@
                }
            },
            methods: $.extend({}, sharedMethods, {
                calculateTableHeight: function () {
                    var viewportHeight = window.innerHeight || document.documentElement.clientHeight || 860;
                    var tableWrap = this.$refs.tableWrap;
                    var pagerBar = this.$refs.pagerBar;
                    if (!tableWrap) {
                        this.tableHeight = Math.max(360, viewportHeight - (this.advancedFiltersVisible ? 360 : 300));
                        return;
                    }
                    var tableTop = tableWrap.getBoundingClientRect().top;
                    var pagerHeight = pagerBar ? pagerBar.offsetHeight : 56;
                    var bottomGap = 56;
                    this.tableHeight = Math.max(320, Math.floor(viewportHeight - tableTop - pagerHeight - bottomGap));
                },
                requestTableLayout: function (delay) {
                    var self = this;
                    if (self.layoutTimer) {
                        clearTimeout(self.layoutTimer);
                    }
                    self.$nextTick(function () {
                        self.calculateTableHeight();
                        self.layoutTimer = setTimeout(function () {
                            var table = self.$refs.dataTable;
                            if (table && typeof table.doLayout === 'function') {
@@ -1475,8 +1642,23 @@
                        }
                    });
                },
                openDetailDialog: function (row) {
                    var self = this;
                    self.dialog.mode = 'detail';
                    self.dialog.visible = true;
                    self.$nextTick(function () {
                        self.resetDialogState();
                        fillFormFromRow(row, self.dialogForm, self.dialogDisplay);
                        if (self.$refs.dialogForm) {
                            self.$refs.dialogForm.clearValidate();
                        }
                    });
                },
                submitDialog: function () {
                    var self = this;
                    if (self.dialog.mode === 'detail') {
                        return;
                    }
                    if (!self.$refs.dialogForm) {
                        return;
                    }
@@ -1589,6 +1771,30 @@
                            self.$message.error('导出失败');
                        }
                    });
                },
                refreshCache: function () {
                    var self = this;
                    self.$confirm('确定刷新Redis缓存吗?', '提示', { type: 'warning' }).then(function () {
                        $.ajax({
                            url: baseUrl + '/config/refreshCache',
                            method: 'POST',
                            headers: self.authHeaders(),
                            success: function (res) {
                                if (self.handleForbidden(res)) {
                                    return;
                                }
                                if (!res || res.code !== 200) {
                                    self.$message.error((res && res.msg) ? res.msg : '刷新缓存失败');
                                    return;
                                }
                                self.$message.success('刷新缓存成功');
                                self.loadTable();
                            },
                            error: function () {
                                self.$message.error('刷新缓存失败');
                            }
                        });
                    }).catch(function () {});
                }
            })
        });
src/main/webapp/static/js/wrkMast/wrkMast.js
New file
@@ -0,0 +1,319 @@
(function () {
    var COLUMN_STORAGE_KEY = "wrk-mast-visible-columns";
    var DEFAULT_COLUMNS = [
        { key: "wrkNo", prop: "wrkNo", label: "工作号", width: 110, sortable: true, align: "center" },
        { key: "wmsWrkNo", prop: "wmsWrkNo", label: "WMS任务号", minWidth: 160, sortable: true },
        { key: "wrkSts$", prop: "wrkSts$", label: "工作状态", minWidth: 120, sortable: true },
        { key: "ioType$", prop: "ioType$", label: "任务类型", minWidth: 120, sortable: true },
        { key: "ioPri", prop: "ioPri", label: "优先级", width: 90, sortable: true, align: "center" },
        { key: "sourceStaNo", prop: "sourceStaNo", label: "源站", width: 90, sortable: true, align: "center" },
        { key: "staNo", prop: "staNo", label: "目标站", width: 90, sortable: true, align: "center" },
        { key: "sourceLocNo", prop: "sourceLocNo", label: "源库位", minWidth: 140, sortable: true },
        { key: "locNo", prop: "locNo", label: "目标库位", minWidth: 140, sortable: true },
        { key: "modiTime$", prop: "modiTime$", label: "修改时间", minWidth: 168, sortable: true },
        { key: "barcode", prop: "barcode", label: "托盘码", minWidth: 150, sortable: true },
        { key: "crnNo", prop: "crnNo", label: "堆垛机", width: 90, sortable: true, align: "center" },
        { key: "dualCrnNo", prop: "dualCrnNo", label: "双工位堆垛机", minWidth: 120, sortable: true, align: "center" },
        { key: "batch", prop: "batch", label: "批次", minWidth: 120, sortable: true },
        { key: "batchSeq", prop: "batchSeq", label: "批次序列", width: 100, sortable: true, align: "center" },
        { key: "systemMsg", prop: "systemMsg", label: "系统消息", minWidth: 220, sortable: false, showOverflow: false }
    ];
    function cloneSearchForm() {
        return {
            condition: "",
            wrk_no: "",
            wms_wrk_no: "",
            loc_no: "",
            source_loc_no: "",
            crn_no: "",
            dual_crn_no: ""
        };
    }
    function loadStoredColumns() {
        try {
            var raw = localStorage.getItem(COLUMN_STORAGE_KEY);
            var parsed = raw ? JSON.parse(raw) : null;
            if (!parsed || !parsed.length) {
                return DEFAULT_COLUMNS.map(function (column) { return column.key; });
            }
            return DEFAULT_COLUMNS.map(function (column) { return column.key; }).filter(function (key) {
                return parsed.indexOf(key) > -1;
            });
        } catch (e) {
            return DEFAULT_COLUMNS.map(function (column) { return column.key; });
        }
    }
    function saveVisibleColumns(keys) {
        localStorage.setItem(COLUMN_STORAGE_KEY, JSON.stringify(keys));
    }
    new Vue({
        el: "#app",
        data: function () {
            return {
                loading: false,
                advancedVisible: false,
                columnPopoverVisible: false,
                tableData: [],
                currentPage: 1,
                pageSize: 30,
                pageSizes: [16, 30, 50, 100, 150, 200],
                pageTotal: 0,
                tableHeight: 520,
                searchForm: cloneSearchForm(),
                sortState: {
                    prop: "",
                    order: ""
                },
                columnDefs: DEFAULT_COLUMNS,
                visibleColumnKeys: loadStoredColumns(),
                layoutTimer: null
            };
        },
        computed: {
            visibleColumns: function () {
                var keys = this.visibleColumnKeys;
                return this.columnDefs.filter(function (column) {
                    return keys.indexOf(column.key) > -1;
                });
            },
            tableRenderKey: function () {
                return this.visibleColumnKeys.join("|");
            }
        },
        created: function () {
            this.loadList();
        },
        mounted: function () {
            this.updateTableHeight();
            window.addEventListener("resize", this.handleResize);
        },
        beforeDestroy: function () {
            window.removeEventListener("resize", this.handleResize);
            if (this.layoutTimer) {
                clearTimeout(this.layoutTimer);
                this.layoutTimer = null;
            }
        },
        methods: {
            buildQueryParams: function () {
                var data = {
                    curr: this.currentPage,
                    limit: this.pageSize
                };
                var key;
                for (key in this.searchForm) {
                    if (Object.prototype.hasOwnProperty.call(this.searchForm, key) && this.searchForm[key] !== "" && this.searchForm[key] !== null) {
                        data[key] = this.searchForm[key];
                    }
                }
                if (this.sortState.prop && this.sortState.order) {
                    data.orderByField = this.sortState.prop;
                    data.orderByType = this.sortState.order === "ascending" ? "asc" : "desc";
                }
                return data;
            },
            loadList: function () {
                var vm = this;
                vm.loading = true;
                $.ajax({
                    url: baseUrl + "/wrkMast/list/auth",
                    headers: { token: localStorage.getItem("token") },
                    method: "GET",
                    data: vm.buildQueryParams(),
                    success: function (res) {
                        if (res.code === 200) {
                            vm.tableData = (res.data && res.data.records) || [];
                            vm.pageTotal = (res.data && res.data.total) || 0;
                            vm.scheduleTableLayout();
                            return;
                        }
                        if (res.code === 403) {
                            top.location.href = baseUrl + "/";
                            return;
                        }
                        vm.$message.error(res.msg || "任务列表加载失败");
                    },
                    error: function () {
                        vm.$message.error("任务列表加载失败");
                    },
                    complete: function () {
                        vm.loading = false;
                    }
                });
            },
            handleSearch: function () {
                this.currentPage = 1;
                this.loadList();
            },
            handleReset: function () {
                this.searchForm = cloneSearchForm();
                this.currentPage = 1;
                this.sortState = {
                    prop: "",
                    order: ""
                };
                this.loadList();
                this.scheduleTableLayout();
            },
            toggleAdvanced: function () {
                this.advancedVisible = !this.advancedVisible;
                this.updateTableHeight();
                this.scheduleTableLayout();
            },
            handleSizeChange: function (size) {
                this.pageSize = size;
                this.currentPage = 1;
                this.loadList();
            },
            handleCurrentChange: function (page) {
                this.currentPage = page;
                this.loadList();
            },
            handleSortChange: function (sort) {
                this.sortState = {
                    prop: sort.prop || "",
                    order: sort.order || ""
                };
                this.currentPage = 1;
                this.loadList();
            },
            isColumnVisible: function (key) {
                return this.visibleColumnKeys.indexOf(key) > -1;
            },
            toggleColumn: function (key, checked) {
                var next = this.visibleColumnKeys.slice();
                var index = next.indexOf(key);
                if (checked && index === -1) {
                    next.push(key);
                }
                if (!checked && index > -1) {
                    if (next.length === 1) {
                        this.$message.warning("至少保留一列");
                        return;
                    }
                    next.splice(index, 1);
                }
                this.visibleColumnKeys = this.columnDefs.map(function (column) {
                    return column.key;
                }).filter(function (columnKey) {
                    return next.indexOf(columnKey) > -1;
                });
                saveVisibleColumns(this.visibleColumnKeys);
                this.scheduleTableLayout();
            },
            showAllColumns: function () {
                this.visibleColumnKeys = this.columnDefs.map(function (column) {
                    return column.key;
                });
                saveVisibleColumns(this.visibleColumnKeys);
                this.scheduleTableLayout();
            },
            resetColumns: function () {
                this.visibleColumnKeys = DEFAULT_COLUMNS.map(function (column) {
                    return column.key;
                });
                saveVisibleColumns(this.visibleColumnKeys);
                this.scheduleTableLayout();
            },
            handleRowCommand: function (command, row) {
                if (command === "complete") {
                    this.completeTask(row);
                    return;
                }
                if (command === "cancel") {
                    this.cancelTask(row);
                }
            },
            completeTask: function (row) {
                var vm = this;
                vm.$confirm("确定完成该任务吗?", "提示", {
                    type: "warning",
                    confirmButtonText: "确定",
                    cancelButtonText: "取消"
                }).then(function () {
                    $.ajax({
                        url: baseUrl + "/openapi/completeTask",
                        contentType: "application/json",
                        headers: { token: localStorage.getItem("token") },
                        data: JSON.stringify({ wrkNo: row.wrkNo }),
                        method: "POST",
                        success: function (res) {
                            if (res.code === 200) {
                                vm.$message.success("完成成功");
                                vm.loadList();
                                return;
                            }
                            if (res.code === 403) {
                                top.location.href = baseUrl + "/";
                                return;
                            }
                            vm.$message.error(res.msg || "完成失败");
                        },
                        error: function () {
                            vm.$message.error("完成失败");
                        }
                    });
                }).catch(function () {});
            },
            cancelTask: function (row) {
                var vm = this;
                vm.$confirm("确定取消该任务吗?", "提示", {
                    type: "warning",
                    confirmButtonText: "确定",
                    cancelButtonText: "取消"
                }).then(function () {
                    $.ajax({
                        url: baseUrl + "/openapi/cancelTask",
                        contentType: "application/json",
                        headers: { token: localStorage.getItem("token") },
                        data: JSON.stringify({ wrkNo: row.wrkNo }),
                        method: "POST",
                        success: function (res) {
                            if (res.code === 200) {
                                vm.$message.success("取消成功");
                                vm.loadList();
                                return;
                            }
                            if (res.code === 403) {
                                top.location.href = baseUrl + "/";
                                return;
                            }
                            vm.$message.error(res.msg || "取消失败");
                        },
                        error: function () {
                            vm.$message.error("取消失败");
                        }
                    });
                }).catch(function () {});
            },
            updateTableHeight: function () {
                var viewport = window.innerHeight || document.documentElement.clientHeight || 860;
                this.tableHeight = Math.max(340, viewport - (this.advancedVisible ? 276 : 222));
            },
            scheduleTableLayout: function () {
                var vm = this;
                vm.updateTableHeight();
                vm.$nextTick(function () {
                    if (vm.layoutTimer) {
                        clearTimeout(vm.layoutTimer);
                    }
                    vm.layoutTimer = setTimeout(function () {
                        if (vm.$refs.dataTable && typeof vm.$refs.dataTable.doLayout === "function") {
                            vm.$refs.dataTable.doLayout();
                        }
                    }, 50);
                });
            },
            handleResize: function () {
                this.scheduleTableLayout();
            }
        }
    });
})();
src/main/webapp/views/ai/diagnosis.html
@@ -644,7 +644,7 @@
  <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
  <script type="text/javascript" src="../../static/vue/element/element.js"></script>
  <script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
  <script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
  <script src="../../static/js/marked.min.js"></script>
  <script src="../../static/js/purify.min.js"></script>
  <script>
src/main/webapp/views/ai/llm_config.html
@@ -472,7 +472,7 @@
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script>
  new Vue({
    el: '#app',
src/main/webapp/views/basOutStationArea/basOutStationArea.html
@@ -80,7 +80,7 @@
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/basOutStationArea/basOutStationArea.js"></script>
</body>
</html>
src/main/webapp/views/basStationDevice/basStationDevice.html
@@ -263,7 +263,7 @@
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/basStationDevice/basStationDevice.js"></script>
</body>
</html>
src/main/webapp/views/config/config.html
@@ -22,6 +22,7 @@
        html,
        body {
            margin: 0;
            height: 100%;
            min-height: 100%;
            color: var(--text-main);
            font-family: "Avenir Next", "PingFang SC", "Microsoft YaHei", sans-serif;
@@ -34,12 +35,16 @@
        .page-shell {
            max-width: 1700px;
            margin: 0 auto;
            height: 100%;
            padding: 14px;
            box-sizing: border-box;
            display: flex;
        }
        .card-shell {
            position: relative;
            flex: 1 1 auto;
            display: flex;
            border-radius: 24px;
            border: 1px solid var(--card-border);
            background:
@@ -53,6 +58,12 @@
        .card-body {
            position: relative;
            z-index: 1;
            flex: 1 1 auto;
            min-height: 0;
            min-width: 0;
            width: 100%;
            display: flex;
            flex-direction: column;
        }
        .list-toolbar {
@@ -164,6 +175,11 @@
        .table-wrap {
            padding: 10px 16px;
            flex: 1 1 auto;
            min-height: 0;
            min-width: 0;
            width: 100%;
            display: flex;
        }
        .table-shell {
@@ -171,6 +187,11 @@
            overflow: hidden;
            border: 1px solid rgba(217, 227, 238, 0.98);
            background: rgba(255, 255, 255, 0.95);
            flex: 1 1 auto;
            min-height: 0;
            min-width: 0;
            width: 100%;
            max-width: 100%;
        }
        .table-shell .el-table {
@@ -436,6 +457,7 @@
                            <el-button slot="reference" size="small" plain icon="el-icon-setting">列设置</el-button>
                        </el-popover>
                        <el-button size="small" plain icon="el-icon-download" :loading="exporting" @click="exportRows">导出</el-button>
                        <el-button size="small" plain type="warning" icon="el-icon-refresh-right" @click="refreshCache">刷新缓存</el-button>
                    </div>
                </div>
            </div>
@@ -510,7 +532,7 @@
                </div>
            </el-collapse-transition>
            <div class="table-wrap">
            <div ref="tableWrap" class="table-wrap">
                <div class="table-shell">
                    <el-table
                        ref="dataTable"
@@ -552,8 +574,9 @@
                                <span v-else>{{ valueOrDash(getTableValue(scope.row, field)) }}</span>
                            </template>
                        </el-table-column>
                        <el-table-column label="操作" width="160" fixed="right" align="center">
                        <el-table-column label="操作" width="190" fixed="right" align="center">
                            <template slot-scope="scope">
                                <el-button type="text" @click="openDetailDialog(scope.row)">详情</el-button>
                                <el-button type="text" @click="openEditDialog(scope.row)">修改</el-button>
                                <el-button type="text" style="color:#f56c6c;" @click="removeRows([scope.row[primaryKeyField]])">删除</el-button>
                            </template>
@@ -562,7 +585,7 @@
                </div>
            </div>
            <div class="pager-bar">
            <div ref="pagerBar" class="pager-bar">
                <el-pagination
                    small
                    background
@@ -580,7 +603,7 @@
    <el-dialog
        class="dialog-panel"
        :title="dialog.mode === 'create' ? '新增 Config' : '修改 Config'"
        :title="dialog.mode === 'create' ? '新增 Config' : (dialog.mode === 'detail' ? '详情 Config' : '修改 Config')"
        :visible.sync="dialog.visible"
        width="760px"
        :close-on-click-modal="false">
@@ -594,7 +617,7 @@
                <el-col
                    v-for="field in editableFields"
                    :key="'dialog-' + field.field"
                    :span="field.textarea || field.kind === 'image' ? 24 : 12">
                    :span="field.dialogSpan || (field.textarea || field.kind === 'image' ? 24 : 12)">
                    <el-form-item :label="field.label" :prop="field.field">
                        <el-date-picker
                            v-if="field.kind === 'date'"
@@ -602,12 +625,14 @@
                            type="datetime"
                            value-format="yyyy-MM-dd HH:mm:ss"
                            :placeholder="'请选择' + field.label"
                            :disabled="isDialogReadonly"
                            style="width: 100%;">
                        </el-date-picker>
                        <el-select
                            v-else-if="field.kind === 'enum'"
                            v-model="dialogForm[field.field]"
                            clearable
                            :disabled="isDialogReadonly"
                            :placeholder="'请选择' + field.label"
                            style="width: 100%;">
                            <el-option
@@ -622,6 +647,7 @@
                            v-model="dialogDisplay[field.field]"
                            :fetch-suggestions="getSuggestionFetcher(field)"
                            :placeholder="'请输入' + field.label"
                            :disabled="isDialogReadonly"
                            style="width: 100%;"
                            @select="handleForeignSelect(field, $event)"
                            @input="handleForeignInput(field)">
@@ -633,6 +659,7 @@
                        <el-switch
                            v-else-if="field.kind === 'checkbox'"
                            v-model="dialogForm[field.field]"
                            :disabled="isDialogReadonly"
                            :active-value="normalizeOptionValue(field, field.checkboxActiveRaw)"
                            :inactive-value="normalizeOptionValue(field, field.checkboxInactiveRaw)"
                            active-color="#13ce66"
@@ -643,11 +670,13 @@
                            v-model.trim="dialogForm[field.field]"
                            type="textarea"
                            :rows="3"
                            :disabled="isDialogReadonly"
                            :placeholder="'请输入' + field.label">
                        </el-input>
                        <el-input
                            v-else
                            v-model.trim="dialogForm[field.field]"
                            :disabled="isDialogReadonly"
                            :placeholder="'请输入' + field.label">
                        </el-input>
                    </el-form-item>
@@ -656,7 +685,7 @@
        </el-form>
        <div slot="footer" class="dialog-footer">
            <el-button @click="dialog.visible = false">取消</el-button>
            <el-button type="primary" :loading="dialog.submitting" @click="submitDialog">保存</el-button>
            <el-button v-if="!isDialogReadonly" type="primary" :loading="dialog.submitting" @click="submitDialog">保存</el-button>
        </div>
    </el-dialog>
</div>
@@ -665,6 +694,6 @@
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
<script type="text/javascript" src="../../static/js/config/config.js?v=20260310" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/config/config.js" charset="utf-8"></script>
</body>
</html>
src/main/webapp/views/debugParam/debugParam.html
@@ -6,7 +6,7 @@
        <title>调试参数</title>
        <link rel="stylesheet" href="../../static/vue/element/element.css">
        <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
        <script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1"></script>
        <script type="text/javascript" src="../../static/js/common.js"></script>
        <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
        <script type="text/javascript" src="../../static/vue/element/element.js"></script>
        <style>
src/main/webapp/views/detail.html
@@ -275,7 +275,7 @@
</body>
<script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../static/js/tools/md5.js"></script>
<script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1"></script>
<script type="text/javascript" src="../static/js/common.js"></script>
<script type="text/javascript" src="../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../static/vue/element/element.js"></script>
<script type="text/javascript" src="../static/js/detail/detail.js?v=20260310_detail_vue3"></script>
src/main/webapp/views/deviceLogs/deviceLogs.html
@@ -203,7 +203,7 @@
</div>
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script src="../../static/vue/js/vue.min.js"></script>
<script src="../../static/vue/element/element.js"></script>
<script src="../../components/MonitorCardKit.js"></script>
src/main/webapp/views/index.html
@@ -858,7 +858,7 @@
</div>
<script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1"></script>
<script type="text/javascript" src="../static/js/common.js"></script>
<script type="text/javascript" src="../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../static/vue/element/element.js"></script>
<script>
@@ -1136,17 +1136,7 @@
        PROFILE_TAB_CONFIG.title = profileConfig.title;
        PROFILE_TAB_CONFIG.group = profileConfig.group;
        for (i = 0; i < this.tabs.length; i++) {
          if (this.isHomeTabUrl(this.tabs[i].url)) {
            this.tabs[i].title = homeConfig.title;
            this.tabs[i].group = homeConfig.group;
            this.tabs[i].home = true;
          } else if (this.resolveViewSrc(this.tabs[i].url) === this.resolveViewSrc(profileConfig.url)) {
            this.tabs[i].title = profileConfig.title;
            this.tabs[i].group = profileConfig.group;
          } else {
            this.tabs[i].title = this.translateTabTitle(this.tabs[i].title);
            this.tabs[i].group = this.tl(this.tabs[i].group);
          }
          this.syncTabMeta(this.tabs[i], homeConfig, profileConfig);
        }
        this.updateDocumentTitle(this.activeTabTitle);
        this.persistTabs();
@@ -1183,6 +1173,56 @@
      },
      translateTabTitle: function (title) {
        return this.tl(title);
      },
      findMenuMeta: function (tab) {
        var normalizedUrl;
        var i;
        var j;
        var group;
        var item;
        if (!tab) {
          return null;
        }
        normalizedUrl = this.resolveViewSrc(tab.url);
        for (i = 0; i < this.menus.length; i++) {
          group = this.menus[i];
          for (j = 0; j < group.subMenu.length; j++) {
            item = group.subMenu[j];
            if ((tab.menuKey && item.tabKey === tab.menuKey) || item.url === normalizedUrl) {
              return {
                group: group,
                item: item
              };
            }
          }
        }
        return null;
      },
      syncTabMeta: function (tab, homeConfig, profileConfig) {
        var menuMeta;
        if (!tab) {
          return;
        }
        if (this.isHomeTabUrl(tab.url)) {
          tab.title = homeConfig.title;
          tab.group = homeConfig.group;
          tab.home = true;
          return;
        }
        if (this.resolveViewSrc(tab.url) === this.resolveViewSrc(profileConfig.url)) {
          tab.title = profileConfig.title;
          tab.group = profileConfig.group;
          return;
        }
        menuMeta = this.findMenuMeta(tab);
        if (menuMeta) {
          tab.title = menuMeta.item.name;
          tab.group = menuMeta.group.menu;
          tab.menuKey = menuMeta.item.tabKey || tab.menuKey;
          return;
        }
        tab.title = this.translateTabTitle(tab.title);
        tab.group = this.tl(tab.group);
      },
      updateDocumentTitle: function (title) {
        document.title = title + " - " + this.t("app.title");
@@ -1473,7 +1513,7 @@
        script = frameDocument.createElement("script");
        script.id = "wcs-i18n-bridge-script";
        script.type = "text/javascript";
        script.src = baseUrl + "/static/js/common.js?v=20260309_i18n_fix1";
        script.src = baseUrl + "/static/js/common.js";
        script.onload = applyFrameI18n;
        frameDocument.head.appendChild(script);
      },
@@ -1742,6 +1782,7 @@
            that.menuLoading = false;
            if (res.code === 200) {
              that.menus = that.normalizeMenuData(res.data || []);
              that.refreshI18nState();
              that.syncMenuStateByUrl(that.activeTabUrl);
            } else if (res.code === 403) {
              top.location.href = baseUrl + "/login";
src/main/webapp/views/locMap/locMap.html
@@ -5,7 +5,7 @@
  <title>库位地图</title>
  <link rel="stylesheet" href="../../static/vue/element/element.css">
  <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
  <script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1"></script>
  <script type="text/javascript" src="../../static/js/common.js"></script>
  <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
  <script type="text/javascript" src="../../static/vue/element/element.js"></script>
  <script src="../../static/js/gsap.min.js"></script>
src/main/webapp/views/login.html
@@ -517,7 +517,7 @@
</body>
<script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../static/js/tools/md5.js"></script>
<script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1"></script>
<script type="text/javascript" src="../static/js/common.js"></script>
<script type="text/javascript" src="../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../static/vue/element/element.js"></script>
<script type="text/javascript" src="../static/js/login/login.js?v=20260310_login_vue"></script>
src/main/webapp/views/notifyReport/notifyReport.html
@@ -544,7 +544,7 @@
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script>
  new Vue({
    el: '#app',
src/main/webapp/views/password.html
@@ -96,7 +96,7 @@
</body>
<script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../static/js/tools/md5.js"></script>
<script type="text/javascript" src="../static/js/common.js?v=20260309_i18n_fix1"></script>
<script type="text/javascript" src="../static/js/common.js"></script>
<script type="text/javascript" src="../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../static/vue/element/element.js"></script>
<script type="text/javascript" src="../static/js/password/password.js?v=20260310_password_vue"></script>
src/main/webapp/views/role/role_power_detail.html
@@ -31,6 +31,6 @@
</body>
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1"></script>
<script type="text/javascript" src="../../static/js/common.js"></script>
<script type="text/javascript" src="../../static/js/role/rolePower.js" charset="utf-8"></script>
</html>
src/main/webapp/views/watch/console.html
@@ -508,7 +508,7 @@
                </style>
                <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
                <script type="text/javascript" src="../../static/js/handlebars/handlebars-v4.5.3.js"></script>
            <script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1"></script>
            <script type="text/javascript" src="../../static/js/common.js"></script>
            <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
            <script type="text/javascript" src="../../static/vue/element/element.js"></script>
            <script src="../../static/js/gsap.min.js"></script>
src/main/webapp/views/watch/console_html.html
@@ -9,7 +9,7 @@
        <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
        <script type="text/javascript" src="../../static/layui/layui.js"></script>
        <script type="text/javascript" src="../../static/js/handlebars/handlebars-v4.5.3.js"></script>
        <script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1"></script>
        <script type="text/javascript" src="../../static/js/common.js"></script>
        <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
        <script type="text/javascript" src="../../static/vue/element/element.js"></script>
    </head>
src/main/webapp/views/watch/console_pixijs.html
@@ -11,7 +11,7 @@
  <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
  <script type="text/javascript" src="../../static/layui/layui.js"></script>
  <script type="text/javascript" src="../../static/js/handlebars/handlebars-v4.5.3.js"></script>
  <script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1"></script>
  <script type="text/javascript" src="../../static/js/common.js"></script>
  <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
  <script type="text/javascript" src="../../static/vue/element/element.js"></script>
  <script src="../../static/js/gsap.min.js"></script>
src/main/webapp/views/watch/stationColorConfig.html
@@ -284,7 +284,7 @@
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
<script type="text/javascript" src="../../static/js/watch/stationColorConfig.js" charset="utf-8"></script>
</body>
</html>
src/main/webapp/views/wrkMast/wrkMast.html
@@ -1,425 +1,421 @@
<!DOCTYPE html>
<html lang="en">
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>任务管理</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="../../static/vue/element/element.css">
    <link rel="stylesheet" href="../../static/css/cool.css">
    <style>
        :root {
            --card-bg: rgba(255, 255, 255, 0.94);
            --card-border: rgba(216, 226, 238, 0.95);
            --text-main: #243447;
        }
    <head>
        <meta charset="UTF-8">
        <title>任务管理</title>
        <link rel="stylesheet" href="../../static/vue/element/element.css">
        <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
        <script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1"></script>
        <script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
        <script type="text/javascript" src="../../static/vue/element/element.js"></script>
        <style>
            [v-cloak] {
                display: none;
            }
            .el-table .success-row {
                background: #b6ff8e;
            }
        </style>
    </head>
        [v-cloak] {
            display: none;
        }
    <body>
        <div id="app" v-cloak style="display: flex;justify-content: center;flex-wrap: wrap;">
            <div style="width: 100%;">
                <el-card class="box-card">
                    <el-form :inline="true" :model="tableSearchParam" class="demo-form-inline">
                        <el-form-item label="">
                            <el-input v-model="tableSearchParam.wrk_no" placeholder="工作号"></el-input>
                        </el-form-item>
                        <el-form-item label="">
                            <el-input v-model="tableSearchParam.wms_wrk_no" placeholder="WMS工作号"></el-input>
                        </el-form-item>
                        <el-form-item label="">
                            <el-input v-model="tableSearchParam.source_loc_no" placeholder="源库位"></el-input>
                        </el-form-item>
                        <el-form-item label="">
                            <el-input v-model="tableSearchParam.loc_no" placeholder="目标库位"></el-input>
                        </el-form-item>
                        <el-form-item label="">
                            <el-input v-model="tableSearchParam.crn_no" placeholder="堆垛机"></el-input>
                        </el-form-item>
                        <el-form-item label="">
                            <el-input v-model="tableSearchParam.dual_crn_no" placeholder="双工位堆垛机"></el-input>
                        </el-form-item>
                        <el-form-item>
                            <el-button type="primary" @click="getTableData">查询</el-button>
                            <el-button type="primary" @click="resetParam">重置</el-button>
                        </el-form-item>
                    </el-form>
                    <el-table ref="singleTable" :data="tableData" style="width: 100%;">
                        <el-table-column property="wrkNo" label="工作号">
                        </el-table-column>
                        <el-table-column property="wmsWrkNo" label="WMS任务号">
                        </el-table-column>
                        <el-table-column property="wrkSts$" label="工作状态">
                        </el-table-column>
                        <el-table-column property="ioType$" label="任务类型">
                        </el-table-column>
                        <el-table-column property="ioPri" label="优先级">
                        </el-table-column>
                        <el-table-column property="sourceStaNo" label="源站">
                        </el-table-column>
                        <el-table-column property="staNo" label="目标站">
                        </el-table-column>
                        <el-table-column property="sourceLocNo" label="源库位">
                        </el-table-column>
                        <el-table-column property="locNo" label="目标库位">
                        </el-table-column>
                        <el-table-column property="modiTime$" label="时间">
                        </el-table-column>
                        <el-table-column property="barcode" label="托盘码">
                        </el-table-column>
                        <el-table-column property="crnNo" label="堆垛机">
                        </el-table-column>
                        <el-table-column property="dualCrnNo" label="双工位堆垛机">
                        </el-table-column>
                        <el-table-column property="batch" label="批次">
                        </el-table-column>
                        <el-table-column property="batchSeq" label="批次序列">
                        </el-table-column>
                        <el-table-column property="systemMsg" label="系统消息">
                        </el-table-column>
                        <el-table-column label="操作" width="100">
                            <template slot-scope="scope">
                                <el-dropdown @command="(command)=>{handleCommand(command, scope.row)}">
                                    <el-button icon="el-icon-more" size="mini" type="primary"></el-button>
                                    <el-dropdown-menu slot="dropdown">
<!--                                        <el-dropdown-item command="change">修改</el-dropdown-item>-->
                                        <el-dropdown-item command="complete">完成</el-dropdown-item>
                                        <el-dropdown-item command="cancel">取消</el-dropdown-item>
<!--                                        <el-dropdown-item command="shuttleCommand">穿梭车指令</el-dropdown-item>-->
                                    </el-dropdown-menu>
                                </el-dropdown>
                            </template>
                        </el-table-column>
                    </el-table>
        html,
        body {
            margin: 0;
            min-height: 100%;
            color: var(--text-main);
            font-family: "Avenir Next", "PingFang SC", "Microsoft YaHei", sans-serif;
            background:
                radial-gradient(1000px 420px at 0% -10%, rgba(44, 107, 193, 0.12), transparent 56%),
                radial-gradient(900px 400px at 100% 0%, rgba(28, 150, 126, 0.10), transparent 58%),
                linear-gradient(180deg, #f2f6fb 0%, #f8fafc 100%);
        }
                    <div style="margin-top: 10px;">
                        <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
                            :current-page="currentPage" :page-sizes="pageSizes" :page-size="pageSize"
                            layout="total, sizes, prev, pager, next, jumper" :total="pageTotal">
                        </el-pagination>
                    </div>
                </el-card>
            </div>
        .page-shell {
            max-width: 1800px;
            margin: 0 auto;
            padding: 14px;
            box-sizing: border-box;
        }
            <el-dialog :title="shuttleCommandTitle" :visible.sync="shuttleCommandVisible">
                <el-table ref="singleTable" :data="shuttleCommandData.commands" style="width: 100%;" :row-class-name="tableRowClassName">
                    <el-table-column property="mode$" label="命令类型">
                    </el-table-column>
                    <el-table-column property="start" label="起点">
                        <template slot-scope="scope">
                            <div v-if="scope.row.nodes">
                                x:{{ scope.row.nodes[0].x }}
                                y:{{ scope.row.nodes[0].y }}
                                z:{{ scope.row.nodes[0].z }}
                            </div>
                        </template>
                    </el-table-column>
                    <el-table-column property="target" label="终点">
                        <template slot-scope="scope">
                            <div v-if="scope.row.nodes">
                                x:{{ scope.row.nodes[scope.row.nodes.length-1].x }}
                                y:{{ scope.row.nodes[scope.row.nodes.length-1].y }}
                                z:{{ scope.row.nodes[scope.row.nodes.length-1].z }}
                            </div>
                        </template>
                    </el-table-column>
                    <el-table-column property="taskNo" label="任务号">
                    </el-table-column>
                    <el-table-column property="shuttleNo" label="穿梭车">
                    </el-table-column>
                    <el-table-column property="complete" label="是否完成">
                        <template slot-scope="scope">
                            <el-switch
                                    v-model="scope.row.complete"
                                    active-color="#13ce66"
                                    @change="changeComplete(scope)">
                            </el-switch>
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" width="100">
                        <template slot-scope="scope">
                            <el-button @click="shuttleCommandRollback(scope)" size="mini">回退指令</el-button>
                        </template>
                    </el-table-column>
                </el-table>
                <div slot="footer" class="dialog-footer">
                    <el-button @click="shuttleCommandVisible = false">关闭</el-button>
                </div>
            </el-dialog>
        .card-shell {
            position: relative;
            border-radius: 24px;
            border: 1px solid var(--card-border);
            background:
                radial-gradient(760px 220px at -8% 0%, rgba(43, 117, 196, 0.05), transparent 55%),
                radial-gradient(680px 200px at 108% 10%, rgba(24, 150, 129, 0.05), transparent 58%),
                var(--card-bg);
            box-shadow: 0 16px 32px rgba(44, 67, 96, 0.08);
            overflow: hidden;
        }
        </div>
        <script>
            var app = new Vue({
                el: '#app',
                data: {
                    tableData: [],
                    currentPage: 1,
                    pageSizes: [16, 30, 50, 100, 150, 200],
                    pageSize: 30,
                    pageTotal: 0,
                    tableSearchParam: {
                        wrk_no: null,
                        wms_wrk_no: null,
                    },
                    shuttleCommandVisible: false,
                    shuttleCommandData: {
                        assignCommand: {
                            commands: []
                        },
                        commandStep: 0
                    },
                    shuttleCommandWrkNo: null,
                    shuttleCommandLabelWidth: '80px',
                    shuttleCommandTitle: ''
                },
                created() {
                    this.init()
                },
                methods: {
                    init() {
                        this.getTableData()
                    },
                    getTableData() {
                        let that = this;
                        let data = JSON.parse(JSON.stringify(this.tableSearchParam))
                        data.curr = this.currentPage
                        data.limit = this.pageSize
                        if (this.tableSearchParam.datetime != null) {
                            data.datetime = null
                            data.create_time = this.tableSearchParam.datetime[0] + " - " + this.tableSearchParam.datetime[1]
                        }
                        $.ajax({
                            url: baseUrl + "/wrkMast/list/auth",
                            headers: {
                                'token': localStorage.getItem('token')
                            },
                            data: data,
                            dataType: 'json',
                            contentType: 'application/json;charset=UTF-8',
                            method: 'GET',
                            success: function(res) {
                                if (res.code == 200) {
                                    that.tableData = res.data.records
                                    that.pageTotal = res.data.total
                                } else if (res.code === 403) {
                                    top.location.href = baseUrl + "/";
                                } else {
                                    that.$message({
                                        message: res.msg,
                                        type: 'error'
                                    });
                                }
                            }
                        });
                    },
                    handleSizeChange(val) {
                        console.log(`每页 ${val} 条`);
                        this.pageSize = val
                        this.getTableData()
                    },
                    handleCurrentChange(val) {
                        console.log(`当前页: ${val}`);
                        this.currentPage = val
                        this.getTableData()
                    },
                    resetParam() {
                        this.tableSearchParam = {
                            task_no: null,
                            status: null,
                            wrk_no: null
                        }
                        this.getTableData()
                    },
                    handleCommand(command, row) {
                        switch (command) {
                            case "complete":
                                this.completeTask(row)
                                break;
                            case "cancel":
                                this.cancelTask(row)
                                break;
                            case "shuttleCommand":
                                this.showShuttleCommand(row.wrkNo)
                                break;
                        }
                    },
                    showShuttleCommand(wrkNo){
                        let that = this;
                        $.ajax({
                            url: baseUrl + "/shuttle/command/query",
                            headers: {
                                'token': localStorage.getItem('token')
                            },
                            data: {
                                wrkNo: wrkNo
                            },
                            method: 'GET',
                            success: function(res) {
                                if (res.code == 200) {
                                    console.log(res)
                                    that.shuttleCommandVisible = true;
                                    that.shuttleCommandData = res.data;
                                    that.shuttleCommandWrkNo = wrkNo;
                                } else if (res.code === 403) {
                                    top.location.href = baseUrl + "/";
                                } else {
                                    that.$message({
                                        message: res.msg,
                                        type: 'error'
                                    });
                                }
                            }
                        });
                    },
                    completeTask(row) {
                        let that = this
                        this.$confirm('确定完成该任务吗?', '提示', {
                            confirmButtonText: '确定',
                            cancelButtonText: '取消',
                            type: 'warning'
                        }).then(() => {
                            $.ajax({
                                url: baseUrl + "/openapi/completeTask",
                                contentType: 'application/json',
                                headers: {
                                    'token': localStorage.getItem('token')
                                },
                                data: JSON.stringify({
                                    wrkNo: row.wrkNo,
                                }),
                                method: 'POST',
                                success: function(res) {
                                    if (res.code == 200) {
                                        that.$message({
                                            message: "完成成功",
                                            type: 'success'
                                        });
                                        that.getTableData()
                                    } else if (res.code === 403) {
                                        top.location.href = baseUrl + "/";
                                    } else {
                                        that.$message({
                                            message: res.msg,
                                            type: 'error'
                                        });
                                    }
                                }
                            });
                        }).catch(() => {
                            // this.$message({
                            //     type: 'info',
                            //     message: '已取消删除'
                            // });
                        });
                    },
                    cancelTask(row) {
                        let that = this
                        this.$confirm('确定取消该任务吗?', '提示', {
                            confirmButtonText: '确定',
                            cancelButtonText: '取消',
                            type: 'warning'
                        }).then(() => {
                            $.ajax({
                                url: baseUrl + "/openapi/cancelTask",
                                contentType: 'application/json',
                                headers: {
                                    'token': localStorage.getItem('token')
                                },
                                data: JSON.stringify({
                                    wrkNo: row.wrkNo,
                                }),
                                method: 'POST',
                                success: function(res) {
                                    if (res.code == 200) {
                                        that.$message({
                                            message: "取消成功",
                                            type: 'success'
                                        });
                                        that.getTableData()
                                    } else if (res.code === 403) {
                                        top.location.href = baseUrl + "/";
                                    } else {
                                        that.$message({
                                            message: res.msg,
                                            type: 'error'
                                        });
                                    }
                                }
                            });
                        }).catch(() => {
                            // this.$message({
                            //     type: 'info',
                            //     message: '已取消删除'
                            // });
                        });
                    },
                    tableRowClassName({row, rowIndex}) {
                        if (rowIndex === this.shuttleCommandData.commandStep) {
                            return 'success-row';
                        }
                        return '';
                    },
                    shuttleCommandRollback(scope) {
                        let that = this;
                        let idx = scope.$index;
                        $.ajax({
                            url: baseUrl + "/shuttle/command/rollback",
                            headers: {
                                'token': localStorage.getItem('token')
                            },
                            data: {
                                wrkNo: that.shuttleCommandWrkNo,
                                commandStep: idx
                            },
                            method: 'GET',
                            success: function(res) {
                                if (res.code == 200) {
                                    that.showShuttleCommand(that.shuttleCommandWrkNo)
                                } else if (res.code === 403) {
                                    top.location.href = baseUrl + "/";
                                } else {
                                    that.$message({
                                        message: res.msg,
                                        type: 'error'
                                    });
                                }
                            }
                        });
                    },
                    changeComplete(scope) {
                        let that = this;
                        let idx = scope.$index;
                        $.ajax({
                            url: baseUrl + "/shuttle/command/completeSwitch",
                            headers: {
                                'token': localStorage.getItem('token')
                            },
                            data: {
                                wrkNo: that.shuttleCommandWrkNo,
                                commandStep: idx,
                                complete: scope.row.complete ? 1 : 0
                            },
                            method: 'GET',
                            success: function(res) {
                                if (res.code == 200) {
                                    that.showShuttleCommand(that.shuttleCommandWrkNo)
                                } else if (res.code === 403) {
                                    top.location.href = baseUrl + "/";
                                } else {
                                    that.$message({
                                        message: res.msg,
                                        type: 'error'
                                    });
                                }
                            }
                        });
                    }
                },
            })
        </script>
    </body>
        .list-toolbar {
            padding: 12px 16px 10px;
            border-bottom: 1px solid rgba(222, 230, 239, 0.92);
        }
        .toolbar-main {
            display: flex;
            align-items: flex-start;
            justify-content: space-between;
            gap: 8px;
            flex-wrap: wrap;
        }
        .toolbar-left {
            flex: 1 1 980px;
            display: flex;
            align-items: center;
            gap: 8px;
            flex-wrap: wrap;
        }
        .toolbar-search {
            flex: 1 1 auto;
            display: flex;
            align-items: center;
            gap: 8px;
            flex-wrap: wrap;
        }
        .toolbar-search-item {
            flex: 0 0 148px;
            min-width: 148px;
        }
        .toolbar-search-item.keyword {
            flex: 0 0 220px;
            min-width: 220px;
        }
        .toolbar-query-actions,
        .toolbar-ops {
            display: flex;
            gap: 8px;
            flex-wrap: wrap;
        }
        .toolbar-ops {
            justify-content: flex-end;
        }
        .list-toolbar .el-input__inner,
        .advanced-panel .el-input__inner {
            height: 32px;
            line-height: 32px;
        }
        .list-toolbar .el-input__icon,
        .advanced-panel .el-input__icon {
            line-height: 32px;
        }
        .list-toolbar .el-button,
        .advanced-panel .el-button {
            padding: 8px 12px;
            border-radius: 8px;
        }
        .advanced-panel {
            padding: 10px 16px 12px;
            border-bottom: 1px solid rgba(222, 230, 239, 0.92);
            background: rgba(248, 251, 254, 0.78);
        }
        .advanced-grid {
            display: grid;
            grid-template-columns: repeat(4, minmax(0, 1fr));
            gap: 8px;
        }
        .advanced-item {
            min-width: 0;
        }
        .table-wrap {
            padding: 10px 16px;
        }
        .table-shell {
            border-radius: 20px;
            overflow: hidden;
            border: 1px solid rgba(217, 227, 238, 0.98);
            background: rgba(255, 255, 255, 0.95);
        }
        .table-shell .el-table {
            border-radius: 20px;
            overflow: hidden;
        }
        .table-shell .el-table th {
            background: #f7fafc;
            color: #53677d;
            font-weight: 700;
        }
        .table-shell .el-table .success-row > td {
            background: rgba(101, 198, 141, 0.14);
        }
        .payload-cell {
            display: inline-block;
            max-width: 280px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        .pager-bar {
            padding: 0 16px 16px;
            display: flex;
            align-items: center;
            justify-content: flex-end;
        }
        .column-popover {
            max-width: 320px;
        }
        .column-popover-head {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 12px;
            margin-bottom: 10px;
            font-size: 13px;
            font-weight: 700;
            color: var(--text-main);
        }
        .column-list {
            display: grid;
            grid-template-columns: repeat(2, minmax(0, 1fr));
            gap: 8px 10px;
            max-height: 280px;
            overflow: auto;
            padding-right: 4px;
        }
        .row-action-trigger {
            min-width: 72px;
            display: inline-flex;
            align-items: center;
            justify-content: center;
        }
        @media (max-width: 1600px) {
            .advanced-grid {
                grid-template-columns: repeat(3, minmax(0, 1fr));
            }
        }
        @media (max-width: 1280px) {
            .advanced-grid {
                grid-template-columns: repeat(2, minmax(0, 1fr));
            }
        }
        @media (max-width: 900px) {
            .page-shell {
                padding: 10px;
            }
            .list-toolbar,
            .advanced-panel,
            .table-wrap,
            .pager-bar {
                padding-left: 12px;
                padding-right: 12px;
            }
            .toolbar-search-item,
            .toolbar-search-item.keyword {
                flex: 1 1 100%;
                min-width: 100%;
            }
            .advanced-grid {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>
<body>
<div id="app" class="page-shell" v-cloak>
    <section class="card-shell">
        <div class="list-toolbar">
            <div class="toolbar-main">
                <div class="toolbar-left">
                    <div class="toolbar-search">
                        <div class="toolbar-search-item keyword">
                            <el-input
                                v-model.trim="searchForm.condition"
                                clearable
                                size="small"
                                placeholder="请输入关键字"
                                @keyup.enter.native="handleSearch">
                            </el-input>
                        </div>
                        <div class="toolbar-search-item">
                            <el-input
                                v-model.trim="searchForm.wrk_no"
                                clearable
                                size="small"
                                placeholder="工作号"
                                @keyup.enter.native="handleSearch">
                            </el-input>
                        </div>
                        <div class="toolbar-search-item">
                            <el-input
                                v-model.trim="searchForm.wms_wrk_no"
                                clearable
                                size="small"
                                placeholder="WMS任务号"
                                @keyup.enter.native="handleSearch">
                            </el-input>
                        </div>
                        <div class="toolbar-search-item">
                            <el-input
                                v-model.trim="searchForm.loc_no"
                                clearable
                                size="small"
                                placeholder="目标库位"
                                @keyup.enter.native="handleSearch">
                            </el-input>
                        </div>
                        <div class="toolbar-query-actions">
                            <el-button size="small" type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
                            <el-button size="small" plain icon="el-icon-refresh-left" @click="handleReset">重置</el-button>
                            <el-button size="small" plain :icon="advancedVisible ? 'el-icon-arrow-up' : 'el-icon-arrow-down'" @click="toggleAdvanced">
                                {{ advancedVisible ? '收起' : '筛选' }}
                            </el-button>
                        </div>
                    </div>
                </div>
                <div class="toolbar-ops">
                    <el-popover
                        v-model="columnPopoverVisible"
                        placement="bottom-end"
                        width="320"
                        trigger="click"
                        popper-class="column-popover">
                        <div class="column-popover-head">
                            <span>列设置</span>
                            <div>
                                <el-button type="text" size="mini" @click="showAllColumns">全选</el-button>
                                <el-button type="text" size="mini" @click="resetColumns">重置</el-button>
                            </div>
                        </div>
                        <div class="column-list">
                            <el-checkbox
                                v-for="column in columnDefs"
                                :key="'column-' + column.key"
                                :value="isColumnVisible(column.key)"
                                @change="toggleColumn(column.key, $event)">
                                {{ column.label }}
                            </el-checkbox>
                        </div>
                        <el-button slot="reference" size="small" plain icon="el-icon-setting">列设置</el-button>
                    </el-popover>
                    <el-button size="small" plain icon="el-icon-refresh" :loading="loading" @click="loadList">刷新</el-button>
                </div>
            </div>
        </div>
        <div v-show="advancedVisible" class="advanced-panel">
            <div class="advanced-grid">
                <div class="advanced-item">
                    <el-input
                        v-model.trim="searchForm.source_loc_no"
                        clearable
                        size="small"
                        placeholder="源库位"
                        @keyup.enter.native="handleSearch">
                    </el-input>
                </div>
                <div class="advanced-item">
                    <el-input
                        v-model.trim="searchForm.crn_no"
                        clearable
                        size="small"
                        placeholder="堆垛机"
                        @keyup.enter.native="handleSearch">
                    </el-input>
                </div>
                <div class="advanced-item">
                    <el-input
                        v-model.trim="searchForm.dual_crn_no"
                        clearable
                        size="small"
                        placeholder="双工位堆垛机"
                        @keyup.enter.native="handleSearch">
                    </el-input>
                </div>
            </div>
        </div>
        <div class="table-wrap">
            <div class="table-shell">
                <el-table
                    ref="dataTable"
                    :key="tableRenderKey"
                    v-loading="loading"
                    :data="tableData"
                    border
                    stripe
                    :height="tableHeight"
                    @sort-change="handleSortChange">
                    <el-table-column
                        v-for="column in visibleColumns"
                        :key="column.key"
                        :prop="column.prop"
                        :label="column.label"
                        :width="column.width"
                        :min-width="column.minWidth"
                        :sortable="column.sortable ? 'custom' : false"
                        :show-overflow-tooltip="column.showOverflow !== false"
                        :align="column.align || 'left'">
                        <template v-if="column.key === 'systemMsg'" slot-scope="scope">
                            <span class="payload-cell">{{ scope.row.systemMsg || '--' }}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" fixed="right" width="100" align="center">
                        <template slot-scope="scope">
                            <el-dropdown trigger="click" @command="handleRowCommand($event, scope.row)">
                                <el-button size="mini" plain class="row-action-trigger">
                                    操作<i class="el-icon-arrow-down el-icon--right"></i>
                                </el-button>
                                <el-dropdown-menu slot="dropdown">
                                    <el-dropdown-item command="complete">完成任务</el-dropdown-item>
                                    <el-dropdown-item command="cancel" divided>取消任务</el-dropdown-item>
                                </el-dropdown-menu>
                            </el-dropdown>
                        </template>
                    </el-table-column>
                </el-table>
            </div>
        </div>
        <div class="pager-bar">
            <el-pagination
                background
                :current-page="currentPage"
                :page-size="pageSize"
                :page-sizes="pageSizes"
                layout="total, sizes, prev, pager, next, jumper"
                :total="pageTotal"
                @size-change="handleSizeChange"
                @current-change="handleCurrentChange">
            </el-pagination>
        </div>
    </section>
</div>
</body>
<script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script>
<script type="text/javascript" src="../../static/js/common.js?v=20260309_i18n_fix1"></script>
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
<script type="text/javascript" src="../../static/js/wrkMast/wrkMast.js?v=20260311_wrk_mast_vue"></script>
</html>