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

This commit is contained in:
shilixia 2021-12-29 14:02:05 +08:00
commit 6e4914b6c8
56 changed files with 2171 additions and 1041 deletions

View File

@ -60,4 +60,7 @@ export default {
#customerForm .el-form-item{ #customerForm .el-form-item{
margin-bottom: 30px!important; margin-bottom: 30px!important;
} }
.overFlowShow .el-tabs__content{
overflow: visible;
}
</style> </style>

View File

@ -240,3 +240,10 @@ export function getCodes() {
method: 'get' method: 'get'
}) })
} }
//工单详情
export function getWorkflowInit(id) {
return request({
url: `/wf/workflow/${id}/init/`,
method: 'get'
})
}

View File

@ -19,11 +19,19 @@
@expand-change="handlerExpand" @expand-change="handlerExpand"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
> >
<el-table-column label="名称"> <el-table-column label="任务编号">
<template slot-scope="scope">{{ scope.row.name }}</template> <template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column> </el-table-column>
<el-table-column label="进度"> <el-table-column label="产品名称">
<template slot-scope="scope">{{ scope.row.name }}</template> <template slot-scope="scope">
<div style="overflow: hidden; text-overflow:ellipsis; white-space: nowrap;">{{ scope.row.productName }}</div>
</template>
</el-table-column>
<el-table-column label="产品型号">
<template slot-scope="scope">{{ scope.row.productNum }}</template>
</el-table-column>
<el-table-column label="生产数量">
<template slot-scope="scope">{{ scope.row.per }}</template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
@ -44,19 +52,12 @@ export default {
BGScrollTop: { BGScrollTop: {
handler: function(newValue) { handler: function(newValue) {
let table = this.$refs.tableMenu.bodyWrapper; let table = this.$refs.tableMenu.bodyWrapper;
// console.log(newValue, table);
table.scrollTo(0, newValue); table.scrollTo(0, newValue);
// table.scrollTo({
// top: newValue,
// left: 0,
// behavior: "smooth"
// });
} }
} }
}, },
data() { data() {
return { return {
// tableData: [],
checkList: [], checkList: [],
isShowHeaderBox: false, isShowHeaderBox: false,
menuOpen: false, menuOpen: false,
@ -68,7 +69,6 @@ export default {
handlerWatchScroll() { handlerWatchScroll() {
let table = this.$refs.tableMenu.bodyWrapper; let table = this.$refs.tableMenu.bodyWrapper;
table.addEventListener("scroll", e => { table.addEventListener("scroll", e => {
// console.log(e.srcElement.scrollTop);
this.$emit("TableScrollTop", e.srcElement.scrollTop); this.$emit("TableScrollTop", e.srcElement.scrollTop);
}); });
}, },

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<el-row style="position: fixed;z-index: 2000;top: 465px;right: 10px;"> <el-row style="position: absolute;z-index: 2000;top: -38px;right: 150px;display: flex">
<el-date-picker <el-date-picker
v-model="timeRange" v-model="timeRange"
type="monthrange" type="monthrange"
@ -11,8 +11,30 @@
value-format="yyyy-MM" value-format="yyyy-MM"
@change="timeRangeChange" @change="timeRangeChange"
> >
<!--test-->
</el-date-picker> </el-date-picker>
<div
class="toolTip"
style="width: 60px;height: 35px;padding-left: 10px; background: white;margin-left: 10px;border: 1px solid #DCDFE6;line-height: 35px;border-radius: 4px"
>
<el-dropdown trigger="click">
<span class="base">
{{currentDaySize.label}}
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu
slot="dropdown"
:style="{ left: this.left + 'px !important' }"
>
<el-dropdown-item
v-for="item in currentDaySizeOptions"
:key="item.value + 'ee'"
@click.native="handleSetDaySize(item)"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-row> </el-row>
<div class="left" style=" width: 400px ;float: left;border-right: 1px solid #f7f7f7"> <div class="left" style=" width: 400px ;float: left;border-right: 1px solid #f7f7f7">
<leftMenu <leftMenu
@ -33,24 +55,6 @@
></div> ></div>
</div> </div>
<div class="chart" ref="chart"> <div class="chart" ref="chart">
<!--<div class="left" :style="{ width: rightLineX + 'px' }">
<leftMenu
:list="list"
ref="leftMenu"
:BGScrollTop.sync="BGScrollTop"
@TableScrollTop="TableScrollTop"
@handlerRowClick="handlerRowClick"
@handlerGroup="handlerGroup"
@handlerExpand="handlerExpand"
></leftMenu>
<div class="rightLine" :style="{ left: rightLineX + 'px' }"></div>
<div
class="rightLine"
:style="{ left: rightLineX + 'px' }"
ref="rightLine"
@mousedown="rightLineMousedown"
></div>
</div>-->
<div class="date"> <div class="date">
<div class="years" v-for="item in allDays" :key="item.year"> <div class="years" v-for="item in allDays" :key="item.year">
<div <div
@ -197,14 +201,6 @@
width: item.widthMe + 'px', width: item.widthMe + 'px',
top: item.top + 'px' top: item.top + 'px'
}" }"
@mouseover="
lineMouseover(
`line${item.id}`,
$event,
item.id,
item.parentId,
index
)"
@mouseleave="lineMouseleave" @mouseleave="lineMouseleave"
@mouseenter=" @mouseenter="
lineMouseenter( lineMouseenter(
@ -241,27 +237,7 @@
</template> </template>
</div> </div>
</div> </div>
<div class="toolTip">
<!--<div class="today base" @click="handleGoToday">今天</div>-->
<el-dropdown trigger="click">
<span class="base">
{{currentDaySize.label}}
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu
slot="dropdown"
:style="{ left: this.left + 'px !important' }"
>
<el-dropdown-item
v-for="item in currentDaySizeOptions"
:key="item.value + 'ee'"
@click.native="handleSetDaySize(item)"
>
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<transition name="el-zoom-in-center"> <transition name="el-zoom-in-center">
<div <div
v-show="isShowMsg" v-show="isShowMsg"
@ -319,6 +295,8 @@
}, },
data() { data() {
return { return {
windowWidth:0,
windowHeight:0,
leftYear:2021, leftYear:2021,
timeRange: [], timeRange: [],
disable: true, disable: true,
@ -390,6 +368,13 @@
}; };
}, },
mounted(){ mounted(){
if(window.innerHeight){
this.windowWidth = window.innerWidth;
this.windowHeight = window.innerHeight;
}else{
this.windowHeight=document.body.clientHeight?document.body.clientHeight:document.documentElement.clientHeight;
this.windowWidth=document.body.clientWidth?document.body.clientWidth:document.documentElement.clientWidth;
}
let currentYear = new Date().getFullYear(); let currentYear = new Date().getFullYear();
this.timeRange = [currentYear+'-01',currentYear+'-12']; this.timeRange = [currentYear+'-01',currentYear+'-12'];
this.list = [...this.proList]; this.list = [...this.proList];
@ -534,10 +519,8 @@
}, },
//修改后续高度 //修改后续高度
resetTop(zindex, reduce, isexpand) { resetTop(zindex, reduce, isexpand) {
// console.log(zindex, reduce, isexpand);
let num = reduce ? -40 : 40; let num = reduce ? -40 : 40;
if (!reduce && !isexpand) { if (!reduce && !isexpand) {
// console.log(2);
this.list.forEach((item, index) => { this.list.forEach((item, index) => {
if (index > zindex) { if (index > zindex) {
item.top = item.top + 40; item.top = item.top + 40;
@ -549,7 +532,6 @@
} }
}); });
} else { } else {
// console.log(1);
this.list.forEach((item, index) => { this.list.forEach((item, index) => {
if (index > zindex) { if (index > zindex) {
item.top = item.top + num * this.list[zindex].children.length; item.top = item.top + num * this.list[zindex].children.length;
@ -600,7 +582,6 @@
let widthMe = width - left; let widthMe = width - left;
parent.widthMe = parent.widthChild = widthMe; parent.widthMe = parent.widthChild = widthMe;
parent.left = left; parent.left = left;
// return parent;
}, },
// 转为分组 // 转为分组
handlerGroup(row) { handlerGroup(row) {
@ -674,10 +655,8 @@
let index = this.list.findIndex(k => { let index = this.list.findIndex(k => {
return k.id == parentId; return k.id == parentId;
}); });
// console.log(parentId);
this.list.forEach(item => { this.list.forEach(item => {
if (item.id == parentId) { if (item.id == parentId) {
// console.log(item, this.list);
item.children.forEach(k => { item.children.forEach(k => {
if (k.id == id) { if (k.id == id) {
k.per = per; k.per = per;
@ -829,6 +808,17 @@
Math.round(end / this.currentDaySize.value) * Math.round(end / this.currentDaySize.value) *
this.currentDaySize.value - this.currentDaySize.value -
this.currentDaySize.value; this.currentDaySize.value;
let infoDomX=0,infoDomY = 0;
if(this.windowWidth-e.clientX>230){
infoDomX = e.clientX;
}else{
infoDomX = this.windowWidth-230;
}
if(this.windowHeight-e.clientY>200){
infoDomY = e.clientY;
}else{
infoDomY = this.windowHeight-200;
}
this.currentProjectMsg = { this.currentProjectMsg = {
name: this.computedList[index].name, name: this.computedList[index].name,
allTime: (end - start) / this.currentDaySize.value + 1, allTime: (end - start) / this.currentDaySize.value + 1,
@ -836,8 +826,8 @@
per1: this.computedList[index].per1, per1: this.computedList[index].per1,
startTime: this.computedWithTime(start), startTime: this.computedWithTime(start),
endTime: this.computedWithTime(end), endTime: this.computedWithTime(end),
left: e.clientX, left:infoDomX ,
top: e.clientY + 20 top: infoDomY
}; };
/* /*
* left: * left:
@ -864,7 +854,6 @@
left: 0, left: 0,
top: 0 top: 0
}; };
// this.handlerSelect();
return; return;
} }
this.currentLineDay = { this.currentLineDay = {
@ -1045,7 +1034,6 @@
}); });
} }
}, },
beforeDestroy() { beforeDestroy() {
window.removeEventListener("scroll", this.handleScroll); window.removeEventListener("scroll", this.handleScroll);
document.onmousemove = document.onmouseup = null; document.onmousemove = document.onmouseup = null;
@ -1055,7 +1043,7 @@
<style lang="scss" scoped> <style lang="scss" scoped>
.chart { .chart {
height: 40vh; height: 280px;
user-select: none; user-select: none;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
@ -1123,6 +1111,7 @@
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px; border-radius: 4px;
border: 1px solid #ebeef5; border: 1px solid #ebeef5;
pointer-events: none;
.lineMsg { .lineMsg {
margin-bottom: 10px; margin-bottom: 10px;
@ -1165,10 +1154,6 @@
} }
.toolTip { .toolTip {
position: fixed;
right: 0;
top: 550px;
z-index: 999;
.base { .base {
display: inline-block; display: inline-block;

View File

@ -687,9 +687,17 @@
that.field = []; //检查项目 that.field = []; //检查项目
let submit = isSubmit=='1'?false:true; let submit = isSubmit=='1'?false:true;
that.formData.forEach((item) => { that.formData.forEach((item) => {
let field_value = null;
if(item.field_type==='int'){
field_value = parseInt(that.checkForm[item.field_key])
}else if(item.field_type==='float'){
field_value = parseFloat(that.checkForm[item.field_key])
}else{
field_value = that.checkForm[item.field_key];
}
that.field.push({ that.field.push({
id: item.id, id: item.id,
field_value: that.checkForm[item.field_key], field_value: field_value,
is_testok: item.is_testok,//单项检查结果 is_testok: item.is_testok,//单项检查结果
is_hidden: item.is_hidden is_hidden: item.is_hidden
}); });

View File

@ -788,9 +788,15 @@
that.field = []; //检查项目 that.field = []; //检查项目
let submit = isSubmit=='1'?false:true; let submit = isSubmit=='1'?false:true;
that.formData.forEach((item) => { that.formData.forEach((item) => {
let field_value = 0;
if(item.field_type==='int'){
field_value = parseInt(that.checkForm[item.field_key])
}else if(item.field_type==='float'){
field_value = parseFloat(that.checkForm[item.field_key])
}
that.field.push({ that.field.push({
id: item.id, id: item.id,
field_value: that.checkForm[item.field_key], field_value: field_value,
is_testok: item.is_testok,//单项检查结果 is_testok: item.is_testok,//单项检查结果
is_hidden: item.is_hidden is_hidden: item.is_hidden
}); });

View File

@ -185,14 +185,6 @@ export const asyncRoutes = [
component: () => import('@/views/pm/management'), component: () => import('@/views/pm/management'),
meta: { title: '生产任务管理', icon: 'example', perms: ['pm_resources'] } meta: { title: '生产任务管理', icon: 'example', perms: ['pm_resources'] }
} }
,
{
path: 'gantt',
name: 'gantt',
component: () => import('@/views/pm/gantt'),
meta: { title: '甘特图', icon: 'example',perms: ['vendor_manage'] },
// hidden: true
}
] ]
} }
, ,

View File

@ -300,6 +300,8 @@
}, },
data() { data() {
return { return {
windowWidth:0,
windowHeight:0,
timeRange:[], timeRange:[],
disable: true, disable: true,
//当前项是否是子集 //当前项是否是子集
@ -499,11 +501,6 @@
TableScrollTop(val) { TableScrollTop(val) {
let lineBG = this.$refs.lineBG; let lineBG = this.$refs.lineBG;
lineBG.scrollTo(0, val); lineBG.scrollTo(0, val);
// lineBG.scrollTo({
// top: val,
// left: 0,
// behavior: "smooth"
// });
}, },
handlerBGScroll(e) { handlerBGScroll(e) {
this.BGScrollTop = this.$refs.chart.scrollTop; this.BGScrollTop = this.$refs.chart.scrollTop;
@ -862,15 +859,9 @@
) * ) *
this.currentDaySize.value + this.currentDaySize.value +
this.currentDaySize.value; this.currentDaySize.value;
let end = let end = parseInt(this.$refs[dom][0].style.left) + parseInt(this.$refs[dom][0].style.width);
parseInt(this.$refs[dom][0].style.left) + end = Math.round(end / this.currentDaySize.value) * this.currentDaySize.value;
parseInt(this.$refs[dom][0].style.width); this.currentLineDay = { start, end };
end =
Math.round(end / this.currentDaySize.value) * this.currentDaySize.value;
this.currentLineDay = {
start,
end
};
this.isHover = true; this.isHover = true;
this.handlerSelect(this.computedList[index]); this.handlerSelect(this.computedList[index]);
this.lineMouseenter(dom, e, id, parentId, index); this.lineMouseenter(dom, e, id, parentId, index);
@ -888,13 +879,21 @@
Math.round( Math.round(
parseInt(this.$refs[dom][0].style.left) / this.currentDaySize.value parseInt(this.$refs[dom][0].style.left) / this.currentDaySize.value
) * this.currentDaySize.value; ) * this.currentDaySize.value;
let end = let end = parseInt(this.$refs[dom][0].style.left) + parseInt(this.$refs[dom][0].style.width);
parseInt(this.$refs[dom][0].style.left) + end = Math.round(end / this.currentDaySize.value) * this.currentDaySize.value - this.currentDaySize.value;
parseInt(this.$refs[dom][0].style.width); debugger;
end = let infoDomX=0,infoDomY = 0;
Math.round(end / this.currentDaySize.value) * if(this.windowWidth-e.clientX>230){
this.currentDaySize.value - infoDomX = e.clientX;
this.currentDaySize.value; }else{
infoDomX = this.windowWidth-230;
}
if(this.windowHeight-e.clientY>200){
infoDomY = e.clientY;
}else{
infoDomY = this.windowHeight-200;
}
// if(){}else{}
this.currentProjectMsg = { this.currentProjectMsg = {
name: this.computedList[index].name, name: this.computedList[index].name,
allTime: (end - start) / this.currentDaySize.value + 1, allTime: (end - start) / this.currentDaySize.value + 1,
@ -902,8 +901,8 @@
per1: this.computedList[index].per1, per1: this.computedList[index].per1,
startTime: this.computedWithTime(start), startTime: this.computedWithTime(start),
endTime: this.computedWithTime(end), endTime: this.computedWithTime(end),
left: e.clientX , left:infoDomX ,
top: e.clientY + 20 top: infoDomY
}; };
/* /*
* left: * left:
@ -1095,6 +1094,10 @@
} }
}, },
watch: { watch: {
detailInfo(e,data) {
showDiv.style.left = (event.pageX - 300) + 'px';
showDiv.style.top = (event.pageY - 120) + 'px';
},
currentDaySize(newValue, oldValue) { currentDaySize(newValue, oldValue) {
this.list.forEach(item => { this.list.forEach(item => {
item.left = (item.left / oldValue.value) * newValue.value; item.left = (item.left / oldValue.value) * newValue.value;
@ -1111,6 +1114,13 @@
} }
}, },
mounted() { mounted() {
if(window.innerHeight){
this.windowWidth = window.innerWidth;
this.windowHeight = window.innerHeight;
}else{
this.windowHeight=document.body.clientHeight?document.body.clientHeight:document.documentElement.clientHeight;
this.windowWidth=document.body.clientWidth?document.body.clientWidth:document.documentElement.clientWidth;
}
document.addEventListener("scroll", this.handleScroll); document.addEventListener("scroll", this.handleScroll);
this.getDay(); this.getDay();
this.setStoneLine(); this.setStoneLine();

View File

@ -34,8 +34,7 @@
fit fit
stripe stripe
style="width: 100%" style="width: 100%"
height="100" height="300"
v-el-height-adaptive-table="{ bottomOffset: 40 }"
> >
<el-table-column type="index" width="50" /> <el-table-column type="index" width="50" />
@ -103,10 +102,14 @@
:limit.sync="listQuery.page_size" :limit.sync="listQuery.page_size"
@pagination="getplanList" @pagination="getplanList"
/> />
<gantt </el-card>
v-if="ganttShow" <el-card class="box-card">
:proList="proList" <div style="height: 40px;line-height: 40px;background: #F5F7FA;padding-left: 20px;">甘特图</div>
></gantt> <gantt
style="position: relative"
v-if="proList.length>0"
:proList="proList"
></gantt>
</el-card> </el-card>
</div> </div>
</template> </template>
@ -138,6 +141,7 @@ export default {
listLoading: true, listLoading: true,
proList: [], proList: [],
}; };
}, },
computed: {}, computed: {},
@ -183,9 +187,12 @@ export default {
obj.planTime = [startTime, endTime]; obj.planTime = [startTime, endTime];
obj.per = item.count; obj.per = item.count;
obj.type = 1; obj.type = 1;
obj.productName = item.product_.name;
obj.productNum = item.product_.specification;
obj.isShow = true; obj.isShow = true;
arr.push(obj); arr.push(obj);
} }
debugger;
that.proList = arr; that.proList = arr;
}); });
} }

View File

@ -87,7 +87,7 @@
@pagination="getplanList" @pagination="getplanList"
/> />
</el-card> </el-card>
<el-tabs type="border-card" v-model="activeName" @tab-click="activeNameClick"> <el-tabs class="overFlowShow" type="border-card" v-model="activeName" @tab-click="activeNameClick">
<el-tab-pane label="订单排产" name="订单排产"> <el-tab-pane label="订单排产" name="订单排产">
<el-table <el-table
:data="orderList.results" :data="orderList.results"
@ -293,6 +293,8 @@
obj.planTime = [startTime, endTime]; obj.planTime = [startTime, endTime];
obj.per = item.count; obj.per = item.count;
obj.type = 1; obj.type = 1;
obj.productName = item.product_.name;
obj.productNum = item.product_.specification;
obj.isShow = true; obj.isShow = true;
arr.push(obj); arr.push(obj);
} }

View File

@ -1,12 +1,11 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-card> <el-card>
<div style="margin-top: 2px">
<div style="margin-top: 2px"> <el-button type="primary" icon="el-icon-plus" @click="handleCreate"
<el-button type="primary" icon="el-icon-plus" @click="handleCreate" >新增
>新增</el-button </el-button>
> </div>
</div>
</el-card> </el-card>
<el-card style="margin-top: 2px"> <el-card style="margin-top: 2px">
<el-table <el-table
@ -14,23 +13,23 @@
:data="customfieldList" :data="customfieldList"
style="width: 100%" style="width: 100%"
> >
<el-table-column type="index" width="50" /> <el-table-column type="index" width="50"/>
<el-table-column label="字段标识"> <el-table-column label="字段标识">
<template slot-scope="scope">{{ scope.row.field_key }}</template> <template slot-scope="scope">{{ scope.row.field_key }}</template>
</el-table-column> </el-table-column>
<el-table-column label="字段名称"> <el-table-column label="字段名称">
<template slot-scope="scope">{{ scope.row.field_name }}</template> <template slot-scope="scope">{{ scope.row.field_name }}</template>
</el-table-column> </el-table-column>
<el-table-column label="字段类型"> <el-table-column label="字段类型">
<template slot-scope="scope">{{ scope.row.field_type }}</template> <template slot-scope="scope">{{ scope.row.field_type }}</template>
</el-table-column> </el-table-column>
<el-table-column label="顺序ID"> <el-table-column label="顺序ID">
<template slot-scope="scope">{{ scope.row.sort }}</template> <template slot-scope="scope">{{ scope.row.sort }}</template>
</el-table-column> </el-table-column>
<el-table-column label="字段描述"> <el-table-column label="字段描述">
<template slot-scope="scope">{{ scope.row.description }}</template> <template slot-scope="scope">{{ scope.row.description }}</template>
</el-table-column> </el-table-column>
<el-table-column label="创建时间"> <el-table-column label="创建时间">
<template slot-scope="scope">{{ scope.row.create_time }}</template> <template slot-scope="scope">{{ scope.row.create_time }}</template>
</el-table-column> </el-table-column>
@ -44,13 +43,15 @@
<el-link <el-link
v-if="checkPermission(['customfield_update'])" v-if="checkPermission(['customfield_update'])"
@click="handleEdit(scope)" @click="handleEdit(scope)"
>编辑</el-link >编辑
</el-link
> >
<el-link <el-link
v-if="checkPermission(['customfield_delete'])" v-if="checkPermission(['customfield_delete'])"
type="danger" type="danger"
@click="handleDeleteCustomfield(scope)" @click="handleDeleteCustomfield(scope)"
>删除</el-link >删除
</el-link
> >
</template> </template>
</el-table-column> </el-table-column>
@ -67,18 +68,18 @@
label-position="right" label-position="right"
:rules="rule1"> :rules="rule1">
<el-form-item label="字段标识" prop="field_key"> <el-form-item label="字段标识" prop="field_key">
<el-input v-model="customfield.field_key" placeholder="字段标识" /> <el-input v-model="customfield.field_key" placeholder="字段标识"/>
</el-form-item> </el-form-item>
<el-form-item label="字段名称" prop="field_name"> <el-form-item label="字段名称" prop="field_name">
<el-input v-model="customfield.field_name" placeholder="字段名称" /> <el-input v-model="customfield.field_name" placeholder="字段名称"/>
</el-form-item> </el-form-item>
<el-form-item label="字段描述" prop="description"> <el-form-item label="字段描述" prop="description">
<el-input v-model="customfield.description" placeholder="字段描述" /> <el-input v-model="customfield.description" placeholder="字段描述"/>
</el-form-item> </el-form-item>
<el-form-item label="占位符" prop="placeholder"> <el-form-item label="占位符" prop="placeholder">
<el-input v-model="customfield.placeholder" placeholder="占位符" /> <el-input v-model="customfield.placeholder" placeholder="占位符"/>
</el-form-item> </el-form-item>
<el-form-item label="字段类型" prop="field_type"> <el-form-item label="展示标签" prop="field_type">
<el-select style="width: 100%" v-model="customfield.field_type" placeholder="请选择"> <el-select style="width: 100%" v-model="customfield.field_type" placeholder="请选择">
<el-option <el-option
v-for="item in options" v-for="item in options"
@ -88,28 +89,53 @@
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="字段标签" v-show="customfield.field_type=='select'||customfield.field_type=='selects'">
<el-input v-model="customfield.label" placeholder="选项类型"/>
</el-form-item>
<el-form-item label="选项" v-show="customfield.field_type=='select'||customfield.field_type=='selects'"> <el-form-item label="选项" v-show="customfield.field_type=='select'||customfield.field_type=='selects'">
<el-button @click.prevent="addDomain" style="border: none;"> <el-button @click.prevent="addDomain" style="border: none;">
<i class="el-icon-circle-plus-outline"></i> <i class="el-icon-circle-plus-outline"></i>
<span style="font-size:14px;">添加</span> <span style="font-size:14px;">添加</span>
</el-button> </el-button>
<el-row v-for="(domain, $index) in choiceOption" :key=domain+$index style="margin-bottom: 10px"> <el-row v-for="(domain, $index) in choiceOption" :key='$index+1' style="margin-bottom: 10px">
<el-col :span="20"> <el-col :span="10">
<el-input v-model="choiceOption[$index]" auto-complete="off"></el-input> <template>
<el-form-item label="id">
<el-input type="number" v-model="choiceOption[$index].id" auto-complete="off" placeholder="id为整数"></el-input>
</el-form-item>
</template>
</el-col>
<el-col :span="10">
<template>
<el-form-item label="name">
<el-input v-model="choiceOption[$index].name" auto-complete="off"></el-input>
</el-form-item>
</template>
</el-col> </el-col>
<el-col :span="3" style="text-align: center" v-if="$index!==0"> <el-col :span="3" style="text-align: center" v-if="$index!==0">
<i class="el-icon-remove-outline" @click.prevent="removeDomain($index,'1')" style="color: red;font-size: 16px;"></i> <i
class="el-icon-remove-outline"
style="color: red;font-size: 16px;"
@click.prevent="removeDomain($index,'1')"
></i>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="顺序ID" prop="sort"> <el-form-item label="顺序ID">
<el-input v-model="customfield.sort" type="number" placeholder="顺序" /> <el-input v-model="customfield.sort" type="number" placeholder="顺序"/>
</el-form-item> </el-form-item>
<el-form-item label="默认值" prop="default_value"> <el-form-item label="默认值">
<el-input v-model="customfield.default_value" placeholder="默认值" /> <el-input v-model="customfield.default_value" placeholder="默认值"/>
</el-form-item> </el-form-item>
<el-form-item label="模板" prop="field_template"> <el-form-item label="模板">
<el-input v-model="customfield.field_template" placeholder="你有一个待办工单:{title}" /> <el-input v-model="customfield.field_template" placeholder="你有一个待办工单:{title}"/>
</el-form-item>
<el-form-item label="是否隐藏">
<el-radio-group v-model="customfield.is_hidden">
<el-radio :label="false">显示</el-radio>
<el-radio :label="true">隐藏</el-radio>
</el-radio-group>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div style="text-align: right"> <div style="text-align: right">
@ -120,39 +146,53 @@
</div> </div>
</template> </template>
<script> <script>
import { getWfCustomfieldList, createWfCustomfield,updateWfCustomfield,deleteWfCustomfield } from "@/api/workflow"; import {getWfCustomfieldList, createWfCustomfield, updateWfCustomfield, deleteWfCustomfield} from "@/api/workflow";
import checkPermission from "@/utils/permission"; import checkPermission from "@/utils/permission";
import vueJsonEditor from 'vue-json-editor' import vueJsonEditor from 'vue-json-editor'
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
const defaultcustomfield = { const defaultcustomfield = {
field_key: "", field_key: "",
field_name: "", field_name: "",
}; };
export default { export default {
components: { Pagination,vueJsonEditor }, components: {Pagination, vueJsonEditor},
name: "CTF", name: "CTF",
props: ["ID"], props: ["ID"],
data() { data() {
return { return {
customfield: { customfield: {
field_key:'', field_key: '',
field_name:'', field_name: '',
placeholder:'', placeholder: '',
field_type:'', field_type: '',
sort:'', sort: '',
default_value:'', label: 'name',
field_template:'', default_value: '',
field_choice:[] field_template: '',
}, field_choice: [],
view_permission_check:false, is_hidden:false,
hasJsonFlag:true, // json是否验证通过 },
hasJsonFlag1:true, // json是否验证通过 fieldHidden: true,
hasJsonFlag2:true, // json是否验证通过 fieldBlock: false,
// customfieldList: { view_permission_check: false,
// count:0 hasJsonFlag: true, // json是否验证通过
// }, hasJsonFlag1: true, // json是否验证通过
customfieldList:[], hasJsonFlag2: true, // json是否验证通过
options: [{ // customfieldList: {
// count:0
// },
customfieldList: [''],
labels:[{
label: '常规',
value: 'name'
},{
label: '名称|id',
value: 'name|id'
},{
label: '人员选择',
value: 'user'
}],
options: [{
value: 'string', value: 'string',
label: '文本' label: '文本'
}, { }, {
@ -167,172 +207,176 @@ export default {
}, { }, {
value: 'datetime', value: 'datetime',
label: '日期时间' label: '日期时间'
},{ }, {
value: 'select', value: 'select',
label: '单选' label: '单选'
},{ }, {
value: 'selects', value: 'selects',
label: '多选' label: '多选'
}, { }, {
value: 'textarea', value: 'textarea',
label: '文本域' label: '文本域'
}, { },{
value: 'file',
label: '附件'
}],
/*, {
value: 'selectuser', value: 'selectuser',
label: '用户名' label: '用户名'
}, { }, {
value: 'selectusers', value: 'selectusers',
label: '多选的用户名' label: '多选的用户名'
}, { }, */
value: 'file',
label: '附件'
}],
boolean_field_display:[], boolean_field_display: [],
choiceOption:[''], choiceOption: [{id:null,name:""}],
field_choice:[], field_choice: [],
dialogVisible: false, dialogVisible: false,
dialogType: "new", dialogType: "new",
rule1: { rule1: {
name: [{ required: true, message: "请输入", trigger: "blur" }], name: [{required: true, message: "请输入", trigger: "blur"}],
description: [{ required: true, message: "请输入", trigger: "blur" }] description: [{required: true, message: "请输入", trigger: "blur"}]
},
};
},
computed: {},
watch: {},
created() {
this.getList();
},
methods: {
//添加字段选项
addDomain() {
this.choiceOption.push({id:null,name:""})
}, },
}; //删除字段选项
}, removeDomain(index) {
computed: {}, this.choiceOption.splice(index, 1)
watch: {}, },
created() { checkPermission,
this.getList();
},
methods: {
//添加字段选项
addDomain() {
this.choiceOption.push('')
},
//删除字段选项
removeDomain(index){
this.choiceOption.splice(index, 1)
},
checkPermission,
getList() { getList() {
getWfCustomfieldList(this.ID).then((response) => {
if (response.data) {
this.customfieldList = response.data;
}
getWfCustomfieldList(this.ID).then((response) => {
if (response.data) {
this.customfieldList = response.data;
}
});
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
},
resetFilter() {
this.listQuery = {
page: 1,
page_size: 20,
}
this.getList();
},
handleCreate() {
this.customfield = Object.assign({}, defaultcustomfield);
this.dialogType = "new";
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handleEdit(scope) {
this.customfield = Object.assign({}, scope.row); // copy obj
this.choiceOption = scope.row.field_choice;
this.dialogType = "edit";
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handlecfgt(scope)
{
this.$router.push({name:"configuration",params:{customfield:scope.row.id}})
}
,
handleDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await deleteWorkflow(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
}); });
}, },
handleFilter() {
this.listQuery.page = 1;
this.getList();
},
resetFilter() {
this.listQuery = {
page: 1,
page_size: 20,
}
this.getList();
},
handleCreate() {
this.customfield = Object.assign({}, defaultcustomfield);
this.dialogType = "new";
this.dialogVisible = true;
/*this.$nextTick(() => {
// this.$refs["Form"].clearValidate();
this.customfield.label = 1;
});*/
},
handleEdit(scope) {
debugger;
this.customfield = Object.assign({}, scope.row); // copy obj
this.choiceOption = scope.row.field_choice;
this.dialogType = "edit";
this.dialogVisible = true;
/*this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});*/
},
handlecfgt(scope) {
this.$router.push({name: "configuration", params: {customfield: scope.row.id}})
}
,
handleDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await this.deleteWorkflow(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
async confirm(form) { async confirm(form) {
this.$refs[form].validate((valid) => { let that = this,choiceArr = [];
if (valid) { this.$refs[form].validate((valid) => {
const isEdit = this.dialogType === "edit"; if (valid) {
if (isEdit) { const isEdit = that.dialogType === "edit";
this.checkJson(); if(that.choiceOption[0]&&that.choiceOption[0].name!==''&&that.choiceOption[0].name!==null&&that.choiceOption[0].name!==undefined){
this.checkJson1(); that.choiceOption.forEach((item)=>{
this.checkJson2(); let obj = new Object();
this.customfield.field_choice = this.choiceOption; obj.id = parseInt(item.id);
updateWfCustomfield(this.customfield.id, this.customfield).then((res) => { obj.name = item.name;
if (res.code >= 200) { choiceArr.push(obj);
this.getList(); })
this.dialogVisible = false; }
this.$message.success("成功"); debugger;
} console.log(choiceArr);
}); that.customfield.field_choice = choiceArr;
console.log(that.customfield);
if (isEdit) {
updateWfCustomfield(this.customfield.id, this.customfield).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
} else {
this.customfield.workflow = this.ID;
createWfCustomfield(this.customfield).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
}
} else { } else {
this.checkJson(); return false;
this.checkJson1();
this.checkJson2();
this.customfield.workflow=this.ID;
this.customfield.field_choice = this.choiceOption;
createWfCustomfield(this.customfield).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
} }
} else {
return false;
}
});
},
handleDeleteCustomfield(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await deleteWfCustomfield(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
}); });
}, },
handleDeleteCustomfield(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await deleteWfCustomfield(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
onJsonChange (value) { /* onJsonChange(value) {
// console.log('更改value:', value); // console.log('更改value:', value);
// 实时保存 // 实时保存
this.onJsonSave(value) this.onJsonSave(value)
}, },
onJsonSave (value) { onJsonSave(value) {
// console.log('保存value:', value); // console.log('保存value:', value);
this.boolean_field_display = value this.boolean_field_display = value
this.hasJsonFlag = true this.hasJsonFlag = true
@ -343,61 +387,61 @@ export default {
}, },
// 检查json // 检查json
checkJson(){ checkJson() {
if (this.hasJsonFlag == false){ if (this.hasJsonFlag == false) {
alert("布尔显示定义json验证失败") // alert("布尔显示定义json验证失败")
return false return false
} else { } else {
alert("布尔显示定义json验证成功") // alert("布尔显示定义json验证成功")
return true return true
} }
}, },
onJsonChange1 (value) { onJsonChange1(value) {
// console.log('更改value:', value); // console.log('更改value:', value);
// 实时保存 // 实时保存
this.onJsonSave1(value) this.onJsonSave1(value)
}, },
onJsonSave1 (value) { onJsonSave1(value) {
this.field_choice = value this.field_choice = value
this.hasJsonFlag1 = true this.hasJsonFlag1 = true
}, },
onError1(value) { onError1(value) {
this.hasJsonFlag1 = false this.hasJsonFlag1 = false
}, },
// 检查json // 检查json
checkJson1(){ checkJson1() {
if (this.hasJsonFlag1 == false){ if (this.hasJsonFlag1 == false) {
alert("选项json验证失败") // alert("选项json验证失败")
return false return false
} else { } else {
alert("选项json1验证成功") // alert("选项json1验证成功")
return true return true
} }
}, },
onJsonChange2 (value) { onJsonChange2(value) {
// console.log('更改value:', value); // console.log('更改value:', value);
// 实时保存 // 实时保存
this.onJsonSave2(value) this.onJsonSave2(value)
}, },
onJsonSave2 (value) { onJsonSave2(value) {
this.field_choice = value this.field_choice = value
this.hasJsonFlag2 = true this.hasJsonFlag2 = true
}, },
onError2(value) { onError2(value) {
this.hasJsonFlag2 = false this.hasJsonFlag2 = false
}, },
// 检查json // 检查json
checkJson2(){ checkJson2() {
if (this.hasJsonFlag2 == false){ if (this.hasJsonFlag2 == false) {
alert("标签json验证失败") // alert("标签json验证失败")
return false return false
} else { } else {
alert("标签json1验证成功") // alert("标签json1验证成功")
return true return true
} }*/
}, // },
}, },
}; };
</script> </script>

View File

@ -428,20 +428,20 @@ export default {
// 检查json // 检查json
checkJson(){ checkJson(){
if (this.hasJsonFlag == false){ if (this.hasJsonFlag == false){
alert("限制表达式json验证失败") // alert("限制表达式json验证失败")
return false return false
} else { } else {
alert("限制表达式json验证成功") // alert("限制表达式json验证成功")
return true return true
} }
}, },
// 检查json // 检查json
checkJson2(){ checkJson2(){
if (this.hasJsonFlag1 == false){ if (this.hasJsonFlag1 == false){
alert("展现表单字段json验证失败") // alert("展现表单字段json验证失败")
return false return false
} else { } else {
alert("展现表单字段json1验证成功") // alert("展现表单字段json1验证成功")
return true return true
} }
}, },

View File

@ -184,14 +184,20 @@
<el-col :span="1" style="height: 1px;"></el-col> <el-col :span="1" style="height: 1px;"></el-col>
<el-col :span="8"> <el-col :span="8">
<el-select style="width: 100%" v-model="item.value" placeholder="请选择状态"> <el-select style="width: 100%" v-model="item.value" placeholder="请选择状态">
<el-option label="只读" value="1"></el-option> <el-option
<el-option label="必填" value="2"></el-option> v-for="item in state_fields"
<el-option label="可选" value="3"></el-option> :label="item.label"
:value="item.value"
:key="item.value"
></el-option>
</el-select> </el-select>
</el-col> </el-col>
<el-col :span="2" style="text-align: center" v-if="$index!==0"> <el-col :span="2" style="text-align: center" v-if="$index!==0">
<i class="el-icon-remove-outline" @click.prevent="removeStatusChange($index)" <i
style="color: red;font-size: 16px;"></i> class="el-icon-remove-outline"
style="color: red;font-size: 16px;"
@click.prevent="removeStatusChange($index)"
></i>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
@ -291,6 +297,21 @@
value: 9, value: 9,
label: '代码获取' label: '代码获取'
}], }],
state_fields:[
{
value: 1,
label: '只读'
},{
value: 2,
label: '必填'
},{
value: 3,
label: '可选'
},{
value: 4,
label: '隐藏'
},
],
display_form_str: [], display_form_str: [],
limit_expression: [], limit_expression: [],
dialogVisible: false, dialogVisible: false,
@ -315,8 +336,8 @@
methods: { methods: {
checkPermission(value){ checkPermission(value){
debugger; /* debugger;
console.log(checkPermission(value)) console.log(checkPermission(value))*/
checkPermission(value); checkPermission(value);
}, },

View File

@ -308,9 +308,9 @@
<el-select style="width: 100%" v-model="item.default_value" placeholder="请选择"> <el-select style="width: 100%" v-model="item.default_value" placeholder="请选择">
<el-option <el-option
v-for="item1 in item.field_choice" v-for="item1 in item.field_choice"
:key="item1" :key="item1.id"
:label="item1" :label="item1.name"
:value="item1" :value="item1.id"
> >
</el-option> </el-option>
</el-select> </el-select>
@ -319,9 +319,9 @@
<el-select style="width: 100%" multiple v-model="item.default_value" placeholder="请选择"> <el-select style="width: 100%" multiple v-model="item.default_value" placeholder="请选择">
<el-option <el-option
v-for="item1 in item.field_choice" v-for="item1 in item.field_choice"
:key="item1" :key="item1.id"
:label="item1" :label="item1.name"
:value="item1" :value="item1.id"
> >
</el-option> </el-option>
</el-select> </el-select>

View File

@ -35,7 +35,7 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-row style="display: flex;flex-wrap: wrap;width:100%;"> <el-row style="display: flex;flex-wrap: wrap;width:100%;">
<el-col v-for="item in ticketDetail.ticket_data_" :key="item.id" :span="12"> <el-col v-for="item in ticketDetail.ticket_data_" :key="item.id" :span="12" v-show="!item.is_hidden">
<el-form-item :label="item.field_name" v-if="item.field_state==='1'"> <el-form-item :label="item.field_name" v-if="item.field_state==='1'">
<span>{{ticketDetail.ticket_data[item.field_key]}}</span> <span>{{ticketDetail.ticket_data[item.field_key]}}</span>
</el-form-item> </el-form-item>
@ -73,9 +73,9 @@
<el-select style="width: 100%" v-model="ticketDetail.ticket_data[item.field_key]" placeholder="请选择"> <el-select style="width: 100%" v-model="ticketDetail.ticket_data[item.field_key]" placeholder="请选择">
<el-option <el-option
v-for="item1 in item.field_choice" v-for="item1 in item.field_choice"
:key="item1" :key="item1.id"
:label="item1" :label="item1.name"
:value="item1" :value="item1.id"
> >
</el-option> </el-option>
</el-select> </el-select>
@ -84,9 +84,9 @@
<el-select style="width: 100%" multiple v-model="ticketDetail.ticket_data[item.field_key]" placeholder="请选择"> <el-select style="width: 100%" multiple v-model="ticketDetail.ticket_data[item.field_key]" placeholder="请选择">
<el-option <el-option
v-for="item1 in item.field_choice" v-for="item1 in item.field_choice"
:key="item1" :key="item1.id"
:label="item1" :label="item1.name"
:value="item1" :value="item1.id"
> >
</el-option> </el-option>
</el-select> </el-select>
@ -132,7 +132,6 @@
<el-button v-if="ticketDetail.in_add_node" class="filter-item" type="primary" @click="handleClick('3')">加签处理</el-button> <el-button v-if="ticketDetail.in_add_node" class="filter-item" type="primary" @click="handleClick('3')">加签处理</el-button>
<el-button v-for="item in operationBtn" :key="item.id" class="filter-item" type="primary" @click="operationSubmit(item.id)">{{item.name}}</el-button> <el-button v-for="item in operationBtn" :key="item.id" class="filter-item" type="primary" @click="operationSubmit(item.id)">{{item.name}}</el-button>
</div> </div>
</div> </div>
</el-card> </el-card>
<el-card style="margin-left: 10px"> <el-card style="margin-left: 10px">
@ -205,6 +204,7 @@
</template> </template>
<script src="https://d3js.org/d3.v4.min.js"></script> <script src="https://d3js.org/d3.v4.min.js"></script>
<script> <script>
import { getOrgAll } from "@/api/org";
import { upUrl, upHeaders } from "@/api/file"; import { upUrl, upHeaders } from "@/api/file";
import { getUserList } from "@/api/user"; import { getUserList } from "@/api/user";
import {getWorkflowList,getWfCustomfieldList,getWfStateList,getWfTransitionList,ticketHandle import {getWorkflowList,getWfCustomfieldList,getWfStateList,getWfTransitionList,ticketHandle
@ -247,6 +247,7 @@
watchedName:'', watchedName:'',
watchedCreateTime:'', watchedCreateTime:'',
logs:[], logs:[],
orgList:[],
staffs:[], staffs:[],
edges: [], edges: [],
nodes: [], nodes: [],
@ -288,156 +289,189 @@
getTicketTransitions(ticketId).then(res=>{ getTicketTransitions(ticketId).then(res=>{
this.operationBtn = res.data; this.operationBtn = res.data;
}) })
getWfFlowSteps( ticketId).then((res)=>{ getOrgAll().then((orgRes) => {
if(res.data){ that.orgList = orgRes.data;
//流程步骤数组 getUserList({page:0}).then(userRes=>{
that.flowSteps = res.data; if(userRes.data){
getTicketDetail( ticketId).then((res)=>{ that.staffs = userRes.data;
if(res.data){ getWfFlowSteps( ticketId).then((res)=>{
that.tooltip = that.createTooltip(); if(res.data){
that.ticketDetail = res.data; //流程步骤数组
that.flowSteps = res.data;
console.log(that.ticketDetail.state_.enable_retreat); getTicketDetail( ticketId).then((res)=>{
debugger; if(res.data){
let state = res.data.state; that.tooltip = that.createTooltip();
let dat = that.flowSteps.filter((item)=>{ that.ticketDetail = res.data;
return item.id==state; for (let i=0;i<that.ticketDetail.ticket_data_.length;i++) {
}) if(that.ticketDetail.ticket_data_[i].label==="sys_user"){
that.sort = dat[0].sort; that.ticketDetail.ticket_data_[i].field_choice = that.staffs;
that.actives = that.flowSteps.indexOf(dat[0]); }
if( that.flowSteps.length-that.actives >1){}else{ if(that.ticketDetail.ticket_data_[i].label==="deptSelect"){
that.actives =that.flowSteps.length; that.ticketDetail.ticket_data_[i].field_choice = that.orgList;
} }
var g = new dagreD3.graphlib.Graph().setGraph({
rankdir: 'DL',
nodesep: 100,
edgesep: 10,//两条线之间的距离
ranksep: 50,//节点之间的距离
marginx: 60,
marginy: 20,
});
//获取state得到节点
getWfStateList(workflow).then((response) => {
if (response.data) {
let nodes = response.data;
// 添加节点
nodes.forEach((item) => {
if(item.sort==that.sort){
g.setNode(item.id, {
// 节点标签
label: item.name,
// 节点形状
shape: 'rect',
toolText: item.name,
//节点样式
style: "fill:#409EFF;stroke:#000",
labelStyle: "fill:#000;",
rx :5,//矩形节点圆角度
ry :5
});
}else{
g.setNode(item.id, {
// 节点标签
label: item.name,
// 节点形状
shape: 'rect',
toolText: item.name,
//节点样式
style: "fill:#fff;stroke:#000",
labelStyle: "fill:#000;",
rx :5,//矩形节点圆角度
ry :5
});
} }
}); console.log(that.ticketDetail.state_.enable_retreat);
g.nodes().forEach(function (v) { let state = res.data.state;
console.log("Node " + v + ": " + JSON.stringify(g.node(v))); let dat = that.flowSteps.filter((item)=>{
}); return item.id==state;
//获取流转得到线 链接关系 })
getWfTransitionList(workflow).then((res)=>{ debugger;
if(res.data){ let state_fields = dat[0].state_fields;
let transitionList = res.data; if(state_fields!=={}){
transitionList.forEach((transitions)=>{ for (let labe in state_fields) {
let transition0 = transitions; for(let j = 0;j<that.ticketDetail.ticket_data_.length;j++){
if (transition0.condition_expression.length>0){ if(that.ticketDetail.ticket_data_[j].field_key===labe){
g.setNode(transition0.source_state_.id+100000, {label: "条件表达式", style: "fill: #a4d088", shape: "diamond"}); debugger;
g.setEdge(transition0.source_state_.id, transition0.source_state_.id+100000, { console.log(labe)
// 边标签 console.log(state_fields[labe])
label: transition0.name, if(state_fields[labe]!==4){
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px" that.ticketDetail.ticket_data_[j].is_hidden = false
}); }else{
let condition_expression = transition0.condition_expression; that.ticketDetail.ticket_data_[j].is_hidden = true
condition_expression.forEach(condition_expression0=>{ }
g.setEdge(transition0.source_state_.id+100000, condition_expression0.target_state, { }
label: condition_expression0.label,
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px"
})
})
}else{
g.setEdge(transition0.source_state_.id, transition0.destination_state_.id, {
// 边标签
label: transition0.name,
// 边样式
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px" // 根据后台数据来改变连线的颜色
});
} }
}) }
g.nodes().length-1 }
g.nodes().forEach(function (v) { that.sort = dat[0].sort;
console.log("Node " + v + ": " + JSON.stringify(g.node(v))); that.actives = that.flowSteps.indexOf(dat[0]);
}); if( that.flowSteps.length-that.actives >1){}else{
// 创建渲染器 that.actives =that.flowSteps.length;
let render = new dagreD3.render(); }
// 选择 svg 并添加一个g元素作为绘图容器. var g = new dagreD3.graphlib.Graph().setGraph({
let svg = d3.select('#mySvg'); rankdir: 'DL',
let svgGroup = svg.append('g'); nodesep: 100,
let zoom = d3.zoom() edgesep: 10,//两条线之间的距离
.on("zoom", function() { ranksep: 50,//节点之间的距离
svgGroup.attr("transform", d3.event.transform); marginx: 60,
}); marginy: 20,
svg.call(zoom); });
// 在绘图容器上运行渲染器生成流程图. //获取state得到节点
render(d3.select("svg g"), g); getWfStateList(workflow).then((response) => {
// this.getTicketFlowlogs(ticketId); if (response.data) {
getTicketFlowlog(ticketId).then(res=>{ let nodes = response.data;
if(res.data){
that.logs = res.data;
svgGroup
.selectAll('g.node')
.on('mouseover', (v) => {
// 假如当前toolText为"",则不展示
//这里就是自定义tooltip的内容
let filList = [], strList = [];
filList = nodes.filter((ii) => {
return ii.name === g.node(v).label
})
if (!filList.length) {
return
}
filList.map((k) => {
let filte = that.logs.filter(item=>{
return item.state == k.id;
})
//每个
let str = '处理人:'+filte[0].participant_.name;
strList.push(str)
})
that.tipVisible(strList)
})
.on('mouseout', () => {
this.tipHidden()
})
}
})
}else{} // 添加节点
}); nodes.forEach((item) => {
} if(item.sort==that.sort){
}); g.setNode(item.id, {
} // 节点标签
}); label: item.name,
} // 节点形状
shape: 'rect',
toolText: item.name,
//节点样式
style: "fill:#409EFF;stroke:#000",
labelStyle: "fill:#000;",
rx :5,//矩形节点圆角度
ry :5
});
}else{
g.setNode(item.id, {
// 节点标签
label: item.name,
// 节点形状
shape: 'rect',
toolText: item.name,
//节点样式
style: "fill:#fff;stroke:#000",
labelStyle: "fill:#000;",
rx :5,//矩形节点圆角度
ry :5
});
}
});
g.nodes().forEach(function (v) {
console.log("Node " + v + ": " + JSON.stringify(g.node(v)));
});
//获取流转得到线 链接关系
getWfTransitionList(workflow).then((res)=>{
if(res.data){
let transitionList = res.data;
transitionList.forEach((transitions)=>{
let transition0 = transitions;
if (transition0.condition_expression.length>0){
g.setNode(transition0.source_state_.id+100000, {label: "条件表达式", style: "fill: #a4d088", shape: "diamond"});
g.setEdge(transition0.source_state_.id, transition0.source_state_.id+100000, {
// 边标签
label: transition0.name,
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px"
});
let condition_expression = transition0.condition_expression;
condition_expression.forEach(condition_expression0=>{
g.setEdge(transition0.source_state_.id+100000, condition_expression0.target_state, {
label: condition_expression0.label,
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px"
})
})
}else{
g.setEdge(transition0.source_state_.id, transition0.destination_state_.id, {
// 边标签
label: transition0.name,
// 边样式
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px" // 根据后台数据来改变连线的颜色
});
}
})
g.nodes().length-1
g.nodes().forEach(function (v) {
console.log("Node " + v + ": " + JSON.stringify(g.node(v)));
});
// 创建渲染器
let render = new dagreD3.render();
// 选择 svg 并添加一个g元素作为绘图容器.
let svg = d3.select('#mySvg');
let svgGroup = svg.append('g');
let zoom = d3.zoom()
.on("zoom", function() {
svgGroup.attr("transform", d3.event.transform);
});
svg.call(zoom);
// 在绘图容器上运行渲染器生成流程图.
render(d3.select("svg g"), g);
// this.getTicketFlowlogs(ticketId);
getTicketFlowlog(ticketId).then(res=>{
if(res.data){
that.logs = res.data;
svgGroup
.selectAll('g.node')
.on('mouseover', (v) => {
// 假如当前toolText为"",则不展示
//这里就是自定义tooltip的内容
let filList = [], strList = [];
filList = nodes.filter((ii) => {
return ii.name === g.node(v).label
})
if (!filList.length) {
return
}
filList.map((k) => {
let filte = that.logs.filter(item=>{
return item.state == k.id;
})
//每个
let str = '处理人:'+filte[0].participant_.name;
strList.push(str)
})
that.tipVisible(strList)
})
.on('mouseout', () => {
this.tipHidden()
})
}
})
}else{}
});
}
});
}
});
}
});
}
})
}); });
}, },
activated(){ activated(){
@ -498,7 +532,6 @@
.style('left', `${d3.event.pageX + 15}px`) .style('left', `${d3.event.pageX + 15}px`)
.style('top', `${d3.event.pageY-10}px`) .style('top', `${d3.event.pageY-10}px`)
}, },
// tooltip隐藏 // tooltip隐藏
tipHidden() { tipHidden() {
this.tooltip this.tooltip
@ -588,9 +621,6 @@
} }
}) })
}, },
addNode(){
},
} }
} }
</script> </script>

View File

@ -247,6 +247,7 @@
>报废 >报废
</el-link>!--> </el-link>!-->
<el-link type="primary" <el-link type="primary"
v-if="!scope.row.ticket"
@click="handleRetrial(scope)" @click="handleRetrial(scope)"
>重审 >重审
</el-link> </el-link>
@ -508,98 +509,78 @@
</el-dialog> </el-dialog>
<!--不合格半成品重审--> <!--不合格半成品重审-->
<el-dialog title="不合格半成品重审" :close-on-click-modal="false" :visible.sync="limitedRetrial"> <el-dialog title="不合格半成品重审" :close-on-click-modal="false" :visible.sync="limitedRetrial">
<el-form label-width="80px" label-position="right"> <el-form label-width="130px" label-position="right">
<el-row v-for="(item, $index) in fieldList.record_data" :key="$index"> <el-form-item :label="item.field_name" v-for="item in customfieldList" :key="item.id" v-show="!item.is_hidden">
<el-form-item <template v-if="item.field_type=='string'">
v-if="item.field_type === 'string'" <el-input v-model="item.default_value" :placeholder="item.description" />
:label="item.field_name" </template>
> <template v-if="item.field_type==='int'">
<el-input placeholder="请输入" v-model="item.sort" /> <el-input v-model="item.default_value" type="number" :placeholder="item.description" oninput="value=value.replace(/[^\d]/g,'')" />
</el-form-item> </template>
<el-form-item <template v-if="item.field_type==='float'">
v-else-if="item.field_type === 'int'" <el-input v-model="item.default_value" type="number" :placeholder="item.description" />
:label="item.field_name" </template>
> <template v-if="item.field_type==='date'">
<el-input
type="number"
placeholder="请输入"
v-model="item.sort"
/>
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'float'"
:label="item.field_name"
>
<el-input
type="number"
placeholder="请输入"
v-model="item.sort"
/>
</el-form-item>
<el-form-item
v-else-if="item.field_type === 'date'"
:label="item.field_name"
>
<el-date-picker <el-date-picker
v-model="item.create_time" v-model="item.default_value"
type="date" type="date"
placeholder="选择日期" placeholder="选择日期"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd"
style="width: 100%" style="width: 100%"
> >
</el-date-picker> </el-date-picker>
</el-form-item> </template>
<el-form-item <template v-if="item.field_type==='datetime'">
v-else-if="item.field_type === 'datetime'"
:label="item.field_name"
>
<el-date-picker <el-date-picker
v-model="item.create_time" v-model="item.default_value"
type="datetime" type="datetime"
placeholder="选择日期" placeholder="选择日期"
value-format="yyyy-MM-dd HH:mm:ss" value-format="yyyy-MM-dd HH:mm:ss"
style="width: 100%" style="width: 100%"
> >
</el-date-picker> </el-date-picker>
</el-form-item> </template>
<el-form-item <template v-if="item.field_type==='select'">
v-else-if="item.field_type === 'select'" <el-select style="width: 100%" v-model="item.default_value" placeholder="请选择">
:label="item.field_name"
>
<el-select
style="width: 100%"
v-model="item.sort"
placeholder="请选择"
>
<el-option <el-option
v-for="item1 in item.field_choice" v-for="item1 in item.field_choice"
:key="item1" :key="item1.id"
:label="item1" :label="item1.name"
:value="item1" :value="item1.id"
> >
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </template>
<el-form-item <template v-if="item.field_type==='selects'">
v-else-if="item.field_type === 'selects'" <el-select style="width: 100%" multiple v-model="item.default_value" placeholder="请选择">
:label="item.field_name"
>
<el-select
style="width: 100%"
v-model="optio"
multiple
placeholder="请选择"
>
<el-option <el-option
v-for="item1 in item.field_choice" v-for="item1 in item.field_choice"
:key="item1" :key="item1.id"
:label="item1" :label="item1.name"
:value="item1" :value="item1.id"
> >
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </template>
</el-row> <template v-if="item.field_type==='textarea'">
<el-input type="textarea" :rows="3" v-model="item.default_value" placeholder="内容" />
</template>
<template v-if="item.field_type==='file'">
<el-upload
ref="upload"
:action="upUrl"
:on-preview="handlePreview"
:on-success="handleUpSuccess"
:on-remove="handleRemove"
:headers="upHeaders"
:file-list="fileList"
:limit="1"
accept=".doc,.docx,.xls,.xlsx,.ppt,.pptx"
>
<el-button size="small" type="primary">上传文件</el-button>
</el-upload>
</template>
</el-form-item>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button @click="limitedRetrial = false"> </el-button> <el-button @click="limitedRetrial = false"> </el-button>
@ -609,12 +590,15 @@
</div> </div>
</template> </template>
<script> <script>
import { getOrgAll } from "@/api/org";
import { getUserList } from "@/api/user";
import {getWarehouseList} from "@/api/inm";
import checkPermission from "@/utils/permission";
import customForm from '@/components/customForm/index'; import customForm from '@/components/customForm/index';
import reviewForm from '@/components/customForm/review'; import reviewForm from '@/components/customForm/review';
import {getwproductList, wproductTest, wproductPutin, createputins,testInit,scrap,getRetrial} from "@/api/wpm"; import {getWfCustomfieldList,createTicket ,getWorkflowInit} from "@/api/workflow";
import checkPermission from "@/utils/permission";
import {getWarehouseList} from "@/api/inm";
import {getMaterialList, getrecordformList, getrffieldList} from "@/api/mtm"; import {getMaterialList, getrecordformList, getrffieldList} from "@/api/mtm";
import {getwproductList, wproductTest, wproductPutin, createputins,testInit,scrap,getRetrial} from "@/api/wpm";
import {getTestRecord,getTestRecordItem,putTestRecordItem,delTestRecordItem,subTestRecordItem} from "@/api/qm"; import {getTestRecord,getTestRecordItem,putTestRecordItem,delTestRecordItem,subTestRecordItem} from "@/api/qm";
import {genTree} from "@/utils"; import {genTree} from "@/utils";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
@ -698,13 +682,17 @@
{ lable: "其他", value: 40 }, { lable: "其他", value: 40 },
], ],
recordList: [], recordList: [],
transitions: null,
options: [], options: [],
listLoading: true, listLoading: true,
fieldList: [], fieldList: [],
originList: [], originList: [],
is_testok: "true", is_testok: "true",
field: [], field: [],
userList: [],//用户列表
orgList: [],//部门列表
recordformList: [], recordformList: [],
customfieldList: [],
recordform: null, recordform: null,
recordId: null, recordId: null,
fifo_detail: "", fifo_detail: "",
@ -728,6 +716,15 @@
dialogFormVisibles: false, dialogFormVisibles: false,
limitedCheckRecord: false, limitedCheckRecord: false,
testrecord: {}, testrecord: {},
retrialItem: {},//复检对象
retrialResponse: {},//复检对象
//复检表单
retrialForm: {
title:'',
transition:null,
workflow:'',
ticket_data:{},
},
WarehouseData: "", WarehouseData: "",
formName: '项目检查表', formName: '项目检查表',
}; };
@ -823,14 +820,64 @@
}, },
//不合格产品重审展示 //不合格产品重审展示
handleRetrial(scope){ handleRetrial(scope){
let that =this;
this.retrialItem = Object.assign({}, scope.row);
console.log(this.retrialItem);
getRetrial(scope.row.id).then(res=>{ getRetrial(scope.row.id).then(res=>{
debugger; that.retrialResponse = res.data;
console.log(res) 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++) {
if(that.customfieldList[i].field_key==="wproduct"){
that.customfieldList[i].default_value = that.retrialResponse.exist_data.wproduct;
}
if(that.customfieldList[i].field_key==="wproduct_name"){
that.customfieldList[i].default_value = that.retrialResponse.exist_data.wproduct_name;
}
if(that.customfieldList[i].field_key==="wproduct_type"){
that.customfieldList[i].default_value = that.retrialResponse.exist_data.wproduct_specification;
}
if(that.customfieldList[i].field_key==="finder"){
that.customfieldList[i].default_value = that.retrialResponse.exist_data.finder;
}
if(that.customfieldList[i].field_key==="find_process"){
that.customfieldList[i].default_value = that.retrialResponse.exist_data.find_process;
}
if(that.customfieldList[i].label==="sys_user"){
that.customfieldList[i].field_choice = that.userList;
}
if(that.customfieldList[i].label==="deptSelect"){
that.customfieldList[i].field_choice = that.orgList;
}
}
}
});
that.limitedRetrial = true;
}) })
}, },
//不合格产品重审提交 //不合格产品重审提交
retrialSubmit(){ retrialSubmit(){
let that = this;
let fields = this.customfieldList;
let obj = new Object();
for(let i=0;i<fields.length;i++){
obj[fields[i].field_key] = fields[i].default_value
}
this.retrialForm.ticket_data = obj;
this.retrialForm.workflow = that.retrialResponse.workflow;
this.retrialForm.title = that.retrialResponse.exist_data.wproduct_name+'的重审';
createTicket(this.retrialForm).then((res) => {
if (res.code >= 200) {
this.getList4();
this.limitedRetrial = false;
this.$message.success("成功");
}
});
}, },
//夹层半成品列表 //夹层半成品列表
getList3() { getList3() {
@ -880,7 +927,6 @@
//点击检验:如果有一个直接进入如果有多个表再进行选择 //点击检验:如果有一个直接进入如果有多个表再进行选择
handleInspection(scope,index) { handleInspection(scope,index) {
//调该物料对应的检查表 //调该物料对应的检查表
// debugger;
let that = this; let that = this;
this.innerIndex = index; this.innerIndex = index;
// this.outerVisible = true; // this.outerVisible = true;
@ -890,7 +936,6 @@
this.recordform = null; this.recordform = null;
getrecordformList(this.listQueryrecordform).then((response) => { getrecordformList(this.listQueryrecordform).then((response) => {
if (response.data) { if (response.data) {
debugger;
this.recordformList = response.data; this.recordformList = response.data;
if(response.data.length===1){ if(response.data.length===1){
that.recordform = response.data[0].id; that.recordform = response.data[0].id;
@ -921,7 +966,6 @@
that.limitedCheckRecord = true; that.limitedCheckRecord = true;
getTestRecord({wproduct:scope.row.id}).then(res=>{ getTestRecord({wproduct:scope.row.id}).then(res=>{
if(res.code==200){ if(res.code==200){
debugger;
that.recordList = res.data.results; that.recordList = res.data.results;
}else{ }else{
this.$message.error(res.msg); this.$message.error(res.msg);
@ -930,7 +974,6 @@
}, },
//点击记录里的检验 //点击记录里的检验
handleInspectionRecord(scope){ handleInspectionRecord(scope){
// debugger;
let that =this; let that =this;
that.recordVisible = false; that.recordVisible = false;
that.recordId = scope.row.id; that.recordId = scope.row.id;
@ -949,7 +992,6 @@
that.hasPicture = true; that.hasPicture = true;
} }
getTestRecordItem(scope.row.id).then((res) => { getTestRecordItem(scope.row.id).then((res) => {
// debugger;
let arr = []; let arr = [];
let fieldList = res.data.record_data; let fieldList = res.data.record_data;
for(let i=0;i<that.fieldList.length;i++){ for(let i=0;i<that.fieldList.length;i++){
@ -972,7 +1014,6 @@
} }
}); });
}else if(that.innerIndex==='2'){//复检 }else if(that.innerIndex==='2'){//复检
debugger;
getTestRecordItem(scope.row.id).then((res) => { getTestRecordItem(scope.row.id).then((res) => {
if (res.data) { if (res.data) {
that.hasPicture = false; that.hasPicture = false;
@ -995,8 +1036,6 @@
} }
that.fieldList.push(obj) that.fieldList.push(obj)
} }
debugger;
console.log(that.fieldList)
// that.fieldList = [...fieldList]; // that.fieldList = [...fieldList];
let arr = fieldList.filter(item => { let arr = fieldList.filter(item => {
return item.field_type === 'draw' return item.field_type === 'draw'
@ -1015,11 +1054,9 @@
}, },
//半产品复检 //半产品复检
handleReview() { handleReview() {
// debugger;
let that = this; let that = this;
testInit({ wproduct: this.wproduct,form: that.recordform}).then((response) => { testInit({ wproduct: this.wproduct,form: that.recordform}).then((response) => {
if (response.data) { if (response.data) {
debugger;
that.hasPicture = false; that.hasPicture = false;
that.recordId = response.data.id; that.recordId = response.data.id;
that.formName = response.data.form_.name; that.formName = response.data.form_.name;
@ -1054,7 +1091,6 @@
//根据选择的表渲染检查项目 //根据选择的表渲染检查项目
submitrecordform(index) { submitrecordform(index) {
// debugger;
let that = this; let that = this;
this.outerVisible = false; this.outerVisible = false;
if (that.recordform != "") { if (that.recordform != "") {
@ -1080,7 +1116,6 @@
} }
}); });
}else if(index==='2'){//复检 }else if(index==='2'){//复检
// debugger;
that.handleReview(); that.handleReview();
} }
} else this.$message.error("请选择检查表!"); } else this.$message.error("请选择检查表!");
@ -1145,7 +1180,6 @@
params.record_data = value.record_data; params.record_data = value.record_data;
params.is_testok = value.is_testok; params.is_testok = value.is_testok;
putTestRecordItem(id,params).then((res) => { putTestRecordItem(id,params).then((res) => {
// debugger;
if (res.code >= 200) { if (res.code >= 200) {
that.recordVisible = false; that.recordVisible = false;
that.limitedReview = false; that.limitedReview = false;
@ -1202,5 +1236,15 @@
this.reload() this.reload()
}, },
}, },
mounted() {
getUserList({page:0}).then(response => {
if (response.data) {
this.userList = response.data
}
});
getOrgAll().then((response) => {
this.orgList = response.data;
});
}
} }
</script> </script>

View File

@ -187,7 +187,7 @@
v-if="item.field_type === 'string'" v-if="item.field_type === 'string'"
:label="item.field_name" :label="item.field_name"
> >
<el-input placeholder="请输入" v-model="item.sort" /> <el-input v-model="item.field_value" placeholder="请输入"/>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-else-if="item.field_type === 'int'" v-else-if="item.field_type === 'int'"
@ -196,7 +196,7 @@
<el-input <el-input
type="number" type="number"
placeholder="请输入" placeholder="请输入"
v-model="item.sort" v-model="item.field_value"
/> />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
@ -206,7 +206,7 @@
<el-input <el-input
type="number" type="number"
placeholder="请输入" placeholder="请输入"
v-model="item.sort" v-model="item.field_value"
/> />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
@ -214,7 +214,7 @@
:label="item.field_name" :label="item.field_name"
> >
<el-date-picker <el-date-picker
v-model="item.create_time" v-model="item.field_value"
type="date" type="date"
placeholder="选择日期" placeholder="选择日期"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd"
@ -227,7 +227,7 @@
:label="item.field_name" :label="item.field_name"
> >
<el-date-picker <el-date-picker
v-model="item.create_time" v-model="item.field_value"
type="datetime" type="datetime"
placeholder="选择日期" placeholder="选择日期"
value-format="yyyy-MM-dd HH:mm:ss" value-format="yyyy-MM-dd HH:mm:ss"
@ -241,7 +241,7 @@
> >
<el-select <el-select
style="width: 100%" style="width: 100%"
v-model="item.sort" v-model="item.field_value"
placeholder="请选择" placeholder="请选择"
> >
<el-option <el-option
@ -259,7 +259,7 @@
> >
<el-select <el-select
style="width: 100%" style="width: 100%"
v-model="optio" v-model="item.field_value"
multiple multiple
placeholder="请选择" placeholder="请选择"
> >
@ -604,6 +604,7 @@ export default {
}, },
fieldList: { fieldList: {
count: 0, count: 0,
name:'',
}, },
listQueryfield: { listQueryfield: {
page: 1, page: 1,
@ -798,7 +799,9 @@ export default {
this.formID = scope.row.id; this.formID = scope.row.id;
recordInit( this.formID).then((response) => { recordInit( this.formID).then((response) => {
if (response.data) { if (response.data) {
debugger;
this.fieldList = response.data; this.fieldList = response.data;
this.fieldList.name = response.data.form_.name;
} }
}); });
this.dialogVisibleForm = true; this.dialogVisibleForm = true;
@ -820,16 +823,21 @@ export default {
let _this = this; let _this = this;
_this.record_data = []; //检查项目 _this.record_data = []; //检查项目
this.fieldList.record_data.forEach((item) => { this.fieldList.record_data.forEach((item) => {
if(item.field_type==='int'){
item.field_value = parseInt(item.field_value)
}else if(item.field_type==='float'){
item.field_value = parseFloat(item.field_value)
}
_this.record_data.push({ _this.record_data.push({
id: item.id, id: item.id,
field_value: item.field_value, field_value:item.field_value,
}); });
}); });
this.record.record_data = _this.record_data; this.record.record_data = _this.record_data;
createRecord(this.formID, this.record).then((res) => { createRecord(this.formID, this.record).then((res) => {
if (res.code >= 200) { if (res.code >= 200) {
this.getrecordLists(); this.getrecordLists();
this.$message.success("创建成功!"); this.$message.success("提交成功!");
this.dialogVisibleForm = false; this.dialogVisibleForm = false;
} }

File diff suppressed because it is too large Load Diff

View File

View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class DevelopConfig(AppConfig):
name = 'apps.develop'

View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@ -0,0 +1,10 @@
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
urlpatterns = [
path('cleandata/', CleanDataView.as_view()),
]

View File

@ -0,0 +1,28 @@
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
from apps.inm.models import FIFO, Inventory, MaterialBatch
from apps.mtm.models import Material
from apps.pm.models import ProductionPlan
from apps.sam.models import Order
from apps.wf.models import Ticket
from apps.wpm.models import Operation
# Create your views here.
class CleanDataView(APIView):
permission_classes = [IsAdminUser]
def post(self, request, format=None):
"""
清空数据库
"""
Order.objects.all().delete(soft=False)
ProductionPlan.objects.all().delete(soft=False)
FIFO.objects.all().delete(soft=False)
Material.objects.filter(type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).update(count=0)
MaterialBatch.objects.filter(material__type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).delete()
Inventory.objects.filter(material__type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).delete()
Ticket.objects.all().delete(soft=False)
Operation.objects.all().delete()
return Response()

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.9 on 2021-12-27 01:48
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wpm', '0038_auto_20211227_0948'),
('inm', '0023_auto_20211216_0945'),
]
operations = [
migrations.AlterField(
model_name='fifoitemproduct',
name='wproduct',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fifoitem_wproduct', to='wpm.wproduct', verbose_name='关联的动态产品'),
),
migrations.AlterField(
model_name='iproduct',
name='wproduct',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='iproduct_wproduct', to='wpm.wproduct', verbose_name='关联的动态产品'),
),
]

View File

@ -92,7 +92,8 @@ class IProduct(BaseModel):
material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE) material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库') warehouse = models.ForeignKey(WareHouse, on_delete=models.CASCADE, verbose_name='所在仓库')
batch = models.CharField('所属批次号', max_length=100, default='') batch = models.CharField('所属批次号', max_length=100, default='')
wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True) wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False,
null=True, blank=True, related_name='iproduct_wproduct')
is_mtested = models.BooleanField('是否军检', default=False) is_mtested = models.BooleanField('是否军检', default=False)
is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True) is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True)
remark_mtest = models.TextField('军检备注', null=True, blank=True) remark_mtest = models.TextField('军检备注', null=True, blank=True)
@ -103,7 +104,8 @@ class FIFOItemProduct(BaseModel):
出入库产品 出入库产品
""" """
fifoitem = models.ForeignKey(FIFOItem, verbose_name='关联出入库具体产品', on_delete=models.CASCADE) fifoitem = models.ForeignKey(FIFOItem, verbose_name='关联出入库具体产品', on_delete=models.CASCADE)
wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True) wproduct = models.ForeignKey('wpm.wproduct', on_delete=models.CASCADE, verbose_name='关联的动态产品', db_constraint=False, null=True, blank=True,
related_name='fifoitem_wproduct')
number = models.CharField('物品编号', max_length=50) number = models.CharField('物品编号', max_length=50)
material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE) material = models.ForeignKey(Material, verbose_name='物料类型', on_delete=models.CASCADE)
iproduct = models.ForeignKey(IProduct, verbose_name='关联库存产品', null=True, blank=True, on_delete=models.SET_NULL) iproduct = models.ForeignKey(IProduct, verbose_name='关联库存产品', null=True, blank=True, on_delete=models.SET_NULL)

View File

@ -74,7 +74,7 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
perms_map = {'*': '*'} perms_map = {'*': '*'}
queryset = FIFOItem.objects.select_related('material', 'fifo').all() queryset = FIFOItem.objects.select_related('material', 'fifo').all()
serializer_class = FIFOItemSerializer serializer_class = FIFOItemSerializer
filterset_fields = ['material', 'fifo'] filterset_fields = ['material', 'fifo', 'fifo__type', 'is_tested', 'is_testok']
search_fields = [] search_fields = []
ordering_fields = ['create_time'] ordering_fields = ['create_time']
ordering = ['-create_time'] ordering = ['-create_time']
@ -99,13 +99,7 @@ class FIFOItemViewSet(ListModelMixin, DestroyModelMixin, GenericViewSet):
obj = serializer.save(create_by = self.request.user) obj = serializer.save(create_by = self.request.user)
tris = [] tris = []
for m in record_data: # 保存记录详情 for m in record_data: # 保存记录详情
form_field = m['form_field']
m['field_name'] = form_field.field_name
m['field_key'] = form_field.field_key
m['field_type'] = form_field.field_type
m['field_value'] = m['field_value'] m['field_value'] = m['field_value']
m['sort'] = form_field.sort
m['need_judge'] = form_field.need_judge
m['is_testok'] = m['is_testok'] if 'is_testok' in m else True m['is_testok'] = m['is_testok'] if 'is_testok' in m else True
m['test_record'] = obj m['test_record'] = obj
tris.append(TestRecordItem(**m)) tris.append(TestRecordItem(**m))

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-27 05:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0017_auto_20211213_1401'),
]
operations = [
migrations.AddField(
model_name='productionplan',
name='process_json',
field=models.JSONField(blank=True, default=list, null=True, verbose_name='按工序的统计数'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-28 06:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0018_productionplan_process_json'),
]
operations = [
migrations.AlterField(
model_name='productionplan',
name='process_json',
field=models.JSONField(blank=True, default=dict, null=True, verbose_name='按工序的统计数'),
),
]

View File

@ -0,0 +1,48 @@
# Generated by Django 3.2.9 on 2021-12-29 01:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pm', '0019_alter_productionplan_process_json'),
]
operations = [
migrations.AddField(
model_name='productionplan',
name='count_notok',
field=models.PositiveIntegerField(default=0, verbose_name='不合格数量'),
),
migrations.AddField(
model_name='subproductionplan',
name='main_count_notok',
field=models.PositiveIntegerField(default=0, verbose_name='不合格数量'),
),
migrations.AddField(
model_name='subproductionprogress',
name='count_notok',
field=models.PositiveIntegerField(default=0, verbose_name='不合格数量'),
),
migrations.AlterField(
model_name='subproductionprogress',
name='count',
field=models.PositiveIntegerField(verbose_name='应出入数'),
),
migrations.AlterField(
model_name='subproductionprogress',
name='count_ok',
field=models.PositiveIntegerField(default=0, verbose_name='合格数量'),
),
migrations.AlterField(
model_name='subproductionprogress',
name='count_pick',
field=models.PositiveIntegerField(default=0, verbose_name='实际领用数'),
),
migrations.AlterField(
model_name='subproductionprogress',
name='count_real',
field=models.PositiveIntegerField(default=0, verbose_name='实际消耗/产出数'),
),
]

View File

@ -27,8 +27,10 @@ class ProductionPlan(CommonAModel):
count = models.PositiveIntegerField('生产数量', default=1) count = models.PositiveIntegerField('生产数量', default=1)
count_real = models.PositiveIntegerField('实际产出数', default=0) count_real = models.PositiveIntegerField('实际产出数', default=0)
count_ok = models.PositiveIntegerField('合格数', default=0) count_ok = models.PositiveIntegerField('合格数', default=0)
count_notok = models.PositiveIntegerField('不合格数量', default=0)
start_date = models.DateField('计划开工日期') start_date = models.DateField('计划开工日期')
end_date = models.DateField('计划完工日期') end_date = models.DateField('计划完工日期')
process_json = models.JSONField('按工序的统计数', default=dict, null=True, blank=True)
is_planed = models.BooleanField('是否已排产', default=False) is_planed = models.BooleanField('是否已排产', default=False)
class Meta: class Meta:
verbose_name = '生产计划' verbose_name = '生产计划'
@ -66,6 +68,7 @@ class SubProductionPlan(CommonAModel):
main_count = models.PositiveIntegerField('应产出数', default=0) main_count = models.PositiveIntegerField('应产出数', default=0)
main_count_real = models.PositiveIntegerField('实际产出数', default=0) main_count_real = models.PositiveIntegerField('实际产出数', default=0)
main_count_ok = models.PositiveIntegerField('合格数', default=0) main_count_ok = models.PositiveIntegerField('合格数', default=0)
main_count_notok = models.PositiveIntegerField('不合格数量', default=0)
steps = models.JSONField('工艺步骤', default=list) steps = models.JSONField('工艺步骤', default=list)
@ -87,7 +90,8 @@ class SubProductionProgress(BaseModel):
material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE) material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE)
is_main = models.BooleanField('是否主产出', default=False) is_main = models.BooleanField('是否主产出', default=False)
type = models.IntegerField('物料应用类型', default=SubprodctionMaterial.type_choices) type = models.IntegerField('物料应用类型', default=SubprodctionMaterial.type_choices)
count = models.IntegerField('应出入数') count = models.PositiveIntegerField('应出入数')
count_pick = models.IntegerField('实际领用数', default=0) count_pick = models.PositiveIntegerField('实际领用数', default=0)
count_real = models.IntegerField('实际消耗/产出数', default=0) count_real = models.PositiveIntegerField('实际消耗/产出数', default=0)
count_ok = models.IntegerField('合格数量', default=0) count_ok = models.PositiveIntegerField('合格数量', default=0)
count_notok = models.PositiveIntegerField('不合格数量', default=0)

View File

@ -48,11 +48,7 @@ class SubProductionPlanUpdateSerializer(serializers.ModelSerializer):
class GenSubPlanSerializer(serializers.Serializer): class GenSubPlanSerializer(serializers.Serializer):
pass pass
class SubProductionProgressSerializer(serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True)
class Meta:
model = SubProductionProgress
fields = '__all__'
class PickNeedSerializer(serializers.Serializer): class PickNeedSerializer(serializers.Serializer):
warehouse = serializers.IntegerField(label="仓库ID") warehouse = serializers.IntegerField(label="仓库ID")
@ -64,3 +60,10 @@ class SubproductionPlanSimpleSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = SubProductionPlan model = SubProductionPlan
fields = ['id', 'number'] fields = ['id', 'number']
class SubProductionProgressSerializer(serializers.ModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True)
subproduction_plan_ = SubproductionPlanSimpleSerializer(source='subproduction_plan', read_only=True)
class Meta:
model = SubProductionProgress
fields = '__all__'

View File

@ -0,0 +1,28 @@
from django.db.models.aggregates import Sum
from apps.pm.models import ProductionPlan, SubProductionPlan
class PmService:
@classmethod
def update_plan_process_json(cls, plan:ProductionPlan):
"""
更新计划按工序统计字段
"""
ret = {}
subplans = SubProductionPlan.objects.filter(production_plan=plan, is_deleted=False)
qs = subplans.values('process', 'process__name', 'process__number').annotate(count=Sum('main_count'),
count_real=Sum('main_count_real'), count_ok=Sum('main_count_ok'), count_notok=Sum('main_count_notok'))
qs_list = list(qs)
for i in qs_list:
ret[i['process__number']] = {
'count':i['count'],
'count_real':i['count_real'],
'count_ok':i['count_ok'],
'count_notok':i['count_notok'],
'rate': round((i['count_ok']/(i['count_ok']+i['count_notok']))*100,2) if (i['count_ok']+i['count_notok']) > 0 else 0
}
plan.process_json = ret
plan.save()

View File

@ -2,6 +2,7 @@ from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from apps.mtm.models import Material from apps.mtm.models import Material
from apps.pm.models import SubProductionPlan, SubProductionProgress from apps.pm.models import SubProductionPlan, SubProductionProgress
from apps.pm.services import PmService
@receiver(post_save, sender=SubProductionProgress) @receiver(post_save, sender=SubProductionProgress)
def update_subplan_main(sender, instance, created, **kwargs): def update_subplan_main(sender, instance, created, **kwargs):
@ -13,8 +14,10 @@ def update_subplan_main(sender, instance, created, **kwargs):
if created: if created:
subplan.main_product = instance.material subplan.main_product = instance.material
subplan.main_count = instance.count subplan.main_count = instance.count
subplan.main_count_real = instance.count_real subplan.main_count_real = instance.count_real
subplan.main_count_ok = instance.count_ok subplan.main_count_ok = instance.count_ok
subplan.main_count_notok = instance.count_notok
if instance.count_ok >= instance.count and instance.count_ok > 0: if instance.count_ok >= instance.count and instance.count_ok > 0:
subplan.state = SubProductionPlan.SUBPLAN_STATE_DONE subplan.state = SubProductionPlan.SUBPLAN_STATE_DONE
elif instance.count_ok < instance.count and instance.count_ok > 0: elif instance.count_ok < instance.count and instance.count_ok > 0:
@ -25,7 +28,12 @@ def update_subplan_main(sender, instance, created, **kwargs):
plan = subplan.production_plan plan = subplan.production_plan
plan.count_real = subplan.main_count_real plan.count_real = subplan.main_count_real
plan.count_ok = subplan.main_count_ok plan.count_ok = subplan.main_count_ok
plan.count_notok = subplan.main_count_notok
plan.save() plan.save()
# 更新计划工序统计字段
PmService.update_plan_process_json(subplan.production_plan)

View File

@ -11,7 +11,7 @@ from apps.mtm.models import Step, SubProduction, SubprodctionMaterial, UsedStep
from apps.pm.filters import SubproductionProgressFilterSet from apps.pm.filters import SubproductionProgressFilterSet
from apps.system.mixins import CreateUpdateModelAMixin from apps.system.mixins import CreateUpdateModelAMixin
from apps.pm.serializers import GenSubPlanSerializer, PickNeedSerializer, PlanDestorySerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer from apps.pm.serializers import GenSubPlanSerializer, PickNeedSerializer, PlanDestorySerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan
from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet
from django.shortcuts import render from django.shortcuts import render
@ -21,6 +21,7 @@ from rest_framework.response import Response
from rest_framework.decorators import action from rest_framework.decorators import action
from django.db.models import F from django.db.models import F
from utils.tools import ranstr from utils.tools import ranstr
from django.db import transaction
# Create your views here. # Create your views here.
def updateOrderPlanedCount(order): def updateOrderPlanedCount(order):
@ -34,7 +35,7 @@ def updateOrderPlanedCount(order):
order.planed_count = planed_count order.planed_count = planed_count
order.save() order.save()
class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModelMixin, GenericViewSet): class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModelMixin, RetrieveModelMixin, GenericViewSet):
""" """
生产计划 生产计划
""" """
@ -77,6 +78,7 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
return Response() return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=GenSubPlanSerializer) @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=GenSubPlanSerializer)
@transaction.atomic
def gen_subplan(self, request, pk=None): def gen_subplan(self, request, pk=None):
""" """
生成子计划 生成子计划

View File

@ -14,6 +14,8 @@ from rest_framework.decorators import action
from django.db.models import F from django.db.models import F
from rest_framework.response import Response from rest_framework.response import Response
from django.utils import timezone from django.utils import timezone
from apps.wf.models import Workflow
# Create your views here. # Create your views here.
class CustomerViewSet(CreateUpdateCustomMixin, ModelViewSet): class CustomerViewSet(CreateUpdateCustomMixin, ModelViewSet):
""" """
@ -168,6 +170,10 @@ class SaleViewSet(CreateUpdateCustomMixin, ListModelMixin, RetrieveModelMixin, C
FIFOItemProduct.objects.bulk_create(ipxs) FIFOItemProduct.objects.bulk_create(ipxs)
# 更新成品库情况 # 更新成品库情况
ips.update(is_saled=True) ips.update(is_saled=True)
# 更新动态产品表情况
from apps.wpm.models import WProduct
WProduct.objects.filter(iproduct_wproduct__sale_iproduct__sale=obj).update(
act_state=WProduct.WPR_ACT_STATE_SELLED)
# 更新库存 # 更新库存
update_inm(fifo) update_inm(fifo)
# 变更审核状态 # 变更审核状态

View File

@ -0,0 +1,38 @@
# Generated by Django 3.2.9 on 2021-12-23 02:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0019_auto_20211221_0923'),
]
operations = [
migrations.AddField(
model_name='customfield',
name='is_hidden',
field=models.BooleanField(default=False, help_text='可用于携带不需要用户查看的信息', verbose_name='是否隐藏'),
),
migrations.AlterField(
model_name='customfield',
name='field_choice',
field=models.JSONField(blank=True, default=list, help_text='选项值格式为list, 例["id":1, "name":"张三"]', verbose_name='选项值'),
),
migrations.AlterField(
model_name='customfield',
name='field_type',
field=models.CharField(choices=[('string', '字符串'), ('int', '整型'), ('float', '浮点'), ('boolean', '布尔'), ('date', '日期'), ('datetime', '日期时间'), ('radio', '单选'), ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), ('cascader', '单选级联'), ('cascaders', '多选级联'), ('select_dg', '弹框单选'), ('select_dgs', '弹框多选'), ('textarea', '文本域'), ('file', '附件')], help_text='5.字符串10.整形15.浮点型20.布尔25.日期30.日期时间35.单选框40.多选框45.下拉列表50.多选下拉列表55.文本域60.用户名, 70.多选的用户名, 80.附件(只保存路径,多个使用逗号隔开)', max_length=50, verbose_name='类型'),
),
migrations.AlterField(
model_name='customfield',
name='label',
field=models.CharField(default='', help_text='处理特殊逻辑使用', max_length=1000, verbose_name='标签'),
),
migrations.AlterField(
model_name='workflow',
name='title_template',
field=models.CharField(blank=True, default='{title}', help_text='工单字段的值可以作为参数写到模板中,格式如:你有一个待办工单:{title}', max_length=50, null=True, verbose_name='标题模板'),
),
]

View File

@ -0,0 +1,33 @@
# Generated by Django 3.2.9 on 2021-12-24 06:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0020_auto_20211223_1006'),
]
operations = [
migrations.AlterField(
model_name='customfield',
name='description',
field=models.CharField(blank=True, help_text='字段的描述信息,可用于显示在字段的下方对该字段的详细描述', max_length=100, null=True, verbose_name='描述'),
),
migrations.AlterField(
model_name='customfield',
name='field_template',
field=models.TextField(blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder', null=True, verbose_name='文本域模板'),
),
migrations.AlterField(
model_name='customfield',
name='field_type',
field=models.CharField(choices=[('string', '字符串'), ('int', '整型'), ('float', '浮点'), ('boolean', '布尔'), ('date', '日期'), ('datetime', '日期时间'), ('radio', '单选'), ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), ('cascader', '单选级联'), ('cascaders', '多选级联'), ('select_dg', '弹框单选'), ('select_dgs', '弹框多选'), ('textarea', '文本域'), ('file', '附件')], help_text='string, int, float, date, datetime, radio, checkbox, select, selects, cascader, cascaders, cascader, cascaders,textarea, file', max_length=50, verbose_name='类型'),
),
migrations.AlterField(
model_name='customfield',
name='placeholder',
field=models.CharField(blank=True, help_text='用户工单详情表单中作为字段的占位符显示', max_length=100, null=True, verbose_name='占位符'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-27 01:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0021_auto_20211224_1426'),
]
operations = [
migrations.AlterField(
model_name='customfield',
name='is_hidden',
field=models.BooleanField(default=False, help_text='可用于携带不需要用户查看的字段信息', verbose_name='是否隐藏'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.9 on 2021-12-27 05:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wf', '0022_alter_customfield_is_hidden'),
]
operations = [
migrations.AlterField(
model_name='customfield',
name='field_type',
field=models.CharField(choices=[('string', '字符串'), ('int', '整型'), ('float', '浮点'), ('boolean', '布尔'), ('date', '日期'), ('datetime', '日期时间'), ('radio', '单选'), ('checkbox', '多选'), ('select', '单选下拉'), ('selects', '多选下拉'), ('cascader', '单选级联'), ('cascaders', '多选级联'), ('select_dg', '弹框单选'), ('select_dgs', '弹框多选'), ('textarea', '文本域'), ('file', '附件')], help_text='string, int, float, date, datetime, radio, checkbox, select, selects, cascader, cascaders, select_dg, select_dgs,textarea, file', max_length=50, verbose_name='类型'),
),
migrations.AlterField(
model_name='customfield',
name='label',
field=models.CharField(default='', help_text='处理特殊逻辑使用,比如sys_user用于获取用户作为选项', max_length=1000, verbose_name='标签'),
),
]

View File

@ -150,26 +150,32 @@ class CustomField(CommonAModel):
('checkbox', '多选'), ('checkbox', '多选'),
('select', '单选下拉'), ('select', '单选下拉'),
('selects', '多选下拉'), ('selects', '多选下拉'),
('cascader', '单选级联'),
('cascaders', '多选级联'),
('select_dg', '弹框单选'),
('select_dgs', '弹框多选'),
('textarea', '文本域'), ('textarea', '文本域'),
('selectuser', '单选用户'), ('file', '附件')
('selectusers', '多选用户'),
('file', '附件'),
('draw', '绘图')
) )
workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流') workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流')
field_type = models.CharField('类型', max_length=50, choices=field_type_choices, help_text='5.字符串10.整形15.浮点型20.布尔25.日期30.日期时间35.单选框40.多选框45.下拉列表50.多选下拉列表55.文本域60.用户名, 70.多选的用户名, 80.附件(只保存路径,多个使用逗号隔开)') field_type = models.CharField('类型', max_length=50, choices=field_type_choices,
help_text='string, int, float, date, datetime, radio, checkbox, select, selects, cascader, cascaders, select_dg, select_dgs,textarea, file')
field_key = models.CharField('字段标识', max_length=50, help_text='字段类型请尽量特殊,避免与系统中关键字冲突') field_key = models.CharField('字段标识', max_length=50, help_text='字段类型请尽量特殊,避免与系统中关键字冲突')
field_name = models.CharField('字段名称', max_length=50) field_name = models.CharField('字段名称', max_length=50)
sort = models.IntegerField('排序', default=0, help_text='工单基础字段在表单中排序为:流水号0,标题20,状态id40,状态名41,创建人80,创建时间100,更新时间120.前端展示工单信息的表单可以根据这个id顺序排列') sort = models.IntegerField('排序', default=0, help_text='工单基础字段在表单中排序为:流水号0,标题20,状态id40,状态名41,创建人80,创建时间100,更新时间120.前端展示工单信息的表单可以根据这个id顺序排列')
default_value = models.CharField('默认值', null=True, blank=True, max_length=100, help_text='前端展示时,可以将此内容作为表单中的该字段的默认值') default_value = models.CharField('默认值', null=True, blank=True, max_length=100, help_text='前端展示时,可以将此内容作为表单中的该字段的默认值')
description = models.CharField('描述', max_length=100, blank=True, default='', help_text='字段的描述信息,可用于显示在字段的下方对该字段的详细描述') description = models.CharField('描述', max_length=100, blank=True, null=True, help_text='字段的描述信息,可用于显示在字段的下方对该字段的详细描述')
placeholder = models.CharField('占位符', max_length=100, blank=True, default='', help_text='用户工单详情表单中作为字段的占位符显示') placeholder = models.CharField('占位符', max_length=100, blank=True, null=True, help_text='用户工单详情表单中作为字段的占位符显示')
field_template = models.TextField('文本域模板', default='', blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder') field_template = models.TextField('文本域模板', null=True, blank=True, help_text='文本域类型字段前端显示时可以将此内容作为字段的placeholder')
boolean_field_display = models.JSONField('布尔类型显示名', default=dict, blank=True, boolean_field_display = models.JSONField('布尔类型显示名', default=dict, blank=True,
help_text='当为布尔类型时候,可以支持自定义显示形式。{"1":"","0":""}或{"1":"需要","0":"不需要"},注意数字也需要引号') help_text='当为布尔类型时候,可以支持自定义显示形式。{"1":"","0":""}或{"1":"需要","0":"不需要"},注意数字也需要引号')
field_choice = models.JSONField('radio、checkbox、select的选项', default=dict, blank=True,
help_text='radio,checkbox,select,multiselect类型可供选择的选项格式为json如:{"1":"中国", "2":"美国"},注意数字也需要引号') field_choice = models.JSONField('选项值', default=list, blank=True,
label = models.JSONField('标签', blank=True, default=dict, help_text='自定义标签json格式调用方可根据标签自行处理特殊场景逻辑loonflow只保存文本内容') help_text='选项值格式为list, 例["id":1, "name":"张三"]')
label = models.CharField('标签', max_length=1000, default='', help_text='处理特殊逻辑使用,比如sys_user用于获取用户作为选项')
# hook = models.CharField('hook', max_length=1000, default='', help_text='获取下拉选项用于动态选项值')
is_hidden = models.BooleanField('是否隐藏', default=False, help_text='可用于携带不需要用户查看的字段信息')
class Ticket(CommonBModel): class Ticket(CommonBModel):
""" """

View File

@ -38,12 +38,33 @@ class TransitionSerializer(serializers.ModelSerializer):
queryset = queryset.select_related('source_state','destination_state') queryset = queryset.select_related('source_state','destination_state')
return queryset return queryset
class AllField(serializers.Field):
def to_representation(self, value):
return value
def to_internal_value(self, data):
return data
class FieldChoiceSerializer(serializers.Serializer):
id = AllField(label='ID')
name = serializers.CharField(label='名称')
class CustomFieldSerializer(serializers.ModelSerializer): class CustomFieldSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = CustomField model = CustomField
fields = '__all__' fields = '__all__'
class CustomFieldCreateUpdateSerializer(serializers.ModelSerializer):
field_choice = FieldChoiceSerializer(label='选项列表', many=True, required=False)
class Meta:
model = CustomField
fields = ['workflow', 'field_type', 'field_key', 'field_name',
'sort', 'default_value', 'description', 'placeholder', 'field_template',
'boolean_field_display', 'field_choice', 'label', 'is_hidden']
class TicketSimpleSerializer(serializers.ModelSerializer): class TicketSimpleSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Ticket model = Ticket
@ -107,8 +128,29 @@ class TicketDetailSerializer(serializers.ModelSerializer):
key = i['field_key'] key = i['field_key']
i['field_state'] = state_fields.get(key, 1) i['field_state'] = state_fields.get(key, 1)
i['field_value'] = ticket_data.get(key, None) i['field_value'] = ticket_data.get(key, None)
i['field_display'] = i['field_value'] # 该字段是用于查看详情直接展示
if i['field_value']:
if 'sys_user' in i['label']:
if isinstance(i['field_value'], list):
i['field_display'] = ','.join(list(User.objects.filter(id__in=i['field_value']).values_list('name', flat=True)))
else:
i['field_display'] = User.objects.get(id=i['field_value']).name
elif i['field_type'] in ['radio', 'select']:
for m in i['field_choice']:
if m['id'] == i['field_value']:
i['field_display'] = m['name']
elif i['field_type'] in ['checkbox', 'selects']:
d_list = []
for m in i['field_choice']:
if m['id'] in i['field_value']:
d_list.append(m['name'])
i['field_display'] = ','.join(d_list)
return all_fields_l return all_fields_l
def filter_display(self, item, field_value):
if item['id'] == field_value:
return
class TicketFlowSerializer(serializers.ModelSerializer): class TicketFlowSerializer(serializers.ModelSerializer):
participant_ = UserSimpleSerializer(source='participant', read_only=True) participant_ = UserSimpleSerializer(source='participant', read_only=True)
state_ = StateSimpleSerializer(source='state', read_only=True) state_ = StateSimpleSerializer(source='state', read_only=True)

View File

@ -9,7 +9,7 @@ from django.core.exceptions import AppRegistryNotReady
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import serializers from rest_framework import serializers
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, TicketCreateSerializer, TicketDestorySerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer from apps.wf.serializers import CustomFieldCreateUpdateSerializer, CustomFieldSerializer, StateSerializer, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, TicketCreateSerializer, TicketDestorySerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from rest_framework.viewsets import GenericViewSet, ModelViewSet from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework.decorators import action, api_view from rest_framework.decorators import action, api_view
@ -111,6 +111,11 @@ class CustomFieldViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin,
filterset_fields = ['workflow', 'field_type'] filterset_fields = ['workflow', 'field_type']
ordering = ['sort'] ordering = ['sort']
def get_serializer_class(self):
if self.action in ['create', 'update']:
return CustomFieldCreateUpdateSerializer
return super().get_serializer_class()
class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet): class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet):
perms_map = {'*':'*'} perms_map = {'*':'*'}
queryset = Ticket.objects.all() queryset = Ticket.objects.all()
@ -133,7 +138,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
return super().get_serializer_class() return super().get_serializer_class()
def filter_queryset(self, queryset): def filter_queryset(self, queryset):
if not self.request.query_params.get('category', None): if not self.detail and not self.request.query_params.get('category', None):
raise APIException('请指定查询分类') raise APIException('请指定查询分类')
return super().filter_queryset(queryset) return super().filter_queryset(queryset)

View File

@ -25,6 +25,7 @@ class WMaterialFilterSet(filters.FilterSet):
class WProductFilterSet(filters.FilterSet): class WProductFilterSet(filters.FilterSet):
tag = filters.CharFilter(method='filter_tag') tag = filters.CharFilter(method='filter_tag')
production_plan = filters.NumberFilter(field_name='subproduction_plan__production_plan')
class Meta: class Meta:
model = WProduct model = WProduct
fields = ['step', 'subproduction_plan', 'material', 'step__process', 'act_state', 'material__type'] fields = ['step', 'subproduction_plan', 'material', 'step__process', 'act_state', 'material__type']

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.9 on 2021-12-27 01:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0037_wproduct_scrap_reason'),
]
operations = [
migrations.AddField(
model_name='wproduct',
name='ng_sign',
field=models.PositiveSmallIntegerField(blank=True, choices=[(10, '返工'), (20, '返修'), (30, '报废'), (40, '让步接收'), (50, '偏离许可'), (60, '降级使用'), (70, '退回供方'), (80, '召回')], null=True, verbose_name='不合格标记'),
),
migrations.AlterField(
model_name='wproduct',
name='act_state',
field=models.IntegerField(choices=[(6, '待复检'), (8, '操作准备中'), (10, '操作进行中'), (20, '待检验'), (26, '待夹层检验'), (30, '已合格'), (40, '已入库'), (50, '不合格'), (60, '待成品检验'), (70, '已报废'), (80, '已售出')], default=0, verbose_name='进行状态'),
),
migrations.AlterField(
model_name='wprouctticket',
name='decision',
field=models.PositiveSmallIntegerField(blank=True, choices=[(10, '返工'), (20, '返修'), (30, '报废'), (40, '让步接收'), (50, '偏离许可'), (60, '降级使用'), (70, '退回供方'), (80, '召回')], null=True, verbose_name='最终决定'),
),
]

View File

@ -0,0 +1,58 @@
# Generated by Django 3.2.9 on 2021-12-28 06:17
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 = [
('inm', '0024_auto_20211227_0948'),
('qm', '0022_auto_20211216_1401'),
('mtm', '0042_alter_recordformfield_field_type'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('pm', '0019_alter_productionplan_process_json'),
('wf', '0023_auto_20211227_1318'),
('wpm', '0038_auto_20211227_0948'),
]
operations = [
migrations.AlterField(
model_name='wprouctticket',
name='step',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.step', verbose_name='所在步骤/发现步骤'),
),
migrations.CreateModel(
name='WproductFlow',
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='删除标记')),
('number', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='物品编号')),
('act_state', models.IntegerField(choices=[(6, '待复检'), (8, '操作准备中'), (10, '操作进行中'), (20, '待检验'), (26, '待夹层检验'), (30, '已合格'), (40, '已入库'), (50, '不合格'), (60, '待成品检验'), (70, '已报废'), (80, '已售出')], default=0, verbose_name='进行状态')),
('is_hidden', models.BooleanField(default=False, verbose_name='是否隐藏')),
('remark', models.CharField(blank=True, max_length=200, null=True, verbose_name='备注')),
('scrap_reason', models.IntegerField(blank=True, choices=[(10, '气泡'), (20, '破点'), (30, '划伤'), (40, '其他')], null=True, verbose_name='报废原因')),
('ng_sign', models.PositiveSmallIntegerField(blank=True, choices=[(10, '返工'), (20, '返修'), (30, '报废'), (40, '让步接收'), (50, '偏离许可'), (60, '降级使用'), (70, '退回供方'), (80, '召回')], null=True, verbose_name='不合格标记')),
('is_lastlog', models.BooleanField(default=True, verbose_name='是否该子计划下的最后一条日志')),
('child', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproductflow')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductflow_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='所属物料状态')),
('operation', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wpm.operation', verbose_name='当前操作')),
('pre_step', models.ForeignKey(blank=True, help_text='已执行完的步骤', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='wl_pre_step', to='mtm.step', verbose_name='已执行到')),
('step', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='wl_step', to='mtm.step', verbose_name='所在步骤')),
('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='pm.subproductionplan', verbose_name='当前子生产计划')),
('test', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='qm.testrecord', verbose_name='当前检验')),
('ticket', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wf.ticket', verbose_name='当前工单')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wproductflow_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
('warehouse', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='inm.warehouse', verbose_name='所在仓库')),
('wproduct', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.wproduct', verbose_name='关联产品')),
],
options={
'abstract': False,
},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.9 on 2021-12-28 07:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0039_auto_20211228_1417'),
]
operations = [
migrations.AlterField(
model_name='wproductflow',
name='number',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='物品编号'),
),
]

View File

@ -36,6 +36,7 @@ class WProduct(CommonAModel):
WPR_ACT_STATE_NOTOK = 50 WPR_ACT_STATE_NOTOK = 50
WPR_ACT_STATE_TOFINALTEST = 60 WPR_ACT_STATE_TOFINALTEST = 60
WPR_ACT_STATE_SCRAP = 70 WPR_ACT_STATE_SCRAP = 70
WPR_ACT_STATE_SELLED = 80
act_state_choices=( act_state_choices=(
(WPR_ACT_STATE_TORETEST, '待复检'), (WPR_ACT_STATE_TORETEST, '待复检'),
(WPR_ACT_STATE_DOWAIT, '操作准备中'), (WPR_ACT_STATE_DOWAIT, '操作准备中'),
@ -46,7 +47,8 @@ class WProduct(CommonAModel):
(WPR_ACT_STATE_INM, '已入库'), (WPR_ACT_STATE_INM, '已入库'),
(WPR_ACT_STATE_NOTOK, '不合格'), (WPR_ACT_STATE_NOTOK, '不合格'),
(WPR_ACT_STATE_TOFINALTEST, '待成品检验'), (WPR_ACT_STATE_TOFINALTEST, '待成品检验'),
(WPR_ACT_STATE_SCRAP, '已报废') (WPR_ACT_STATE_SCRAP, '已报废'),
(WPR_ACT_STATE_SELLED, '已售出'),
) )
SCRAP_REASON_QIPAO = 10 SCRAP_REASON_QIPAO = 10
SCRAP_REASON_PODIAN = 20 SCRAP_REASON_PODIAN = 20
@ -58,6 +60,26 @@ class WProduct(CommonAModel):
(30, '划伤'), (30, '划伤'),
(40, '其他') (40, '其他')
) )
NG_BACK_WORK = 10
NG_BACK_FIX = 20
NG_SCRAP = 30
NG_ACCEPT = 40
NG_PERMIT = 50
NG_DOWN = 60
NG_BACK_FROM = 70
NG_RECALL = 80
ng_choices = (
(NG_BACK_WORK, '返工'),
(NG_BACK_FIX, '返修'),
(NG_SCRAP, '报废'),
(NG_ACCEPT, '让步接收'),
(NG_PERMIT, '偏离许可'),
(NG_DOWN, '降级使用'),
(NG_BACK_FROM, '退回供方'),
(NG_RECALL, '召回')
)
number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50) number = models.CharField('物品编号', unique=True, null=True, blank=True, max_length=50)
material = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE) material = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE)
pre_step = models.ForeignKey(Step, verbose_name='已执行到', help_text='已执行完的步骤', null=True, blank=True, on_delete=models.CASCADE, related_name='w_pre_step') pre_step = models.ForeignKey(Step, verbose_name='已执行到', help_text='已执行完的步骤', null=True, blank=True, on_delete=models.CASCADE, related_name='w_pre_step')
@ -69,6 +91,9 @@ class WProduct(CommonAModel):
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='wproduct_subplan') subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE, related_name='wproduct_subplan')
scrap_reason = models.IntegerField('报废原因', choices=scrap_reason_choices, null=True, blank=True) scrap_reason = models.IntegerField('报废原因', choices=scrap_reason_choices, null=True, blank=True)
ng_sign = models.PositiveSmallIntegerField('不合格标记', choices=ng_choices, null=True, blank=True)
warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True) warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True)
operation = models.ForeignKey('wpm.operation', verbose_name='当前操作', operation = models.ForeignKey('wpm.operation', verbose_name='当前操作',
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation') on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_operation')
@ -89,14 +114,43 @@ class WprouctTicket(CommonAModel):
""" """
玻璃审批工单 玻璃审批工单
""" """
number = models.CharField('物品编号', null=True, blank=True, max_length=50) number = models.CharField('物品编号', null=True, blank=True, max_length=50)
wproduct = models.ForeignKey(WProduct, verbose_name='关联产品', on_delete=models.CASCADE) wproduct = models.ForeignKey(WProduct, verbose_name='关联产品', on_delete=models.CASCADE)
material = models.ForeignKey(Material, verbose_name='所在物料状态', on_delete=models.CASCADE) material = models.ForeignKey(Material, verbose_name='所在物料状态', on_delete=models.CASCADE)
step = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE) step = models.ForeignKey(Step, verbose_name='所在步骤/发现步骤', on_delete=models.CASCADE)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='所在子生产计划', on_delete=models.CASCADE) subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='所在子生产计划', on_delete=models.CASCADE)
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.CASCADE, related_name='wt_ticket') ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单', on_delete=models.CASCADE, related_name='wt_ticket')
decision = models.CharField('最终决定', null=True, blank=True, max_length=100) decision = models.PositiveSmallIntegerField('最终决定', choices=WProduct.ng_choices, null=True, blank=True)
class WproductFlow(CommonAModel):
"""
动态产品表日志
"""
wproduct = models.ForeignKey(WProduct, on_delete=models.CASCADE, verbose_name='关联产品', null=True, blank=True)
number = models.CharField('物品编号', null=True, blank=True, max_length=50)
material = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE)
pre_step = models.ForeignKey(Step, verbose_name='已执行到', help_text='已执行完的步骤', null=True, blank=True, on_delete=models.CASCADE, related_name='wl_pre_step')
step = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True, related_name='wl_step')
act_state = models.IntegerField('进行状态', default=0, choices=WProduct.act_state_choices)
is_hidden = models.BooleanField('是否隐藏', default=False)
child = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE)
remark = models.CharField('备注', max_length=200, null=True, blank=True)
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='当前子生产计划', on_delete=models.CASCADE)
scrap_reason = models.IntegerField('报废原因', choices= WProduct.scrap_reason_choices, null=True, blank=True)
ng_sign = models.PositiveSmallIntegerField('不合格标记', choices= WProduct.ng_choices, null=True, blank=True)
warehouse = models.ForeignKey(WareHouse, verbose_name='所在仓库', on_delete=models.SET_NULL, null=True, blank=True)
operation = models.ForeignKey('wpm.operation', verbose_name='当前操作',
on_delete=models.SET_NULL, null=True, blank=True)
test = models.ForeignKey('qm.testrecord', verbose_name='当前检验',
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)
is_lastlog = models.BooleanField('是否该子计划下的最后一条日志', default=True)
class Pick(CommonADModel): class Pick(CommonADModel):
""" """

