Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop

This commit is contained in:
shilixia 2022-01-21 15:19:49 +08:00
commit c35051df0c
60 changed files with 3832 additions and 1499 deletions

1
.gitignore vendored
View File

@ -4,4 +4,5 @@ deploy.sh
package-lock.json package-lock.json
.idea/ .idea/
.vscode/ .vscode/
.idea/
server/static/ server/static/

View File

@ -7,3 +7,11 @@ export function getPlanGantt(data) {
params: data params: data
}) })
} }
//合格率
export function getProcessYield(data) {
return request({
url: '/srm/process/yield/',
method: 'post',
data
})
}

View File

@ -0,0 +1,124 @@
<template>
<div class="tableMneu">
<div
class="mask"
v-if="isShowHeaderBox || menuOpen"
@click="maskClick"
></div>
<el-table
ref="tableMenu"
:data="tableData"
size="mini"
fit
style="width: 100%;border-top: 1px solid #f5f5f5"
row-key="id"
height="100%"
default-expand-all
highlight-current-row
@row-click="handlerRowClick"
@expand-change="handlerExpand"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
>
<el-table-column label="任务编号" prop="name" width="140" show-overflow-tooltip>
</el-table-column>
<el-table-column label="产品名称" prop="productName" width="120" show-overflow-tooltip>
</el-table-column>
<el-table-column label="产品型号" prop="productNum">
</el-table-column>
<el-table-column label="生产数量" prop="per">
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
props: {
list: Array,
BGScrollTop: Number
},
computed: {
tableData() {
return this.list;
}
},
watch: {
BGScrollTop: {
handler: function(newValue) {
let table = this.$refs.tableMenu.bodyWrapper;
table.scrollTo(0, newValue);
}
}
},
data() {
return {
checkList: [],
isShowHeaderBox: false,
menuOpen: false,
//当前点击的row
currentRow: {}
};
},
methods: {
handlerWatchScroll() {
let table = this.$refs.tableMenu.bodyWrapper;
table.addEventListener("scroll", e => {
this.$emit("TableScrollTop", e.srcElement.scrollTop);
});
},
handlerSelect(row) {
this.$refs.tableMenu.setCurrentRow(row);
},
handlerExpand(row, expand) {
// console.log(row, expanded);
this.$emit("handlerExpand", row, expand);
},
maskClick() {
this.isShowHeaderBox = false;
this.menuOpen = false;
this.currentRow = {};
},
handlerRowClick(row) {
this.$emit("handlerRowClick", row);
},
},
mounted() {
this.handlerWatchScroll();
}
};
</script>
<style lang="scss" scoped>
.el-table__header>.has-gutter>tr>th{
height: 41px!important;
}
.tableMneu {
width: 100%;
position: relative;
height: 100%;
.mask {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: transparent;
z-index: 9999;
}
}
.tableMneu {
.el-table th.gutter {
display: table-cell !important;
}
.el-table td,
.el-table th {
padding: 8px 0;
}
.el-table--border td {
border-right: none;
}
}
.el-table th.el-table__cell>.cell{
font-size: 12px!important;
}
</style>

View File

@ -19,7 +19,7 @@
@expand-change="handlerExpand" @expand-change="handlerExpand"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
> >
<el-table-column label="任务编号" prop="name"> <el-table-column label="任务编号" prop="name" width="140" show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column label="产品名称" prop="productName" width="120" show-overflow-tooltip> <el-table-column label="产品名称" prop="productName" width="120" show-overflow-tooltip>
</el-table-column> </el-table-column>

File diff suppressed because it is too large Load Diff

View File

@ -35,12 +35,56 @@
} }
}, },
mounted() { mounted() {
this.init(); this.openTheCamera();
}, },
methods: { methods: {
openTheCamera () {
this.$nextTick(function () {
let _this = this;
this.video = document.getElementById('video');
// 旧版本浏览器可能根本不支持mediaDevices我们首先设置一个空对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {}
}
// 一些浏览器实现了部分mediaDevices我们不能只分配一个对象
// 使用getUserMedia因为它会覆盖现有的属性
// 这里如果缺少getUserMedia属性就添加它
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
// 首先获取现存的getUserMedia(如果存在)
let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia;
// 有些浏览器不支持会返回错误信息
// 保持接口一致
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'))
}
// 否则使用Promise将调用包装到旧的navigator.getUserMedia
return new Promise(function (resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject)
})
}
}
let constraints = { audio: false, video: { width: this.videoWidth, height: this.videoHeight, transform: 'scaleX(-1)' } };
navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
// 旧的浏览器可能没有srcObject
if ('srcObject' in _this.video) {
_this.video.srcObject = stream
} else {
// 避免在新的浏览器中使用它因为它正在被弃用
_this.video.src = window.URL.createObjectURL(stream)
}
_this.video.onloadedmetadata = function (e) {
_this.video.play();
};
_this.init();
}).catch(err => {
console.log(err)
})
});
},
// 初始化设置 // 初始化设置
init() { init() {
this.video = document.getElementById('video'); // this.video = document.getElementById('video');
this.screenshotCanvas = document.getElementById('screenshotCanvas'); this.screenshotCanvas = document.getElementById('screenshotCanvas');
let canvas = document.getElementById('canvas'); let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d'); let context = canvas.getContext('2d');
@ -64,7 +108,6 @@
event.data.forEach(function (rect) { event.data.forEach(function (rect) {
context.strokeStyle = '#0764B7'; context.strokeStyle = '#0764B7';
context.strokeRect(rect.x, rect.y, rect.width, rect.height); context.strokeRect(rect.x, rect.y, rect.width, rect.height);
// window.plot(rect.x, rect.y, rect.width, rect.height+20);
// 避免重复发送请求 // 避免重复发送请求
if(!_this.uploadLock){ if(!_this.uploadLock){
_this.uploadLock = true; _this.uploadLock = true;
@ -133,7 +176,10 @@
that.uploadLock = false; that.uploadLock = false;
// this.$message.error('面部识别失败请重新验证'); // this.$message.error('面部识别失败请重新验证');
}); });
} },
closeCamera () {
this.video.srcObject.getTracks()[0].stop();
},
} }
} }
</script> </script>

View File

@ -24,12 +24,55 @@
} }
}, },
mounted() { mounted() {
this.init(); this.openTheCamera();
}, },
methods: { methods: {
openTheCamera () {
this.$nextTick(function () {
let _this = this;
this.video = document.getElementById('video');
// 旧版本浏览器可能根本不支持mediaDevices我们首先设置一个空对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {}
}
// 一些浏览器实现了部分mediaDevices我们不能只分配一个对象
// 使用getUserMedia因为它会覆盖现有的属性
// 这里如果缺少getUserMedia属性就添加它
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
// 首先获取现存的getUserMedia(如果存在)
let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia;
// 有些浏览器不支持会返回错误信息
// 保持接口一致
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'))
}
// 否则使用Promise将调用包装到旧的navigator.getUserMedia
return new Promise(function (resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject)
})
}
}
let constraints = { audio: false, video: { width: this.videoWidth, height: this.videoHeight, transform: 'scaleX(-1)' } };
navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
// 旧的浏览器可能没有srcObject
if ('srcObject' in _this.video) {
_this.video.srcObject = stream
} else {
// 避免在新的浏览器中使用它因为它正在被弃用
_this.video.src = window.URL.createObjectURL(stream)
}
_this.video.onloadedmetadata = function (e) {
_this.video.play();
};
_this.init();
}).catch(err => {
console.log(err)
})
});
},
// 初始化设置 // 初始化设置
init() { init() {
this.video = document.getElementById('video');
this.screenshotCanvas = document.getElementById('screenshotCanvas'); this.screenshotCanvas = document.getElementById('screenshotCanvas');
let canvas = document.getElementById('canvas'); let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d'); let context = canvas.getContext('2d');
@ -46,7 +89,6 @@
let _this = this; let _this = this;
//添加事件 //添加事件
tracker.on('track', function (event) { tracker.on('track', function (event) {
// 检测出人脸 绘画人脸位置 // 检测出人脸 绘画人脸位置
context.clearRect(0, 0, canvas.width, canvas.height); context.clearRect(0, 0, canvas.width, canvas.height);
// 给每个人脸绘制对应的框 // 给每个人脸绘制对应的框
@ -112,7 +154,10 @@
that.uploadLock = false; that.uploadLock = false;
// this.$message.error('面部识别失败请重新验证'); // this.$message.error('面部识别失败请重新验证');
}); });
} },
closeCamera () {
this.video.srcObject.getTracks()[0].stop();
},
} }
} }
</script> </script>

File diff suppressed because it is too large Load Diff

View File

@ -64,10 +64,10 @@
</el-tabs> </el-tabs>
</div> </div>
</div> </div>
<el-dialog :visible.sync="limitedPhoto"> <el-dialog :visible.sync="limitedPhoto" @close="closeCamera">
<div style="font-size: 28px;color: #333333;text-align: center;font-weight: bold;margin-bottom: 15px;">打卡</div> <div style="font-size: 28px;color: #333333;text-align: center;font-weight: bold;margin-bottom: 15px;">打卡</div>
<div class="testTracking"> <div class="testTracking">
<faceLogin name="faceLogin" @func="getMsgFormSon"></faceLogin> <faceLogin ref="faceTracking" name="faceLogin" @func="getMsgFormSon"></faceLogin>
</div> </div>
</el-dialog> </el-dialog>
</div> </div>
@ -179,9 +179,61 @@
//人脸登录 //人脸登录
takePhoto(){ takePhoto(){
this.limitedPhoto = true; this.limitedPhoto = true;
this.openTheCamera();
},
/*打开相机*/
openTheCamera () {
this.$nextTick(function () {
let _this = this;
this.thisVideo = document.getElementById('videoCamera');
// 旧版本浏览器可能根本不支持mediaDevices我们首先设置一个空对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {}
}
// 一些浏览器实现了部分mediaDevices我们不能只分配一个对象
// 使用getUserMedia因为它会覆盖现有的属性
// 这里如果缺少getUserMedia属性就添加它
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
// 首先获取现存的getUserMedia(如果存在)
let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia;
// 有些浏览器不支持会返回错误信息
// 保持接口一致
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'))
}
// 否则使用Promise将调用包装到旧的navigator.getUserMedia
return new Promise(function (resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject)
})
}
}
let constraints = { audio: false, video: { width: this.videoWidth, height: this.videoHeight, transform: 'scaleX(-1)' } };
navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
// 旧的浏览器可能没有srcObject
if ('srcObject' in _this.thisVideo) {
_this.thisVideo.srcObject = stream
} else {
// 避免在新的浏览器中使用它因为它正在被弃用
_this.thisVideo.src = window.URL.createObjectURL(stream)
}
_this.thisVideo.onloadedmetadata = function (e) {
_this.thisVideo.play();
}
}).catch(err => {
console.log(err)
})
});
},
/*关闭相机*/
closeCamera () {
debugger;
this.$refs.faceTracking.closeCamera();
// this.thisVideo.srcObject.getTracks()[0].stop();
}, },
getMsgFormSon(data){ getMsgFormSon(data){
this.limitedPhoto = data; // this.limitedPhoto = data;
}, },
}, },
}; };

View File

