feat(roll): 完成轧辊管理全栈模块

DB:mill_roll(轧辊库)+ mill_roll_change(换辊记录),已执行到服务器

后端:
- MillRoll / MillRollChange domain
- Mapper 接口 + XML(keyProperty 正确:rollId/changeId)
- Service + ServiceImpl(换辊时自动更新轧辊 status 为 Online Use)
- MillRollController /mill/roll + MillRollChangeController /mill/rollChange

前端:
- api/mill/roll.js 8个接口函数
- views/mill/roll.vue 三段式布局
  ·上:换辊数据历史表格
  ·左下:当前辊系参数(6辊图形 CSS 圆圈 + 编号/径/时间展示)
  ·右下:轧辊库表格 + 条件查询 + 更换/添加/修改/删除操作
- 路由注册 /mill/roll

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-29 13:53:37 +08:00
parent c4dc5ded57
commit 01b6b810a6
16 changed files with 1436 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
package com.ruoyi.mill.controller;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.mill.domain.MillRollChange;
import com.ruoyi.mill.service.IMillRollChangeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/mill/rollChange")
public class MillRollChangeController extends BaseController {
@Autowired
private IMillRollChangeService changeService;
@GetMapping("/list")
public TableDataInfo list(MillRollChange query) {
startPage();
return getDataTable(changeService.selectList(query));
}
@GetMapping("/current")
public AjaxResult current() {
return AjaxResult.success(changeService.selectLatest());
}
@PostMapping
public AjaxResult add(@RequestBody MillRollChange change) {
return toAjax(changeService.addChange(change));
}
}

View File

@@ -0,0 +1,47 @@
package com.ruoyi.mill.controller;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.mill.domain.MillRoll;
import com.ruoyi.mill.service.IMillRollService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/mill/roll")
public class MillRollController extends BaseController {
@Autowired
private IMillRollService rollService;
@GetMapping("/list")
public TableDataInfo list(MillRoll query) {
startPage();
return getDataTable(rollService.selectList(query));
}
@GetMapping("/{id}")
public AjaxResult getInfo(@PathVariable Long id) {
return AjaxResult.success(rollService.selectById(id));
}
@PostMapping
public AjaxResult add(@RequestBody MillRoll roll) {
int rows = rollService.insert(roll);
if (rows > 0) {
return AjaxResult.success(roll.getRollId());
}
return AjaxResult.error("新增失败");
}
@PutMapping
public AjaxResult edit(@RequestBody MillRoll roll) {
return toAjax(rollService.update(roll));
}
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids) {
return toAjax(rollService.deleteByIds(ids));
}
}

View File

@@ -0,0 +1,33 @@
package com.ruoyi.mill.domain;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import java.math.BigDecimal;
/** 轧辊库 */
@Data
public class MillRoll extends BaseEntity {
private Long rollId;
/** 轧辊编号 */
private String rollNo;
/** 轧辊类型 WR/IR/BR */
private String rollType;
/** 使用状态 */
private String status;
/** 初始辊径(mm) */
private BigDecimal initialDia;
/** 当前辊径(mm) */
private BigDecimal currentDia;
/** 标志位 */
private String flag;
/** 删除标志 0正常 2删除 */
private String delFlag;
}

View File

@@ -0,0 +1,60 @@
package com.ruoyi.mill.domain;
import com.ruoyi.common.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/** 换辊记录 */
@Data
public class MillRollChange extends BaseEntity {
private Long changeId;
/** 换辊时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date changeTime;
/** 换辊状态 */
private String changeStatus;
/** 上工作辊编号 */
private String upperWrNo;
/** 上工作辊径(mm) */
private BigDecimal upperWrDia;
/** 下工作辊编号 */
private String lowerWrNo;
/** 下工作辊径(mm) */
private BigDecimal lowerWrDia;
/** 上中间辊编号 */
private String upperIrNo;
/** 上中间辊径(mm) */
private BigDecimal upperIrDia;
/** 下中间辊编号 */
private String lowerIrNo;
/** 下中间辊径(mm) */
private BigDecimal lowerIrDia;
/** 上支承辊编号 */
private String upperBrNo;
/** 上支承辊径(mm) */
private BigDecimal upperBrDia;
/** 下支承辊编号 */
private String lowerBrNo;
/** 下支承辊径(mm) */
private BigDecimal lowerBrDia;
/** 删除标志 0正常 2删除 */
private String delFlag;
}

View File

@@ -0,0 +1,13 @@
package com.ruoyi.mill.mapper;
import com.ruoyi.mill.domain.MillRollChange;
import java.util.List;
public interface MillRollChangeMapper {
List<MillRollChange> selectList(MillRollChange query);
MillRollChange selectLatest();
int insert(MillRollChange change);
}

View File

@@ -0,0 +1,20 @@
package com.ruoyi.mill.mapper;
import com.ruoyi.mill.domain.MillRoll;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface MillRollMapper {
List<MillRoll> selectList(MillRoll query);
MillRoll selectById(Long rollId);
int insert(MillRoll roll);
int update(MillRoll roll);
int deleteByIds(Long[] ids);
int updateStatus(@Param("rollId") Long rollId, @Param("status") String status);
}

View File

@@ -0,0 +1,13 @@
package com.ruoyi.mill.service;
import com.ruoyi.mill.domain.MillRollChange;
import java.util.List;
public interface IMillRollChangeService {
List<MillRollChange> selectList(MillRollChange query);
MillRollChange selectLatest();
int addChange(MillRollChange change);
}

