fix(ui): 修复图标重叠、改名、去头像、重构生产计划页

- 修复全局紧凑样式导致前缀图标与输入框文字重叠问题
  (补充 .el-input--prefix 的 padding-left 覆盖规则)
- 登录页恢复 38px 输入高度并修正前缀图标对齐
- 所有环境标题统一改为「科伦普冷轧双机架控制平台」
  (env.development / env.production / env.staging / vue.config.js / settings.js)
- Navbar 去除用户头像图片,改用图标+用户名文字,移除「布局设置」菜单项
- 生产计划页重构为左侧钢卷列表+右侧详情布局(与工艺管理页一致)
  工艺方案改为可选项,支持不绑定方案直接新建钢卷

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-28 15:54:45 +08:00
parent f1e0303645
commit 41abb2337a
9 changed files with 397 additions and 339 deletions

View File

@@ -1,10 +1,10 @@
# 页面标题
VUE_APP_TITLE = 若依管理系统
VUE_APP_TITLE = 科伦普冷轧双机架控制平台
# 开发环境配置
ENV = 'development'
# 若依管理系统/开发环境
# 科伦普冷轧双机架控制平台/开发环境
VUE_APP_BASE_API = '/dev-api'
# 路由懒加载

View File

@@ -1,8 +1,8 @@
# 页面标题
VUE_APP_TITLE = 若依管理系统
VUE_APP_TITLE = 科伦普冷轧双机架控制平台
# 生产环境配置
ENV = 'production'
# 若依管理系统/生产环境
# 科伦普冷轧双机架控制平台/生产环境
VUE_APP_BASE_API = '/prod-api'

View File

@@ -1,5 +1,5 @@
# 页面标题
VUE_APP_TITLE = 若依管理系统
VUE_APP_TITLE = 科伦普冷轧双机架控制平台
BABEL_ENV = production
@@ -8,5 +8,5 @@ NODE_ENV = production
# 测试环境配置
ENV = 'staging'
# 若依管理系统/测试环境
# 科伦普冷轧双机架控制平台/测试环境
VUE_APP_BASE_API = '/stage-api'

View File

@@ -37,9 +37,14 @@ $--font-path: '~element-ui/lib/theme-chalk/fonts';
font-size: 12px !important;
padding: 0 8px !important;
}
// 有前缀图标时恢复左侧 padding避免图标与文字重叠
.el-input--prefix .el-input__inner { padding-left: 28px !important; }
.el-input__prefix { height: 28px !important; line-height: 28px !important; display: flex; align-items: center; }
.el-input--medium .el-input__inner { height: 28px !important; line-height: 28px !important; }
.el-input--small .el-input__inner { height: 24px !important; line-height: 24px !important; }
.el-input--mini .el-input__inner { height: 22px !important; line-height: 22px !important; }
.el-input--mini.el-input--prefix .el-input__inner { padding-left: 26px !important; }
.el-input--small.el-input--prefix .el-input__inner { padding-left: 26px !important; }
// textarea
.el-textarea__inner { font-size: 12px !important; padding: 4px 8px !important; }

View File

