feat(material): 去Tab分段展示 + 总图段位标签带 + 跟踪表段列 / 移除看板与产能分析
- 移除 Tab 切换,5 段(入口/酸洗/清洗/烘干/出口)顺序堆叠 - 总图 SVG 顶部加段位色带(5 段不同色),同时跟踪表新增「段」列 - 修复横向溢出(minmax(0,1fr) + auto-fit 槽卡片 + overflow-x:hidden) - 删除菜单与路由中的「生产看板」「产能分析」,首页重定向到 /plan Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -14,14 +14,8 @@ const routes = [
|
||||
path: '/',
|
||||
component: () => import('@/views/Layout.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
redirect: '/dashboard',
|
||||
redirect: '/plan',
|
||||
children: [
|
||||
{
|
||||
path: 'dashboard',
|
||||
name: 'Dashboard',
|
||||
component: () => import('@/views/Dashboard.vue'),
|
||||
meta: { title: '生产看板', icon: 'el-icon-monitor', requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: 'material',
|
||||
name: 'Material',
|
||||
@@ -70,12 +64,6 @@ const routes = [
|
||||
component: () => import('@/views/Quality.vue'),
|
||||
meta: { title: '质量管理', icon: 'el-icon-medal', requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: 'capacity',
|
||||
name: 'Capacity',
|
||||
component: () => import('@/views/Capacity.vue'),
|
||||
meta: { title: '产能分析', icon: 'el-icon-s-data', requiresAuth: true }
|
||||
},
|
||||
]
|
||||
},
|
||||
{ path: '*', redirect: '/' }
|
||||
|
||||
@@ -70,7 +70,6 @@ const IC = {
|
||||
}
|
||||
|
||||
const MENU = [
|
||||
{ path: '/dashboard', title: '生产看板', icon: IC.dashboard },
|
||||
{ path: '/plan', title: '计划管理', icon: IC.plan },
|
||||
{ path: '/material', title: '物料跟踪', icon: IC.material },
|
||||
{ path: '/production', title: '实绩管理', icon: IC.production },
|
||||
@@ -79,7 +78,6 @@ const MENU = [
|
||||
{ path: '/downtime', title: '停机管理', icon: IC.downtime },
|
||||
{ path: '/inspection', title: '设备巡检', icon: IC.inspection },
|
||||
{ path: '/quality', title: '质量管理', icon: IC.quality },
|
||||
{ path: '/capacity', title: '产能分析', icon: IC.capacity },
|
||||
]
|
||||
|
||||
export default {
|
||||
|
||||
@@ -36,8 +36,16 @@
|
||||
<div class="line-wrap card">
|
||||
<div class="card-header">推拉酸洗线 - 物料跟踪总图</div>
|
||||
<div class="line-body">
|
||||
<svg viewBox="0 0 1900 280" preserveAspectRatio="xMidYMid meet" class="line-svg">
|
||||
<rect x="0" y="0" width="1900" height="280" fill="#0a1218" />
|
||||
<svg viewBox="0 -32 1900 312" preserveAspectRatio="xMidYMid meet" class="line-svg">
|
||||
<rect x="0" y="-32" width="1900" height="312" fill="#0a1218" />
|
||||
|
||||
<!-- 段位标签带 -->
|
||||
<g v-for="s in sections" :key="'sec-'+s.name">
|
||||
<rect :x="s.bandX" y="-28" :width="s.bandW" height="22" :fill="s.color" opacity="0.18" rx="3"/>
|
||||
<rect :x="s.bandX" y="-28" :width="s.bandW" height="22" fill="none" :stroke="s.color" stroke-width="1" opacity="0.7" rx="3"/>
|
||||
<text :x="s.labelX" y="-13" text-anchor="middle" font-size="12" font-weight="bold" :fill="s.color"
|
||||
font-family="Arial,sans-serif">{{ s.name }}</text>
|
||||
</g>
|
||||
|
||||
<!-- 顶部标签 -->
|
||||
<g v-for="eq in equipments" :key="'lab-'+eq.k" font-family="Arial,sans-serif">
|
||||
@@ -232,19 +240,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 下半: 分段 Tab -->
|
||||
<div class="card sec-card">
|
||||
<div class="tab-bar">
|
||||
<div v-for="t in tabs" :key="t.k" :class="['tab', { active: tab === t.k }]" @click="tab = t.k">
|
||||
{{ t.label }}
|
||||
<span class="tab-cnt">{{ t.count }}</span>
|
||||
</div>
|
||||
<div class="tab-spacer"></div>
|
||||
<span class="hd-cnt">{{ rtItems.length }} 项实时数据</span>
|
||||
<!-- 入口段 -->
|
||||
<section class="sec">
|
||||
<div class="sec-title-bar">入口段
|
||||
<span class="ch-badge">在线 {{ onlinePlans.length }} / 生产中 {{ producingPlan ? 1 : 0 }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 入口段 -->
|
||||
<div v-if="tab === 'entry'" class="pane">
|
||||
<div class="pane">
|
||||
<div class="pane-grid entry-grid">
|
||||
<!-- 左:在线计划 + 移动按钮 -->
|
||||
<div class="sub-card">
|
||||
@@ -291,8 +292,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 酸洗段 -->
|
||||
<div v-if="tab === 'acid'" class="pane">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 酸洗段 -->
|
||||
<section class="sec">
|
||||
<div class="sec-title-bar">酸洗段(5 槽)</div>
|
||||
<div class="pane">
|
||||
<div class="tank-grid">
|
||||
<div v-for="(a, i) in acid" :key="'a'+i" class="sub-card">
|
||||
<div class="sub-header acid">{{ i+1 }}# 酸洗槽</div>
|
||||
@@ -323,8 +329,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 漂洗段 -->
|
||||
<div v-if="tab === 'rinse'" class="pane">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 漂洗段 -->
|
||||
<section class="sec">
|
||||
<div class="sec-title-bar">漂洗段(5 级)+ 烘干</div>
|
||||
<div class="pane">
|
||||
<div class="tank-grid">
|
||||
<div v-for="(r, i) in rinse" :key="'r'+i" class="sub-card">
|
||||
<div class="sub-header rinse">{{ i+1 }}# 漂洗</div>
|
||||
@@ -358,8 +369,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 出口段 -->
|
||||
<div v-if="tab === 'exit'" class="pane">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 出口段 -->
|
||||
<section class="sec">
|
||||
<div class="sec-title-bar">出口段</div>
|
||||
<div class="pane">
|
||||
<div class="pane-grid two">
|
||||
<div class="sub-card">
|
||||
<div class="sub-header">三辊张力装置</div>
|
||||
@@ -393,12 +409,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 跟踪表 -->
|
||||
<div v-if="tab === 'track'" class="pane">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 跟踪表 -->
|
||||
<section class="sec">
|
||||
<div class="sec-title-bar">物料跟踪表 <span class="hd-cnt">{{ equipments.length }} 台设备</span></div>
|
||||
<div class="pane">
|
||||
<table class="data-table compact tracking-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:32px;">#</th>
|
||||
<th style="width:72px;">段</th>
|
||||
<th>设备</th>
|
||||
<th style="width:64px;">状态</th>
|
||||
<th>当前钢卷</th>
|
||||
@@ -411,6 +433,11 @@
|
||||
<tr v-for="(eq, i) in equipments" :key="eq.k"
|
||||
:class="{ 'row-active': eq.k === currentEquipment.k, 'row-passed': i < currentEquipment.idx, 'row-pending': i > currentEquipment.idx }">
|
||||
<td class="td-num">{{ i + 1 }}</td>
|
||||
<td>
|
||||
<span class="sec-tag" :style="{ color: sectionColor(eq.section), borderColor: sectionColor(eq.section) }">
|
||||
{{ eq.section }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ eq.label }}</td>
|
||||
<td>
|
||||
<span v-if="eq.k === currentEquipment.k" class="badge badge-yellow">加工中</span>
|
||||
@@ -425,7 +452,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -435,27 +462,35 @@ function rnd(base, amp) { return base + (Math.random() - 0.5) * amp }
|
||||
function fix(v, n = 1) { return Number(v).toFixed(n) }
|
||||
|
||||
const EQUIPMENTS = [
|
||||
{ k:'uncoiler', label:'开卷机', type:'coiler', code:'DC-1' },
|
||||
{ k:'straightener', label:'九辊矫直机', type:'rolls9', code:'STR-9' },
|
||||
{ k:'crop_shear', label:'切头剪', type:'shear', code:'CRP' },
|
||||
{ k:'acid1', label:'酸洗槽1', type:'acid', idx:0 },
|
||||
{ k:'acid2', label:'酸洗槽2', type:'acid', idx:1 },
|
||||
{ k:'acid3', label:'酸洗槽3', type:'acid', idx:2 },
|
||||
{ k:'acid4', label:'酸洗槽4', type:'acid', idx:3 },
|
||||
{ k:'acid5', label:'酸洗槽5', type:'acid', idx:4 },
|
||||
{ k:'rinse', label:'漂洗段', type:'rinse' },
|
||||
{ k:'dryer', label:'热风烘干段', type:'dryer' },
|
||||
{ k:'br1', label:'1号夹送辊', type:'pinch', code:'BR-1' },
|
||||
{ k:'loop', label:'活套坑', type:'loop' },
|
||||
{ k:'br2', label:'2号夹送辊', type:'pinch', code:'BR-2' },
|
||||
{ k:'br3', label:'3号夹送辊', type:'pinch', code:'BR-3' },
|
||||
{ k:'tension', label:'三辊张力装置', type:'tension3', code:'TEN-3' },
|
||||
{ k:'leveler', label:'平整机', type:'leveler', code:'SPM' },
|
||||
{ k:'tail_shear', label:'切尾剪', type:'shear', code:'TLS' },
|
||||
{ k:'oiler', label:'静电涂油机', type:'oiler', code:'EOL' },
|
||||
{ k:'recoiler', label:'卷取机', type:'recoiler', code:'REC-1' },
|
||||
{ k:'uncoiler', label:'开卷机', type:'coiler', code:'DC-1', section:'入口段' },
|
||||
{ k:'straightener', label:'九辊矫直机', type:'rolls9', code:'STR-9', section:'入口段' },
|
||||
{ k:'crop_shear', label:'切头剪', type:'shear', code:'CRP', section:'入口段' },
|
||||
{ k:'acid1', label:'酸洗槽1', type:'acid', idx:0, section:'酸洗段' },
|
||||
{ k:'acid2', label:'酸洗槽2', type:'acid', idx:1, section:'酸洗段' },
|
||||
{ k:'acid3', label:'酸洗槽3', type:'acid', idx:2, section:'酸洗段' },
|
||||
{ k:'acid4', label:'酸洗槽4', type:'acid', idx:3, section:'酸洗段' },
|
||||
{ k:'acid5', label:'酸洗槽5', type:'acid', idx:4, section:'酸洗段' },
|
||||
{ k:'rinse', label:'漂洗段', type:'rinse', section:'清洗段' },
|
||||
{ k:'dryer', label:'热风烘干段', type:'dryer', section:'烘干段' },
|
||||
{ k:'br1', label:'1号夹送辊', type:'pinch', code:'BR-1', section:'出口段' },
|
||||
{ k:'loop', label:'活套坑', type:'loop', section:'出口段' },
|
||||
{ k:'br2', label:'2号夹送辊', type:'pinch', code:'BR-2', section:'出口段' },
|
||||
{ k:'br3', label:'3号夹送辊', type:'pinch', code:'BR-3', section:'出口段' },
|
||||
{ k:'tension', label:'三辊张力装置', type:'tension3', code:'TEN-3', section:'出口段' },
|
||||
{ k:'leveler', label:'平整机', type:'leveler', code:'SPM', section:'出口段' },
|
||||
{ k:'tail_shear', label:'切尾剪', type:'shear', code:'TLS', section:'出口段' },
|
||||
{ k:'oiler', label:'静电涂油机', type:'oiler', code:'EOL', section:'出口段' },
|
||||
{ k:'recoiler', label:'卷取机', type:'recoiler', code:'REC-1', section:'出口段' },
|
||||
]
|
||||
|
||||
const SECTION_COLORS = {
|
||||
'入口段': '#5a8fc8',
|
||||
'酸洗段': '#ffaa44',
|
||||
'清洗段': '#3aa0c8',
|
||||
'烘干段': '#e87a3a',
|
||||
'出口段': '#88c070',
|
||||
}
|
||||
|
||||
// 默认辊缝值 (mm)
|
||||
const DEFAULT_GAP = {
|
||||
straightener: 4.20,
|
||||
@@ -514,19 +549,9 @@ export default {
|
||||
_plansTimer: null,
|
||||
plans: [],
|
||||
moving: false,
|
||||
tab: 'entry',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
tabs() {
|
||||
return [
|
||||
{ k: 'entry', label: '入口段', count: this.onlinePlans.length },
|
||||
{ k: 'acid', label: '酸洗段', count: 5 },
|
||||
{ k: 'rinse', label: '漂洗段', count: 5 },
|
||||
{ k: 'exit', label: '出口段', count: 3 },
|
||||
{ k: 'track', label: '物料跟踪表', count: this.equipments.length },
|
||||
]
|
||||
},
|
||||
entryItems() {
|
||||
const f = (v, n=1) => Number(v).toFixed(n)
|
||||
return [
|
||||
@@ -554,6 +579,29 @@ export default {
|
||||
const step = (xEnd - xStart) / (n - 1)
|
||||
return EQUIPMENTS.map((e, i) => ({ ...e, x: xStart + step * i }))
|
||||
},
|
||||
sections() {
|
||||
const eqs = this.equipments
|
||||
const groups = []
|
||||
let cur = null
|
||||
eqs.forEach((e, i) => {
|
||||
if (!cur || cur.name !== e.section) {
|
||||
if (cur) groups.push(cur)
|
||||
cur = { name: e.section, color: SECTION_COLORS[e.section] || '#9aa8b6',
|
||||
startIdx: i, endIdx: i, x0: e.x, x1: e.x }
|
||||
} else {
|
||||
cur.endIdx = i
|
||||
cur.x1 = e.x
|
||||
}
|
||||
})
|
||||
if (cur) groups.push(cur)
|
||||
const half = (eqs[1].x - eqs[0].x) / 2
|
||||
return groups.map(g => ({
|
||||
...g,
|
||||
bandX: g.x0 - half + 4,
|
||||
bandW: (g.x1 - g.x0) + half * 2 - 8,
|
||||
labelX: (g.x0 + g.x1) / 2,
|
||||
}))
|
||||
},
|
||||
weldX() {
|
||||
const p = Math.max(0, Math.min(1, this.weld.position))
|
||||
return 50 + (1850 - 50) * p
|
||||
@@ -635,6 +683,7 @@ export default {
|
||||
methods: {
|
||||
fmt(v, n = 2) { return v != null && v !== '' ? Number(v).toFixed(n) : '—' },
|
||||
fix(v, n = 1) { return Number(v).toFixed(n) },
|
||||
sectionColor(s) { return SECTION_COLORS[s] || '#9aa8b6' },
|
||||
fmtTime(t) { return t ? t.slice(0, 16).replace('T', ' ') : '—' },
|
||||
async loadPlans() {
|
||||
try {
|
||||
@@ -777,7 +826,12 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
@import '@/assets/styles/variables';
|
||||
|
||||
.mat-page { display: flex; flex-direction: column; gap: 10px; }
|
||||
.mat-page { display: flex; flex-direction: column; gap: 10px; min-width: 0; overflow-x: hidden; }
|
||||
.sec-tag {
|
||||
display: inline-block; font-size: 10.5px; padding: 1px 6px;
|
||||
border: 1px solid; border-radius: 3px; background: rgba(0,0,0,.25);
|
||||
font-weight: 600; letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.status-bar {
|
||||
display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
|
||||
@@ -791,34 +845,28 @@ export default {
|
||||
|
||||
.line-wrap { padding: 0; }
|
||||
.line-body { padding: 6px 10px 10px; background: #0a1218; }
|
||||
.line-svg { width: 100%; height: 280px; display: block; }
|
||||
.line-svg { width: 100%; height: 312px; display: block; }
|
||||
|
||||
.sec-card { padding: 0; }
|
||||
.tab-bar {
|
||||
display: flex; align-items: center; gap: 2px;
|
||||
border-bottom: 1px solid $border; padding: 0 10px;
|
||||
background: #161d24;
|
||||
.sec {
|
||||
background: $bg-card; border: 1px solid $border; border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.tab {
|
||||
padding: 9px 16px; font-size: 12.5px; cursor: pointer;
|
||||
color: $text-muted; border-bottom: 2px solid transparent;
|
||||
user-select: none;
|
||||
.sec-title-bar {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 7px 12px; font-size: 13px; color: #c8d4e0; font-weight: 600;
|
||||
background: #161d24; border-bottom: 1px solid $border;
|
||||
}
|
||||
.tab:hover { color: #c8d4e0; }
|
||||
.tab.active { color: $sms-highlight; border-bottom-color: $sms-highlight; font-weight: 600; }
|
||||
.tab-cnt {
|
||||
display: inline-block; margin-left: 5px;
|
||||
background: rgba(255,255,255,.06); border-radius: 9px;
|
||||
padding: 0 6px; font-size: 10px; color: #9aa8b6;
|
||||
}
|
||||
.tab.active .tab-cnt { background: rgba(0,200,255,.15); color: #00c8ff; }
|
||||
.tab-spacer { flex: 1; }
|
||||
|
||||
.pane { padding: 10px 12px; }
|
||||
.pane-grid { display: grid; grid-template-columns: 1fr 1.4fr; gap: 10px; }
|
||||
.pane-grid.two { grid-template-columns: 1fr 1fr; }
|
||||
.entry-grid { grid-template-columns: 1fr 1.3fr; }
|
||||
.tank-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; }
|
||||
.pane-grid {
|
||||
display: grid; gap: 10px;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr);
|
||||
}
|
||||
.pane-grid.two { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
|
||||
.entry-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr); }
|
||||
.tank-grid {
|
||||
display: grid; gap: 8px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
}
|
||||
|
||||
.sub-card { background: #0f161c; border: 1px solid $border; border-radius: 4px; display: flex; flex-direction: column; }
|
||||
.sub-header {
|
||||
|
||||
Reference in New Issue
Block a user