View File

@@ -0,0 +1,17 @@
package com.ruoyi.mill.service;
import com.ruoyi.mill.domain.MillRoll;
import java.util.List;
public interface IMillRollService {
List<MillRoll> selectList(MillRoll query);
MillRoll selectById(Long rollId);
int insert(MillRoll roll);
int update(MillRoll roll);
int deleteByIds(Long[] ids);
}

View File

@@ -0,0 +1,77 @@
package com.ruoyi.mill.service.impl;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.mill.domain.MillRoll;
import com.ruoyi.mill.domain.MillRollChange;
import com.ruoyi.mill.mapper.MillRollChangeMapper;
import com.ruoyi.mill.mapper.MillRollMapper;
import com.ruoyi.mill.service.IMillRollChangeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
@Service
public class MillRollChangeServiceImpl implements IMillRollChangeService {
@Autowired
private MillRollChangeMapper changeMapper;
@Autowired
private MillRollMapper rollMapper;
@Override
public List<MillRollChange> selectList(MillRollChange query) {
return changeMapper.selectList(query);
}
@Override
public MillRollChange selectLatest() {
return changeMapper.selectLatest();
}
@Override
@Transactional(rollbackFor = Exception.class)
public int addChange(MillRollChange change) {
String user = SecurityUtils.getUsername();
change.setCreateBy(user);
change.setUpdateBy(user);
change.setDelFlag("0");
if (change.getChangeTime() == null) {
change.setChangeTime(new Date());
}
if (change.getChangeStatus() == null || change.getChangeStatus().isEmpty()) {
change.setChangeStatus("新辊换上");
}
int rows = changeMapper.insert(change);
// Update roll status to "Online Use" for each populated roll position
updateRollStatusByNo(change.getUpperWrNo(), "Online Use");
updateRollStatusByNo(change.getLowerWrNo(), "Online Use");
updateRollStatusByNo(change.getUpperIrNo(), "Online Use");
updateRollStatusByNo(change.getLowerIrNo(), "Online Use");
updateRollStatusByNo(change.getUpperBrNo(), "Online Use");
updateRollStatusByNo(change.getLowerBrNo(), "Online Use");
return rows;
}
/**
* Find roll by roll_no and update its status.
* Silently skips if roll_no is blank or roll not found.
*/
private void updateRollStatusByNo(String rollNo, String newStatus) {
if (rollNo == null || rollNo.trim().isEmpty()) {
return;
}
MillRoll query = new MillRoll();
query.setRollNo(rollNo.trim());
List<MillRoll> list = rollMapper.selectList(query);
if (!list.isEmpty()) {
rollMapper.updateStatus(list.get(0).getRollId(), newStatus);
}
}
}

View File

@@ -0,0 +1,49 @@
package com.ruoyi.mill.service.impl;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.mill.domain.MillRoll;
import com.ruoyi.mill.mapper.MillRollMapper;
import com.ruoyi.mill.service.IMillRollService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class MillRollServiceImpl implements IMillRollService {
@Autowired
private MillRollMapper rollMapper;
@Override
public List<MillRoll> selectList(MillRoll query) {
return rollMapper.selectList(query);
}
@Override
public MillRoll selectById(Long rollId) {
return rollMapper.selectById(rollId);
}
@Override
public int insert(MillRoll roll) {
String user = SecurityUtils.getUsername();
roll.setCreateBy(user);
roll.setUpdateBy(user);
roll.setDelFlag("0");
if (roll.getStatus() == null || roll.getStatus().isEmpty()) {
roll.setStatus("Offline");
}
return rollMapper.insert(roll);
}
@Override
public int update(MillRoll roll) {
roll.setUpdateBy(SecurityUtils.getUsername());
return rollMapper.update(roll);
}
@Override
public int deleteByIds(Long[] ids) {
return rollMapper.deleteByIds(ids);
}
}

View File

