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
.idea/
.vscode/
.idea/
server/static/

View File

@ -7,3 +7,11 @@ export function getPlanGantt(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"
: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 label="产品名称" prop="productName" width="120" show-overflow-tooltip>
</el-table-column>

File diff suppressed because it is too large Load Diff

View File

@ -35,12 +35,56 @@
}
},
mounted() {
this.init();
this.openTheCamera();
},
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() {
this.video = document.getElementById('video');
// this.video = document.getElementById('video');
this.screenshotCanvas = document.getElementById('screenshotCanvas');
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
@ -64,7 +108,6 @@
event.data.forEach(function (rect) {
context.strokeStyle = '#0764B7';
context.strokeRect(rect.x, rect.y, rect.width, rect.height);
// window.plot(rect.x, rect.y, rect.width, rect.height+20);
// 避免重复发送请求
if(!_this.uploadLock){
_this.uploadLock = true;
@ -133,7 +176,10 @@
that.uploadLock = false;
// this.$message.error('面部识别失败请重新验证');
});
}
},
closeCamera () {
this.video.srcObject.getTracks()[0].stop();
},
}
}
</script>

View File

@ -24,12 +24,55 @@
}
},
mounted() {
this.init();
this.openTheCamera();
},
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() {
this.video = document.getElementById('video');
this.screenshotCanvas = document.getElementById('screenshotCanvas');
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
@ -46,7 +89,6 @@
let _this = this;
//添加事件
tracker.on('track', function (event) {
// 检测出人脸 绘画人脸位置
context.clearRect(0, 0, canvas.width, canvas.height);
// 给每个人脸绘制对应的框
@ -112,7 +154,10 @@
that.uploadLock = false;
// this.$message.error('面部识别失败请重新验证');
});
}
},
closeCamera () {
this.video.srcObject.getTracks()[0].stop();
},
}
}
</script>

View File