@@ -27,16 +27,14 @@
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
<div class="avatar-wrapper">
<img :src="avatar" class="user-avatar">
<i class="el-icon-user-solid user-icon" />
<span class="user-name">{{ name }}</span>
<i class="el-icon-caret-bottom" />
</div>
<el-dropdown-menu slot="dropdown">
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
</router-link>
<el-dropdown-item @click.native="setting = true">
<span>布局设置</span>
</el-dropdown-item>
<el-dropdown-item divided @click.native="logout">
<span>退出登录</span>
</el-dropdown-item>
@@ -71,7 +69,7 @@ export default {
computed: {
...mapGetters([
'sidebar',
'avatar',
'name',
'device'
]),
setting: {
@@ -173,25 +171,33 @@ export default {
}
.avatar-container {
margin-right: 30px;
margin-right: 16px;
.avatar-wrapper {
margin-top: 5px;
position: relative;
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
color: #5a6f85;
font-size: 12px;
.user-avatar {
cursor: pointer;
width: 40px;
height: 40px;
border-radius: 10px;
.user-icon {
font-size: 16px;
color: #1d4e89;
}
.user-name {
font-size: 12px;
color: #2c3e50;
max-width: 80px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.el-icon-caret-bottom {
cursor: pointer;
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
font-size: 11px;
color: #909399;
}
}
}

View File

@@ -2,7 +2,7 @@ module.exports = {
/**
* 网站标题
*/
title: '冷轧双机架二级控制系统',
title: '科伦普冷轧双机架控制平台',
/**
* 侧边栏主题:固定使用工业深色主题

View File

@@ -56,7 +56,7 @@
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
<span>Copyright © 2024-2025 科伦普冷轧双机架控制平台 All Rights Reserved.</span>
</div>
</div>
</template>
@@ -178,14 +178,23 @@ export default {
padding: 25px 25px 5px 25px;
.el-input {
height: 38px;
input {
height: 38px;
.el-input__inner {
height: 38px !important;
line-height: 38px !important;
}
&.el-input--prefix .el-input__inner {
padding-left: 34px !important;
}
.el-input__prefix {
height: 38px !important;
line-height: 38px !important;
}
}
.input-icon {
height: 39px;
width: 14px;
height: 38px;
width: 16px;
margin-left: 2px;
vertical-align: middle;
}
}
.login-tip {

View File

@@ -1,208 +1,190 @@
<template>
<div class="plan-page">
<!-- 上方轧制队列 -->
<div class="queue-section">
<div class="section-header">
<!-- 左侧钢卷队列列表 -->
<div class="left-panel">
<div class="panel-header">
<span>轧制队列</span>
</div>
<el-table :data="planList" border size="mini" class="queue-table"
:row-class-name="queueRowClass"
@current-change="handleQueueSelect"
highlight-current-row
height="calc(50vh - 120px)">
<el-table-column label="序号" prop="sortNo" width="50" align="center" fixed />
<el-table-column label="钢卷编号" prop="inMatNo" width="110" />
<el-table-column label="合金牌号" prop="alloyNo" width="90" />
<el-table-column label="采料厚度(mm)" prop="inMatThick" width="105" align="right" />
<el-table-column label="成品厚度(mm)" prop="outThick" width="105" align="right" />
<el-table-column label="采料宽度(mm)" prop="inMatWidth" width="105" align="right" />
<el-table-column label="道次数" prop="passCount" width="65" align="center" />
<el-table-column label="采料长度(m)" prop="inMatLength" width="100" align="right" />
<el-table-column label="采料重量(t)" prop="inMatWeight" width="100" align="right" />
<el-table-column label="采料外径(mm)" prop="inMatOd" width="105" align="right" />
<el-table-column label="采料内径(mm)" prop="inMatId" width="105" align="right" />
<el-table-column label="生产状态" prop="prodStatus" width="80" align="center">
<template slot-scope="{ row }">
<span :class="prodStatusClass(row.prodStatus)">{{ prodStatusLabel(row.prodStatus) }}</span>
</template>
</el-table-column>
<el-table-column label="工艺方案" prop="recipeNo" min-width="100" />
</el-table>
</div>
<!-- 下方左侧道次 + 右侧操作区 -->
<div class="bottom-section">
<!-- 左侧轧制工艺 -->
<div class="pass-panel">
<div class="section-header">
<span>轧制工艺</span>
<span v-if="selectedPlan" class="recipe-tag">{{ selectedPlan.recipeNo }}</span>
</div>
<el-table :data="passList" border size="mini" class="pass-table"
:row-class-name="passRowClass"
height="calc(50vh - 130px)">
<el-table-column label="道次" prop="passNo" width="45" align="center" fixed />
<el-table-column label="入口厚(mm)" prop="inThick" width="85" align="right" />
<el-table-column label="出口厚(mm)" prop="outThick" width="85" align="right" />
<el-table-column label="轧制力(kN)" prop="rollForce" width="85" align="right" />
<el-table-column label="入口张力" prop="inTension" width="75" align="right" />
<el-table-column label="出口张力" prop="outTension" width="75" align="right" />
<el-table-column label="最高速度" prop="maxSpeed" width="75" align="right" />
<el-table-column label="入口单位张力" prop="inUnitTension" width="100" align="right" />
<el-table-column label="出口单位张力" prop="outUnitTension" width="100" align="right" />
<el-table-column label="压下量(mm)" prop="reduction" width="85" align="right">
<template slot-scope="{ row }">
<span class="calc-val">{{ row.reduction }}</span>
</template>
</el-table-column>
<el-table-column label="总压下量(mm)" prop="totalReduction" width="100" align="right">
<template slot-scope="{ row }">
<span class="calc-val">{{ row.totalReduction }}</span>
</template>
</el-table-column>
</el-table>
<el-button type="primary" size="mini" icon="el-icon-plus" @click="handleAdd">新增</el-button>
</div>
<!-- 右侧查询 + 操作 -->
<div class="op-panel">
<div class="section-header"><span>条件查询</span></div>
<div class="query-form">
<el-form size="mini" label-width="72px">
<el-form-item label="钢卷编号">
<el-input v-model="query.inMatNo" clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="日期范围">
<el-date-picker v-model="query.dateRange" type="daterange"
range-separator="~" start-placeholder="开始" end-placeholder="结束"
value-format="yyyy-MM-dd" style="width:100%" />
</el-form-item>
<el-form-item label=" " label-width="72px">
<el-checkbox v-model="query.hideFinished">不显示生产完成</el-checkbox>
</el-form-item>
<el-form-item label=" " label-width="72px">
<el-button size="mini" type="primary" icon="el-icon-search" @click="loadList">条件查询</el-button>
<el-button size="mini" icon="el-icon-refresh" @click="resetQuery">全部显示</el-button>
</el-form-item>
</el-form>
</div>
<!-- 查询栏 -->
<div class="search-bar">
<el-input v-model="query.inMatNo" placeholder="钢卷编号" size="mini" clearable
prefix-icon="el-icon-search" @input="loadList" />
<el-checkbox v-model="query.hideFinished" size="mini" @change="loadList" style="margin-top:6px;font-size:11px">不显示完成</el-checkbox>
</div>
<div class="section-header" style="margin-top:8px"><span>队列操作</span></div>
<div class="op-buttons">
<el-button size="mini" type="primary" icon="el-icon-plus" @click="handleAdd">钢卷增加</el-button>
<el-button size="mini" icon="el-icon-edit" :disabled="!selectedPlan" @click="handleEdit">修改</el-button>
<el-button size="mini" type="danger" icon="el-icon-delete" :disabled="!selectedPlan" @click="handleDelete">删除</el-button>
<el-button size="mini" icon="el-icon-top" :disabled="!selectedPlan" @click="handleMoveUp">Up 上移</el-button>
<el-button size="mini" icon="el-icon-bottom" :disabled="!selectedPlan" @click="handleMoveDown">Down 下移</el-button>
<!-- 列表 -->
<div class="coil-list">
<div v-for="p in planList" :key="p.id"
:class="['coil-item', { active: selectedId === p.id }, prodItemClass(p.prodStatus)]"
@click="handleSelect(p)">
<div class="coil-item__no">
<span>{{ p.sortNo }}. {{ p.inMatNo }}</span>
<span :class="prodStatusClass(p.prodStatus)" class="coil-status-dot">{{ prodStatusLabel(p.prodStatus) }}</span>
</div>
<div class="coil-item__info">
<span>{{ p.alloyNo }}</span>
<span>{{ p.inMatThick }} {{ p.outThick }} mm</span>
<span v-if="p.recipeNo">{{ p.recipeNo }}</span>
</div>
</div>
<div v-if="planList.length === 0" class="empty-tip">暂无计划</div>
</div>
</div>
<!-- 底部状态栏 -->
<div class="footer-bar">
<div class="coil-info-block">
<span class="coil-label">当前带卷</span>
<span class="coil-field">卷号<b>{{ currentCoil.inMatNo || '—' }}</b></span>
<span class="coil-field">采料厚度<b>{{ currentCoil.inMatThick || '—' }} mm</b></span>
<span class="coil-field">成品厚度<b>{{ currentCoil.outThick || '—' }} mm</b></span>
<span class="coil-field">宽度<b>{{ currentCoil.inMatWidth || '—' }} mm</b></span>
<span class="coil-field">合金号<b>{{ currentCoil.alloyNo || '—' }}</b></span>
<!-- 右侧详情 -->
<div class="right-panel">
<div v-if="!form.id && !isNew" class="no-select">
<i class="el-icon-document"></i>
<p>请在左侧选择钢卷</p>
</div>
<div class="coil-divider"></div>
<div class="coil-info-block">
<span class="coil-label next">下一带卷</span>
<span class="coil-field">卷号<b>{{ nextCoil.inMatNo || '—' }}</b></span>
<span class="coil-field">采料厚度<b>{{ nextCoil.inMatThick || '—' }} mm</b></span>
<span class="coil-field">成品厚度<b>{{ nextCoil.outThick || '—' }} mm</b></span>
<span class="coil-field">宽度<b>{{ nextCoil.inMatWidth || '—' }} mm</b></span>
<span class="coil-field">合金号<b>{{ nextCoil.alloyNo || '—' }}</b></span>
</div>
</div>
<!-- 新增/修改对话框 -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="640px"
:close-on-click-modal="false" append-to-body>
<el-form :model="form" :rules="rules" ref="formRef" size="mini" label-width="90px">
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="钢卷编号" prop="inMatNo">
<el-input v-model="form.inMatNo" :disabled="!isNew" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合金牌号" prop="alloyNo">
<el-input v-model="form.alloyNo" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="工艺方案" prop="recipeId">
<el-select v-model="form.recipeId" filterable placeholder="选择工艺方案" style="width:100%"
@change="onRecipeChange">
<el-option v-for="r in recipeOptions" :key="r.id"
:label="`${r.recipeNo}${r.alloyNo}`" :value="r.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="成品厚度" prop="outThick">
<el-input v-model="form.outThick"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采料厚度" prop="inMatThick">
<el-input v-model="form.inMatThick"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采料宽度" prop="inMatWidth">
<el-input v-model="form.inMatWidth"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采料长度">
<el-input v-model="form.inMatLength"><template slot="append">m</template></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采料重量">
<el-input v-model="form.inMatWeight"><template slot="append">t</template></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采料外径">
<el-input v-model="form.inMatOd"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采料内径">
<el-input v-model="form.inMatId"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备注">
<el-input v-model="form.remark" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer">
<el-button size="mini" @click="dialogVisible = false">取消</el-button>
<el-button size="mini" type="primary" @click="handleSave">确定</el-button>
</div>
</el-dialog>
<template v-else>
<!-- 顶部操作栏 -->
<div class="detail-header">
<span>{{ isNew ? '新建钢卷计划' : form.inMatNo }}</span>
<div class="btn-group">
<el-button size="mini" type="primary" icon="el-icon-check" @click="handleSave">保存</el-button>
<el-button size="mini" icon="el-icon-refresh" @click="handleReset">重置</el-button>
<el-button v-if="!isNew" size="mini" icon="el-icon-top" @click="handleMoveUp">上移</el-button>
<el-button v-if="!isNew" size="mini" icon="el-icon-bottom" @click="handleMoveDown">下移</el-button>
<el-button v-if="!isNew" size="mini" type="danger" icon="el-icon-delete" @click="handleDelete">删除</el-button>
</div>
</div>
<!-- 基本信息表单 -->
<el-form :model="form" :rules="rules" ref="formRef" size="mini" label-width="88px" class="coil-form">
<el-row :gutter="16">
<el-col :span="6">
<el-form-item label="钢卷编号" prop="inMatNo">
<el-input v-model="form.inMatNo" :disabled="!isNew" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="合金牌号" prop="alloyNo">
<el-input v-model="form.alloyNo" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="工艺方案">
<el-select v-model="form.recipeId" filterable clearable placeholder="可选" style="width:100%"
@change="onRecipeChange">
<el-option v-for="r in recipeOptions" :key="r.id"
:label="`${r.recipeNo}${r.alloyNo}`" :value="r.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="采料厚度" prop="inMatThick">
<el-input v-model="form.inMatThick"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="成品厚度" prop="outThick">
<el-input v-model="form.outThick"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="4">
<el-form-item label="采料宽度">
<el-input v-model="form.inMatWidth"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="采料长度">
<el-input v-model="form.inMatLength"><template slot="append">m</template></el-input>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="采料重量">
<el-input v-model="form.inMatWeight"><template slot="append">t</template></el-input>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="采料外径">
<el-input v-model="form.inMatOd"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="采料内径">
<el-input v-model="form.inMatId"><template slot="append">mm</template></el-input>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label="备注">
<el-input v-model="form.remark" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 轧制工艺道次只读展示 -->
<div class="pass-section">
<div class="pass-header">
<span>轧制工艺参数</span>
<span v-if="form.recipeNo" class="recipe-tag">{{ form.recipeNo }}</span>
<span v-else class="recipe-tag gray">未绑定工艺方案</span>
</div>
<el-table :data="passList" border size="mini" class="pass-table"
:row-class-name="passRowClass" height="calc(100vh - 400px)">
<el-table-column label="道次" prop="passNo" width="50" align="center" fixed>
<template slot-scope="{ row }"><b>{{ row.passNo }}</b></template>
</el-table-column>
<el-table-column label="入口厚度(mm)" prop="inThick" width="100" align="right" />
<el-table-column label="出口厚度(mm)" prop="outThick" width="100" align="right" />
<el-table-column label="宽度(mm)" prop="width" width="85" align="right" />
<el-table-column label="轧制力(kN)" prop="rollForce" width="85" align="right" />
<el-table-column label="入口张力(kN)" prop="inTension" width="95" align="right" />
<el-table-column label="出口张力(kN)" prop="outTension" width="95" align="right" />
<el-table-column label="最高速度(m/min)" prop="maxSpeed" width="110" align="right" />
<el-table-column label="入口单位张力" prop="inUnitTension" width="100" align="right" />
<el-table-column label="出口单位张力" prop="outUnitTension" width="100" align="right" />
<el-table-column label="压下量(mm)" prop="reduction" width="90" align="right">
<template slot-scope="{ row }"><span class="calc-val">{{ row.reduction }}</span></template>
</el-table-column>
<el-table-column label="总压下量(mm)" prop="totalReduction" width="100" align="right">
<template slot-scope="{ row }"><span class="calc-val">{{ row.totalReduction }}</span></template>
</el-table-column>
</el-table>
</div>
<!-- 底部当前/下一带卷信息栏 -->
<div class="footer-bar">
<div class="coil-info-block">
<span class="coil-label">当前带卷</span>
<span class="coil-field">卷号<b>{{ currentCoil.inMatNo || '—' }}</b></span>
<span class="coil-field">采料厚度<b>{{ currentCoil.inMatThick || '—' }} mm</b></span>
<span class="coil-field">成品厚度<b>{{ currentCoil.outThick || '—' }} mm</b></span>
<span class="coil-field">宽度<b>{{ currentCoil.inMatWidth || '—' }} mm</b></span>
<span class="coil-field">合金号<b>{{ currentCoil.alloyNo || '—' }}</b></span>
</div>
<div class="coil-divider"></div>
<div class="coil-info-block">
<span class="coil-label next">下一带卷</span>
<span class="coil-field">卷号<b>{{ nextCoil.inMatNo || '—' }}</b></span>
<span class="coil-field">采料厚度<b>{{ nextCoil.inMatThick || '—' }} mm</b></span>
<span class="coil-field">成品厚度<b>{{ nextCoil.outThick || '—' }} mm</b></span>
<span class="coil-field">宽度<b>{{ nextCoil.inMatWidth || '—' }} mm</b></span>
<span class="coil-field">合金号<b>{{ nextCoil.alloyNo || '—' }}</b></span>
</div>
</div>
</template>
</div>
</div>
</template>
<script>
import { listPlan, getPlan, addPlan, updatePlan, delPlan, moveUpPlan, moveDownPlan } from '@/api/mill/plan'
import { listPlan, addPlan, updatePlan, delPlan, moveUpPlan, moveDownPlan } from '@/api/mill/plan'
import { listAllRecipe, getRecipeDetail } from '@/api/mill/recipe'
const emptyForm = () => ({
id: null, inMatNo: '', alloyNo: '', recipeId: null, recipeNo: '',
outThick: '', inMatThick: '', inMatWidth: '', inMatLength: '',
inMatWeight: '', inMatOd: '', inMatId: '', remark: ''
inMatWeight: '', inMatOd: '', inMatId: '', passCount: 0, remark: ''
})
export default {
@@ -210,31 +192,28 @@ export default {
data() {
return {
planList: [],
selectedPlan: null,
selectedId: null,
isNew: false,
form: emptyForm(),
passList: [],
recipeOptions: [],
query: { inMatNo: '', dateRange: null, hideFinished: false },
dialogVisible: false,
isNew: true,
form: emptyForm(),
query: { inMatNo: '', hideFinished: false },
rules: {
inMatNo: [{ required: true, message: '请输入钢卷编号', trigger: 'blur' }],
alloyNo: [{ required: true, message: '请输入合金牌号', trigger: 'blur' }],
recipeId: [{ required: true, message: '请选择工艺方案', trigger: 'change' }],
outThick: [{ required: true, message: '请输入成品厚度', trigger: 'blur' }],
inMatThick:[{ required: true, message: '请输入采料厚度', trigger: 'blur' }],
inMatWidth:[{ required: true, message: '请输入采料宽度', trigger: 'blur' }],
inMatNo: [{ required: true, message: '请输入钢卷编号', trigger: 'blur' }],
alloyNo: [{ required: true, message: '请输入合金牌号', trigger: 'blur' }],
inMatThick: [{ required: true, message: '请输入采料厚度', trigger: 'blur' }],
outThick: [{ required: true, message: '请输入成品厚度', trigger: 'blur' }],
}
}
},
computed: {
dialogTitle() { return this.isNew ? '新增钢卷' : '修改钢卷' },
currentCoil() {
return this.planList.find(p => p.prodStatus === '1') || {}
},
nextCoil() {
const idx = this.planList.findIndex(p => p.prodStatus === '1')
return idx >= 0 && idx + 1 < this.planList.length ? this.planList[idx + 1] : (this.planList[0] || {})
if (idx >= 0 && idx + 1 < this.planList.length) return this.planList[idx + 1]
return this.planList.find(p => p.prodStatus === '0') || {}
}
},
mounted() {
@@ -244,24 +223,17 @@ export default {
methods: {
loadList() {
const params = { inMatNo: this.query.inMatNo }
if (this.query.hideFinished) params.prodStatus = '!2'
if (this.query.dateRange && this.query.dateRange.length === 2) {
params.beginDate = this.query.dateRange[0]
params.endDate = this.query.dateRange[1]
}
if (this.query.hideFinished) params.hideProdStatus = '2'
listPlan(params).then(res => {
this.planList = res.data || []
})
},
resetQuery() {
this.query = { inMatNo: '', dateRange: null, hideFinished: false }
this.loadList()
},
handleQueueSelect(row) {
this.selectedPlan = row
if (!row) { this.passList = []; return }
if (row.recipeId) {
getRecipeDetail(row.recipeId).then(res => {
handleSelect(p) {
this.selectedId = p.id
this.isNew = false
this.form = { ...p }
if (p.recipeId) {
getRecipeDetail(p.recipeId).then(res => {
this.passList = (res.data && res.data.passList) || []
})
} else {
@@ -270,13 +242,9 @@ export default {
},
handleAdd() {
this.isNew = true
this.selectedId = null
this.form = emptyForm()
this.dialogVisible = true
},
handleEdit() {
this.isNew = false
this.form = { ...this.selectedPlan }
this.dialogVisible = true
this.passList = []
},
handleSave() {
this.$refs.formRef.validate(valid => {
@@ -286,49 +254,56 @@ export default {
const api = this.isNew ? addPlan : updatePlan
api(this.form).then(() => {
this.$message.success('保存成功')
this.dialogVisible = false
this.loadList()
this.isNew = false
})
})
},
handleReset() {
if (this.isNew) { this.form = emptyForm(); this.passList = [] }
else this.handleSelect({ ...this.form })
},
handleDelete() {
this.$confirm(`确定删除钢卷「${this.selectedPlan.inMatNo}」?`, '提示', { type: 'warning' }).then(() => {
delPlan(this.selectedPlan.id).then(() => {
this.$confirm(`确定删除钢卷「${this.form.inMatNo}」?`, '提示', { type: 'warning' }).then(() => {
delPlan(this.form.id).then(() => {
this.$message.success('删除成功')
this.selectedPlan = null
this.passList = []
this.form = emptyForm(); this.passList = []
this.selectedId = null; this.isNew = false
this.loadList()
})
})
},
handleMoveUp() {
moveUpPlan(this.selectedPlan.id).then(() => this.loadList())
moveUpPlan(this.form.id).then(() => this.loadList())
},
handleMoveDown() {
moveDownPlan(this.selectedPlan.id).then(() => this.loadList())
moveDownPlan(this.form.id).then(() => this.loadList())
},
onRecipeChange(recipeId) {
if (!recipeId) { this.passList = []; this.form.recipeNo = ''; return }
const r = this.recipeOptions.find(x => x.id === recipeId)
if (r) {
if (!this.form.alloyNo) this.form.alloyNo = r.alloyNo
if (!this.form.inMatThick) this.form.inMatThick = r.inThick
if (!this.form.outThick) this.form.outThick = r.outThick
if (!this.form.inMatWidth) this.form.inMatWidth = r.outWidth
this.form.recipeNo = r.recipeNo
getRecipeDetail(recipeId).then(res => {
this.passList = (res.data && res.data.passList) || []
})
}
},
queueRowClass({ row }) {
if (row.prodStatus === '1') return 'row-rolling'
if (row.prodStatus === '0' && this.currentCoil.sortNo && row.sortNo === this.currentCoil.sortNo + 1) return 'row-next'
return ''
},
passRowClass({ rowIndex }) {
return rowIndex % 2 === 0 ? '' : 'alt-row'
},
prodStatusLabel(s) {
return { '0': '待轧', '1': '轧制中', '2': '已完成', '9': '异常' }[s] || s
return { '0': '待轧', '1': '轧制中', '2': '已完成', '9': '异常' }[s] || '待轧'
},
prodStatusClass(s) {
return { '0': 'status-wait', '1': 'status-rolling', '2': 'status-done', '9': 'status-err' }[s] || ''
return { '0': 'dot-wait', '1': 'dot-rolling', '2': 'dot-done', '9': 'dot-err' }[s] || 'dot-wait'
},
prodItemClass(s) {
return { '1': 'item-rolling', '2': 'item-done' }[s] || ''
}
}
}
@@ -337,67 +312,164 @@ export default {
<style scoped lang="scss">
.plan-page {
display: flex;
flex-direction: column;
height: calc(100vh - 84px);
background: #f0f2f5;
padding: 8px 12px;
padding: 10px 12px;
box-sizing: border-box;
gap: 8px;
gap: 0;
}
/* ── 通用 section header ── */
.section-header {
/* ── 左侧面板 ── */
.left-panel {
width: 240px;
flex-shrink: 0;
background: #fff;
border: 1px solid #dde1e6;
border-radius: 3px;
display: flex;
flex-direction: column;
margin-right: 10px;
overflow: hidden;
}
.panel-header {
background: #1c2b3a;
color: #ecf0f1;
padding: 6px 12px;
font-size: 12px;
font-weight: 700;
padding: 7px 10px;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
font-weight: 700;
flex-shrink: 0;
}
.search-bar {
padding: 6px 8px;
border-bottom: 1px solid #e4e7ed;
flex-shrink: 0;
}
.coil-list {
flex: 1;
overflow-y: auto;
}
.coil-item {
padding: 7px 10px;
border-bottom: 1px solid #f0f2f5;
cursor: pointer;
transition: background .15s;
&:hover { background: #f7f9fc; }
&.active { background: #e8f0fb; border-left: 3px solid #1d4e89; }
&.item-rolling { background: #fff8ee; }
&.item-done { opacity: 0.55; }
&__no {
font-size: 12px;
font-weight: 700;
color: #1c2b3a;
display: flex;
justify-content: space-between;
align-items: center;
}
&__info {
display: flex;
gap: 6px;
font-size: 11px;
color: #7f8c8d;
margin-top: 2px;
}
}
.coil-status-dot {
font-size: 10px;
font-weight: 400;
&.dot-wait { color: #909399; }
&.dot-rolling { color: #d68910; font-weight: 700; }
&.dot-done { color: #2471a3; }
&.dot-err { color: #c0392b; font-weight: 700; }
}
.empty-tip {
text-align: center;
color: #bdc3c7;
padding: 24px 0;
font-size: 12px;
}
/* ── 右侧面板 ── */
.right-panel {
flex: 1;
background: #fff;
border: 1px solid #dde1e6;
border-radius: 3px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.no-select {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #bdc3c7;
font-size: 13px;
i { font-size: 48px; margin-bottom: 10px; }
}
.detail-header {
background: #1c2b3a;
color: #ecf0f1;
padding: 7px 12px;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
font-weight: 700;
flex-shrink: 0;
}
.btn-group { display: flex; gap: 6px; }
.coil-form {
padding: 10px 12px 4px;
flex-shrink: 0;
border-bottom: 1px solid #e4e7ed;
}
/* ── 道次区域 ── */
.pass-section {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.pass-header {
padding: 6px 12px;
background: #f7f9fc;
border-bottom: 1px solid #e4e7ed;
display: flex;
align-items: center;
gap: 10px;
font-size: 12px;
font-weight: 700;
color: #1c2b3a;
flex-shrink: 0;
border-radius: 3px 3px 0 0;
}
.recipe-tag {
font-size: 11px;
font-weight: 400;
color: #a9bcd0;
}
/* ── 上方队列 ── */
.queue-section {
background: #fff;
border: 1px solid #dde1e6;
border-radius: 3px;
display: flex;
flex-direction: column;
flex-shrink: 0;
}
.queue-table {
::v-deep .row-rolling td { background: #fef3e2 !important; }
::v-deep .row-next td { background: #fdfbe4 !important; }
::v-deep .el-table__row.current-row td { background: #e8f0fb !important; }
}
/* ── 下方区域 ── */
.bottom-section {
display: flex;
flex: 1;
gap: 8px;
overflow: hidden;
}
/* 左侧道次表 */
.pass-panel {
flex: 1;
background: #fff;
border: 1px solid #dde1e6;
border-radius: 3px;
display: flex;
flex-direction: column;
overflow: hidden;
color: #1d4e89;
background: #e8f0fb;
padding: 1px 6px;
border-radius: 2px;
&.gray { color: #909399; background: #f4f4f5; }
}
.pass-table {
@@ -411,40 +483,12 @@ export default {
color: #1d4e89;
}
/* 右侧操作区 */
.op-panel {
width: 260px;
flex-shrink: 0;
background: #fff;
border: 1px solid #dde1e6;
border-radius: 3px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.query-form {
padding: 10px 12px 4px;
border-bottom: 1px solid #e4e7ed;
}
.op-buttons {
padding: 10px 12px;
display: flex;
flex-direction: column;
gap: 6px;
.el-button { width: 100%; justify-content: flex-start; }
}
/* ── 底部状态栏 ── */
.footer-bar {
background: #1c2b3a;
border-radius: 3px;
padding: 6px 16px;
display: flex;
align-items: center;
gap: 0;
flex-shrink: 0;
font-size: 11px;
}
@@ -476,10 +520,4 @@ export default {
background: #2e4057;
margin: 0 20px;
}
/* ── 状态标签 ── */
.status-wait { color: #7f8c8d; }
.status-rolling { color: #d68910; font-weight: 700; }
.status-done { color: #2471a3; }
.status-err { color: #c0392b; font-weight: 700; }
</style>

View File

@@ -7,7 +7,7 @@ function resolve(dir) {
const CompressionPlugin = require('compression-webpack-plugin')
const name = process.env.VUE_APP_TITLE || '若依管理系统' // 网页标题
const name = process.env.VUE_APP_TITLE || '科伦普冷轧双机架控制平台' // 网页标题
const port = process.env.port || process.env.npm_config_port || 80 // 端口