@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.mill.mapper.MillRollChangeMapper">
<resultMap id="BaseRM" type="com.ruoyi.mill.domain.MillRollChange">
<id property="changeId" column="change_id"/>
<result property="changeTime" column="change_time"/>
<result property="changeStatus" column="change_status"/>
<result property="upperWrNo" column="upper_wr_no"/>
<result property="upperWrDia" column="upper_wr_dia"/>
<result property="lowerWrNo" column="lower_wr_no"/>
<result property="lowerWrDia" column="lower_wr_dia"/>
<result property="upperIrNo" column="upper_ir_no"/>
<result property="upperIrDia" column="upper_ir_dia"/>
<result property="lowerIrNo" column="lower_ir_no"/>
<result property="lowerIrDia" column="lower_ir_dia"/>
<result property="upperBrNo" column="upper_br_no"/>
<result property="upperBrDia" column="upper_br_dia"/>
<result property="lowerBrNo" column="lower_br_no"/>
<result property="lowerBrDia" column="lower_br_dia"/>
<result property="delFlag" column="del_flag"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="remark" column="remark"/>
</resultMap>
<sql id="cols">
change_id, change_time, change_status,
upper_wr_no, upper_wr_dia, lower_wr_no, lower_wr_dia,
upper_ir_no, upper_ir_dia, lower_ir_no, lower_ir_dia,
upper_br_no, upper_br_dia, lower_br_no, lower_br_dia,
del_flag, create_by, create_time, update_by, update_time, remark
</sql>
<select id="selectList" resultMap="BaseRM">
SELECT <include refid="cols"/> FROM mill_roll_change
WHERE del_flag = '0'
<if test="changeStatus != null and changeStatus != ''">
AND change_status = #{changeStatus}
</if>
ORDER BY change_time DESC
</select>
<select id="selectLatest" resultMap="BaseRM">
SELECT <include refid="cols"/> FROM mill_roll_change
WHERE del_flag = '0'
ORDER BY change_id DESC
LIMIT 1
</select>
<insert id="insert" useGeneratedKeys="true" keyProperty="changeId">
INSERT INTO mill_roll_change (
change_time, change_status,
upper_wr_no, upper_wr_dia, lower_wr_no, lower_wr_dia,
upper_ir_no, upper_ir_dia, lower_ir_no, lower_ir_dia,
upper_br_no, upper_br_dia, lower_br_no, lower_br_dia,
del_flag, create_by, create_time, update_by, update_time, remark
) VALUES (
#{changeTime}, #{changeStatus},
#{upperWrNo}, #{upperWrDia}, #{lowerWrNo}, #{lowerWrDia},
#{upperIrNo}, #{upperIrDia}, #{lowerIrNo}, #{lowerIrDia},
#{upperBrNo}, #{upperBrDia}, #{lowerBrNo}, #{lowerBrDia},
'0', #{createBy}, NOW(), #{updateBy}, NOW(), #{remark}
)
</insert>
</mapper>

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.mill.mapper.MillRollMapper">
<resultMap id="BaseRM" type="com.ruoyi.mill.domain.MillRoll">
<id property="rollId" column="roll_id"/>
<result property="rollNo" column="roll_no"/>
<result property="rollType" column="roll_type"/>
<result property="status" column="status"/>
<result property="initialDia" column="initial_dia"/>
<result property="currentDia" column="current_dia"/>
<result property="flag" column="flag"/>
<result property="delFlag" column="del_flag"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="remark" column="remark"/>
</resultMap>
<sql id="cols">
roll_id, roll_no, roll_type, status, initial_dia, current_dia,
flag, del_flag, create_by, create_time, update_by, update_time, remark
</sql>
<select id="selectList" resultMap="BaseRM">
SELECT <include refid="cols"/> FROM mill_roll
WHERE del_flag = '0'
<if test="rollNo != null and rollNo != ''">
AND roll_no LIKE CONCAT('%', #{rollNo}, '%')
</if>
<if test="rollType != null and rollType != ''">
AND roll_type = #{rollType}
</if>
<if test="status != null and status != ''">
AND status = #{status}
</if>
ORDER BY roll_id ASC
</select>
<select id="selectById" resultMap="BaseRM">
SELECT <include refid="cols"/> FROM mill_roll
WHERE roll_id = #{rollId} AND del_flag = '0'
</select>
<insert id="insert" useGeneratedKeys="true" keyProperty="rollId">
INSERT INTO mill_roll (
roll_no, roll_type, status, initial_dia, current_dia,
flag, del_flag, create_by, create_time, update_by, update_time, remark
) VALUES (
#{rollNo}, #{rollType},
<choose>
<when test="status != null and status != ''">#{status}</when>
<otherwise>'Offline'</otherwise>
</choose>,
#{initialDia}, #{currentDia},
#{flag}, '0',
#{createBy}, NOW(), #{updateBy}, NOW(), #{remark}
)
</insert>
<update id="update">
UPDATE mill_roll
<set>
<if test="rollNo != null and rollNo != ''">roll_no = #{rollNo},</if>
<if test="rollType != null and rollType != ''">roll_type = #{rollType},</if>
<if test="status != null">status = #{status},</if>
<if test="initialDia != null">initial_dia = #{initialDia},</if>
<if test="currentDia != null">current_dia = #{currentDia},</if>
<if test="flag != null">flag = #{flag},</if>
<if test="remark != null">remark = #{remark},</if>
update_by = #{updateBy},
update_time = NOW()
</set>
WHERE roll_id = #{rollId} AND del_flag = '0'
</update>
<update id="deleteByIds">
UPDATE mill_roll SET del_flag = '2', update_time = NOW()
WHERE roll_id IN
<foreach collection="array" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</update>
<update id="updateStatus">
UPDATE mill_roll SET status = #{status}, update_time = NOW()
WHERE roll_id = #{rollId}
</update>
</mapper>

View File

@@ -0,0 +1,33 @@
import request from '@/utils/request'
export function listRoll(query) {
return request({ url: '/mill/roll/list', method: 'get', params: query })
}
export function getRoll(id) {
return request({ url: `/mill/roll/${id}`, method: 'get' })
}
export function addRoll(data) {
return request({ url: '/mill/roll', method: 'post', data })
}
export function updateRoll(data) {
return request({ url: '/mill/roll', method: 'put', data })
}
export function delRoll(ids) {
return request({ url: `/mill/roll/${ids}`, method: 'delete' })
}
export function listRollChange(query) {
return request({ url: '/mill/rollChange/list', method: 'get', params: query })
}
export function getCurrentRolls() {
return request({ url: '/mill/rollChange/current', method: 'get' })
}
export function addRollChange(data) {
return request({ url: '/mill/rollChange', method: 'post', data })
}

View File

@@ -205,6 +205,19 @@ export const constantRoutes = [
}
]
},
{
path: '/mill',
component: Layout,
hidden: true,
children: [
{
path: 'roll',
component: () => import('@/views/mill/roll'),
name: 'MillRoll',
meta: { title: '轧辊管理', icon: '' }
}
]
},
]
// 动态路由,基于用户权限动态去加载