@ -1,47 +1,122 @@
<template>
<div class="dashboard-container">
<el-card class="dashboardTopCard">
<div class="dashboardTopCard">
<div class="CardTitleWrap">
<span class="verticalLine"></span><span class="dashboardCardTitle">数据统计</span>
</div>
<div class="cards" @click="toDetail('1')">
<div class="cardCurrentText">本月{{contractTotalCurrent}}</div>
<div class="cardTotalText">{{contractTotalCount}}</div>
<div>合同总数</div>
<div style="padding: 20px;overflow: hidden;">
<div class="cardsWrap" @click="toDetail('1')">
<div class="svgIconWrap">
<svg-icon
icon-class="form"
class="svgIcon"
/>
</div>
<div class="cards" @click="toDetail('2')">
<div class="cardCurrentText">本月{{orderTotalCurrent}}</div>
<div class="cardTotalText">{{orderTotalCount}}</div>
<div>生产订单总数</div>
<div class="totalCountWrap">
<span class="totalCountText">本月合同数</span>
<div class="totalCountNum">
<span class="totalCount">{{contractTotalCurrent}}</span>
<span></span>
</div>
<div class="cards" @click="toDetail('3')">
<div class="cardCurrentText">在制{{planTotalCurrent}}</div>
<div class="cardTotalText">{{planTotalCount}}</div>
<div>已排产生产任务</div>
</div>
<div class="cards" @click="toDetail('4')">
<div class="cardCurrentText">本月4</div>
<div class="cardTotalText">20</div>
<div>累计交付产品数</div>
</div>
<div class="cards" @click="toDetail('5')">
<div class="cardCurrentText">本月2</div>
<div class="cardTotalText">2</div>
<div>累计不合格产品数</div>
<div class="cardsWrap" @click="toDetail('2')">
<div class="svgIconWrap">
<svg-icon
icon-class="form"
class="svgIcon"
/>
</div>
</el-card>
<el-row class="dashboardSubRow" :gutter="5">
<el-col :span="12">
<el-card style="border-radius: 10px;">
<div class="dashboardCardHand">
<div class="totalCountWrap">
<span class="totalCountText">本月生产订单数</span>
<div class="totalCountNum">
<span class="totalCount">{{orderTotalCurrent}}</span>
<span></span>
</div>
</div>
</div>
<div class="cardsWrap" @click="toDetail('3')">
<div class="svgIconWrap">
<svg-icon
icon-class="form"
class="svgIcon"
/>
</div>
<div class="totalCountWrap">
<span class="totalCountText">本月在制任务</span>
<div class="totalCountNum">
<span class="totalCount">{{planTotalCurrent}}</span>
<span></span>
</div>
</div>
</div>
<div class="cardsWrap" @click="toDetail('4')">
<div class="svgIconWrap">
<svg-icon
icon-class="form"
class="svgIcon"
/>
</div>
<div class="totalCountWrap">
<span class="totalCountText">本月交付产品</span>
<div class="totalCountNum">
<span class="totalCount">30</span>
<span></span>
</div>
</div>
</div>
<div class="cardsWrap" @click="toDetail('5')">
<div class="svgIconWrap">
<svg-icon
icon-class="form"
class="svgIcon"
/>
</div>
<div class="totalCountWrap">
<span class="totalCountText">本月不合格产品</span>
<div class="totalCountNum">
<span class="totalCount">30</span>
<span></span>
</div>
</div>
</div>
</div>
</div>
<div id="dashboardMiddle" class="dashboardMiddle">
<el-row>
<el-col :span="15">
<div class="CardTitleWrap">
<span class="verticalLine"></span><span class="dashboardCardTitle">任务排期</span>
<span class="verticalLine"></span><span class="dashboardCardTitle">任务进度</span>
</div>
</el-col>
<el-col :span="1" style="height: 1px"></el-col>
<el-col :span="8">
<div class="CardTitleWrap">
<span class="verticalLine"></span><span class="dashboardCardTitle">成品率</span>
</div>
</el-col>
</el-row>
<el-row class="dashboardCardPadding">
<el-col :span="15">
<div style="height: 40px;line-height: 40px;"></div>
<div>
<gantt
v-if="proList.length>0"
style="position: relative"
:proList="proList"
:ganttHeight="ganttHeight"
></gantt>
</div>
</el-col>
<el-col :span="1" style="height: 1px"></el-col>
<el-col :span="8">
<div class="dashboardCardHand">
<div class="dashboardCardFilter">
<el-date-picker
v-model="tableDate"
type="month"
placeholder="查询日期"
v-model="chartDate"
type="monthrange"
start-placeholder="开始日期"
end-placeholder="结束日期"
range-separator=""
format="yyyy 年 MM 月"
value-format="yyyy-MM"
@ -49,74 +124,30 @@
>
</el-date-picker>
<div class="convenientWrap">
<div class="convenientBtn" :class="{activeIndex:tableIndex==='1'}" @click="convenientClick('1','week')">本周</div>
<div class="convenientBtn" :class="{activeIndex:tableIndex==='2'}" @click="convenientClick('1','month')">本月</div>
<div class="convenientBtn" :class="{activeIndex:tableIndex==='3'}" @click="convenientClick('1','quarter')">三个月</div>
<div class="convenientBtn" :class="{activeIndex:chartIndex==='1'}" @click="convenientClick('1','week')">
本周
</div>
<div class="convenientBtn" :class="{activeIndex:chartIndex==='2'}"
@click="convenientClick('1','month')">本月
</div>
<div class="convenientBtn" :class="{activeIndex:chartIndex==='3'}"
@click="convenientClick('1','quarter')">三个月
</div>
</div>
</div>
<el-table
v-loading="listLoadingPlan"
:data="planList"
border fit stripe
:height="cardTabelHeight"
>
<el-table-column type="index" width="50" />
<el-table-column label="任务编号" prop="number">
</el-table-column>
<el-table-column label="产品名称">
<template slot-scope="scope">{{ scope.row.product_.name }}</template>
</el-table-column>
<el-table-column label="产品型号">
<template slot-scope="scope">{{ scope.row.product_.specification }}</template>
</el-table-column>
<el-table-column label="生产数量" prop="count">
</el-table-column>
<el-table-column label="开始时间" prop="start_date">
</el-table-column>
<el-table-column label="结束时间" prop="end_date">
</el-table-column>
</el-table>
</el-card>
</el-col>
<el-col :span="12">
<el-card style="border-radius: 10px;">
<div class="dashboardCardHand">
<div class="CardTitleWrap">
<span class="verticalLine"></span><span class="dashboardCardTitle">成品率</span>
</div>
<!--<div class="dashboardCardTitle">成品率</div>-->
<div class="dashboardCardFilter">
<el-date-picker
v-model="chartDate"
type="month"
placeholder="查询日期"
range-separator=""
format="yyyy 年 MM 月"
value-format="yyyy-MM"
@change="searchTimeChange('2')"
>
</el-date-picker>
<div class="convenientWrap">
<div class="convenientBtn" :class="{activeIndex:chartIndex==='1'}" @click="convenientClick('2','week')">本周</div>
<div class="convenientBtn" :class="{activeIndex:chartIndex==='2'}" @click="convenientClick('2','month')">本月</div>
<div class="convenientBtn" :class="{activeIndex:chartIndex==='3'}" @click="convenientClick('2','quarter')">三个月</div>
</div>
</div>
</div>
<div id="chartColumn" style="width:100%;" :style="{height:cardTabelHeight+'px'}"></div>
</el-card>
<div id="chartColumn" style="width:100%;" :style="{height:ganttHeight+'px'}"></div>
</el-col>
</el-row>
<el-row class="dashboardSubRow" :gutter="5">
</div>
<el-row :gutter="5">
<el-col :span="12">
<el-card style="border-radius: 10px;">
<div class="dashboardBottomRow">
<div class="dashboardCardHand">
<div class="CardTitleWrap">
<span class="verticalLine"></span><span class="dashboardCardTitle">库存列表</span>
<span class="stockMore" @click="stockMore">更多>></span>
</div>
<!--<div class="dashboardCardTitle">库存列表 </div>-->
<div class="block">
<el-pagination
:current-page.sync="stockPage"
@ -129,19 +160,22 @@
</el-pagination>
</div>
</div>
<div class="dashboardCardPadding">
<el-table
v-loading="listLoadingStock"
:data="stockList"
border fit stripe
fit stripe
size="mini"
:height="cardTabelHeight"
style="border-top: 1px solid #f5f5f5;"
>
<el-table-column label="物料编号" prop="material_">
<template slot-scope="scope">{{ scope.row.material_.number }}</template>
</el-table-column>
<el-table-column label="物料名称" prop="material_">
<el-table-column label="物料名称" prop="material_" show-overflow-tooltip>
<template slot-scope="scope">{{ scope.row.material_.name }}</template>
</el-table-column>
<el-table-column label="规格型号" prop="material_">
<el-table-column label="规格型号" prop="material_" show-overflow-tooltip>
<template slot-scope="scope">{{ scope.row.material_.specification }}</template>
</el-table-column>
<el-table-column label="物料类型" prop="material_">
@ -156,15 +190,15 @@
<template slot-scope="scope">{{ scope.row.warehouse_.name }}</template>
</el-table-column>
</el-table>
</el-card>
</div>
</div>
</el-col>
<el-col :span="12">
<el-card style="height: 350px;border-radius: 10px;">
<div class="dashboardBottomRow">
<div class="dashboardCardHand">
<div class="CardTitleWrap">
<span class="verticalLine"></span><span class="dashboardCardTitle">提醒</span>
</div>
<!--<div class="dashboardCardTitle">提醒</div>-->
<div class="block">
<el-pagination
:current-page.sync="remindPage"
@ -177,29 +211,16 @@
</el-pagination>
</div>
</div>
<el-tabs type="card" :style="{height:cardTabelHeight+'px'}" v-model="activeName" @tab-click="activeNameClick">
<div class="dashboardCardPadding">
<el-tabs type="card" :style="{height:cardTabelHeight+'px'}" v-model="activeName"
@tab-click="activeNameClick">
<el-tab-pane label="库存警告" name="库存警告">
<ul class="lists">
<li class="listItem">
<ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()" @mouseleave="Up()">
<li v-for="item in list" :key="item.id" class="listItem">
<div class="itemText">
<span>玻璃低于安全库存</span><span>2021-12-30</span>
<span>{{item.name}}</span><span style="float: right">2021-12-30</span>
</div>
</li>
<li class="listItem" v-for="item in warningList" :key="item.id">
<div class="itemText">
<span>{{item}}</span><span>2021-12-20</span>
</div>
</li>
<!--<li class="listItem">
<div class="itemText">
<span>玻璃低于安全库存</span><span>2021-11-30</span>
</div>
</li>
<li class="listItem">
<div class="itemText">
<span>某某批货临近交货日期</span><span>2021-11-20</span>
</div>
</li>-->
</ul>
</el-tab-pane>
<el-tab-pane label="临近交货" name="临近交货">
@ -231,7 +252,8 @@
</ul>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</div>
</el-col>
</el-row>
</div>
@ -240,16 +262,22 @@
<script>
import echarts from 'echarts'
import {mapGetters} from 'vuex';
import {getPlanGantt} from "@/api/srm";
import {getProcessYield} from "@/api/srm";
import {getMaterialList} from "@/api/mtm";
import {getInventoryList} from "@/api/inm";
import {getProductionplanList} from "@/api/pm";
import {getmaterialbatchList} from "@/api/inm";
import gantt from "@/components/Gantt/dashGantt";
import {getContractList, getOrderList} from "@/api/sam";
export default {
components: {gantt},
name: 'Dashboard',
data() {
return {
animate: false,
intNum: null,
chartColumn: null,
week: null,
currentTime: null,
@ -265,15 +293,20 @@ export default {
tableIndex: null,
chartIndex: null,
tableDate: '2021-12',
chartDate:'2021-12',
chartDate: [],
proList: [],
planList: [],
stockList: [],
remindList: [],
warningList: [],
list: [
{id:1,name:'HIehd9',card:'3337',sco:'REF-32'},
{id:1,name:'HIehd9',card:'3337',sco:'REF-32'},
{id:1,name:'HIehd9',card:'3337',sco:'REF-32'}
{id: 1, name: 'HIehd91', card: '3337', sco: 'REF-32'},
{id: 2, name: 'HIehd92', card: '3337', sco: 'REF-32'},
{id: 3, name: 'HIehd93', card: '3337', sco: 'REF-32'},
{id: 4, name: 'HIehd94', card: '3337', sco: 'REF-32'},
{id: 5, name: 'HIehd95', card: '3337', sco: 'REF-32'},
{id: 6, name: 'HIehd96', card: '3337', sco: 'REF-32'},
{id: 7, name: 'HIehd97', card: '3337', sco: 'REF-32'},
],
options: {
"1": '成品',
@ -284,7 +317,10 @@ export default {
"6": '辅助工装',
},
activeName: '库存警告',
chartData: {
xAxisData: ["冷加工", "热弯", "钢化", "镀膜", "夹层", "包边", "装框"],
seriesData: [80, 95, 96, 96, 96, 98, 99, 100],
},
contractTotalCount: null,//合同总数
contractTotalCurrent: null,
orderTotalCount: null,//生产订单总数
@ -296,6 +332,7 @@ export default {
listLoadingPlan: false,
listLoadingStock: false,
cardTabelHeight: null,
ganttHeight: 0,
}
},
computed: {
@ -306,8 +343,25 @@ export default {
])
},
methods: {
openWord(){
window.open("http://47.95.0.242:2222/media/2021/09/07/004-%E8%AF%B7%E5%81%87%E5%8D%95_LL8uZdx.docx");
getNoticeData() {
this.ScrollUp();
},
ScrollUp() {
this.intNum = setInterval(() => {
this.animate = true;
setTimeout(() => {
this.list.push(this.list[0]);
this.list.shift();
this.animate = false;
}, 500)
}, 1000);
},
//鼠标移上去停止
Stop() {
clearInterval(this.intNum);
},
Up() {
this.ScrollUp();
},
getStatisticsData() {
let that = this;
@ -353,11 +407,35 @@ export default {
}
});
//获取交付产品
//获取不合格产品
getMaterialList({page:0,tag:'low_inm'}).then((response) => {
//获取成品率
let d = new Date(that.currentYear, month, 0);
let days = d.getDate();
let start = that.currentYear + '-' + that.currentMonth;
let startDate = start + '-01';
let endDate = start + '-' + days;
that.chartDate = [start, start];
getProcessYield({datetime_start: startDate, datetime_end: endDate}).then((response) => {
if (response.data) {
that.warningList = response.data;
let list = response.data;
let xAxisData = [], seriesData = [];
list.forEach(item => {
xAxisData.push(item.name);
let rate = item.rate * 100;
seriesData.push(rate.toFixed(2))
});
that.chartData.xAxisData = xAxisData;
that.chartData.seriesData = seriesData;
this.drawChart();
}
});
//获取库存警告
getMaterialList({tag: 'low_inm'}).then((response) => {
if (response.data) {
that.warningList = response.data.results;
}
});
},
@ -394,6 +472,7 @@ export default {
},
//图标渲染
drawChart() {
let that = this;
this.chartColumn = echarts.init(document.getElementById('chartColumn'));
this.chartColumn.setOption({
// title: { text: '成品率' },
@ -429,7 +508,7 @@ export default {
splitLine: {
show: false, //去掉X轴分割线
},
data: ["冷加工", "热弯", "钢化", "镀膜", "夹层", "包边", "装框", "成品"]
data: that.chartData.xAxisData,
},
yAxis: {
axisLine: {
@ -451,8 +530,8 @@ export default {
series: [{
name: '成品率',
type: 'bar',
barWidth: 40,
data: this.seriesData,
barWidth: 20,
data: that.chartData.seriesData,
label: {
show: true, //开启显示
position: 'top', //在上方显示
@ -464,13 +543,13 @@ export default {
},
itemStyle: {
normal: {
/*color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: '#adcff3'
}, {
offset: 1,
color: '#409EFF'
}]),*/
}]),
/*color: function(params) {
let colorList = [
@ -492,13 +571,13 @@ export default {
}
]);
},*/
color: function(params) {
/*color: function(params) {
let colorList = [
'#5fcdc2','#a378e4','#fea94c','#f27197',
'#409eff','#5fcdc2','#a378e4','#fea94c'
];
return colorList[params.dataIndex]
},
},*/
}
},
}]
@ -513,7 +592,7 @@ export default {
} else if (index === '2') {
this.$router.push({name: 'order', params: {page: 1, page_size: 20}})
} else if (index === '3') {
this.$router.push({name:'plan',params:{page:1,page_size:20}})
this.$router.push({name: 'management', params: {page: 1, page_size: 20}})
} else if (index === '4') {
this.$router.push({name: 'product', params: {page: 1, page_size: 20, material__type: 1}})
} else if (index === '5') {
@ -556,18 +635,22 @@ export default {
startTime = yea + '-' + mon + '-01';
}
if (index === '1') {
this.tableDate = null;
this.tableIndex = activeIndex;
this.listLoadingPlan = true;
getProductionplanList({page:0,create_time_start:startTime,create_time_end:endTime}).then((response) => {
if (response.data) {
that.planList = response.data;
}
this.listLoadingPlan = false;
});
}else{
this.chartDate = null;
this.chartIndex = activeIndex;
getProcessYield({datetime_start: startTime, datetime_end: endTime}).then((response) => {
if (response.data) {
let list = response.data;
let xAxisData = [], seriesData = [];
list.forEach(item => {
xAxisData.push(item.name);
let rate = item.rate * 100;
seriesData.push(rate.toFixed(2))
});
that.chartData.xAxisData = xAxisData;
that.chartData.seriesData = seriesData;
this.drawChart();
}
});
}
//根据时间和类型获取数据
},
@ -578,33 +661,34 @@ export default {
let year = null, month = null, days = null;
if (index === '1') {
this.tableIndex = null;
startDate = this.tableDate+'-01';
year = this.tableDate.split("-")[0];
month = this.tableDate.split("-")[1];
startDate = that.chartDate[0] + '-01';
year = that.chartDate[1].split("-")[0];
month = that.chartDate[1].split("-")[1];
let d = new Date(year, month, 0);
days = d.getDate();
endDate = this.tableDate + '-' + days;
this.listLoadingPlan = true;
getProductionplanList({page:0,create_time_start:startDate,create_time_end:endDate}).then((response) => {
getProcessYield({datetime_start: startDate, datetime_end: endDate}).then((response) => {
if (response.data) {
that.planList = response.data;
}
this.listLoadingPlan = false;
let list = response.data;
let xAxisData = [], seriesData = [];
list.forEach(item => {
xAxisData.push(item.name);
let rate = item.rate * 100;
seriesData.push(rate.toFixed(2))
});
that.chartData.xAxisData = xAxisData;
that.chartData.seriesData = seriesData;
this.drawChart();
}
});
}else{
this.chartIndex = null;
startDate = this.chartDate+'-01';
year = this.chartDate.split("-")[0];
month = this.chartDate.split("-")[1];
let d = new Date(year, month, 0);
days = d.getDate();
endDate = this.chartDate + '-'+days;
}
},
//库存
handleStockSizeChange(val) {
this.stockPageSize = val;
this.stockPage = 1;
},
//库存
handleStockCurrentChange(val) {
let that = this;
this.listLoading = true;
@ -617,17 +701,15 @@ export default {
this.listLoading = false;
});
},
activeNameClick(tab, event) {
//提示
activeNameClick(tab) {
let that = this;
// debugger;
// console.log(tab, event);
if (tab.label === '库存警告') {
getMaterialList({page: 0, tag: 'low_inm'}).then((response) => {
if (response.data) {
that.warningList = response.data;
that.remindTotal = response.data.length;
}
this.listLoadingPlan = false;
});
} else if (tab.label === '临近交货') {
getmaterialbatchList({page: 0, tag: 'expired'}).then((response) => {
@ -635,7 +717,6 @@ export default {
that.warningList = response.data;
that.remindTotal = response.data.length;
}
this.listLoadingPlan = false;
});
} else if (tab.label === '过期提醒') {
getmaterialbatchList({page: 0, tag: 'expired'}).then((response) => {
@ -643,7 +724,6 @@ export default {
that.warningList = response.data;
that.remindTotal = response.data.length;
}
this.listLoadingPlan = false;
});
}
},
@ -654,18 +734,91 @@ export default {
handleRemindCurrentChange(val) {
console.log(`当前页: ${val}`);
},
getGanttData() {
let that = this;
getPlanGantt({}).then(res => {
if (res.code === 200) {
let arr = [];
let list = res.data.results;
list.forEach(item => {
if (!item.children || item.children.length < 1) {
let startTime = new Date(item.start_date).getTime();
let endTime = new Date(item.end_date).getTime();
let obj = new Object();
obj.name = item.number;
obj.id = item.id;
obj.top = 20;
obj.startTime = startTime;
obj.endTime = endTime;
obj.planTime = [startTime, endTime];
obj.per = item.count;
obj.type = 1;
obj.productName = item.product_.name;
obj.productNum = item.product_.specification;
obj.isShow = true;
arr.push(obj);
} else if (item.children && item.children.length >= 1) {
let startTime = new Date(item.start_date).getTime();
let endTime = new Date(item.end_date).getTime();
let temp = [];
let parentId = item.id;
let children = item.children;
children.forEach(child => {
let start = new Date(child.start_date).getTime();
let end = new Date(child.end_date).getTime();
let objChild = new Object();
objChild.name = child.number;
objChild.id = child.id;
objChild.top = 50;
objChild.parentId = parentId;
objChild.startTime = start;
objChild.endTime = end;
objChild.planTime = [start, end];
objChild.per = child.count;
objChild.per1 = child.count_real;
objChild.type = 1;
objChild.productName = child.product_.name;
objChild.productNum = child.product_.specification;
objChild.isShow = true;
temp.push(objChild);
});
let obj = new Object();
obj.name = item.number;
obj.id = item.id;
obj.top = 20;
obj.startTime = startTime;
obj.endTime = endTime;
obj.planTime = [startTime, endTime];
obj.per = item.count;
obj.per1 = item.count_real;
obj.type = 3;
obj.productName = item.product_.name;
obj.productNum = item.product_.specification;
obj.children = temp;
obj.isShow = true;
arr.push(obj);
}
that.proList = arr;
});
} else {
that.$message.error(res.msg);
}
})
},
},
mounted() {
// let he = document.documentElement.clientHeight || document.body.clientHeight;
let hei = document.getElementsByClassName('app-main')[0].clientHeight;
let heig = document.getElementsByClassName('dashboardTopCard')[0].clientHeight;
this.cardTabelHeight = ((hei - heig - 130) / 2);
document.getElementById('chartColumn').style.height = this.cardTabelHeight+'px';
this.drawChart();
this.ganttHeight = this.cardTabelHeight - 50;
document.getElementById('chartColumn').style.height = this.cardTabelHeight - 35 + 'px';
this.getPlanList();
this.getStockList();
this.getGanttData();
this.getStatisticsData();
this.getNoticeData();
},
updated() {
this.drawChart()
@ -674,90 +827,130 @@ export default {
</script>
<style lang="scss" scoped>
ul{
margin: 0!important;
}
.el-card.is-always-shadow {
height: auto !important;
}
.dashboard-container {
margin: 5px 6px;
}
.dashboard-text {
font-size: 30px;
line-height: 46px;
}
.dashboardTopCard{
.dashboardTopCard, .dashboardMiddle, .dashboardBottomRow {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
margin-bottom: 5px;
border-radius: 10px;
.cards{
width: 18%;
color: #ffffff;
background: #5fcdc2;
text-align: center;
border-radius: 10px;
padding: 10px;
line-height: 30px;
background: #ffffff;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
}
.dashboardBottomRow {
margin-bottom: 0;
}
.dashboardCardPadding {
padding: 5px 20px 20px 20px;
}
/**/
.cardsWrap {
display: flex;
width: 20%;
color: #777777;
float: left;
margin: 15px 1%;
.cardCurrentText{
text-align: left;
padding-left: 20px;
font-size: 14px;
}
.cardTotalText{
font-size: 30px;
.svgIconWrap {
margin-right: 20px;
width: 50px;
height: 50px;
border-radius: 15px;
text-align: center;
.svgIcon {
font-size: 24px;
margin-top: 13px
}
}
.cards:nth-of-type(2){
background:#5fcdc2;;
}
.cards:nth-child(3){
background:#a378e4;;
}
.cards:nth-of-type(4){
background:#fea94c;;
}
.cards:nth-of-type(5){
background:#f27197;;
}
.cards:nth-of-type(6){
background:#409eff;;
}
.cards:hover{
box-shadow: 0 0 12px 5px rgba(0,0,0,.1);
.cardsWrap:nth-child(1) {
.svgIconWrap {
background: #e9f3ff;
.svgIcon {
color: #409EFF;
}
}
}
.cardsWrap:nth-child(2) {
.svgIconWrap {
background: #fff1de;
.svgIcon {
color: #ffb23f;
}
}
}
.cardsWrap:nth-child(3) {
.svgIconWrap {
background: #d9f6d8;
.svgIcon {
color: #54cb48;
}
}
}
.cardsWrap:nth-child(4) {
.svgIconWrap {
background: #f0e8fd;
.svgIcon {
color: #a378e4;
}
}
}
.cardsWrap:nth-child(5) {
.svgIconWrap {
background: #f7e5ea;
.svgIcon {
color: #f27197;
}
}
}
.totalCountText {
height: 20px;
line-height: 20px
}
.totalCountNum {
height: 30px;
}
.totalCount {
font-size: 25px;
font-weight: bold;
color: #626262;
}
/**/
.CardTitleWrap {
display: flex;
height: 35px;
line-height: 35px;
padding-left: 1%;
padding-top: 6px;
box-sizing: border-box;
border-bottom: 1px solid #f5f5f5;
/*padding-top: 6px;*/
.verticalLine {
width: 4px;
height: 20px;
background: orangered;
height: 15px;
background: #409EFF;
margin-right: 7px;
margin-top: 6px;
margin-top: 8px;
}
.dashboardCardTitle {
height: 35px;
line-height:35px;
font-size: 20px;
height: 34px;
line-height: 34px;
font-size: 14px;
font-weight: bold;
width: 70px;
vertical-align: middle;
margin-right: 7px;
}
.stockMore {
font-size: 14px;
font-size: 12px;
font-weight: normal;
color: #409EFF;
cursor: pointer;
}
}
.dashboardSubRow{
margin-bottom: 5px;
/*成品率筛选条件*/
.dashboardCardHand {
display: flex;
justify-content: space-between;
@ -767,12 +960,13 @@ export default {
display: flex;
border: 1px solid #DCDFE6;
border-radius: 6px;
height: 35px;
line-height:35px;
height: 30px;
line-height: 30px;
margin-left: 10px;
font-size: 12px;
.convenientBtn {
cursor: pointer;
width: 60px;
width: 50px;
text-align: center;
border-right: 1px solid #DCDFE6;
}
@ -789,13 +983,18 @@ export default {
}
}
}
}
.anim {
transition: all 0.5s;
margin-top: -35px; //高度等于行高
}
.lists {
height: 100%;
line-height: 35px;
transition: top 0.5s;
overflow-y: scroll;
padding-right: 40px;
margin: 0 !important;
.listItem {
height: 40px;
line-height: 40px;
@ -812,4 +1011,8 @@ export default {
height: 100% !important;
}
}
#dashboardMiddle .el-range-editor--medium.el-input__inner,
#dashboardMiddle .el-range-editor.el-input__inner {
height: 30px !important;
}
</style>

View File

@ -64,10 +64,10 @@
</el-tabs>
</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 class="testTracking">
<faceLogin name="faceLogin" @func="getMsgFormSon"></faceLogin>
<faceLogin ref="faceTracking" name="faceLogin" @func="getMsgFormSon"></faceLogin>
</div>
</el-dialog>
</div>
@ -179,9 +179,61 @@
//人脸登录
takePhoto(){
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){
this.limitedPhoto = data;
// this.limitedPhoto = data;
},
},
};

View File

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

View File

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

View File

@ -3,15 +3,15 @@
<el-card style="margin-top: 2px">
<el-descriptions title="任务详情" :column="4" border style="margin-bottom: 20px">
<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_.specification}}</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_.specification}}
</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="计划开工时间">{{productionplan.start_date}}</el-descriptions-item>
<el-descriptions-item label="计划完工时间">{{productionplan.end_date}}</el-descriptions-item>
</el-descriptions>
<el-table
:data="wproduct"
border
@ -20,34 +20,30 @@
style="width: 100%"
height="500"
>
<el-table-column type="index" label="序号" width="50"/>
<el-table-column label="玻璃编号/产品编号">
<template slot-scope="scope">{{ scope.row.number }}</template>
</el-table-column>
<el-table-column label="所在子工序">
<template slot-scope="scope" >{{
scope.row.step_.name
}}</template>
<template slot-scope="scope">
{{scope.row.step_.name}}
</template>
</el-table-column>
<el-table-column label="产品状态">
<template slot-scope="scope">{{
actstate_[scope.row.act_state]
}}</template>
}}
</template>
</el-table-column>
<el-table-column label="生产状态">
<template slot-scope="scope" >{{
scope.row.step_.name
}}</template>
<template slot-scope="scope">
{{scope.row.step_.name}}
</template>
</el-table-column>
<el-table-column label="最后检验结果">
<template slot-scope="scope">
<el-span v-if="scope.row.last_test_result==false">不合格</el-span>
<el-span v-if="scope.row.last_test_result==true">合格</el-span>
<span v-if="scope.row.last_test_result==false">不合格</span>
<span v-if="scope.row.last_test_result==true">合格</span>
</template>
</el-table-column>
<el-table-column label="生产记录">
@ -61,19 +57,15 @@
width="220px"
>
<template slot-scope="scope">
<el-link
v-if="checkPermission(['material_delete'])"
type="primary"
@click="handleoption(scope)"
>生成流程卡</el-link
>
>生成流程卡
</el-link>
</template>
</el-table-column>
</el-table>
<!--检验记录-->
<el-dialog title="检验记录" :visible.sync="limitedCheckRecord">
<el-table
@ -85,66 +77,49 @@
<el-table-column label="表单名称">
<template slot-scope="scope">{{ scope.row.form_.name }}</template>
</el-table-column>
<el-table-column align="center" label="操作">
<template slot-scope="scope">
<el-link
@click="handleRecordDetail(scope)"
>查看
</el-link>
</template>
</el-table-column>
</el-table>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false"> </el-button>
</div>
</el-dialog>
<!--非检查表显示-->
<el-dialog
width="60%"
:title="formName"
:visible.sync="recordVisible"
:close-on-click-modal="false"
@close="recordCancel"
>
<customForm
v-if="recordVisible"
:results="fieldList"
:hasPicture="hasPicture"
:formID="recordform"
:wproduct="wproduct"
:recordId="recordId"
:isDisabled="isDisabled"
@recordSubmit="recordSubmit"
@recordSave="recordSave"
@recordCancel="recordCancel"
/>
<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>
</el-card>
</div>
</template>
<script>
import { getProductionplan,getsubproductionplanList } from "@/api/pm";
import { getwproductList,getrecordList} from "@/api/wpm";
import {getProductionplan} from "@/api/pm";
import {getwproductList, getrecordList,recordInit} from "@/api/wpm";
import checkPermission from "@/utils/permission";
import customForm from '@/components/customForm/index'
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() {
return {
productionplan: {
@ -164,7 +139,8 @@ export default {
30: '已接受',
40: '生产中',
50: '已完成',
60:'军检完成'},
60: '军检完成'
},
actstate_: {
6: "待复检",
10: "操作进行中",
@ -180,23 +156,17 @@ export default {
process_json: null,
productionplanID: null,
dialogVisibleForm: false,
tableForm:{
name:'',
},
recordform: defaultrecordform,
dialogType: "new",
dialogVisible: false,
dialogType1: "new",
dialogVisible1: false,
tableForm: defaultrecordform,
checkForm: {
hhh: '',
},
recordVisible: false,
customfieldList: [],
recordform: null,
recordId: null,
fifo_detail: "",
formName: "项目检查表",
@ -229,32 +199,24 @@ export default {
obj = response.data.process_json[item];
process_json.push(obj)
}
this.process_json = process_json;
}
});
},
getwproductList()
{
getwproductList() {
getwproductList({production_plan: this.id, page: 0,}).then((response) => {
if (response.data) {
this.wproduct = response.data;
}
});
},
//查看该玻璃检验记录表
handleoption(scope) {
this.$router.push({name: "processcard", params: {id: scope.row.id},})
},
//查看生产记录
select(scope)
{
select(scope) {
this.limitedCheckRecord = true;
getrecordList({wproduct: scope.row.id, page: 0}).then(res => {
if (res.code == 200) {
@ -262,52 +224,32 @@ export default {
}
})
},
//点击记录里的查看
handleRecordDetail(scope) {
let that = this;
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) => {
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.fieldList = [];
recordInit(scope.row.id).then((res) => {
if (res.code >= 200) {
that.recordVisible = true;
});
});
that.formName = res.data.form_.name;
that.fieldList = res.data.record_data;
}
}
);
})
},
},
};
</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"
:visible.sync="recordVisible"
:close-on-click-modal="false"
@close="recordCancel"
>
<el-row>
<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-descriptions title="任务详情" :column="5" border style="margin-bottom: 20px">
<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_.specification}}</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_.specification}}
</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>
<el-card style="margin-bottom: 20px">
<div slot="header" class="clearfix">
<span>下料清单</span>
</div>
<el-table
:data="cut"
@ -20,10 +21,7 @@
stripe
style="width: 100%"
>
<el-table-column label="序号" type="index" width="50"/>
<el-table-column label="玻璃批次">
<template slot-scope="scope">{{ scope.row.from_batch }}</template>
</el-table-column>
@ -62,9 +60,6 @@
<el-table-column label="检验员">
<template slot-scope="scope">{{ scope.row.create_by_.username }}</template>
</el-table-column>
</el-table>
</el-card>
<el-tabs v-model="activeName" type="card">
@ -77,51 +72,41 @@
stripe
style="width: 100%"
height="500"
:load="load"
:tree-props="{children: 'children'}">
<el-table-column type="index" width="50"/>
<el-table-column label="玻璃编号">
<template slot-scope="scope">{{ scope.row.number }}</template>
</el-table-column>
<el-table-column label="所在子工序">
<template slot-scope="scope" >{{
scope.row.step_.name
}}</template>
<template slot-scope="scope">
{{scope.row.step_.name}}
</template>
</el-table-column>
<el-table-column label="玻璃状态">
<template slot-scope="scope" >{{
scope.row.material_.name
}}</template>
<template slot-scope="scope">
{{scope.row.material_.name}}
</template>
</el-table-column>
<el-table-column label="状态">
<template slot-scope="scope" >{{
actstate_[scope.row.act_state]
}}</template>
<template slot-scope="scope">
{{actstate_[scope.row.act_state]}}
</template>
</el-table-column>
<el-table-column
align="center"
label="过程记录"
width="220px"
>
<template slot-scope="scope">
<el-link
v-if="checkPermission(['material_delete'])"
type="primary"
@click="handleoption(scope)"
>查看</el-link
>
>查看
</el-link>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="工序" name="2">
<el-table
@ -132,51 +117,37 @@
style="width: 100%"
height="500"
>
<el-table-column type="index" width="50"/>
<el-table-column label="工序名称">
<template slot-scope="scope">{{ scope.row.process_name }}</template>
</el-table-column>
<el-table-column label="玻璃数量">
<template slot-scope="scope">{{ scope.row.count_real }}</template>
</el-table-column>
<el-table-column label="合格数量">
<template slot-scope="scope">{{ scope.row.count_ok }}</template>
</el-table-column>
<el-table-column label="不合格数量">
<template slot-scope="scope">{{ scope.row.count_notok }}</template>
</el-table-column>
<el-table-column
align="center"
label="过程记录"
width="220px"
>
<template slot-scope="scope">
<el-link
v-if="checkPermission(['material_delete'])"
type="primary"
@click="handleprocess(scope)"
>查看</el-link
>
>查看
</el-link>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
<script>
@ -209,7 +180,8 @@ export default {
30: '已接受',
40: '生产中',
50: '已完成',
60:'军检完成'},
60: '军检完成'
},
actstate_: {
6: "待复检",
10: "操作进行中",
@ -268,8 +240,7 @@ export default {
});
},
getwproductList()
{
getwproductList() {
getwproductList({production_plan: this.id, page: 0,}).then((response) => {
if (response.data) {
this.wproduct = response.data;
@ -278,8 +249,7 @@ export default {
});
},
//下料清单
gecutList()
{
gecutList() {
getcutList({production_plan: this.id, page: 0}).then((response) => {
if (response.data) {
this.cut = response.data;
@ -291,7 +261,15 @@ export default {
//查看该玻璃检验记录表
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
},
})
},
//查看工序对应的玻璃

View File

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

View File

@ -102,10 +102,12 @@
<p>创建时间 {{watchedCreateTime}}</p>
</el-col>
</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>
</div>
</div>
</div>
<el-dialog
:visible.sync="dialogVisible"
:title="dialogType === 'edit' ? '编辑工作流' : '新增工作流'">
@ -481,7 +483,7 @@ export default {
margin: 10vh auto 0;
text-align: center;
border-radius: 2px;
max-height: 75vh;
max-height: 80vh;
overflow-y: scroll;
}
.svgItem{

View File

@ -241,7 +241,7 @@
</el-step>
</el-steps>
<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>
</div>

View File

@ -613,6 +613,27 @@
<el-button type="primary" @click="retrialSubmit"> </el-button>
</div>
</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>
</template>
<script>
@ -755,6 +776,7 @@
dialogFormVisible: false,
dialogFormVisibles: false,
limitedCheckRecord: false,
recordFinishedVisible: false,
testrecord: {},
retrialItem: {},//复检对象
retrialResponse: {},//复检对象
@ -862,13 +884,10 @@
handleRetrial(scope) {
let that = this;
this.retrialItem = Object.assign({}, scope.row);
console.log(this.retrialItem);
getRetrial(scope.row.id).then(res => {
that.retrialResponse = res.data;
getWorkflowInit(res.data.workflow).then((response) => {
if (response.data) {
debugger;
console.log(response.data);
that.retrialForm.transition = response.data.transitions[0].id;
that.customfieldList = response.data.field_list;
for (let i = 0; i < that.customfieldList.length; i++) {
@ -926,10 +945,8 @@
if (response.data) {
this.wproductList3 = response.data;
}
});
},
//半成品批量入库
handleCreate() {
this.dialogFormVisibles = true;
@ -941,7 +958,6 @@
_this.mutipID = [];
this.$refs.multipleTable.selection.forEach((item) => {
_this.mutipID.push(item.id);
});
createputins({
warehouse: this.form.warehouse,
@ -1093,44 +1109,13 @@
handleRecordDetail(scope) {
let that = this;
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) => {
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;
if (res.code >= 200) {
that.recordFinishedVisible = true;
that.formName = res.data.form_.name;
that.fieldList = res.data.record_data;
}
}
arr.push(obj)
}
that.fieldList = arr;
that.$nextTick(() => {
that.isDisabled = true;
that.recordVisible = true;
});
})
}
});
},
//半产品复检
handleReview() {
@ -1331,3 +1316,16 @@
}
}
</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 django.urls import path, include
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 = [
path('cleandata/', CleanDataView.as_view()),
path('update_cutting/', UpdateCuttingView.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 apps.inm.models import FIFO, FIFOItem, Inventory, MaterialBatch
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.wf.models import Ticket
from apps.wpm.models import Operation, OperationMaterial, WProduct, WproductFlow
@ -80,3 +80,14 @@ class UpdateFIFOItem(APIView):
i.is_testok = None
i.save()
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
import django.utils.timezone as timezone
from django.db.models.query import QuerySet
from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File
#from apps.mtm.models import Process
from apps.system.models import CommonADModel, CommonAModel, CommonBModel, Organization, User, Dict, File
from utils.model import SoftModel, BaseModel
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 = (
(0, '完好'),
(1, '限用'),
(2, '在修'),
(3, '禁用')
)
statedm_choices = (
(0, '合格'),
(1, '准用'),
(2, '限用'),
(3, '禁用'),
(4, '停用'),
(5, '封存')
)
(EQUIP_STATE_OK, '完好'),
(EQUIP_STATE_LIMIT, '限用'),
(EQUIP_STATE_FIX, '在修'),
(EQUIP_STATE_DISABLE, '禁用')
)
state2_choices = (
(EQUIP_STATE_OK, '合格'),
(EQUIP_STATE_DISABLE, '禁用')
)
EQUIP_TYPE_PRO = 1
EQUIP_TYPE_TEST = 2
type_choices = (
(1, '生产设备'),
(2, '检验工具')
@ -54,20 +56,21 @@ class Equipment(CommonBModel):
factory = models.CharField('生产厂', max_length=50, null=True, blank=True)
production_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)
place = models.CharField('存放位置', max_length=50, null=True, blank=True)
count = models.IntegerField('数量', default=0)
keeper = models.ForeignKey(User, verbose_name='保管人', on_delete=models.CASCADE, null=True, blank=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)#监视,测量设备
way = models.IntegerField('校准或检定方式', choices=way_choices, default=1)#监视,测量设备
standard = models.CharField('溯源标准或依据', max_length=200, blank=True, null=True)#监视,测量设备
cycle = models.IntegerField('校准或检定周期', default=0)#监视,测量设备
usetype = models.IntegerField('使用类别', choices=usetype_choices, default=1)#监视,测量设备
statedm = models.IntegerField('设备状态', choices=statedm_choices, default=0)#监视,测量设备
# 以下是监视测量设备单独字段
mgmtype = models.IntegerField('管理类别', choices=mgmtype_choices, default=1)
way = models.IntegerField('校准或检定方式', choices=way_choices, default=1)
standard = models.CharField('溯源标准或依据', max_length=200, blank=True, null=True)
cycle = models.IntegerField('校准或检定周期(月)', null=True, blank=True)
usetype = models.IntegerField('使用类别', choices=usetype_choices, default=1)
check_date = models.DateField('最近校准检查日期', blank=True, null=True)
next_check_date = models.DateField('预计下次校准检查日期',blank=True, null=True)
class Meta:
verbose_name = '设备信息'
verbose_name_plural = verbose_name
@ -75,8 +78,10 @@ class Equipment(CommonBModel):
def __str__(self):
return self.number + '-' + self.name
class Equipmentrecord(CommonBModel):
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)
class ECheckRecord(CommonADModel):
"""
校准鉴定记录
"""
equipment = models.ForeignKey(Equipment, verbose_name='校准检定设备', on_delete=models.CASCADE)
check_date = models.DateField('校准检查日期')
description = models.CharField('描述', max_length=200, blank=True, null=True)

View File

@ -1,13 +1,12 @@
from apps.mtm.models import Step
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
from .models import Equipment,Equipmentrecord
from rest_framework import exceptions
from .models import Equipment, ECheckRecord
from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSerializer
class EquipmentSerializer(ModelSerializer):
belong_dept_ = OrganizationSimpleSerializer(source='belong_dept', read_only=True)
class EquipmentListSerializer(ModelSerializer):
keeper_ = UserSimpleSerializer(source='keeper', read_only=True)
step_ = serializers.SerializerMethodField()
class Meta:
@ -17,24 +16,35 @@ class EquipmentSerializer(ModelSerializer):
@staticmethod
def setup_eager_loading(queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.select_related('belong_dept','keeper')
queryset = queryset.select_related('keeper')
return queryset
def get_step_(self, obj):
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 Meta:
model = Equipment
fields = ['id', 'number', 'name', 'state']
fields = ['id', 'number', 'name', 'state', 'model']
class EquipmentrecordSerializer(ModelSerializer):
equipment_ = EquipmentSerializer(source='equipment', read_only=True)
class ECheckRecordListSerializer(ModelSerializer):
equipment_ = EquipmentSimpleSerializer(source='equipment', read_only=True)
class Meta:
model = Equipmentrecord
model = ECheckRecord
fields = '__all__'
@staticmethod
@ -43,6 +53,11 @@ class EquipmentrecordSerializer(ModelSerializer):
queryset = queryset.select_related('equipment')
return queryset
class EChcekRecordCreateSerializer(ModelSerializer):
class Meta:
model = ECheckRecord
fields = ['equipment', 'check_date', 'description']
class DaqCreateSerializer(serializers.Serializer):
number = serializers.CharField()
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 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 rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('equipment', EquipmentViewSet, basename='equipment')
router.register('equipmentrecord', EquipmentrecordViewSet, basename='equipmentrecord')
router.register('echeck_record', EChcekRecordViewSet, basename='echeck_record')
urlpatterns = [
path('daq/', DaqView.as_view()),
path('', include(router.urls)),

View File

@ -1,14 +1,20 @@
from datetime import timedelta
from django.shortcuts import render
from numpy import delete
from rest_framework.exceptions import APIException
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.response import Response
from apps.em.models import Equipment,Equipmentrecord
from apps.em.serializers import DaqCreateSerializer, EquipmentSerializer,EquipmentrecordSerializer
from apps.em.models import Equipment, ECheckRecord
from apps.em.serializers import DaqCreateSerializer, EChcekRecordCreateSerializer, ECheckRecordListSerializer, \
EquipmentCreateUpdateSerializer, EquipmentListSerializer
from apps.em.services import EmService
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
from django.utils import timezone
from django.db import transaction
# Create your views here.
class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet):
@ -18,48 +24,48 @@ class EquipmentViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet)
perms_map = {'get': '*', 'post': 'equipment_create',
'put': 'equipment_update', 'delete': 'equipment_delete'}
queryset = Equipment.objects.all()
serializer_class = EquipmentSerializer
serializer_class = EquipmentListSerializer
search_fields = ['number', 'name','description']
filterset_fields = ['belong_dept', 'keeper', 'type']
filterset_fields = ['keeper', 'type']
ordering_fields = ['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.
class EquipmentrecordViewSet(CreateUpdateModelAMixin, OptimizationMixin, ModelViewSet):
class EChcekRecordViewSet(CreateUpdateModelAMixin, OptimizationMixin,
CreateModelMixin, RetrieveModelMixin, ListModelMixin, DestroyModelMixin, GenericViewSet):
"""
设备校准-增删改查
"""
perms_map = {'get': '*', 'post': 'equipmentrecord_create',
'put': 'equipmentrecord_update', 'delete': 'equipmentrecord_delete'}
queryset = Equipmentrecord.objects.all()
serializer_class = EquipmentrecordSerializer
perms_map = {'get': '*', 'post': 'echeckrecord_create', 'delete': 'echeckrecord_delete'}
queryset = ECheckRecord.objects.all()
serializer_class = ECheckRecordListSerializer
filterset_fields = ['equipment']
ordering_fields = ['create_time']
ordering = ['-create_time']
ordering = ['-id']
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):
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.is_valid(raise_exception=True)
serializer.save()
return Response(status=status.HTTP_200_OK)
def update(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()
id = self.get_object()
serializer = self.get_serializer(id, data=data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(status=status.HTTP_200_OK)
instance = serializer.save(create_by=request.user)
EmService.update_check_date(equip=instance.equipment)
return Response()
@transaction.atomic
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
instance.delete()
EmService.update_check_date(equip=instance.equipment)
return Response()
import uuid
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):
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 rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from .models import Employee
from .models import ClockRecord, Employee
from apps.system.serializers import UserListSerializer, UserSimpleSerializer
from django.db.models.query import Prefetch
@ -23,3 +23,13 @@ class EmployeeSerializer(ModelSerializer):
class FaceLoginSerializer(serializers.Serializer):
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 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 rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('employee', EmployeeViewSet, basename='employee')
router.register('clock_record', ClockRecordViewSet, basename='clock_record')
urlpatterns = [
path('facelogin/', FaceLogin.as_view()),
path('', include(router.urls)),

View File

@ -1,10 +1,13 @@
from django.shortcuts import render
from django.utils import timezone
from rest_framework.response import Response
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.hrm.models import Employee
from apps.hrm.serializers import EmployeeSerializer, FaceLoginSerializer
from apps.hrm.models import ClockRecord, Employee
from apps.hrm.serializers import ClockRecordListSerializer, EmployeeSerializer, FaceClockCreateSerializer, FaceLoginSerializer
import face_recognition
from django.conf import settings
from django.core.cache import cache
@ -12,8 +15,10 @@ import logging
from rest_framework.generics import CreateAPIView
from rest_framework import status
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework import exceptions
from apps.system.models import User
from apps.system.serializers import UserSimpleSerializer
from rest_framework.permissions import AllowAny
logger = logging.getLogger('log')
@ -50,9 +55,56 @@ class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMix
except:
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 os
def tran64(s):
missing_padding = len(s) % 4
@ -70,41 +122,13 @@ class FaceLogin(CreateAPIView):
"""
人脸识别登录
"""
# serializer = FaceLoginSerializer(data=request.data)
# serializer.is_valid(raise_exception=True)
filename = str(uuid.uuid4())
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])
base64_data = base64.urlsafe_b64decode(tran64(request.data.get('base64').replace(' ', '+')))
user, msg = HRMService.face_compare_from_base64(base64_data)
if user:
refresh = RefreshToken.for_user(user)
return Response({
'refresh': str(refresh),
'access': str(refresh.access_token),
'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):
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:
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
class WareHouseSerializer(serializers.ModelSerializer):
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)
product = models.ForeignKey(Material, verbose_name='产品', on_delete=models.CASCADE)
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)
class Meta:

View File

@ -72,6 +72,11 @@ class SubProductionSerializer(serializers.ModelSerializer):
model = SubProduction
fields = '__all__'
class SubProductionCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = SubProduction
fields = ['name', 'product', 'process', 'need_combtest', 'sort']
class OtherMaterialSerializer(serializers.ModelSerializer):
class Meta:
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.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 rest_framework.decorators import action
from rest_framework.response import Response
@ -80,6 +80,10 @@ class SubProductionViewSet(CreateUpdateModelAMixin, ModelViewSet):
search_fields = ['name']
serializer_class = SubProductionSerializer
ordering = ['sort']
def get_serializer_class(self):
if self.action in ['create', 'update']:
return SubProductionCreateUpdateSerializer
return super().get_serializer_class()
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.views import APIView
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.serializers import MaterialBatchSerializer
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)
steps = Step.objects.filter(usedstep__is_deleted=False, usedstep__subproduction__in=subproductions)
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)

View File

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

View File

@ -20,3 +20,7 @@ class PlanGanttSerializer(serializers.ModelSerializer):
def get_children(self, obj):
subplans = SubProductionPlan.objects.filter(production_plan=obj).order_by('process__number')
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 rest_framework.routers import DefaultRouter
from apps.srm.views import GanttPlan
from apps.srm.views import GanttPlan, ProcessYieldView
router = DefaultRouter()
urlpatterns = [
path('gantt/plan/', GanttPlan.as_view()),
path('process/yield/', ProcessYieldView.as_view()),
path('', include(router.urls)),
]

View File

@ -1,9 +1,14 @@
from django.shortcuts import render
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 apps.mtm.models import Process, Step
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.
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')
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(
'self', null=True, blank=True, on_delete=models.SET_NULL, verbose_name='上级主管')
roles = models.ManyToManyField(Role, blank=True, verbose_name='角色')
is_atwork = models.BooleanField('当前在岗', default=False)
class Meta:
verbose_name = '用户信息'

View File

@ -141,7 +141,9 @@ class UserListSerializer(serializers.ModelSerializer):
class Meta:
model = User
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
def setup_eager_loading(queryset):

View File

@ -41,6 +41,12 @@ class WProductFilterSet(filters.FilterSet):
def filter_tag(self, queryset, name, value):
if value == 'no_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

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='当前工单',
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_mtestok = models.BooleanField('是否军检合格', 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)
ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
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_mtestok = models.BooleanField('是否军检合格', 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')
equip = models.ForeignKey(Equipment, verbose_name='生产设备',
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)

View File

@ -19,6 +19,7 @@ from apps.system.models import User
from apps.system.serializers import UserSimpleSerializer
from apps.wpm.models import Operation, OperationEquip, OperationMaterial, OperationWproduct, Pick, WMaterial, WProduct, OperationRecord, OperationRecordItem, WprouctTicket
from django.db import transaction
from apps.sam.models import Order
class PickHalfSerializer(serializers.Serializer):
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)
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
children = serializers.SerializerMethodField()
to_order_ = OrderSimpleSerializer(source='to_order', read_only=True)
class Meta:
model = WProduct
fields = '__all__'
@ -561,3 +563,7 @@ class WproductMtestSerializer(serializers.ModelSerializer):
class Meta:
model = WProduct
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: # 成品检验
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
else:
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)
count_ok = objs.filter(act_state__in=[WProduct.WPR_ACT_STATE_INM,
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,
WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_DOING]).count()
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.wpm.filters import CuttingFilterSet, OperationRecordFilterSet, WMaterialFilterSet, WProductFilterSet
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, \
OperationMaterialCreate1ListSerailizer, OperationMaterialCreate1Serailizer, OperationMaterialCreate2ListSerailizer, \
@ -27,7 +27,7 @@ from apps.wpm.serializers import CuttingListSerializer, OperationEquipListSerial
PickSerializer, OperationInitSerializer, OperationSubmitSerializer, ScrapSerializer, WMaterialListSerializer, \
WProductCardSerializer, WProductDetailSerializer, WProductListSerializer, \
WpmTestFormInitSerializer, WproductMtestSerializer, WproductPutInSerializer, \
WproductPutInsSerializer, WproductTicketListSerializer
WproductPutInsSerializer, WproductTicketListSerializer, WproductToOrderSerializer
from rest_framework.response import Response
from django.db import transaction
@ -148,7 +148,7 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
"""
perms_map = {'*': '*'}
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
filterset_class = WProductFilterSet
search_fields = ['number']
@ -441,6 +441,30 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
ret.append([str(index + 1), item['step_name'], item['actions']])
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):
"""
@ -559,6 +583,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
ope = OperationEquip()
ope.operation = op
ope.equip = i
ope.state = i.state
ope.save()
# 查询所需的工具工装
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 = renderer_context.get("response")
response_body.code = response.status_code
if response_body.code >= 400: # 响应异常
status_code = response.status_code # Http状态异常码
if status_code >= 400: # 如果http响应异常
if isinstance(data, dict) and 'code' in data: # 如果自定义了异常码
response_body = data
else:
response_body.data = data # data里是详细异常信息
prefix = ""
if isinstance(data, dict):
prefix = list(data.keys())[0]
data = data[prefix]
if isinstance(data, list):
elif isinstance(data, list):
data = data[0]
response_body.msg = prefix + ":" + str(data) # 取一部分放入msg,方便前端alert
else:
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)