@ -121,7 +121,7 @@
<el-form <el-form
ref="Form" ref="Form"
:model="material" :model="material"
label-width="80px" label-width="100px"
label-position="right" label-position="right"
:rules="rule1" :rules="rule1"
> >
@ -131,41 +131,42 @@
<el-form-item label="物料编号" prop="number"> <el-form-item label="物料编号" prop="number">
<el-input v-model="material.number" placeholder="物料编号"/> <el-input v-model="material.number" placeholder="物料编号"/>
</el-form-item> </el-form-item>
<el-form-item label="规格型号" prop="specification"> <el-form-item label="规格型号">
<el-input v-model="material.specification" placeholder="规格型号"/> <el-input v-model="material.specification" placeholder="规格型号"/>
</el-form-item> </el-form-item>
<el-form-item label="计量单位" prop="unit"> <el-form-item label="计量单位">
<el-select style="width: 100%" v-model="material.unit" placeholder="请选择"> <el-select style="width: 100%" v-model="material.unit" placeholder="请选择">
<el-option <el-option
v-for="item in unitoptions" v-for="item in unitoptions"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value"> :value="item.value"
>
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="物料类别" prop="type"> <el-form-item label="物料类别">
<el-select style="width: 100%" v-model="material.type" placeholder="请选择物料类别">
<el-select style="width: 100%" v-model="material.type" placeholder="请选择">
<el-option <el-option
v-for="item in options" v-for="item in options"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value"> :value="item.value"
>
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="单片玻璃数量" prop="piece_count" v-if="material.type==1"> <el-form-item label="单片玻璃数量" v-if="material.type==1">
<el-input v-model="material.piece_count" placeholder="单片玻璃数量"/> <el-input v-model="material.piece_count" placeholder="单片玻璃数量"/>
</el-form-item> </el-form-item>
<el-form-item label="安全库存数">
<el-input-number style="width: 100%;" v-model="material.count_safe" :step="1" :min="0" step-strictly placeholder="安全库存数"></el-input-number>
<el-form-item label="排序" prop="sort_str"> </el-form-item>
<el-input v-model="material.sort_str" placeholder="排序"/> <el-form-item label="排序">
<el-input-number style="width: 100%;" v-model="material.sort_str" :step="1" :min="0" step-strictly placeholder="排序"></el-input-number>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div style="text-align: right"> <div style="text-align: right">
<el-button type="danger" @click="dialogVisible = false">取消</el-button> <el-button type="danger" @click="dialogVisible = false">取消</el-button>
@ -189,8 +190,14 @@
import {genTree} from "@/utils"; import {genTree} from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaultmaterial = { const defaultmaterial = {
name: "", name: null,
number: "", number: null,
specification: null,
unit: null,
type: null,
piece_count: null,
sort_str: null,
count_safe: 0,
processes: [], processes: [],
}; };
export default { export default {
@ -270,19 +277,13 @@
created() { created() {
this.getList(); this.getList();
this.getProcessList(); this.getProcessList();
}, },
methods: { methods: {
checkPermission, checkPermission,
//物料详情 //物料详情
handledetail(scope) handledetail(scope){
{
this.$router.push({name: "MaterialDetail", params: { id: scope.row.id,type: scope.row.type }, }) this.$router.push({name: "MaterialDetail", params: { id: scope.row.id,type: scope.row.type }, })
}, },
//选项卡切换 //选项卡切换
handleClick(tab) { handleClick(tab) {
this.listLoading = true; this.listLoading = true;
@ -311,10 +312,8 @@
}, },
//绑定工序 //绑定工序
handlebind(scope) { handlebind(scope) {
this.$router.push({name: "MaterialDO", params: {id: scope.row.id},}) this.$router.push({name: "MaterialDO", params: {id: scope.row.id},})
} },
,
handleFilter() { handleFilter() {
this.listQuery.page = 1; this.listQuery.page = 1;
this.getList(); this.getList();
@ -324,7 +323,7 @@
this.listQuery = { this.listQuery = {
page: 1, page: 1,
page_size: 20, page_size: 20,
} };
this.getList(); this.getList();
}, },
handleCreate() { handleCreate() {
@ -335,7 +334,6 @@
this.$refs["Form"].clearValidate(); this.$refs["Form"].clearValidate();
}); });
}, },
handleEdit(scope) { handleEdit(scope) {
this.material = Object.assign({}, scope.row); // copy obj this.material = Object.assign({}, scope.row); // copy obj
this.dialogType = "edit"; this.dialogType = "edit";
@ -359,7 +357,6 @@
console.error(err); console.error(err);
}); });
}, },
async confirm(form) { async confirm(form) {
this.$refs[form].validate((valid) => { this.$refs[form].validate((valid) => {
if (valid) { if (valid) {

View File

@ -82,8 +82,8 @@
<div style="text-align: right"> <div style="text-align: right">
<el-button type="danger" @click="dialogVisible = false">取消 <el-button type="danger" @click="dialogVisible = false">取消
</el-button> </el-button>
<!--<el-button type="primary" @click="recordformconfirm('Forms')">确认</el-button>--> <el-button type="primary" @click="recordformconfirm('Forms')">确认</el-button>
<el-button type="primary" @click="recordformcon">管理员授权</el-button> <!--<el-button type="primary" @click="recordformcon">管理员授权</el-button>-->
</div> </div>
</el-dialog> </el-dialog>
<!--表格展示--> <!--表格展示-->

View File

@ -3,15 +3,15 @@
<el-card style="margin-top: 2px"> <el-card style="margin-top: 2px">
<el-descriptions title="任务详情" :column="4" border style="margin-bottom: 20px"> <el-descriptions title="任务详情" :column="4" border style="margin-bottom: 20px">
<el-descriptions-item label="任务编号">{{productionplan.number}}</el-descriptions-item> <el-descriptions-item label="任务编号">{{productionplan.number}}</el-descriptions-item>
<el-descriptions-item label="产品名称" v-if="productionplan.product_">{{productionplan.product_.name}}</el-descriptions-item> <el-descriptions-item label="产品名称" v-if="productionplan.product_">{{productionplan.product_.name}}
<el-descriptions-item label="规格型号" v-if="productionplan.product_">{{productionplan.product_.specification}}</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="规格型号" v-if="productionplan.product_">{{productionplan.product_.specification}}
</el-descriptions-item>
<el-descriptions-item label="生产数量">{{productionplan.count}}</el-descriptions-item> <el-descriptions-item label="生产数量">{{productionplan.count}}</el-descriptions-item>
<el-descriptions-item label="生产状态">{{state_[productionplan.state]}}</el-descriptions-item> <el-descriptions-item label="生产状态">{{state_[productionplan.state]}}</el-descriptions-item>
<el-descriptions-item label="计划开工时间">{{productionplan.start_date}}</el-descriptions-item> <el-descriptions-item label="计划开工时间">{{productionplan.start_date}}</el-descriptions-item>
<el-descriptions-item label="计划完工时间">{{productionplan.end_date}}</el-descriptions-item> <el-descriptions-item label="计划完工时间">{{productionplan.end_date}}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<el-table <el-table
:data="wproduct" :data="wproduct"
border border
@ -20,38 +20,34 @@
style="width: 100%" style="width: 100%"
height="500" height="500"
> >
<el-table-column type="index" label="序号" width="50"/>
<el-table-column type="index" label="序号" width="50" /> <el-table-column label="玻璃编号/产品编号">
<template slot-scope="scope">{{ scope.row.number }}</template>
<el-table-column label="玻璃编号/产品编号" >
<template slot-scope="scope" >{{ scope.row.number }}</template>
</el-table-column> </el-table-column>
<el-table-column label="所在子工序"> <el-table-column label="所在子工序">
<template slot-scope="scope" >{{ <template slot-scope="scope">
scope.row.step_.name {{scope.row.step_.name}}
}}</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="产品状态" > <el-table-column label="产品状态">
<template slot-scope="scope">{{ <template slot-scope="scope">{{
actstate_[scope.row.act_state] actstate_[scope.row.act_state]
}}</template> }}
</template>
</el-table-column> </el-table-column>
<el-table-column label="生产状态"> <el-table-column label="生产状态">
<template slot-scope="scope" >{{ <template slot-scope="scope">
scope.row.step_.name {{scope.row.step_.name}}
}}</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="最后检验结果"> <el-table-column label="最后检验结果">
<template slot-scope="scope" > <template slot-scope="scope">
<el-span v-if="scope.row.last_test_result==false">不合格</el-span> <span v-if="scope.row.last_test_result==false">不合格</span>
<el-span v-if="scope.row.last_test_result==true">合格</el-span> <span v-if="scope.row.last_test_result==true">合格</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="生产记录"> <el-table-column label="生产记录">
<template slot-scope="scope" > <template slot-scope="scope">
<el-button @click="select(scope)">查看</el-button> <el-button @click="select(scope)">查看</el-button>
</template> </template>
</el-table-column> </el-table-column>
@ -61,19 +57,15 @@
width="220px" width="220px"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<el-link <el-link
v-if="checkPermission(['material_delete'])" v-if="checkPermission(['material_delete'])"
type="primary" type="primary"
@click="handleoption(scope)" @click="handleoption(scope)"
>生成流程卡</el-link >生成流程卡
> </el-link>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!--检验记录--> <!--检验记录-->
<el-dialog title="检验记录" :visible.sync="limitedCheckRecord"> <el-dialog title="检验记录" :visible.sync="limitedCheckRecord">
<el-table <el-table
@ -85,86 +77,70 @@
<el-table-column label="表单名称"> <el-table-column label="表单名称">
<template slot-scope="scope">{{ scope.row.form_.name }}</template> <template slot-scope="scope">{{ scope.row.form_.name }}</template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="操作"> <el-table-column align="center" label="操作">
<template slot-scope="scope"> <template slot-scope="scope">
<el-link <el-link
@click="handleRecordDetail(scope)" @click="handleRecordDetail(scope)"
>查看 >查看
</el-link> </el-link>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false"> </el-button> <el-button @click="dialogFormVisible = false"> </el-button>
</div> </div>
</el-dialog> </el-dialog>
<!--非检查表显示--> <!--非检查表显示-->
<el-dialog <el-dialog
width="60%"
:title="formName" :title="formName"
:visible.sync="recordVisible" :visible.sync="recordVisible"
:close-on-click-modal="false" :close-on-click-modal="false"
@close="recordCancel"
> >
<customForm <el-row>
v-if="recordVisible" <el-col v-for="item in fieldList" :key="item.id" :span="12">
:results="fieldList" <div class="items" v-if="item.field_type!=='draw'">
:hasPicture="hasPicture" <span class="itemLabel">{{item.field_name}}</span>
:formID="recordform" <span>{{item.field_value}}</span>
:wproduct="wproduct" </div>
:recordId="recordId" </el-col>
:isDisabled="isDisabled" <el-col v-for="item in fieldList" :key="item.id" :span="24">
@recordSubmit="recordSubmit" <div class="items" v-if="item.field_type==='draw'" style="height: 400px">
@recordSave="recordSave" <span class="itemLabel">{{item.field_name}}</span>
@recordCancel="recordCancel" <img style="width: 45%;vertical-align: text-top;" :src="'http://47.95.0.242:2222'+item.field_value"/>
/> </div>
</el-col>
</el-row>
</el-dialog> </el-dialog>
</el-card> </el-card>
</div> </div>
</template> </template>
<script> <script>
import {getProductionplan} from "@/api/pm";
import { getProductionplan,getsubproductionplanList } from "@/api/pm"; import {getwproductList, getrecordList,recordInit} from "@/api/wpm";
import { getwproductList,getrecordList} from "@/api/wpm"; import checkPermission from "@/utils/permission";
import checkPermission from "@/utils/permission"; const defaultrecordform = {enabled: false};
import customForm from '@/components/customForm/index' export default {
import {getrffieldList} from "@/api/mtm";
import { getTestRecord ,getTestRecordItem} from "@/api/qm";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaultrecordform = {enabled:false};
export default {
components: { Pagination,customForm },
data() { data() {
return { return {
productionplan:{ productionplan: {
number:"" number: ""
}, },
activeName:"1", activeName: "1",
wproduct:[], wproduct: [],
recordList:[], recordList: [],
limitedCheckRecord:false, limitedCheckRecord: false,
listQuery: { listQuery: {
page: 1, page: 1,
page_size: 20, page_size: 20,
}, },
state_:{ state_: {
10:'制定中', 10: '制定中',
20:'已下达', 20: '已下达',
30:'已接受', 30: '已接受',
40:'生产中', 40: '生产中',
50:'已完成', 50: '已完成',
60:'军检完成'}, 60: '军检完成'
},
actstate_: { actstate_: {
6: "待复检", 6: "待复检",
10: "操作进行中", 10: "操作进行中",
@ -177,26 +153,20 @@ export default {
26: "待夹层检验", 26: "待夹层检验",
70: "报废", 70: "报废",
}, },
process_json:null, process_json: null,
productionplanID:null, productionplanID: null,
dialogVisibleForm: false, dialogVisibleForm: false,
tableForm:{
name:'',
},
recordform: defaultrecordform, recordform: defaultrecordform,
dialogType: "new", dialogType: "new",
dialogVisible: false, dialogVisible: false,
dialogType1: "new", dialogType1: "new",
dialogVisible1: false, dialogVisible1: false,
tableForm: defaultrecordform,
checkForm: { checkForm: {
hhh: '', hhh: '',
}, },
recordVisible: false, recordVisible: false,
customfieldList: [], customfieldList: [],
recordform: null,
recordId: null, recordId: null,
fifo_detail: "", fifo_detail: "",
formName: "项目检查表", formName: "项目检查表",
@ -221,93 +191,65 @@ export default {
getProductionplan(this.id).then((response) => { getProductionplan(this.id).then((response) => {
if (response.data) { if (response.data) {
this.productionplan = response.data; this.productionplan = response.data;
this.productionplanID=response.data.id; this.productionplanID = response.data.id;
let process_json = []; let process_json = [];
for(let item in response.data.process_json){ for (let item in response.data.process_json) {
let obj = new Object(); let obj = new Object();
obj = response.data.process_json[item]; obj = response.data.process_json[item];
process_json.push(obj) process_json.push(obj)
} }
this.process_json = process_json;
this.process_json= process_json;
} }
}); });
}, },
getwproductList() {
getwproductList() getwproductList({production_plan: this.id, page: 0,}).then((response) => {
{
getwproductList({production_plan:this.id,page:0,}).then((response) => {
if (response.data) { if (response.data) {
this.wproduct = response.data; this.wproduct = response.data;
} }
}); });
}, },
//查看该玻璃检验记录表 //查看该玻璃检验记录表
handleoption(scope){ handleoption(scope) {
this.$router.push({name: "processcard", params: {id: scope.row.id},})
this.$router.push({name: "processcard", params: { id: scope.row.id }, })
}, },
//查看生产记录 //查看生产记录
select(scope) select(scope) {
{ this.limitedCheckRecord = true;
this.limitedCheckRecord=true; getrecordList({wproduct: scope.row.id, page: 0}).then(res => {
getrecordList({wproduct: scope.row.id,page:0}).then(res => {
if (res.code == 200) { if (res.code == 200) {
this.recordList = res.data; this.recordList = res.data;
} }
}) })
}, },
//点击记录里的查看 //点击记录里的查看
handleRecordDetail(scope) { handleRecordDetail(scope) {
let that = this; let that = this;
that.recordVisible = false; that.fieldList = [];
that.recordId = scope.row.id; recordInit(scope.row.id).then((res) => {
that.recordform = scope.row.form; if (res.code >= 200) {
that.formName = scope.row.form_.name;
getrffieldList({ form: this.recordform, page: 1, page_size: 100 }).then(
(response) => {
if (response.data) {
that.hasPicture = false;
let fieldList = response.data.results;
that.fieldList = [...fieldList];
let arr = fieldList.filter((item) => {
return item.field_type === "draw";
});
if (arr.length > 0) {
that.hasPicture = true;
}
getTestRecordItem(scope.row.id).then((res) => {
let arr = [];
let fieldList = res.data.record_data;
for (let i = 0; i < that.fieldList.length; i++) {
let obj = that.fieldList[i];
obj.is_testok = null;
for (let j = 0; j < fieldList.length; j++) {
if (that.fieldList[i].field_key === fieldList[j].field_key) {
obj.id = fieldList[j].id;
obj.is_testok = fieldList[j].is_testok;
obj.field_value = fieldList[j].field_value;
}
}
arr.push(obj);
}
that.fieldList = arr;
that.$nextTick(() => {
that.isDisabled = true;
that.recordVisible = true; that.recordVisible = true;
}); that.formName = res.data.form_.name;
}); that.fieldList = res.data.record_data;
} }
} })
);
}, },
}, },
}; };
</script> </script>
<style scoped>
.items {
height: 35px;
line-height: 35px;
padding-left: 20px;
}
.itemLabel {
font-size: 14px;
color: #606266;
font-weight: 600;
}
</style>

View File

@ -119,7 +119,6 @@
:title="formName" :title="formName"
:visible.sync="recordVisible" :visible.sync="recordVisible"
:close-on-click-modal="false" :close-on-click-modal="false"
@close="recordCancel"
> >
<el-row> <el-row>
<el-col v-for="item in fieldList" :key="item.id" :span="12"> <el-col v-for="item in fieldList" :key="item.id" :span="12">

View File

@ -3,15 +3,16 @@
<el-card style="margin-top: 2px"> <el-card style="margin-top: 2px">
<el-descriptions title="任务详情" :column="5" border style="margin-bottom: 20px"> <el-descriptions title="任务详情" :column="5" border style="margin-bottom: 20px">
<el-descriptions-item label="任务编号">{{productionplan.number}}</el-descriptions-item> <el-descriptions-item label="任务编号">{{productionplan.number}}</el-descriptions-item>
<el-descriptions-item label="产品名称" v-if="productionplan.product_">{{productionplan.product_.name}}</el-descriptions-item> <el-descriptions-item label="产品名称" v-if="productionplan.product_">{{productionplan.product_.name}}
<el-descriptions-item label="规格型号" v-if="productionplan.product_">{{productionplan.product_.specification}}</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="规格型号" v-if="productionplan.product_">{{productionplan.product_.specification}}
</el-descriptions-item>
<el-descriptions-item label="生产状态">{{state_[productionplan.state]}}</el-descriptions-item> <el-descriptions-item label="生产状态">{{state_[productionplan.state]}}</el-descriptions-item>
<el-descriptions-item label="不合格品数量">{{productionplan.count_notok}}</el-descriptions-item> <el-descriptions-item label="不合格品数量">{{productionplan.count_notok}}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<el-card style="margin-bottom: 20px"> <el-card style="margin-bottom: 20px">
<div slot="header" class="clearfix"> <div slot="header" class="clearfix">
<span>下料清单</span> <span>下料清单</span>
</div> </div>
<el-table <el-table
:data="cut" :data="cut"
@ -20,55 +21,49 @@
stripe stripe
style="width: 100%" style="width: 100%"
> >
<el-table-column label="序号" type="index" width="50"/>
<el-table-column label="序号" type="index" width="50" /> <el-table-column label="玻璃批次">
<template slot-scope="scope">{{ scope.row.from_batch }}</template>
<el-table-column label="玻璃批次" >
<template slot-scope="scope" >{{ scope.row.from_batch }}</template>
</el-table-column> </el-table-column>
<el-table-column label="生产型号" > <el-table-column label="生产型号">
<template slot-scope="scope" >{{ scope.row.from_material_.specification }}</template> <template slot-scope="scope">{{ scope.row.from_material_.specification }}</template>
</el-table-column> </el-table-column>
<el-table-column label="切裁规格" > <el-table-column label="切裁规格">
<template slot-scope="scope" >{{ scope.row.from_material_.specification }}</template> <template slot-scope="scope">{{ scope.row.from_material_.specification }}</template>
</el-table-column> </el-table-column>
<el-table-column label="切裁板数" > <el-table-column label="切裁板数">
<template slot-scope="scope" >{{ scope.row.count_cut }}</template> <template slot-scope="scope">{{ scope.row.count_cut }}</template>
</el-table-column> </el-table-column>
<el-table-column label="切裁片数" > <el-table-column label="切裁片数">
<template slot-scope="scope" >{{ scope.row.count_real }}</template> <template slot-scope="scope">{{ scope.row.count_real }}</template>
</el-table-column> </el-table-column>
<el-table-column label="成品数量" > <el-table-column label="成品数量">
<template slot-scope="scope" >{{ scope.row.count_ok }}</template> <template slot-scope="scope">{{ scope.row.count_ok }}</template>
</el-table-column> </el-table-column>
<el-table-column label="操作人" > <el-table-column label="操作人">
<template slot-scope="scope" >{{ scope.row.create_by_.username }}</template> <template slot-scope="scope">{{ scope.row.create_by_.username }}</template>
</el-table-column> </el-table-column>
<el-table-column label="甩片原因及数量(片)" > <el-table-column label="甩片原因及数量(片)">
<el-table-column label="气泡" > <el-table-column label="气泡">
<template slot-scope="scope" >{{ scope.row.count_qipao }}</template> <template slot-scope="scope">{{ scope.row.count_qipao }}</template>
</el-table-column> </el-table-column>
<el-table-column label="破点" > <el-table-column label="破点">
<template slot-scope="scope" >{{ scope.row.count_podian }}</template> <template slot-scope="scope">{{ scope.row.count_podian }}</template>
</el-table-column> </el-table-column>
<el-table-column label="划伤" > <el-table-column label="划伤">
<template slot-scope="scope" >{{ scope.row.count_hua }}</template> <template slot-scope="scope">{{ scope.row.count_hua }}</template>
</el-table-column> </el-table-column>
<el-table-column label="其他" > <el-table-column label="其他">
<template slot-scope="scope" >{{ scope.row.count_other }}</template> <template slot-scope="scope">{{ scope.row.count_other }}</template>
</el-table-column> </el-table-column>
</el-table-column> </el-table-column>
<el-table-column label="检验员" > <el-table-column label="检验员">
<template slot-scope="scope" >{{ scope.row.create_by_.username }}</template> <template slot-scope="scope">{{ scope.row.create_by_.username }}</template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-card> </el-card>
<el-tabs v-model="activeName" type="card" > <el-tabs v-model="activeName" type="card">
<el-tab-pane label="玻璃" name="1" > <el-tab-pane label="玻璃" name="1">
<el-table <el-table
:data="wproduct" :data="wproduct"
border border
@ -77,53 +72,43 @@
stripe stripe
style="width: 100%" style="width: 100%"
height="500" height="500"
:load="load"
:tree-props="{children: 'children'}"> :tree-props="{children: 'children'}">
<el-table-column type="index" width="50"/>
<el-table-column type="index" width="50" /> <el-table-column label="玻璃编号">
<template slot-scope="scope">{{ scope.row.number }}</template>
<el-table-column label="玻璃编号" >
<template slot-scope="scope" >{{ scope.row.number }}</template>
</el-table-column> </el-table-column>
<el-table-column label="所在子工序"> <el-table-column label="所在子工序">
<template slot-scope="scope" >{{ <template slot-scope="scope">
scope.row.step_.name {{scope.row.step_.name}}
}}</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="玻璃状态"> <el-table-column label="玻璃状态">
<template slot-scope="scope" >{{ <template slot-scope="scope">
scope.row.material_.name {{scope.row.material_.name}}
}}</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="状态" > <el-table-column label="状态">
<template slot-scope="scope" >{{ <template slot-scope="scope">
actstate_[scope.row.act_state] {{actstate_[scope.row.act_state]}}
}}</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
align="center" align="center"
label="过程记录" label="过程记录"
width="220px" width="220px"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<el-link <el-link
v-if="checkPermission(['material_delete'])" v-if="checkPermission(['material_delete'])"
type="primary" type="primary"
@click="handleoption(scope)" @click="handleoption(scope)"
>查看</el-link >查看
> </el-link>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="工序" name="2" > <el-tab-pane label="工序" name="2">
<el-table <el-table
:data="process_json" :data="process_json"
border border
@ -132,84 +117,71 @@
style="width: 100%" style="width: 100%"
height="500" height="500"
> >
<el-table-column type="index" width="50"/>
<el-table-column type="index" width="50" /> <el-table-column label="工序名称">
<template slot-scope="scope">{{ scope.row.process_name }}</template>
<el-table-column label="工序名称" >
<template slot-scope="scope" >{{ scope.row.process_name }}</template>
</el-table-column> </el-table-column>
<el-table-column label="玻璃数量" > <el-table-column label="玻璃数量">
<template slot-scope="scope" >{{ scope.row.count_real }}</template> <template slot-scope="scope">{{ scope.row.count_real }}</template>
</el-table-column> </el-table-column>
<el-table-column label="合格数量">
<el-table-column label="合格数量" > <template slot-scope="scope">{{ scope.row.count_ok }}</template>
<template slot-scope="scope" >{{ scope.row.count_ok }}</template>
</el-table-column> </el-table-column>
<el-table-column label="不合格数量">
<el-table-column label="不合格数量" > <template slot-scope="scope">{{ scope.row.count_notok }}</template>
<template slot-scope="scope" >{{ scope.row.count_notok }}</template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
align="center" align="center"
label="过程记录" label="过程记录"
width="220px" width="220px"
> >
<template slot-scope="scope"> <template slot-scope="scope">
<el-link <el-link
v-if="checkPermission(['material_delete'])" v-if="checkPermission(['material_delete'])"
type="primary" type="primary"
@click="handleprocess(scope)" @click="handleprocess(scope)"
>查看</el-link >查看
> </el-link>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</el-card> </el-card>
</div> </div>
</template> </template>
<script> <script>
import { getProductionplan,getsubproductionplanList } from "@/api/pm"; import {getProductionplan, getsubproductionplanList} from "@/api/pm";
import { getwproductList,getcutList} from "@/api/wpm"; import {getwproductList, getcutList} from "@/api/wpm";
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
import {getTestRecord} from "@/api/qm"; import {getTestRecord} from "@/api/qm";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
export default { export default {
components: { Pagination }, components: {Pagination},
data() { data() {
return { return {
productionplan:{ productionplan: {
number:"" number: ""
}, },
cut:[], cut: [],
activeName:"1", activeName: "1",
wproduct:[], wproduct: [],
subproductionplanList: "", subproductionplanList: "",
listQuery: { listQuery: {
page: 1, page: 1,
page_size: 20, page_size: 20,
}, },
state_:{ state_: {
10:'制定中', 10: '制定中',
20:'已下达', 20: '已下达',
30:'已接受', 30: '已接受',
40:'生产中', 40: '生产中',
50:'已完成', 50: '已完成',
60:'军检完成'}, 60: '军检完成'
},
actstate_: { actstate_: {
6: "待复检", 6: "待复检",
10: "操作进行中", 10: "操作进行中",
@ -222,8 +194,8 @@ export default {
26: "待夹层检验", 26: "待夹层检验",
70: "报废", 70: "报废",
}, },
process_json:null, process_json: null,
productionplanID:null, productionplanID: null,
}; };
}, },
computed: {}, computed: {},
@ -243,24 +215,24 @@ export default {
getProductionplan(this.id).then((response) => { getProductionplan(this.id).then((response) => {
if (response.data) { if (response.data) {
this.productionplan = response.data; this.productionplan = response.data;
this.productionplanID=response.data.id; this.productionplanID = response.data.id;
let process_json = []; let process_json = [];
for(let item in response.data.process_json){ for (let item in response.data.process_json) {
let obj = new Object(); let obj = new Object();
obj = response.data.process_json[item]; obj = response.data.process_json[item];
process_json.push(obj) process_json.push(obj)
} }
this.process_json= process_json; this.process_json = process_json;
} }
}); });
}, },
getspList() { getspList() {
getsubproductionplanList({page:0,production_plan:this.id}).then((response) => { getsubproductionplanList({page: 0, production_plan: this.id}).then((response) => {
if (response.data) { if (response.data) {
this.subproductionplanList = response.data; this.subproductionplanList = response.data;
} }
@ -268,9 +240,8 @@ export default {
}); });
}, },
getwproductList() getwproductList() {
{ getwproductList({production_plan: this.id, page: 0,}).then((response) => {
getwproductList({production_plan:this.id,page:0,}).then((response) => {
if (response.data) { if (response.data) {
this.wproduct = response.data; this.wproduct = response.data;
} }
@ -278,9 +249,8 @@ export default {
}); });
}, },
//下料清单 //下料清单
gecutList() gecutList() {
{ getcutList({production_plan: this.id, page: 0}).then((response) => {
getcutList({production_plan:this.id,page:0}).then((response) => {
if (response.data) { if (response.data) {
this.cut = response.data; this.cut = response.data;
} }
@ -289,17 +259,25 @@ export default {
}, },
//查看该玻璃检验记录表 //查看该玻璃检验记录表
handleoption(scope){ handleoption(scope) {
this.$router.push({name: "taskrecordfrom", params: { id: scope.row.id ,productionplanid:this.id ,number:scope.row.number,process:scope.row.step_.name}, }) this.$router.push({
name: "taskrecordfrom",
params: {
id: scope.row.id,
productionplanid: this.id,
number: scope.row.number,
process: scope.row.step_.name
},
})
}, },
//查看工序对应的玻璃 //查看工序对应的玻璃
handleprocess (scope){ handleprocess(scope) {
this.$router.push({name: "wproduct", params: { id: scope.row.process,productionplanid:this.id} }) this.$router.push({name: "wproduct", params: {id: scope.row.process, productionplanid: this.id}})
} }
}, },
}; };
</script> </script>

View File

@ -46,7 +46,6 @@
:title="formName" :title="formName"
:visible.sync="recordVisible" :visible.sync="recordVisible"
:close-on-click-modal="false" :close-on-click-modal="false"
@close="recordCancel"
> >
<el-row> <el-row>
<el-col v-for="item in fieldList" :key="item.id" :span="12"> <el-col v-for="item in fieldList" :key="item.id" :span="12">

View File

@ -102,10 +102,12 @@
<p>创建时间 {{watchedCreateTime}}</p> <p>创建时间 {{watchedCreateTime}}</p>
</el-col> </el-col>
</el-row> </el-row>
<svg height=800 id="mySvg" style="width: 100%;"> <div style="width: 90%;margin: auto;">
<svg height=1000 id="mySvg" style="width:100%!important;">
</svg> </svg>
</div> </div>
</div> </div>
</div>
<el-dialog <el-dialog
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
:title="dialogType === 'edit' ? '编辑工作流' : '新增工作流'"> :title="dialogType === 'edit' ? '编辑工作流' : '新增工作流'">
@ -481,7 +483,7 @@ export default {
margin: 10vh auto 0; margin: 10vh auto 0;
text-align: center; text-align: center;
border-radius: 2px; border-radius: 2px;
max-height: 75vh; max-height: 80vh;
overflow-y: scroll; overflow-y: scroll;
} }
.svgItem{ .svgItem{

View File

@ -241,7 +241,7 @@
</el-step> </el-step>
</el-steps> </el-steps>
<div style="width: 90%;margin: auto;"> <div style="width: 90%;margin: auto;">
<svg height=800 id="mySvg" style="width:100%!important;"> <svg height=1000 id="mySvg" style="width:100%!important;">
</svg> </svg>
</div> </div>

View File

@ -613,6 +613,27 @@
<el-button type="primary" @click="retrialSubmit"> </el-button> <el-button type="primary" @click="retrialSubmit"> </el-button>
</div> </div>
</el-dialog> </el-dialog>
<!--已完成检查表查看-->
<el-dialog
:title="formName"
:visible.sync="recordFinishedVisible"
:close-on-click-modal="false"
>
<el-row>
<el-col v-for="item in fieldList" :key="item.id" :span="12">
<div class="items" v-if="item.field_type!=='draw'">
<span class="itemLabel">{{item.field_name}}</span>
<span>{{item.field_value}}</span>
</div>
</el-col>
<el-col v-for="item in fieldList" :key="item.id" :span="24">
<div class="items" v-if="item.field_type==='draw'" style="height: 400px">
<span class="itemLabel">{{item.field_name}}</span>
<img style="width: 45%;vertical-align: text-top;" :src="'http://47.95.0.242:2222'+item.field_value"/>
</div>
</el-col>
</el-row>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
@ -755,6 +776,7 @@
dialogFormVisible: false, dialogFormVisible: false,
dialogFormVisibles: false, dialogFormVisibles: false,
limitedCheckRecord: false, limitedCheckRecord: false,
recordFinishedVisible: false,
testrecord: {}, testrecord: {},
retrialItem: {},//复检对象 retrialItem: {},//复检对象
retrialResponse: {},//复检对象 retrialResponse: {},//复检对象
@ -862,13 +884,10 @@
handleRetrial(scope) { handleRetrial(scope) {
let that = this; let that = this;
this.retrialItem = Object.assign({}, scope.row); this.retrialItem = Object.assign({}, scope.row);
console.log(this.retrialItem);
getRetrial(scope.row.id).then(res => { getRetrial(scope.row.id).then(res => {
that.retrialResponse = res.data; that.retrialResponse = res.data;
getWorkflowInit(res.data.workflow).then((response) => { getWorkflowInit(res.data.workflow).then((response) => {
if (response.data) { if (response.data) {
debugger;
console.log(response.data);
that.retrialForm.transition = response.data.transitions[0].id; that.retrialForm.transition = response.data.transitions[0].id;
that.customfieldList = response.data.field_list; that.customfieldList = response.data.field_list;
for (let i = 0; i < that.customfieldList.length; i++) { for (let i = 0; i < that.customfieldList.length; i++) {
@ -926,10 +945,8 @@
if (response.data) { if (response.data) {
this.wproductList3 = response.data; this.wproductList3 = response.data;
} }
}); });
}, },
//半成品批量入库 //半成品批量入库
handleCreate() { handleCreate() {
this.dialogFormVisibles = true; this.dialogFormVisibles = true;
@ -941,7 +958,6 @@
_this.mutipID = []; _this.mutipID = [];
this.$refs.multipleTable.selection.forEach((item) => { this.$refs.multipleTable.selection.forEach((item) => {
_this.mutipID.push(item.id); _this.mutipID.push(item.id);
}); });
createputins({ createputins({
warehouse: this.form.warehouse, warehouse: this.form.warehouse,
@ -1093,44 +1109,13 @@
handleRecordDetail(scope) { handleRecordDetail(scope) {
let that = this; let that = this;
that.fieldList = []; that.fieldList = [];
that.recordVisible = false;
that.recordId = scope.row.id;
that.recordform = scope.row.form;
that.formName = scope.row.form_.name;
getrffieldList({form: this.recordform, page: 1, page_size: 100}).then((response) => {
if (response.data) {
that.hasPicture = false;
let fieldList = response.data.results;
that.fieldList = [...fieldList];
let arr = fieldList.filter(item => {
return item.field_type === 'draw'
});
if (arr.length > 0) {
that.hasPicture = true;
}
getTestRecordItem(scope.row.id).then((res) => { getTestRecordItem(scope.row.id).then((res) => {
let arr = []; if (res.code >= 200) {
let fieldList = res.data.record_data; that.recordFinishedVisible = true;
for (let i = 0; i < that.fieldList.length; i++) { that.formName = res.data.form_.name;
let obj = that.fieldList[i]; that.fieldList = res.data.record_data;
obj.is_testok = null;
for (let j = 0; j < fieldList.length; j++) {
if (that.fieldList[i].field_key === fieldList[j].field_key) {
obj.id = fieldList[j].id;
obj.is_testok = fieldList[j].is_testok;
obj.field_value = fieldList[j].field_value;
} }
}
arr.push(obj)
}
that.fieldList = arr;
that.$nextTick(() => {
that.isDisabled = true;
that.recordVisible = true;
});
}) })
}
});
}, },
//半产品复检 //半产品复检
handleReview() { handleReview() {
@ -1331,3 +1316,16 @@
} }
} }
</script> </script>
<style scoped>
.items {
height: 35px;
line-height: 35px;
padding-left: 20px;
}
.itemLabel {
font-size: 14px;
color: #606266;
font-weight: 600;
}
</style>

View File

@ -2,13 +2,14 @@ from django.db.models import base
from rest_framework import urlpatterns from rest_framework import urlpatterns
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from apps.develop.views import CleanDataView, UpdateCuttingView, UpdateFIFOItem, UpdateLastTestResult from apps.develop.views import CleanDataView, UpdateCuttingView, UpdateFIFOItem, UpdateLastTestResult, UpdateSpg
urlpatterns = [ urlpatterns = [
path('cleandata/', CleanDataView.as_view()), path('cleandata/', CleanDataView.as_view()),
path('update_cutting/', UpdateCuttingView.as_view()), path('update_cutting/', UpdateCuttingView.as_view()),
path('update_last_result/', UpdateLastTestResult.as_view()), path('update_last_result/', UpdateLastTestResult.as_view()),
path('update_last_result/', UpdateLastTestResult.as_view()), path('update_last_result/', UpdateLastTestResult.as_view()),
path('update_fifoitem/', UpdateFIFOItem.as_view()) path('update_fifoitem/', UpdateFIFOItem.as_view()),
path('update_spg/', UpdateSpg.as_view())
] ]

View File

@ -6,7 +6,7 @@ from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response from rest_framework.response import Response
from apps.inm.models import FIFO, FIFOItem, Inventory, MaterialBatch from apps.inm.models import FIFO, FIFOItem, Inventory, MaterialBatch
from apps.mtm.models import Material from apps.mtm.models import Material
from apps.pm.models import ProductionPlan from apps.pm.models import ProductionPlan, SubProductionPlan
from apps.sam.models import Order from apps.sam.models import Order
from apps.wf.models import Ticket from apps.wf.models import Ticket
from apps.wpm.models import Operation, OperationMaterial, WProduct, WproductFlow from apps.wpm.models import Operation, OperationMaterial, WProduct, WproductFlow
@ -80,3 +80,14 @@ class UpdateFIFOItem(APIView):
i.is_testok = None i.is_testok = None
i.save() i.save()
return Response() return Response()
class UpdateSpg(APIView):
permission_classes = [IsAdminUser]
@transaction.atomic
def post(self, request, format=None):
"""
冷加工重新计算合格率
"""
for i in SubProductionPlan.objects.filter(subproduction__process__id=1):
WpmServies.update_subproduction_progress_main(sp=i)
return Response()

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2022-01-18 00:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('em', '0009_auto_20210916_1108'),
]
operations = [
migrations.AlterField(
model_name='equipment',
name='state',
field=models.PositiveIntegerField(choices=[(0, '完好'), (1, '限用'), (2, '在修'), (3, '禁用')], default=0, verbose_name='设备状态'),
),
]

View File

@ -0,0 +1,63 @@
# Generated by Django 3.2.9 on 2022-01-20 01:56
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('em', '0010_alter_equipment_state'),
]
operations = [
migrations.CreateModel(
name='ECheckRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('check_date', models.DateField(blank=True, null=True, verbose_name='校准检查日期')),
('description', models.CharField(blank=True, max_length=200, null=True, verbose_name='描述')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='echeckrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
],
options={
'abstract': False,
},
),
migrations.RemoveField(
model_name='equipment',
name='belong_dept',
),
migrations.RemoveField(
model_name='equipment',
name='statedm',
),
migrations.AddField(
model_name='equipment',
name='next_check_date',
field=models.DateField(blank=True, null=True, verbose_name='下次校准检查日期'),
),
migrations.AlterField(
model_name='equipment',
name='state',
field=models.PositiveIntegerField(choices=[(10, '完好'), (20, '限用'), (30, '在修'), (40, '禁用')], default=0, verbose_name='设备状态'),
),
migrations.DeleteModel(
name='Equipmentrecord',
),
migrations.AddField(
model_name='echeckrecord',
name='equipment',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='em.equipment', verbose_name='校准检定设备'),
),
migrations.AddField(
model_name='echeckrecord',
name='update_by',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='echeckrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人'),
),
]

View File

@ -0,0 +1,43 @@
# Generated by Django 3.2.9 on 2022-01-20 02:48
import datetime
from django.db import migrations, models
import django.db.models.deletion
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('em', '0011_auto_20220120_0956'),
]
operations = [
migrations.AddField(
model_name='equipment',
name='check_date',
field=models.DateField(blank=True, null=True, verbose_name='最近校准检查日期'),
),
migrations.AlterField(
model_name='echeckrecord',
name='check_date',
field=models.DateField(default=datetime.datetime(2022, 1, 20, 2, 48, 20, 706844, tzinfo=utc), verbose_name='校准检查日期'),
preserve_default=False,
),
migrations.AlterField(
model_name='echeckrecord',
name='equipment',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='em.equipment', verbose_name='校准检定设备'),
preserve_default=False,
),
migrations.AlterField(
model_name='equipment',
name='cycle',
field=models.IntegerField(blank=True, null=True, verbose_name='校准或检定周期(月)'),
),
migrations.AlterField(
model_name='equipment',
name='next_check_date',
field=models.DateField(blank=True, null=True, verbose_name='预计下次校准检查日期'),
),
]

View File

@ -3,32 +3,34 @@ from django.contrib.auth.models import AbstractUser
from django.db.models.base import Model from django.db.models.base import Model
import django.utils.timezone as timezone import django.utils.timezone as timezone
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File from apps.system.models import CommonADModel, CommonAModel, CommonBModel, Organization, User, Dict, File
#from apps.mtm.models import Process
from utils.model import SoftModel, BaseModel from utils.model import SoftModel, BaseModel
from simple_history.models import HistoricalRecords from simple_history.models import HistoricalRecords
class Equipment(CommonBModel): class Equipment(CommonAModel):
""" """
设备台账信息 设备台账信息
""" """
EQUIP_STATE_OK = 10
EQUIP_STATE_LIMIT = 20
EQUIP_STATE_FIX = 30
EQUIP_STATE_DISABLE = 40
state_choices = ( state_choices = (
(0, '完好'), (EQUIP_STATE_OK, '完好'),
(1, '限用'), (EQUIP_STATE_LIMIT, '限用'),
(2, '在修'), (EQUIP_STATE_FIX, '在修'),
(3, '禁用') (EQUIP_STATE_DISABLE, '禁用')
)
statedm_choices = (
(0, '合格'),
(1, '准用'),
(2, '限用'),
(3, '禁用'),
(4, '停用'),
(5, '封存')
)
)
state2_choices = (
(EQUIP_STATE_OK, '合格'),
(EQUIP_STATE_DISABLE, '禁用')
)
EQUIP_TYPE_PRO = 1
EQUIP_TYPE_TEST = 2
type_choices = ( type_choices = (
(1, '生产设备'), (1, '生产设备'),
(2, '检验工具') (2, '检验工具')
@ -54,20 +56,21 @@ class Equipment(CommonBModel):
factory = models.CharField('生产厂', max_length=50, null=True, blank=True) factory = models.CharField('生产厂', max_length=50, null=True, blank=True)
production_date = models.DateField('生产日期', null=True, blank=True) production_date = models.DateField('生产日期', null=True, blank=True)
buy_date = models.DateField('购置日期', null=True, blank=True) buy_date = models.DateField('购置日期', null=True, blank=True)
state = models.CharField('设备状态', max_length=11, choices=state_choices, default=0) state = models.PositiveIntegerField('设备状态', choices=state_choices, default=0)
parameter = models.TextField('技术参数', null=True, blank=True) parameter = models.TextField('技术参数', null=True, blank=True)
place = models.CharField('存放位置', max_length=50, null=True, blank=True) place = models.CharField('存放位置', max_length=50, null=True, blank=True)
count = models.IntegerField('数量', default=0) count = models.IntegerField('数量', default=0)
keeper = models.ForeignKey(User, verbose_name='保管人', on_delete=models.CASCADE, null=True, blank=True) keeper = models.ForeignKey(User, verbose_name='保管人', on_delete=models.CASCADE, null=True, blank=True)
description = models.CharField('描述', max_length=200, blank=True, null=True) description = models.CharField('描述', max_length=200, blank=True, null=True)
#process = models.ForeignKey(Process, verbose_name='工序', on_delete=models.CASCADE, null=True, blank=True) # 以下是监视测量设备单独字段
mgmtype = models.IntegerField('管理类别', choices=mgmtype_choices, default=1)#监视,测量设备 mgmtype = models.IntegerField('管理类别', choices=mgmtype_choices, default=1)
way = models.IntegerField('校准或检定方式', choices=way_choices, default=1)#监视,测量设备 way = models.IntegerField('校准或检定方式', choices=way_choices, default=1)
standard = models.CharField('溯源标准或依据', max_length=200, blank=True, null=True)#监视,测量设备 standard = models.CharField('溯源标准或依据', max_length=200, blank=True, null=True)
cycle = models.IntegerField('校准或检定周期', default=0)#监视,测量设备 cycle = models.IntegerField('校准或检定周期(月)', null=True, blank=True)
usetype = models.IntegerField('使用类别', choices=usetype_choices, default=1)#监视,测量设备 usetype = models.IntegerField('使用类别', choices=usetype_choices, default=1)
statedm = models.IntegerField('设备状态', choices=statedm_choices, default=0)#监视,测量设备
check_date = models.DateField('最近校准检查日期', blank=True, null=True)
next_check_date = models.DateField('预计下次校准检查日期',blank=True, null=True)
class Meta: class Meta:
verbose_name = '设备信息' verbose_name = '设备信息'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
@ -75,8 +78,10 @@ class Equipment(CommonBModel):
def __str__(self): def __str__(self):
return self.number + '-' + self.name return self.number + '-' + self.name
class Equipmentrecord(CommonBModel): class ECheckRecord(CommonADModel):
equipment = models.ForeignKey(Equipment, verbose_name='校准检定设备', on_delete=models.CASCADE, null=True, blank=True) """
recentlydate = models.DateField('最近一次校准/检定日期',blank=True, null=True) 校准鉴定记录
nextdate = models.DateField('下次应校准或检定日期',blank=True, null=True) """
equipment = models.ForeignKey(Equipment, verbose_name='校准检定设备', on_delete=models.CASCADE)
check_date = models.DateField('校准检查日期')
description = models.CharField('描述', max_length=200, blank=True, null=True) description = models.CharField('描述', max_length=200, blank=True, null=True)

View File

@ -1,13 +1,12 @@
from apps.mtm.models import Step from apps.mtm.models import Step
from rest_framework import serializers from rest_framework import serializers
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from rest_framework import exceptions
from .models import Equipment,Equipmentrecord from .models import Equipment, ECheckRecord
from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSerializer from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSerializer
class EquipmentSerializer(ModelSerializer): class EquipmentListSerializer(ModelSerializer):
belong_dept_ = OrganizationSimpleSerializer(source='belong_dept', read_only=True)
keeper_ = UserSimpleSerializer(source='keeper', read_only=True) keeper_ = UserSimpleSerializer(source='keeper', read_only=True)
step_ = serializers.SerializerMethodField() step_ = serializers.SerializerMethodField()
class Meta: class Meta:
@ -17,24 +16,35 @@ class EquipmentSerializer(ModelSerializer):
@staticmethod @staticmethod
def setup_eager_loading(queryset): def setup_eager_loading(queryset):
""" Perform necessary eager loading of data. """ """ Perform necessary eager loading of data. """
queryset = queryset.select_related('belong_dept','keeper') queryset = queryset.select_related('keeper')
return queryset return queryset
def get_step_(self, obj): def get_step_(self, obj):
return Step.objects.filter(equipments=obj).values('id', 'name', 'number') return Step.objects.filter(equipments=obj).values('id', 'name', 'number')
class EquipmentCreateUpdateSerializer(ModelSerializer):
class Meta:
model = Equipment
exclude = ['create_by', 'update_by', 'create_time', 'update_time', 'check_date', 'next_check_date']
def validate(self, attrs):
if attrs['type'] == Equipment.EQUIP_TYPE_TEST:
if attrs['state'] not in [Equipment.EQUIP_STATE_OK, Equipment.EQUIP_STATE_DISABLE]:
raise exceptions.APIException('设备状态错误')
return super().validate(attrs)
class EquipmentSimpleSerializer(ModelSerializer): class EquipmentSimpleSerializer(ModelSerializer):
class Meta: class Meta:
model = Equipment model = Equipment
fields = ['id', 'number', 'name', 'state'] fields = ['id', 'number', 'name', 'state', 'model']
class EquipmentrecordSerializer(ModelSerializer): class ECheckRecordListSerializer(ModelSerializer):
equipment_ = EquipmentSerializer(source='equipment', read_only=True) equipment_ = EquipmentSimpleSerializer(source='equipment', read_only=True)
class Meta: class Meta:
model = Equipmentrecord model = ECheckRecord
fields = '__all__' fields = '__all__'
@staticmethod @staticmethod
@ -43,6 +53,11 @@ class EquipmentrecordSerializer(ModelSerializer):
queryset = queryset.select_related('equipment') queryset = queryset.select_related('equipment')
return queryset return queryset
class EChcekRecordCreateSerializer(ModelSerializer):
class Meta:
model = ECheckRecord
fields = ['equipment', 'check_date', 'description']
class DaqCreateSerializer(serializers.Serializer): class DaqCreateSerializer(serializers.Serializer):
number = serializers.CharField() number = serializers.CharField()
file = serializers.FileField() file = serializers.FileField()

View File

@ -0,0 +1,20 @@
from tabnanny import check
from apps.em.models import ECheckRecord, Equipment
from dateutil.relativedelta import relativedelta
from django.utils import timezone
class EmService:
@classmethod
def update_check_date(cls, equip:Equipment):
# 根据校准检定记录变更下次日期
check = ECheckRecord.objects.filter(equipment=equip).order_by('id').last()
if check:
equip.check_date = check.check_date
if equip.cycle:
equip.next_check_date = equip.check_date + relativedelta(months=equip.cycle)
else:
equip.next_check_date = None
else:
equip.check_date = None
equip.next_check_date = None
equip.save()

View File

@ -1,12 +1,12 @@
from django.db.models import base from django.db.models import base
from rest_framework import urlpatterns from rest_framework import urlpatterns
from apps.em.views import DaqView, EquipmentViewSet,EquipmentrecordViewSet from apps.em.views import DaqView, EquipmentViewSet, EChcekRecordViewSet
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
router = DefaultRouter() router = DefaultRouter()
router.register('equipment', EquipmentViewSet, basename='equipment') router.register('equipment', EquipmentViewSet, basename='equipment')
router.register('equipmentrecord', EquipmentrecordViewSet, basename='equipmentrecord') router.register('echeck_record', EChcekRecordViewSet, basename='echeck_record')
urlpatterns = [ urlpatterns = [
path('daq/', DaqView.as_view()), path('daq/', DaqView.as_view()),
path('', include(router.urls)), path('', include(router.urls)),

View File

@ -1,14 +1,20 @@
from datetime import timedelta
from django.shortcuts import render from django.shortcuts import render
from numpy import delete
from rest_framework.exceptions import APIException from rest_framework.exceptions import APIException
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin, ListModelMixin, DestroyModelMixin
from rest_framework import serializers, status from rest_framework import serializers, status
from rest_framework.response import Response from rest_framework.response import Response
from apps.em.models import Equipment,Equipmentrecord from apps.em.models import Equipment, ECheckRecord
from apps.em.serializers import DaqCreateSerializer, EquipmentSerializer,EquipmentrecordSerializer from apps.em.serializers import DaqCreateSerializer, EChcekRecordCreateSerializer, ECheckRecordListSerializer, \
EquipmentCreateUpdateSerializer, EquipmentListSerializer
from apps.em.services import EmService
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from django.utils import timezone
from django.db import transaction
# Create your views here. # Create your views here.
class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet): class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet):
@ -18,48 +24,48 @@ class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet)
perms_map = {'get': '*', 'post': 'equipment_create', perms_map = {'get': '*', 'post': 'equipment_create',
'put': 'equipment_update', 'delete': 'equipment_delete'} 'put': 'equipment_update', 'delete': 'equipment_delete'}
queryset = Equipment.objects.all() queryset = Equipment.objects.all()
serializer_class = EquipmentSerializer serializer_class = EquipmentListSerializer
search_fields = ['number', 'name','description'] search_fields = ['number', 'name','description']
filterset_fields = ['belong_dept', 'keeper', 'type'] filterset_fields = ['keeper', 'type']
ordering_fields = ['create_time'] ordering_fields = ['create_time']
ordering = ['-create_time'] ordering = ['-create_time']
def get_serializer_class(self):
if self.action in ['create', 'update']:
return EquipmentCreateUpdateSerializer
return super().get_serializer_class()
# Create your views here. # Create your views here.
class EquipmentrecordViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet): class EChcekRecordViewSet(CreateUpdateModelAMixin, OptimizationMixin,
CreateModelMixin, RetrieveModelMixin, ListModelMixin, DestroyModelMixin, GenericViewSet):
""" """
设备校准-增删改查 设备校准-增删改查
""" """
perms_map = {'get': '*', 'post': 'equipmentrecord_create', perms_map = {'get': '*', 'post': 'echeckrecord_create', 'delete': 'echeckrecord_delete'}
'put': 'equipmentrecord_update', 'delete': 'equipmentrecord_delete'} queryset = ECheckRecord.objects.all()
queryset = Equipmentrecord.objects.all() serializer_class = ECheckRecordListSerializer
serializer_class = EquipmentrecordSerializer
filterset_fields = ['equipment'] filterset_fields = ['equipment']
ordering_fields = ['create_time'] ordering = ['-id']
ordering = ['-create_time']
def get_serializer_class(self):
if self.action in ['create']:
return EChcekRecordCreateSerializer
return super().get_serializer_class()
@transaction.atomic
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
data = request.data
if data.get('equipment', None):
equipment = Equipment.objects.get(pk=data['equipment'])
equipment.statedm = data['state']
equipment.save()
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
serializer.save() instance = serializer.save(create_by=request.user)
return Response(status=status.HTTP_200_OK) EmService.update_check_date(equip=instance.equipment)
def update(self, request, *args, **kwargs): return Response()
data = request.data
if data.get('equipment', None): @transaction.atomic
equipment = Equipment.objects.get(pk=data['equipment']) def destroy(self, request, *args, **kwargs):
equipment.statedm = data['state'] instance = self.get_object()
equipment.save() instance.delete()
id = self.get_object() EmService.update_check_date(equip=instance.equipment)
serializer = self.get_serializer(id, data=data) return Response()
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(status=status.HTTP_200_OK)
import uuid import uuid
import os import os

View File

@ -0,0 +1,9 @@
from django_filters import rest_framework as filters
from apps.hrm.models import ClockRecord
class ClockRecordFilterSet(filters.FilterSet):
create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte')
create_time_end = filters.DateFilter(field_name="create_time", lookup_expr='lte')
class Meta:
model = ClockRecord
fields = ['create_by', 'create_time_start', 'create_time_end']

View File

@ -0,0 +1,32 @@
# Generated by Django 3.2.9 on 2022-01-21 06:45
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('hrm', '0003_employee_face_data'),
]
operations = [
migrations.CreateModel(
name='ClockRecord',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('type', models.PositiveSmallIntegerField(choices=[(10, '上班打卡')], default=10, verbose_name='打卡类型')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='clockrecord_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='clockrecord_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
]

View File

@ -36,8 +36,12 @@ class Employee(CommonAModel):
def __str__(self): def __str__(self):
return self.name return self.name
class Attendance(CommonADModel): class ClockRecord(CommonADModel):
""" """
出勤记录 打卡记录
""" """
ClOCK_WORK1 = 10
type_choice = (
(ClOCK_WORK1, '上班打卡'),
)
type = models.PositiveSmallIntegerField('打卡类型', choices=type_choice, default=ClOCK_WORK1)

View File

@ -1,7 +1,7 @@
from apps.system.models import User from apps.system.models import User
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from rest_framework import serializers from rest_framework import serializers
from .models import Employee from .models import ClockRecord, Employee
from apps.system.serializers import UserListSerializer, UserSimpleSerializer from apps.system.serializers import UserListSerializer, UserSimpleSerializer
from django.db.models.query import Prefetch from django.db.models.query import Prefetch
@ -23,3 +23,13 @@ class EmployeeSerializer(ModelSerializer):
class FaceLoginSerializer(serializers.Serializer): class FaceLoginSerializer(serializers.Serializer):
base64 = serializers.CharField() base64 = serializers.CharField()
class FaceClockCreateSerializer(serializers.Serializer):
base64 = serializers.CharField()
class ClockRecordListSerializer(serializers.ModelSerializer):
create_by_ = UserSimpleSerializer(source='create_by', read_only=True)
class Meta:
model = ClockRecord
fields = '__all__'

View File

@ -0,0 +1,39 @@
from django.conf import settings
import uuid
import face_recognition
import os
from apps.hrm.models import Employee
from apps.system.models import User
class HRMService:
@classmethod
def face_compare_from_base64(cls, base64_data):
filename = str(uuid.uuid4())
filepath = settings.BASE_DIR +'/temp/' + filename +'.png'
with open(filepath, 'wb') as f:
f.write(base64_data)
try:
unknown_picture = face_recognition.load_image_file(filepath)
unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0]
os.remove(filepath)
except:
os.remove(filepath)
return None, '头像解码失败'
# 匹配人脸库
user_faces = Employee.objects.filter(face_data__isnull=False,
user__is_active=True).values('user', 'face_data')
user_l = []
face_l = []
for i in user_faces:
user_l.append(i['user'])
face_l.append(i['face_data'])
results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.5)
for index, value in enumerate(results):
if value:
# 识别成功
user = User.objects.get(id=user_l[index])
return user, ''
return None, '识别失败'

View File

@ -0,0 +1,8 @@
from __future__ import absolute_import, unicode_literals
from celery import shared_task
@shared_task
def x():
print('ok')

View File

@ -1,11 +1,12 @@
from django.db.models import base from django.db.models import base
from rest_framework import urlpatterns from rest_framework import urlpatterns
from apps.hrm.views import EmployeeViewSet, FaceLogin from apps.hrm.views import ClockRecordViewSet, EmployeeViewSet, FaceLogin
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
router = DefaultRouter() router = DefaultRouter()
router.register('employee', EmployeeViewSet, basename='employee') router.register('employee', EmployeeViewSet, basename='employee')
router.register('clock_record', ClockRecordViewSet, basename='clock_record')
urlpatterns = [ urlpatterns = [
path('facelogin/', FaceLogin.as_view()), path('facelogin/', FaceLogin.as_view()),
path('', include(router.urls)), path('', include(router.urls)),

View File

@ -1,10 +1,13 @@
from django.shortcuts import render from django.shortcuts import render
from django.utils import timezone
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet, GenericViewSet from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, CreateModelMixin, ListModelMixin
from apps.hrm.filters import ClockRecordFilterSet
from apps.hrm.services import HRMService
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from apps.hrm.models import Employee from apps.hrm.models import ClockRecord, Employee
from apps.hrm.serializers import EmployeeSerializer, FaceLoginSerializer from apps.hrm.serializers import ClockRecordListSerializer, EmployeeSerializer, FaceClockCreateSerializer, FaceLoginSerializer
import face_recognition import face_recognition
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
@ -12,8 +15,10 @@ import logging
from rest_framework.generics import CreateAPIView from rest_framework.generics import CreateAPIView
from rest_framework import status from rest_framework import status
from rest_framework_simplejwt.tokens import RefreshToken from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework import exceptions
from apps.system.models import User from apps.system.models import User
from apps.system.serializers import UserSimpleSerializer
from rest_framework.permissions import AllowAny
logger = logging.getLogger('log') logger = logging.getLogger('log')
@ -50,9 +55,56 @@ class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMix
except: except:
logger.error('人脸识别出错') logger.error('人脸识别出错')
import uuid
class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
"""
打卡记录
"""
perms_map = {'get':'*', 'post':'*'}
authentication_classes = []
permission_classes = [AllowAny]
queryset = ClockRecord.objects.select_related('create_by').all()
serializer_class = ClockRecordListSerializer
filterset_class = ClockRecordFilterSet
ordering = ['-pk']
def get_serializer_class(self):
if self.action == 'create':
return FaceClockCreateSerializer
return super().get_serializer_class()
def create(self, request, *args, **kwargs):
now = timezone.now()
now_local = timezone.localtime()
if 8<=now_local.hour<=17:
base64_data = base64.urlsafe_b64decode(tran64(
request.data.get('base64').replace(' ', '+')))
user, msg = HRMService.face_compare_from_base64(base64_data)
if user:
ins, created = ClockRecord.objects.get_or_create(
create_by=request.user, create_time__hour__range = [8,18],
create_time__year=now.year, create_time__month=now.month,
create_time__day=now.day,
defaults={
'type':ClockRecord.ClOCK_WORK1,
'create_by':user,
'create_time':now
})
if not created:
ins.create_time = now
ins.save()
# 设为在岗
user.is_atwork = True
user.save()
return Response(UserSimpleSerializer(instance=user).data)
return Response(msg, status=status.HTTP_400_BAD_REQUEST)
return Response('非打卡时间范围', status=status.HTTP_400_BAD_REQUEST)
import base64 import base64
import os
def tran64(s): def tran64(s):
missing_padding = len(s) % 4 missing_padding = len(s) % 4
@ -70,41 +122,13 @@ class FaceLogin(CreateAPIView):
""" """
人脸识别登录 人脸识别登录
""" """
# serializer = FaceLoginSerializer(data=request.data) base64_data = base64.urlsafe_b64decode(tran64(request.data.get('base64').replace(' ', '+')))
# serializer.is_valid(raise_exception=True) user, msg = HRMService.face_compare_from_base64(base64_data)
filename = str(uuid.uuid4()) if user:
filepath = settings.BASE_DIR +'/temp/' + filename +'.png'
with open(filepath, 'wb') as f:
data = tran64(request.data.get('base64').replace(' ', '+'))
f.write(base64.urlsafe_b64decode(data))
# picture_of_me = face_recognition.load_image_file(settings.BASE_DIR +'/temp/me.png')
# my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
#results = face_recognition.compare_faces([my_face_encoding], unknown_face_encoding, tolerance=0.2)
try:
unknown_picture = face_recognition.load_image_file(filepath)
unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0]
os.remove(filepath)
except:
os.remove(filepath)
return Response('头像解码失败', status=status.HTTP_400_BAD_REQUEST)
# 匹配人脸库
user_faces = Employee.objects.filter(face_data__isnull=False, user__is_active=True).values('user', 'face_data')
user_l = []
face_l = []
for i in user_faces:
user_l.append(i['user'])
face_l.append(i['face_data'])
results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.5)
for index, value in enumerate(results):
if value:
# 识别成功
user = User.objects.get(id=user_l[index])
refresh = RefreshToken.for_user(user) refresh = RefreshToken.for_user(user)
return Response({ return Response({
'refresh': str(refresh), 'refresh': str(refresh),
'access': str(refresh.access_token), 'access': str(refresh.access_token),
'username':user.username 'username':user.username
}) })
return Response('未找到对应用户', status=status.HTTP_400_BAD_REQUEST) return Response(msg, status=status.HTTP_400_BAD_REQUEST)

View File

@ -21,7 +21,10 @@ class MbFilterSet(filters.FilterSet):
class IProductFilterSet(filters.FilterSet): class IProductFilterSet(filters.FilterSet):
order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__order") order = filters.NumberFilter(field_name="wproduct__subproduction_plan__production_plan__order")
to_order = filters.NumberFilter(field_name="wproduct__to_order")
update_time_start = filters.DateFilter(field_name="update_time", lookup_expr='gte')
update_time_end = filters.DateFilter(field_name="update_time", lookup_expr='lte')
class Meta: class Meta:
model = IProduct model = IProduct
fields = ['material', 'warehouse', 'batch', 'order', 'material__type'] fields = ['material', 'warehouse', 'batch', 'order', 'material__type',
'is_saled', 'update_time_start', 'update_time_end', 'to_order']

View File

@ -9,6 +9,7 @@ from apps.mtm.serializers import MaterialSimpleSerializer
from django.db import transaction from django.db import transaction
class WareHouseSerializer(serializers.ModelSerializer): class WareHouseSerializer(serializers.ModelSerializer):
create_by_ = UserSimpleSerializer('create_by', read_only=True) create_by_ = UserSimpleSerializer('create_by', read_only=True)

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2022-01-19 07:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mtm', '0043_auto_20220106_0942'),
]
operations = [
migrations.AddField(
model_name='subproduction',
name='need_combtest',
field=models.BooleanField(default=False, verbose_name='需要质检'),
),
]

View File

@ -210,6 +210,7 @@ class SubProduction(CommonAModel):
name = models.CharField('命名', max_length=50, null=True, blank=True) name = models.CharField('命名', max_length=50, null=True, blank=True)
product = models.ForeignKey(Material, verbose_name='产品', on_delete=models.CASCADE) product = models.ForeignKey(Material, verbose_name='产品', on_delete=models.CASCADE)
process = models.ForeignKey(Process, verbose_name='隶属大工序', on_delete=models.CASCADE, related_name='subproduction_process') process = models.ForeignKey(Process, verbose_name='隶属大工序', on_delete=models.CASCADE, related_name='subproduction_process')
need_combtest = models.BooleanField('需要质检', default=False)
sort = models.IntegerField('排序号', default=1) sort = models.IntegerField('排序号', default=1)
class Meta: class Meta:

View File

@ -72,6 +72,11 @@ class SubProductionSerializer(serializers.ModelSerializer):
model = SubProduction model = SubProduction
fields = '__all__' fields = '__all__'
class SubProductionCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = SubProduction
fields = ['name', 'product', 'process', 'need_combtest', 'sort']
class OtherMaterialSerializer(serializers.ModelSerializer): class OtherMaterialSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = SubprodctionMaterial model = SubprodctionMaterial

View File

@ -4,7 +4,7 @@ from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelM
from apps.mtm.filters import MaterialFilterSet, TechDocFilterset from apps.mtm.filters import MaterialFilterSet, TechDocFilterset
from apps.mtm.models import Material, Process, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc, UsedStep, SubProduction from apps.mtm.models import Material, Process, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc, UsedStep, SubProduction
from apps.mtm.serializers import InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OtherMaterialSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormDetailSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionSerializer, SubprodctionMaterialListSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer, UsedStepUpdateSerializer from apps.mtm.serializers import InputMaterialSerializer, InputMaterialUpdateSerializer, MaterialDetailSerializer, MaterialSerializer, MaterialSimpleSerializer, OtherMaterialSerializer, OutputMaterialSerializer, OutputMaterialUpdateSerializer, ProcessSerializer, RecordFormCreateSerializer, RecordFormDetailSerializer, RecordFormFieldCreateSerializer, RecordFormFieldSerializer, RecordFormFieldUpdateSerializer, RecordFormSerializer, RecordFormUpdateSerializer, StepDetailSerializer, StepSerializer, SubProductionCreateUpdateSerializer, SubProductionSerializer, SubprodctionMaterialListSerializer, TechDocCreateSerializer, TechDocListSerializer, TechDocUpdateSerializer, UsedStepCreateSerializer, UsedStepListSerializer, UsedStepUpdateSerializer
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
@ -80,6 +80,10 @@ class SubProductionViewSet(CreateUpdateModelAMixin, ModelViewSet):
search_fields = ['name'] search_fields = ['name']
serializer_class = SubProductionSerializer serializer_class = SubProductionSerializer
ordering = ['sort'] ordering = ['sort']
def get_serializer_class(self):
if self.action in ['create', 'update']:
return SubProductionCreateUpdateSerializer
return super().get_serializer_class()
class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet): class InputMaterialViewSet(CreateUpdateModelAMixin, ModelViewSet):
""" """

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.9 on 2022-01-18 00:39
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('sam', '0010_auto_20211208_1408'),
('pm', '0022_auto_20211229_1429'),
]
operations = [
migrations.AlterField(
model_name='productionplan',
name='order',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='plan_order', to='sam.order', verbose_name='关联订单'),
),
]

View File

@ -4,7 +4,7 @@ from django.db import transaction
from rest_framework import serializers from rest_framework import serializers
from rest_framework.views import APIView from rest_framework.views import APIView
from apps.em.models import Equipment from apps.em.models import Equipment
from apps.em.serializers import EquipmentSerializer from apps.em.serializers import EquipmentSimpleSerializer
from apps.inm.models import MaterialBatch from apps.inm.models import MaterialBatch
from apps.inm.serializers import MaterialBatchSerializer from apps.inm.serializers import MaterialBatchSerializer
from apps.mtm.models import Step, SubProduction, SubprodctionMaterial, UsedStep from apps.mtm.models import Step, SubProduction, SubprodctionMaterial, UsedStep
@ -248,5 +248,5 @@ class ResourceViewSet(GenericViewSet):
subproductions = SubProduction.objects.filter(product__id__in=rdata_l, is_deleted=False) subproductions = SubProduction.objects.filter(product__id__in=rdata_l, is_deleted=False)
steps = Step.objects.filter(usedstep__is_deleted=False, usedstep__subproduction__in=subproductions) steps = Step.objects.filter(usedstep__is_deleted=False, usedstep__subproduction__in=subproductions)
equips = Equipment.objects.filter(step_equips__in=steps, is_deleted=False).distinct() equips = Equipment.objects.filter(step_equips__in=steps, is_deleted=False).distinct()
serializer = EquipmentSerializer(instance=equips, many=True) serializer = EquipmentSimpleSerializer(instance=equips, many=True)
return Response(serializer.data) return Response(serializer.data)

View File

@ -44,7 +44,7 @@ class ContractCreateUpdateSerializer(serializers.ModelSerializer):
class OrderCreateUpdateSerializer(serializers.ModelSerializer): class OrderCreateUpdateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Order model = Order
fields = ['number', 'customer', 'contract', 'product', 'count', 'delivery_date'] fields = ['customer', 'contract', 'product', 'count', 'delivery_date']
def create(self, validated_data): def create(self, validated_data):
validated_data['number'] = 'DD' + ranstr(7) validated_data['number'] = 'DD' + ranstr(7)
@ -60,6 +60,7 @@ class OrderSerializer(serializers.ModelSerializer):
class OrderSimpleSerializer(serializers.ModelSerializer): class OrderSimpleSerializer(serializers.ModelSerializer):
contract_ = ContractSimpleSerializer(source='contract', read_only=True) contract_ = ContractSimpleSerializer(source='contract', read_only=True)
customer_ = CustomerSimpleSerializer(source='customer', read_only=True)
class Meta: class Meta:
model = Order model = Order
fields = '__all__' fields = '__all__'

View File

@ -20,3 +20,7 @@ class PlanGanttSerializer(serializers.ModelSerializer):
def get_children(self, obj): def get_children(self, obj):
subplans = SubProductionPlan.objects.filter(production_plan=obj).order_by('process__number') subplans = SubProductionPlan.objects.filter(production_plan=obj).order_by('process__number')
return SubplanGanttSerializer(instance=subplans, many=True).data return SubplanGanttSerializer(instance=subplans, many=True).data
class ProcessYieldSerializer(serializers.Serializer):
datetime_start = serializers.DateField(label='开始时间', required=False, allow_null=True)
datetime_end = serializers.DateField(label='结束时间', required=False, allow_null=True)

View File

@ -3,11 +3,12 @@ from rest_framework import urlpatterns
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from apps.srm.views import GanttPlan from apps.srm.views import GanttPlan, ProcessYieldView
router = DefaultRouter() router = DefaultRouter()
urlpatterns = [ urlpatterns = [
path('gantt/plan/', GanttPlan.as_view()), path('gantt/plan/', GanttPlan.as_view()),
path('process/yield/', ProcessYieldView.as_view()),
path('', include(router.urls)), path('', include(router.urls)),
] ]

View File

@ -1,9 +1,14 @@
from django.shortcuts import render from django.shortcuts import render
from rest_framework import serializers from rest_framework import serializers
from rest_framework.generics import ListAPIView from rest_framework.generics import ListAPIView, CreateAPIView
from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from apps.mtm.models import Process, Step
from apps.pm.models import ProductionPlan, SubProductionPlan from apps.pm.models import ProductionPlan, SubProductionPlan
from apps.srm.serializers import PlanGanttSerializer from apps.srm.serializers import PlanGanttSerializer, ProcessYieldSerializer
from apps.wpm.models import WProduct, WproductFlow
from django.db.models import Count
# Create your views here. # Create your views here.
class GanttPlan(ListAPIView): class GanttPlan(ListAPIView):
@ -15,4 +20,48 @@ class GanttPlan(ListAPIView):
queryset = ProductionPlan.objects.filter(is_deleted=False, is_planed=True).prefetch_related('subplan_plan', 'subplan_plan__process') queryset = ProductionPlan.objects.filter(is_deleted=False, is_planed=True).prefetch_related('subplan_plan', 'subplan_plan__process')
ordering = ['-id'] ordering = ['-id']
class ProcessYieldView(CreateAPIView):
"""
工序成品率统计
"""
perms_map = {'get':'*'}
serializer_class = ProcessYieldSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
wpfs = WproductFlow.objects.filter(is_lastlog=True)
if vdata.get('datetime_start', None):
wpfs = wpfs.filter(update_time__gte = vdata.get('datetime_start'))
if vdata.get('datetime_end', None):
wpfs = wpfs.filter(update_time__lte = vdata.get('datetime_end'))
# 根据产品日志记录进行聚合
count_ok_g = list(wpfs.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM,
WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_SELLED]).values('step__process__id').annotate(count_ok=Count('id')))
count_notok_g = list(
(
wpfs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP]).exclude(step__process__id=1)
| wpfs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP],
step__process__id=1).exclude(number=None)
)\
.values('step__process__id',
).annotate(count_notok=Count('id')))
ret = []
process_l = list(Process.objects.filter(is_deleted=False).order_by('number').values('id', 'name'))
for i in process_l:
ret_item = {'id':i['id'], 'name':i['name'], 'count_ok':0, 'count_notok':0, 'rate':1}
for m in count_ok_g:
if m['step__process__id'] == ret_item['id']:
ret_item['count_ok'] = m['count_ok']
for n in count_notok_g:
if n['step__process__id'] == ret_item['id']:
ret_item['count_notok'] = n['count_notok']
rate = (ret_item['count_ok']/(ret_item['count_ok']+ret_item['count_notok'])) \
if ret_item['count_ok']+ret_item['count_notok']>0 else 1
ret_item['rate'] = rate
ret.append(ret_item)
return Response(ret)

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2022-01-21 05:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('system', '0003_auto_20210812_0909'),
]
operations = [
migrations.AddField(
model_name='user',
name='is_atwork',
field=models.BooleanField(default=False, verbose_name='当前在岗'),
),
]

View File

@ -116,6 +116,7 @@ class User(AbstractUser):
superior = models.ForeignKey( superior = models.ForeignKey(
'self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='上级主管') 'self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='上级主管')
roles = models.ManyToManyField(Role, blank=True, verbose_name='角色') roles = models.ManyToManyField(Role, blank=True, verbose_name='角色')
is_atwork = models.BooleanField('当前在岗', default=False)
class Meta: class Meta:
verbose_name = '用户信息' verbose_name = '用户信息'

View File

@ -141,7 +141,9 @@ class UserListSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = User model = User
fields = ['id', 'name', 'phone', 'email', 'position', fields = ['id', 'name', 'phone', 'email', 'position',
'username', 'is_active', 'date_joined', 'dept_name', 'dept', 'roles', 'avatar', 'roles_name'] 'username', 'is_active', 'date_joined',
'dept_name', 'dept', 'roles', 'avatar',
'roles_name', 'is_atwork']
@staticmethod @staticmethod
def setup_eager_loading(queryset): def setup_eager_loading(queryset):

View File

@ -41,6 +41,12 @@ class WProductFilterSet(filters.FilterSet):
def filter_tag(self, queryset, name, value): def filter_tag(self, queryset, name, value):
if value == 'no_scrap': if value == 'no_scrap':
queryset = queryset.exclude(act_state=WProduct.WPR_ACT_STATE_SCRAP) queryset = queryset.exclude(act_state=WProduct.WPR_ACT_STATE_SCRAP)
elif value == 'notok':
queryset = queryset.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP])\
.exclude(step__process__id = 1) # 不算冷加工的报废
elif value == 'ok':
queryset = queryset.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM,
WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_SELLED])
return queryset return queryset

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2022-01-18 00:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0048_operationwproduct_place'),
]
operations = [
migrations.AddField(
model_name='operationequip',
name='state',
field=models.PositiveSmallIntegerField(choices=[(0, '完好'), (1, '限用'), (2, '在修'), (3, '禁用')], default=0, verbose_name='当前设备状态'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2022-01-20 02:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0049_operationequip_state'),
]
operations = [
migrations.AlterField(
model_name='operationequip',
name='state',
field=models.PositiveSmallIntegerField(choices=[(10, '完好'), (20, '限用'), (30, '在修'), (40, '禁用')], default=10, verbose_name='当前设备状态'),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.9 on 2022-01-20 07:41
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('sam', '0010_auto_20211208_1408'),
('wpm', '0050_alter_operationequip_state'),
]
operations = [
migrations.AddField(
model_name='wproduct',
name='to_order',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sam.order', verbose_name='指派的订单'),
),
migrations.AddField(
model_name='wproductflow',
name='to_order',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sam.order', verbose_name='指派的订单'),
),
]

View File

@ -119,6 +119,7 @@ class WProduct(CommonAModel):
ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单', ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_ticket') on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_ticket')
to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE)
is_mtested = models.BooleanField('是否军检', default=False) is_mtested = models.BooleanField('是否军检', default=False)
is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True) is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True)
remark_mtest = models.TextField('军检备注', null=True, blank=True) remark_mtest = models.TextField('军检备注', null=True, blank=True)
@ -192,7 +193,7 @@ class WproductFlow(CommonAModel):
on_delete=models.SET_NULL, null=True, blank=True) on_delete=models.SET_NULL, null=True, blank=True)
ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单', ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
on_delete=models.SET_NULL, null=True, blank=True) on_delete=models.SET_NULL, null=True, blank=True)
to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE)
is_mtested = models.BooleanField('是否军检', default=False) is_mtested = models.BooleanField('是否军检', default=False)
is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True) is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True)
remark_mtest = models.TextField('军检备注', null=True, blank=True) remark_mtest = models.TextField('军检备注', null=True, blank=True)
@ -336,4 +337,5 @@ class OperationEquip(BaseModel):
Operation, verbose_name='关联操作', on_delete=models.CASCADE, related_name='oe_operation') Operation, verbose_name='关联操作', on_delete=models.CASCADE, related_name='oe_operation')
equip = models.ForeignKey(Equipment, verbose_name='生产设备', equip = models.ForeignKey(Equipment, verbose_name='生产设备',
on_delete=models.CASCADE, related_name='oe_equip') on_delete=models.CASCADE, related_name='oe_equip')
state = models.PositiveSmallIntegerField('当前设备状态', choices=Equipment.state_choices, default=Equipment.EQUIP_STATE_OK)
remark = models.TextField('备注', null=True, blank=True) remark = models.TextField('备注', null=True, blank=True)