View File

@@ -0,0 +1,822 @@
<template>
<div class="roll-page">
<!-- ===== TOP: 换辊数据 ===== -->
<div class="change-section">
<div class="section-header"><span>换辊数据</span></div>
<el-table :data="changeList" border size="mini" class="change-table"
height="calc(35vh - 80px)"
:row-class-name="changeRowClass">
<el-table-column label="换辊时间" prop="changeTime" width="140" align="center" />
<el-table-column label="状态" prop="changeStatus" width="90" align="center">
<template slot-scope="{ row }">
<span class="status-tag">{{ row.changeStatus }}</span>
</template>
</el-table-column>
<el-table-column label="上工作辊编号" prop="upperWrNo" width="110" />
<el-table-column label="上工作辊径" prop="upperWrDia" width="95" align="right" />
<el-table-column label="下工作辊编号" prop="lowerWrNo" width="110" />
<el-table-column label="下工作辊径" prop="lowerWrDia" width="95" align="right" />
<el-table-column label="上中间辊编号" prop="upperIrNo" width="110" />
<el-table-column label="上中间辊径" prop="upperIrDia" width="95" align="right" />
<el-table-column label="下中间辊编号" prop="lowerIrNo" width="110" />
<el-table-column label="下中间辊径" prop="lowerIrDia" width="95" align="right" />
<el-table-column label="上支承辊编号" prop="upperBrNo" width="110" />
<el-table-column label="上支承辊径" prop="upperBrDia" width="95" align="right" />
<el-table-column label="下支承辊编号" prop="lowerBrNo" width="110" />
<el-table-column label="下支承辊径" prop="lowerBrDia" width="95" align="right" />
</el-table>
</div>
<!-- ===== BOTTOM: Two panels ===== -->
<div class="bottom-section">
<!-- LEFT: 当前辊系参数 graphical display -->
<div class="rolls-panel">
<div class="section-header"><span>当前辊系参数</span></div>
<div class="rolls-body">
<div class="roll-stack">
<!-- 上支承辊 -->
<div class="roll-row">
<div class="roll-circle br-circle">
<span class="roll-abbr">上支承辊</span>
</div>
<div class="roll-info">
<div class="roll-info-row">
<span class="info-label">辊号</span>
<span class="info-val">{{ currentRolls.upperBrNo || '--' }}</span>
</div>
<div class="roll-info-row">
<span class="info-label">辊径</span>
<span class="info-val">{{ currentRolls.upperBrDia != null ? currentRolls.upperBrDia + ' mm' : '--' }}</span>
</div>
</div>
</div>
<div class="stack-connector"></div>
<!-- 上中间辊 -->
<div class="roll-row">
<div class="roll-circle ir-circle">
<span class="roll-abbr">上中间辊</span>
</div>
<div class="roll-info">
<div class="roll-info-row">
<span class="info-label">辊号</span>
<span class="info-val">{{ currentRolls.upperIrNo || '--' }}</span>
</div>
<div class="roll-info-row">
<span class="info-label">辊径</span>
<span class="info-val">{{ currentRolls.upperIrDia != null ? currentRolls.upperIrDia + ' mm' : '--' }}</span>
</div>
</div>
</div>
<div class="stack-connector"></div>
<!-- 上工作辊 -->
<div class="roll-row">
<div class="roll-circle wr-circle">
<span class="roll-abbr">上工作辊</span>
</div>
<div class="roll-info">
<div class="roll-info-row">
<span class="info-label">辊号</span>
<span class="info-val">{{ currentRolls.upperWrNo || '--' }}</span>
</div>
<div class="roll-info-row">
<span class="info-label">辊径</span>
<span class="info-val">{{ currentRolls.upperWrDia != null ? currentRolls.upperWrDia + ' mm' : '--' }}</span>
</div>
</div>
</div>
<div class="nip-line">
<span class="nip-label">轧制线</span>
</div>
<!-- 下工作辊 -->
<div class="roll-row">
<div class="roll-circle wr-circle">
<span class="roll-abbr">下工作辊</span>
</div>
<div class="roll-info">
<div class="roll-info-row">
<span class="info-label">辊号</span>
<span class="info-val">{{ currentRolls.lowerWrNo || '--' }}</span>
</div>
<div class="roll-info-row">
<span class="info-label">辊径</span>
<span class="info-val">{{ currentRolls.lowerWrDia != null ? currentRolls.lowerWrDia + ' mm' : '--' }}</span>
</div>
</div>
</div>
<div class="stack-connector"></div>
<!-- 下中间辊 -->
<div class="roll-row">
<div class="roll-circle ir-circle">
<span class="roll-abbr">下中间辊</span>
</div>
<div class="roll-info">
<div class="roll-info-row">
<span class="info-label">辊号</span>
<span class="info-val">{{ currentRolls.lowerIrNo || '--' }}</span>
</div>
<div class="roll-info-row">
<span class="info-label">辊径</span>
<span class="info-val">{{ currentRolls.lowerIrDia != null ? currentRolls.lowerIrDia + ' mm' : '--' }}</span>
</div>
</div>
</div>
<div class="stack-connector"></div>
<!-- 下支承辊 -->
<div class="roll-row">
<div class="roll-circle br-circle">
<span class="roll-abbr">下支承辊</span>
</div>
<div class="roll-info">
<div class="roll-info-row">
<span class="info-label">辊号</span>
<span class="info-val">{{ currentRolls.lowerBrNo || '--' }}</span>
</div>
<div class="roll-info-row">
<span class="info-label">辊径</span>
<span class="info-val">{{ currentRolls.lowerBrDia != null ? currentRolls.lowerBrDia + ' mm' : '--' }}</span>
</div>
</div>
</div>
</div>
<!-- 换辊时间 -->
<div class="change-time-bar">
<span class="ct-label">换辊时间</span>
<span class="ct-val">{{ currentRolls.changeTime || '--' }}</span>
</div>
</div>
</div>
<!-- RIGHT: 轧辊库数据 -->
<div class="rolldb-panel">
<div class="section-header"><span>轧辊库数据</span></div>
<!-- Query bar -->
<div class="query-bar">
<el-select v-model="queryField" size="mini" style="width:110px;margin-right:6px">
<el-option label="轧辊编号" value="rollNo" />
<el-option label="轧辊类型" value="rollType" />
<el-option label="使用状态" value="status" />
</el-select>
<el-input v-model="queryValue" size="mini" placeholder="查询条件" style="width:140px;margin-right:6px"
@keyup.enter.native="handleConditionQuery" clearable />
<el-button size="mini" type="primary" icon="el-icon-search" @click="handleConditionQuery">条件查询</el-button>
<el-button size="mini" icon="el-icon-refresh" @click="handleAllQuery">全部查询</el-button>
</div>
<!-- Action buttons -->
<div class="action-bar">
<el-button size="mini" type="primary" icon="el-icon-refresh-right" @click="openChangeRollDialog">更换轧辊</el-button>
<el-button size="mini" icon="el-icon-plus" @click="openAddDialog">轧辊添加</el-button>
<el-button size="mini" icon="el-icon-edit" :disabled="!selectedRoll" @click="openEditDialog">轧辊修改</el-button>
<el-button size="mini" icon="el-icon-delete" :disabled="!selectedRoll" @click="handleDelete">轧辊删除</el-button>
</div>
<!-- Roll library table -->
<el-table :data="rollList" border size="mini" class="roll-table"
height="calc(65vh - 160px)"
highlight-current-row
@current-change="handleRollSelect">
<el-table-column label="ID" prop="rollId" width="60" align="center" />
<el-table-column label="轧辊编号" prop="rollNo" width="110" />
<el-table-column label="轧辊类型" prop="rollType" width="80" align="center">
<template slot-scope="{ row }">
<span :class="rollTypeClass(row.rollType)">{{ row.rollType }}</span>
</template>
</el-table-column>
<el-table-column label="使用状态" prop="status" width="100" align="center">
<template slot-scope="{ row }">
<span :class="rollStatusClass(row.status)">{{ row.status }}</span>
</template>
</el-table-column>
<el-table-column label="初始辊径" prop="initialDia" width="85" align="right">
<template slot-scope="{ row }">{{ row.initialDia }} mm</template>
</el-table-column>
<el-table-column label="当前辊径" prop="currentDia" width="85" align="right">
<template slot-scope="{ row }">{{ row.currentDia }} mm</template>
</el-table-column>
<el-table-column label="标志位" prop="flag" width="65" align="center" />
<el-table-column label="备注" prop="remark" min-width="80" />
</el-table>
</div>
</div>
<!-- ===== Dialog: 轧辊添加/修改 ===== -->
<el-dialog :title="rollDialogTitle" :visible.sync="rollDialogVisible"
width="480px" append-to-body>
<el-form :model="rollForm" :rules="rollRules" ref="rollFormRef"
size="mini" label-width="90px">
<el-form-item label="轧辊编号" prop="rollNo">
<el-input v-model="rollForm.rollNo" placeholder="请输入轧辊编号" />
</el-form-item>
<el-form-item label="轧辊类型" prop="rollType">
<el-select v-model="rollForm.rollType" placeholder="请选择" style="width:100%">
<el-option label="WR (工作辊)" value="WR" />
<el-option label="IR (中间辊)" value="IR" />
<el-option label="BR (支承辊)" value="BR" />
</el-select>
</el-form-item>
<el-form-item label="使用状态">
<el-select v-model="rollForm.status" placeholder="请选择" style="width:100%">
<el-option label="Offline" value="Offline" />
<el-option label="Online Use" value="Online Use" />
<el-option label="Scrap" value="Scrap" />
<el-option label="Grinding" value="Grinding" />
</el-select>
</el-form-item>
<el-form-item label="初始辊径(mm)">
<el-input-number v-model="rollForm.initialDia" :precision="2" :step="0.01"
:min="0" style="width:100%" />
</el-form-item>
<el-form-item label="当前辊径(mm)">
<el-input-number v-model="rollForm.currentDia" :precision="2" :step="0.01"
:min="0" style="width:100%" />
</el-form-item>
<el-form-item label="标志位">
<el-input v-model="rollForm.flag" placeholder="如 U / D / 0" maxlength="4" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="rollForm.remark" type="textarea" :rows="2" />
</el-form-item>
</el-form>
<span slot="footer">
<el-button size="mini" @click="rollDialogVisible = false"> </el-button>
<el-button size="mini" type="primary" @click="submitRollForm"> </el-button>
</span>
</el-dialog>
<!-- ===== Dialog: 更换轧辊 ===== -->
<el-dialog title="更换轧辊" :visible.sync="changeDialogVisible"
width="640px" append-to-body>
<el-form :model="changeForm" ref="changeFormRef" size="mini" label-width="110px">
<el-form-item label="换辊状态">
<el-select v-model="changeForm.changeStatus" style="width:180px">
<el-option label="新辊换上" value="新辊换上" />
<el-option label="磨后换上" value="磨后换上" />
<el-option label="换辊检修" value="换辊检修" />
<el-option label="计划换辊" value="计划换辊" />
</el-select>
</el-form-item>
<div class="change-dialog-grid">
<!-- 上支承辊 -->
<div class="change-group-header">支承辊</div>
<el-form-item label="上支承辊编号">
<el-input v-model="changeForm.upperBrNo" placeholder="辊号" />
</el-form-item>
<el-form-item label="上支承辊径(mm)">
<el-input-number v-model="changeForm.upperBrDia" :precision="2" :min="0" style="width:100%" />
</el-form-item>
<el-form-item label="下支承辊编号">
<el-input v-model="changeForm.lowerBrNo" placeholder="辊号" />
</el-form-item>
<el-form-item label="下支承辊径(mm)">
<el-input-number v-model="changeForm.lowerBrDia" :precision="2" :min="0" style="width:100%" />
</el-form-item>
<!-- 中间辊 -->
<div class="change-group-header">中间辊</div>
<el-form-item label="上中间辊编号">
<el-input v-model="changeForm.upperIrNo" placeholder="辊号" />
</el-form-item>
<el-form-item label="上中间辊径(mm)">
<el-input-number v-model="changeForm.upperIrDia" :precision="2" :min="0" style="width:100%" />
</el-form-item>
<el-form-item label="下中间辊编号">
<el-input v-model="changeForm.lowerIrNo" placeholder="辊号" />
</el-form-item>
<el-form-item label="下中间辊径(mm)">
<el-input-number v-model="changeForm.lowerIrDia" :precision="2" :min="0" style="width:100%" />
</el-form-item>
<!-- 工作辊 -->
<div class="change-group-header">工作辊</div>
<el-form-item label="上工作辊编号">
<el-input v-model="changeForm.upperWrNo" placeholder="辊号" />
</el-form-item>
<el-form-item label="上工作辊径(mm)">
<el-input-number v-model="changeForm.upperWrDia" :precision="2" :min="0" style="width:100%" />
</el-form-item>
<el-form-item label="下工作辊编号">
<el-input v-model="changeForm.lowerWrNo" placeholder="辊号" />
</el-form-item>
<el-form-item label="下工作辊径(mm)">
<el-input-number v-model="changeForm.lowerWrDia" :precision="2" :min="0" style="width:100%" />
</el-form-item>
</div>
<el-form-item label="备注">
<el-input v-model="changeForm.remark" type="textarea" :rows="2" />
</el-form-item>
</el-form>
<span slot="footer">
<el-button size="mini" @click="changeDialogVisible = false"> </el-button>
<el-button size="mini" type="primary" @click="submitChangeForm">确认换辊</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {
listRoll, getRoll, addRoll, updateRoll, delRoll,
listRollChange, getCurrentRolls, addRollChange
} from '@/api/mill/roll'
export default {
name: 'MillRoll',
data() {
return {
// 换辊数据
changeList: [],
// 当前辊系
currentRolls: {},
// 轧辊库
rollList: [],
selectedRoll: null,
// 查询
queryField: 'rollNo',
queryValue: '',
// 轧辊添加/修改 dialog
rollDialogVisible: false,
rollDialogTitle: '轧辊添加',
isNewRoll: true,
rollForm: {
rollId: null,
rollNo: '',
rollType: 'WR',
status: 'Offline',
initialDia: 0,
currentDia: 0,
flag: '',
remark: ''
},
rollRules: {
rollNo: [{ required: true, message: '请输入轧辊编号', trigger: 'blur' }],
rollType: [{ required: true, message: '请选择轧辊类型', trigger: 'change' }]
},
// 换辊 dialog
changeDialogVisible: false,
changeForm: {
changeStatus: '新辊换上',
upperWrNo: '', upperWrDia: 0,
lowerWrNo: '', lowerWrDia: 0,
upperIrNo: '', upperIrDia: 0,
lowerIrNo: '', lowerIrDia: 0,
upperBrNo: '', upperBrDia: 0,
lowerBrNo: '', lowerBrDia: 0,
remark: ''
}
}
},
mounted() {
this.loadChangeList()
this.loadCurrentRolls()
this.loadRollList()
},
methods: {
// ===== Load data =====
loadChangeList() {
listRollChange({}).then(res => {
this.changeList = res.rows || []
})
},
loadCurrentRolls() {
getCurrentRolls().then(res => {
this.currentRolls = res.data || {}
})
},
loadRollList(query) {
listRoll(query || {}).then(res => {
this.rollList = res.rows || []
})
},
// ===== Query =====
handleConditionQuery() {
const q = {}
if (this.queryValue && this.queryValue.trim()) {
q[this.queryField] = this.queryValue.trim()
}
this.loadRollList(q)
},
handleAllQuery() {
this.queryValue = ''
this.loadRollList({})
},
// ===== Roll table =====
handleRollSelect(row) {
this.selectedRoll = row || null
},
// ===== Add roll =====
openAddDialog() {
this.isNewRoll = true
this.rollDialogTitle = '轧辊添加'
this.rollForm = {
rollId: null,
rollNo: '',
rollType: 'WR',
status: 'Offline',
initialDia: 0,
currentDia: 0,
flag: '',
remark: ''
}
this.rollDialogVisible = true
this.$nextTick(() => {
this.$refs.rollFormRef && this.$refs.rollFormRef.clearValidate()
})
},
// ===== Edit roll =====
openEditDialog() {
if (!this.selectedRoll) return
this.isNewRoll = false
this.rollDialogTitle = '轧辊修改'
this.rollForm = { ...this.selectedRoll }
this.rollDialogVisible = true
this.$nextTick(() => {
this.$refs.rollFormRef && this.$refs.rollFormRef.clearValidate()
})
},
submitRollForm() {
this.$refs.rollFormRef.validate(valid => {
if (!valid) return
const api = this.isNewRoll ? addRoll : updateRoll
api(this.rollForm).then(() => {
this.$message.success('保存成功')
this.rollDialogVisible = false
this.loadRollList()
}).catch(() => {
this.$message.error('保存失败,请检查输入')
})
})
},
// ===== Delete roll =====
handleDelete() {
if (!this.selectedRoll) return
this.$confirm(`确定删除轧辊「${this.selectedRoll.rollNo}」?`, '提示', { type: 'warning' }).then(() => {
delRoll(this.selectedRoll.rollId).then(() => {
this.$message.success('删除成功')
this.selectedRoll = null
this.loadRollList()
})
}).catch(() => {})
},
// ===== Change rolls =====
openChangeRollDialog() {
// Pre-fill from current rolls
const c = this.currentRolls || {}
this.changeForm = {
changeStatus: '新辊换上',
upperWrNo: c.upperWrNo || '',
upperWrDia: c.upperWrDia != null ? Number(c.upperWrDia) : 0,
lowerWrNo: c.lowerWrNo || '',
lowerWrDia: c.lowerWrDia != null ? Number(c.lowerWrDia) : 0,
upperIrNo: c.upperIrNo || '',
upperIrDia: c.upperIrDia != null ? Number(c.upperIrDia) : 0,
lowerIrNo: c.lowerIrNo || '',
lowerIrDia: c.lowerIrDia != null ? Number(c.lowerIrDia) : 0,
upperBrNo: c.upperBrNo || '',
upperBrDia: c.upperBrDia != null ? Number(c.upperBrDia) : 0,
lowerBrNo: c.lowerBrNo || '',
lowerBrDia: c.lowerBrDia != null ? Number(c.lowerBrDia) : 0,
remark: ''
}
this.changeDialogVisible = true
},
submitChangeForm() {
addRollChange(this.changeForm).then(() => {
this.$message.success('换辊记录已保存')
this.changeDialogVisible = false
this.loadChangeList()
this.loadCurrentRolls()
this.loadRollList()
}).catch(() => {
this.$message.error('换辊失败,请重试')
})
},
// ===== Style helpers =====
changeRowClass({ rowIndex }) {
return rowIndex === 0 ? 'row-latest' : ''
},
rollTypeClass(type) {
return { WR: 'type-wr', IR: 'type-ir', BR: 'type-br' }[type] || ''
},
rollStatusClass(status) {
return {
'Online Use': 'sts-online',
'Offline': 'sts-offline',
'Scrap': 'sts-scrap',
'Grinding': 'sts-grinding'
}[status] || ''
}
}
}
</script>
<style scoped lang="scss">
.roll-page {
display: flex;
flex-direction: column;
height: calc(100vh - 84px);
background: #f0f2f5;
padding: 8px 12px;
box-sizing: border-box;
gap: 8px;
}
.section-header {
background: #1c2b3a;
color: #ecf0f1;
padding: 6px 12px;
font-size: 12px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
border-radius: 3px 3px 0 0;
}
/* ===== TOP section ===== */
.change-section {
flex-shrink: 0;
height: 35vh;
background: #fff;
border: 1px solid #dde1e6;
border-radius: 3px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.change-table {
::v-deep .row-latest td {
background: #e8f0fb !important;
font-weight: 600;
}
}
.status-tag {
font-size: 11px;
color: #1d4e89;
font-weight: 600;
}
/* ===== BOTTOM section ===== */
.bottom-section {
display: flex;
flex: 1;
gap: 8px;
overflow: hidden;
min-height: 0;
}
/* ===== LEFT: 当前辊系参数 ===== */
.rolls-panel {
width: 280px;
flex-shrink: 0;
background: #fff;
border: 1px solid #dde1e6;
border-radius: 3px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.rolls-body {
flex: 1;
display: flex;
flex-direction: column;
padding: 10px 12px 6px;
overflow-y: auto;
}
.roll-stack {
flex: 1;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.roll-row {
display: flex;
align-items: center;
gap: 12px;
}
.roll-circle {
width: 56px;
height: 56px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
flex-direction: column;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.3), 0 1px 3px rgba(0,0,0,0.2);
}
.br-circle {
background: radial-gradient(circle at 35% 35%, #2e4a6e, #1c2b3a);
border: 2px solid #3d6494;
width: 60px;
height: 60px;
}
.ir-circle {
background: radial-gradient(circle at 35% 35%, #25446a, #182538);
border: 2px solid #3a5a86;
width: 50px;
height: 50px;
}
.wr-circle {
background: radial-gradient(circle at 35% 35%, #1f3a5c, #152232);
border: 2px solid #2d4d73;
width: 42px;
height: 42px;
}
.roll-abbr {
color: #a9bcd0;
font-size: 9px;
font-weight: 700;
text-align: center;
line-height: 1.2;
padding: 0 2px;
}
.roll-info {
display: flex;
flex-direction: column;
gap: 3px;
}
.roll-info-row {
display: flex;
align-items: center;
gap: 6px;
}
.info-label {
font-size: 11px;
color: #909399;
width: 28px;
flex-shrink: 0;
}
.info-val {
font-size: 11px;
color: #1d4e89;
font-weight: 600;
font-family: 'Courier New', monospace;
background: #f0f4fa;
padding: 1px 6px;
border-radius: 2px;
border: 1px solid #d0daea;
min-width: 80px;
}
.stack-connector {
width: 2px;
height: 10px;
background: #3d6494;
margin-left: 27px;
}
.nip-line {
width: 100%;
border-top: 2px dashed #1d4e89;
display: flex;
align-items: center;
justify-content: center;
margin: 4px 0;
position: relative;
}
.nip-label {
font-size: 10px;
color: #1d4e89;
background: #fff;
padding: 0 6px;
position: absolute;
right: 0;
top: -8px;
}
.change-time-bar {
padding: 8px 0 4px;
border-top: 1px solid #e4e7ed;
margin-top: 6px;
flex-shrink: 0;
}
.ct-label {
font-size: 11px;
color: #909399;
font-weight: 700;
}
.ct-val {
font-size: 12px;
color: #1d4e89;
font-weight: 600;
}
/* ===== RIGHT: 轧辊库数据 ===== */
.rolldb-panel {
flex: 1;
background: #fff;
border: 1px solid #dde1e6;
border-radius: 3px;
display: flex;
flex-direction: column;
overflow: hidden;
min-width: 0;
}
.query-bar {
padding: 8px 12px 4px;
border-bottom: 1px solid #eaeef2;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 4px;
flex-shrink: 0;
}
.action-bar {
padding: 6px 12px;
border-bottom: 1px solid #eaeef2;
display: flex;
gap: 6px;
flex-shrink: 0;
}
.roll-table {
flex: 1;
::v-deep .el-table__row.current-row td {
background: #e8f0fb !important;
}
}
/* Roll type badges */
.type-wr { color: #1d4e89; font-weight: 700; }
.type-ir { color: #5a5a9a; font-weight: 700; }
.type-br { color: #4a6a8a; font-weight: 700; }
/* Roll status */
.sts-online { color: #1d4e89; font-weight: 700; }
.sts-offline { color: #909399; }
.sts-scrap { color: #c0392b; }
.sts-grinding { color: #d68910; }
/* ===== Change dialog ===== */
.change-dialog-grid {
.change-group-header {
font-size: 11px;
font-weight: 700;
color: #fff;
background: #1c2b3a;
padding: 3px 10px;
border-radius: 2px;
margin: 8px 0 6px;
}
}
</style>

44
sql/mill_roll_tables.sql Normal file
View File

@@ -0,0 +1,44 @@
DROP TABLE IF EXISTS `mill_roll`;
CREATE TABLE `mill_roll` (
`roll_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`roll_no` varchar(20) NOT NULL COMMENT '轧辊编号',
`roll_type` char(2) NOT NULL COMMENT '轧辊类型 WR/IR/BR',
`status` varchar(20) DEFAULT 'Offline' COMMENT '使用状态',
`initial_dia` decimal(8,2) DEFAULT 0.00 COMMENT '初始辊径(mm)',
`current_dia` decimal(8,2) DEFAULT 0.00 COMMENT '当前辊径(mm)',
`flag` varchar(4) DEFAULT '' COMMENT '标志位 U/D/0等',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '删除标志 0正常 2删除',
PRIMARY KEY (`roll_id`),
UNIQUE KEY `uk_roll_no` (`roll_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='轧辊库';
DROP TABLE IF EXISTS `mill_roll_change`;
CREATE TABLE `mill_roll_change` (
`change_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`change_time` datetime DEFAULT NULL COMMENT '换辊时间',
`change_status` varchar(20) DEFAULT '' COMMENT '换辊状态',
`upper_wr_no` varchar(20) DEFAULT '' COMMENT '上工作辊编号',
`upper_wr_dia` decimal(8,2) DEFAULT 0.00 COMMENT '上工作辊径(mm)',
`lower_wr_no` varchar(20) DEFAULT '' COMMENT '下工作辊编号',
`lower_wr_dia` decimal(8,2) DEFAULT 0.00 COMMENT '下工作辊径(mm)',
`upper_ir_no` varchar(20) DEFAULT '' COMMENT '上中间辊编号',
`upper_ir_dia` decimal(8,2) DEFAULT 0.00 COMMENT '上中间辊径(mm)',
`lower_ir_no` varchar(20) DEFAULT '' COMMENT '下中间辊编号',
`lower_ir_dia` decimal(8,2) DEFAULT 0.00 COMMENT '下中间辊径(mm)',
`upper_br_no` varchar(20) DEFAULT '' COMMENT '上支承辊编号',
`upper_br_dia` decimal(8,2) DEFAULT 0.00 COMMENT '上支承辊径(mm)',
`lower_br_no` varchar(20) DEFAULT '' COMMENT '下支承辊编号',
`lower_br_dia` decimal(8,2) DEFAULT 0.00 COMMENT '下支承辊径(mm)',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '删除标志 0正常 2删除',
PRIMARY KEY (`change_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='换辊记录';