View File

@ -382,6 +382,7 @@ class OperationMaterialCreate2Serailizer(serializers.ModelSerializer):
class OperationMaterialCreate2ListSerailizer(serializers.ListSerializer): class OperationMaterialCreate2ListSerailizer(serializers.ListSerializer):
child=OperationMaterialCreate2Serailizer() child=OperationMaterialCreate2Serailizer()
class OperationMaterialCreate3Serializer(serializers.ModelSerializer): class OperationMaterialCreate3Serializer(serializers.ModelSerializer):
material = serializers.PrimaryKeyRelatedField(required=True, queryset=Material.objects.all()) material = serializers.PrimaryKeyRelatedField(required=True, queryset=Material.objects.all())
class Meta: class Meta:

View File

@ -1,9 +1,13 @@
from django.utils import timezone
from typing import List from typing import List
from django.db.models.expressions import F
from apps.pm.models import SubProductionPlan, SubProductionProgress from apps.pm.models import SubProductionPlan, SubProductionProgress
from apps.mtm.models import Material, Step, SubprodctionMaterial from apps.mtm.models import Material, Step, SubprodctionMaterial
from apps.qm.models import TestRecord from apps.qm.models import TestRecord
from apps.system.models import User from apps.system.models import User
from apps.wpm.models import WProduct from apps.wf.models import State, TicketFlow, Transition
from apps.wpm.models import WProduct, WproductFlow, WprouctTicket
from utils.tools import ranstr from utils.tools import ranstr
class WpmServies(object): class WpmServies(object):
@ -46,21 +50,72 @@ class WpmServies(object):
if is_testok: if is_testok:
if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检 if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检
wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT
elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验 elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验
wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST
else: else:
wproduct.act_state = WProduct.WPR_ACT_STATE_OK wproduct.act_state = WProduct.WPR_ACT_STATE_OK
if wproduct.number is None: # 产生半成品编号 if wproduct.number is None: # 产生半成品编号
wproduct.number = 'WP'+ranstr(7) wproduct.number = 'WP'+ranstr(7)
# 更新子计划状态
# 更新子计划主产品数 # 去除ng_sign
instance = SubProductionProgress.objects.get(subproduction_plan=wproduct.subproduction_plan, if wproduct.ng_sign:
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) wt = WprouctTicket.objects.order_by('id').last() #取最后的工单
instance.count_ok = instance.count_ok + 1 # 这个地方可能会有问题 if wt.step.process == test.step.process:
instance.save() wproduct.ng_sign = None
ticket = wt.ticket
ticket_data = wt.ticket_data
ticket_data['retest_result'] = 1
ticket.update_by = user
ticket.save()
# 创建处理日志
TicketFlow.objects.create(ticket=ticket, state=ticket.state,
participant_type=State.PARTICIPANT_TYPE_PERSONAL,
intervene_type=0,
participant=user)
# 更新子计划相关进度
cls.update_subproduction_progress_main(sp=wproduct.subproduction_plan)
else:# 如果不合格 else:# 如果不合格
wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK wproduct.act_state = WProduct.WPR_ACT_STATE_NOTOK
# 需要走不合格品审理的工单 # 需要走不合格品审理的工单
# 如果已经是返工返修的产品
if wproduct.ng_sign:
wt = WprouctTicket.objects.order_by('id').last() #取最后的工单
if wt.step.process == test.step.process:
ticket = wt.ticket
ticket_data = wt.ticket_data
ticket_data['retest_result'] = 0
ticket.update_by = user
ticket.save()
# 创建处理日志
TicketFlow.objects.create(ticket=ticket, state=ticket.state,
participant_type=State.PARTICIPANT_TYPE_PERSONAL,
intervene_type=0,
participant=user)
wproduct.update_by = user wproduct.update_by = user
wproduct.update_time = timezone.now()
wproduct.test = None wproduct.test = None
wproduct.save() wproduct.save()
@classmethod
def update_subproduction_progress_main(cls, sp:SubProductionPlan):
"""
根据产品变动日志更新生产进度
"""
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_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,
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT).first()
if ins:
ins.count_ok = count_ok
ins.count_notok = count_notok
ins.count_real = count_real
ins.save()