View File

@ -19,6 +19,7 @@ from apps.system.models import User
from apps.system.serializers import UserSimpleSerializer from apps.system.serializers import UserSimpleSerializer
from apps.wpm.models import Operation, OperationEquip, OperationMaterial, OperationWproduct, Pick, WMaterial, WProduct, OperationRecord, OperationRecordItem, WprouctTicket from apps.wpm.models import Operation, OperationEquip, OperationMaterial, OperationWproduct, Pick, WMaterial, WProduct, OperationRecord, OperationRecordItem, WprouctTicket
from django.db import transaction from django.db import transaction
from apps.sam.models import Order
class PickHalfSerializer(serializers.Serializer): class PickHalfSerializer(serializers.Serializer):
id = serializers.PrimaryKeyRelatedField(queryset=SubProductionProgress.objects.all(), label='子计划进度ID') id = serializers.PrimaryKeyRelatedField(queryset=SubProductionProgress.objects.all(), label='子计划进度ID')
@ -155,6 +156,7 @@ class WProductListSerializer(serializers.ModelSerializer):
subproduction_plan_ = SubproductionPlanSimpleSerializer(source='subproduction_plan', read_only=True) subproduction_plan_ = SubproductionPlanSimpleSerializer(source='subproduction_plan', read_only=True)
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True) warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
children = serializers.SerializerMethodField() children = serializers.SerializerMethodField()
to_order_ = OrderSimpleSerializer(source='to_order', read_only=True)
class Meta: class Meta:
model = WProduct model = WProduct
fields = '__all__' fields = '__all__'
@ -561,3 +563,7 @@ class WproductMtestSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = WProduct model = WProduct
fields = ['remark_mtest', 'is_mtestok'] fields = ['remark_mtest', 'is_mtestok']
class WproductToOrderSerializer(serializers.Serializer):
wproducts = serializers.PrimaryKeyRelatedField(queryset=WProduct.objects.all(), many=True)
order = serializers.PrimaryKeyRelatedField(queryset=Order.objects.all())

