Merge branch 'develop' of https://e.coding.net/ctcdevteam/hberp/hberp into develop
This commit is contained in:
commit
c378f99965
|
|
@ -3,4 +3,5 @@ ENV = 'production'
|
|||
|
||||
# base api
|
||||
VUE_APP_BASE_API = 'http://47.95.0.242:2222/api'
|
||||
#VUE_APP_BASE_API = 'http://127.0.0.1:8000/api'
|
||||
|
||||
|
|
|
|||
|
|
@ -14,9 +14,19 @@ export default {
|
|||
},
|
||||
data(){
|
||||
return{
|
||||
isRouterAlive:true
|
||||
isRouterAlive:true,
|
||||
timer:null
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
// this.$store.dispatch("user/getCount", {})
|
||||
this.timer = window.setInterval(() => {
|
||||
setTimeout(() => {
|
||||
this.$store.dispatch("user/getCount", {})
|
||||
},0)
|
||||
},30000)
|
||||
|
||||
},
|
||||
methods:{
|
||||
reload(){
|
||||
this.isRouterAlive=false;
|
||||
|
|
@ -24,6 +34,9 @@ export default {
|
|||
this.isRouterAlive=true;
|
||||
})
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
clearInterval(this.timer)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -36,4 +49,9 @@ export default {
|
|||
.el-step__title.is-process{
|
||||
color: #409EFF;
|
||||
}
|
||||
.navbarBadge .el-badge__content.is-fixed{
|
||||
top: 15px;
|
||||
right: 18px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import request from '@/utils/request'
|
||||
export function faceLogin(data) {
|
||||
return request({
|
||||
url: '/system/facelogin/',
|
||||
url: '/hrm/facelogin/',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
|
|
|
|||
|
|
@ -163,13 +163,45 @@ export function ticketAccpet(id,data) {
|
|||
})
|
||||
}
|
||||
//撤回工单,允许创建人在指定状态撤回工单至初始状态
|
||||
export function getTicketRetreat(id,data) {
|
||||
export function ticketRetreat(id,data) {
|
||||
return request({
|
||||
url: `/wf/ticket/${id}/retreat/`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//关闭工单,仅允许创建人在初始状态关闭工单
|
||||
export function ticketAddNode(id,data) {
|
||||
return request({
|
||||
url: `/wf/ticket/${id}/add_node/`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//加签
|
||||
export function ticketClose(id,data) {
|
||||
return request({
|
||||
url: `/wf/ticket/${id}/close/`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//加签
|
||||
export function ticketAddNodeEnd(id,data) {
|
||||
return request({
|
||||
url: `/wf/ticket/${id}/add_node_end/`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//工单删除
|
||||
export function ticketDestory(data) {
|
||||
return request({
|
||||
url: `/wf/ticket/destory/`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//工单详情
|
||||
export function getTicketDetail(id) {
|
||||
return request({
|
||||
|
|
@ -193,3 +225,18 @@ export function getTicketFlowlog(id) {
|
|||
method: 'get'
|
||||
})
|
||||
}
|
||||
//工单代办数量
|
||||
export function getCount(data) {
|
||||
return request({
|
||||
url: `/wf/ticket/duty_agg/`,
|
||||
method: 'get',
|
||||
params:data
|
||||
})
|
||||
}
|
||||
//工单代办数量
|
||||
export function getCodes() {
|
||||
return request({
|
||||
url: `/wf/participant_from_code`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
<div class="right-menu">
|
||||
<template>
|
||||
<el-badge v-if="count.total_count>0" :value="count.total_count" class="item right-menu-item navbarBadge" @click.native="gotoTicketPage">
|
||||
<el-icon class="el-icon-s-management" style="font-size: 25px;color: #409EFF;padding-top: 12px;cursor: pointer;"></el-icon>
|
||||
</el-badge>
|
||||
<search id="header-search" class="right-menu-item" />
|
||||
<el-tooltip content="全局组件大小" effect="dark" placement="bottom">
|
||||
<size-select id="size-select" class="right-menu-item hover-effect" />
|
||||
|
|
@ -69,9 +72,18 @@ export default {
|
|||
'sidebar',
|
||||
'avatar',
|
||||
'name',
|
||||
'count',
|
||||
])
|
||||
},
|
||||
methods: {
|
||||
gotoTicketPage(){
|
||||
let path = this.$route.path;
|
||||
if(path==='/workflow/ticket'){
|
||||
this.$message.success("已在当前页面");
|
||||
}else{
|
||||
this.$router.push({name:'ticket',params:{}})
|
||||
}
|
||||
},
|
||||
toggleSideBar() {
|
||||
this.$store.dispatch('app/toggleSideBar')
|
||||
},
|
||||
|
|
|
|||
|
|
@ -8,6 +8,15 @@
|
|||
<tags-view />
|
||||
</div>
|
||||
<app-main />
|
||||
|
||||
<div class="floatDiv" @click="gotoTicketPage" v-if="count.total_count>0">
|
||||
<el-badge :value="count.total_count" class="item ">
|
||||
<el-icon class="el-icon-s-management" style="font-size: 30px;color: #409EFF;padding-top: 12px;"></el-icon>
|
||||
</el-badge>
|
||||
<div class="typeWrap">
|
||||
<div class="detailItem" v-for="item in count.details" :key="item.workflow">{{item.workflow__name}}:{{item.count}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -15,6 +24,7 @@
|
|||
<script>
|
||||
import { Navbar, Sidebar, AppMain,TagsView } from './components'
|
||||
import ResizeMixin from './mixin/ResizeHandler'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'Layout',
|
||||
|
|
@ -26,6 +36,9 @@ export default {
|
|||
},
|
||||
mixins: [ResizeMixin],
|
||||
computed: {
|
||||
...mapGetters([
|
||||
'count',
|
||||
]),
|
||||
sidebar() {
|
||||
return this.$store.state.app.sidebar
|
||||
},
|
||||
|
|
@ -47,7 +60,16 @@ export default {
|
|||
methods: {
|
||||
handleClickOutside() {
|
||||
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
|
||||
}
|
||||
},
|
||||
|
||||
gotoTicketPage(){
|
||||
let path = this.$route.path;
|
||||
if(path==='/workflow/ticket'){
|
||||
this.$message.success("已在当前页面");
|
||||
}else{
|
||||
this.$router.push({name:'ticket',params:{}})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -92,4 +114,35 @@ export default {
|
|||
.mobile .fixed-header {
|
||||
width: 100%;
|
||||
}
|
||||
.floatDiv{
|
||||
position: fixed;
|
||||
z-index: 3000;
|
||||
bottom: 10vh;
|
||||
right: 5vh;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
border-radius: 26px;
|
||||
border: 2px solid #409EFF;
|
||||
}
|
||||
.typeWrap{
|
||||
display: none;
|
||||
background: #ffffff;
|
||||
padding: 10px;
|
||||
box-shadow: 0 0 7px 2px #d3dce6;
|
||||
position: absolute;
|
||||
right: 60px;
|
||||
bottom: 0;
|
||||
}
|
||||
.floatDiv:hover>.typeWrap{
|
||||
display: block;
|
||||
}
|
||||
.detailItem{
|
||||
height: 30px;
|
||||
color: #888888;
|
||||
line-height: 30px;
|
||||
width: max-content;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -86,13 +86,13 @@ export const asyncRoutes = [
|
|||
component: Layout,
|
||||
redirect: '/mtm/material/',
|
||||
name: 'mtm',
|
||||
meta: { title: '制造管理', icon: 'example', perms: ['procurement_set'] },
|
||||
meta: { title: '制造管理', icon: 'example', perms: ['mtm_manage'] },
|
||||
children: [
|
||||
{
|
||||
path: 'material',
|
||||
name: 'material',
|
||||
component: () => import('@/views/mtm/material'),
|
||||
meta: { title: '物料清单', icon: 'example', perms: ['vendor_manage'] }
|
||||
meta: { title: '物料清单', icon: 'example', perms: ['mtm_material'] }
|
||||
}
|
||||
,
|
||||
{
|
||||
|
|
@ -106,7 +106,7 @@ export const asyncRoutes = [
|
|||
path: 'process',
|
||||
name: 'process',
|
||||
component: () => import('@/views/mtm/process'),
|
||||
meta: { title: '工序管理', icon: 'example', perms: ['vendor_manage'] }
|
||||
meta: { title: '工序管理', icon: 'example', perms: ['mtm_process'] }
|
||||
},
|
||||
{
|
||||
path: 'step/:id',
|
||||
|
|
@ -127,7 +127,7 @@ export const asyncRoutes = [
|
|||
path: '/mtm/productprocess/',
|
||||
name: 'productprocess',
|
||||
component: () => import('@/views/mtm/productprocess'),
|
||||
meta: { title: '产品管理', icon: 'example', perms: ['vendor_manage'] }
|
||||
meta: { title: '产品管理', icon: 'example', perms: ['mtm_productprocess'] }
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ export const asyncRoutes = [
|
|||
component: Layout,
|
||||
redirect: '/pm/plan',
|
||||
name: 'pm',
|
||||
meta: { title: '生产管理', icon: 'example', perms: ['equipment_set'] },
|
||||
meta: { title: '生产管理', icon: 'example', perms: ['pm_manage'] },
|
||||
children: [
|
||||
{
|
||||
path: 'plan',
|
||||
|
|
@ -157,13 +157,13 @@ export const asyncRoutes = [
|
|||
path: 'resources',
|
||||
name: 'resources',
|
||||
component: () => import('@/views/pm/resources'),
|
||||
meta: { title: '生产资源配置', icon: 'example', perms: ['index_manage'] }
|
||||
meta: { title: '生产资源配置', icon: 'example', perms: ['pm_resources'] }
|
||||
},
|
||||
{
|
||||
path: 'testitem',
|
||||
name: 'testitem',
|
||||
component: () => import('@/views/pm/plan'),
|
||||
meta: { title: '生产作业管理', icon: 'example', perms: ['index_manage'] }
|
||||
meta: { title: '生产作业管理', icon: 'example', perms: ['pm_testitem'] }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -194,31 +194,31 @@ export const asyncRoutes = [
|
|||
component: Layout,
|
||||
redirect: '/em/equipment',
|
||||
name: 'em',
|
||||
meta: { title: '设备管理', icon: 'example', perms: ['equipment_set'] },
|
||||
meta: { title: '设备管理', icon: 'example', perms: ['em_manage'] },
|
||||
children: [
|
||||
{
|
||||
path: 'equipment',
|
||||
name: 'equipment',
|
||||
component: () => import('@/views/em/equipment'),
|
||||
meta: { title: '生产设备', icon: 'example', perms: ['index_manage'] }
|
||||
meta: { title: '生产设备', icon: 'example', perms: ['em_equipment'] }
|
||||
},
|
||||
{
|
||||
path: 'detection ',
|
||||
name: 'detection ',
|
||||
component: () => import('@/views/em/detection'),
|
||||
meta: { title: '监视和测量设备', icon: 'example', perms: ['index_manage'] }
|
||||
meta: { title: '监视和测量设备', icon: 'example', perms: ['em_detection'] }
|
||||
},
|
||||
{
|
||||
path: 'record',
|
||||
name: 'record',
|
||||
component: () => import('@/views/em/record'),
|
||||
meta: { title: '校准检定记录', icon: 'example', perms: ['index_manage'] }
|
||||
meta: { title: '校准检定记录', icon: 'example', perms: ['em_record'] }
|
||||
},
|
||||
{
|
||||
path: 'detection ',
|
||||
name: 'detection ',
|
||||
component: () => import('@/views/em/detection'),
|
||||
meta: { title: '运维记录', icon: 'example', perms: ['index_manage'] }
|
||||
meta: { title: '运维记录', icon: 'example', perms: ['em_detection'] }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -349,32 +349,39 @@ export const asyncRoutes = [
|
|||
component: Layout,
|
||||
redirect: '/workflow/index',
|
||||
name: 'workflow',
|
||||
meta: { title: '工作流', icon: 'example', perms: ['workflow_set'] },
|
||||
meta: { title: '工作流', icon: 'example', perms: ['workflow_manage'] },
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
name: 'index',
|
||||
component: () => import('@/views/workflow/index'),
|
||||
meta: { title: '工作流配置', icon: 'example', perms: ['workflow_manage'] }
|
||||
meta: { title: '工作流配置', icon: 'example', perms: ['workflow_index'] }
|
||||
},
|
||||
{
|
||||
path: 'ticket',
|
||||
name: 'ticket',
|
||||
component: () => import('@/views/workflow/ticket'),
|
||||
meta: { title: '工单管理', icon: 'example', perms: ['workflow_manage'] },
|
||||
meta: { title: '工单管理', icon: 'example' ,noCache: true, perms: ['workflow_ticket'] },
|
||||
},
|
||||
{
|
||||
path: 'workFlowTickets',
|
||||
name: 'workFlowTickets',
|
||||
component: () => import('@/views/workflow/workFlowTickets'),
|
||||
meta: { title: '工单管理', icon: 'example' ,noCache: true,},
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'configuration',
|
||||
name: 'configuration',
|
||||
component: () => import('@/views/workflow/configuration'),
|
||||
meta: { title: '人员信息详情', icon: 'example', perms: ['workflow_manage'] },
|
||||
meta: { title: '人员信息详情', icon: 'example' },
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: 'ticketHandle',
|
||||
name: 'ticketHandle',
|
||||
component: () => import('@/views/workflow/ticketHandle'),
|
||||
meta: { title: '工单处理', icon: 'example', perms: ['workflow_manage'] },
|
||||
meta: { title: '工单处理', icon: 'example',noCache: true,},
|
||||
hidden: true
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const getters = {
|
|||
avatar: state => state.user.avatar,
|
||||
name: state => state.user.name,
|
||||
perms: state => state.user.perms,
|
||||
count: state => state.user.count,
|
||||
size: state => state.app.size,
|
||||
permission_routes: state => state.permission.routes,
|
||||
visitedViews: state => state.tagsView.visitedViews,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { login, logout, getInfo } from '@/api/user'
|
||||
import { getCount } from '@/api/workflow'
|
||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||
import { resetRouter } from '@/router'
|
||||
|
||||
|
|
@ -7,6 +8,7 @@ const getDefaultState = () => {
|
|||
token: getToken(),
|
||||
name: '',
|
||||
avatar: '',
|
||||
count: {},
|
||||
perms: []
|
||||
}
|
||||
}
|
||||
|
|
@ -28,6 +30,9 @@ const mutations = {
|
|||
},
|
||||
SET_PERMS: (state, perms) => {
|
||||
state.perms = perms
|
||||
},
|
||||
SET_COUNT: (state, count) => {
|
||||
state.count = count
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,15 +95,27 @@ const actions = {
|
|||
},
|
||||
|
||||
// remove token
|
||||
resetToken({ commit }) {
|
||||
resetToken({ commit },data) {
|
||||
return new Promise(resolve => {
|
||||
removeToken() // must remove token first
|
||||
commit('RESET_STATE')
|
||||
removeToken(); // must remove token first
|
||||
commit('RESET_STATE');
|
||||
commit('SET_TOKEN', data.access);
|
||||
setToken(data.access);
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
setSize({ commit }, size) {
|
||||
commit('SET_SIZE', size)
|
||||
},
|
||||
getCount({ commit }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getCount({}).then((res) => {
|
||||
commit('SET_COUNT', res.data);
|
||||
resolve()
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,21 +86,21 @@ div:focus {
|
|||
.el-dialog__header {
|
||||
padding: 10px 10px 6px;
|
||||
}
|
||||
// .el-dialog{
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// margin:0 !important;
|
||||
// position:absolute;
|
||||
// top:50%;
|
||||
// left:50%;
|
||||
// transform:translate(-50%,-50%);
|
||||
// /*height:600px;*/
|
||||
// max-height:calc(100% - 30px);
|
||||
// max-width:calc(100% - 30px);
|
||||
// }
|
||||
.el-dialog{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin:0 !important;
|
||||
position:absolute;
|
||||
top:50%;
|
||||
left:50%;
|
||||
transform:translate(-50%,-50%);
|
||||
/*height:600px;*/
|
||||
max-height:calc(100% - 30px);
|
||||
max-width:calc(100% - 30px);
|
||||
}
|
||||
.el-dialog .el-dialog__body{
|
||||
// flex:1;
|
||||
// overflow: auto;
|
||||
flex:1;
|
||||
overflow: auto;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
<template>
|
||||
<div class="dashboard-container">
|
||||
<el-badge v-if="count.total_count>0" :value="count.total_count" class="item" @click.native="gotoTicketPage">
|
||||
<el-icon class="el-icon-s-management" style="font-size: 70px;color: #d29898"></el-icon>
|
||||
</el-badge>
|
||||
<div></div>
|
||||
<div class="dashboard-text">name: {{ name }}</div>
|
||||
<div class="dashboard-text">perms: <span v-for="perm in perms" :key="perm">{{ perm }}</span></div>
|
||||
</div>
|
||||
|
|
@ -13,8 +17,19 @@ export default {
|
|||
computed: {
|
||||
...mapGetters([
|
||||
'name',
|
||||
'perms'
|
||||
'perms',
|
||||
'count'
|
||||
])
|
||||
},
|
||||
methods:{
|
||||
gotoTicketPage(){
|
||||
let path = this.$route.path;
|
||||
if(path==='/workflow/ticket'){
|
||||
this.$message.success("已在当前页面");
|
||||
}else{
|
||||
this.$router.push({name:'ticket',params:{}})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
name="password"
|
||||
tabindex="2"
|
||||
auto-complete="on"
|
||||
id="passwordInput"
|
||||
@keyup.enter.native="handleLogin"
|
||||
><svg-icon
|
||||
slot="prefix"
|
||||
|
|
@ -139,6 +140,7 @@
|
|||
this.$store
|
||||
.dispatch("user/login", this.loginForm)
|
||||
.then(() => {
|
||||
this.$store.dispatch("user/getCount", {})
|
||||
this.$router.push({ path: this.redirect || "/" });
|
||||
this.loading = false;
|
||||
localStorage.setItem("rem_username", this.loginForm.username);
|
||||
|
|
@ -147,6 +149,7 @@
|
|||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
|
||||
} else {
|
||||
console.log("error submit!!");
|
||||
return false;
|
||||
|
|
@ -200,7 +203,9 @@
|
|||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
#passwordInput{
|
||||
padding-right: 35px;
|
||||
}
|
||||
.show-pwd {
|
||||
height: 39px;
|
||||
margin-right: 2px;
|
||||
|
|
|
|||
|
|
@ -13,13 +13,15 @@
|
|||
|
||||
<script>
|
||||
import {faceLogin} from "@/api/testModel";
|
||||
import { setToken, removeToken } from '@/utils/auth'
|
||||
import { login, getInfo } from '@/api/user'
|
||||
// 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'],
|
||||
inject:['reload'],
|
||||
data () {
|
||||
return {
|
||||
videoWidth: 500,
|
||||
|
|
@ -69,9 +71,25 @@
|
|||
let image = new Image();
|
||||
image = canvas.toDataURL('image/png');
|
||||
document.getElementById('res').innerHTML = '<img src="'+image+'">';
|
||||
let imgData = {base64:image};
|
||||
let img = image.split(',')[1];
|
||||
let imgData = {base64:img};
|
||||
faceLogin(imgData).then((res) => {
|
||||
if (res.code >= 200) {
|
||||
debugger;
|
||||
console.log(res);
|
||||
let data = res.data;
|
||||
this.$confirm("是否切换登陆人?", "提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
await this.$store.dispatch("user/resetToken",data);
|
||||
await this.$store.dispatch("user/getInfo",data.access);
|
||||
window.location.reload();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
this.$message.success("成功");
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@
|
|||
<p>创建时间 :{{watchedCreateTime}}</p>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<svg height=800 id="mySvg" style="width: max-content!important">
|
||||
<svg height=800 id="mySvg" style="width: 100%;">
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -278,7 +278,7 @@ export default {
|
|||
});
|
||||
},
|
||||
handleTicket(scope){
|
||||
this.$router.push({name:"ticket",params:{workflow:scope.row.id}})
|
||||
this.$router.push({name:"workFlowTickets",params:{workflow:scope.row.id}})
|
||||
},
|
||||
async confirm(form) {
|
||||
debugger;
|
||||
|
|
@ -321,12 +321,12 @@ export default {
|
|||
that.limitedWatch = true;
|
||||
that.$nextTick(()=>{
|
||||
var g = new dagreD3.graphlib.Graph().setGraph({
|
||||
align: 'DL',
|
||||
rankdir: 'DL',
|
||||
nodesep: 100,
|
||||
edgesep: 100,
|
||||
ranksep: 50,
|
||||
marginx: 0,
|
||||
marginy: 50,
|
||||
edgesep: 10,//两条线之间的距离
|
||||
ranksep: 50,//节点之间的距离
|
||||
marginx: 160,
|
||||
marginy: 20,
|
||||
});
|
||||
//获取state得到节点
|
||||
getWfStateList(workFlow).then((response) => {
|
||||
|
|
@ -343,8 +343,6 @@ export default {
|
|||
//节点样式
|
||||
style: "fill:#fff;stroke:#000",
|
||||
labelStyle: "fill:#000;",
|
||||
// width: 83,
|
||||
// height: 40,
|
||||
rx :5,//矩形节点圆角度
|
||||
ry :5
|
||||
});
|
||||
|
|
@ -356,9 +354,8 @@ export default {
|
|||
getWfTransitionList(workFlow).then((res)=>{
|
||||
if(res.data){
|
||||
let transitionList = res.data;
|
||||
transitionList.forEach((transitions)=>{
|
||||
let transition0 = transitions;
|
||||
if (transition0.condition_expression.length>3){
|
||||
transitionList.forEach((transition0)=>{
|
||||
if (transition0.condition_expression.length>0){
|
||||
debugger;
|
||||
g.setNode(transition0.source_state_.id+100000, {label: "条件表达式",style: "stroke: #000;fill: #afa", shape: "diamond"});
|
||||
g.setEdge(transition0.source_state_.id, transition0.source_state_.id+100000, {
|
||||
|
|
@ -366,7 +363,7 @@ export default {
|
|||
label: transition0.name,
|
||||
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px"
|
||||
});
|
||||
let condition_expression = JSON.parse(transition0.condition_expression);
|
||||
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,
|
||||
|
|
@ -386,7 +383,6 @@ export default {
|
|||
g.nodes().forEach(function (v) {
|
||||
console.log("Node " + v + ": " + JSON.stringify(g.node(v)));
|
||||
});
|
||||
|
||||
// 创建渲染器
|
||||
let render = new dagreD3.render();
|
||||
// 选择 svg 并添加一个g元素作为绘图容器.
|
||||
|
|
@ -399,6 +395,7 @@ export default {
|
|||
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
},
|
||||
closeMark(){
|
||||
|
|
@ -415,33 +412,25 @@ export default {
|
|||
this.hasJsonFlag = true
|
||||
},
|
||||
onError(value) {
|
||||
// console.log("json错误了value:", value);
|
||||
this.hasJsonFlag = false
|
||||
},
|
||||
onJsonChange1 (value) {
|
||||
// console.log('更改value:', value);
|
||||
// 实时保存
|
||||
this.onJsonSave1(value)
|
||||
},
|
||||
onJsonSave1 (value) {
|
||||
// console.log('保存value:', value);
|
||||
this.display_form_str = value
|
||||
this.hasJsonFlag1 = true
|
||||
},
|
||||
onError1(value) {
|
||||
// console.log("json错误了value:", value);
|
||||
this.hasJsonFlag1 = false
|
||||
},
|
||||
// 检查json
|
||||
checkJson(){
|
||||
if (this.hasJsonFlag == false){
|
||||
// console.log("json验证失败")
|
||||
// this.$message.error("json验证失败")
|
||||
alert("限制表达式json验证失败")
|
||||
return false
|
||||
} else {
|
||||
// console.log("json验证成功")
|
||||
// this.$message.success("json验证成功")
|
||||
alert("限制表达式json验证成功")
|
||||
return true
|
||||
}
|
||||
|
|
@ -449,13 +438,9 @@ export default {
|
|||
// 检查json
|
||||
checkJson2(){
|
||||
if (this.hasJsonFlag1 == false){
|
||||
// console.log("json验证失败")
|
||||
// this.$message.error("json验证失败")
|
||||
alert("展现表单字段json验证失败")
|
||||
return false
|
||||
} else {
|
||||
// console.log("json验证成功")
|
||||
// this.$message.success("json验证成功")
|
||||
alert("展现表单字段json1验证成功")
|
||||
return true
|
||||
}
|
||||
|
|
@ -464,6 +449,19 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
::-webkit-scrollbar {
|
||||
width: 15px;
|
||||
}
|
||||
::-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,.5);
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.svgMark{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
@ -483,6 +481,8 @@ export default {
|
|||
margin: 10vh auto 0;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
max-height: 75vh;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.svgItem{
|
||||
padding: 20px 40px 0 ;
|
||||
|
|
|
|||
|
|
@ -1,51 +1,49 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-card>
|
||||
|
||||
<div style="margin-top: 10px">
|
||||
<el-button type="primary" icon="el-icon-plus" @click="handleCreate"
|
||||
>新增</el-button
|
||||
>
|
||||
</div>
|
||||
<div style="margin-top: 10px">
|
||||
<el-button type="primary" icon="el-icon-plus" @click="handleCreate">新增
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card style="margin-top: 10px">
|
||||
<el-table
|
||||
:data="wfstateList"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column type="index" width="50" />
|
||||
<el-table-column width="180" label="名称">
|
||||
<el-table-column type="index" width="50"/>
|
||||
<el-table-column width="180" label="名称">
|
||||
<template slot-scope="scope">{{ scope.row.name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="180" label="是否隐藏">
|
||||
<template slot-scope="scope">
|
||||
<el-table-column width="180" label="是否隐藏">
|
||||
<template slot-scope="scope">
|
||||
{{ !!(scope.row.is_hidde)?'是':'否' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="180" label="顺序ID">
|
||||
<el-table-column width="180" label="顺序ID">
|
||||
<template slot-scope="scope">{{ scope.row.sort }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="180" label="类型">
|
||||
<el-table-column width="180" label="类型">
|
||||
<template slot-scope="scope">
|
||||
<el-tag effect="plain" v-if="scope.row.type==0">
|
||||
普通类型
|
||||
<el-tag effect="plain" v-if="scope.row.type==0">
|
||||
普通类型
|
||||
</el-tag>
|
||||
<el-tag effect="plain" v-if="scope.row.type==1">
|
||||
初始状态
|
||||
<el-tag effect="plain" v-if="scope.row.type==1">
|
||||
初始状态
|
||||
</el-tag>
|
||||
<el-tag effect="plain" v-if="scope.row.type==2">
|
||||
结束状态
|
||||
结束状态
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="180" label="参与人类型">
|
||||
<el-table-column width="180" label="参与人类型">
|
||||
<template slot-scope="scope">{{ options_[scope.row.participant_type] }}</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column width="180" label="创建时间">
|
||||
|
||||
<el-table-column width="180" label="创建时间">
|
||||
<template slot-scope="scope">{{ scope.row.create_time }}</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
|
||||
<el-table-column
|
||||
align="center"
|
||||
|
|
@ -53,21 +51,19 @@
|
|||
width="220px"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<!-- v-if="checkPermission(['workflow_update'])" -->
|
||||
<el-link
|
||||
v-if="checkPermission(['wfstate_update'])"
|
||||
@click="handleEdit(scope)"
|
||||
>编辑</el-link
|
||||
>
|
||||
>编辑
|
||||
</el-link>
|
||||
<el-link
|
||||
v-if="checkPermission(['wfstate_delete'])"
|
||||
type="danger"
|
||||
@click="handleDelete(scope)"
|
||||
>删除</el-link
|
||||
>
|
||||
>删除
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
</el-card>
|
||||
<el-dialog
|
||||
:visible.sync="dialogVisible"
|
||||
|
|
@ -81,7 +77,7 @@
|
|||
:rules="rule1"
|
||||
>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="wfstate.name" placeholder="名称" />
|
||||
<el-input v-model="wfstate.name" placeholder="名称"/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="是否隐藏"
|
||||
|
|
@ -90,61 +86,75 @@
|
|||
>
|
||||
<el-switch v-model="wfstate.is_hidden"></el-switch>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态顺序" prop="sort">
|
||||
<el-input v-model="wfstate.sort" type="number" placeholder="状态顺序" />
|
||||
|
||||
<el-form-item label="状态顺序" prop="sort">
|
||||
<el-input v-model="wfstate.sort" type="number" placeholder="状态顺序"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态类型" prop="type">
|
||||
|
||||
<el-select style="width: 100%" v-model="wfstate.type" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
<el-form-item label="状态类型" prop="type">
|
||||
|
||||
<el-select style="width: 100%" v-model="wfstate.type" placeholder="请选择">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
<el-form-item
|
||||
label="允许撤回"
|
||||
prop="enable_retreat"
|
||||
label-width="120px"
|
||||
>
|
||||
<el-switch v-model="wfstate.enable_retreat"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="参与者类型" prop="participant_type">
|
||||
<el-select style="width: 100%" v-model="wfstate.participant_type" placeholder="请选择" @change="typeChange">
|
||||
<el-option
|
||||
v-for="item in typeoptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
<el-form-item label="参与者类型" prop="participant_type">
|
||||
<el-select style="width: 100%" v-model="wfstate.participant_type" placeholder="请选择" @change="typeChange">
|
||||
<el-option
|
||||
v-for="item in typeoptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="参与者" prop="participant" v-if="wfstate.participant_type==1">
|
||||
<el-select style="width: 100%" v-model="participant" placeholder="请选择参与者">
|
||||
<el-select style="width: 100%" v-model="participant" placeholder="请选择参与者">
|
||||
<el-option v-for="item in staffs" :key="item.id" :label="item.name" :value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="参与者" prop="participant" v-if="wfstate.participant_type==2">
|
||||
<el-select style="width: 100%" v-model="participants" multiple placeholder="请选择参与者">
|
||||
<el-select style="width: 100%" v-model="participants" multiple placeholder="请选择参与者">
|
||||
<el-option v-for="item in staffs" :key="item.id" :label="item.name" :value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分配方式" prop="participant" v-if="wfstate.participant_type==2">
|
||||
<el-select style="width: 100%" v-model="wfstate.distribute_type" placeholder="请选择分配方式">
|
||||
<el-select style="width: 100%" v-model="wfstate.distribute_type" placeholder="请选择分配方式">
|
||||
<el-option label="主动接单" value="1"></el-option>
|
||||
<el-option label="直接处理" value="2"></el-option>
|
||||
<el-option label="随机分配" value="3"></el-option>
|
||||
<el-option label="全部处理" value="4"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="部门" prop="participant" v-if="wfstate.participant_type==3">
|
||||
<el-select style="width: 100%" v-model="participant" placeholder="请选择部门">
|
||||
<el-option v-for="item in departments" :key="item.id" :label="item.name" :value="item.id">
|
||||
<el-form-item label="角色" prop="participant" v-if="wfstate.participant_type==4">
|
||||
<el-select style="width: 100%" v-model="participants" placeholder="请选择角色">
|
||||
<el-option v-for="item in roles" :key="item.id" :label="item.name" :value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="过滤策略" prop="participant" v-if="wfstate.participant_type==4">
|
||||
<el-select style="width: 100%" v-model="wfstate.filter_policy" placeholder="请选择过滤策略">
|
||||
<el-option label="无" value="0"></el-option>
|
||||
<el-option label="和工单同属以及上级部门" value="1"></el-option>
|
||||
<el-option label="和创建人同属以及上级部门" value="2"></el-option>
|
||||
<el-option label="和上步处理人同属以及上级部门" value="3"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="代码选择" prop="participant" v-if="wfstate.participant_type==9">
|
||||
<el-select style="width: 100%" v-model="participants" placeholder="请选择代码">
|
||||
<el-option v-for="item in codes" :key="item.func" :label="item.name" :value="item.func">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
|
@ -152,21 +162,23 @@
|
|||
<el-button @click="addWordStatusChange">添加修改</el-button>
|
||||
<el-row v-for="(item,$index) in statusChange" :key="item+$index" style="margin-top: 10px">
|
||||
<el-col :span="11">
|
||||
<el-select style="width: 100%" v-model="item.name" placeholder="请选择字段">
|
||||
<el-option v-for="item in customfieldList" :key="item.id" :label="item.field_name" :value="item.field_key">
|
||||
<el-select style="width: 100%" v-model="item.name" placeholder="请选择字段">
|
||||
<el-option v-for="item in customfieldList" :key="item.id" :label="item.field_name"
|
||||
:value="item.field_key">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="1" style="height: 1px;"></el-col>
|
||||
<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 label="必填" value="2"></el-option>
|
||||
<el-option label="可选" value="3"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="2" style="text-align: center" v-if="$index!==0">
|
||||
<i class="el-icon-remove-outline" @click.prevent="removeStatusChange($index)" style="color: red;font-size: 16px;"></i>
|
||||
<i class="el-icon-remove-outline" @click.prevent="removeStatusChange($index)"
|
||||
style="color: red;font-size: 16px;"></i>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
|
|
@ -179,53 +191,57 @@
|
|||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { getWfStateList, createWfState,updateWfState,deleteWfState,getWfCustomfieldList } from "@/api/workflow";
|
||||
import { getOrganizationList,getUserList } from "@/api/user";
|
||||
import checkPermission from "@/utils/permission";
|
||||
const defaultwfstate = {
|
||||
name: "",
|
||||
};
|
||||
export default {
|
||||
components: { },
|
||||
name: "State",
|
||||
props: ["ID"],
|
||||
data() {
|
||||
return {
|
||||
wfstate: {
|
||||
name:'',
|
||||
is_hidden:'',
|
||||
sort:'',
|
||||
type:'',
|
||||
enable_retreat:'',
|
||||
participant_type:'',
|
||||
participant:'',
|
||||
distribute_type:'',//分发类型
|
||||
state_fields:{}//字段状态是否可写
|
||||
},
|
||||
statusChange:[],
|
||||
participant:'',
|
||||
participants:[],
|
||||
is_hidden:false,
|
||||
enable_retreat:false,
|
||||
staffs:[],
|
||||
departments:[],
|
||||
customfieldList:[],
|
||||
/*wfstateList: {
|
||||
count:0
|
||||
},*/
|
||||
wfstateList:[],
|
||||
options_:{
|
||||
"0":'无处理',
|
||||
"1":'个人',
|
||||
"2":'多人',
|
||||
"3":'部门',
|
||||
"4":'角色',
|
||||
"5":'变量',
|
||||
"6":'普通类型',
|
||||
"7":'工单字段',
|
||||
"8":'父工单字段',
|
||||
},
|
||||
options: [{
|
||||
import {getWfStateList, createWfState, updateWfState,
|
||||
deleteWfState, getWfCustomfieldList , getCodes } from "@/api/workflow";
|
||||
import { getUserList} from "@/api/user";
|
||||
import { getRoleAll } from "@/api/role";
|
||||
import checkPermission from "@/utils/permission";
|
||||
const defaultwfstate = {
|
||||
name: "",
|
||||
};
|
||||
export default {
|
||||
components: {},
|
||||
name: "State",
|
||||
props: ["ID"],
|
||||
data() {
|
||||
return {
|
||||
wfstate: {
|
||||
name: '',
|
||||
is_hidden: '',
|
||||
sort: '',
|
||||
type: '',
|
||||
enable_retreat: '',
|
||||
participant_type: '',
|
||||
filter_policy: '',
|
||||
distribute_type: '',//分发类型
|
||||
state_fields: {}//字段状态是否可写
|
||||
},
|
||||
statusChange: [],
|
||||
participant: '',
|
||||
participants: [],
|
||||
is_hidden: false,
|
||||
enable_retreat: false,
|
||||
staffs: [],
|
||||
roles: [],
|
||||
codes: [],
|
||||
customfieldList: [],
|
||||
/*wfstateList: {
|
||||
count:0
|
||||
},*/
|
||||
wfstateList: [],
|
||||
options_: {
|
||||
"0": '无处理',
|
||||
"1": '个人',
|
||||
"2": '多人',
|
||||
// "3": '部门',
|
||||
"4": '角色',
|
||||
// "5": '变量',
|
||||
"6": '脚本',
|
||||
"7": '工单的字段',
|
||||
// "8": '父工单的字段',
|
||||
"9": '代码获取',
|
||||
},
|
||||
options: [{
|
||||
value: 0,
|
||||
label: '普通类型'
|
||||
}, {
|
||||
|
|
@ -235,7 +251,7 @@ export default {
|
|||
value: 2,
|
||||
label: '结束状态'
|
||||
}],
|
||||
typeoptions: [{
|
||||
typeoptions: [{
|
||||
value: 0,
|
||||
label: '无处理'
|
||||
}, {
|
||||
|
|
@ -245,182 +261,193 @@ export default {
|
|||
value: 2,
|
||||
label: '多人'
|
||||
}
|
||||
, {
|
||||
value: 3,
|
||||
label: '部门'
|
||||
}
|
||||
, {
|
||||
value: 4,
|
||||
label: '角色'
|
||||
}
|
||||
, {
|
||||
value: 5,
|
||||
label: '变量'
|
||||
value: 6,
|
||||
label: '脚本'
|
||||
}
|
||||
, {
|
||||
value: 7,
|
||||
label: '工单字段'
|
||||
label: '工单的字段'
|
||||
}
|
||||
, {
|
||||
value: 8,
|
||||
label: '父工单字段'
|
||||
value: 9,
|
||||
label: '代码获取'
|
||||
}],
|
||||
display_form_str:[],
|
||||
limit_expression:[],
|
||||
dialogVisible: false,
|
||||
dialogType: "new",
|
||||
rule1: {
|
||||
name: [{ required: true, message: "请输入", trigger: "blur" }],
|
||||
sort: [{ required: true, message: "请输入", trigger: "blur" }],
|
||||
type:[{ required: true, message: "选择", trigger: "blur" }],
|
||||
display_form_str: [],
|
||||
limit_expression: [],
|
||||
dialogVisible: false,
|
||||
dialogType: "new",
|
||||
rule1: {
|
||||
name: [{required: true, message: "请输入", trigger: "blur"}],
|
||||
sort: [{required: true, message: "请输入", trigger: "blur"}],
|
||||
type: [{required: true, message: "选择", trigger: "blur"}],
|
||||
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
|
||||
this.getList();
|
||||
this.getUser();
|
||||
this.getDepartment();
|
||||
},
|
||||
mounted(){
|
||||
this.getCodes();
|
||||
},
|
||||
methods: {
|
||||
|
||||
checkPermission(value){
|
||||
debugger;
|
||||
console.log(checkPermission(value))
|
||||
checkPermission(value);
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
|
||||
this.getList();
|
||||
this.getUser();
|
||||
this.getDepartment();
|
||||
},
|
||||
methods: {
|
||||
checkPermission,
|
||||
|
||||
getList() {
|
||||
|
||||
getWfStateList(this.ID).then((response) => {
|
||||
|
||||
if (response.data) {
|
||||
this.wfstateList = response.data;
|
||||
}
|
||||
|
||||
});
|
||||
getWfCustomfieldList(this.ID).then((response) => {
|
||||
if (response.data) {
|
||||
this.customfieldList = response.data;
|
||||
}
|
||||
getList() {
|
||||
|
||||
});
|
||||
},
|
||||
getUser(){
|
||||
getUserList({}).then(res=>{
|
||||
if(res.data){
|
||||
this.staffs = res.data.results;
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepartment(){
|
||||
getOrganizationList().then(res=>{
|
||||
if(res.data){
|
||||
this.departments = res.data;
|
||||
}
|
||||
})
|
||||
},
|
||||
//参与者类型变化
|
||||
typeChange(){
|
||||
this.participant = '';
|
||||
this.participants = [];
|
||||
},
|
||||
//打开新建
|
||||
handleCreate() {
|
||||
this.wfstate = Object.assign({}, defaultwfstate);
|
||||
this.dialogType = "new";
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs["Form"].clearValidate();
|
||||
});
|
||||
},
|
||||
//打开编辑
|
||||
handleEdit(scope) {
|
||||
this.wfstate = Object.assign({}, scope.row); // copy obj
|
||||
this.participants = this.wfstate.participant;
|
||||
this.participant = this.wfstate.participant;
|
||||
/////
|
||||
debugger;
|
||||
console.log(this.wfstate.state_fields);
|
||||
let arr = [];
|
||||
for (let pro in this.wfstate.state_fields) {
|
||||
let obj = new Object();
|
||||
obj.name = pro;
|
||||
obj.value = this.wfstate.state_fields[pro];
|
||||
arr.push(obj)
|
||||
}
|
||||
this.statusChange = arr;
|
||||
this.wfstate.distribute_type = this.wfstate.distribute_type.toString();
|
||||
this.dialogType = "edit";
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs["Form"].clearValidate();
|
||||
});
|
||||
},
|
||||
addWordStatusChange(){
|
||||
this.statusChange.push({name:'',value:''})
|
||||
},
|
||||
removeStatusChange(index){
|
||||
this.statusChange.splice(index, 1)
|
||||
},
|
||||
//编辑新建
|
||||
async confirm(form) {
|
||||
this.$refs[form].validate((valid) => {
|
||||
if (valid) {
|
||||
const isEdit = this.dialogType === "edit";
|
||||
// this.wfstate.participant = 1;
|
||||
let state_fields = {};
|
||||
if(this.statusChange.length>0){
|
||||
for(let i=0;i<this.statusChange.length;i++){
|
||||
state_fields[this.statusChange[i].name] = this.statusChange[i].value;
|
||||
}
|
||||
getWfStateList(this.ID).then((response) => {
|
||||
|
||||
if (response.data) {
|
||||
this.wfstateList = response.data;
|
||||
}
|
||||
debugger;
|
||||
console.log(state_fields);
|
||||
this.wfstate.state_fields = state_fields;
|
||||
this.wfstate.participant = this.participant!==''?this.participant:this.participants;
|
||||
if (isEdit) {
|
||||
updateWfState(this.wfstate.id, this.wfstate).then((res) => {
|
||||
if (res.code >= 200) {
|
||||
this.getList();
|
||||
this.dialogVisible = false;
|
||||
this.$message.success("成功");
|
||||
this.getList();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.wfstate.workflow=this.ID;
|
||||
createWfState(this.wfstate).then((res) => {
|
||||
if (res.code >= 200) {
|
||||
this.getList();
|
||||
this.dialogVisible = false;
|
||||
this.getList();
|
||||
this.$message.success("成功");
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
handleDelete(scope) {
|
||||
this.$confirm("确认删除?", "警告", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "error",
|
||||
})
|
||||
.then(async () => {
|
||||
await deleteWfState(scope.row.id);
|
||||
this.getList();
|
||||
this.$message.success("成功");
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
getWfCustomfieldList(this.ID).then((response) => {
|
||||
if (response.data) {
|
||||
this.customfieldList = response.data;
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
getUser() {
|
||||
getUserList({}).then(res => {
|
||||
if (res.data) {
|
||||
this.staffs = res.data.results;
|
||||
}
|
||||
})
|
||||
},
|
||||
getDepartment() {
|
||||
getRoleAll().then(res => {
|
||||
if (res.data) {
|
||||
this.roles = res.data;
|
||||
}
|
||||
})
|
||||
},
|
||||
getCodes(){
|
||||
getCodes().then(res=>{
|
||||
// debugger;
|
||||
// console.log(res);
|
||||
this.codes = res.data;
|
||||
})
|
||||
},
|
||||
//参与者类型变化
|
||||
typeChange() {
|
||||
this.participant = '';
|
||||
this.participants = [];
|
||||
},
|
||||
//打开新建
|
||||
handleCreate() {
|
||||
this.wfstate = Object.assign({}, defaultwfstate);
|
||||
this.dialogType = "new";
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs["Form"].clearValidate();
|
||||
});
|
||||
},
|
||||
//打开编辑
|
||||
handleEdit(scope) {
|
||||
this.wfstate = Object.assign({}, scope.row); // copy obj
|
||||
this.participants = this.wfstate.participant;
|
||||
this.participant = this.wfstate.participant;
|
||||
/////
|
||||
debugger;
|
||||
console.log(this.wfstate.state_fields);
|
||||
let arr = [];
|
||||
for (let pro in this.wfstate.state_fields) {
|
||||
let obj = new Object();
|
||||
obj.name = pro;
|
||||
obj.value = this.wfstate.state_fields[pro];
|
||||
arr.push(obj)
|
||||
}
|
||||
this.statusChange = arr;
|
||||
this.wfstate.distribute_type = this.wfstate.distribute_type.toString();
|
||||
this.wfstate.filter_policy = this.wfstate.filter_policy.toString();
|
||||
this.dialogType = "edit";
|
||||
this.dialogVisible = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs["Form"].clearValidate();
|
||||
});
|
||||
},
|
||||
addWordStatusChange() {
|
||||
this.statusChange.push({name: '', value: ''})
|
||||
},
|
||||
removeStatusChange(index) {
|
||||
this.statusChange.splice(index, 1)
|
||||
},
|
||||
//编辑新建
|
||||
async confirm(form) {
|
||||
this.$refs[form].validate((valid) => {
|
||||
if (valid) {
|
||||
const isEdit = this.dialogType === "edit";
|
||||
// this.wfstate.participant = 1;
|
||||
let state_fields = {};
|
||||
if (this.statusChange.length > 0) {
|
||||
for (let i = 0; i < this.statusChange.length; i++) {
|
||||
state_fields[this.statusChange[i].name] = this.statusChange[i].value;
|
||||
}
|
||||
}
|
||||
debugger;
|
||||
console.log(state_fields);
|
||||
this.wfstate.state_fields = state_fields;
|
||||
this.wfstate.participant = this.wfstate.participant_type === 1 ? this.participant : this.participants;
|
||||
if (isEdit) {
|
||||
updateWfState(this.wfstate.id, this.wfstate).then((res) => {
|
||||
if (res.code >= 200) {
|
||||
this.getList();
|
||||
this.dialogVisible = false;
|
||||
this.$message.success("成功");
|
||||
this.getList();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.wfstate.workflow = this.ID;
|
||||
createWfState(this.wfstate).then((res) => {
|
||||
if (res.code >= 200) {
|
||||
this.getList();
|
||||
this.dialogVisible = false;
|
||||
this.getList();
|
||||
this.$message.success("成功");
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
handleDelete(scope) {
|
||||
this.$confirm("确认删除?", "警告", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "error",
|
||||
})
|
||||
.then(async () => {
|
||||
await deleteWfState(scope.row.id);
|
||||
this.getList();
|
||||
this.$message.success("成功");
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -29,12 +29,15 @@
|
|||
</el-card>
|
||||
<el-tabs v-model="pageForm.category" type="border-card" @tab-click="handleClick">
|
||||
<el-tab-pane label="待处理" name="duty">
|
||||
<el-table :data="tickets"
|
||||
border fit stripe
|
||||
style="width: 100%"
|
||||
height="100"
|
||||
highlight-current-row
|
||||
v-el-height-adaptive-table="{bottomOffset: 60}">
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="tickets"
|
||||
border fit stripe
|
||||
style="width: 100%"
|
||||
height="100"
|
||||
highlight-current-row
|
||||
v-el-height-adaptive-table="{bottomOffset: 100}"
|
||||
>
|
||||
<el-table-column label="工单标题" min-width="100" prop="title">
|
||||
</el-table-column>
|
||||
<el-table-column label="当前状态" min-width="100">
|
||||
|
|
@ -61,10 +64,13 @@
|
|||
<el-table-column align="center" label="操作">
|
||||
<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-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 v-else-if="(scope.row.act_state==1||scope.row.act_state==3)&&scope.row.participant_type!==2&&scope.row.state_.type===0" type="primary" @click="handleDetail(scope)">处理</el-link>
|
||||
<el-link v-if="scope.row.state_.type==1&&userId==1" type="danger" @click="handleClose(scope,'2')">关闭</el-link>
|
||||
<!--如果state_.retreat为可退回,则显示撤回按钮 state_.type==1处于草稿状态 -->
|
||||
<el-link v-if="scope.row.state_.enable_retreat&&userId==scope.row.create_by&&scope.row.state_.type!==1" type="danger" @click="handleClose(scope,'1')">撤回</el-link>
|
||||
<el-link type="success" @click="handleDelete(scope)">删除</el-link>
|
||||
<el-link type="success" @click="handlePicture(scope)">查看流程图</el-link>
|
||||
<el-link type="success" @click="handleLogs(scope)">工单日志</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
|
@ -107,7 +113,12 @@
|
|||
</el-table-column>
|
||||
<el-table-column align="center" label="操作">
|
||||
<template slot-scope="scope">
|
||||
<el-link type="primary" v-if="scope.row.act_state==4" @click="handlePicture(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.act_state==3)&&scope.row.participant_type!==2&&scope.row.state_.type===0" type="primary" @click="handleDetail(scope)">处理</el-link>
|
||||
-->
|
||||
<el-link v-if="scope.row.state_.type==1&&userId==1" type="danger" @click="handleClose(scope,'2')">关闭</el-link>
|
||||
<el-link type="success" @click="handlePicture(scope)">查看流程图</el-link>
|
||||
<el-link type="success" @click="handleLogs(scope)">工单日志</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
|
@ -150,10 +161,11 @@
|
|||
</el-table-column>
|
||||
<el-table-column align="center" label="操作">
|
||||
<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-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 v-if="scope.row.state_.enable_retreat&&scope.row.state_.type!==1" type="danger" @click="handleClose(scope,'1')">撤回</el-link>
|
||||
<el-link v-if="scope.row.state_.type==1" type="danger" @click="handleClose(scope,'2')">关闭</el-link>
|
||||
<el-link type="success" @click="handleDelete(scope)">删除</el-link>
|
||||
<el-link type="success" @click="handlePicture(scope)">查看流程图</el-link>
|
||||
<el-link type="success" @click="handleLogs(scope)">工单日志</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
|
@ -197,7 +209,7 @@
|
|||
<el-table-column align="center" label="操作">
|
||||
<template slot-scope="scope">
|
||||
<el-link type="danger" @click="handlePicture(scope)">查看流程图</el-link>
|
||||
<el-link v-if="scope.row.act_state==1" type="danger" @click="handleDetail(scope)">处理</el-link>
|
||||
<el-link type="success" @click="handleLogs(scope)">工单日志</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
|
@ -224,25 +236,27 @@
|
|||
<el-step :title="item.name" v-for="item in flowSteps " :key="item.id">
|
||||
</el-step>
|
||||
</el-steps>
|
||||
<svg height=800 id="mySvg" style="width:100%!important;">
|
||||
</svg>
|
||||
<div style="width: 90%;margin: auto;">
|
||||
<svg height=800 id="mySvg" style="width:100%!important;">
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog :visible.sync="limitedRetreat" title="工单撤回">
|
||||
<el-dialog :visible.sync="limitedRetreat" :title="handleTitle">
|
||||
<el-row>
|
||||
<el-form ref="Form" :model="handleForm" label-width="100px" label-position="right" :rules="handleRule">
|
||||
|
||||
<el-col :span="2" style="height: 1px;"></el-col>
|
||||
<el-col :span="20" style="margin:6vh 0">
|
||||
<el-form-item label="撤回原因">
|
||||
<el-form ref="Form" :model="handleForm" label-width="100px" label-position="right">
|
||||
<el-col :span="1" style="height: 1px;"></el-col>
|
||||
<el-col :span="22" style="margin:3vh 0">
|
||||
<el-form-item :label="handleLabel">
|
||||
<el-input type="textarea" :rows="3" v-model="handleForm.suggestion" placeholder="撤回原因"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<div style="text-align: center">
|
||||
<el-button class="filter-item" type="" @click="retreatCancel">取消</el-button>
|
||||
<el-button class="filter-item" type="primary" @click="retreatSubmit">确定</el-button>
|
||||
<el-button class="filter-item" type="" @click="handleCancel">取消</el-button>
|
||||
<el-button class="filter-item" type="primary" @click="handleSubmit">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog :visible.sync="limitedAdd" title="新增工单">
|
||||
|
|
@ -337,13 +351,49 @@
|
|||
<el-button type="primary" @click="confirm('Form')">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog :visible.sync="limitedFlowLogs" title="工单日志">
|
||||
<el-table :data="floeLogs" fit stripe
|
||||
style="width: 100%;border-top:1px solid #EBEEF5;"
|
||||
height="100"
|
||||
highlight-current-row
|
||||
v-el-height-adaptive-table="{bottomOffset: 60}">
|
||||
<el-table-column label="工单标题" min-width="100">
|
||||
<template slot-scope="scope" v-if="scope.row.ticket_data">
|
||||
<span>{{scope.row.ticket_data.title}}中</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="当前状态" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.act_state==0" label="草稿中" value="scope.row.act_state">草稿中</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==1" label="进行中" value="scope.row.act_state">进行中</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==2" label="被退回" value="scope.row.act_state">被退回</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==3" label="被撤回" value="scope.row.act_state">被撤回</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==4" label="已完成" value="scope.row.act_state">已完成</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==5" label="已关闭" value="scope.row.act_state">已关闭</el-tag>
|
||||
</template>
|
||||
</el-table-column>-->
|
||||
<el-table-column label="进行状态" min-width="100">
|
||||
<template slot-scope="scope" v-if="scope.row.state_">
|
||||
<span v-if="scope.row.state_.type==0">{{scope.row.state_.name}}中</span>
|
||||
<span v-else>已{{scope.row.state_.name}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作人" min-width="100">
|
||||
<template slot-scope="scope" v-if="scope.row.participant_">{{ scope.row.participant_.name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作意见" min-width="100" prop="suggestion">
|
||||
</el-table-column>
|
||||
<el-table-column label="更新时间" min-width="100" prop="update_time">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
<script>
|
||||
import { upUrl, upHeaders } from "@/api/file";
|
||||
import {getWorkflowList,getWfCustomfieldList,createTicket,getWfStateList,getTickets,ticketAccpet,getWfTransitionList,
|
||||
ticketHandle,getWfFlowSteps,getTicketDetail,getTicketTransitions,getTicketFlowlog,getTicketRetreat} from "@/api/workflow";
|
||||
ticketHandle,getWfFlowSteps,getTicketDetail,getTicketTransitions,getTicketFlowlog,ticketRetreat,ticketClose,ticketDestory} from "@/api/workflow";
|
||||
import Pagination from "@/components/Pagination";
|
||||
import dagreD3 from 'dagre-d3'
|
||||
import * as d3 from 'd3'
|
||||
|
|
@ -389,6 +439,8 @@
|
|||
limitedStep:false,
|
||||
limitedAdd:false,
|
||||
limitedRetreat:false,
|
||||
limitedFlowLogs:false,
|
||||
floeLogs:[],
|
||||
nodes: [],
|
||||
tooltip:null,
|
||||
edges: [],
|
||||
|
|
@ -397,11 +449,12 @@
|
|||
operationBtn:[],
|
||||
customfields:[],
|
||||
transitions:[],
|
||||
handleLabel:'撤回原因',
|
||||
handleTitle:'工单撤回',
|
||||
rule1:{
|
||||
title: [{ required: true, message: "请输入", trigger: "blur" }],
|
||||
workflow: [{ required: true, message: "选择", trigger: "blur" }]
|
||||
}
|
||||
|
||||
,
|
||||
}
|
||||
},
|
||||
|
|
@ -416,15 +469,6 @@
|
|||
this.getStates();
|
||||
this.getWorkFlow();
|
||||
},
|
||||
activated(){
|
||||
this.workflow = this.$route.params.workflow;
|
||||
if(this.workflow){//有传参
|
||||
this.pageForm.workflow = parseInt(this.workflow);
|
||||
}
|
||||
this.getList();
|
||||
this.getStates();
|
||||
this.getWorkFlow();
|
||||
},
|
||||
methods:{
|
||||
getList(){
|
||||
this.listLoading = true;
|
||||
|
|
@ -439,16 +483,12 @@
|
|||
getTicketFlowlogs(id){
|
||||
getTicketFlowlog(id).then(res=>{
|
||||
if(res.data){
|
||||
debugger;
|
||||
console.log(res)
|
||||
this.logs = res.data;
|
||||
}
|
||||
})
|
||||
},
|
||||
handleClick(tab, event) {
|
||||
console.log(tab, event);
|
||||
console.log(this.pageForm.category);
|
||||
debugger;
|
||||
this.getList();
|
||||
},
|
||||
//获取工作流所有状态
|
||||
|
|
@ -563,30 +603,60 @@
|
|||
}
|
||||
});
|
||||
},
|
||||
retreatCancel(){
|
||||
handleCancel(){
|
||||
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("成功");
|
||||
})
|
||||
handleSubmit(){
|
||||
debugger;
|
||||
if(this.handleTitle === '撤回工单'){
|
||||
this.$confirm("确认撤回工单吗?", "温馨提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
.then(async () => {
|
||||
ticketRetreat(this.retreatId,this.handleForm).then(res=>{
|
||||
this.limitedRetreat = false;
|
||||
this.getList();
|
||||
this.$store.dispatch("user/getCount", {})
|
||||
this.$message.success("成功");
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}else{
|
||||
this.$confirm("确认关闭工单吗?", "温馨提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(async () => {
|
||||
ticketClose(this.retreatId,this.handleForm).then(res=>{
|
||||
this.limitedRetreat = false;
|
||||
this.getList();
|
||||
this.$store.dispatch("user/getCount", {})
|
||||
this.$message.success("成功");
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
handleRetreat(scope){
|
||||
handleClose(scope,index){
|
||||
if(index==='1'){
|
||||
this.handleTitle = '撤回工单';
|
||||
this.handleLabel='撤回原因';
|
||||
}else{
|
||||
this.handleTitle = '关闭工单';
|
||||
this.handleLabel='关闭原因';
|
||||
}
|
||||
this.limitedRetreat = true;
|
||||
this.retreatId = scope.row.id;
|
||||
},
|
||||
handleLogs(){},
|
||||
handlePicture(scope){
|
||||
let that = this;
|
||||
that.dialogVisible = true;
|
||||
|
|
@ -594,8 +664,6 @@
|
|||
that.watchedCreateTime = scope.row.create_time;
|
||||
that.pageForm.workflow = scope.row.workflow;
|
||||
let ticketId = scope.row.id;
|
||||
// that.getStates();
|
||||
// that.getTicketFlowlogs(ticketId);
|
||||
getWfFlowSteps( ticketId).then((res)=>{
|
||||
if(res.data){
|
||||
debugger;
|
||||
|
|
@ -616,12 +684,12 @@
|
|||
that.actives =that.flowSteps.length;
|
||||
}
|
||||
var g = new dagreD3.graphlib.Graph().setGraph({
|
||||
align: 'DL',
|
||||
rankdir: 'DL',
|
||||
nodesep: 100,
|
||||
edgesep: 100,
|
||||
ranksep: 50,
|
||||
marginx: 300,
|
||||
marginy: 50,
|
||||
edgesep: 10,//两条线之间的距离
|
||||
ranksep: 50,//节点之间的距离
|
||||
marginx: 60,
|
||||
marginy: 20,
|
||||
});
|
||||
//获取state得到节点
|
||||
getWfStateList(that.pageForm.workflow).then((response) => {
|
||||
|
|
@ -629,9 +697,6 @@
|
|||
let nodes = response.data;
|
||||
// 添加节点
|
||||
nodes.forEach((item) => {
|
||||
/*debugger;
|
||||
console.log(item.sort)
|
||||
console.log(that.state)*/
|
||||
if(item.sort==that.sort){
|
||||
g.setNode(item.id, {
|
||||
// 节点标签
|
||||
|
|
@ -645,7 +710,6 @@
|
|||
rx :5,//矩形节点圆角度
|
||||
ry :5
|
||||
});
|
||||
|
||||
}else{
|
||||
g.setNode(item.id, {
|
||||
// 节点标签
|
||||
|
|
@ -677,12 +741,14 @@
|
|||
g.setEdge(transition0.source_state_.id, transition0.source_state_.id+100000, {
|
||||
// 边标签
|
||||
label: transition0.name,
|
||||
// curve: d3.curveBasis,
|
||||
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,
|
||||
// curve: d3.curveBasis,
|
||||
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px"
|
||||
})
|
||||
})
|
||||
|
|
@ -690,6 +756,7 @@
|
|||
g.setEdge(transition0.source_state_.id, transition0.destination_state_.id, {
|
||||
// 边标签
|
||||
label: transition0.name,
|
||||
// curve: d3.curveBasis,
|
||||
// 边样式
|
||||
style: "fill:#ffffff;stroke:#c0c1c3;stroke-width:1.5px" // 根据后台数据来改变连线的颜色
|
||||
});
|
||||
|
|
@ -699,6 +766,10 @@
|
|||
g.nodes().forEach(function (v) {
|
||||
console.log("Node " + v + ": " + JSON.stringify(g.node(v)));
|
||||
});
|
||||
console.log("1111111111111");
|
||||
g.edges().forEach(function (e) {
|
||||
console.log("Edge " + e.v + " -> " + e.w + ": " + JSON.stringify(g.edge(e)));
|
||||
});
|
||||
// 创建渲染器
|
||||
let render = new dagreD3.render();
|
||||
// 选择 svg 并添加一个g元素作为绘图容器.
|
||||
|
|
@ -726,10 +797,14 @@
|
|||
if (!filList.length) {
|
||||
return
|
||||
}
|
||||
debugger;
|
||||
console.log(filList)
|
||||
filList.map((k) => {
|
||||
let filte = that.logs.filter(item=>{
|
||||
return item.state_.id = k.id;
|
||||
return item.state == k.id;
|
||||
})
|
||||
debugger;
|
||||
console.log(filte)
|
||||
//每个
|
||||
let str = '处理人:'+filte[0].participant_.name;
|
||||
strList.push(str)
|
||||
|
|
@ -800,6 +875,31 @@
|
|||
}
|
||||
})
|
||||
},
|
||||
handleLogs(scope){
|
||||
let id = scope.row.id;
|
||||
this.limitedFlowLogs = true;
|
||||
getTicketFlowlog(id).then(res=>{
|
||||
if(res.data){
|
||||
this.floeLogs = res.data;
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDelete(scope){
|
||||
let data = new Object(),ids=[];
|
||||
ids.push(scope.row.id);
|
||||
data.ids = ids;
|
||||
this.$confirm("确认删除该工单吗?", "温馨提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
ticketDestory(data).then(res=>{
|
||||
if(res.code==200){
|
||||
this.getList();
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
stepclick(){},
|
||||
closeMark(){
|
||||
this.dialogVisible = false;
|
||||
|
|
@ -809,6 +909,19 @@
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
::-webkit-scrollbar {
|
||||
width: 15px;
|
||||
}
|
||||
::-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,.5);
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.svgMark{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
@ -828,6 +941,8 @@
|
|||
margin: 10vh auto 0;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
max-height: 80vh;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.svgItem{
|
||||
padding: 20px 40px 0 ;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-card style="margin-left: 10px">
|
||||
<el-form ref="Form" :model="handleForm" label-width="100px" label-position="right" :rules="handleRule">
|
||||
<el-form ref="Form" :model="ticketForm" label-width="100px" label-position="right" :rules="ticketFormRule">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="标题" style="margin-bottom: 0">
|
||||
<span>{{ticketDetail.title}}</span>
|
||||
|
|
@ -114,24 +114,102 @@
|
|||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="审批意见">
|
||||
<el-input type="textarea" :rows="3" v-model="handleForm.suggestion" placeholder="审批意见"/>
|
||||
<el-input type="textarea" :rows="3" v-model="ticketForm.suggestion" placeholder="审批意见"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<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>
|
||||
<div style="text-align: center;display: flex;justify-content: space-between;">
|
||||
<div>
|
||||
<template v-if="ticketDetail.state_">
|
||||
<!--state_.type===0普通状态&&act_state==1进行中participant_type!==2处理人不是多人!ticketDetail.in_add_node非加签状态-->
|
||||
<el-button v-if="ticketDetail.act_state==1&&ticketDetail.state_.type===0&&!ticketDetail.in_add_node" type="primary" @click="handleAdd">加签</el-button>
|
||||
<el-button v-if="ticketDetail.state_.type===1" type="primary" @click="handleClick('2')">关闭</el-button>
|
||||
<el-button v-if="ticketDetail.state_.enable_retreat&&(userId==ticketDetail.create_by)&&ticketDetail.state_.type!==1" type="danger" @click="handleClick('1')">撤回</el-button>
|
||||
</template>
|
||||
</div>
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card style="margin-left: 10px">
|
||||
<el-table :data="logs" fit stripe
|
||||
style="width: 100%;border-top:1px solid #EBEEF5;"
|
||||
height="100"
|
||||
highlight-current-row
|
||||
v-el-height-adaptive-table="{bottomOffset: 60}">
|
||||
<el-table-column label="工单标题" min-width="100">
|
||||
<template slot-scope="scope" v-if="scope.row.ticket_data">
|
||||
<span>{{scope.row.ticket_data.title}}中</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="进行状态" min-width="100">
|
||||
<template slot-scope="scope" v-if="scope.row.state_">
|
||||
<span v-if="scope.row.state_.type==0">{{scope.row.state_.name}}中</span>
|
||||
<span v-else>已{{scope.row.state_.name}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作人" min-width="100">
|
||||
<template slot-scope="scope" v-if="scope.row.participant_">{{ scope.row.participant_.name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作意见" min-width="100" prop="suggestion">
|
||||
</el-table-column>
|
||||
<el-table-column label="更新时间" min-width="100" prop="update_time">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!--撤回1、关闭2、加签结束3-->
|
||||
<el-dialog :visible.sync="limitedHandle" :title="handleTitle">
|
||||
<el-row>
|
||||
<el-form ref="Form" :model="handleForm" label-width="100px" label-position="right">
|
||||
<el-col :span="1" style="height: 1px;"></el-col>
|
||||
<el-col :span="22" style="margin:3vh 0">
|
||||
<el-form-item :label="handleLabel">
|
||||
<el-input type="textarea" :rows="3" v-model="handleForm.suggestion" placeholder="撤回原因"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<div style="text-align: center">
|
||||
<el-button class="filter-item" type="" @click="retreatCancel">取消</el-button>
|
||||
<el-button class="filter-item" type="primary" @click="handleSubmit">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog :visible.sync="limitedAdd" title="加签">
|
||||
<el-row>
|
||||
<el-form ref="Form" :model="addForm" label-width="100px" label-position="right">
|
||||
<el-col :span="22">
|
||||
<el-form-item label="加签操作人">
|
||||
<el-select style="width: 100%" v-model="addForm.toadd_user" placeholder="加签操作人">
|
||||
<el-option v-for="item in staffs" :key="item.id" :label="item.name" :value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="加签原因">
|
||||
<el-input type="textarea" :rows="3" v-model="addForm.suggestion" placeholder="加签原因"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<div style="text-align: center">
|
||||
<el-button class="filter-item" type="" @click="addCancel">取消</el-button>
|
||||
<el-button class="filter-item" type="primary" @click="addSubmit">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script src="https://d3js.org/d3.v4.min.js"></script>
|
||||
<script>
|
||||
import { upUrl, upHeaders } from "@/api/file";
|
||||
import {getWorkflowList,getWfCustomfieldList,getWfStateList,getWfTransitionList,
|
||||
ticketHandle,getWfFlowSteps,getTicketDetail,getTicketTransitions,getTicketFlowlog } from "@/api/workflow";
|
||||
import { getUserList } from "@/api/user";
|
||||
import {getWorkflowList,getWfCustomfieldList,getWfStateList,getWfTransitionList,ticketHandle
|
||||
,getWfFlowSteps,getTicketDetail,getTicketTransitions,getTicketFlowlog,ticketRetreat,
|
||||
ticketAddNode,ticketAddNodeEnd,ticketClose } from "@/api/workflow";
|
||||
import Pagination from "@/components/Pagination";
|
||||
import dagreD3 from 'dagre-d3'
|
||||
import * as d3 from 'd3'
|
||||
|
|
@ -145,19 +223,31 @@
|
|||
actives:4,
|
||||
ticketId:0,
|
||||
stateSteps:0,
|
||||
handleForm:{
|
||||
ticketForm:{
|
||||
transition:'',
|
||||
ticket_data:{},
|
||||
suggestion:'',
|
||||
},
|
||||
handleType:'1',
|
||||
handleLabel:'撤回原因',
|
||||
handleTitle:'工单撤回',
|
||||
handleForm:{
|
||||
suggestion:'',
|
||||
},
|
||||
addForm:{
|
||||
toadd_user:'',
|
||||
suggestion:'',
|
||||
},
|
||||
tooltip:null,
|
||||
handleRule:{},
|
||||
upUrl: upUrl(),
|
||||
upHeaders: upHeaders(),
|
||||
userId:'',
|
||||
workflow:'',
|
||||
watchedName:'',
|
||||
watchedCreateTime:'',
|
||||
logs:[],
|
||||
staffs:[],
|
||||
edges: [],
|
||||
nodes: [],
|
||||
tooltip: [],
|
||||
|
|
@ -167,11 +257,15 @@
|
|||
ticketDetail:{},
|
||||
operationBtn:[],
|
||||
customfields:[],
|
||||
transitions:[]
|
||||
transitions:[],
|
||||
limitedAdd:false,
|
||||
limitedHandle:false,
|
||||
ticketFormRule:{},
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
let that = this;
|
||||
that.userId = sessionStorage.getItem('userId')
|
||||
let ticketId = that.ticketId = this.$route.params.ticketId;
|
||||
let workflow = that.workflow = this.$route.params.workflow;
|
||||
let handleTicketId = sessionStorage.getItem('handleTicketId');
|
||||
|
|
@ -190,6 +284,7 @@
|
|||
ticketId = that.ticketId = handleTicketId;
|
||||
workflow = that.workflow = handleTicketWorkflow;
|
||||
}
|
||||
this.getTicketFlowlogs(ticketId);
|
||||
getTicketTransitions(ticketId).then(res=>{
|
||||
this.operationBtn = res.data;
|
||||
})
|
||||
|
|
@ -201,6 +296,9 @@
|
|||
if(res.data){
|
||||
that.tooltip = that.createTooltip();
|
||||
that.ticketDetail = res.data;
|
||||
|
||||
console.log(that.ticketDetail.state_.enable_retreat);
|
||||
debugger;
|
||||
let state = res.data.state;
|
||||
let dat = that.flowSteps.filter((item)=>{
|
||||
return item.id==state;
|
||||
|
|
@ -211,12 +309,12 @@
|
|||
that.actives =that.flowSteps.length;
|
||||
}
|
||||
var g = new dagreD3.graphlib.Graph().setGraph({
|
||||
align: 'DL',
|
||||
rankdir: 'DL',
|
||||
nodesep: 100,
|
||||
edgesep: 100,
|
||||
ranksep: 50,
|
||||
marginx: 50,
|
||||
marginy: 50,
|
||||
edgesep: 10,//两条线之间的距离
|
||||
ranksep: 50,//节点之间的距离
|
||||
marginx: 60,
|
||||
marginy: 20,
|
||||
});
|
||||
//获取state得到节点
|
||||
getWfStateList(workflow).then((response) => {
|
||||
|
|
@ -319,7 +417,7 @@
|
|||
}
|
||||
filList.map((k) => {
|
||||
let filte = that.logs.filter(item=>{
|
||||
return item.state_.id = k.id;
|
||||
return item.state == k.id;
|
||||
})
|
||||
//每个
|
||||
let str = '处理人:'+filte[0].participant_.name;
|
||||
|
|
@ -345,6 +443,13 @@
|
|||
activated(){
|
||||
},
|
||||
methods:{
|
||||
getUser(){
|
||||
getUserList({}).then(res=>{
|
||||
if(res.data){
|
||||
this.staffs = res.data.results;
|
||||
}
|
||||
})
|
||||
},
|
||||
//工单流转记录
|
||||
getTicketFlowlogs(id){
|
||||
getTicketFlowlog(id).then(res=>{
|
||||
|
|
@ -403,19 +508,89 @@
|
|||
.style('display', 'none')
|
||||
},
|
||||
operationSubmit(id){
|
||||
this.handleForm.transition = id;
|
||||
this.handleForm.ticket_data = this.ticketDetail.ticket_data;
|
||||
this.ticketForm.transition = id;
|
||||
this.ticketForm.ticket_data = this.ticketDetail.ticket_data;
|
||||
let obj = new Object();
|
||||
obj.transition = id;
|
||||
obj.ticket_data = this.ticketDetail.ticket_data;
|
||||
obj.suggestion = this.handleForm.suggestion;
|
||||
console.log(this.handleForm);
|
||||
obj.suggestion = this.ticketForm.suggestion;
|
||||
console.log(this.ticketForm);
|
||||
ticketHandle(this.ticketId,obj).then(res=>{
|
||||
if (res.data){
|
||||
this.$store.dispatch("user/getCount", {})
|
||||
this.$router.replace({name:"ticket"})
|
||||
}
|
||||
})
|
||||
},
|
||||
retreatCancel(){
|
||||
this.limitedHandle = false;
|
||||
},
|
||||
handleSubmit(){
|
||||
let text = '',url='';
|
||||
if(this.handleType==='1'){
|
||||
text = "确认撤回工单吗?";
|
||||
url = ticketRetreat;
|
||||
}else if(this.handleType==='2'){
|
||||
text = "确认关闭工单吗?";
|
||||
url = ticketClose;
|
||||
}if(this.handleType==='3'){
|
||||
text = "确认处理工单吗?";
|
||||
url = ticketAddNodeEnd;
|
||||
}
|
||||
this.$confirm(text, "温馨提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(async () => {
|
||||
url(this.ticketId,this.handleForm).then(res=>{
|
||||
this.limitedHandle = false;
|
||||
this.$store.dispatch("user/getCount", {})
|
||||
this.$router.replace({name:"ticket"})
|
||||
this.$message.success("成功");
|
||||
})
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
},
|
||||
handleClick(index){
|
||||
this.limitedHandle = true;
|
||||
this.handleType = index;
|
||||
if(index==='1'){
|
||||
this.handleTitle = '工单撤回';
|
||||
this.handleLabel = '撤回原因';
|
||||
}else if(index==='2'){
|
||||
this.handleTitle = '工单关闭';
|
||||
this.handleLabel = '关闭原因';
|
||||
}else if(index==='3'){
|
||||
this.handleTitle = '加签处理';
|
||||
this.handleLabel = '加签意见';
|
||||
}
|
||||
},
|
||||
handleAdd(){
|
||||
this.getUser();
|
||||
this.limitedAdd = true;
|
||||
},
|
||||
addCancel(){
|
||||
this.limitedAdd = false;
|
||||
},
|
||||
addSubmit(){
|
||||
ticketAddNode(this.ticketId,this.addForm).then(res=>{
|
||||
if(res.data){
|
||||
this.limitedAdd = false;
|
||||
this.$nextTick(function () {
|
||||
this.limitedAdd = true;
|
||||
this.$store.dispatch("user/getCount", {})
|
||||
this.$router.replace({name:"ticket"})
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
addNode(){
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,197 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-card style="margin-bottom: 10px">
|
||||
<div>
|
||||
<el-select v-model="filter.workflow" placeholder="工作流" clearable style="width: 200px" class="filter-item" @change="handleFilter">
|
||||
<el-option
|
||||
v-for="item in workFlows"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select v-model="filter.act_state" placeholder="工单进行状态" clearable style="width: 200px" class="filter-item" @change="handleFilter">
|
||||
<el-option label="草稿中" value="0" />
|
||||
<el-option label="进行中" value="1" />
|
||||
<el-option label="被退回" value="2" />
|
||||
<el-option label="被撤回" value="3" />
|
||||
<el-option label="已完成" value="4" />
|
||||
<el-option label="已关闭" value="5" />
|
||||
</el-select>
|
||||
<el-button
|
||||
class="filter-item"
|
||||
type="primary"
|
||||
icon="el-icon-refresh-left"
|
||||
@click="resetFilter"
|
||||
>重置</el-button>
|
||||
<el-button
|
||||
class="filter-item"
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="handleFilter"
|
||||
>搜索</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card>
|
||||
<el-table :data="tickets"
|
||||
border fit stripe
|
||||
style="width: 100%"
|
||||
height="100"
|
||||
highlight-current-row
|
||||
v-el-height-adaptive-table="{bottomOffset: 60}">
|
||||
<el-table-column label="工单标题" min-width="100" prop="title">
|
||||
</el-table-column>
|
||||
<el-table-column label="当前状态" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.act_state==0" label="草稿中" value="scope.row.act_state">草稿中</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==1" label="进行中" value="scope.row.act_state">进行中</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==2" label="被退回" value="scope.row.act_state">被退回</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==3" label="被撤回" value="scope.row.act_state">被撤回</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==4" label="已完成" value="scope.row.act_state">已完成</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==5" label="已关闭" value="scope.row.act_state">已关闭</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="进行状态" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.state_.type==0">{{scope.row.state_.name}}中</span>
|
||||
<span v-else>已{{scope.row.state_.name}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" min-width="100">
|
||||
<template slot-scope="scope">{{ scope.row.workflow_.name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" min-width="100" prop="create_time">
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="操作">
|
||||
<template slot-scope="scope">
|
||||
<el-link type="success" @click="handleLogs(scope)">工单日志</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
:total="total"
|
||||
:page.sync="filter.page"
|
||||
:limit.sync="filter.page_size"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</el-card>
|
||||
<el-dialog :visible.sync="limitedFlowLogs" title="工单日志">
|
||||
<el-table :data="floeLogs" fit stripe
|
||||
style="width: 100%;border-top:1px solid #EBEEF5;"
|
||||
height="100"
|
||||
highlight-current-row
|
||||
v-el-height-adaptive-table="{bottomOffset: 60}">
|
||||
<el-table-column label="工单标题" min-width="100">
|
||||
<template slot-scope="scope" v-if="scope.row.ticket_data">
|
||||
<span>{{scope.row.ticket_data.title}}中</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column label="当前状态" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.act_state==0" label="草稿中" value="scope.row.act_state">草稿中</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==1" label="进行中" value="scope.row.act_state">进行中</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==2" label="被退回" value="scope.row.act_state">被退回</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==3" label="被撤回" value="scope.row.act_state">被撤回</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==4" label="已完成" value="scope.row.act_state">已完成</el-tag>
|
||||
<el-tag v-else-if="scope.row.act_state==5" label="已关闭" value="scope.row.act_state">已关闭</el-tag>
|
||||
</template>
|
||||
</el-table-column>-->
|
||||
<el-table-column label="进行状态" min-width="100">
|
||||
<template slot-scope="scope" v-if="scope.row.state_">
|
||||
<span v-if="scope.row.state_.type==0">{{scope.row.state_.name}}中</span>
|
||||
<span v-else>已{{scope.row.state_.name}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作人" min-width="100">
|
||||
<template slot-scope="scope" v-if="scope.row.participant_">{{ scope.row.participant_.name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作意见" min-width="100" prop="suggestion">
|
||||
</el-table-column>
|
||||
<el-table-column label="更新时间" min-width="100" prop="update_time">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {getWorkflowList,getTickets,getTicketFlowlog} from "@/api/workflow";
|
||||
import Pagination from "@/components/Pagination";
|
||||
export default {
|
||||
name: "workFlowTickets",
|
||||
components: { Pagination },
|
||||
inject:['reload'],
|
||||
data(){
|
||||
return{
|
||||
sort:0,
|
||||
total:0,
|
||||
filter:{
|
||||
page:1,
|
||||
page_size:20,
|
||||
state:'',
|
||||
act_state:'',
|
||||
workflow:'',
|
||||
category:'all',
|
||||
search:'',
|
||||
},
|
||||
listLoading:false,
|
||||
limitedFlowLogs:false,
|
||||
floeLogs:[],
|
||||
tickets:[],
|
||||
workFlows:[],
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.workflow = this.$route.params.workflow;
|
||||
if(this.workflow){//有传参
|
||||
this.filter.workflow = parseInt(this.workflow);
|
||||
}
|
||||
this.getList();
|
||||
this.getWorkFlow();
|
||||
},
|
||||
methods:{
|
||||
getList(){
|
||||
this.listLoading = true;
|
||||
getTickets( this.filter).then((res)=>{
|
||||
if(res.data.results){
|
||||
this.total = res.data.count;
|
||||
this.tickets = res.data.results;
|
||||
this.listLoading = false;
|
||||
}
|
||||
})
|
||||
},
|
||||
//获取工作流
|
||||
getWorkFlow(){
|
||||
let listForm = {page:0};
|
||||
getWorkflowList(listForm).then((response) => {
|
||||
if (response.data) {
|
||||
this.workFlows = response.data;
|
||||
}
|
||||
});
|
||||
},
|
||||
//查询搜索
|
||||
handleFilter(){
|
||||
this.filter.page=1;
|
||||
this.getList();
|
||||
},
|
||||
//筛选重置
|
||||
resetFilter(){
|
||||
this.filter.workflow = '';
|
||||
this.filter.act_state = '';
|
||||
this.getList();
|
||||
},
|
||||
handleLogs(scope){
|
||||
let id = scope.row.id;
|
||||
this.limitedFlowLogs = true;
|
||||
getTicketFlowlog(id).then(res=>{
|
||||
if(res.data){
|
||||
this.floeLogs = res.data;
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-18 05:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('hrm', '0002_auto_20210924_1127'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='employee',
|
||||
name='face_data',
|
||||
field=models.JSONField(blank=True, null=True, verbose_name='人脸识别数据'),
|
||||
),
|
||||
]
|
||||
|
|
@ -28,9 +28,12 @@ class Employee(CommonAModel):
|
|||
academic = models.CharField('学历', max_length=50, null=True, blank=True)
|
||||
jobstate = models.IntegerField('在职状态', choices=jobstate_choices, default=1)
|
||||
job = models.ForeignKey(Position, null=True, blank=True, on_delete=models.SET_NULL, verbose_name='岗位')
|
||||
face_data = models.JSONField('人脸识别数据', null=True, blank=True)
|
||||
class Meta:
|
||||
verbose_name = '员工补充信息'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return self.name
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from apps.system.models import User
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
|
||||
from rest_framework import serializers
|
||||
from .models import Employee
|
||||
from apps.system.serializers import UserListSerializer, UserSimpleSerializer
|
||||
from django.db.models.query import Prefetch
|
||||
|
|
@ -20,3 +20,6 @@ class EmployeeSerializer(ModelSerializer):
|
|||
# queryset=User.objects.filter(employee_user__isnull=True))
|
||||
# )
|
||||
# return queryset
|
||||
|
||||
class FaceLoginSerializer(serializers.Serializer):
|
||||
base64 = serializers.CharField()
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
from django.db.models import base
|
||||
from rest_framework import urlpatterns
|
||||
from apps.hrm.views import EmployeeViewSet
|
||||
from apps.hrm.views import EmployeeViewSet, FaceLogin
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('employee', EmployeeViewSet, basename='employee')
|
||||
urlpatterns = [
|
||||
path('facelogin/', FaceLogin.as_view()),
|
||||
path('', include(router.urls)),
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,34 @@
|
|||
from django.shortcuts import render
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet, GenericViewSet
|
||||
from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin
|
||||
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
|
||||
from apps.hrm.models import Employee
|
||||
from apps.hrm.serializers import EmployeeSerializer
|
||||
from apps.hrm.serializers import EmployeeSerializer, FaceLoginSerializer
|
||||
import face_recognition
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
import logging
|
||||
from rest_framework.generics import CreateAPIView
|
||||
from rest_framework import status
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
|
||||
from apps.system.models import User
|
||||
logger = logging.getLogger('log')
|
||||
|
||||
|
||||
def load_face_data(username:int, path:str):
|
||||
"""
|
||||
将某用户face_encoding加载进缓存
|
||||
"""
|
||||
face_datas = cache.get_or_set('face_datas', {}, timeout=None)
|
||||
photo_path = settings.BASE_DIR + path
|
||||
picture_of_me = face_recognition.load_image_file(photo_path)
|
||||
my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
|
||||
face_datas[username] = my_face_encoding
|
||||
cache.set('face_datas', face_datas, timeout=None)
|
||||
return my_face_encoding
|
||||
|
||||
# Create your views here.
|
||||
class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMixin, RetrieveModelMixin, GenericViewSet):
|
||||
"""
|
||||
|
|
@ -12,4 +37,73 @@ class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMix
|
|||
perms_map = {'get': '*', 'put': 'employee_update'}
|
||||
queryset = Employee.objects.all()
|
||||
serializer_class = EmployeeSerializer
|
||||
ordering = ['-pk']
|
||||
ordering = ['-pk']
|
||||
|
||||
def perform_update(self, serializer):
|
||||
instance = serializer.save(update_by = self.request.user)
|
||||
try:
|
||||
photo_path = settings.BASE_DIR + instance.photo
|
||||
picture_of_me = face_recognition.load_image_file(photo_path)
|
||||
my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
|
||||
instance.face_data = my_face_encoding.tolist()
|
||||
instance.save()
|
||||
except:
|
||||
logger.error('人脸识别出错')
|
||||
|
||||
import uuid
|
||||
import base64
|
||||
import os
|
||||
|
||||
def tran64(s):
|
||||
missing_padding = len(s) % 4
|
||||
if missing_padding != 0:
|
||||
s = s+'='* (4 - missing_padding)
|
||||
return s
|
||||
|
||||
class FaceLogin(CreateAPIView):
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
serializer_class = FaceLoginSerializer
|
||||
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
人脸识别登录
|
||||
"""
|
||||
# serializer = FaceLoginSerializer(data=request.data)
|
||||
# serializer.is_valid(raise_exception=True)
|
||||
filename = str(uuid.uuid4())
|
||||
filepath = settings.BASE_DIR +'/temp/' + filename +'.png'
|
||||
with open(filepath, 'wb') as f:
|
||||
data = tran64(request.data.get('base64').replace(' ', '+'))
|
||||
f.write(base64.urlsafe_b64decode(data))
|
||||
# picture_of_me = face_recognition.load_image_file(settings.BASE_DIR +'/temp/me.png')
|
||||
# my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
|
||||
#results = face_recognition.compare_faces([my_face_encoding], unknown_face_encoding, tolerance=0.2)
|
||||
try:
|
||||
unknown_picture = face_recognition.load_image_file(filepath)
|
||||
unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0]
|
||||
os.remove(filepath)
|
||||
except:
|
||||
return Response('头像解码失败', status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# 匹配人脸库
|
||||
user_faces = Employee.objects.filter(face_data__isnull=False, user__is_active=True).values('user', 'face_data')
|
||||
user_l = []
|
||||
face_l = []
|
||||
for i in user_faces:
|
||||
user_l.append(i['user'])
|
||||
face_l.append(i['face_data'])
|
||||
|
||||
results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.5)
|
||||
for index, value in enumerate(results):
|
||||
if value:
|
||||
# 识别成功
|
||||
user = User.objects.get(id=user_l[index])
|
||||
refresh = RefreshToken.for_user(user)
|
||||
return Response({
|
||||
'refresh': str(refresh),
|
||||
'access': str(refresh.access_token),
|
||||
'username':user.username
|
||||
})
|
||||
return Response('未找到对应用户', status=status.HTTP_400_BAD_REQUEST)
|
||||
|
|
@ -6,7 +6,7 @@ from apps.system.models import CommonAModel, CommonBModel, Organization, User, D
|
|||
from utils.model import SoftModel, BaseModel
|
||||
from simple_history.models import HistoricalRecords
|
||||
from apps.mtm.models import Material
|
||||
|
||||
from apps.pm.models import SubProductionPlan
|
||||
|
||||
|
||||
class WareHouse(CommonAModel):
|
||||
|
|
@ -57,4 +57,13 @@ class FIFO(CommonAModel):
|
|||
(3, '采购入库'),
|
||||
(4, '生产入库')
|
||||
)
|
||||
type = models.IntegerField('出入库类型', default=1)
|
||||
type = models.IntegerField('出入库类型', default=1)
|
||||
operator = models.ForeignKey(User, verbose_name='操作人', on_delete=models.CASCADE)
|
||||
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.DO_NOTHING, null=True, blank=True)
|
||||
|
||||
# class FIFODetail(CommonAModel):
|
||||
# """
|
||||
# 领料详细记录
|
||||
# """
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-18 02:57
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mtm', '0022_auto_20211014_0944'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='recordformfield',
|
||||
name='high_limit',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name='上限值'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recordformfield',
|
||||
name='high_rule',
|
||||
field=models.IntegerField(blank=True, choices=[(1, '小于'), (2, '小于等于')], null=True, verbose_name='上限规则'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recordformfield',
|
||||
name='low_limit',
|
||||
field=models.FloatField(blank=True, null=True, verbose_name='下限值'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recordformfield',
|
||||
name='low_rule',
|
||||
field=models.IntegerField(blank=True, choices=[(1, '大于'), (2, '大于等于')], null=True, verbose_name='下限规则'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recordformfield',
|
||||
name='need_judge',
|
||||
field=models.BooleanField(default=False, verbose_name='需要判定'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recordformfield',
|
||||
name='rule_expression',
|
||||
field=models.JSONField(default=list, help_text='判定表达式, 格式为[{"expression":"{value} > 3 and {value}<10"}] 其中{}用于填充工单的字段key,运算时会换算成实际的值,符合条件返回true,表达式只支持简单的运算或datetime/time运算.以首次匹配成功的条件为准,所以多个条件不要有冲突', verbose_name='判定表达式'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='usedstep',
|
||||
name='step',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='usedstep', to='mtm.step', verbose_name='子工序'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-19 01:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mtm', '0023_auto_20211018_1057'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='recordformfield',
|
||||
name='rule_expression',
|
||||
field=models.JSONField(default=list, help_text='判定表达式, 格式为[{"expression":"{value} > 3 and {value}<10"}] 其中{}用于填充的字段key,运算时会换算成实际的值,符合条件返回true,表达式只支持简单的运算或datetime/time运算.以首次匹配成功的条件为准,所以多个条件不要有冲突', verbose_name='判定表达式'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-21 08:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mtm', '0024_alter_recordformfield_rule_expression'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='outputmaterial',
|
||||
name='is_main',
|
||||
field=models.BooleanField(default=True, verbose_name='是否主产出'),
|
||||
),
|
||||
]
|
||||
|
|
@ -110,6 +110,14 @@ class RecordFormField(CommonAModel):
|
|||
('selects', '多选下拉'),
|
||||
('textarea', '文本域'),
|
||||
)
|
||||
high_rule_choices = (
|
||||
(1, '小于'),
|
||||
(2, '小于等于'),
|
||||
)
|
||||
low_rule_choices = (
|
||||
(1, '大于'),
|
||||
(2, '大于等于'),
|
||||
)
|
||||
form = models.ForeignKey(RecordForm, on_delete=models.CASCADE, verbose_name='关联表格')
|
||||
field_type = models.CharField('类型', max_length=50, choices=field_type_choices)
|
||||
field_key = models.CharField('字段标识', max_length=50, help_text='字段类型请尽量特殊,避免与系统中关键字冲突')
|
||||
|
|
@ -119,6 +127,13 @@ class RecordFormField(CommonAModel):
|
|||
field_choice = models.JSONField('radio、checkbox、select的选项', default=dict, blank=True, null=True,
|
||||
help_text='radio,checkbox,select,multiselect类型可供选择的选项,格式为json如:{"1":"中国", "2":"美国"},注意数字也需要引号')
|
||||
sort = models.IntegerField('排序号', default=1)
|
||||
need_judge = models.BooleanField('需要判定', default=False)
|
||||
high_limit = models.FloatField('上限值', null=True, blank=True)
|
||||
high_rule = models.IntegerField('上限规则', choices=high_rule_choices, null=True, blank=True)
|
||||
low_limit = models.FloatField('下限值', null=True, blank=True)
|
||||
low_rule = models.IntegerField('下限规则', choices=low_rule_choices, null=True, blank=True)
|
||||
rule_expression = models.JSONField('判定表达式', default=list, help_text='判定表达式, 格式为[{"expression":"{value} > 3 and {value}<10"}] 其中{}用于填充的字段key,运算时会换算成实际的值,符合条件返回true,表达式只支持简单的运算或datetime/time运算.以首次匹配成功的条件为准,所以多个条件不要有冲突' )
|
||||
|
||||
class Meta:
|
||||
verbose_name = '记录表格字段'
|
||||
verbose_name_plural = verbose_name
|
||||
|
|
@ -162,6 +177,7 @@ class OutputMaterial(CommonAModel):
|
|||
输出物料
|
||||
"""
|
||||
material = models.ForeignKey(Material, verbose_name='输出物料', on_delete=models.CASCADE, related_name='outputmaterial')
|
||||
is_main = models.BooleanField('是否主产出', default=True) # 以该产品完成度计算进度
|
||||
count = models.FloatField('产出量', default=1)
|
||||
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
|
||||
sort = models.IntegerField('排序号', default=1)
|
||||
|
|
@ -174,7 +190,7 @@ class UsedStep(CommonAModel):
|
|||
"""
|
||||
涉及的生产子工序
|
||||
"""
|
||||
step = models.ForeignKey(Step, verbose_name='子工序', on_delete=models.CASCADE, related_name='usedsteps')
|
||||
step = models.ForeignKey(Step, verbose_name='子工序', on_delete=models.CASCADE, related_name='usedstep')
|
||||
remark = models.TextField('生产备注', null=True, blank=True)
|
||||
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
|
||||
|
||||
|
|
|
|||
|
|
@ -98,9 +98,11 @@ class InputMaterialUpdateSerializer(serializers.ModelSerializer):
|
|||
class OutputMaterialSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = OutputMaterial
|
||||
fields = ['count', 'sort', 'material', 'subproduction']
|
||||
fields = ['count', 'sort', 'material', 'subproduction', 'is_main']
|
||||
|
||||
def create(self, validated_data):
|
||||
if OutputMaterial.objects.filter(subproduction=validated_data['subproduction'], is_deleted=False, is_main=True).exists():
|
||||
raise ValidationError('主产出只能有1个')
|
||||
if OutputMaterial.objects.filter(material=validated_data['material'], subproduction=validated_data['subproduction'], is_deleted=False).exists():
|
||||
raise ValidationError('该物料已存在')
|
||||
return super().create(validated_data)
|
||||
|
|
@ -108,7 +110,7 @@ class OutputMaterialSerializer(serializers.ModelSerializer):
|
|||
class OutputMaterialUpdateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = OutputMaterial
|
||||
fields = ['count', 'sort']
|
||||
fields = ['count', 'sort', 'is_main']
|
||||
|
||||
class UsedStepCreateSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
|
|
@ -175,7 +177,7 @@ class RecordFormFieldSerializer(serializers.ModelSerializer):
|
|||
class RecordFormFieldCreateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = RecordFormField
|
||||
fields = ['form', 'field_type', 'field_key', 'field_name', 'boolean_field_display', 'field_choice', 'sort']
|
||||
fields = ['form', 'field_type', 'field_key', 'field_name', 'boolean_field_display', 'field_choice', 'sort', 'need_judge', 'high_limit', 'high_rule', 'low_limit', 'low_rule', 'rule_expression']
|
||||
|
||||
def validate(self, data):
|
||||
if RecordFormField.objects.filter(field_key=data['field_key'], form=data['form'], is_deleted=False).exists():
|
||||
|
|
@ -185,7 +187,7 @@ class RecordFormFieldCreateSerializer(serializers.ModelSerializer):
|
|||
class RecordFormFieldUpdateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = RecordFormField
|
||||
fields = ['field_type', 'field_name', 'boolean_field_display', 'field_choice', 'sort']
|
||||
fields = ['field_type', 'field_name', 'boolean_field_display', 'field_choice', 'sort', 'need_judge', 'high_limit', 'high_rule', 'low_limit', 'low_rule', 'rule_expression']
|
||||
|
||||
class RecordFormFieldSimpleSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-15 02:14
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pm', '0003_auto_20211014_1503'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='subproductionplan',
|
||||
name='steps',
|
||||
field=models.JSONField(default=list, verbose_name='工艺步骤'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-19 01:44
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mtm', '0024_alter_recordformfield_rule_expression'),
|
||||
('pm', '0004_subproductionplan_steps'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='subproductionplan',
|
||||
name='state',
|
||||
field=models.IntegerField(default=0, verbose_name='状态'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='SubProductionProgress',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
|
||||
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
|
||||
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
|
||||
('type', models.IntegerField(default=1, verbose_name='物料应用类型')),
|
||||
('count', models.IntegerField(verbose_name='应出入数')),
|
||||
('count_real', models.IntegerField(verbose_name='实际出入数')),
|
||||
('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mtm.material', verbose_name='关联物料')),
|
||||
('subproduction_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='progress_subplan', to='pm.subproductionplan', verbose_name='关联子生产计划')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-19 01:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pm', '0005_auto_20211019_0944'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='subproductionprogress',
|
||||
name='count_real',
|
||||
field=models.IntegerField(default=0, verbose_name='实际出入数'),
|
||||
),
|
||||
]
|
||||
|
|
@ -31,20 +31,35 @@ class SubProductionPlan(CommonAModel):
|
|||
"""
|
||||
子生产计划
|
||||
"""
|
||||
state_choices=(
|
||||
(0, '制定中'),
|
||||
(1, '已下达'),
|
||||
(2, '已接收'),
|
||||
(3, '生产中'),
|
||||
(4, '已完成')
|
||||
)
|
||||
production_plan = models.ForeignKey(ProductionPlan, verbose_name='关联主生产计划', on_delete=models.CASCADE)
|
||||
subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE)
|
||||
start_date = models.DateField('计划开工日期')
|
||||
end_date = models.DateField('计划完工日期')
|
||||
workshop = models.ForeignKey(Organization, verbose_name='生产车间', on_delete=models.CASCADE)
|
||||
process = models.ForeignKey(Process, verbose_name='关联大工序', on_delete=models.CASCADE)
|
||||
# steps = models.JSONField('工艺步骤', default=list)
|
||||
steps = models.JSONField('工艺步骤', default=list)
|
||||
state = models.IntegerField('状态', default=0)
|
||||
class Meta:
|
||||
verbose_name = '子生产计划'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
# class ProductionProgress(BaseModel):
|
||||
# """
|
||||
# 子计划生产进度
|
||||
# """
|
||||
# subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE)
|
||||
# material = models.
|
||||
class SubProductionProgress(BaseModel):
|
||||
"""
|
||||
子计划生产进度统计表
|
||||
"""
|
||||
type_choices=(
|
||||
(1, '输入物料'),
|
||||
(2, '输出物料')
|
||||
)
|
||||
subproduction_plan = models.ForeignKey(SubProductionPlan, verbose_name='关联子生产计划', on_delete=models.CASCADE, related_name='progress_subplan')
|
||||
material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE)
|
||||
type = models.IntegerField('物料应用类型', default=1)
|
||||
count = models.IntegerField('应出入数')
|
||||
count_real = models.IntegerField('实际出入数', default=0)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from apps.pm.models import ProductionPlan, SubProductionPlan
|
||||
from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress
|
||||
from rest_framework import serializers
|
||||
from apps.sam.serializers import OrderSerializer
|
||||
from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer
|
||||
|
|
@ -35,3 +35,12 @@ class SubProductionPlanUpdateSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
model = SubProductionPlan
|
||||
fields = ['start_date', 'end_date']
|
||||
|
||||
class GenSubPlanSerializer(serializers.Serializer):
|
||||
pass
|
||||
|
||||
class SubProductionProgressSerializer(serializers.ModelSerializer):
|
||||
material_ = MaterialSimpleSerializer(source='material', read_only=True)
|
||||
class Meta:
|
||||
model = SubProductionProgress
|
||||
fields = '__all__'
|
||||
|
|
@ -2,17 +2,18 @@ from rest_framework import serializers
|
|||
from rest_framework.views import APIView
|
||||
from apps.em.models import Equipment
|
||||
from apps.em.serializers import EquipmentSerializer
|
||||
from apps.mtm.models import InputMaterial, Step, SubProduction, UsedStep
|
||||
from apps.mtm.models import InputMaterial, OutputMaterial, Step, SubProduction, UsedStep
|
||||
from apps.system.mixins import CreateUpdateModelAMixin
|
||||
from apps.pm.serializers import ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer
|
||||
from apps.pm.serializers import GenSubPlanSerializer, ProductionPlanCreateFromOrderSerializer, ProductionPlanSerializer, ResourceCalListSerializer, ResourceCalSerializer, SubProductionPlanListSerializer, SubProductionPlanUpdateSerializer, SubProductionProgressSerializer
|
||||
from rest_framework.mixins import CreateModelMixin, ListModelMixin, UpdateModelMixin
|
||||
from apps.pm.models import ProductionPlan, SubProductionPlan
|
||||
from apps.pm.models import ProductionPlan, SubProductionProgress, SubProductionPlan
|
||||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||
from django.shortcuts import render
|
||||
from apps.sam.models import Order
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.decorators import action
|
||||
from django.db.models import F
|
||||
# Create your views here.
|
||||
|
||||
def updateOrderPlanedCount(order):
|
||||
|
|
@ -59,7 +60,7 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
|
|||
updateOrderPlanedCount(instance.order)
|
||||
return Response()
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer)
|
||||
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=GenSubPlanSerializer)
|
||||
def gen_subplan(self, request, pk=None):
|
||||
"""
|
||||
生成子计划
|
||||
|
|
@ -69,9 +70,16 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel
|
|||
raise APIException('已生成子计划')
|
||||
subps = SubProduction.objects.filter(product=production_plan.product).order_by('process__number')
|
||||
for i in subps:
|
||||
SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i,
|
||||
steps = Step.objects.filter(usedstep__subproduction=i, usedstep__subproduction__is_deleted=False,
|
||||
usedstep__is_deleted=False, is_deleted=False).values('id', 'number', 'name', 'usedstep__remark')
|
||||
instance = SubProductionPlan.objects.create(production_plan=production_plan, subproduction=i,
|
||||
start_date=production_plan.start_date, end_date=production_plan.end_date,
|
||||
workshop=i.process.workshop, process=i.process, create_by=request.user)
|
||||
workshop=i.process.workshop, process=i.process, create_by=request.user,
|
||||
steps = list(steps))
|
||||
for m in InputMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'):
|
||||
SubProductionProgress.objects.create(material=m.material, type=1, count=m.count, subproduction_plan=instance)
|
||||
for m in OutputMaterial.objects.filter(subproduction=i, is_deleted=False).order_by('sort'):
|
||||
SubProductionProgress.objects.create(material=m.material, type=2, count=m.count, subproduction_plan=instance)
|
||||
production_plan.is_planed=True
|
||||
production_plan.save()
|
||||
return Response()
|
||||
|
|
@ -92,7 +100,28 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo
|
|||
return SubProductionPlanListSerializer
|
||||
elif self.action == 'update':
|
||||
return SubProductionPlanUpdateSerializer
|
||||
return SubProductionPlanListSerializer
|
||||
|
||||
@action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=SubProductionProgressSerializer)
|
||||
def progress(self, request, pk=None):
|
||||
"""
|
||||
生产进度详情
|
||||
"""
|
||||
obj = self.get_object()
|
||||
serializer = SubProductionProgressSerializer(instance=obj.progress_subplan, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer)
|
||||
def issue(self, request, pk=None):
|
||||
"""
|
||||
下达任务
|
||||
"""
|
||||
obj = self.get_object()
|
||||
if obj.state == 0:
|
||||
obj.state = 1
|
||||
obj.save()
|
||||
return Response()
|
||||
raise APIException('计划状态有误')
|
||||
|
||||
class ResourceViewSet(GenericViewSet):
|
||||
|
||||
|
|
@ -135,8 +164,8 @@ class ResourceViewSet(GenericViewSet):
|
|||
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)
|
||||
steps = Step.objects.filter(usedstep__is_deleted=False, usedstep__subproduction__in=subproductions)
|
||||
equips = Equipment.objects.filter(step_equips__in=steps, is_deleted=False).distinct()
|
||||
serializer = EquipmentSerializer(instance=equips, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
|
|
|||
|
|
@ -199,5 +199,3 @@ class UserCreateSerializer(serializers.ModelSerializer):
|
|||
return phone
|
||||
|
||||
|
||||
class FaceLoginSerializer(serializers.Serializer):
|
||||
base64 = serializers.CharField()
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from django.urls import path, include
|
||||
from .views import FaceLogin, TaskList, UserViewSet, OrganizationViewSet, PermissionViewSet, RoleViewSet, PositionViewSet, TestView, DictTypeViewSet, DictViewSet, PTaskViewSet
|
||||
from .views import TaskList, UserViewSet, OrganizationViewSet, PermissionViewSet, RoleViewSet, PositionViewSet, TestView, DictTypeViewSet, DictViewSet, PTaskViewSet
|
||||
from rest_framework import routers
|
||||
|
||||
|
||||
|
|
@ -15,6 +15,5 @@ router.register('ptask', PTaskViewSet, basename="ptask")
|
|||
urlpatterns = [
|
||||
path('', include(router.urls)),
|
||||
path('task/', TaskList.as_view()),
|
||||
path('test/', TestView.as_view()),
|
||||
path('facelogin/', FaceLogin.as_view())
|
||||
path('test/', TestView.as_view())
|
||||
]
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ from rest_framework.views import APIView
|
|||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from rest_framework.exceptions import ValidationError, ParseError
|
||||
from apps.hrm.models import Employee
|
||||
from utils.queryset import get_child_queryset2
|
||||
|
||||
from .filters import UserFilter
|
||||
|
|
@ -29,7 +30,7 @@ from .models import (Dict, DictType, File, Organization, Permission, Position,
|
|||
Role, User)
|
||||
from .permission import RbacPermission, get_permission_list
|
||||
from .permission_data import RbacFilterSet
|
||||
from .serializers import (DictSerializer, DictTypeSerializer, FaceLoginSerializer, FileSerializer,
|
||||
from .serializers import (DictSerializer, DictTypeSerializer, FileSerializer,
|
||||
OrganizationSerializer, PermissionSerializer,
|
||||
PositionSerializer, RoleSerializer, PTaskSerializer,PTaskCreateUpdateSerializer,
|
||||
UserCreateSerializer, UserListSerializer,
|
||||
|
|
@ -352,43 +353,3 @@ class FileViewSet(CreateModelMixin, DestroyModelMixin, RetrieveModelMixin, ListM
|
|||
instance.save()
|
||||
|
||||
|
||||
|
||||
#import face_recognition
|
||||
import uuid
|
||||
import base64
|
||||
import os
|
||||
|
||||
def tran64(s):
|
||||
missing_padding = len(s) % 4
|
||||
if missing_padding != 0:
|
||||
s = s+'='* (4 - missing_padding)
|
||||
return s
|
||||
|
||||
class FaceLogin(CreateAPIView):
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
serializer_class = FaceLoginSerializer
|
||||
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
人脸识别登录
|
||||
"""
|
||||
# serializer = FaceLoginSerializer(data=request.data)
|
||||
# serializer.is_valid(raise_exception=True)
|
||||
filename = str(uuid.uuid4())
|
||||
filepath = settings.BASE_DIR +'/temp/' + filename +'.png'
|
||||
with open(filepath, 'wb') as f:
|
||||
data = tran64(request.data.get('base64').replace(' ', '+'))
|
||||
f.write(base64.urlsafe_b64decode(data))
|
||||
# picture_of_me = face_recognition.load_image_file(settings.BASE_DIR +'/temp/me.png')
|
||||
# my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
|
||||
# unknown_picture = face_recognition.load_image_file(filepath)
|
||||
# unknown_face_encoding = face_recognition.face_encodings(unknown_picture)[0]
|
||||
#results = face_recognition.compare_faces([my_face_encoding], unknown_face_encoding, tolerance=0.2)
|
||||
os.remove(filepath)
|
||||
# if results[0] == True:
|
||||
# return Response('这是曹前明')
|
||||
# else:
|
||||
# return Response('这不是曹前明')
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ class TicketFilterSet(filters.FilterSet):
|
|||
elif value == 'duty':
|
||||
queryset = queryset.filter(participant__contains=user.id).exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED])
|
||||
elif value == 'worked':
|
||||
queryset = queryset.filter(ticketflow_ticket__participant=user).exclude(create_by=user)
|
||||
queryset = queryset.filter(ticketflow_ticket__participant=user).exclude(create_by=user).order_by('-update_time').distinct()
|
||||
elif value == 'cc':
|
||||
queryset = queryset.filter(ticketflow_ticket__participant_cc__contains=user.id).exclude(create_by=user).order_by('-update_time').distinct()
|
||||
elif value == 'all':
|
||||
pass
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-19 01:58
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('wf', '0013_alter_ticketflow_transition'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='WfScript',
|
||||
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='删除标记')),
|
||||
('usage', models.IntegerField(choices=[(1, '获取处理人'), (2, '执行操作')], default=1, verbose_name='脚本用途')),
|
||||
('wait', models.BooleanField(default=True, verbose_name='是否等待执行完成')),
|
||||
('name', models.CharField(max_length=100, verbose_name='脚本名称')),
|
||||
('content', models.TextField(verbose_name='脚本内容')),
|
||||
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wfscript_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
|
||||
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wfscript_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
|
||||
('workflow', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wf.workflow', verbose_name='关联工作流')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-21 02:49
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('system', '0003_auto_20210812_0909'),
|
||||
('wf', '0014_wfscript'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='state',
|
||||
name='filter_policy',
|
||||
field=models.IntegerField(choices=[(0, '无'), (1, '和工单同属一及上级部门'), (2, '和创建人同属一及上级部门'), (3, '和上步处理人同属一及上级部门')], default=0, verbose_name='参与人过滤策略'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ticket',
|
||||
name='belong_dept',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ticket_belong_dept', to='system.organization', verbose_name='所属部门'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='state',
|
||||
name='participant_type',
|
||||
field=models.IntegerField(blank=True, choices=[(0, '无处理人'), (1, '个人'), (2, '多人'), (3, '部门'), (4, '角色'), (5, '变量'), (6, '脚本'), (7, '工单的字段'), (8, '父工单的字段'), (9, '代码获取')], default=1, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本,7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容。 初始状态请选择类型5,参与人填create_by', verbose_name='参与者类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='participant_type',
|
||||
field=models.IntegerField(choices=[(0, '无处理人'), (1, '个人'), (2, '多人'), (3, '部门'), (4, '角色'), (5, '变量'), (6, '脚本'), (7, '工单的字段'), (8, '父工单的字段'), (9, '代码获取')], default=0, help_text='0.无处理人,1.个人,2.多人', verbose_name='当前处理人类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticketflow',
|
||||
name='participant_type',
|
||||
field=models.IntegerField(choices=[(0, '无处理人'), (1, '个人'), (2, '多人'), (3, '部门'), (4, '角色'), (5, '变量'), (6, '脚本'), (7, '工单的字段'), (8, '父工单的字段'), (9, '代码获取')], default=0, help_text='0.无处理人,1.个人,2.多人', verbose_name='处理人类型'),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
# Generated by Django 3.2.6 on 2021-10-24 15:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wf', '0015_auto_20211021_1049'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='state',
|
||||
name='participant_cc',
|
||||
field=models.JSONField(blank=True, default=list, help_text='抄送给(userid列表)', verbose_name='抄送给'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ticketflow',
|
||||
name='participant_cc',
|
||||
field=models.JSONField(blank=True, default=list, help_text='抄送给(userid列表)', verbose_name='抄送给'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='wfscript',
|
||||
name='func_str',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='函数名'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='state',
|
||||
name='participant_type',
|
||||
field=models.IntegerField(blank=True, choices=[(0, '无处理人'), (1, '个人'), (2, '多人'), (4, '角色'), (6, '脚本'), (7, '工单的字段'), (9, '代码获取')], default=1, help_text='0.无处理人,1.个人,2.多人,3.部门,4.角色,5.变量(支持工单创建人,创建人的leader),6.脚本,7.工单的字段内容(如表单中的"测试负责人",需要为用户名或者逗号隔开的多个用户名),8.父工单的字段内容。 初始状态请选择类型5,参与人填create_by', verbose_name='参与者类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='participant_type',
|
||||
field=models.IntegerField(choices=[(0, '无处理人'), (1, '个人'), (2, '多人'), (4, '角色'), (6, '脚本'), (7, '工单的字段'), (9, '代码获取')], default=0, help_text='0.无处理人,1.个人,2.多人', verbose_name='当前处理人类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticketflow',
|
||||
name='intervene_type',
|
||||
field=models.IntegerField(choices=[(0, '正常处理'), (1, '转交'), (2, '加签'), (3, '加签处理完成'), (4, '接单'), (5, '评论'), (6, '删除'), (7, '强制关闭'), (8, '强制修改状态'), (9, 'hook操作'), (10, '撤回'), (11, '抄送')], default=0, help_text='流转类型', verbose_name='干预类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticketflow',
|
||||
name='participant_type',
|
||||
field=models.IntegerField(choices=[(0, '无处理人'), (1, '个人'), (2, '多人'), (4, '角色'), (6, '脚本'), (7, '工单的字段'), (9, '代码获取')], default=0, help_text='0.无处理人,1.个人,2.多人', verbose_name='处理人类型'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='wfscript',
|
||||
name='usage',
|
||||
field=models.IntegerField(choices=[(1, '获取处理人'), (2, '执行操作')], default=2, verbose_name='脚本用途'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
from random import choice
|
||||
from django.db import models
|
||||
from django.db.models.base import Model
|
||||
import django.utils.timezone as timezone
|
||||
|
|
@ -39,16 +40,18 @@ class State(CommonAModel):
|
|||
PARTICIPANT_TYPE_ROBOT = 6
|
||||
PARTICIPANT_TYPE_FIELD = 7
|
||||
PARTICIPANT_TYPE_PARENT_FIELD = 8
|
||||
PARTICIPANT_TYPE_FORMCODE = 9
|
||||
state_participanttype_choices = (
|
||||
(0, '无处理人'),
|
||||
(PARTICIPANT_TYPE_PERSONAL, '个人'),
|
||||
(PARTICIPANT_TYPE_MULTI, '多人'),
|
||||
(PARTICIPANT_TYPE_DEPT, '部门'),
|
||||
# (PARTICIPANT_TYPE_DEPT, '部门'),
|
||||
(PARTICIPANT_TYPE_ROLE, '角色'),
|
||||
(PARTICIPANT_TYPE_VARIABLE, '变量'),
|
||||
# (PARTICIPANT_TYPE_VARIABLE, '变量'),
|
||||
(PARTICIPANT_TYPE_ROBOT, '脚本'),
|
||||
(PARTICIPANT_TYPE_FIELD, '工单的字段'),
|
||||
(PARTICIPANT_TYPE_PARENT_FIELD, '父工单的字段')
|
||||
# (PARTICIPANT_TYPE_PARENT_FIELD, '父工单的字段'),
|
||||
(PARTICIPANT_TYPE_FORMCODE, '代码获取')
|
||||
)
|
||||
STATE_DISTRIBUTE_TYPE_ACTIVE = 1 # 主动接单
|
||||
STATE_DISTRIBUTE_TYPE_DIRECT = 2 # 直接处理(当前为多人的情况,都可以处理,而不需要先接单)
|
||||
|
|
@ -64,6 +67,13 @@ class State(CommonAModel):
|
|||
STATE_FIELD_READONLY= 1 # 字段只读
|
||||
STATE_FIELD_REQUIRED = 2 # 字段必填
|
||||
STATE_FIELD_OPTIONAL = 3 # 字段可选
|
||||
|
||||
state_filter_choices=(
|
||||
(0, '无'),
|
||||
(1, '和工单同属一及上级部门'),
|
||||
(2, '和创建人同属一及上级部门'),
|
||||
(3, '和上步处理人同属一及上级部门'),
|
||||
)
|
||||
name = models.CharField('名称', max_length=50)
|
||||
workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='所属工作流')
|
||||
is_hidden = models.BooleanField('是否隐藏', default=False, help_text='设置为True时,获取工单步骤api中不显示此状态(当前处于此状态时除外)')
|
||||
|
|
@ -74,6 +84,8 @@ class State(CommonAModel):
|
|||
participant = models.JSONField('参与者', default=list, blank=True, help_text='可以为空(无处理人的情况,如结束状态)、userid、userid列表\部门id\角色id\变量(create_by,create_by_tl)\脚本记录的id等,包含子工作流的需要设置处理人为loonrobot')
|
||||
state_fields = models.JSONField('表单字段', default=dict, help_text='json格式字典存储,包括读写属性1:只读,2:必填,3:可选. 示例:{"create_time":1,"title":2, "sn":1}, 内置特殊字段participant_info.participant_name:当前处理人信息(部门名称、角色名称),state.state_name:当前状态的状态名,workflow.workflow_name:工作流名称') # json格式存储,包括读写属性1:只读,2:必填,3:可选,4:不显示, 字典的字典
|
||||
distribute_type = models.IntegerField('分配方式', default=1, choices=state_distribute_choices, help_text='1.主动接单(如果当前处理人实际为多人的时候,需要先接单才能处理) 2.直接处理(即使当前处理人实际为多人,也可以直接处理) 3.随机分配(如果实际为多人,则系统会随机分配给其中一个人) 4.全部处理(要求所有参与人都要处理一遍,才能进入下一步)')
|
||||
filter_policy = models.IntegerField('参与人过滤策略', default=0, choices=state_filter_choices)
|
||||
participant_cc = models.JSONField('抄送给', default=list, blank=True, help_text='抄送给(userid列表)')
|
||||
|
||||
class Transition(CommonAModel):
|
||||
"""
|
||||
|
|
@ -97,6 +109,7 @@ class Transition(CommonAModel):
|
|||
TRANSITION_INTERVENE_TYPE_ALTER_STATE = 8 # 强制修改状态操作
|
||||
TRANSITION_INTERVENE_TYPE_HOOK = 9 # hook操作
|
||||
TRANSITION_INTERVENE_TYPE_RETREAT = 10 # 撤回
|
||||
TRANSITION_INTERVENE_TYPE_CC = 11 # 抄送
|
||||
|
||||
intervene_type_choices = (
|
||||
(0, '正常处理'),
|
||||
|
|
@ -109,7 +122,8 @@ class Transition(CommonAModel):
|
|||
(TRANSITION_INTERVENE_TYPE_CLOSE, '强制关闭'),
|
||||
(TRANSITION_INTERVENE_TYPE_ALTER_STATE, '强制修改状态'),
|
||||
(TRANSITION_INTERVENE_TYPE_HOOK, 'hook操作'),
|
||||
(TRANSITION_INTERVENE_TYPE_RETREAT, '撤回')
|
||||
(TRANSITION_INTERVENE_TYPE_RETREAT, '撤回'),
|
||||
(TRANSITION_INTERVENE_TYPE_CC, '抄送')
|
||||
)
|
||||
|
||||
name = models.CharField('操作', max_length=50)
|
||||
|
|
@ -155,7 +169,7 @@ class CustomField(CommonAModel):
|
|||
help_text='radio,checkbox,select,multiselect类型可供选择的选项,格式为json如:{"1":"中国", "2":"美国"},注意数字也需要引号')
|
||||
label = models.JSONField('标签', blank=True, default=dict, help_text='自定义标签,json格式,调用方可根据标签自行处理特殊场景逻辑,loonflow只保存文本内容')
|
||||
|
||||
class Ticket(CommonAModel):
|
||||
class Ticket(CommonBModel):
|
||||
"""
|
||||
工单
|
||||
"""
|
||||
|
|
@ -177,9 +191,9 @@ class Ticket(CommonAModel):
|
|||
category_choices =(
|
||||
('all', '全部'),
|
||||
('owner', '我创建的'),
|
||||
('duty', '代办'),
|
||||
('duty', '待办'),
|
||||
('worked', '我处理的'),
|
||||
('relation', '抄送我的')
|
||||
('cc', '抄送我的')
|
||||
)
|
||||
title = models.CharField('标题', max_length=500, blank=True, default='', help_text="工单标题")
|
||||
workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, verbose_name='关联工作流')
|
||||
|
|
@ -207,4 +221,21 @@ class TicketFlow(BaseModel):
|
|||
participant = models.ForeignKey(User, verbose_name='处理人', on_delete=models.SET_NULL, null=True, blank=True, related_name='ticketflow_participant')
|
||||
state = models.ForeignKey(State, verbose_name='当前状态', default=0, blank=True, on_delete=models.CASCADE)
|
||||
ticket_data = models.JSONField('工单数据', default=dict, blank=True, help_text='可以用于记录当前表单数据,json格式')
|
||||
intervene_type = models.IntegerField('干预类型', default=0, help_text='流转类型', choices=Transition.intervene_type_choices)
|
||||
intervene_type = models.IntegerField('干预类型', default=0, help_text='流转类型', choices=Transition.intervene_type_choices)
|
||||
participant_cc = models.JSONField('抄送给', default=list, blank=True, help_text='抄送给(userid列表)')
|
||||
|
||||
class WfScript(CommonAModel):
|
||||
"""
|
||||
执行脚本
|
||||
"""
|
||||
usage_choices =(
|
||||
(1, '获取处理人'),
|
||||
(2, '执行操作'),
|
||||
)
|
||||
usage = models.IntegerField('脚本用途', default=2, choices=usage_choices)
|
||||
wait = models.BooleanField('是否等待执行完成', default=True)
|
||||
name = models.CharField('脚本名称', max_length=100)
|
||||
workflow = models.ForeignKey(Workflow, verbose_name='关联工作流', null=True, blank=True, on_delete=models.SET_NULL)
|
||||
func_str = models.CharField('函数名', max_length=50, null=True, blank=True)
|
||||
content = models.TextField('脚本内容')
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
class GetParticipants:
|
||||
|
||||
all_funcs = [
|
||||
{'func':'get_create_by', 'name':'获取工单创建人'}
|
||||
]
|
||||
|
||||
# def all_funcs(self):
|
||||
# # return list(filter(lambda x: x.startswith('get_') and callable(getattr(self, x)), dir(self)))
|
||||
# return [(func, getattr(self, func).__doc__) for func in dir(self) if callable(getattr(self, func)) and func.startswith('get_')]
|
||||
|
||||
def get_create_by(self, state:dict={}, ticket:dict={}, ticket_data:dict={}, request={}):
|
||||
"""工单创建人"""
|
||||
participant = ticket.create_by.id
|
||||
return participant
|
||||
|
|
@ -136,7 +136,10 @@ class TicketCloseSerializer(serializers.Serializer):
|
|||
|
||||
class TicketAddNodeSerializer(serializers.Serializer):
|
||||
suggestion = serializers.CharField(label="加签说明", required = False)
|
||||
add_node_man = serializers.IntegerField(label='加签人')
|
||||
toadd_user = serializers.IntegerField(label='发送给谁去加签')
|
||||
|
||||
class TicketAddNodeEndSerializer(serializers.Serializer):
|
||||
suggestion = serializers.CharField(label="加签意见", required = False)
|
||||
suggestion = serializers.CharField(label="加签意见", required = False)
|
||||
|
||||
class TicketDestorySerializer(serializers.Serializer):
|
||||
ids = serializers.ListField(child=serializers.IntegerField(), label='工单ID列表')
|
||||
|
|
@ -7,6 +7,9 @@ from rest_framework.exceptions import APIException
|
|||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
import random
|
||||
from .scripts import GetParticipants
|
||||
from utils.queryset import get_parent_queryset
|
||||
|
||||
class WfService(object):
|
||||
@staticmethod
|
||||
def get_worlflow_states(workflow:Workflow):
|
||||
|
|
@ -103,7 +106,7 @@ class WfService(object):
|
|||
|
||||
|
||||
@classmethod
|
||||
def get_next_state_by_transition_and_ticket_info(cls, ticket:Ticket, transition: Transition, ticket_data:dict={})->object:
|
||||
def get_next_state_by_transition_and_ticket_info(cls, ticket:Ticket, transition: Transition, ticket_data:dict={}, request:dict={})->object:
|
||||
"""
|
||||
获取下个节点状态
|
||||
"""
|
||||
|
|
@ -111,16 +114,19 @@ class WfService(object):
|
|||
destination_state = transition.destination_state
|
||||
ticket_all_value = cls.get_ticket_all_field_value(ticket)
|
||||
ticket_all_value.update(**ticket_data)
|
||||
for key, value in ticket_all_value.items():
|
||||
if isinstance(ticket_all_value[key], str):
|
||||
ticket_all_value[key] = "'" + ticket_all_value[key] + "'"
|
||||
if transition.condition_expression:
|
||||
for i in transition.condition_expression:
|
||||
expression = i['expression'].format(**ticket_all_value)
|
||||
import datetime, time # 用于支持条件表达式中对时间的操作
|
||||
if eval(expression):
|
||||
if eval(expression, {"__builtins__":None}, {'datetime':datetime, 'time':time}):
|
||||
destination_state = State.objects.get(pk=i['target_state'])
|
||||
return destination_state
|
||||
|
||||
@classmethod
|
||||
def get_ticket_state_participant_info(cls, state:State, ticket:Ticket, ticket_data:dict={}):
|
||||
def get_ticket_state_participant_info(cls, state:State, ticket:Ticket, ticket_data:dict={}, request={}):
|
||||
"""
|
||||
获取工单目标状态实际的处理人, 处理人类型
|
||||
"""
|
||||
|
|
@ -141,17 +147,34 @@ class WfService(object):
|
|||
multi_all_person_dict = {}
|
||||
destination_participant_type, destination_participant = state.participant_type, state.participant
|
||||
if destination_participant_type == State.PARTICIPANT_TYPE_FIELD:
|
||||
destination_participant = ticket_data.get(destination_participant, None) if destination_participant in ticket_data else Ticket.ticket_data.get(destination_participant, None)
|
||||
destination_participant = ticket_data.get(destination_participant, 0) if destination_participant in ticket_data \
|
||||
else Ticket.ticket_data.get(destination_participant, 0)
|
||||
|
||||
elif destination_participant_type == State.PARTICIPANT_TYPE_DEPT:#单部门
|
||||
destination_participant = list(User.objects.filter(dept=destination_participant).values_list('id', flat=True))
|
||||
elif destination_participant_type == State.PARTICIPANT_TYPE_FORMCODE:#代码获取
|
||||
destination_participant = getattr(GetParticipants, destination_participant)(state=state, ticket=ticket, ticket_data=ticket_data, request=request)
|
||||
|
||||
elif destination_participant_type == State.PARTICIPANT_TYPE_ROLE:#单角色
|
||||
destination_participant = list(User.objects.filter(roles=destination_participant).values_list('id', flat=True))
|
||||
|
||||
elif destination_participant_type == State.PARTICIPANT_TYPE_DEPT:#部门
|
||||
destination_participant = list(User.objects.filter(dept__in=destination_participant).values_list('id', flat=True))
|
||||
|
||||
elif destination_participant_type == State.PARTICIPANT_TYPE_ROLE:#角色
|
||||
user_queryset = User.objects.filter(roles__in=destination_participant)
|
||||
# 如果选择了角色, 需要走过滤策略
|
||||
if ticket.filter_policy == 1:
|
||||
depts = get_parent_queryset(ticket.belong_dept)
|
||||
user_queryset = user_queryset.filter(dept__in=depts)
|
||||
elif ticket.filter_policy == 2:
|
||||
depts = get_parent_queryset(ticket.create_by.dept)
|
||||
user_queryset = user_queryset.filter(dept__in=depts)
|
||||
elif ticket.filter_policy == 3:
|
||||
depts = get_parent_queryset(request.user.dept)
|
||||
user_queryset = user_queryset.filter(dept__in=depts)
|
||||
destination_participant = list(user_queryset.values_list('id', flat=True))
|
||||
if type(destination_participant) == list:
|
||||
destination_participant_type = State.PARTICIPANT_TYPE_MULTI
|
||||
destination_participant = list(set(destination_participant))
|
||||
if len(destination_participant) == 1: # 如果只有一个人
|
||||
destination_participant_type = State.PARTICIPANT_TYPE_PERSONAL
|
||||
destination_participant = destination_participant[0]
|
||||
else:
|
||||
destination_participant_type = State.PARTICIPANT_TYPE_PERSONAL
|
||||
if destination_participant_type == State.PARTICIPANT_TYPE_MULTI:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from django.db.models import base
|
||||
from rest_framework import urlpatterns
|
||||
from apps.wf.views import CustomFieldViewSet, StateViewSet, TicketFlowViewSet, TicketViewSet, TransitionViewSet, WorkflowViewSet
|
||||
from apps.wf.views import CustomFieldViewSet, FromCodeListView, StateViewSet, TicketFlowViewSet, TicketViewSet, TransitionViewSet, WorkflowViewSet
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
|
|
@ -12,6 +12,7 @@ router.register('customfield', CustomFieldViewSet, basename='wf_customfield')
|
|||
router.register('ticket', TicketViewSet, basename='wf_ticket')
|
||||
router.register('ticketflow', TicketFlowViewSet, basename='wf_ticketflow')
|
||||
urlpatterns = [
|
||||
path('participant_from_code', FromCodeListView.as_view()),
|
||||
path('', include(router.urls)),
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
from django.db.models import query
|
||||
from rest_framework.views import APIView
|
||||
from apps.system.models import User
|
||||
from apps.wf.filters import TicketFilterSet
|
||||
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, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, TicketCreateSerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer
|
||||
from apps.wf.serializers import 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 rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||
from rest_framework.decorators import action, api_view
|
||||
|
|
@ -13,8 +15,18 @@ from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin,
|
|||
from apps.wf.services import WfService
|
||||
from rest_framework.exceptions import APIException, PermissionDenied
|
||||
from rest_framework import status
|
||||
from django.db.models import Count
|
||||
from .scripts import GetParticipants
|
||||
|
||||
|
||||
# Create your views here.
|
||||
class FromCodeListView(APIView):
|
||||
def get(self, request, format=None):
|
||||
"""
|
||||
获取处理人代码列表
|
||||
"""
|
||||
return Response(GetParticipants.all_funcs)
|
||||
|
||||
class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
|
||||
perms_map = {'get': '*', 'post': 'workflow_create',
|
||||
'put': 'workflow_update', 'delete': 'workflow_delete'}
|
||||
|
|
@ -132,11 +144,12 @@ 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, create_by=request.user, act_state=Ticket.TICKET_ACT_STATE_DRAFT) # 先创建出来
|
||||
ticket = serializer.save(state=start_state, create_by=request.user, act_state=Ticket.TICKET_ACT_STATE_DRAFT, belong_dept=request.user.dept) # 先创建出来
|
||||
|
||||
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)
|
||||
destination_participant = participant_info.get('destination_participant', None)
|
||||
destination_participant = participant_info.get('destination_participant', 0)
|
||||
multi_all_person = participant_info.get('multi_all_person', {}) # 多人需要全部处理情况
|
||||
sn = WfService.get_ticket_sn(ticket.workflow) # 流水号
|
||||
if next_state.type == State.STATE_TYPE_END:
|
||||
|
|
@ -163,8 +176,29 @@ 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)
|
||||
# 开始状态需要抄送
|
||||
if start_state.participant_cc:
|
||||
TicketFlow.objects.create(ticket=ticket, state=ticket.start_state,
|
||||
participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
|
||||
participant=None, participant_cc=start_state.participant_cc)
|
||||
# 目标状态需要抄送
|
||||
if next_state.participant_cc:
|
||||
TicketFlow.objects.create(ticket=ticket, state=ticket.next_state,
|
||||
participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
|
||||
participant=None, participant_cc=next_state.participant_cc)
|
||||
return Response(TicketSerializer(instance=ticket).data)
|
||||
|
||||
@action(methods=['get'], detail=False, perms_map={'get':'*'})
|
||||
def duty_agg(self, request, pk=None):
|
||||
"""
|
||||
工单待办聚合
|
||||
"""
|
||||
ret = {}
|
||||
queryset = Ticket.objects.filter(participant__contains=request.user.id, is_deleted=False)\
|
||||
.exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED])
|
||||
ret['total_count'] = queryset.count()
|
||||
ret['details'] = list(queryset.values('workflow', 'workflow__name').annotate(count = Count('workflow')))
|
||||
return Response(ret)
|
||||
|
||||
@action(methods=['post'], detail=True, perms_map={'post':'*'})
|
||||
def handle(self, request, pk=None):
|
||||
|
|
@ -189,7 +223,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))
|
||||
destination_state = WfService.get_next_state_by_transition_and_ticket_info(ticket, transition, ticket_data)
|
||||
destination_state = WfService.get_next_state_by_transition_and_ticket_info(ticket, transition, ticket_data, request)
|
||||
multi_all_person = ticket.multi_all_person
|
||||
if multi_all_person:
|
||||
multi_all_person[request.user.id] =dict(transition=transition.id)
|
||||
|
|
@ -197,7 +231,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
if WfService.check_dict_has_all_same_value(multi_all_person):
|
||||
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
|
||||
destination_participant_type = participant_info.get('destination_participant_type', 0)
|
||||
destination_participant = participant_info.get('destination_participant', None)
|
||||
destination_participant = participant_info.get('destination_participant', 0)
|
||||
multi_all_person = {}
|
||||
else:
|
||||
# 处理人没有没有全部处理完成或者处理动作不一致
|
||||
|
|
@ -206,12 +240,12 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
destination_participant = []
|
||||
for key, value in multi_all_person.items():
|
||||
if not value:
|
||||
destination_participant.push(key)
|
||||
destination_participant.append(key)
|
||||
else:
|
||||
# 当前处理人类型非全部处理
|
||||
participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
|
||||
destination_participant_type = participant_info.get('destination_participant_type', 0)
|
||||
destination_participant = participant_info.get('destination_participant', None)
|
||||
destination_participant = participant_info.get('destination_participant', 0)
|
||||
multi_all_person = participant_info.get('multi_all_person', {})
|
||||
|
||||
# 更新工单信息:基础字段及自定义字段, add_relation字段 需要下个处理人是部门、角色等的情况
|
||||
|
|
@ -240,6 +274,11 @@ 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)
|
||||
# 目标状态需要抄送
|
||||
if destination_state.participant_cc:
|
||||
TicketFlow.objects.create(ticket=ticket, state=ticket.destination_state,
|
||||
participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
|
||||
participant=None, participant_cc=destination_state.participant_cc)
|
||||
return Response(TicketSerializer(instance=ticket).data)
|
||||
|
||||
|
||||
|
|
@ -321,7 +360,7 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
"""
|
||||
ticket = self.get_object()
|
||||
data = request.data
|
||||
add_user = User.objects.get(pk=data['add_node_man'])
|
||||
add_user = User.objects.get(pk=data['toadd_user'])
|
||||
ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL
|
||||
ticket.participant = add_user.id
|
||||
ticket.in_add_node = True
|
||||
|
|
@ -342,8 +381,8 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
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.add_node_man = None
|
||||
ticket.save()
|
||||
# 更新流转记录
|
||||
suggestion = request.data.get('suggestion', '') # 加签意见
|
||||
|
|
@ -375,6 +414,15 @@ class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin
|
|||
else:
|
||||
return Response('工单不可关闭', status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=TicketDestorySerializer)
|
||||
def destory(self, request, pk=None):
|
||||
"""
|
||||
批量物理删除
|
||||
"""
|
||||
Ticket.objects.filter(id__in=request.data.get('ids', [])).delete(soft=False)
|
||||
return Response()
|
||||
|
||||
|
||||
|
||||
class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
class WpmConfig(AppConfig):
|
||||
name = 'apps.wpm'
|
||||
verbose_name = '车间生产'
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
from django.db import models
|
||||
from django.db.models.base import Model
|
||||
import django.utils.timezone as timezone
|
||||
from django.db.models.query import QuerySet
|
||||
from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File
|
||||
from utils.model import SoftModel, BaseModel
|
||||
from simple_history.models import HistoricalRecords
|
||||
from apps.mtm.models import Material, Step, RecordForm
|
||||
|
||||
class Product(CommonAModel):
|
||||
"""
|
||||
产品(所有生产过程中出现过的)
|
||||
"""
|
||||
act_state_choices=(
|
||||
(0, '待执行'),
|
||||
(1, '进行中'),
|
||||
(2, '已完成')
|
||||
)
|
||||
number = models.CharField('物品编号', primary_key=True, null=True, blank=True, max_length=50)
|
||||
m_state = models.ForeignKey(Material, verbose_name='所属物料状态', on_delete=models.CASCADE)
|
||||
p_state = models.ForeignKey(Step, verbose_name='所在步骤', on_delete=models.CASCADE, null=True, blank=True)
|
||||
act_state = models.IntegerField('进行状态', default=0)
|
||||
remark = models.CharField('备注', max_length=200, null=True, blank=True)
|
||||
|
||||
class ProductForm(CommonAModel):
|
||||
"""
|
||||
记录表格
|
||||
"""
|
||||
record_form = models.ForeignKey(RecordForm, verbose_name='所用表格', on_delete=models.CASCADE)
|
||||
data = models.JSONField('记录的数据', default=dict, blank=True)
|
||||
|
||||
|
||||
class ProductFlow(BaseModel):
|
||||
"""
|
||||
产品流转日志
|
||||
"""
|
||||
product = models.ForeignKey(Product, verbose_name='产品', on_delete=models.CASCADE)
|
||||
|
||||
|
||||
class Vendor(CommonAModel):
|
||||
"""
|
||||
供应商信息
|
||||
"""
|
||||
name = models.CharField('供应商名称', max_length=50, unique=True)
|
||||
contact = models.CharField('联系人', max_length=20)
|
||||
contact_phone = models.CharField('联系电话', max_length=11, unique=True)
|
||||
address = models.CharField('地址', max_length=200, null=True, blank=True)
|
||||
description = models.CharField('描述', max_length=200, blank=True, null=True)
|
||||
material = models.CharField('供应的物料', max_length=200, blank=True, null=True)
|
||||
class Meta:
|
||||
verbose_name = '供应商信息'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
from rest_framework.serializers import ModelSerializer
|
||||
|
||||
from .models import Vendor
|
||||
|
||||
|
||||
class VendorSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
model = Vendor
|
||||
fields = '__all__'
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
from django.db.models import base
|
||||
from rest_framework import urlpatterns
|
||||
from apps.pum.views import VendorViewSet
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('vendor', VendorViewSet, basename='vendor')
|
||||
urlpatterns = [
|
||||
path('', include(router.urls)),
|
||||
]
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
from django.shortcuts import render
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from apps.pum.models import Vendor
|
||||
from apps.pum.serializers import VendorSerializer
|
||||
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
|
||||
|
||||
|
||||
|
||||
|
||||
# Create your views here.
|
||||
class VendorViewSet(CreateUpdateModelAMixin, ModelViewSet):
|
||||
"""
|
||||
供应商-增删改查
|
||||
"""
|
||||
perms_map = {'get': '*', 'post': 'vendor_create',
|
||||
'put': 'vendor_update', 'delete': 'vendor_delete'}
|
||||
queryset = Vendor.objects.all()
|
||||
serializer_class = VendorSerializer
|
||||
search_fields = ['name', 'contact']
|
||||
filterset_fields = []
|
||||
ordering_fields = ['create_time']
|
||||
ordering = ['-create_time']
|
||||
|
|
@ -10,3 +10,4 @@ drf-yasg==1.20.0
|
|||
psutil==5.8.0
|
||||
pillow==8.3.1
|
||||
opencv-python==4.5.3.56
|
||||
django-celery-results==2.2.0
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ INSTALLED_APPS = [
|
|||
'django.contrib.staticfiles',
|
||||
'corsheaders',
|
||||
'django_celery_beat',
|
||||
'django_celery_results',
|
||||
'drf_yasg',
|
||||
'rest_framework',
|
||||
"django_filters",
|
||||
|
|
@ -55,7 +56,8 @@ INSTALLED_APPS = [
|
|||
'apps.inm',
|
||||
'apps.sam',
|
||||
'apps.qm',
|
||||
'apps.pm'
|
||||
'apps.pm',
|
||||
# 'apps.wpm'
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
|
@ -202,6 +204,7 @@ CELERY_BROKER_URL = "redis://redis:6379/0" # 任务存储
|
|||
CELERYD_MAX_TASKS_PER_CHILD = 100 # 每个worker最多执行300个任务就会被销毁,可防止内存泄露
|
||||
CELERY_TIMEZONE = 'Asia/Shanghai' # 设置时区
|
||||
CELERY_ENABLE_UTC = True # 启动时区设置
|
||||
CELERY_RESULT_BACKEND = 'django-db'
|
||||
|
||||
# swagger配置
|
||||
SWAGGER_SETTINGS = {
|
||||
|
|
|
|||
|
|
@ -57,4 +57,14 @@ def get_child_queryset2(obj, hasParent=True):
|
|||
while child_queryset:
|
||||
queryset = queryset | child_queryset
|
||||
child_queryset = cls.objects.filter(parent__in=child_queryset)
|
||||
return queryset
|
||||
return queryset
|
||||
|
||||
def get_parent_queryset(obj, hasSelf=True):
|
||||
cls = type(obj)
|
||||
ids = []
|
||||
if hasSelf:
|
||||
ids.append(obj.id)
|
||||
while obj.parent:
|
||||
obj = obj.parent
|
||||
ids.append(obj.id)
|
||||
return cls.objects.filter(id__in=ids)
|
||||
Loading…
Reference in New Issue