Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop
This commit is contained in:
commit
7ed06d8b40
|
@ -13,7 +13,7 @@ module.exports = {
|
|||
|
||||
// add your custom rules here
|
||||
//it is base on https://github.com/vuejs/eslint-config-vue
|
||||
rules: {
|
||||
/* rules: {
|
||||
"vue/max-attributes-per-line": [2, {
|
||||
"singleline": 10,
|
||||
"multiline": {
|
||||
|
@ -194,5 +194,5 @@ module.exports = {
|
|||
objectsInObjects: false
|
||||
}],
|
||||
'array-bracket-spacing': [2, 'never']
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"nprogress": "0.2.0",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
"vue": "^2.6.14",
|
||||
"vue-json-editor": "^1.4.3",
|
||||
"vue-router": "^3.5.2",
|
||||
"vuex": "^3.6.2",
|
||||
"xlsx": "^0.17.1"
|
||||
|
|
|
@ -45,6 +45,11 @@ import Breadcrumb from '@/components/Breadcrumb'
|
|||
import Hamburger from '@/components/Hamburger'
|
||||
|
||||
export default {
|
||||
data(){
|
||||
return{
|
||||
name:''
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Breadcrumb,
|
||||
Hamburger
|
||||
|
|
|
@ -339,6 +339,27 @@ export const asyncRoutes = [
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/testModel',
|
||||
component: Layout,
|
||||
redirect: '/testModel/faceLogin/',
|
||||
name: 'testModel',
|
||||
meta: { title: '测试模块', icon: 'example' },
|
||||
children: [
|
||||
{
|
||||
path: 'faceLogin',
|
||||
name: 'faceLogin',
|
||||
component: () => import('@/views/testModel/faceLogin'),
|
||||
meta: { title: '人脸识别登录', icon: 'example' }
|
||||
},
|
||||
{
|
||||
path: 'markImage',
|
||||
name: 'markImage',
|
||||
component: () => import('@/views/testModel/markImage'),
|
||||
meta: { title: '图片标记', icon: 'example' }
|
||||
}
|
||||
]
|
||||
},
|
||||
// 404 page must be placed at the end !!!
|
||||
{ path: '*', redirect: '/404', hidden: true }
|
||||
]
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: "Login",
|
||||
data() {
|
||||
// const validateUsername = (rule, value, callback) => {
|
||||
|
@ -160,29 +160,29 @@ export default {
|
|||
this.loginForm.password = localStorage.getItem("rem_password");
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.login {
|
||||
.login {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
background-image: url("../../assets/beijing.jpg");
|
||||
background-size: cover;
|
||||
}
|
||||
.title {
|
||||
}
|
||||
.title {
|
||||
margin: 30px auto 30px auto;
|
||||
text-align: center;
|
||||
color: #0174d7;
|
||||
}
|
||||
.logo {
|
||||
}
|
||||
.logo {
|
||||
width: 350px;
|
||||
height: 140px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-form {
|
||||
.login-form {
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
width: 400px;
|
||||
|
@ -198,18 +198,18 @@ export default {
|
|||
width: 14px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.show-pwd {
|
||||
.show-pwd {
|
||||
height: 39px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.login-tip {
|
||||
}
|
||||
.login-tip {
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
color: #bfbfbf;
|
||||
}
|
||||
.login-code {
|
||||
}
|
||||
.login-code {
|
||||
width: 33%;
|
||||
height: 38px;
|
||||
float: right;
|
||||
|
@ -217,16 +217,16 @@ export default {
|
|||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
.show-pwd {
|
||||
}
|
||||
.show-pwd {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 7px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.login-code-img {
|
||||
}
|
||||
.login-code-img {
|
||||
height: 38px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
<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">拍照</Button>
|
||||
<div id="res"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import "tracking/build/data/face-min.js";
|
||||
// import "tracking/build/data/mouth-min.js";
|
||||
// import "tracking/build/data/tracking-min.js";
|
||||
// import "tracking/examples/assets/stats.min.js";
|
||||
// import "tracking/examples/assets/stats.min.js";
|
||||
export default {
|
||||
props:['src'],
|
||||
data () {
|
||||
return {
|
||||
videoWidth: 900,
|
||||
videoHeight: 700,
|
||||
videoArr:[],//所有的摄像头
|
||||
modelSel:'',//
|
||||
myInterval: null,
|
||||
imgSrc: '',
|
||||
isHasFace:false,//默认没有人脸
|
||||
tracker:null,
|
||||
}
|
||||
},
|
||||
created(){
|
||||
},
|
||||
mounted(){
|
||||
// this.openUserMedia();
|
||||
this.callCamera();
|
||||
this.changePhoto();
|
||||
// this.checkFace();
|
||||
if(this.src!='user'){
|
||||
// this.myInterval = setInterval(()=>{
|
||||
// this.setImage();
|
||||
// },2000)
|
||||
}
|
||||
},
|
||||
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');
|
||||
var video = document.getElementById("myVideo");
|
||||
context.drawImage(video,0,0,this.videoWidth,this.videoHeight);
|
||||
var image = new Image();
|
||||
image = canvas.toDataURL('image/png');
|
||||
document.getElementById('res').innerHTML = '<img style="border: 1px solid #666666;" src="'+image+'">';
|
||||
},
|
||||
// 关闭摄像头
|
||||
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);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 检查取景框是否有人脸
|
||||
* */
|
||||
checkFace() {
|
||||
// var video = document.getElementById("video");
|
||||
let canvas = document.getElementById("myCanvas");
|
||||
let context = canvas.getContext('2d');
|
||||
this.tracker = new tracking.ObjectTracker("face");
|
||||
this.tracker.setInitialScale(4);
|
||||
this.tracker.setStepSize(2);
|
||||
this.tracker.setEdgesDensity(0.1);
|
||||
this.trackerTask = tracking.track("#video", this.tracker, { camera: true });
|
||||
let self = this;
|
||||
this.tracker.on("track", event=> {
|
||||
if (event.data.length <= 0) {
|
||||
self.setFace(false);
|
||||
return;
|
||||
}
|
||||
self.setFace(true);
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
event.data.forEach(function (rect) {
|
||||
context.strokeStyle = '#a64ceb';
|
||||
context.strokeRect(rect.x, rect.y, rect.width, rect.height);
|
||||
context.font = '11px Helvetica';
|
||||
context.fillStyle = "#fff";
|
||||
context.fillText('x: ' + rect.x + 'px', rect.x + rect.width + 5, rect.y + 11);
|
||||
context.fillText('y: ' + rect.y + 'px', rect.x + rect.width + 5, rect.y + 22);
|
||||
});
|
||||
});
|
||||
},
|
||||
setFace(data){
|
||||
this.isHasFace = data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
beforeDestroy () {
|
||||
clearInterval(this.myInterval);
|
||||
// 停止侦测
|
||||
// this.trackerTask.stop();
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.testTracking {
|
||||
min-height: 700px;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.buttonDiv {
|
||||
bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
<template>
|
||||
<div>
|
||||
<button type="button" class="btn btn-warning" @click="error1">错误</button>
|
||||
<button @click="word1()">文字</button>
|
||||
<button @click="restuya()">清除批注</button>
|
||||
<button @click="back()">回退</button>
|
||||
<button @click="saveTu()">保存</button>
|
||||
<hr>
|
||||
<!--<img src="./../../assets/404_images/404.png">-->
|
||||
<div style="position: relative;">
|
||||
<canvas id="canvas" width="700" height="500" >
|
||||
您的浏览器不支持绘图,请升级或更换浏览器!
|
||||
</canvas>
|
||||
<input type="text" value="" class="hide" id="txt" placeholder="请输入文字">
|
||||
<textarea class="hide" id="word" cols="30" rows="10" placeholder="请输入文字" autofocus></textarea>
|
||||
<input id="inputV" type="hidden" value="1">
|
||||
<span>保存的图片</span>
|
||||
<div id="res"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
let preDrawAry = [];
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
canvas:null,
|
||||
ctx:null,
|
||||
myCanvas_rect:null,
|
||||
Txt:null,
|
||||
word:null,
|
||||
widths:0,
|
||||
heights:0,
|
||||
lineW:3,
|
||||
colorF:"#e42343",
|
||||
imgData:'',
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.canvas = document.getElementById('canvas');
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
this.myCanvas_rect = this.canvas.getBoundingClientRect();
|
||||
this.Txt = document.getElementById('txt');
|
||||
this.word = document.getElementById('word');
|
||||
this.widths = this.myCanvas_rect.width;
|
||||
this.heights = this.myCanvas_rect.height;
|
||||
let imgs = new Image();
|
||||
imgs.src = "./../../assets/404_images/404.png";
|
||||
this.ctx.drawImage(imgs,0,0,this.widths,this.heights);
|
||||
preDrawAry = [];
|
||||
},
|
||||
methods: {
|
||||
// 叉号
|
||||
error1(){
|
||||
let canvas1 = document.getElementById('canvas');
|
||||
let ctx1 = canvas1.getContext('2d');
|
||||
this.Txt.style.display="none";
|
||||
this.word.style.display="none";
|
||||
ctx1.closePath();
|
||||
this.canvas.onmousedown=function () {
|
||||
this.imgData= ctx1.getImageData(0,0,canvas1.width,canvas1.height);
|
||||
preDrawAry.push(this.imgData);
|
||||
ctx1.beginPath();
|
||||
ctx1.strokeStyle = "#e42343";
|
||||
ctx1.lineWidth = "3";
|
||||
ctx1.lineJoin="round";
|
||||
};
|
||||
//鼠标按下的位置
|
||||
this.canvas.onmouseup=function (ev) {
|
||||
let oldX = ev.offsetX;
|
||||
let oldY = ev.offsetY;
|
||||
ctx1.moveTo(oldX,oldY);
|
||||
ctx1.lineTo(ev.offsetX+10,ev.offsetY+10);
|
||||
ctx1.moveTo(ev.offsetX+10,ev.offsetY);
|
||||
ctx1.lineTo(ev.offsetX,ev.offsetY+10);
|
||||
ctx1.stroke();
|
||||
};
|
||||
this.ctx.closePath();
|
||||
},
|
||||
|
||||
// 文字先写字
|
||||
text(){
|
||||
let canvas2 = document.getElementById('canvas');
|
||||
let ctx2 = canvas2.getContext('2d');
|
||||
let Txt2 = document.getElementById('txt');
|
||||
Txt2.style.display="block";
|
||||
this.word.style.display="none";
|
||||
ctx2.font="16px Microsoft Yahei";
|
||||
canvas2.onmousedown=function (ev) {
|
||||
this.imgData=ctx2.getImageData(0,0,canvas2.width,canvas2.height);
|
||||
preDrawAry.push(this.imgData);
|
||||
var v = Txt2.value;
|
||||
// console.log(v);
|
||||
if (v != '') {
|
||||
var oldX = ev.offsetX;
|
||||
var oldY = ev.offsetY;
|
||||
// console.log(oldX,oldY);
|
||||
ctx2.moveTo(oldX,oldY);
|
||||
canvas2.onmouseup=function () {
|
||||
ctx2.fillStyle=this.colorF;
|
||||
ctx2.fillText(v,oldX,oldY);
|
||||
canvas2.TextAutoLine(v,canvas2,oldX,oldY,20);
|
||||
// Txt.value = "";
|
||||
Txt2.style.display="none";
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 文字
|
||||
word1(){
|
||||
let canvas3 = document.getElementById('canvas');
|
||||
let ctx3 = canvas3.getContext('2d');
|
||||
let Txt3 = document.getElementById('txt');
|
||||
let word3 = document.getElementById('word');
|
||||
Txt3.style.display="none";
|
||||
ctx3.font="16px Microsoft Yahei";
|
||||
canvas3.onmousedown=function () {
|
||||
ctx3.closePath();
|
||||
}
|
||||
canvas3.onmouseup=function (ev) {
|
||||
var inputV= document.getElementById('inputV').value;
|
||||
if(inputV == 1){
|
||||
document.getElementById('word').focus();
|
||||
// console.log(ev.offsetX,ev.offsetY);
|
||||
var oldX = ev.offsetX;
|
||||
var oldY = ev.offsetY;
|
||||
word3.style.display="block";
|
||||
word3.style.left=oldX+'px';
|
||||
word3.style.top =oldY+'px';
|
||||
|
||||
word3.onblur=function () {
|
||||
let v = word3.value;
|
||||
if(v != '' && v != ' '){
|
||||
this.imgData=ctx3.getImageData(0,0,canvas3.width,canvas3.height);
|
||||
let img = ctx3.getImageData(0,0,canvas3.width,canvas3.height);
|
||||
debugger;
|
||||
preDrawAry.push(img);
|
||||
ctx3.moveTo(oldX,oldY);
|
||||
ctx3.fillStyle="#e42343";
|
||||
// ctx3.fillText(v,oldX,oldY+10);
|
||||
// this.canvasTextAutoLine(v,canvas3,oldX,oldY,20);
|
||||
|
||||
let lineWidth = 0;
|
||||
let canvasWidth = canvas3.width;
|
||||
let lastSubStrIndex= 0;
|
||||
for(let i=0;i<v.length;i++){
|
||||
lineWidth+=ctx3.measureText(v[i]).width;
|
||||
if(lineWidth>canvasWidth-oldX){
|
||||
ctx3.fillText(v.substring(lastSubStrIndex,i),oldX,oldY);
|
||||
oldY+=20;
|
||||
lineWidth=0;
|
||||
lastSubStrIndex=i;
|
||||
}
|
||||
if(i==v.length-1){
|
||||
ctx3.fillText(v.substring(lastSubStrIndex,i+1),oldX,oldY);
|
||||
}
|
||||
}
|
||||
inputV ="2";
|
||||
word3.value = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 文字过长超出换行toDataURL()
|
||||
canvasTextAutoLine(str,canvas,initX,initY,lineHeight){
|
||||
let ctx = canvas.getContext("2d");
|
||||
let lineWidth = 0;
|
||||
let canvasWidth = canvas.width;
|
||||
let lastSubStrIndex= 0;
|
||||
for(let i=0;i<str.length;i++){
|
||||
lineWidth+=ctx.measureText(str[i]).width;
|
||||
if(lineWidth>canvasWidth-initX){
|
||||
ctx.fillText(str.substring(lastSubStrIndex,i),initX,initY);
|
||||
initY+=lineHeight;
|
||||
lineWidth=0;
|
||||
lastSubStrIndex=i;
|
||||
}
|
||||
if(i==str.length-1){
|
||||
ctx.fillText(str.substring(lastSubStrIndex,i+1),initX,initY);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 删除批注
|
||||
restuya(){
|
||||
this.word.style.display="none";
|
||||
this.Txt.style.display="none";
|
||||
this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);
|
||||
window.location.reload();
|
||||
},
|
||||
|
||||
// 撤销
|
||||
back(){
|
||||
this.word.style.display="none";
|
||||
this.Txt.style.display="none";
|
||||
if(preDrawAry.length>0) {
|
||||
// console.log(this.preDrawAry)
|
||||
var popData = preDrawAry.pop();
|
||||
this.ctx.putImageData(popData, 0, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// 回退一次
|
||||
put(){
|
||||
this.ctx.putImageData(this.imgData,0,0);
|
||||
},
|
||||
|
||||
//保存
|
||||
saveTu(){
|
||||
var image = new Image();
|
||||
image = this.canvas.toDataURL('image/png');
|
||||
document.getElementById('res').innerHTML = '<img style="border: 1px solid #666666;" src="'+image+'">';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
canvas{
|
||||
border:1px solid #000000;
|
||||
cursor: crosshair;
|
||||
}
|
||||
.hide{
|
||||
display: none;
|
||||
}
|
||||
#txt{
|
||||
position: absolute;
|
||||
top: 1%;
|
||||
left: 1%;
|
||||
width: 575px;
|
||||
height: 30px;
|
||||
border:1px solid #e42343;
|
||||
}
|
||||
#word{
|
||||
position: absolute;
|
||||
width: 220px;
|
||||
height: 80px;
|
||||
padding: 0 2px;
|
||||
background:none;
|
||||
color:#e42343;
|
||||
border: 1px dashed #b9b9b9;
|
||||
}
|
||||
#word::-webkit-input-placeholder{
|
||||
color:#e42343;
|
||||
}
|
||||
#word::-moz-placeholder{
|
||||
color:#e42343;
|
||||
}
|
||||
#word::placeholder{
|
||||
color:#e42343;
|
||||
}
|
||||
#res{
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
|
@ -18,7 +18,7 @@ class MaterialDetailSerializer(serializers.ModelSerializer):
|
|||
fields = '__all__'
|
||||
|
||||
def get_processes_(self, obj):
|
||||
objs = Process.objects.filter(id__in=obj.processes)
|
||||
objs = Process.objects.filter(id__in=obj.processes).order_by('number')
|
||||
return ProcessSimpleSerializer(instance=objs, many=True).data
|
||||
|
||||
|
||||
|
|
|
@ -53,16 +53,16 @@ class ProcessViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet):
|
|||
ordering_fields = ['number']
|
||||
ordering = ['number']
|
||||
|
||||
@action(methods=['get'], detail=True, perms_map={'get':'process_update'}, pagination_class=None, serializer_class=StepSerializer)
|
||||
@action(methods=['get'], detail=True, perms_map={'get':'process_update'}, pagination_class=None, serializer_class=StepDetailSerializer)
|
||||
def steps(self, request, pk=None):
|
||||
"""
|
||||
工序下的子工序
|
||||
"""
|
||||
process = self.get_object()
|
||||
serializer = self.serializer_class(instance=Step.objects.filter(process=process, is_deleted=False), many=True)
|
||||
serializer = self.serializer_class(instance=Step.objects.prefetch_related('equipments').filter(process=process, is_deleted=False), many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
class StepViewSet(CreateUpdateModelAMixin, CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
|
||||
class StepViewSet(OptimizationMixin, CreateUpdateModelAMixin, CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
|
||||
"""
|
||||
子工序-增删改查
|
||||
"""
|
||||
|
@ -157,6 +157,15 @@ class RecordFormViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet
|
|||
return RecordFormUpdateSerializer
|
||||
return RecordFormSerializer
|
||||
|
||||
@action(methods=['get'], detail=True, perms_map={'get':'*'}, pagination_class=None, serializer_class=RecordFormFieldSerializer)
|
||||
def fields(self, request, pk=None):
|
||||
"""
|
||||
表格下的字段
|
||||
"""
|
||||
instance = self.get_object()
|
||||
serializer = self.serializer_class(instance=RecordFormField.objects.filter(form=instance, is_deleted=False), many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class RecordFormFieldViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet):
|
||||
"""
|
||||
|
|
|
@ -79,6 +79,9 @@ class Transition(CommonAModel):
|
|||
"""
|
||||
工作流流转,定时器,条件(允许跳过), 条件流转与定时器不可同时存在
|
||||
"""
|
||||
TRANSITION_ATTRIBUTE_TYPE_ACCEPT = 1 # 同意
|
||||
TRANSITION_ATTRIBUTE_TYPE_REFUSE = 2 # 拒绝
|
||||
TRANSITION_ATTRIBUTE_TYPE_OTHER = 3 # 其他
|
||||
attribute_type_choices = (
|
||||
(1, '同意'),
|
||||
(2, '拒绝'),
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from apps.system.serializers import UserSimpleSerializer
|
||||
import rest_framework
|
||||
from rest_framework import serializers
|
||||
|
||||
from .models import State, Ticket, Workflow, Transition, CustomField
|
||||
from .models import State, Ticket, TicketFlow, Workflow, Transition, CustomField
|
||||
|
||||
|
||||
class WorkflowSerializer(serializers.ModelSerializer):
|
||||
|
@ -42,6 +43,10 @@ class CustomFieldSerializer(serializers.ModelSerializer):
|
|||
model = CustomField
|
||||
fields = '__all__'
|
||||
|
||||
class TicketSimpleSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Ticket
|
||||
fields = '__all__'
|
||||
|
||||
class TicketCreateSerializer(serializers.ModelSerializer):
|
||||
transition = serializers.IntegerField(label='流转ID')
|
||||
|
@ -65,3 +70,12 @@ class TicketSerializer(serializers.ModelSerializer):
|
|||
def setup_eager_loading(queryset):
|
||||
queryset = queryset.select_related('workflow','state')
|
||||
return queryset
|
||||
|
||||
class TicketFlowSerializer(serializers.ModelSerializer):
|
||||
participant_ = UserSimpleSerializer(source='participant', read_only=True)
|
||||
state_ = StateSimpleSerializer(source='state', read_only=True)
|
||||
class Meta:
|
||||
model = TicketFlow
|
||||
fields = '__all__'
|
||||
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
from apps.wf.serializers import TicketSerializer, TicketSimpleSerializer
|
||||
from typing import Tuple
|
||||
from apps.system.models import User
|
||||
from apps.wf.models import CustomField, State, Ticket, Transition, Workflow
|
||||
|
@ -198,7 +199,7 @@ class WfService(object):
|
|||
:return:
|
||||
"""
|
||||
# 获取工单基础表中的字段中的字段信息
|
||||
field_info_dict = ticket.get_dict()
|
||||
field_info_dict = TicketSimpleSerializer(instance=ticket).data
|
||||
# 获取自定义字段的值
|
||||
custom_fields_queryset = cls.get_workflow_custom_fields(ticket.workflow)
|
||||
for i in custom_fields_queryset:
|
||||
|
|
|
@ -2,7 +2,7 @@ from django.core.exceptions import AppRegistryNotReady
|
|||
from rest_framework.response import Response
|
||||
from rest_framework import serializers
|
||||
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
|
||||
from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer
|
||||
from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketCreateSerializer, TicketFlowSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||
from rest_framework.decorators import action, api_view
|
||||
|
@ -116,7 +116,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
if value == State.STATE_FIELD_REQUIRED:
|
||||
if key not in ticket_data or not ticket_data[key]:
|
||||
raise APIException('字段{}必填'.format(key))
|
||||
ticket = serializer.save(state=start_state) # 先创建出来
|
||||
ticket = serializer.save(state=start_state, create_by=request.user) # 先创建出来
|
||||
next_state = WfService.get_next_state_by_transition_and_ticket_info(ticket=ticket, transition=transition)
|
||||
participant_info = WfService.get_ticket_state_participant_info(state=next_state, ticket=ticket, ticket_data=ticket.ticket_data)
|
||||
destination_participant_type = participant_info.get('destination_participant_type', 0)
|
||||
|
@ -134,7 +134,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
if title_template:
|
||||
all_ticket_data = {**rdata, **rdata['ticket_data']}
|
||||
title = title_template.format(**all_ticket_data)
|
||||
# 更新工单
|
||||
# 更新一下信息
|
||||
ticket.sn=sn
|
||||
ticket.title=title
|
||||
ticket.state=next_state
|
||||
|
@ -146,7 +146,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
# 新增流转记录
|
||||
TicketFlow.objects.create(ticket=ticket, state=start_state, ticket_data=WfService.get_ticket_all_field_value(ticket),
|
||||
suggestion=rdata.get('suggestion',''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
|
||||
participant=ticket.create_by, transition=transition, create_by=request.user)
|
||||
participant=ticket.create_by, transition=transition)
|
||||
return Response(TicketSerializer(instance=ticket).data)
|
||||
|
||||
|
||||
|
@ -204,14 +204,14 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
ticket.participant = destination_participant
|
||||
ticket.multi_all_person = multi_all_person
|
||||
if destination_state.type == State.STATE_TYPE_END:
|
||||
ticket.act_state = State.TICKET_ACT_STATE_FINISH
|
||||
ticket.act_state = Ticket.TICKET_ACT_STATE_FINISH
|
||||
elif destination_state.type == State.STATE_TYPE_START:
|
||||
ticket.act_state = State.TICKET_ACT_STATE_DRAFT
|
||||
ticket.act_state = Ticket.TICKET_ACT_STATE_DRAFT
|
||||
else:
|
||||
ticket.act_state = State.TICKET_ACT_STATE_ONGOING
|
||||
ticket.act_state = Ticket.TICKET_ACT_STATE_ONGOING
|
||||
|
||||
if transition.attribute_type == State.TRANSITION_ATTRIBUTE_TYPE_REFUSE:
|
||||
transition.act_state = State.TICKET_ACT_STATE_BACK
|
||||
if transition.attribute_type == Transition.TRANSITION_ATTRIBUTE_TYPE_REFUSE:
|
||||
transition.act_state = Ticket.TICKET_ACT_STATE_BACK
|
||||
|
||||
# 只更新必填和可选的字段
|
||||
for key, value in ticket.state.state_fields.items():
|
||||
|
@ -223,7 +223,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
# 更新工单流转记录
|
||||
TicketFlow.objects.create(ticket=ticket, state=source_state, ticket_data=WfService.get_ticket_all_field_value(ticket),
|
||||
suggestion=data.get('suggestion',''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
|
||||
participant=request.user, transition=transition, create_by=request.user)
|
||||
participant=request.user, transition=transition)
|
||||
return Response(TicketSerializer(instance=ticket).data)
|
||||
|
||||
|
||||
|
@ -244,3 +244,15 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
ticket = self.get_object()
|
||||
transitions = WfService.get_ticket_transitions(ticket)
|
||||
return Response(TransitionSerializer(instance=transitions, many=True).data)
|
||||
|
||||
|
||||
class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
||||
"""
|
||||
工单日志
|
||||
"""
|
||||
perms_map = {'*':'*'}
|
||||
queryset = TicketFlow.objects.all()
|
||||
serializer_class = TicketFlowSerializer
|
||||
search_fields = ['suggestion']
|
||||
filterset_fields = ['paticipant', 'state', 'ticket']
|
||||
ordering = ['-create_time']
|
Loading…
Reference in New Issue