Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop
This commit is contained in:
commit
9592cb5fbc
|
@ -162,6 +162,14 @@ export function ticketAccpet(id,data) {
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
//撤回工单,允许创建人在指定状态撤回工单至初始状态
|
||||||
|
export function getTicketRetreat(id,data) {
|
||||||
|
return request({
|
||||||
|
url: `/wf/ticket/${id}/retreat/`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
//工单详情
|
//工单详情
|
||||||
export function getTicketDetail(id) {
|
export function getTicketDetail(id) {
|
||||||
return request({
|
return request({
|
||||||
|
@ -169,7 +177,8 @@ export function getTicketDetail(id) {
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
//工单详情
|
|
||||||
|
//工单流转
|
||||||
export function getTicketTransitions(id) {
|
export function getTicketTransitions(id) {
|
||||||
return request({
|
return request({
|
||||||
url: `/wf/ticket/${id}/transitions/`,
|
url: `/wf/ticket/${id}/transitions/`,
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
<template>
|
||||||
|
<div style="width: 100%;height: 100%;">
|
||||||
|
<div class="testTracking">
|
||||||
|
<canvas style="display:none;" id="myCanvas" ref="canvas" :width="videoWidth" :height="videoHeight"></canvas>
|
||||||
|
<!--图片展示-->
|
||||||
|
<video ref="video" id="myVideo" :width="videoWidth" :height="videoHeight" autoplay style="display: block;margin:0 auto;border: 2px solid #333333;"></video>
|
||||||
|
<!--确认-->
|
||||||
|
<Button type="primary" @click="setImage" class="takePhoto">拍照</Button>
|
||||||
|
<div id="res"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {faceLogin} from "@/api/testModel";
|
||||||
|
export default {
|
||||||
|
props:['src'],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
videoWidth: 500,
|
||||||
|
videoHeight: 400,
|
||||||
|
videoArr:[],//所有的摄像头
|
||||||
|
modelSel:'',//
|
||||||
|
myInterval: null,
|
||||||
|
imgSrc: '',
|
||||||
|
isHasFace:false,//默认没有人脸
|
||||||
|
tracker:null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
},
|
||||||
|
mounted(){
|
||||||
|
this.callCamera();
|
||||||
|
this.changePhoto();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 调用摄像头
|
||||||
|
callCamera () {
|
||||||
|
// H5调用电脑摄像头API
|
||||||
|
navigator.mediaDevices.getUserMedia({
|
||||||
|
video: true
|
||||||
|
}).then(success => {
|
||||||
|
// 摄像头开启成功
|
||||||
|
this.$refs['video'].srcObject = success;
|
||||||
|
// 实时拍照效果
|
||||||
|
this.$refs['video'].play()
|
||||||
|
}).catch(error => {
|
||||||
|
alert('摄像头开启失败,请检查摄像头是否可用!')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 拍照
|
||||||
|
setImage () {
|
||||||
|
let canvas = document.getElementById("myCanvas");
|
||||||
|
let context = canvas.getContext('2d');
|
||||||
|
let video = document.getElementById("myVideo");
|
||||||
|
context.drawImage(video,0,0,90,68);
|
||||||
|
let image = new Image();
|
||||||
|
image = canvas.toDataURL('image/png');
|
||||||
|
document.getElementById('res').innerHTML = '<img src="'+image+'">';
|
||||||
|
let imgData = {base64:image};
|
||||||
|
faceLogin(imgData).then((res) => {
|
||||||
|
if (res.code >= 200) {
|
||||||
|
this.$message.success("成功");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 关闭摄像头
|
||||||
|
closeCamera () {
|
||||||
|
if (!this.$refs['video'].srcObject) return;
|
||||||
|
let stream = this.$refs['video'].srcObject;
|
||||||
|
let tracks = stream.getTracks();
|
||||||
|
tracks.forEach(track => {
|
||||||
|
track.stop()
|
||||||
|
});
|
||||||
|
this.$refs['video'].srcObject = null
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
//切换本地摄像头
|
||||||
|
changePhoto(){
|
||||||
|
/**得到所有的设备*/
|
||||||
|
navigator.mediaDevices.enumerateDevices()
|
||||||
|
.then((devices)=> {
|
||||||
|
this.videoArr = [];
|
||||||
|
devices.forEach((item)=> {
|
||||||
|
if(item.kind == 'videoinput'){
|
||||||
|
this.videoArr.push({
|
||||||
|
'label': item.kind,
|
||||||
|
'id': item.deviceId
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(err=>{
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**打开摄像头*/
|
||||||
|
getUserMedia(constraints, success, error) {
|
||||||
|
if (navigator.mediaDevices.getUserMedia) {
|
||||||
|
//最新的标准API
|
||||||
|
navigator.mediaDevices.getUserMedia(constraints).then(success=>{
|
||||||
|
// 摄像头开启成功
|
||||||
|
this.$refs['video'].srcObject = success;
|
||||||
|
// 实时拍照效果
|
||||||
|
this.$refs['video'].play()
|
||||||
|
}).catch(error);
|
||||||
|
|
||||||
|
} else if (navigator.webkitGetUserMedia) {
|
||||||
|
//webkit核心浏览器
|
||||||
|
navigator.webkitGetUserMedia(constraints,success, error)
|
||||||
|
} else if (navigator.mozGetUserMedia) {
|
||||||
|
//firfox浏览器
|
||||||
|
navigator.mozGetUserMedia(constraints, success, error);
|
||||||
|
} else if (navigator.getUserMedia) {
|
||||||
|
//旧版API
|
||||||
|
navigator.getUserMedia(constraints, success, error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setFace(data){
|
||||||
|
this.isHasFace = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.testTracking {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.takePhoto{
|
||||||
|
margin: 30px auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -53,7 +53,7 @@ const actions = {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getInfo(state.token).then(response => {
|
getInfo(state.token).then(response => {
|
||||||
const { data } = response
|
const { data } = response
|
||||||
|
sessionStorage.setItem('userId',data.id);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
reject('验证失败,重新登陆.')
|
reject('验证失败,重新登陆.')
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
|
|
||||||
|
@ -42,12 +41,10 @@
|
||||||
{{cpuData.percent}}%
|
{{cpuData.percent}}%
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-card shadow="always">
|
<el-card shadow="always">
|
||||||
|
|
||||||
<div slot="header" class="clearfix">
|
<div slot="header" class="clearfix">
|
||||||
<span>内存</span>
|
<span>内存</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,12 +80,10 @@
|
||||||
{{memoryData.used}}
|
{{memoryData.used}}
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-card shadow="always">
|
<el-card shadow="always">
|
||||||
|
|
||||||
<div slot="header" class="clearfix">
|
<div slot="header" class="clearfix">
|
||||||
<span>硬盘</span>
|
<span>硬盘</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -124,13 +119,9 @@
|
||||||
{{diskData.used}}
|
{{diskData.used}}
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-card class="box-card">
|
<el-card class="box-card">
|
||||||
|
@ -150,19 +141,20 @@
|
||||||
type="primary"
|
type="primary"
|
||||||
icon="el-icon-search"
|
icon="el-icon-search"
|
||||||
@click="handleFilter"
|
@click="handleFilter"
|
||||||
>搜索</el-button
|
>搜索
|
||||||
>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
class="filter-item"
|
class="filter-item"
|
||||||
type="primary"
|
type="primary"
|
||||||
icon="el-icon-refresh-left"
|
icon="el-icon-refresh-left"
|
||||||
@click="resetFilter"
|
@click="resetFilter"
|
||||||
>重置</el-button
|
>重置
|
||||||
>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<el-table
|
<el-table
|
||||||
:data="tableData"
|
:data="tableData"
|
||||||
style="width: 100%;height:400px">
|
style="width: 100%;height:400px"
|
||||||
|
>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="name"
|
prop="name"
|
||||||
label="日志名称"
|
label="日志名称"
|
||||||
|
@ -176,35 +168,29 @@
|
||||||
<el-table-column
|
<el-table-column
|
||||||
fixed="right"
|
fixed="right"
|
||||||
label="操作"
|
label="操作"
|
||||||
width="100">
|
width="100"
|
||||||
|
>
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button @click="handleClick(scope.row)" type="text" size="small">查看详情</el-button>
|
<el-button @click="handleClick(scope.row)" type="text" size="small">查看详情</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
|
|
||||||
:visible.sync="dialogVisible"
|
:visible.sync="dialogVisible"
|
||||||
width="80%"
|
width="80%"
|
||||||
height:300px
|
title="日志详情"
|
||||||
>
|
>
|
||||||
|
<div v-html="logdec" class="dialogDiv"></div>
|
||||||
|
|
||||||
<div v-html="logdec"> </div>
|
|
||||||
|
|
||||||
|
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {getlogList, getLog, getServerList} from "@/api/moritor";
|
import {getlogList, getLog, getServerList} from "@/api/moritor";
|
||||||
const defaultCMA = {
|
|
||||||
|
|
||||||
}
|
const defaultCMA = {}
|
||||||
export default {
|
export default {
|
||||||
components: {},
|
components: {},
|
||||||
data() {
|
data() {
|
||||||
|
@ -258,16 +244,30 @@ export default {
|
||||||
getLog(row.name).then((response) => {
|
getLog(row.name).then((response) => {
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
this.logdec = response.data.replace(/\n/gm, "<br/>")
|
this.logdec = response.data.replace(/\n/gm, "<br/>")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-track{
|
||||||
|
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.2);
|
||||||
|
background-color: #fefefe;
|
||||||
|
border-radius: 7px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb{
|
||||||
|
border-radius: 7px;
|
||||||
|
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.2);
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
.dialogDiv{
|
||||||
|
height: 70vh;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
.text {
|
.text {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
@ -281,6 +281,7 @@ export default {
|
||||||
display: table;
|
display: table;
|
||||||
content: "";
|
content: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
.clearfix:after {
|
.clearfix:after {
|
||||||
clear: both
|
clear: both
|
||||||
}
|
}
|
||||||
|
@ -290,8 +291,18 @@ export default {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
}
|
}
|
||||||
.row{border-bottom: 1px solid #dfe6ec;color: #909399;font-weight: 500;padding: 10px;}
|
|
||||||
|
.row {
|
||||||
|
border-bottom: 1px solid #dfe6ec;
|
||||||
|
color: #909399;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.rowlist {
|
.rowlist {
|
||||||
border-bottom: 1px solid #dfe6ec;color:#606266;font-weight: 500;padding: 10px;
|
border-bottom: 1px solid #dfe6ec;
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -17,13 +17,22 @@
|
||||||
<span>保存的图片</span>
|
<span>保存的图片</span>
|
||||||
<div id="res"></div>
|
<div id="res"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<button @click="takePhoto()">拍照</button>
|
||||||
|
<el-dialog :visible.sync="limitedPhoto" title="人脸登录">
|
||||||
|
<div class="testTracking">
|
||||||
|
<faceLogin name="faceLogin"></faceLogin>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import faceLogin from '@/views/testModel/faceLogin'
|
||||||
let preDrawAry = [];
|
let preDrawAry = [];
|
||||||
let img = new Image();
|
// let img = new Image();
|
||||||
export default {
|
export default {
|
||||||
|
components:{
|
||||||
|
faceLogin
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
canvas:null,
|
canvas:null,
|
||||||
|
@ -36,6 +45,7 @@
|
||||||
lineW:3,
|
lineW:3,
|
||||||
colorF:"#e42343",
|
colorF:"#e42343",
|
||||||
imgData:'',
|
imgData:'',
|
||||||
|
limitedPhoto:false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted(){
|
mounted(){
|
||||||
|
@ -52,25 +62,26 @@
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
_this.draw();
|
_this.draw();
|
||||||
},1000)
|
},1000)
|
||||||
/*
|
|
||||||
this.ctx .clearRect(0, 0, 700, 500);
|
|
||||||
let imgs = new Image();
|
|
||||||
imgs.src ="./../../assets/404_images/404.png";
|
|
||||||
setTimeout(function(){
|
|
||||||
this.draw();
|
|
||||||
this.ctx.drawImage(imgs,0,0,700,500);
|
|
||||||
},1000)*/
|
|
||||||
/* imgs.onload=function(){
|
|
||||||
this.ctx.drawImage(imgs,0,0,700,500);
|
|
||||||
this.ctx.closePath();
|
|
||||||
|
|
||||||
let pattern=this.ctx.createPattern(imgs,"no-repeat")//不加;号
|
|
||||||
this.ctx.fillStyle=pattern;
|
|
||||||
this.ctx.fillRect(0,0,700,500);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
takePhoto(){
|
||||||
|
this.limitedPhoto = true;
|
||||||
|
},
|
||||||
|
takeClick(){
|
||||||
|
let canvas = document.getElementById("myCanvas");
|
||||||
|
let context = canvas.getContext('2d');
|
||||||
|
let video = document.getElementById("myVideo");
|
||||||
|
context.drawImage(video,0,0,90,68);
|
||||||
|
let image = new Image();
|
||||||
|
image = canvas.toDataURL('image/png');
|
||||||
|
document.getElementById('res').innerHTML = '<img src="'+image+'">';
|
||||||
|
let imgData = {base64:image};
|
||||||
|
faceLogin(imgData).then((res) => {
|
||||||
|
if (res.code >= 200) {
|
||||||
|
this.$message.success("成功");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
draw(){
|
draw(){
|
||||||
debugger;
|
debugger;
|
||||||
let canvasImg = document.getElementById("canvasImg");
|
let canvasImg = document.getElementById("canvasImg");
|
||||||
|
@ -238,24 +249,6 @@
|
||||||
var image = new Image();
|
var image = new Image();
|
||||||
image = canvas.toDataURL('image/png');
|
image = canvas.toDataURL('image/png');
|
||||||
document.getElementById('res').innerHTML = '<img style="border: 1px solid #666666;" src="'+image+'">';
|
document.getElementById('res').innerHTML = '<img style="border: 1px solid #666666;" src="'+image+'">';
|
||||||
console.log(image);
|
|
||||||
debugger;
|
|
||||||
/*img.setAttribute('crossOrigin', 'Anonymous') ;// 解决某些图片跨域(有些图片仍不可使用)
|
|
||||||
img.src = 'url';
|
|
||||||
img.onload = (imgs) => {
|
|
||||||
let base64 = this.setBase64(imgs);
|
|
||||||
var arr = base64.split(',');
|
|
||||||
var mime = arr[0].match(/:(.*?);/)[1] ;// 获取图片的类型 (image/jpg)
|
|
||||||
var bstr = atob(arr[1]); // 将base64转码
|
|
||||||
var n = bstr.length ;// 获得转码长度
|
|
||||||
var u8arr = new Uint8Array(n); // 获得length个为0的数组
|
|
||||||
while (n--) {
|
|
||||||
u8arr[n] = bstr.charCodeAt(n) // 获得unicode码
|
|
||||||
}
|
|
||||||
this.file = new File([u8arr], 'faceImg', { type: mime }); // 生成文件
|
|
||||||
console.log(this.file);
|
|
||||||
this.url = base64
|
|
||||||
}*/
|
|
||||||
},
|
},
|
||||||
setBase64(img) {
|
setBase64(img) {
|
||||||
let ctx = document.createElement('canvas');
|
let ctx = document.createElement('canvas');
|
||||||
|
|
|
@ -61,7 +61,9 @@
|
||||||
<el-table-column align="center" label="操作">
|
<el-table-column align="center" label="操作">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-link v-if="scope.row.state_.distribute_type==1&&scope.row.participant_type==2" type="danger" @click="handleGetTicket(scope)">接单</el-link>
|
<el-link v-if="scope.row.state_.distribute_type==1&&scope.row.participant_type==2" type="danger" @click="handleGetTicket(scope)">接单</el-link>
|
||||||
<el-link v-else-if="scope.row.act_state==1&&scope.row.participant_type!==2" type="primary" @click="handleDetail(scope)">处理</el-link>
|
<el-link v-else-if="(scope.row.act_state==1||scope.row.act_state==3)&&scope.row.participant_type!==2" type="primary" @click="handleDetail(scope)">处理</el-link>
|
||||||
|
<!--如果state_.retreat为可退回,则显示撤回按钮-->
|
||||||
|
<el-link v-if="scope.row.state_.enable_retreat&&userId==scope.row.create_by" type="danger" @click="handleRetreat(scope)">撤回</el-link>
|
||||||
<el-link type="success" @click="handlePicture(scope)">查看流程图</el-link>
|
<el-link type="success" @click="handlePicture(scope)">查看流程图</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
@ -147,9 +149,10 @@
|
||||||
<el-table-column label="创建时间" min-width="100" prop="create_time">
|
<el-table-column label="创建时间" min-width="100" prop="create_time">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" label="操作">
|
<el-table-column align="center" label="操作">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope" v-if="scope.row.state_">
|
||||||
<el-link v-if="scope.row.state_.distribute_type==1&&scope.row.participant_type==2" type="danger" @click="handleGetTicket(scope)">接单</el-link>
|
<el-link v-if="scope.row.state_.distribute_type==1&&scope.row.participant_type==2" type="danger" @click="handleGetTicket(scope)">接单</el-link>
|
||||||
<el-link v-else-if="scope.row.act_state==1&&scope.row.participant_type!==2" type="primary" @click="handleDetail(scope)">处理</el-link>
|
<el-link v-else-if="(scope.row.act_state==1||scope.row.act_state==3)&&scope.row.participant_type!==2" type="primary" @click="handleDetail(scope)">处理</el-link>
|
||||||
|
<el-link v-if="scope.row.state_.enable_retreat" type="danger" @click="handleRetreat(scope)">撤回</el-link>
|
||||||
<el-link type="success" @click="handlePicture(scope)">查看流程图</el-link>
|
<el-link type="success" @click="handlePicture(scope)">查看流程图</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
@ -225,120 +228,21 @@
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-dialog :visible.sync="limitedStep" title="工单处理">
|
<el-dialog :visible.sync="limitedRetreat" title="工单撤回">
|
||||||
<el-steps :active="actives" spac="400px" align-center="" style="padding-top: 20px;">
|
|
||||||
<el-step :title="item.name" v-for="item in flowSteps "
|
|
||||||
:key="item.id" @click.native=stepclick(item.id)>
|
|
||||||
</el-step>
|
|
||||||
</el-steps>
|
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-form ref="Form" :model="handleForm" label-width="100px" label-position="right" :rules="handleRule">
|
<el-form ref="Form" :model="handleForm" label-width="100px" label-position="right" :rules="handleRule">
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="标题" style="margin-bottom: 0">
|
|
||||||
<span>{{ticketDetail.title}}</span>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="工作流" style="margin-bottom: 0">
|
|
||||||
<span v-if="ticketDetail.workflow_">{{ticketDetail.workflow_.name}}</span>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="流水号" style="margin-bottom: 0">
|
|
||||||
<span>{{ticketDetail.sn}}</span>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<el-form-item label="创建时间" style="margin-bottom: 0">
|
|
||||||
<span>{{ticketDetail.create_time}}</span>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col v-for="item in ticketDetail.ticket_data_" :key="item.id" :span="12">
|
|
||||||
<el-form-item :label="item.field_name" v-if="item.field_state==='1'" style="margin-bottom: 0">
|
|
||||||
<span>{{ticketDetail.ticket_data[item.field_key]}}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="item.field_name" v-else>
|
|
||||||
<template v-if="item.field_type=='string'">
|
|
||||||
<el-input v-model="ticketDetail.ticket_data[item.field_key]" :placeholder="item.description"/>
|
|
||||||
</template>
|
|
||||||
<template v-if="item.field_type==='int'">
|
|
||||||
<el-input v-model="ticketDetail.ticket_data[item.field_key]" type="number" :placeholder="item.description" oninput="value=value.replace(/[^\d]/g,'')" />
|
|
||||||
</template>
|
|
||||||
<template v-if="item.field_type==='float'">
|
|
||||||
<el-input v-model="ticketDetail.ticket_data[item.field_key]" type="number" :placeholder="item.description" />
|
|
||||||
</template>
|
|
||||||
<template v-if="item.field_type==='date'">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="ticketDetail.ticket_data[item.field_key]"
|
|
||||||
type="date"
|
|
||||||
placeholder="选择日期"
|
|
||||||
value-format="yyyy-MM-dd"
|
|
||||||
style="width: 100%"
|
|
||||||
>
|
|
||||||
</el-date-picker>
|
|
||||||
</template>
|
|
||||||
<template v-if="item.field_type==='datetime'">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="ticketDetail.ticket_data[item.field_key]"
|
|
||||||
type="datetime"
|
|
||||||
placeholder="选择日期"
|
|
||||||
value-format="yyyy-MM-dd HH:mm:ss"
|
|
||||||
style="width: 100%"
|
|
||||||
>
|
|
||||||
</el-date-picker>
|
|
||||||
</template>
|
|
||||||
<template v-if="item.field_type==='select'">
|
|
||||||
<el-select style="width: 100%" v-model="ticketDetail.ticket_data[item.field_key]" placeholder="请选择">
|
|
||||||
<el-option
|
|
||||||
v-for="item1 in item.field_choice"
|
|
||||||
:key="item1"
|
|
||||||
:label="item1"
|
|
||||||
:value="item1"
|
|
||||||
>
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</template>
|
|
||||||
<template v-if="item.field_type==='selects'">
|
|
||||||
<el-select style="width: 100%" multiple v-model="ticketDetail.ticket_data[item.field_key]" placeholder="请选择">
|
|
||||||
<el-option
|
|
||||||
v-for="item1 in item.field_choice"
|
|
||||||
:key="item1"
|
|
||||||
:label="item1"
|
|
||||||
:value="item1"
|
|
||||||
>
|
|
||||||
</el-option>
|
|
||||||
</el-select>
|
|
||||||
</template>
|
|
||||||
<template v-if="item.field_type==='textarea'">
|
|
||||||
<el-input type="textarea" :rows="3" v-model="ticketDetail.ticket_data[item.field_key]" 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-col>
|
|
||||||
<el-col>
|
|
||||||
<el-form-item label="审批意见">
|
|
||||||
<el-input v-model="handleForm.suggestion" placeholder="审批意见"/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
|
<el-col :span="2" style="height: 1px;"></el-col>
|
||||||
|
<el-col :span="20" style="margin:6vh 0">
|
||||||
|
<el-form-item label="撤回原因">
|
||||||
|
<el-input type="textarea" :rows="3" v-model="handleForm.suggestion" placeholder="撤回原因"/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-row>
|
</el-row>
|
||||||
<div style="text-align: center">
|
<div style="text-align: center">
|
||||||
<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 class="filter-item" type="" @click="retreatCancel">取消</el-button>
|
||||||
|
<el-button class="filter-item" type="primary" @click="retreatSubmit">确定</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<el-dialog :visible.sync="limitedAdd" title="新增工单">
|
<el-dialog :visible.sync="limitedAdd" title="新增工单">
|
||||||
|
@ -439,7 +343,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { upUrl, upHeaders } from "@/api/file";
|
import { upUrl, upHeaders } from "@/api/file";
|
||||||
import {getWorkflowList,getWfCustomfieldList,createTicket,getWfStateList,getTickets,ticketAccpet,getWfTransitionList,
|
import {getWorkflowList,getWfCustomfieldList,createTicket,getWfStateList,getTickets,ticketAccpet,getWfTransitionList,
|
||||||
ticketHandle,getWfFlowSteps,getTicketDetail,getTicketTransitions,getTicketFlowlog } from "@/api/workflow";
|
ticketHandle,getWfFlowSteps,getTicketDetail,getTicketTransitions,getTicketFlowlog,getTicketRetreat} from "@/api/workflow";
|
||||||
import Pagination from "@/components/Pagination";
|
import Pagination from "@/components/Pagination";
|
||||||
import dagreD3 from 'dagre-d3'
|
import dagreD3 from 'dagre-d3'
|
||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3'
|
||||||
|
@ -467,8 +371,6 @@
|
||||||
transition:''
|
transition:''
|
||||||
},
|
},
|
||||||
handleForm:{
|
handleForm:{
|
||||||
transition:'',
|
|
||||||
ticket_data:{},
|
|
||||||
suggestion:'',
|
suggestion:'',
|
||||||
},
|
},
|
||||||
handleRule:{},
|
handleRule:{},
|
||||||
|
@ -486,6 +388,7 @@
|
||||||
listLoading:false,
|
listLoading:false,
|
||||||
limitedStep:false,
|
limitedStep:false,
|
||||||
limitedAdd:false,
|
limitedAdd:false,
|
||||||
|
limitedRetreat:false,
|
||||||
nodes: [],
|
nodes: [],
|
||||||
tooltip:null,
|
tooltip:null,
|
||||||
edges: [],
|
edges: [],
|
||||||
|
@ -504,6 +407,7 @@
|
||||||
},
|
},
|
||||||
mounted(){
|
mounted(){
|
||||||
debugger;
|
debugger;
|
||||||
|
this.userId = sessionStorage.getItem('userId')
|
||||||
this.workflow = this.$route.params.workflow;
|
this.workflow = this.$route.params.workflow;
|
||||||
if(this.workflow){//有传参
|
if(this.workflow){//有传参
|
||||||
this.pageForm.workflow = parseInt(this.workflow);
|
this.pageForm.workflow = parseInt(this.workflow);
|
||||||
|
@ -659,6 +563,30 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
retreatCancel(){
|
||||||
|
this.limitedRetreat = false;
|
||||||
|
},
|
||||||
|
retreatSubmit(){
|
||||||
|
this.$confirm("确认撤回吗?", "温馨提示", {
|
||||||
|
confirmButtonText: "确认",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning",
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
getTicketRetreat(this.retreatId,this.handleForm).then(res=>{
|
||||||
|
this.limitedRetreat = false;
|
||||||
|
this.getList();
|
||||||
|
this.$message.success("成功");
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleRetreat(scope){
|
||||||
|
this.limitedRetreat = true;
|
||||||
|
this.retreatId = scope.row.id;
|
||||||
|
},
|
||||||
handlePicture(scope){
|
handlePicture(scope){
|
||||||
let that = this;
|
let that = this;
|
||||||
that.dialogVisible = true;
|
that.dialogVisible = true;
|
||||||
|
@ -863,15 +791,6 @@
|
||||||
handleDetail(scope){
|
handleDetail(scope){
|
||||||
this.$router.push({name:"ticketHandle",params:{ticketId:scope.row.id,workflow:scope.row.workflow}})
|
this.$router.push({name:"ticketHandle",params:{ticketId:scope.row.id,workflow:scope.row.workflow}})
|
||||||
},
|
},
|
||||||
operationSubmit(id){
|
|
||||||
this.handleForm.transition = id;
|
|
||||||
this.handleForm.ticket_data = this.ticketDetail.ticket_data;
|
|
||||||
ticketHandle(this.ticketId,this.handleForm).then(res=>{
|
|
||||||
if (res.data){
|
|
||||||
this.limitedStep = false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
//接单
|
//接单
|
||||||
handleGetTicket(scope){
|
handleGetTicket(scope){
|
||||||
let ticketId = scope.row.id;
|
let ticketId = scope.row.id;
|
||||||
|
|
|
@ -23,6 +23,7 @@ class EquipmentSerializer(ModelSerializer):
|
||||||
def get_step_(self, obj):
|
def get_step_(self, obj):
|
||||||
return Step.objects.filter(equipments=obj).values('id', 'name', 'number')
|
return Step.objects.filter(equipments=obj).values('id', 'name', 'number')
|
||||||
|
|
||||||
|
|
||||||
class EquipmentSimpleSerializer(ModelSerializer):
|
class EquipmentSimpleSerializer(ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Equipment
|
model = Equipment
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Generated by Django 3.2.6 on 2021-10-14 01:44
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('em', '0009_auto_20210916_1108'),
|
||||||
|
('mtm', '0021_auto_20211013_0856'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='subproduction',
|
||||||
|
name='process',
|
||||||
|
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='subproduction_process', to='mtm.process', verbose_name='隶属大工序'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='step',
|
||||||
|
name='equipments',
|
||||||
|
field=models.ManyToManyField(related_name='step_equips', to='em.Equipment', verbose_name='使用设备'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -64,7 +64,7 @@ class Step(CommonAModel):
|
||||||
number = models.CharField('步骤编号', max_length=100, null=True, blank=True)
|
number = models.CharField('步骤编号', max_length=100, null=True, blank=True)
|
||||||
instruction_content = models.TextField('相应操作指导', null=True, blank=True)
|
instruction_content = models.TextField('相应操作指导', null=True, blank=True)
|
||||||
sort = models.IntegerField('排序号', default=1)
|
sort = models.IntegerField('排序号', default=1)
|
||||||
equipments = models.ManyToManyField(Equipment, verbose_name='使用设备')
|
equipments = models.ManyToManyField(Equipment, verbose_name='使用设备', related_name='step_equips')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = '工序步骤'
|
verbose_name = '工序步骤'
|
||||||
|
@ -134,6 +134,7 @@ class SubProduction(CommonAModel):
|
||||||
"""
|
"""
|
||||||
name = models.CharField('命名', max_length=50, null=True, blank=True)
|
name = models.CharField('命名', max_length=50, null=True, blank=True)
|
||||||
product = models.ForeignKey(Material, verbose_name='产品', on_delete=models.CASCADE)
|
product = models.ForeignKey(Material, verbose_name='产品', on_delete=models.CASCADE)
|
||||||
|
process = models.ForeignKey(Process, verbose_name='隶属大工序', on_delete=models.CASCADE, related_name='subproduction_process')
|
||||||
sort = models.IntegerField('排序号', default=1)
|
sort = models.IntegerField('排序号', default=1)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -17,8 +17,9 @@ class MaterialDetailSerializer(serializers.ModelSerializer):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
def get_processes_(self, obj):
|
def get_processes_(self, obj):
|
||||||
steps = UsedStep.objects.filter(subproduction__product=obj).values_list('step', flat=True)
|
# steps = UsedStep.objects.filter(subproduction__product=obj).values_list('step', flat=True)
|
||||||
objs = Process.objects.filter(step_process__id__in=steps).distinct().order_by('number')
|
# objs = Process.objects.filter(step_process__id__in=steps).distinct().order_by('number')
|
||||||
|
objs = Process.objects.filter(subproduction_process__product=obj, subproduction_process__is_deleted=False, is_deleted=False).order_by('number')
|
||||||
return ProcessSimpleSerializer(instance=objs, many=True).data
|
return ProcessSimpleSerializer(instance=objs, many=True).data
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@ class StepDetailSerializer(serializers.ModelSerializer):
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
class SubProductionSerializer(serializers.ModelSerializer):
|
class SubProductionSerializer(serializers.ModelSerializer):
|
||||||
|
process_ = ProcessSimpleSerializer(source='process', read_only=True)
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SubProduction
|
model = SubProduction
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
|
@ -74,8 +74,8 @@ class SubProductionViewSet(CreateUpdateModelAMixin, ModelViewSet):
|
||||||
产品生产分解增删改查
|
产品生产分解增删改查
|
||||||
"""
|
"""
|
||||||
perms_map={'*':'*'}
|
perms_map={'*':'*'}
|
||||||
queryset = SubProduction.objects.all()
|
queryset = SubProduction.objects.select_related('process').all()
|
||||||
filterset_fields = ['product']
|
filterset_fields = ['product', 'process']
|
||||||
search_fields = ['name']
|
search_fields = ['name']
|
||||||
serializer_class = SubProductionSerializer
|
serializer_class = SubProductionSerializer
|
||||||
ordering = ['sort']
|
ordering = ['sort']
|
||||||
|
|
|
@ -15,3 +15,10 @@ class ProductionPlanSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ProductionPlan
|
model = ProductionPlan
|
||||||
fields ='__all__'
|
fields ='__all__'
|
||||||
|
|
||||||
|
class ResourceCalSerializer(serializers.Serializer):
|
||||||
|
id = serializers.IntegerField(label='产品ID')
|
||||||
|
count = serializers.IntegerField(label='生产数量')
|
||||||
|
|
||||||
|
class ResourceCalListSerializer(serializers.ListSerializer):
|
||||||
|
child = ResourceCalSerializer()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from apps.pm.views import ProductionPlanViewSet
|
from apps.pm.views import ProductionPlanViewSet, ResourceViewSet
|
||||||
from django.db.models import base
|
from django.db.models import base
|
||||||
from rest_framework import urlpatterns
|
from rest_framework import urlpatterns
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
@ -6,7 +6,7 @@ from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('productionplan', ProductionPlanViewSet, basename='productionplan')
|
router.register('productionplan', ProductionPlanViewSet, basename='productionplan')
|
||||||
|
router.register('resource', ResourceViewSet, basename='resource')
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', include(router.urls)),
|
path('', include(router.urls)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
from apps.em.models import Equipment
|
||||||
|
from apps.em.serializers import EquipmentSerializer
|
||||||
|
from apps.mtm.models import InputMaterial, Step, SubProduction, UsedStep
|
||||||
from apps.system.mixins import CreateUpdateModelAMixin
|
from apps.system.mixins import CreateUpdateModelAMixin
|
||||||
from apps.pm.serializers import ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer
|
from apps.pm.serializers import ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer
|
||||||
from rest_framework.mixins import CreateModelMixin, ListModelMixin
|
from rest_framework.mixins import CreateModelMixin, ListModelMixin
|
||||||
from apps.pm.models import ProductionPlan
|
from apps.pm.models import ProductionPlan
|
||||||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||||
|
@ -8,6 +11,7 @@ from django.shortcuts import render
|
||||||
from apps.sam.models import Order
|
from apps.sam.models import Order
|
||||||
from rest_framework.exceptions import APIException
|
from rest_framework.exceptions import APIException
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.decorators import action
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
def updateOrderPlanedCount(order):
|
def updateOrderPlanedCount(order):
|
||||||
|
@ -52,5 +56,50 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
|
||||||
updateOrderPlanedCount(instance.order)
|
updateOrderPlanedCount(instance.order)
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
class ResourceCalculate(APIView):
|
class ResourceViewSet(GenericViewSet):
|
||||||
pass
|
|
||||||
|
perms_map = {'*': '*'}
|
||||||
|
@action(methods=['post'], detail=False, perms_map={'get':'*'}, serializer_class=ResourceCalListSerializer)
|
||||||
|
def cal(self, request, pk=None):
|
||||||
|
"""
|
||||||
|
物料消耗计算
|
||||||
|
"""
|
||||||
|
rdata = request.data
|
||||||
|
serializer = self.get_serializer(data=rdata)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
res_d_list = []
|
||||||
|
res = []
|
||||||
|
for i in rdata:
|
||||||
|
materials = InputMaterial.objects.filter(subproduction__product__id=i['id'],
|
||||||
|
subproduction__is_deleted=False, is_deleted=False, material__type__in=[3,4]).order_by('material__number')\
|
||||||
|
.values('material__id', 'material__name', 'material__number', 'material__type', 'count', 'material__count')
|
||||||
|
l_m = list(materials)
|
||||||
|
for m in l_m:
|
||||||
|
if m['material__id'] in res_d_list:
|
||||||
|
index = res_d_list.index(m['material__id'])
|
||||||
|
res[index]['count'] = res[index]['count'] + m['count']*i['count']
|
||||||
|
else:
|
||||||
|
res_d_list.append(m['material__id'])
|
||||||
|
res.append({'id':m['material__id'], 'name':m['material__name'],
|
||||||
|
'type':m['material__type'], 'number':m['material__number'],
|
||||||
|
'count':m['count']*i['count'], 'inv_count':m['material__count']})
|
||||||
|
return Response(res)
|
||||||
|
|
||||||
|
@action(methods=['post'], detail=False, perms_map={'get':'*'}, serializer_class=ResourceCalListSerializer)
|
||||||
|
def cal_equip(self, request, pk=None):
|
||||||
|
"""
|
||||||
|
设备状态查看
|
||||||
|
"""
|
||||||
|
rdata = request.data
|
||||||
|
serializer = self.get_serializer(data=rdata)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
rdata_l = []
|
||||||
|
for i in rdata:
|
||||||
|
rdata_l.append(i['id'])
|
||||||
|
subproductions = SubProduction.objects.filter(product__id__in=rdata_l, is_deleted=False)
|
||||||
|
steps = Step.objects.filter(usedsteps__is_deleted=False, usedsteps__subproduction__in=subproductions)
|
||||||
|
equips = Equipment.objects.filter(step_equips__in=steps, is_deleted=False)
|
||||||
|
serializer = EquipmentSerializer(instance=equips, many=True)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.2.6 on 2021-10-14 01:44
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('wf', '0012_ticketflow_intervene_type'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='ticketflow',
|
||||||
|
name='transition',
|
||||||
|
field=models.ForeignKey(blank=True, help_text='与worklow.Transition关联, 为空时表示认为干预的操作', null=True, on_delete=django.db.models.deletion.CASCADE, to='wf.transition', verbose_name='流转id'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -201,7 +201,7 @@ class TicketFlow(BaseModel):
|
||||||
工单流转日志
|
工单流转日志
|
||||||
"""
|
"""
|
||||||
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, verbose_name='关联工单', related_name='ticketflow_ticket')
|
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, verbose_name='关联工单', related_name='ticketflow_ticket')
|
||||||
transition = models.ForeignKey(Transition, verbose_name='流转id', help_text='与worklow.Transition关联, 为0时表示认为干预的操作', on_delete=models.CASCADE, null=True, blank=True)
|
transition = models.ForeignKey(Transition, verbose_name='流转id', help_text='与worklow.Transition关联, 为空时表示认为干预的操作', on_delete=models.CASCADE, null=True, blank=True)
|
||||||
suggestion = models.CharField('处理意见', max_length=10000, default='', blank=True)
|
suggestion = models.CharField('处理意见', max_length=10000, default='', blank=True)
|
||||||
participant_type = models.IntegerField('处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.state_participanttype_choices)
|
participant_type = models.IntegerField('处理人类型', default=0, help_text='0.无处理人,1.个人,2.多人', choices=State.state_participanttype_choices)
|
||||||
participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True, related_name='ticketflow_participant')
|
participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True, related_name='ticketflow_participant')
|
||||||
|
|
|
@ -130,3 +130,13 @@ class TicketHandleSerializer(serializers.Serializer):
|
||||||
|
|
||||||
class TicketRetreatSerializer(serializers.Serializer):
|
class TicketRetreatSerializer(serializers.Serializer):
|
||||||
suggestion = serializers.CharField(label="撤回原因", required = False)
|
suggestion = serializers.CharField(label="撤回原因", required = False)
|
||||||
|
|
||||||
|
class TicketCloseSerializer(serializers.Serializer):
|
||||||
|
suggestion = serializers.CharField(label="关闭原因", required = False)
|
||||||
|
|
||||||
|
class TicketAddNodeSerializer(serializers.Serializer):
|
||||||
|
suggestion = serializers.CharField(label="加签说明", required = False)
|
||||||
|
add_node_man = serializers.IntegerField(label='加签人')
|
||||||
|
|
||||||
|
class TicketAddNodeEndSerializer(serializers.Serializer):
|
||||||
|
suggestion = serializers.CharField(label="加签意见", required = False)
|
|
@ -33,6 +33,17 @@ class WfService(object):
|
||||||
except:
|
except:
|
||||||
raise Exception('工作流状态配置错误')
|
raise Exception('工作流状态配置错误')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_workflow_end_state(workflow:Workflow):
|
||||||
|
"""
|
||||||
|
获取工作流结束状态
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
wf_state_obj = State.objects.get(workflow=workflow, type=State.STATE_TYPE_END, is_deleted=False)
|
||||||
|
return wf_state_obj
|
||||||
|
except:
|
||||||
|
raise Exception('工作流状态配置错误')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_workflow_custom_fields(workflow:Workflow):
|
def get_workflow_custom_fields(workflow:Workflow):
|
||||||
"""
|
"""
|
||||||
|
@ -124,7 +135,7 @@ class WfService(object):
|
||||||
"""
|
"""
|
||||||
到达结束状态
|
到达结束状态
|
||||||
"""
|
"""
|
||||||
return dict(destination_participant_type=State.PARTICIPANT_TYPE_PERSONAL,
|
return dict(destination_participant_type=0,
|
||||||
destination_participant=0,
|
destination_participant=0,
|
||||||
multi_all_person={})
|
multi_all_person={})
|
||||||
multi_all_person_dict = {}
|
multi_all_person_dict = {}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
from apps.system.models import User
|
||||||
from apps.wf.filters import TicketFilterSet
|
from apps.wf.filters import TicketFilterSet
|
||||||
from django.core.exceptions import AppRegistryNotReady
|
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, TicketCreateSerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer
|
from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, TicketCreateSerializer, 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
|
||||||
|
@ -11,6 +12,7 @@ from apps.wf.models import CustomField, Ticket, Workflow, State, Transition, Tic
|
||||||
from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin, OptimizationMixin
|
from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin, OptimizationMixin
|
||||||
from apps.wf.services import WfService
|
from apps.wf.services import WfService
|
||||||
from rest_framework.exceptions import APIException, PermissionDenied
|
from rest_framework.exceptions import APIException, PermissionDenied
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
|
class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
|
||||||
|
@ -225,7 +227,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
||||||
ticket.act_state = Ticket.TICKET_ACT_STATE_ONGOING
|
ticket.act_state = Ticket.TICKET_ACT_STATE_ONGOING
|
||||||
|
|
||||||
if transition.attribute_type == Transition.TRANSITION_ATTRIBUTE_TYPE_REFUSE:
|
if transition.attribute_type == Transition.TRANSITION_ATTRIBUTE_TYPE_REFUSE:
|
||||||
transition.act_state = Ticket.TICKET_ACT_STATE_BACK
|
ticket.act_state = Ticket.TICKET_ACT_STATE_BACK
|
||||||
|
|
||||||
# 只更新必填和可选的字段
|
# 只更新必填和可选的字段
|
||||||
for key, value in ticket.state.state_fields.items():
|
for key, value in ticket.state.state_fields.items():
|
||||||
|
@ -312,16 +314,66 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
||||||
participant=request.user, transition=None)
|
participant=request.user, transition=None)
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
@action(methods=['post'], detail=True, perms_map={'post':'*'})
|
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=TicketAddNodeSerializer)
|
||||||
def add_node(self, request, pk=None):
|
def add_node(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
加签
|
加签
|
||||||
"""
|
"""
|
||||||
|
ticket = self.get_object()
|
||||||
|
data = request.data
|
||||||
|
add_user = User.objects.get(pk=data['add_node_man'])
|
||||||
|
ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL
|
||||||
|
ticket.participant = add_user.id
|
||||||
|
ticket.in_add_node = True
|
||||||
|
ticket.add_node_man = request.user
|
||||||
|
ticket.save()
|
||||||
|
# 更新流转记录
|
||||||
|
suggestion = request.data.get('suggestion', '') # 加签说明
|
||||||
|
TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
|
||||||
|
suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_ADD_NODE,
|
||||||
|
participant=request.user, transition=None)
|
||||||
|
return Response()
|
||||||
|
|
||||||
|
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=TicketAddNodeEndSerializer)
|
||||||
|
def add_node_end(self, request, pk=None):
|
||||||
|
"""
|
||||||
|
加签完成
|
||||||
|
"""
|
||||||
|
ticket = self.get_object()
|
||||||
|
ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL
|
||||||
|
ticket.in_add_node = False
|
||||||
|
ticket.add_node_man = None
|
||||||
|
ticket.participant = ticket.add_node_man.id
|
||||||
|
ticket.save()
|
||||||
|
# 更新流转记录
|
||||||
|
suggestion = request.data.get('suggestion', '') # 加签意见
|
||||||
|
TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
|
||||||
|
suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_ADD_NODE_END,
|
||||||
|
participant=request.user, transition=None)
|
||||||
|
return Response()
|
||||||
|
|
||||||
|
|
||||||
|
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=TicketCloseSerializer)
|
||||||
def close(self, request, pk=None):
|
def close(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
关闭工单(超级管理员或者创建人在初始状态)
|
关闭工单(创建人在初始状态)
|
||||||
"""
|
"""
|
||||||
|
ticket = self.get_object()
|
||||||
|
if ticket.state.type == State.STATE_TYPE_START and ticket.create_by==request.user:
|
||||||
|
end_state = WfService.get_workflow_end_state(ticket.workflow)
|
||||||
|
ticket.state = end_state
|
||||||
|
ticket.participant_type = 0
|
||||||
|
ticket.participant = 0
|
||||||
|
ticket.act_state = Ticket.TICKET_ACT_STATE_CLOSED
|
||||||
|
ticket.save()
|
||||||
|
# 更新流转记录
|
||||||
|
suggestion = request.data.get('suggestion', '') # 关闭原因
|
||||||
|
TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
|
||||||
|
suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CLOSE,
|
||||||
|
participant=request.user, transition=None)
|
||||||
|
return Response()
|
||||||
|
else:
|
||||||
|
return Response('工单不可关闭', status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
||||||
|
|
|
@ -56,7 +56,7 @@ class FitJSONRenderer(JSONRenderer):
|
||||||
data = data[prefix]
|
data = data[prefix]
|
||||||
if isinstance(data, list):
|
if isinstance(data, list):
|
||||||
data = data[0]
|
data = data[0]
|
||||||
response_body.msg = prefix + ":" + data # 取一部分放入msg,方便前端alert
|
response_body.msg = prefix + ":" + str(data) # 取一部分放入msg,方便前端alert
|
||||||
else:
|
else:
|
||||||
response_body.data = data
|
response_body.data = data
|
||||||
renderer_context.get("response").status_code = 200 # 统一成200响应,用code区分
|
renderer_context.get("response").status_code = 200 # 统一成200响应,用code区分
|
||||||
|
|
Loading…
Reference in New Issue