8-锡膏管理(前后端)
1.数据库设计
MOM_SOLDER_CABINET_OPERATION 锡膏柜操作记录
MOM_SOLDER_ENTER_CHECK 锡膏红胶来料质检
MOM_SOLDER_INVENTORY 锡膏红胶库存数据
MOM_SOLDER_INVENTORY_LOG 锡膏红胶出入库数据
MOM_SOLDER_MOVE_LOG 锡膏红胶的移库记录
2.锡膏红胶标贴打印
2.1 操作流程
- 用户进入界面后,系统自动获取当前工厂下打印模板及打印机填充到下拉框
- 在用户输入物料编码、批次、供应商及数量时,自动按照批次解析成生产日期
- 在用户点击打印时自动形成前端输入数量的条码调用打印机及打印模板执行打印
2.2 调用打印服务
传入参数,后端会调用函数解析好SFC,通过打印服务打印SFC
条码格式: 物料编码,唯一码,批次解析后的周期,供应商编码-批次
打印机标识:type字段为锡膏绑定
打印服务调用: 先会获取打印模板,然后获取打印设备,前端调用接口获取 ,传回参数到后端调用打印机
2.3 前端
采用这种方式绑定:
<!-- 打印模板 -->
<VantSelect
:rules="[{ required: true, message: '打印模板不能为空'}]"
:value.sync="printerTemplateId"
label="打印模板"
:options="printTemplateData"/>
3.锡膏红胶质检
3.1 操作流程
- 用户进入锡膏红胶来料质检主界面后,自动获取当前工厂三个月内的质检数据填充到表单中,支持按照查询条件查询数据,
- 支持用户手动发起新增质检,前端在进入质检执行界面后,调用接口填充待质检项,业务手动填入质检数据执行质检;
- 对页面中的质检数据,需能勾选单行后点击编辑按钮调用后端接口获取质检数据执行编辑,也可勾选多行执行删除;对页面表单中的文件,也支持点击查看对应文件预览及下载。
3.2 后端逻辑参考
三张主要的质检表:
MOM_CHECK_DATA 确认项主表(表头)
MOM_CHECK_DATA_ITEM 确认项子表(存数据的表) 通过UUID关联 CHECK_ID唯一标识
MOM_CHECK_ITEM 确认项模板数据
操作流程:
用户先在配置项进行配置,配置数据会保存到MOM_CHECK_ITEM中去,MOM_CHECK_ITEM会生成一份模板的数据(配置项只有一份),进行角色配置后,登陆某一角色的时候,会带出对应的MOM_CHECK_ITEM出来可以填。
用户填写一份质检数据后,首先会生成一条MOM_CHECK_DATA数据,其中MOM_CHECK_DATA通过UUID关联到MOM_CHECK_DATA_ITEM的UUID,MOM_CHECK_DATA_ITEM存的就是质检数据,通过CHECK_ID关联到MOM_CHECK_ITEM的模板。
数据表关系: 一条质检数据,对应多条质检项数据,质检项数据又跟质检模板关联
MOM_CHECK_DATA的status设置为:SolderPaste_Check ,代表锡膏红胶专属
3.3 前端
前端参考solderpaste_enter_check.html和solderpaste_enter_check_add.html
3.3.1 新增质检
通过Layer.open方法跳转:
layer.open({
type: 2,
title: "新增质检",
closeBtn: 1,
area: ['85%', '100%'],
content: ROOT + `/SolderCabinet/solderpaste_enter_check_add.html`,
yes:function(index, layero){
layer.close(index);
searchBtn();
},
end: function () {
searchBtn();
},
success:function () {
//弹层开启后 Enter回车 遮罩层无限弹处理
$(':focus').blur();
}
});
yes和end是回调函数的处理,这里调用小框后,可以采取一个默认销毁层,直接进到end回调处理比较好
3.3.2 编辑质检
编辑质检,需要传入ID,方便后端抓取数据进行填充,可以采用这种方式进行传参:
function editClick(){
let selection = grid.selection;
if(selection.length !== 1){
layer.msg($.i18n.prop("hint.selectOnlyOne"),{icon:7});
return false;
}
let mainId = selection[0].ID;
layer.open({
type: 2,
title:false,
closeBtn:0,
area: ['100%', '100%'],
content: ROOT + `/SolderCabinet/solderpaste_enter_check_add.html?mainId=${mainId}`,
yes:function(index, layero){
layer.close(index);
searchBtn();
},
end: function () {
searchBtn();
},
success:function () {
//弹层开启后 Enter回车 遮罩层无限弹处理
$(':focus').blur();
}
});
}
3.3.3 踩坑点
html之间的传参有时候会需要传参中文,使用uri的方式直接传,会出现乱码,需要先编码,传参,解码后获取在处理:
let encodeitem = encodeURIComponent(selection[0].ITEM);
let encodetechnologyType = encodeURIComponent(selection[0].TECHNOLOGY_TYPE);
let encodebatch = encodeURIComponent(selection[0].BATCH);
直接进行监听,取值,调后端接口:
document.addEventListener('DOMContentLoaded', function() {
const urlParams = new URLSearchParams(window.location.search);
item = decodeURIComponent(urlParams.get('encodeitem'));
technologyType = decodeURIComponent(urlParams.get('encodetechnologyType'));
batch = decodeURIComponent(urlParams.get('encodebatch'));
// 标头
document.getElementById("item").value = item;
document.getElementById("technologyType").value = technologyType;
document.getElementById("batch").value = batch;
let parmas={
item:item,
technologyType:technologyType,
batch:batch
}
// 接口
});
需要传类似id的List到后端的时候,用逗号隔开传,不要转类似Json的
let ids = [];
for(let index in selection){
ids.push(selection[index].ID);
}
let params = {
site:SITE,
selectIds:ids.join(',')
};
后端用List或者String来接都可以,按照情况解析即可
4.锡膏红胶入库
操作流程: 用户点击进入锡膏红胶入库界面后,先选择工艺,随后扫条码,扫描条码时,自动解析条码规则,并按照工厂+物料编码+批次号的维度查询质检记录表中是否存在对应的质检记录,且校验唯一码是否在系统存在,存在对应质检记录且唯一码不在库存数据中存在即可扫描填充到表单 中,可重复扫描后一次性提交入库;
注意: 先去质检里面质检一条该批次下的合格数据,条码来源打印接口的输出
5.锡膏红胶移库
操作流程:
- 进入界面后,系统默认下拉框为按单瓶移库,操作人默认当前登录账号的工号;
- 描条码后,调用后端接口校验条码是否存在、状态是否在库且解析后的生产日期是否为当前库存数据中最早的锡膏红胶,
- 通过校验后解析条码带入表单中,并支持删除勾选行及提交现有表单信息、登录工厂及操作人调用后端接口执行保存;
- 不允许扫描多瓶后删除最新的提交,加校验
5.1 校验是否最早
5.1.1 单瓶
SQL直接按照批次号,序列号,入库时间这三个维度(顺序)进行查询,组装数据返回到前端
前端校验:将当前扫码的数据与当前的数据第一条进行比较,SFC匹配代表是最早的,同时设置一个游标来移动,确保每次扫码带出的都是最早的数据
async initSingleMove(){
// 先校验当前库存中是否有可以进行移库的数据
if(this.sfcData.length === 0){
Dialog.alert({message:'当前没有在库可移库的数据'});
this.sfc='';
this.$refs.sfc.focus();
return false;
}
// 先进行校验,判断当前输入的条码是不是后端返回中的第一条数据(FIFO)
if(this.sfc != this.sfcData[this.flag].sfc){
Dialog.alert({message:'当前条码不是库存中最早的数据'});
this.sfc='';
this.$refs.sfc.focus();
return false;
}
//组装参数调用校验条码接口
let params = {
site:this.site,
sfc:this.sfc
};
await verifySingleMoveData(params)
.then(res => {
if(res.code === '0'){
let data = res.data;
let addJudge = true;
//若条码重复扫描则不新增行
if(this.tableData.length===0){
data.id = this.sfcData[this.flag].id;
data.technologyType = this.sfcData[this.flag].technologyType;
data.weight = this.sfcData[this.flag].weight;
this.tableData.push(data);
this.flag = this.flag + 1;
}
for(let i=0;i<this.tableData.length;i++){
if(this.tableData[i].keyCode === data.keyCode){
addJudge = false;
break;
}
}
if(addJudge){
data.id = this.sfcData[this.flag].id;
data.technologyType = this.sfcData[this.flag].technologyType;
data.weight = this.sfcData[this.flag].weight;
this.tableData.push(data);
this.flag = this.flag + 1;
}
this.info_error = "";
} else {
Dialog.alert({message:res.message});
this.info_error = res.message;
return false;
}
});
},
删除校验:由于游标的存在,必须从下往上删除,确保每次删除的都是表单中最新的,最应该被删除的
onDelete(){
//获取表格数据
let $table = this.$refs.xTable;
//获取选中行数据
let list = $table.getCheckboxRecords();
let len = list.length;
//判断选中的长度
if(len < 1){
Notify({type:'danger',message:''+this.$t('请至少选择一条数据进行删除')});
return false;
}
//校验选中的数据是不是从下往上删除
if(len === 1){
console.log(list[0].id);
console.log(this.sfcData[this.flag - 1].id);
if(list[0].id != this.sfcData[this.flag - 1].id){
Dialog.alert({message:'删除请按照从下往上选择的规则'});
this.sfc='';
return false;
}
let index = this.tableData.findIndex(item => item._X_ROW_KEY === list[0]._X_ROW_KEY);
this.flag = this.flag - 1;
this.tableData.splice(index, 1);
}
if(len > 1 && len < this.tableData.length){
console.log(list);
for(let i = 0;i<list.length;i++){
console.log(list[i].id != this.sfcData[this.flag - 1].id);
if(list[i].id != this.sfcData[this.flag - 1].id){
Dialog.alert({message:'删除请按照从下往上选择的规则'});
this.sfc='';
return false;
}
let index = this.tableData.findIndex(item => item._X_ROW_KEY === list[i]._X_ROW_KEY);
this.flag = this.flag - 1;
this.tableData.splice(index, 1);
}
}
if(len === this.tableData.length){
this.tableData = [];
this.flag = 0;
}
},
5.1.2 批次
直接从库中按照批次号,序列号,入库时间这三个维度(顺序)进行查询,按照批次来匹配数据即可,不匹配则提示
5.2 移库和取消移库
校验通过,修改状态则进行移库,取消移库则是可以批量取消,同样是修改状态即可,这两种操作都会记录到MOM_SOLDER_INVENTORY_LOG记录表和MOM_SOLDER_MOVE_LOG中
6.库存主数据
操作流程:
- 入库:锡膏红胶入库
- 移库:锡膏红胶转移,拿到锡膏柜中
- 冷冻:在锡膏柜中进行冷冻,也就是移库完成了
- 报废:对其进行报废
6.1 锡膏报废
不是每个用户都可以进行报废操作,此处做一个权限管控,先去BASE中造一个角色组
角色编码:SolderCabinetAdministrators
@Override
public OperationResult verifyUser(String roleCode) {
UserInfo user = WebUserUtil.getUser();
if(user == null){
throw new RuntimeException("token is empty!");
}
String userId = user.getUserId();
String userName = user.getUserName();
String key = String.format("%s:%s:%s", "userRole", roleCode, userId);
// 先取redis再取base
String value = redisService.getValue(key);
if(StringUtils.isBlank(value)){
boolean checkResult = userUtils.checkUserRole(roleCode, userId, userName);
value = String.valueOf(checkResult);
redisService.set(key, value, 5, TimeUnit.MINUTES);
}
if(value.equals("true")){
return OperationResult.buildSuccessResult("true");
}
return OperationResult.buildSuccessResult("false");
}
通过权限管控控制是否展现报废按钮
**报废:**报废有三种情况:审核中,无需审核,审核完成,正常选择需要审批,选择人员就进入审核中,无需审批就是直接进入报废,审核过了就会进到审核完成。
6.2 审核
设置审核通过和审核驳回两种状态
6.3 库存数据盘点
按照状态进行数据盘点
7.状态表
库存状态:
库存状态 | 在库 | 移库 | 冷冻 | 报废 |
---|---|---|---|---|
0 | 1 | 2 | 3 |
操作类型:
操作类型 | 入库 | 移库 | 取消移库 | 冷冻 | 报废 |
---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 |
移库类型:
移库类型 | 单瓶 | 批次 |
---|---|---|
0 | 1 |
移库状态:
移库状态 | 移库中 | 移库完成(冷冻) | 取消移库 |
---|---|---|---|
0 | 1 | 2 |
审核状态:
审核状态 | 审核中 | 无需审核 | 审核通过 | 审核驳回 |
---|---|---|---|---|
0 | 1 | 2 | 3 |
审核操作:
审核操作 | 审核通过 | 审核驳回 |
---|---|---|
0 | 1 |