View File

@ -1,9 +1,14 @@
from django.db.models.expressions import F
from django.db.models.signals import post_save from django.db.models.signals import post_save
from apps.mtm.models import Step, SubprodctionMaterial
from apps.pm.models import SubProductionPlan, SubProductionProgress
from apps.qm.models import TestRecord from apps.qm.models import TestRecord
from apps.wf.models import Ticket from apps.wf.models import Ticket
from django.dispatch import receiver from django.dispatch import receiver
from rest_framework import exceptions
from apps.wpm.models import WProduct, WprouctTicket from apps.wpm.models import WProduct, WproductFlow, WprouctTicket
from apps.wpm.models import OperationWproduct
from apps.wpm.services import WpmServies
@receiver(post_save, sender=Ticket) @receiver(post_save, sender=Ticket)
@ -41,20 +46,53 @@ def handleTicket(sender, instance, created, **kwargs):
执行操作决定 执行操作决定
""" """
ticket_data = instance.ticket_data ticket_data = instance.ticket_data
wt = instance.wt_ticket wt = WprouctTicket.objects.get(ticket=instance)
wp = wt.wproduct wp = wt.wproduct
decision = WProduct.NG_BACK_WORK
if 'decision_1' in ticket_data and ticket_data['decision_1']: if 'decision_1' in ticket_data and ticket_data['decision_1']:
wt.decision = ticket_data['decision_1'] decision = ticket_data['decision_1']
if ticket_data['decision_1'] in ['返工', '返修']:
pass
elif ticket_data['decision_1'] in ['让步接收']:
wp.act_state = WProduct.WPR_ACT_STATE_OK
elif 'decision_2' in ticket_data and ticket_data['decision_2']: elif 'decision_2' in ticket_data and ticket_data['decision_2']:
wp.decision = ticket_data['decision_2'] decision = ticket_data['decision_2']
if ticket_data['decision_2'] in ['返工', '返修']:
pass
elif ticket_data['decision_2'] in ['让步接收']: wp.ng_sign = decision
wp.act_state = WProduct.WPR_ACT_STATE_OK if decision in [WProduct.NG_BACK_WORK, WProduct.NG_BACK_FIX]:
wt.save() step = Step.objects.get(id=ticket_data['back_step'])
wp.save() wp.step = step
# 找到当时所属的计划
sp = SubProductionPlan.objects.filter(ow_subplan__wproduct=wp,
ow_subplan__operation__is_submited=True, ow_subplan__operation__step=step).first()
if sp:
wp.subproduction_plan = sp
wt.save()
wp.ticket = None # 解除当前工单
wp.act_state = WProduct.WPR_ACT_STATE_DOWAIT
wp.save()
# 更新子计划合格进度
WpmServies.update_subproduction_progress_main(sp=sp)
else:
raise exceptions.APIException('返回步骤点错误')
elif decision in [WProduct.NG_ACCEPT, WProduct.NG_PERMIT]:
wp.act_state = WProduct.WPR_ACT_STATE_OK
wp.ng_sign = decision
wt.save()
wp.ticket = None # 解除当前工单
wp.save()
@receiver(post_save, sender=WProduct)
def update_wproduct_log(sender, instance, created, **kwargs):
"""
更新产品变动日志
"""
# update_fields = kwargs['update_fields']
WproductFlow.objects.filter(wproduct=instance, subproduction_plan=instance.subproduction_plan).update(is_lastlog=False)
ins = WproductFlow()
ins.wproduct = instance
for f in WproductFlow._meta.fields:
if f.name not in ['id', 'create_time', 'update_time', 'wproduct', 'is_lastlog']:
setattr(ins, f.name, getattr(instance, f.name, None))
ins.save()

