二级前端页面修改
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -11,45 +11,109 @@
|
|||||||
|
|
||||||
<!-- 生产计划队列 -->
|
<!-- 生产计划队列 -->
|
||||||
<div v-if="!isLoading" class="plan-queue-panel">
|
<div v-if="!isLoading" class="plan-queue-panel">
|
||||||
<div class="panel-title">
|
<div class="queue-header">
|
||||||
<i class="el-icon-document"></i> 生产计划队列
|
<div class="queue-title">
|
||||||
<el-tag size="small" type="success">{{ planQueue.length }} 个计划</el-tag>
|
<i class="el-icon-s-order"></i> 生产计划
|
||||||
</div>
|
</div>
|
||||||
<div class="plan-queue-list">
|
<div class="queue-tabs">
|
||||||
<el-empty v-if="planQueue.length === 0" description="暂无生产计划"></el-empty>
|
|
||||||
<div v-else class="plan-items">
|
|
||||||
<div
|
<div
|
||||||
v-for="(plan, index) in planQueue"
|
class="queue-tab"
|
||||||
:key="plan.id || plan.planid || index"
|
:class="{ active: activeQueueTab === 'current' }"
|
||||||
class="plan-item"
|
@click="activeQueueTab = 'current'"
|
||||||
:class="getPlanItemClass(plan)"
|
|
||||||
@click="selectPlan(plan)"
|
|
||||||
>
|
>
|
||||||
<div class="plan-order" :class="getPlanOrderClass(plan.status)">
|
<i class="el-icon-s-flag"></i>
|
||||||
{{ index + 1 }}
|
<span>当前计划</span>
|
||||||
</div>
|
<el-badge :value="currentPlans.length" class="tab-badge" v-if="currentPlans.length > 0" />
|
||||||
<div class="plan-info">
|
</div>
|
||||||
<div class="plan-row">
|
<div
|
||||||
<span class="plan-label">计划ID:</span>
|
class="queue-tab"
|
||||||
<span class="plan-value">{{ plan.planid || '-' }}</span>
|
:class="{ active: activeQueueTab === 'all' }"
|
||||||
</div>
|
@click="activeQueueTab = 'all'"
|
||||||
<div class="plan-row">
|
>
|
||||||
<span class="plan-label">钢卷号:</span>
|
<i class="el-icon-document"></i>
|
||||||
<span class="plan-value">{{ plan.coilid || '-' }}</span>
|
<span>全部计划</span>
|
||||||
</div>
|
<el-badge :value="planQueue.length" class="tab-badge" v-if="planQueue.length > 0" />
|
||||||
<div class="plan-row">
|
|
||||||
<span class="plan-label">钢种:</span>
|
|
||||||
<span class="plan-value">{{ plan.steelGrade || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="plan-status">
|
|
||||||
<el-tag :type="getPlanStatusTagType(plan.status)" size="mini" effect="dark">
|
|
||||||
{{ getPlanStatusText(plan.status) }}
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 当前计划内容 -->
|
||||||
|
<div v-if="activeQueueTab === 'current'">
|
||||||
|
<div class="plan-queue-list">
|
||||||
|
<el-empty v-if="currentPlans.length === 0" description="暂无当前计划" :image-size="80"></el-empty>
|
||||||
|
<div v-else class="plan-items">
|
||||||
|
<div
|
||||||
|
v-for="(plan, index) in currentPlans"
|
||||||
|
:key="plan.id || plan.planid || index"
|
||||||
|
class="plan-item"
|
||||||
|
:class="getPlanItemClass(plan)"
|
||||||
|
@click="selectPlan(plan)"
|
||||||
|
>
|
||||||
|
<div class="plan-order" :class="getPlanOrderClass(plan.status)">
|
||||||
|
{{ index + 1 }}
|
||||||
|
</div>
|
||||||
|
<div class="plan-info">
|
||||||
|
<div class="plan-row">
|
||||||
|
<span class="plan-label">计划ID:</span>
|
||||||
|
<span class="plan-value">{{ plan.planid || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="plan-row">
|
||||||
|
<span class="plan-label">钢卷号:</span>
|
||||||
|
<span class="plan-value">{{ plan.coilid || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="plan-row">
|
||||||
|
<span class="plan-label">钢种:</span>
|
||||||
|
<span class="plan-value">{{ plan.steelGrade || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="plan-status">
|
||||||
|
<el-tag :type="getPlanStatusTagType(plan.status)" size="mini" effect="dark">
|
||||||
|
{{ getPlanStatusText(plan.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 全部计划内容 -->
|
||||||
|
<div v-if="activeQueueTab === 'all'">
|
||||||
|
<div class="plan-queue-list">
|
||||||
|
<el-empty v-if="planQueue.length === 0" description="暂无生产计划" :image-size="80"></el-empty>
|
||||||
|
<div v-else class="plan-items">
|
||||||
|
<div
|
||||||
|
v-for="(plan, index) in planQueue"
|
||||||
|
:key="plan.id || plan.planid || index"
|
||||||
|
class="plan-item"
|
||||||
|
:class="getPlanItemClass(plan)"
|
||||||
|
@click="selectPlan(plan)"
|
||||||
|
>
|
||||||
|
<div class="plan-order" :class="getPlanOrderClass(plan.status)">
|
||||||
|
{{ index + 1 }}
|
||||||
|
</div>
|
||||||
|
<div class="plan-info">
|
||||||
|
<div class="plan-row">
|
||||||
|
<span class="plan-label">计划ID:</span>
|
||||||
|
<span class="plan-value">{{ plan.planid || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="plan-row">
|
||||||
|
<span class="plan-label">钢卷号:</span>
|
||||||
|
<span class="plan-value">{{ plan.coilid || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="plan-row">
|
||||||
|
<span class="plan-label">钢种:</span>
|
||||||
|
<span class="plan-value">{{ plan.steelGrade || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="plan-status">
|
||||||
|
<el-tag :type="getPlanStatusTagType(plan.status)" size="mini" effect="dark">
|
||||||
|
{{ getPlanStatusText(plan.status) }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- WebSocket 连接状态指示 -->
|
<!-- WebSocket 连接状态指示 -->
|
||||||
@@ -203,6 +267,18 @@
|
|||||||
<el-button size="mini" type="text" icon="el-icon-close" @click="selectedPlan = null"></el-button>
|
<el-button size="mini" type="text" icon="el-icon-close" @click="selectedPlan = null"></el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="plan-detail-content">
|
<div class="plan-detail-content">
|
||||||
|
<!-- 位置信息(如果在产线上) -->
|
||||||
|
<div v-if="selectedPlanPosition" class="position-alert">
|
||||||
|
<div class="position-icon">
|
||||||
|
<i class="el-icon-location"></i>
|
||||||
|
</div>
|
||||||
|
<div class="position-info">
|
||||||
|
<div class="position-label">当前位置</div>
|
||||||
|
<div class="position-name">{{ selectedPlanPosition.positionNameCn }}</div>
|
||||||
|
<div class="position-code">{{ selectedPlanPosition.positionNameEn }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<el-descriptions :column="1" border size="small">
|
<el-descriptions :column="1" border size="small">
|
||||||
<el-descriptions-item label="计划ID">{{ selectedPlan.planid || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="计划ID">{{ selectedPlan.planid || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="钢卷号">{{ selectedPlan.coilid || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="钢卷号">{{ selectedPlan.coilid || '-' }}</el-descriptions-item>
|
||||||
@@ -502,6 +578,9 @@ export default {
|
|||||||
// 加载状态
|
// 加载状态
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
|
|
||||||
|
// 计划队列tab
|
||||||
|
activeQueueTab: 'all',
|
||||||
|
|
||||||
// 生产计划队列
|
// 生产计划队列
|
||||||
planQueue: [],
|
planQueue: [],
|
||||||
|
|
||||||
@@ -561,6 +640,40 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
// 当前计划(ONLINE 或 PRODUCING状态)
|
||||||
|
currentPlans() {
|
||||||
|
return this.planQueue.filter(plan =>
|
||||||
|
['ONLINE', 'PRODUCING', 'PRODUCTING'].includes(plan.status)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 显示的计划列表(根据tab切换)
|
||||||
|
displayedPlans() {
|
||||||
|
if (this.activeQueueTab === 'current') {
|
||||||
|
return this.currentPlans
|
||||||
|
}
|
||||||
|
return this.planQueue
|
||||||
|
},
|
||||||
|
|
||||||
|
// 选中计划的位置信息
|
||||||
|
selectedPlanPosition() {
|
||||||
|
if (!this.selectedPlan || !this.matMapList || this.matMapList.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从matMapList中查找匹配的位置
|
||||||
|
// matMapList中的字段是驼峰命名:planId, matId
|
||||||
|
// 计划对象中的字段可能是:planid, coilid
|
||||||
|
const position = this.matMapList.find(item =>
|
||||||
|
(item.planId && item.planId === this.selectedPlan.planid) ||
|
||||||
|
(item.planId && item.planId === this.selectedPlan.planId) ||
|
||||||
|
(item.matId && item.matId === this.selectedPlan.coilid) ||
|
||||||
|
(item.matId && item.matId === this.selectedPlan.matId)
|
||||||
|
)
|
||||||
|
|
||||||
|
return position
|
||||||
|
},
|
||||||
|
|
||||||
getOperateTitle() {
|
getOperateTitle() {
|
||||||
const titleMap = {
|
const titleMap = {
|
||||||
'ONLINE': '钢卷上线',
|
'ONLINE': '钢卷上线',
|
||||||
@@ -884,6 +997,12 @@ export default {
|
|||||||
const autoFlagText = data.autoFlag === 1 ? '[手动]' : '[自动]'
|
const autoFlagText = data.autoFlag === 1 ? '[手动]' : '[自动]'
|
||||||
const config = this.getOperationConfig(data.operation)
|
const config = this.getOperationConfig(data.operation)
|
||||||
|
|
||||||
|
// 检测到上线、生产中、生产完成等关键操作时,刷新生产计划队列
|
||||||
|
if (['ONLINE', 'PRODUCING', 'PRODUCT'].includes(data.operation)) {
|
||||||
|
console.log(`检测到${operationText}操作,刷新生产计划队列...`)
|
||||||
|
this.refreshPlanQueue()
|
||||||
|
}
|
||||||
|
|
||||||
// 右上角通知(所有操作都显示)
|
// 右上角通知(所有操作都显示)
|
||||||
this.$notify({
|
this.$notify({
|
||||||
title: `${config.icon} ${config.title}`,
|
title: `${config.icon} ${config.title}`,
|
||||||
@@ -1026,6 +1145,66 @@ export default {
|
|||||||
|
|
||||||
// ============ API 方法 ============
|
// ============ API 方法 ============
|
||||||
|
|
||||||
|
// 刷新生产计划队列(独立方法,可被信号触发)
|
||||||
|
async refreshPlanQueue() {
|
||||||
|
try {
|
||||||
|
// 由于后端默认只查NEW和READY,需要分别查询多个状态然后合并
|
||||||
|
const statuses = ['ONLINE', 'PRODUCING', 'READY', 'NEW']
|
||||||
|
const allPlans = []
|
||||||
|
|
||||||
|
// 并行查询所有状态
|
||||||
|
const requests = statuses.map(status =>
|
||||||
|
getPlanQueue({ status }).catch(err => {
|
||||||
|
console.warn(`查询${status}状态失败:`, err)
|
||||||
|
return { code: 500, data: [] }
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const results = await Promise.all(requests)
|
||||||
|
|
||||||
|
// 合并所有查询结果
|
||||||
|
results.forEach((res, index) => {
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
allPlans.push(...res.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 去重(根据id)
|
||||||
|
const uniquePlans = Array.from(
|
||||||
|
new Map(allPlans.map(plan => [plan.id, plan])).values()
|
||||||
|
)
|
||||||
|
|
||||||
|
// 定义状态优先级
|
||||||
|
const statusPriority = {
|
||||||
|
'PRODUCING': 1,
|
||||||
|
'PRODUCTING': 1,
|
||||||
|
'ONLINE': 2,
|
||||||
|
'READY': 3,
|
||||||
|
'NEW': 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序
|
||||||
|
const sortedPlans = uniquePlans.sort((a, b) => {
|
||||||
|
const priorityA = statusPriority[a.status] || 500
|
||||||
|
const priorityB = statusPriority[b.status] || 500
|
||||||
|
if (priorityA !== priorityB) {
|
||||||
|
return priorityA - priorityB
|
||||||
|
}
|
||||||
|
return (a.seqid || 0) - (b.seqid || 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 最多显示 5 个计划
|
||||||
|
this.planQueue = sortedPlans.slice(0, 5)
|
||||||
|
|
||||||
|
console.log('查询到的总计划数:', uniquePlans.length, '条')
|
||||||
|
console.log('显示的计划:', this.planQueue.length, '条', this.planQueue)
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error('获取生产计划队列失败:', err)
|
||||||
|
this.planQueue = this.generateMockPlanQueue()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
try {
|
try {
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
@@ -1033,35 +1212,11 @@ export default {
|
|||||||
|
|
||||||
// 只在 WebSocket 未连接时使用 API 数据
|
// 只在 WebSocket 未连接时使用 API 数据
|
||||||
if (!this.socketStatus.matmap) {
|
if (!this.socketStatus.matmap) {
|
||||||
this.matMapList = res.data.matMapList || []
|
this.matMapList = res.data.matMapList || []
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取生产计划队列
|
// 获取生产计划队列
|
||||||
try {
|
await this.refreshPlanQueue()
|
||||||
const planRes = await getPlanQueue({})
|
|
||||||
if (planRes.code === 200 && planRes.data) {
|
|
||||||
// 筛选出 NEW、READY 和 PRODUCING 状态的计划
|
|
||||||
const validStatuses = ['NEW', 'READY', 'PRODUCING']
|
|
||||||
let filteredPlans = (planRes.data || [])
|
|
||||||
.filter(plan => validStatuses.includes(plan.status))
|
|
||||||
.sort((a, b) => (a.seqid || 0) - (b.seqid || 0))
|
|
||||||
|
|
||||||
// 如果有多个 PRODUCING,只保留第一个
|
|
||||||
const producingPlans = filteredPlans.filter(p => p.status === 'PRODUCING')
|
|
||||||
const otherPlans = filteredPlans.filter(p => p.status !== 'PRODUCING')
|
|
||||||
|
|
||||||
if (producingPlans.length > 0) {
|
|
||||||
this.planQueue = [producingPlans[0], ...otherPlans].slice(0, 5)
|
|
||||||
} else {
|
|
||||||
this.planQueue = otherPlans.slice(0, 5)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.planQueue = this.generateMockPlanQueue()
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('获取生产计划队列失败:', err)
|
|
||||||
this.planQueue = this.generateMockPlanQueue()
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('获取钢卷数据失败:', err)
|
console.error('获取钢卷数据失败:', err)
|
||||||
@@ -1095,21 +1250,30 @@ export default {
|
|||||||
const statusMap = {
|
const statusMap = {
|
||||||
NEW: '新建',
|
NEW: '新建',
|
||||||
READY: '就绪',
|
READY: '就绪',
|
||||||
|
ONLINE: '上线',
|
||||||
PRODUCING: '生产中',
|
PRODUCING: '生产中',
|
||||||
PRODUCT: '已完成',
|
PRODUCT: '生产完成',
|
||||||
COMPLETED: '已完成'
|
PAY_OVER: '甩尾',
|
||||||
|
UNLOAD: '卸卷',
|
||||||
|
COMPLETED: '已完成',
|
||||||
|
CANCELLED: '已取消'
|
||||||
}
|
}
|
||||||
return statusMap[status] || status || '待处理'
|
return statusMap[status] || status || '未知'
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取计划状态标签类型
|
// 获取计划状态标签类型
|
||||||
getPlanStatusTagType(status) {
|
getPlanStatusTagType(status) {
|
||||||
const typeMap = {
|
const typeMap = {
|
||||||
NEW: '',
|
NEW: 'info',
|
||||||
READY: 'warning',
|
READY: 'primary',
|
||||||
|
ONLINE: 'warning',
|
||||||
PRODUCING: 'success',
|
PRODUCING: 'success',
|
||||||
|
PRODUCTING: 'success', // 兼容带T的写法
|
||||||
PRODUCT: 'info',
|
PRODUCT: 'info',
|
||||||
COMPLETED: 'info'
|
PAY_OVER: '',
|
||||||
|
UNLOAD: 'danger',
|
||||||
|
COMPLETED: 'info',
|
||||||
|
CANCELLED: 'danger'
|
||||||
}
|
}
|
||||||
return typeMap[status] || 'info'
|
return typeMap[status] || 'info'
|
||||||
},
|
},
|
||||||
@@ -1118,8 +1282,11 @@ export default {
|
|||||||
getPlanItemClass(plan) {
|
getPlanItemClass(plan) {
|
||||||
const classes = []
|
const classes = []
|
||||||
|
|
||||||
if (plan.status === 'PRODUCING') {
|
if (plan.status === 'PRODUCING' || plan.status === 'PRODUCTING') {
|
||||||
classes.push('plan-active')
|
classes.push('plan-producing')
|
||||||
|
}
|
||||||
|
if (plan.status === 'ONLINE') {
|
||||||
|
classes.push('plan-online')
|
||||||
}
|
}
|
||||||
if (plan.status === 'READY') {
|
if (plan.status === 'READY') {
|
||||||
classes.push('plan-ready')
|
classes.push('plan-ready')
|
||||||
@@ -1133,7 +1300,8 @@ export default {
|
|||||||
|
|
||||||
// 获取计划序号样式类
|
// 获取计划序号样式类
|
||||||
getPlanOrderClass(status) {
|
getPlanOrderClass(status) {
|
||||||
if (status === 'PRODUCING') return 'order-producing'
|
if (status === 'PRODUCING' || status === 'PRODUCTING') return 'order-producing'
|
||||||
|
if (status === 'ONLINE') return 'order-online'
|
||||||
if (status === 'READY') return 'order-ready'
|
if (status === 'READY') return 'order-ready'
|
||||||
if (status === 'NEW') return 'order-new'
|
if (status === 'NEW') return 'order-new'
|
||||||
return ''
|
return ''
|
||||||
@@ -1168,6 +1336,23 @@ export default {
|
|||||||
this.selectedPlan = plan
|
this.selectedPlan = plan
|
||||||
// 选择计划时清除选中的设备
|
// 选择计划时清除选中的设备
|
||||||
this.selectedCard = null
|
this.selectedCard = null
|
||||||
|
|
||||||
|
// 调试:查找并显示位置信息
|
||||||
|
const position = this.matMapList.find(item =>
|
||||||
|
(item.planId && item.planId === plan.planid) ||
|
||||||
|
(item.planId && item.planId === plan.planId) ||
|
||||||
|
(item.matId && item.matId === plan.coilid) ||
|
||||||
|
(item.matId && item.matId === plan.matId)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (position) {
|
||||||
|
console.log('✅ 找到计划位置:', position.positionNameCn, '(', position.positionNameEn, ')')
|
||||||
|
console.log('匹配详情 - matId:', position.matId, 'planId:', position.planId)
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ 未找到计划位置')
|
||||||
|
console.log('matMapList有', this.matMapList.length, '个设备:', this.matMapList)
|
||||||
|
console.log('计划信息 - planid:', plan.planid, 'planId:', plan.planId, 'coilid:', plan.coilid, 'matId:', plan.matId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1828,10 +2013,72 @@ export default {
|
|||||||
/* 生产计划队列面板 */
|
/* 生产计划队列面板 */
|
||||||
.plan-queue-panel {
|
.plan-queue-panel {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
padding: 15px;
|
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 队列头部 */
|
||||||
|
.queue-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 15px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-bottom: 1px solid #e4e7ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue-title i {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tab切换按钮组 */
|
||||||
|
.queue-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue-tab {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
background: transparent;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue-tab:hover {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue-tab.active {
|
||||||
|
color: #409eff;
|
||||||
|
font-weight: 600;
|
||||||
|
border-bottom-color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue-tab i {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue-tab .tab-badge {
|
||||||
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plan-queue-panel .panel-title {
|
.plan-queue-panel .panel-title {
|
||||||
@@ -1850,14 +2097,15 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.plan-queue-list {
|
.plan-queue-list {
|
||||||
max-height: 240px;
|
max-height: none;
|
||||||
overflow-y: auto;
|
overflow-y: visible;
|
||||||
|
padding: 15px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plan-items {
|
.plan-items {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
overflow-x: auto;
|
flex-wrap: wrap;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1880,14 +2128,20 @@ export default {
|
|||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.plan-item.plan-active {
|
.plan-item.plan-producing {
|
||||||
background: #f0f9ff;
|
background: #f0f9ff;
|
||||||
border-color: #67c23a;
|
border-color: #67c23a;
|
||||||
|
animation: pulse-green 1.5s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-item.plan-online {
|
||||||
|
background: #fdf6ec;
|
||||||
|
border-color: #e6a23c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plan-item.plan-ready {
|
.plan-item.plan-ready {
|
||||||
background: #fdf6ec;
|
background: #ecf5ff;
|
||||||
border-color: #e6a23c;
|
border-color: #409eff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plan-item.plan-selected {
|
.plan-item.plan-selected {
|
||||||
@@ -1896,6 +2150,18 @@ export default {
|
|||||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 绿色闪烁动画 */
|
||||||
|
@keyframes pulse-green {
|
||||||
|
0%, 100% {
|
||||||
|
border-color: #67c23a;
|
||||||
|
box-shadow: 0 0 0 0 rgba(103, 194, 58, 0.4);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
border-color: #85ce61;
|
||||||
|
box-shadow: 0 0 0 4px rgba(103, 194, 58, 0.6), 0 0 15px rgba(103, 194, 58, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.plan-order {
|
.plan-order {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
@@ -1912,16 +2178,33 @@ export default {
|
|||||||
|
|
||||||
.plan-order.order-producing {
|
.plan-order.order-producing {
|
||||||
background: #67c23a;
|
background: #67c23a;
|
||||||
|
animation: blink-green-dot 1.2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-order.order-online {
|
||||||
|
background: #e6a23c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plan-order.order-ready {
|
.plan-order.order-ready {
|
||||||
background: #e6a23c;
|
background: #409eff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plan-order.order-new {
|
.plan-order.order-new {
|
||||||
background: #909399;
|
background: #909399;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 绿色点闪烁动画 */
|
||||||
|
@keyframes blink-green-dot {
|
||||||
|
0%, 100% {
|
||||||
|
background: #67c23a;
|
||||||
|
box-shadow: 0 0 5px rgba(103, 194, 58, 0.5);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background: #85ce61;
|
||||||
|
box-shadow: 0 0 10px rgba(103, 194, 58, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.plan-info {
|
.plan-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -2050,6 +2333,55 @@ export default {
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 位置信息提示卡片 */
|
||||||
|
.position-alert {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background: #f0f9ff;
|
||||||
|
border: 1px solid #b3d8ff;
|
||||||
|
border-left: 4px solid #409eff;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background: #409eff;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-code {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #606266;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
.plan-time-info {
|
.plan-time-info {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
padding-top: 15px;
|
padding-top: 15px;
|
||||||
|
|||||||
Reference in New Issue
Block a user