-
This commit is contained in:
@@ -465,6 +465,10 @@ a:hover { text-decoration: underline; }
|
||||
aspect-ratio: 4/3;
|
||||
object-fit: cover;
|
||||
}
|
||||
.camera-img.arm {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.camera-placeholder {
|
||||
width: 100%;
|
||||
aspect-ratio: 4/3;
|
||||
@@ -1076,3 +1080,114 @@ a:hover { text-decoration: underline; }
|
||||
.machine-cell.mstatus-pending { background: #141e28; border-color: #2a3a4a; }
|
||||
.machine-cell.mstatus-active { background: #1a2535; border-color: #4fc3f7; }
|
||||
.machine-cell.mstatus-completed { background: #152522; border-color: #2e7d32; }
|
||||
|
||||
/* ===== 报关单选择 ===== */
|
||||
.customs-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.customs-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.customs-select {
|
||||
flex: 1;
|
||||
max-width: 400px;
|
||||
padding: 10px 12px;
|
||||
background: #1a2535;
|
||||
border: 1px solid #2a3a4a;
|
||||
border-radius: 8px;
|
||||
color: #e0e0e0;
|
||||
font-size: 14px;
|
||||
outline: none;
|
||||
}
|
||||
.customs-select:focus {
|
||||
border-color: #4fc3f7;
|
||||
}
|
||||
.customs-select:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.customs-select option {
|
||||
background: #1a2535;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
.customs-select:disabled option {
|
||||
opacity: 1;
|
||||
}
|
||||
.customs-info {
|
||||
font-size: 13px;
|
||||
color: #8899aa;
|
||||
}
|
||||
.customs-badge {
|
||||
font-size: 13px;
|
||||
}
|
||||
.customs-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
/* ===== 数据表格 ===== */
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
}
|
||||
.data-table th {
|
||||
text-align: left;
|
||||
padding: 10px 12px;
|
||||
background: #0f1923;
|
||||
color: #8899aa;
|
||||
font-weight: 500;
|
||||
border-bottom: 1px solid #2a3a4a;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.data-table td {
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid #1a2a3a;
|
||||
color: #ccc;
|
||||
}
|
||||
.data-table tbody tr:hover {
|
||||
background: #1a2a3a;
|
||||
}
|
||||
.clickable-row {
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.clickable-row:hover {
|
||||
background: #1a2535 !important;
|
||||
}
|
||||
.row-selected {
|
||||
background: #142a3a !important;
|
||||
border-left: 3px solid #4fc3f7;
|
||||
}
|
||||
|
||||
/* ===== Badge 状态标签 ===== */
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.badge-unknown { background: #2a3441; color: #8899aa; }
|
||||
.badge-normal { background: #1a3a2a; color: #4caf50; }
|
||||
.badge-active { background: #1a3050; color: #4fc3f7; }
|
||||
.badge-finished { background: #1a3a2a; color: #4caf50; }
|
||||
.badge-waiting { background: #3a3020; color: #ffc107; }
|
||||
.badge-error { background: #3a1a1a; color: #f44336; }
|
||||
|
||||
/* ===== 分页控件 ===== */
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 16px 0 8px;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ createApp({
|
||||
agvCameraSrc: '/api/camera/refresh?t=' + Date.now(),
|
||||
armCameraSrc: '/api/camera/arm_preview?t=' + Date.now(),
|
||||
agvCameraError: false,
|
||||
hasAgvCamera: false, // AGV 车体是否有可用相机
|
||||
armCameraError: false,
|
||||
reconnectingDevice: null
|
||||
}
|
||||
@@ -36,6 +37,7 @@ createApp({
|
||||
},
|
||||
mounted() {
|
||||
this.refresh()
|
||||
this.refreshCameraCapabilities()
|
||||
setInterval(this.refreshStatus, 3000)
|
||||
this.refreshCams()
|
||||
setInterval(() => this.refreshCams(), 2000)
|
||||
@@ -47,6 +49,17 @@ createApp({
|
||||
this.armCameraSrc = '/api/camera/arm_preview?t=' + Date.now()
|
||||
}
|
||||
},
|
||||
async refreshCameraCapabilities() {
|
||||
try {
|
||||
const res = await fetch(API + '/api/camera/capabilities')
|
||||
const data = await res.json()
|
||||
this.hasAgvCamera = data.has_agv_camera
|
||||
} catch (e) { this.hasAgvCamera = false }
|
||||
},
|
||||
refreshAgvCamera() {
|
||||
this.agvCameraSrc = '/api/camera/refresh?t=' + Date.now()
|
||||
this.agvCameraError = false
|
||||
},
|
||||
async refresh() {
|
||||
await this.refreshStatus()
|
||||
await this.loadPoints()
|
||||
@@ -58,6 +71,10 @@ createApp({
|
||||
this.agvConnected = data.agv_connected
|
||||
this.armConnected = data.arm_connected
|
||||
this.cameraOpened = data.camera_opened
|
||||
// 尝试从后端获取摄像头能力,若无字段则保持默认 false
|
||||
if (data.has_agv_camera !== undefined) {
|
||||
this.hasAgvCamera = data.has_agv_camera
|
||||
}
|
||||
this.armCameraOpened = data.arm_camera_opened
|
||||
this.mapLoaded = data.map_loaded
|
||||
this.currentState = data.state || 'idle'
|
||||
|
||||
@@ -9,6 +9,7 @@ createApp({
|
||||
tasks: [],
|
||||
report: null,
|
||||
armCameraOpened: false,
|
||||
hasAgvCamera: false,
|
||||
agvPreviewUrl: API + '/api/camera/preview',
|
||||
armPreviewUrl: '',
|
||||
polling: null,
|
||||
@@ -70,6 +71,13 @@ createApp({
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
async checkAgvCameraCapabilities() {
|
||||
try {
|
||||
const res = await fetch(API + '/api/camera/capabilities')
|
||||
const data = await res.json()
|
||||
this.hasAgvCamera = data.has_agv_camera
|
||||
} catch (e) { this.hasAgvCamera = false }
|
||||
},
|
||||
poll() {
|
||||
this.refresh()
|
||||
this.pollLogs()
|
||||
|
||||
@@ -66,6 +66,15 @@ createApp({
|
||||
armSnapshotLoading: false,
|
||||
newQrName: '',
|
||||
armInitialPose: [0, 0, 0, 0, 0, 0],
|
||||
// 报关单
|
||||
customsList: [],
|
||||
customsLoading: false,
|
||||
customsPage: 1,
|
||||
customsPageSize: 15,
|
||||
customsTotal: 0,
|
||||
selectedCustomsId: '',
|
||||
selectedCustomsName: '',
|
||||
customsMachines: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -76,6 +85,13 @@ createApp({
|
||||
this.armCameraUrl = API + '/api/camera/arm_preview?t=' + Date.now()
|
||||
},
|
||||
computed: {
|
||||
customsTotalPages() {
|
||||
return Math.max(1, Math.ceil(this.customsTotal / this.customsPageSize))
|
||||
},
|
||||
customsPageData() {
|
||||
// 前端显示 pagination data — 但我们在 API 后端做分页,所以这里只是引用
|
||||
return this.customsList
|
||||
},
|
||||
hasQr() {
|
||||
return !!(this.selectedMachine && this.selectedMachine.qr)
|
||||
},
|
||||
@@ -1187,5 +1203,64 @@ createApp({
|
||||
alert('❌ 复位请求失败: ' + e.message)
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
// ===== 报关单方法 =====
|
||||
async loadCustomsList() {
|
||||
this.customsLoading = true
|
||||
try {
|
||||
const url = API + '/api/customs/list?pageNum=' + this.customsPage + '&pageSize=' + this.customsPageSize
|
||||
const res = await fetch(url)
|
||||
const d = await res.json()
|
||||
if (d.ok && d.data) {
|
||||
const raw = d.data
|
||||
let list = []
|
||||
let total = 0
|
||||
if (raw.rows) { list = raw.rows; total = raw.total || list.length }
|
||||
else if (raw.records) { list = raw.records; total = raw.total || list.length }
|
||||
else if (Array.isArray(raw)) { list = raw; total = list.length }
|
||||
else if (raw.data && raw.data.rows) { list = raw.data.rows; total = raw.data.total || list.length }
|
||||
else if (raw.data && raw.data.records) { list = raw.data.records; total = raw.data.total || list.length }
|
||||
else if (raw.data && Array.isArray(raw.data)) { list = raw.data; total = list.length }
|
||||
this.customsList = list
|
||||
this.customsTotal = total || list.length
|
||||
} else {
|
||||
this.customsList = []
|
||||
this.customsTotal = 0
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载报关单列表失败', e)
|
||||
this.customsList = []
|
||||
this.customsTotal = 0
|
||||
} finally {
|
||||
this.customsLoading = false
|
||||
}
|
||||
},
|
||||
async selectCustomsRow(item) {
|
||||
const id = item.id || item.customsId || item.customs_id || ''
|
||||
if (!id) return
|
||||
this.selectedCustomsId = id
|
||||
this.selectedCustomsName = item.customsNo || item.customs_no || item.name || item.customsName || item.customs_name || id
|
||||
this.customsMachines = []
|
||||
try {
|
||||
const res = await fetch(API + '/api/customs/machines?customsId=' + encodeURIComponent(id))
|
||||
const d = await res.json()
|
||||
if (d.ok && d.data) {
|
||||
const raw = d.data
|
||||
let machines = []
|
||||
if (raw.rows) { machines = raw.rows }
|
||||
else if (raw.records) { machines = raw.records }
|
||||
else if (raw.data && Array.isArray(raw.data)) { machines = raw.data }
|
||||
else if (Array.isArray(raw)) { machines = raw }
|
||||
else if (Array.isArray(raw.data)) { machines = raw.data }
|
||||
this.customsMachines = machines
|
||||
} else {
|
||||
this.customsMachines = []
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('加载机器列表失败', e)
|
||||
this.customsMachines = []
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
}).mount('#app')
|
||||
|
||||
Reference in New Issue
Block a user