View File

@ -54,7 +54,8 @@ class WpmServies(object):
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验 elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验
wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.step.type == Step.STEP_TYPE_COMB: # 夹层检验 elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and \
wproduct.subproduction_plan.subproduction.need_combtest : # 配置中需要质检
wproduct.act_state = WProduct.WPR_ACT_STATE_TOCOMBTEST wproduct.act_state = WProduct.WPR_ACT_STATE_TOCOMBTEST
else: else:
wproduct.act_state = WProduct.WPR_ACT_STATE_OK wproduct.act_state = WProduct.WPR_ACT_STATE_OK
@ -113,7 +114,11 @@ class WpmServies(object):
objs = WproductFlow.objects.filter(subproduction_plan=sp, is_lastlog=True) objs = WproductFlow.objects.filter(subproduction_plan=sp, is_lastlog=True)
count_ok = objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM, count_ok = objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM,
WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_SELLED]).count() WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_SELLED]).count()
count_notok = objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP]).count() count_notok = (
objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP]).exclude(step__process__id=1)
| objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_NOTOK, WProduct.WPR_ACT_STATE_SCRAP],
step__process__id=1).exclude(number=None)
).count()
count_real = objs.exclude(act_state__in=[WProduct.WPR_ACT_STATE_TORETEST, count_real = objs.exclude(act_state__in=[WProduct.WPR_ACT_STATE_TORETEST,
WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_DOING]).count() WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_DOING]).count()
ins = SubProductionProgress.objects.filter(subproduction_plan=sp, ins = SubProductionProgress.objects.filter(subproduction_plan=sp,

View File

@ -16,7 +16,7 @@ from rest_framework.decorators import action
from apps.wf.models import Workflow from apps.wf.models import Workflow
from apps.wpm.filters import CuttingFilterSet, OperationRecordFilterSet, WMaterialFilterSet, WProductFilterSet from apps.wpm.filters import CuttingFilterSet, OperationRecordFilterSet, WMaterialFilterSet, WProductFilterSet
from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, \ from apps.wpm.models import OperationEquip, OperationWproduct, Pick, PickWproduct, WMaterial, WProduct, Operation, \
OperationMaterial, OperationRecord, OperationRecordItem, WprouctTicket OperationMaterial, OperationRecord, OperationRecordItem, WproductFlow, WprouctTicket
from apps.wpm.serializers import CuttingListSerializer, OperationEquipListSerializer, OperationEquipUpdateSerializer, \ from apps.wpm.serializers import CuttingListSerializer, OperationEquipListSerializer, OperationEquipUpdateSerializer, \
OperationMaterialCreate1ListSerailizer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2ListSerailizer, \ OperationMaterialCreate1ListSerailizer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2ListSerailizer, \
@ -27,7 +27,7 @@ from apps.wpm.serializers import CuttingListSerializer, OperationEquipListSerial
PickSerializer, OperationInitSerializer, OperationSubmitSerializer, ScrapSerializer, WMaterialListSerializer, \ PickSerializer, OperationInitSerializer, OperationSubmitSerializer, ScrapSerializer, WMaterialListSerializer, \
WProductCardSerializer, WProductDetailSerializer, WProductListSerializer, \ WProductCardSerializer, WProductDetailSerializer, WProductListSerializer, \
WpmTestFormInitSerializer, WproductMtestSerializer, WproductPutInSerializer, \ WpmTestFormInitSerializer, WproductMtestSerializer, WproductPutInSerializer, \
WproductPutInsSerializer, WproductTicketListSerializer WproductPutInsSerializer, WproductTicketListSerializer, WproductToOrderSerializer
from rest_framework.response import Response from rest_framework.response import Response
from django.db import transaction from django.db import transaction
@ -148,7 +148,7 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
""" """
perms_map = {'*': '*'} perms_map = {'*': '*'}
queryset = WProduct.objects.select_related('step', 'material', queryset = WProduct.objects.select_related('step', 'material',
'subproduction_plan', 'warehouse').prefetch_related('wproduct_child') 'subproduction_plan', 'warehouse', 'to_order').prefetch_related('wproduct_child')
serializer_class = WProductListSerializer serializer_class = WProductListSerializer
filterset_class = WProductFilterSet filterset_class = WProductFilterSet
search_fields = ['number'] search_fields = ['number']
@ -441,6 +441,30 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
ret.append([str(index + 1), item['step_name'], item['actions']]) ret.append([str(index + 1), item['step_name'], item['actions']])
return Response(ret) return Response(ret)
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=WproductToOrderSerializer)
@transaction.atomic
def to_order(self, request, pk=None):
"""
指派发货订单
"""
serializer = WproductToOrderSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
vdata = serializer.validated_data
wps = WProduct.objects.filter(id__in = [i.id for i in vdata.get('wproducts')])
wp = wps.first()
order = vdata['order']
if wp.material != order.product:
raise exceptions.ValidationError('所选订单与产品不符')
for i in wps:
if i.material != wp.material and i.material.type != Material.MA_TYPE_GOOD and i.act_state \
not in [WProduct.WPR_ACT_STATE_OK, WProduct.WPR_ACT_STATE_INM]:
raise exceptions.ValidationError('所选产品错误')
for i in wps:
i.to_order = order
i.update_by = request.user
i.save()
WpmServies.add_wproduct_flow_log(i,change_str='to_order')
return Response()
class WproductTicketViewSet(ListModelMixin, GenericViewSet): class WproductTicketViewSet(ListModelMixin, GenericViewSet):
""" """
@ -559,6 +583,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
ope = OperationEquip() ope = OperationEquip()
ope.operation = op ope.operation = op
ope.equip = i ope.equip = i
ope.state = i.state
ope.save() ope.save()
# 查询所需的工具工装 # 查询所需的工具工装
for i in SubprodctionMaterial.objects.filter(type=SubprodctionMaterial.SUB_MA_TYPE_TOOL, for i in SubprodctionMaterial.objects.filter(type=SubprodctionMaterial.SUB_MA_TYPE_TOOL,

View File

@ -47,17 +47,20 @@ class FitJSONRenderer(JSONRenderer):
""" """
response_body = BaseResponse() response_body = BaseResponse()
response = renderer_context.get("response") response = renderer_context.get("response")
response_body.code = response.status_code status_code = response.status_code # Http状态异常码
if response_body.code >= 400: # 响应异常 if status_code >= 400: # 如果http响应异常
if isinstance(data, dict) and 'code' in data: # 如果自定义了异常码
response_body = data
else:
response_body.data = data # data里是详细异常信息 response_body.data = data # data里是详细异常信息
prefix = "" prefix = ""
if isinstance(data, dict): if isinstance(data, dict):
prefix = list(data.keys())[0] prefix = list(data.keys())[0]
data = data[prefix] data = data[prefix]
if isinstance(data, list): elif isinstance(data, list):
data = data[0] data = data[0]
response_body.msg = prefix + ":" + str(data) # 取一部分放入msg,方便前端alert response_body.msg = prefix + ":" + str(data) # 取一部分放入msg,方便前端alert
else: else:
response_body.data = data response_body.data = data
renderer_context.get("response").status_code = 200 # 统一成200响应,用code区分 renderer_context.get("response").status_code = 200 # 统一成200响应,body里code区分业务异常
return super(FitJSONRenderer, self).render(response_body.dict, accepted_media_type, renderer_context) return super(FitJSONRenderer, self).render(response_body.dict, accepted_media_type, renderer_context)