View File

@ -1,3 +1,4 @@
from django.db.models.expressions import F
from django.shortcuts import render from django.shortcuts import render
from rest_framework.generics import CreateAPIView, GenericAPIView from rest_framework.generics import CreateAPIView, GenericAPIView
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
@ -179,14 +180,21 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
半成品 半成品
""" """
perms_map={'*':'*'} perms_map={'*':'*'}
queryset = WProduct.objects.select_related('step', 'material', 'subproduction_plan').filter(is_hidden=False) queryset = WProduct.objects.select_related('step', 'material', 'subproduction_plan')
serializer_class = WProductListSerializer serializer_class = WProductListSerializer
filterset_class = WProductFilterSet filterset_class = WProductFilterSet
search_fields = ['number'] search_fields = ['number']
ordering_fields = ['id'] ordering_fields = ['id']
ordering = ['id'] ordering = ['id']
def get_queryset(self):
queryset = self.queryset
if self.request.query_params.get('tag', None) != 'show_hidden':
queryset = queryset.filter(is_hidden=False)
return queryset
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestFormInitSerializer) @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=WpmTestFormInitSerializer)
@transaction.atomic
def test_init(self, request, pk=None): def test_init(self, request, pk=None):
""" """
检验记录创建及初始化 检验记录创建及初始化
@ -225,6 +233,8 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
savedict['type'] = TestRecord.TEST_COMB savedict['type'] = TestRecord.TEST_COMB
tr = TestRecord.objects.create(**savedict) tr = TestRecord.objects.create(**savedict)
wproduct.test = tr wproduct.test = tr
wproduct.update_by = request.user
wproduct.update_time = timezone.now()
wproduct.save() wproduct.save()
# 创建检验条目 # 创建检验条目
for i in RecordFormField.objects.filter(form=form, is_deleted=False): for i in RecordFormField.objects.filter(form=form, is_deleted=False):
@ -282,7 +292,12 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
FIFOItemProduct.objects.bulk_create(ips) FIFOItemProduct.objects.bulk_create(ips)
# 更新库存并修改半成品进行状态 # 更新库存并修改半成品进行状态
update_inm(fifo) update_inm(fifo)
wproducts.update(act_state=WProduct.WPR_ACT_STATE_INM, warehouse=warehouse, update_by=request.user, update_time=timezone.now()) for i in wproducts:
i.act_state = WProduct.WPR_ACT_STATE_INM
i.warehouse = warehouse
i.update_by = request.user
i.update_time = timezone.now()
i.save()
return Response() return Response()
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=WproductPutInSerializer) @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=WproductPutInSerializer)
@ -343,7 +358,8 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
vdata = serializer.validated_data vdata = serializer.validated_data
if obj.act_state == WProduct.WPR_ACT_STATE_NOTOK: if obj.act_state == WProduct.WPR_ACT_STATE_NOTOK:
pass pass
elif obj.step.process.id == 1: # 如果是冷加工可直接报废 elif obj.step.process.id == 1 and \
obj.act_state in [WProduct.WPR_ACT_STATE_DOWAIT, WProduct.WPR_ACT_STATE_TOTEST]: # 如果是冷加工可直接报废
if vdata.get('scrap_reason', None): if vdata.get('scrap_reason', None):
obj.scrap_reason = vdata['scrap_reason'] obj.scrap_reason = vdata['scrap_reason']
else: else:
@ -352,6 +368,7 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
raise exceptions.APIException('该产品不可报废') raise exceptions.APIException('该产品不可报废')
obj.act_state = WProduct.WPR_ACT_STATE_SCRAP obj.act_state = WProduct.WPR_ACT_STATE_SCRAP
obj.update_by = request.user obj.update_by = request.user
obj.update_time = timezone.now()
obj.save() obj.save()
return Response() return Response()
@ -369,12 +386,14 @@ class WProductViewSet(ListModelMixin, GenericViewSet):
发起不合格审理单 发起不合格审理单
""" """
obj = self.get_object() obj = self.get_object()
if obj.act_state != WProduct.WPR_ACT_STATE_NOTOK: if obj.act_state != WProduct.WPR_ACT_STATE_NOTOK or obj.ng_sign is not None\
raise exceptions.APIException('非检验不合格产品不可发起不合格审理') or obj.ticket is not None:
raise exceptions.APIException('该产品不可发起不合格审理')
workflow = Workflow.objects.filter(name='不合格品审理单', is_deleted=False).first() workflow = Workflow.objects.filter(name='不合格品审理单', is_deleted=False).first()
if workflow: if workflow:
exist_data = { exist_data = {
'wproduct':obj.id, 'wproduct':obj.id,
'wproduct_number':obj.number,
'wproduct_name':obj.material.name, 'wproduct_name':obj.material.name,
'wproduct_specification':obj.material.specification, 'wproduct_specification':obj.material.specification,
'finder':request.user.id, 'finder':request.user.id,
@ -537,11 +556,10 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
else: else:
wp.act_state = WProduct.WPR_ACT_STATE_TOTEST wp.act_state = WProduct.WPR_ACT_STATE_TOTEST
wp.material = wsp.main_product wp.material = wsp.main_product
# 更新子计划进度 # 更新子计划生产进度
instance = SubProductionProgress.objects.get(subproduction_plan=wsp, # 如果产品有返工标记不做计算
is_main=True, type=SubprodctionMaterial.SUB_MA_TYPE_OUT) if wp.ng_sign not in [WProduct.NG_BACK_FIX, WProduct.NG_BACK_WORK]:
instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨 WpmServies.update_subproduction_progress_main(sp=wsp)
instance.save()
wp.operation = None wp.operation = None
wp.update_by = request.user wp.update_by = request.user
wp.save() wp.save()
@ -554,7 +572,8 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
act_state=WProduct.WPR_ACT_STATE_DOWAIT, remark='', act_state=WProduct.WPR_ACT_STATE_DOWAIT, remark='',
subproduction_plan=i.subproduction_plan) subproduction_plan=i.subproduction_plan)
for x in range(i.count): for x in range(i.count):
WProduct.objects.create(**wpr) WProduct.objects.create(**wpr)
WpmServies.update_subproduction_progress_main(sp=i.subproduction_plan)
elif step.type == Step.STEP_TYPE_COMB: elif step.type == Step.STEP_TYPE_COMB:
oms_w = OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT, oms_w = OperationMaterial.objects.filter(operation=op, type=SubprodctionMaterial.SUB_MA_TYPE_OUT,
subproduction_progress__is_main=True) subproduction_progress__is_main=True)
@ -571,9 +590,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd
else: else:
wproduct.act_state = WProduct.WPR_ACT_STATE_TOTEST wproduct.act_state = WProduct.WPR_ACT_STATE_TOTEST
# 更新子计划进度 # 更新子计划进度
instance = oms_w.subproduction_progress WpmServies.update_subproduction_progress_main(sp=oms_w.subproduction_plan)
instance.count_real = instance.count_real + 1 # 这个地方可能会有问题,不够严谨
instance.save()
wproduct.create_by = request.user wproduct.create_by = request.user
wproduct.save() wproduct.save()
# 隐藏原半成品 # 隐藏原半成品

View File

@ -58,7 +58,8 @@ INSTALLED_APPS = [
'apps.qm', 'apps.qm',
'apps.pm', 'apps.pm',
'apps.wpm', 'apps.wpm',
'apps.srm' 'apps.srm',
'apps.develop'
] ]
MIDDLEWARE = [ MIDDLEWARE = [

View File

@ -70,6 +70,7 @@ urlpatterns = [
path('api/pm/', include('apps.pm.urls')), path('api/pm/', include('apps.pm.urls')),
path('api/wpm/', include('apps.wpm.urls')), path('api/wpm/', include('apps.wpm.urls')),
path('api/srm/', include('apps.srm.urls')), path('api/srm/', include('apps.srm.urls')),
path('api/develop/', include('apps.develop.urls')),
# 工具 # 工具
path('api/utils/signature/', GenSignature.as_view()), path('api/utils/signature/', GenSignature.as_view()),
path('api/utils/develop/', UpdateDevelop.as_view()), path('api/utils/develop/', UpdateDevelop.as_view()),