合同监控归档基础

jg-waiwang-pro
mhsnet 8 months ago
parent 2f35ed7b5a
commit 153ca38516

@ -0,0 +1,181 @@
<template>
<el-dialog :title="title" :close-on-click-modal="false" width='600px' :visible.sync="visible"
class="JNPF-dialog JNPF-dialog_center" lock-scroll append-to-body>
<el-form ref="dataForm" :model="dataForm" label-width="80px" :rules="fromRules">
<template v-if="eventType==='transfer'||eventType==='assign'||eventType==='cancel'">
<el-form-item label="指派节点" prop="nodeCode" v-if="eventType==='assign'">
<el-select v-model="dataForm.nodeCode" placeholder="请选择指派节点">
<el-option v-for="item in assignNodeList" :key="item.nodeCode" :label="item.nodeName"
:value="item.nodeCode" />
</el-select>
</el-form-item>
<el-form-item :label="label+'给谁'" prop="freeApproverUserId"
v-if="eventType==='transfer'||eventType==='assign'"
:rules="[{ required: true, message: `请选择${label}给谁`, trigger: 'click' }]">
<JnpfUserSelect v-model="dataForm.freeApproverUserId" :placeholder="`请选择${label}给谁`" />
</el-form-item>
<el-form-item :label="`${label}原因`" prop="handleOpinion"
v-if="eventType==='assign'||eventType==='cancel'||(eventType==='transfer'&&properties&&properties.hasOpinion)">
<el-input v-model="dataForm.handleOpinion" :placeholder="`请输入${label}原因`" type="textarea"
:rows="4" />
</el-form-item>
<el-form-item :label="`${label}附件`" prop="fileList"
v-if="eventType==='assign'||eventType==='cancel'||(eventType==='transfer'&&properties&&properties.hasOpinion)">
<JnpfUploadFile v-model="dataForm.fileList" :limit="3" />
</el-form-item>
<el-form-item label="手写签名" required v-if="properties.hasSign&&eventType==='transfer'">
<div class="sign-main">
<img :src="signImg" alt="" v-if="signImg" class="sign-img">
<div @click="addSign" class="sign-style">
<i class="icon-ym icon-ym-signature add-sign"></i>
<span class="sign-title" v-if="!signImg"></span>
</div>
</div>
</el-form-item>
</template>
<template v-else>
<template v-if="properties.hasOpinion">
<el-form-item :label="`${label}原因`" prop="handleOpinion">
<el-input v-model="dataForm.handleOpinion" :placeholder="`请输入${label}原因`"
type="textarea" :rows="4" />
</el-form-item>
<el-form-item :label="`${label}附件`" prop="fileList">
<JnpfUploadFile v-model="dataForm.fileList" :limit="3" />
</el-form-item>
</template>
<el-form-item label="手写签名" required v-if="properties.hasSign">
<div class="sign-main">
<img :src="signImg" alt="" v-if="signImg" class="sign-img">
<div @click="addSign" class="sign-style">
<i class="icon-ym icon-ym-signature add-sign"></i>
<span class="sign-title" v-if="!signImg"></span>
</div>
</div>
</el-form-item>
</template>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">{{$t('common.cancelButton')}}</el-button>
<el-button type="primary" @click="handleSure()" :loading="btnLoading">
{{$t('common.confirmButton')}}
</el-button>
</span>
<SignImgDialog v-if="signVisible" ref="SignImg" :lineWidth='3' :userInfo='userInfo'
:isDefault='1' @close="signDialog" />
</el-dialog>
</template>
<script>
import SignImgDialog from '@/components/SignImgDialog'
import { mapGetters } from "vuex"
export default {
components: { SignImgDialog },
props: {
assignNodeList: {
type: Array,
default: () => []
}
},
data() {
return {
visible: false,
properties: {},
eventType: '',
dataForm: {
handleOpinion: '',
freeApproverUserId: '',
nodeCode: '',
fileList: [],
},
signVisible: false,
fromRules: {
nodeCode: [
{ required: true, message: '请选择指派节点', trigger: 'change' }
]
},
signImg: '',
btnLoading: false,
title: '',
label: ''
}
},
computed: {
...mapGetters(['userInfo'])
},
methods: {
init(properties, eventType) {
this.visible = true
this.properties = properties
this.eventType = eventType || ''
this.dataForm.handleOpinion = ''
this.dataForm.freeApproverUserId = ''
this.dataForm.nodeCode = ''
this.dataForm.fileList = []
this.signImg = ""
if (this.properties.hasSign) this.signImg = this.userInfo.signImg
switch (eventType) {
case 'transfer':
this.title = '转审'
this.label = '转审'
break;
case 'revoke':
this.title = '撤回流程'
this.label = '撤回'
break;
case 'recall':
this.title = '撤回审核'
this.label = '撤回'
break;
case 'assign':
this.title = '指派'
this.label = '指派'
break;
case 'cancel':
this.title = '终止审核不可恢复'
this.label = '终止'
break;
default:
break;
}
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
})
},
addSign() {
this.signVisible = true
this.$nextTick(() => {
this.$refs.SignImg.init()
})
},
signDialog(val) {
this.signVisible = false
if (val) {
this.signImg = val
}
},
handleSure() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
if (this.properties.hasSign && !this.signImg && this.eventType != 'assign' && this.eventType != 'cancel') {
this.$message({
message: '请签名',
type: 'error'
})
return
}
let query = {
signImg: this.signImg,
...this.dataForm
}
this.btnLoading = true
this.$emit('submit', query)
}
})
},
closeDialog() {
this.btnLoading = false
this.visible = false
},
}
}
</script>

@ -0,0 +1,232 @@
<template>
<el-dialog :title="eventType==='audit'?'审批通过':'审批退回'" :close-on-click-modal="false"
:visible.sync="visible" class="JNPF-dialog JNPF-dialog_center" lock-scroll append-to-body
:before-close="beforeClose" width='600px'>
<el-form ref="dataForm" :model="dataForm"
:label-width="dataForm.candidateList.length||branchList.length?'130px':'80px'">
<template v-if="eventType==='audit'">
<el-form-item label="分支选择" prop="branchList" v-if="branchList.length"
:rules="[{ required: true, message: `分支不能为空`, trigger: 'change' }]">
<el-select v-model="dataForm.branchList" multiple placeholder="请选择审批分支" clearable
@change="onBranchChange">
<el-option v-for="item in branchList" :key="item.nodeId" :label="item.nodeName"
:value="item.nodeId" />
</el-select>
</el-form-item>
<el-form-item :label="item.nodeName+item.label" :prop="'candidateList.' + i + '.value'"
v-for="(item,i) in dataForm.candidateList" :key="i" :rules="item.rules">
<candidate-user-select v-model="item.value" multiple :placeholder="'请选择'+item.label"
:taskId="taskId" :formData="formData" :nodeId="item.nodeId" v-if="item.hasCandidates" />
<JnpfUserSelect v-model="item.value" multiple :placeholder="'请选择'+item.label" title="候选人员"
v-else />
</el-form-item>
</template>
<template v-if="properties&&properties.rejectType &&eventType!=='audit'&&showReject">
<el-form-item label="退回节点" prop="rejectStep">
<el-select v-model="dataForm.rejectStep" placeholder="请选择退回节点"
:disabled='properties.rejectStep!=="2"'>
<el-option v-for="item in rejectList" :key="item.nodeCode" :label="item.nodeName"
:value="item.nodeCode">
</el-option>
</el-select>
<template v-if="properties.rejectType==3">
<el-form-item prop="rejectRadio">
<el-radio-group v-model="dataForm.rejectType" class="form-item-content">
<el-radio :label="1">重新审批
<el-tooltip content="若流程为A->B->C,C退回至A则C->A->B->C" placement="top">
<i class="el-icon-question tooltip-question"></i>
</el-tooltip>
</el-radio>
<el-radio :label="2">直接提交给我
<el-tooltip content="若流程为A->B->C,C退回至A则C->A->C" placement="top">
<i class="el-icon-question tooltip-question"></i>
</el-tooltip>
</el-radio>
</el-radio-group>
</el-form-item>
</template>
</el-form-item>
</template>
<el-form-item label="抄送人员" prop="copyIds" v-if="properties&&properties.isCustomCopy">
<JnpfUserSelect v-model="copyIds" placeholder="请选择" multiple />
</el-form-item>
<el-form-item label="审批意见" prop="handleOpinion" v-if="properties&&properties.hasOpinion">
<el-input v-model="dataForm.handleOpinion" placeholder="请输入审批意见" type="textarea"
:rows="4" />
<CommonWordsDialog ref="commonWordsDialog" @change="common" />
</el-form-item>
<el-form-item label="审批附件" prop="fileList" v-if="properties&&properties.hasOpinion">
<JnpfUploadFile v-model="dataForm.fileList" :limit="3" />
</el-form-item>
<el-form-item label="审批签名" required v-if="properties&&properties.hasSign">
<div class="sign-main">
<img :src="signImg" alt="" v-if="signImg" class="sign-img">
<div @click="addSign" class="sign-style">
<i class="icon-ym icon-ym-signature add-sign"></i>
<span class="sign-title" v-if="!signImg"></span>
</div>
</div>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">{{$t('common.cancelButton')}}</el-button>
<el-button type="primary" @click="handleApproval()" :loading="btnLoading">
{{$t('common.confirmButton')}}
</el-button>
</span>
<SignImgDialog v-if="signVisible" ref="SignImg" :lineWidth='3' :userInfo='userInfo'
:isDefault='1' @close="signDialog" />
</el-dialog>
</template>
<script>
import SignImgDialog from '@/components/SignImgDialog'
import { mapGetters } from "vuex"
import CandidateUserSelect from './CandidateUserSelect'
import CommonWordsDialog from './CommonWordsDialog'
export default {
components: { SignImgDialog, CandidateUserSelect, CommonWordsDialog },
data() {
return {
visible: false,
branchList: [],
dataForm: {
branchList: [],
candidateList: [],
handleOpinion: '',
fileList: [],
rejectStep: '',
rejectType: 1
},
copyIds: [],
signVisible: false,
freeApproverUserId: '',
signImg: '',
btnLoading: false,
properties: {},
eventType: '',
taskId: '',
formData: {
flowId: '',
data: '{}'
},
showReject: false,
rejectList: [],
commonWordsVisible: false,
}
},
computed: {
...mapGetters(['userInfo'])
},
methods: {
init(properties, taskId, eventType, branchList, candidateList, flowId) {
this.visible = true
this.properties = properties
this.taskId = taskId
this.eventType = eventType || ''
this.branchList = branchList || []
this.dataForm.candidateList = candidateList || []
this.dataForm.branchList = []
this.dataForm.handleOpinion = ''
this.formData.flowId = flowId
this.dataForm.rejectType = this.properties.rejectType == 3 ? 1 : this.properties.rejectType
this.copyIds = []
this.signImg = ""
if (this.properties && this.properties.hasSign) this.signImg = this.userInfo.signImg
this.freeApproverUserId = ''
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
})
this.showReject = properties.showReject
this.rejectList = properties.rejectList
this.dataForm.rejectStep = properties.nodeCode
},
onBranchChange(val) {
const defaultList = this.dataForm.candidateList.filter(o => o.isDefault)
if (!val.length) return this.dataForm.candidateList = defaultList
let list = []
for (let i = 0; i < val.length; i++) {
inner: for (let j = 0; j < this.branchList.length; j++) {
let o = this.branchList[j]
if (val[i] === o.nodeId && o.isCandidates) {
list.push({
...o,
label: '审批人',
value: [],
rules: [{ required: true, message: `审批人不能为空`, trigger: 'click' }]
})
break inner
}
}
}
this.dataForm.candidateList = [...defaultList, ...list]
},
handleApproval() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
if (this.properties.hasSign && !this.signImg) {
this.$message({
message: '请签名',
type: 'error'
})
return
}
let query = {
handleOpinion: this.dataForm.handleOpinion,
signImg: this.signImg,
copyIds: this.copyIds.join(','),
branchList: this.dataForm.branchList,
fileList: this.dataForm.fileList,
rejectType: this.dataForm.rejectType
}
if (this.eventType === 'reject') query.rejectStep = this.dataForm.rejectStep
if (this.dataForm.candidateList.length) {
let candidateList = {}
for (let i = 0; i < this.dataForm.candidateList.length; i++) {
candidateList[this.dataForm.candidateList[i].nodeId] = this.dataForm.candidateList[i].value
}
query.candidateList = candidateList
}
if (this.eventType === 'audit' && this.properties.hasFreeApprover) {
query = { freeApproverUserId: this.freeApproverUserId, ...query }
}
this.btnLoading = true
this.$emit('submit', query)
}
})
},
common(val) {
this.commonWordsVisible = false
if (val) {
this.dataForm.handleOpinion += val.commonWordsText
}
},
addSign() {
this.signVisible = true
this.$nextTick(() => {
this.$refs.SignImg.init()
})
},
signDialog(val) {
this.signVisible = false
if (val) {
this.signImg = val
}
},
closeDialog() {
this.btnLoading = false
this.visible = false
},
beforeClose() {
this.visible = false
this.$refs.commonWordsDialog.close()
},
}
}
</script>
<style lang="scss" scoped>
.commonWords-button {
margin-top: 57px;
}
</style>

@ -0,0 +1,105 @@
<template>
<el-dialog title="提交审核" :close-on-click-modal="false"
class="JNPF-dialog JNPF-dialog_center form-script-dialog" lock-scroll append-to-body
v-bind="$attrs" width="600px" :modal-append-to-body="false" v-on="$listeners" @open="onOpen">
<el-form ref="candidateForm" :model="candidateForm"
:label-width="candidateForm.candidateList.length||branchList.length?'130px':'80px'">
<el-form-item label="分支选择" prop="branchList" v-if="branchList && branchList.length"
:rules="[{ required: true, message: `分支不能为空`, trigger: 'change' }]">
<el-select v-model="candidateForm.branchList" multiple placeholder="请选择审批分支" clearable
@change="onBranchChange">
<el-option v-for="item in branchList" :key="item.nodeId" :label="item.nodeName"
:value="item.nodeId" />
</el-select>
</el-form-item>
<el-form-item :label="item.nodeName+item.label" :prop="'candidateList.' + i + '.value'"
v-for="(item,i) in candidateForm.candidateList" :key="i" :rules="item.rules">
<candidate-user-select v-model="item.value" multiple :placeholder="'请选择'+item.label"
:taskId="taskId" :formData="formData" :nodeId="item.nodeId" v-if="item.hasCandidates" />
<JnpfUserSelect v-model="item.value" multiple :placeholder="'请选择'+item.label" title="候选人员"
v-else />
</el-form-item>
<el-form-item label="抄送人员" v-if="isCustomCopy">
<JnpfUserSelect v-model="candidateForm.copyIds" placeholder="请选择" multiple />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="closeDialog">{{$t('common.cancelButton')}}</el-button>
<el-button type="primary" @click="submitCandidate()" :loading="btnLoading">
{{$t('common.confirmButton')}}
</el-button>
</span>
</el-dialog>
</template>
<script>
import CandidateUserSelect from './CandidateUserSelect'
export default {
components: { CandidateUserSelect },
props: ['candidateList', 'branchList', 'taskId', 'formData', 'isCustomCopy'],
data() {
return {
candidateForm: {
copyIds: [],
branchList: [],
candidateList: []
},
btnLoading: false
}
},
methods: {
onOpen() {
this.candidateForm.candidateList = this.candidateList.map(o => ({
...o,
isDefault: true,
label: '审批人',
value: [],
rules: [{ required: true, message: `审批人不能为空`, trigger: 'click' }]
}))
this.candidateForm.branchList = []
this.$nextTick(() => {
this.$refs['candidateForm'].resetFields()
})
},
onBranchChange(val) {
const defaultList = this.candidateForm.candidateList.filter(o => o.isDefault)
if (!val.length) return this.candidateForm.candidateList = defaultList
let list = []
for (let i = 0; i < val.length; i++) {
inner: for (let j = 0; j < this.branchList.length; j++) {
let o = this.branchList[j]
if (val[i] === o.nodeId && o.isCandidates) {
list.push({
...o,
label: '审批人',
value: [],
rules: [{ required: true, message: `审批人不能为空`, trigger: 'click' }]
})
break inner
}
}
}
this.candidateForm.candidateList = [...defaultList, ...list]
},
submitCandidate() {
this.$refs['candidateForm'].validate((valid) => {
if (valid) {
this.btnLoading = true
let candidateList = {}
for (let i = 0; i < this.candidateForm.candidateList.length; i++) {
candidateList[this.candidateForm.candidateList[i].nodeId] = this.candidateForm.candidateList[i].value
}
this.$emit('submitCandidate', {
candidateList,
branchList: this.candidateForm.branchList,
copyIds: this.candidateForm.copyIds.join(','),
})
}
})
},
closeDialog() {
this.$emit('update:visible', false)
},
}
}
</script>

@ -0,0 +1,398 @@
<template>
<div class="popupSelect-container">
<div class="el-select" @click.stop="openDialog">
<div class="el-select__tags" v-if="multiple" ref="tags"
:style="{ 'max-width': inputWidth - 32 + 'px', width: '100%',cursor:'pointer' }">
<span v-if="collapseTags && tagsList.length">
<el-tag :closable="!selectDisabled" :size="collapseTagSize" type="info"
@close="deleteTag($event, 0)" disable-transitions>
<span class="el-select__tags-text">{{ tagsList[0].fullName }}</span>
</el-tag>
<el-tag v-if="tagsList.length > 1" :closable="false" type="info" disable-transitions>
<span class="el-select__tags-text">+ {{ tagsList.length - 1 }}</span>
</el-tag>
</span>
<transition-group @after-leave="resetInputHeight" v-if="!collapseTags">
<el-tag v-for="(item,i) in tagsList" :key="item.id" :size="collapseTagSize"
:closable="!selectDisabled" type="info" @close="deleteTag($event, i)"
disable-transitions>
<span class="el-select__tags-text">{{ item.fullName }}</span>
</el-tag>
</transition-group>
</div>
<el-input ref="reference" v-model="innerValue" type="text" :placeholder="currentPlaceholder"
:disabled="selectDisabled" readonly :validate-event="false"
:tabindex="(multiple) ? '-1' : null" @mouseenter.native="inputHovering = true"
@mouseleave.native="inputHovering = false">
<template slot="suffix">
<i v-show="!showClose"
:class="['el-select__caret', 'el-input__icon', 'el-icon-arrow-up']"></i>
<i v-if="showClose" class="el-select__caret el-input__icon el-icon-circle-close"
@click="handleClearClick"></i>
</template>
</el-input>
</div>
<el-dialog title="候选人员" :close-on-click-modal="false" :visible.sync="visible"
class="JNPF-dialog JNPF-dialog_center transfer-dialog" lock-scroll append-to-body
width="800px" :modal-append-to-body="false" @close="onClose">
<div class="transfer__body">
<div class="transfer-pane">
<div class="transfer-pane__tools">
<el-input placeholder="请输入关键词查询" v-model="listQuery.keyword"
@keyup.enter.native="search" clearable class="search-input">
<el-button slot="append" icon="el-icon-search" @click="search"></el-button>
</el-input>
</div>
<div class="transfer-pane__body left-pane">
<div class="custom-title">全部数据</div>
<div class="single-list" ref="candidate">
<template v-if="list.length">
<div v-for="(item,index) in list" :key="index" class="selected-item-user"
@click="handleNodeClick(item)">
<div class="selected-item-main">
<el-avatar :size="36" :src="define.comUrl+item.headIcon"
class="selected-item-headIcon">
</el-avatar>
<div class="selected-item-text">
<p class="name">{{item.fullName}}</p>
<p class="organize" :title="item.organize">{{item.organize}}</p>
</div>
</div>
</div>
</template>
<el-empty description="暂无数据" :image-size="120" v-else></el-empty>
</div>
</div>
</div>
<div class="transfer-pane">
<div class="transfer-pane__tools">
<span>已选</span>
<el-button @click="removeAll" type="text" class="removeAllBtn">清空列表</el-button>
</div>
<div class="transfer-pane__body shadow right-pane">
<template v-if="selectedData.length">
<div v-for="(item,index) in selectedData" :key="index" class="selected-item-user">
<div class="selected-item-main">
<el-avatar :size="36" :src="define.comUrl+item.headIcon"
class="selected-item-headIcon">
</el-avatar>
<div class="selected-item-text">
<p class="name">{{item.fullName}}
<i class="el-icon-delete" @click="removeData(index)"></i>
</p>
<p class="organize" :title="item.organize">{{item.organize}}</p>
</div>
</div>
</div>
</template>
<el-empty description="暂无数据" :image-size="120" v-else></el-empty>
</div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="visible=false">{{$t('common.cancelButton')}}</el-button>
<el-button type="primary" @click="confirm">{{$t('common.confirmButton')}}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { CandidateUser } from '@/api/workFlow/FlowBefore'
import { getUserInfoList } from '@/api/permission/user'
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
export default {
name: 'CandidateUser',
inject: {
elForm: {
default: ''
},
elFormItem: {
default: ''
}
},
props: {
value: {
type: [String, Array],
default: ''
},
placeholder: {
type: String,
default: '请选择'
},
disabled: {
type: Boolean,
default: false
},
multiple: {
type: Boolean,
default: true
},
collapseTags: {
type: Boolean,
default: false
},
clearable: {
type: Boolean,
default: true
},
taskId: {
type: String,
default: ''
},
nodeId: {
type: String,
default: ''
},
formData: {
type: Object,
default: () => ({})
},
size: String,
},
data() {
return {
visible: false,
innerValue: '',
loading: false,
listQuery: {
keyword: '',
currentPage: 1,
pageSize: 20,
},
total: 0,
list: [],
listLoading: true,
selectedData: [],
tagsList: [],
inputHovering: false,
inputWidth: 0,
initialInputHeight: 0,
finish: false,
}
},
watch: {
value(val) {
this.setDefault()
},
selectDisabled() {
this.$nextTick(() => {
this.resetInputHeight();
});
},
},
computed: {
showClose() {
let hasValue = this.multiple
? Array.isArray(this.value) && this.value.length > 0
: this.value !== undefined && this.value !== null && this.value !== '';
let criteria = this.clearable &&
!this.selectDisabled &&
this.inputHovering &&
hasValue;
return criteria;
},
currentPlaceholder() {
if (this.multiple && Array.isArray(this.value) && this.value.length) {
return ''
} else {
return this.placeholder
}
},
selectDisabled() {
return this.disabled || (this.elForm || {}).disabled;
},
_elFormItemSize() {
return (this.elFormItem || {}).elFormItemSize;
},
selectSize() {
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
},
collapseTagSize() {
return ['small', 'mini'].indexOf(this.selectSize) > -1
? 'mini'
: 'small';
},
},
created() {
this.setDefault()
},
mounted() {
addResizeListener(this.$el, this.handleResize);
const reference = this.$refs.reference;
if (reference && reference.$el) {
const sizeMap = {
medium: 36,
small: 32,
mini: 28
};
const input = reference.$el.querySelector('input');
this.initialInputHeight = input.getBoundingClientRect().height || sizeMap[this.selectSize];
}
if (this.multiple) {
this.resetInputHeight();
}
this.$nextTick(() => {
if (reference && reference.$el) {
this.inputWidth = reference.$el.getBoundingClientRect().width;
}
});
this.setDefault()
},
beforeDestroy() {
if (this.$el && this.handleResize) removeResizeListener(this.$el, this.handleResize);
},
methods: {
onClose() { },
clear() {
if (this.disabled) return
this.innerValue = ''
this.selectedData = []
this.$emit('input', '')
this.$emit('change', '', '')
},
openDialog() {
if (this.disabled) return
this.visible = true
this.finish = false
this.listQuery.keyword = ''
this.$nextTick(() => {
this.bindScroll()
this.search()
this.setDefault()
})
},
confirm() {
if (this.multiple) {
this.innerValue = ''
this.tagsList = JSON.parse(JSON.stringify(this.selectedData))
let selectedIds = this.selectedData.map(o => o.id)
this.$emit('input', selectedIds)
this.$emit('change', selectedIds, this.selectedData)
} else {
if (!this.selectedData.length) {
this.innerValue = ''
this.$emit('input', '')
this.$emit('change', '', {})
this.visible = false
return
}
this.innerValue = this.selectedData[0].fullName
let selectedIds = this.selectedData.map(o => o.id)
this.$emit('input', selectedIds[0])
this.$emit('change', selectedIds[0], this.selectedData[0])
}
this.visible = false
},
bindScroll() {
let _this = this,
vBody = _this.$refs.candidate;
vBody.addEventListener("scroll", function () {
if (vBody.scrollHeight - vBody.clientHeight - vBody.scrollTop <= 200 && !_this.listLoading && !_this.finish) {
_this.listQuery.currentPage += 1
_this.initData()
}
});
},
search() {
this.listQuery.currentPage = 1
this.listQuery.pageSize = 20
this.list = []
this.finish = false
this.initData()
},
initData() {
this.listLoading = true
let query = {
...this.listQuery,
...this.formData,
nodeCode: this.nodeId
}
CandidateUser(this.taskId || 0, query).then((res) => {
if (res.data.list.length < this.listQuery.pageSize) {
this.finish = true
}
this.list = [...this.list, ...res.data.list]
this.total = res.data.pagination.total
this.listLoading = false
})
},
handleNodeClick(item) {
const boo = this.selectedData.some(o => o.id === item.id)
if (boo) return
this.multiple ? this.selectedData.push(item) : this.selectedData = [item]
},
removeAll() {
this.selectedData = []
},
removeData(index) {
this.selectedData.splice(index, 1)
},
setDefault() {
if (!this.value || !this.value.length) {
this.innerValue = ''
this.selectedData = []
this.tagsList = []
return
}
const arr = this.multiple ? this.value : [this.value]
getUserInfoList(arr).then(res => {
this.selectedData = res.data.list
if (this.multiple) {
this.innerValue = ''
this.tagsList = JSON.parse(JSON.stringify(this.selectedData))
} else {
this.innerValue = this.selectedData.length ? this.selectedData[0].fullName : ''
}
this.$nextTick(() => {
if (this.multiple) {
this.resetInputHeight();
}
});
})
},
deleteTag(event, index) {
this.selectedData.splice(index, 1)
this.confirm()
event.stopPropagation();
},
handleClearClick(event) {
this.selectedData = []
this.confirm()
event.stopPropagation();
},
resetInputWidth() {
this.inputWidth = this.$refs.reference.$el.getBoundingClientRect().width;
},
handleResize() {
this.resetInputWidth();
if (this.multiple) this.resetInputHeight();
},
resetInputHeight() {
if (this.collapseTags) return;
this.$nextTick(() => {
if (!this.$refs.reference) return;
let inputChildNodes = this.$refs.reference.$el.childNodes;
let input = [].filter.call(inputChildNodes, item => item.tagName === 'INPUT')[0];
const tags = this.$refs.tags;
const tagsHeight = tags ? Math.round(tags.getBoundingClientRect().height) : 0;
const sizeInMap = this.initialInputHeight || 40;
input.style.height = this.selectedData.length === 0
? sizeInMap + 'px'
: Math.max(
tags ? (tagsHeight + (tagsHeight > sizeInMap ? 6 : 0)) : 0,
sizeInMap
) + 'px';
});
},
resetInputWidth() {
this.inputWidth = this.$refs.reference.$el.getBoundingClientRect().width;
},
handleResize() {
this.resetInputWidth();
if (this.multiple) this.resetInputHeight();
}
},
}
</script>

@ -0,0 +1,410 @@
<template>
<div class="comment-container">
<div class="comment-list" v-loading="listLoading && listQuery.currentPage==1"
ref="infiniteBody">
<template v-if="list.length">
<div v-for="(item,i) in list" :key="i" class="item">
<div class="head">
<el-avatar :size="40" :src="define.comUrl + item.creatorUserHeadIcon" class="avatar" />
<p class="username">{{item.creatorUser}}</p>
<el-link :underline="false" class="del-btn" @click="delComment(item.id,i)" type="danger"
v-if="item.isDel">删除</el-link>
<span class="time">{{item.creatorTime|toDate()}}</span>
</div>
<div class="content">
<p class="text">{{item.text}}</p>
<div class="img-list" v-if="item.imageList.length">
<el-image :src="define.comUrl+cItem.url" class="img-item"
v-for="(cItem,ci) in item.imageList" :key="ci"
:preview-src-list="getImgList(item.imageList)" :z-index="10000">
</el-image>
</div>
<div class="file-List" v-if="item.fileList.length">
<JnpfUploadFile v-model="item.fileList" detailed disabled></JnpfUploadFile>
</div>
</div>
</div>
</template>
<el-empty description="暂无数据" :image-size="120" v-else></el-empty>
</div>
<el-dialog title="流程评论" :visible.sync="dialogVisible" :close-on-click-modal="false"
class="JNPF-dialog JNPF-dialog_center" lock-scroll append-to-body width="600px">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="0">
<el-form-item prop="text">
<el-input v-model="dataForm.text" placeholder="请输入评论" type="textarea" :rows="4" />
</el-form-item>
<el-form-item>
<el-upload :action="define.comUploadUrl+'/annexpic'" :headers="uploadHeaders"
ref="elUploadImg" :on-success="handleImgSuccess" multiple :show-file-list="false"
accept="image/*" :before-upload="beforeImgUpload" :on-exceed="handleImgExceed"
:limit="9" class="upload-btn">
<el-button size="small" icon="el-icon-picture">图片</el-button>
</el-upload>
<el-button size="small" icon="el-icon-upload" @click="uploadFile"></el-button>
</el-form-item>
<el-form-item prop="image" v-if="dataForm.image.length">
<div class="img-list">
<div class="img-item" v-for="(item,i) in dataForm.image" :key="i">
<el-image :src="define.comUrl+item.url" class=""
:preview-src-list="getImgList(dataForm.image)" :z-index="100">
</el-image>
<div class="badge" @click.stop="handleImgRemove(i)">
<i class="el-icon-close"></i>
</div>
</div>
</div>
</el-form-item>
<el-form-item prop="file">
<ul class="el-upload-list el-upload-list el-upload-list--text">
<li class="el-upload-list__item is-success" v-for="(file,index) in dataForm.file"
:key="file.fileId">
<a class="el-upload-list__item-name">
<i class="el-icon-paperclip"></i>
{{file.name}}{{file.fileSize?`${jnpf.toFileSize(file.fileSize)}`:''}}
</a>
<i class="el-icon-view" title="查看" @click="handleFilePreview(file)"></i>
<i class="el-icon-download" title="下载" @click="handleFileClick(file)"></i>
<label class="el-upload-list__item-status-label">
<i class="el-icon-upload-success el-icon-circle-check"></i>
</label>
<i class="el-icon-close" title="删除" @click="handleFileRemove(index)"></i>
</li>
</ul>
<fileUploader ref="fileUploader" :limit="2" :fileSize="50" type="annex"
:accept="fileAccept" @fileSuccess="fileSuccess" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible=false">{{$t('common.cancelButton')}}
</el-button>
<el-button type="primary" @click="addComment()" :loading="btnLoading">
{{$t('common.confirmButton')}}</el-button>
</span>
</el-dialog>
<Preview :visible.sync="previewVisible" :file="activeFile" />
</div>
</template>
<script>
import { getCommentList, createComment, delComment } from '@/api/workFlow/FlowEngine'
import { getDownloadUrl } from '@/api/common'
import Preview from '@/components/Jnpf/Upload/Preview'
import FileUploader from './FileUploader'
export default {
name: 'comments',
components: { Preview, FileUploader },
props: {
id: { type: String, default: '' },
},
data() {
return {
list: [],
listQuery: {
currentPage: 1,
pageSize: 20,
sort: 'desc',
sidx: ''
},
total: 0,
listLoading: false,
btnLoading: false,
dataRule: {},
dataForm: {
text: '',
file: [],
image: [],
},
dialogVisible: false,
finish: false,
uploadHeaders: { Authorization: this.$store.getters.token },
imgUploading: false,
previewVisible: false,
fileAccept: '.xls,.xlsx,.doc,.docx,.pdf,.txt,.ppt,.pptx',
activeFile: {}
}
},
methods: {
init() {
this.list = []
this.listQuery = {
currentPage: 1,
pageSize: 20,
sort: 'desc',
sidx: ''
}
this.finish = false
this.initData()
this.$nextTick(() => {
this.bindScroll()
})
},
bindScroll() {
let _this = this,
vBody = _this.$refs.infiniteBody;
vBody.addEventListener("scroll", function () {
if (vBody.scrollHeight - vBody.clientHeight - vBody.scrollTop <= 600 && !_this.listLoading && !_this.finish) {
_this.listQuery.currentPage += 1
_this.initData()
}
});
},
initData() {
this.listLoading = true
let query = {
...this.listQuery,
taskId: this.id
}
getCommentList(query).then(res => {
let list = res.data.list.map(o => ({
...o,
fileList: o.file ? JSON.parse(o.file) : [],
imageList: o.image ? JSON.parse(o.image) : [],
}))
if (res.data.list.length < this.listQuery.pageSize) {
this.finish = true
}
this.list = [...this.list, ...list]
this.total = res.data.pagination.total
this.listLoading = false
}).catch(() => {
this.listLoading = false
})
},
showCommentDialog() {
this.dialogVisible = true
this.dataForm.image = []
this.dataForm.file = []
this.$nextTick(() => {
this.$refs.elUploadImg.uploadFiles = []
this.$refs['dataForm'].resetFields()
})
},
getImgList(list) {
const newList = list.map(o => this.define.comUrl + o.url)
return newList
},
addComment() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.btnLoading = true
let query = {
text: this.dataForm.text,
file: JSON.stringify(this.dataForm.file),
image: JSON.stringify(this.dataForm.image),
taskId: this.id
}
createComment(query).then(res => {
this.$message({
message: res.msg,
type: 'success',
duration: 1500,
onClose: () => {
this.dialogVisible = false
this.btnLoading = false
this.init()
}
})
}).catch(() => { this.btnLoading = false })
}
})
},
delComment(id, index) {
this.$confirm(this.$t('common.delTip'), this.$t('common.tipTitle'), {
type: 'warning'
}).then(() => {
delComment(id).then(res => {
this.list.splice(index, 1)
this.$message({
type: 'success',
message: res.msg
})
})
}).catch(() => { });
},
beforeImgUpload(file) {
let isRightSize = file.size < 50 * 1024 * 1024
if (!isRightSize) {
this.$message.error(`图片大小超过50MB`)
return isRightSize;
}
let isAccept = new RegExp('image/*').test(file.type)
if (!isAccept) {
this.$message.error(`请上传图片`)
return isAccept;
}
if (isRightSize && isAccept) this.imgUploading = true
return isRightSize && isAccept;
},
handleImgSuccess(res, file, fileList) {
if (res.code == 200) {
this.dataForm.image.push({
name: file.name,
fileId: res.data.name,
url: res.data.url
})
} else {
this.$refs.elUploadImg.uploadFiles.splice(fileList.length - 1, 1)
fileList.filter(o => o.uid != file.uid)
this.$message({ message: res.msg, type: 'error', duration: 1500 })
}
this.imgUploading = false
},
handleImgExceed(files, fileList) {
this.$message.warning(`当前限制最多可以上传9张图片`)
},
handleImgRemove(index) {
this.dataForm.image.splice(index, 1)
this.$refs.elUploadImg.uploadFiles.splice(index, 1)
},
uploadFile() {
const isTopLimit = this.dataForm.file.length >= 2
if (isTopLimit) {
this.$message.error(`当前限制最多可以上传2个文件`)
return false
}
this.$refs.fileUploader && this.$refs.fileUploader.openUploader()
},
fileSuccess(data) {
const isTopLimit = this.dataForm.file.length >= 2
if (isTopLimit) {
this.$message.error(`当前限制最多可以上传2个文件`)
return false
}
this.dataForm.file.push(data)
},
handleFileRemove(index) {
this.dataForm.file.splice(index, 1)
},
handleFileClick(file) {
if (!file.fileId) return
getDownloadUrl('annex', file.fileId).then(res => {
this.jnpf.downloadFile(res.data.url, file.name)
})
},
handleFilePreview(file) {
this.activeFile = file
this.previewVisible = true
}
}
}
</script>
<style lang="scss" scoped>
.comment-container {
height: 100%;
overflow: hidden;
.comment-list {
height: 100%;
overflow: auto;
.item {
padding: 0 30px;
margin-bottom: 20px;
.head {
display: flex;
align-items: center;
.avatar {
flex-shrink: 0;
margin-right: 20px;
}
.username {
line-height: 40px;
color: #333;
flex: 1;
}
.head-right {
flex-shrink: 0;
}
.time {
flex-shrink: 0;
font-size: 14px;
color: #666;
margin-left: 20px;
}
}
.content {
font-size: 14px;
padding-left: 60px;
.text {
line-height: 30px;
color: #666;
}
.img-list {
margin-top: 6px;
.img-item {
width: 80px;
height: 80px;
overflow: hidden;
margin: 0 6px 6px 0;
// border: 1px solid #c0ccda;
border-radius: 6px;
}
}
}
}
}
}
.JNPF-dialog {
.upload-btn {
display: inline-block;
margin-right: 20px;
}
.img-list {
display: flex;
.img-item {
width: 40px;
height: 40px;
position: relative;
margin-right: 10px;
.el-image {
width: 100%;
height: 100%;
}
.badge {
background-color: #f56c6c;
border-radius: 10px;
color: #fff;
display: block;
font-size: 12px;
height: 18px;
width: 18px;
line-height: 18px;
text-align: center;
border: 1px solid #fff;
position: absolute;
right: -9px;
top: -9px;
cursor: pointer;
z-index: 10001;
}
}
}
.el-upload-list__item {
.el-upload-list__item-name {
margin-right: 70px;
.el-icon-paperclip {
line-height: 25px;
}
}
.el-icon-download {
display: inline-block;
position: absolute;
top: 5px;
right: 25px;
cursor: pointer;
opacity: 0.75;
color: #606266;
}
.el-icon-view {
display: inline-block;
position: absolute;
top: 5px;
right: 45px;
cursor: pointer;
opacity: 0.75;
color: #606266;
}
}
}
</style>

@ -0,0 +1,270 @@
<template>
<div>
<el-popover placement="bottom-start" width="500" trigger="manual" ref="popover"
v-model="visible" class="popover-container">
<JNPF-table :data="list" class="table" :border="false" @row-click="rowClick" :hasNO="false"
:show-header='false'>
<el-table-column label="字段" prop="commonWordsText" align="left">
</el-table-column>
<el-table-column label="操作" width="100">
<template slot-scope="scope">
<el-button type="text" @click="addOrUpdateHandle(scope.row.id)"
v-if="scope.row.commonWordsType" icon="el-icon-edit-outline"
class="outline"></el-button>
<el-button type="text" class="JNPF-table-delBtn outline" icon="el-icon-delete"
v-if="scope.row.commonWordsType" @click="handleDel(scope.row.id)"></el-button>
</template>
</el-table-column>
</JNPF-table>
<div class="actions">
<el-button @click="visible = false">{{$t('common.cancelButton')}}</el-button>
<el-button type="primary" @click="addOrUpdateHandle()">
</el-button>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">{{$t('common.cancelButton')}}</el-button>
<el-button type="primary" @click="select()" :loading="btnLoading">
{{$t('common.confirmButton')}}
</el-button>
</span>
<el-button slot="reference" type="text" @click="openDialog"
icon="el-icon-plus">常用语</el-button>
</el-popover>
<el-dialog :title="!this.dataForm.id?'新增审批常用语':'编辑审批常用语'" :close-on-click-modal="false"
width='600px' :visible.sync="commonWordsVisible" class="JNPF-dialog JNPF-dialog_center"
@close='cancel()' lock-scroll append-to-body>
<el-form ref="dataForm" :model="dataForm" :rules="dataRule" v-loading="formLoading"
label-width="100px">
<el-form-item label="常用语" prop="commonWordsText">
<el-input v-model="dataForm.commonWordsText" placeholder="输入常用语" />
</el-form-item>
<el-form-item label="排序" prop="sortCode">
<el-input-number :min="0" :max="999999" v-model="dataForm.sortCode"
controls-position="right" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="cancel()">{{$t('common.cancelButton')}}</el-button>
<el-button type="primary" :loading="dataFormBtnLoading" @click="dataFormSubmit(1)"
:disabled='commonBtnLoading'>
{{$t('common.confirmButton')}}</el-button>
<el-button @click="dataFormSubmit(2)" type="primary" v-if="!this.dataForm.id"
:loading="commonBtnLoading" :disabled='dataFormBtnLoading'>
保存并新增
</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {
getSelector,
Update,
Create,
getCommonWordsInfo,
deleteCommonWords
} from '@/api/system/commonWords'
export default {
components: {},
props: {
},
data() {
return {
visible: false,
eventType: '',
dataForm: {
systemNames: [],
systemIds: [],
commonWordsText: '',
commonWordsType: 1,
sortCode: '',
enabledMark: 1,
},
dataRule: {
commonWordsText: [
{ required: true, message: '请输入常用语', trigger: 'blur' },
],
},
list: [],
listQuery: {
keyword: '',
currentPage: 1,
pageSize: 20
},
checked: '',
checkedRow: {},
signVisible: false,
signImg: '',
btnLoading: false,
title: '',
label: '',
commonWordsVisible: false,
commonBtnLoading: false,
formLoading: false,
dataFormBtnLoading: false,
state: false
}
},
computed: {
},
methods: {
close() {
this.visible = false
},
openDialog() {
this.state = false
this.visible = true
this.initData()
},
addOrUpdateHandle(id) {
if (id) this.state = true
this.commonWordsVisible = true
this.formLoading = true
this.dataForm.id = id || 0
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
if (this.dataForm.id) {
getCommonWordsInfo(this.dataForm.id).then(res => {
this.dataForm = res.data || {}
this.formLoading = false
})
}
this.formLoading = false
})
},
select() {
if (this.state) return
this.$emit('input', this.checked)
this.$emit('change', this.checkedRow)
this.visible = false
},
initData() {
getSelector().then(res => {
this.list = res.data.list || []
this.listLoading = false
}).catch(() => {
this.listLoading = false
})
},
cancel() {
this.commonWordsVisible = false
this.checked = ''
this.checkedRow = {}
this.openDialog()
},
clear() {
this.checked = ''
this.checkedRow = {}
this.$emit('input', this.checked)
this.$emit('change', this.checked, this.checkedRow)
},
closeDialog() {
this.btnLoading = false
this.visible = false
},
rowClick(row) {
console.log(222)
this.checked = row.id
this.checkedRow = row
this.select()
},
handleDel(id) {
this.state = true
this.$confirm(this.$t('common.delTip'), this.$t('common.tipTitle'), {
type: 'warning',
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true;
setTimeout(() => {
instance.confirmButtonLoading = false;
done();
}, 1000);
} else {
this.state = false
done()
}
}
}).then(() => {
deleteCommonWords(id).then(res => {
this.$message({
type: 'success',
message: res.msg,
duration: 1500,
onClose: () => {
this.checked = ''
this.checkedRow = {}
this.openDialog()
}
})
})
}).catch(() => { })
},
dataFormSubmit(type) {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
if (type == 2) {
this.continueBtnLoading = true
} else {
this.dataFormBtnLoading = true
}
const formMethod = this.dataForm.id ? Update : Create
formMethod(this.dataForm).then(res => {
if (type == 2) {
this.$message({
message: res.msg,
type: 'success',
duration: 1500,
})
this.$nextTick(() => {
this.continueBtnLoading = false
this.$refs['dataForm'].resetFields()
})
} else {
this.$message({
message: res.msg,
type: 'success',
duration: 1500,
onClose: () => {
this.commonWordsVisible = false
this.commonBtnLoading = false
this.dataFormBtnLoading = false
this.state = false
this.checked = ''
this.checkedRow = {}
this.openDialog()
}
})
}
}).catch(() => {
this.btnLoading = false
this.commonWordsVisible = false
})
}
})
}
}
}
</script>
<style lang="scss" scoped>
.outline {
font-size: 18px;
}
.table {
height: 219px;
overflow: auto;
}
.actions {
text-align: right;
margin-top: 10px;
}
>>> .el-table tr:last-child td {
border-bottom: unset;
}
>>> .el-table {
border-top: unset !important;
}
</style>

@ -0,0 +1,56 @@
<template>
<el-dialog title="异常处理" :close-on-click-modal="false" class="JNPF-dialog JNPF-dialog_center"
lock-scroll append-to-body v-bind="$attrs" width="600px" :modal-append-to-body="false"
v-on="$listeners" @open="onOpen">
<el-form label-width="100px" ref="dataForm" :model="dataForm">
<el-form-item :label="item.nodeName" :prop="'nodeList.' + i + '.value'"
v-for="(item,i) in dataForm.nodeList" :key="i" :rules="item.rules">
<JnpfUserSelect v-model="item.value" multiple placeholder="请选择异常处理人员" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="closeDialog">{{$t('common.cancelButton')}}</el-button>
<el-button type="primary" @click="submit()">{{$t('common.confirmButton')}}
</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
props: ['nodeList'],
data() {
return {
dataForm: {
nodeList: []
}
}
},
methods: {
onOpen() {
this.dataForm.nodeList = this.nodeList.map(o => ({
...o,
value: [],
rules: [{ required: true, message: `异常处理人员不能为空`, trigger: 'click' }]
}))
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
})
},
submit() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
let nodeList = {}
for (let i = 0; i < this.dataForm.nodeList.length; i++) {
nodeList[this.dataForm.nodeList[i].nodeId] = this.dataForm.nodeList[i].value
}
this.$emit('submit', nodeList)
}
})
},
closeDialog() {
this.$emit('update:visible', false)
},
}
}
</script>

@ -0,0 +1,112 @@
<template>
<transition name="el-zoom-in-center">
<div class="JNPF-preview-main order-form">
<div class="JNPF-common-page-header">
<el-page-header @back="goBack" :content="title" />
<div class="options">
<el-button @click="goBack()">{{$t('common.cancelButton')}}</el-button>
</div>
</div>
<div class="main">
<JNPF-table v-loading="listLoading" :data="list" :hasNO="false">
<el-table-column type="index" width="50" label="序号" align="center" />
<el-table-column prop="fullName" label="事件名称" show-overflow-tooltip width="150" />
<el-table-column prop="interfaceName" label="接口名称" show-overflow-tooltip width="150" />
<el-table-column prop="interfaceCode" label="接口编码" width="150" />
<el-table-column prop="creatorTime" label="执行时间" width="100">
<template
slot-scope="scope">{{scope.row.creatorTime | toDate("yyyy-MM-dd HH:mm:ss" ) }}</template>
</el-table-column>
<el-table-column label="执行结果" min-width="200" prop="result" />
</JNPF-table>
</div>
</div>
</transition>
</template>
<script>
import { eventLog } from '@/api/workFlow/FlowBefore'
export default {
components: {},
data() {
return {
title: '',
taskNodeId: '',
listLoading: false,
list: []
}
},
methods: {
goBack() {
this.$emit('close')
},
init(id, fullName) {
this.taskNodeId = id || ''
this.title = fullName + '审批节点名称'
this.initData()
},
initData() {
this.listLoading = true
eventLog(this.taskNodeId).then(res => {
this.list = res.data.list || []
this.listLoading = false
})
},
}
}
</script>
.main {
overflow: hidden;
display: flex;
flex-direction: column;
padding: 0;
>>> .el-table {
flex: 1;
}
.JNPF-common-search-box-right {
margin-right: 10px;
}
.item-on {
position: absolute;
top: 50%;
left: 0;
right: -1px;
margin-top: -8px;
height: 16px;
background: #409eff;
-webkit-transition: all 0.4s;
transition: all 0.4s;
&.item-start {
left: 50%;
// &:after {
// position: absolute;
// top: 16px;
// left: 0;
// z-index: 1;
// content: "";
// width: 0;
// height: 0;
// border-color: #409eff transparent transparent;
// border-width: 6px 6px 6px 0;
// border-style: solid;
// }
}
&.item-end {
right: 50%;
// &:after {
// position: absolute;
// top: 16px;
// right: 0;
// z-index: 1;
// content: "";
// width: 0;
// height: 0;
// border-color: transparent #409eff;
// border-width: 0 6px 6px 0;
// border-style: solid;
// }
}
}
}
</style>

@ -0,0 +1,183 @@
<template>
<div id="common-file-uploader" :class="{'hasDefault': !!value.length}">
<uploader ref="uploader" :options="options" :autoStart="false" @file-added="onFileAdded"
@file-success="onFileSuccess" @file-progress="onFileProgress" @file-error="onFileError"
class="uploader-app" @complete="onComplete" :file-status-text="statusText">
<uploader-unsupport></uploader-unsupport>
<uploader-btn id="file-uploader-btn" ref="uploadBtn" :attrs="attrs">选择文件</uploader-btn>
<uploader-list>
<template slot-scope="{ fileList }">
<ul class="el-upload-list el-upload-list el-upload-list--text">
<li class="el-upload-list__item" v-for="file in fileList" :key="file.id">
<uploader-file :class="'file_' + file.id" ref="files" :file="file" :list="true">
<template slot-scope="props">
<FileItem :file="props.file" :list="props.list" />
</template>
</uploader-file>
</li>
</ul>
</template>
</uploader-list>
</uploader>
</div>
</template>
<script>
import { chunkMerge } from '@/api/common'
import uploadMixin from '@/components/Jnpf/Upload/vue-simple-uploader/mixin'
const units = {
KB: 1024,
MB: 1024 * 1024,
GB: 1024 * 1024 * 1024
}
export default {
props: {
value: {
type: Array,
default: () => []
},
type: {
type: String,
default: 'annex'
},
limit: {
type: Number,
default: 0
},
accept: {
type: String,
default: '*'
},
sizeUnit: {
type: String,
default: 'MB'
},
fileSize: {
default: 5
}
},
mixins: [uploadMixin],
data() {
return {
}
},
computed: {
acceptText() {
let txt = ''
switch (this.accept) {
case 'image/*':
txt = '图片'
break;
case 'video/*':
txt = '视频'
break;
case 'audio/*':
txt = '音频'
break;
case '.xls,.xlsx':
txt = 'excel'
break;
case '.doc,.docx':
txt = 'word'
break;
case '.pdf':
txt = 'pdf'
break;
case '.txt':
txt = 'txt'
break;
default:
txt = ''
break;
}
return txt
},
},
methods: {
beforeUpload(file) {
const isTopLimit = this.limit ? this.value.length >= this.limit : false
if (isTopLimit) {
this.$message.error(`当前限制最多可以上传${this.limit}个文件`)
return false
}
const unitNum = units[this.sizeUnit]
let isRightSize = this.fileSize ? file.size / unitNum < this.fileSize : true
if (!isRightSize) {
this.$message.error(`文件大小超过${this.fileSize}${this.sizeUnit}`)
return isRightSize
}
let extension = file.getExtension()
let isAccept = this.accept.indexOf(extension) > -1
if (!isAccept) {
this.$message.error(`请选择${this.accept}类型的文件`)
return isAccept
}
return true
},
handelSuccess(file) {
const form = new FormData()
form.append('identifier', file.uniqueIdentifier)
form.append('fileName', file.name)
form.append('fileSize', file.size)
form.append('fileType', file.getType())
form.append('extension', file.getExtension())
form.append('type', this.type)
chunkMerge(form).then(res => {
//
this.$set(file, 'customCompleted', true)
let data = {
name: file.name,
fileId: res.data.name,
fileSize: res.data.fileSize,
fileExtension: res.data.fileExtension,
fileVersionId: res.data.fileVersionId,
url: res.data.url
}
this.$emit('fileSuccess', data)
file.cancel()
}).catch(() => {
file.cancel()
})
}
}
}
</script>
<style lang="scss" scoped>
#common-file-uploader {
margin: 0;
padding: 0;
font-size: 0;
&.hasDefault {
.el-upload-list__item:first-child {
margin-top: 5px;
}
}
.el-upload-list {
>>> .uploader-file {
border-bottom: none;
height: 25px !important;
line-height: 25px;
&:hover {
background-color: #f5f7fa;
}
}
}
>>> .uploader-file-icon {
&:before {
content: '' !important;
}
}
>>> .uploader-file-actions > span {
margin-right: 6px;
}
}
/* 隐藏上传按钮 */
#file-uploader-btn {
position: absolute;
clip: rect(0, 0, 0, 0);
}
</style>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,437 @@
<template>
<div class="popupSelect-container">
<div class="el-select" @click.stop="flowSelect">
<div class="el-select__tags" v-if="multiple" ref="tags"
:style="{ 'max-width': inputWidth - 32 + 'px', width: '100%',cursor:'pointer' }">
<transition-group @after-leave="resetInputHeight" v-if="!collapseTags">
<el-tag v-for="(item,i) in tagsList" :key="item.id" :size="collapseTagSize"
:closable="!selectDisabled" type="info" @close="deleteTag($event, i)"
disable-transitions>
<span
class="el-select__tags-text">{{item.fullName.length>20?item.fullName.substring(0,19)+'...':item.fullName}}/{{item.enCode.length>30?item.enCode.substring(0,30)+'...':item.enCode}}</span>
</el-tag>
</transition-group>
</div>
<el-input ref="reference" v-model="innerValue" type="text" :placeholder="currentPlaceholder"
:disabled="selectDisabled" readonly :validate-event="false"
:tabindex="(multiple) ? '-1' : null" @mouseenter.native="inputHovering = true"
@mouseleave.native="inputHovering = false">
<template slot="suffix">
<i v-show="!showClose"
:class="['el-select__caret', 'el-input__icon', 'el-icon-arrow-up']"></i>
<i v-if="showClose" class="el-select__caret el-input__icon el-icon-circle-close"
@click="handleClearClick"></i>
</template>
</el-input>
</div>
<el-dialog title="委托流程" :visible.sync="visible" :before-close="cancelConfirm"
class="JNPF-dialog JNPF-dialog_center JNPF-dialog-tree-select" lock-scroll append-to-body
width="1000px">
<div class="JNPF-common-layout">
<div class="JNPF-common-layout-left" style="width: 150px;">
<el-scrollbar class="JNPF-common-el-tree-scrollbar " v-loading="treeLoading">
<el-tree ref="treeBox" :data="categoryList" default-expand-all :current-node-key="0"
highlight-current :expand-on-click-node="false" node-key="id" lock-scroll
@node-click="handleNodeClick" class="JNPF-common-el-tree">
<span class="custom-tree-node" slot-scope="{ data }" :title="data.fullName">
<span class="text" :title="data.fullName" style="margin-left:20px">
{{data.fullName}}
</span>
</span>
</el-tree>
</el-scrollbar>
</div>
<div class="JNPF-common-layout-center">
<el-row class="JNPF-common-search-box" :gutter="16">
<el-form @submit.native.prevent>
<el-col :span="8">
<el-form-item label="关键词">
<el-input v-model="keyword" placeholder="请输入关键词查询" clearable
@keyup.enter.native="search()" class="search-input" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="search()"></el-button>
<el-button icon="el-icon-refresh-right" @click="reset()"></el-button>
</el-form-item>
</el-col>
</el-form>
<div class="JNPF-common-search-box-right">
<el-tooltip effect="dark" content="刷新" placement="top">
<el-link icon="icon-ym icon-ym-Refresh JNPF-common-head-icon" :underline="false"
@click="initData()" />
</el-tooltip>
</div>
</el-row>
<div class="JNPF-common-layout-main JNPF-flex-main">
<JNPF-table v-loading="listLoading" :data="tableData" :border="false"
highlight-current-row ref="multipleTable" @select="handleSelection"
@select-all="handleSelectionAll" :hasNO="false" has-c>
<el-table-column prop="fullName" label="流程名称" />
<el-table-column prop="enCode" label="流程编码" />
<el-table-column prop="type" label="流程类型" width="120">
<template slot-scope="scope">
<span>{{ scope.row.type == 0? "发起流程" : "功能流程" }}</span>
</template>
</el-table-column>
</JNPF-table>
<pagination :total="total" :page.sync="listQuery.currentPage"
:limit.sync="listQuery.pageSize" @pagination="initData" />
</div>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="cancelConfirm()">{{$t('common.cancelButton')}}</el-button>
<el-button type="primary" @click="confirm()">{{$t('common.confirmButton')}}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
import { FlowEngineAll, FlowEngineListByIds } from '@/api/workFlow/FlowEngine'
export default {
name: 'flowSelect',
inject: {
elForm: {
default: ''
},
elFormItem: {
default: ''
}
},
props: {
value: {
type: Array,
default: []
},
name: {
type: String,
default: ''
},
clearable: {
type: Boolean,
default: true
},
multiple: {
type: Boolean,
default: false
},
collapseTags: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: '全部流程'
},
},
data() {
return {
tableData: [],
innerValue: '',
listQuery: {
currentPage: 1,
pageSize: 20,
sort: 'desc',
},
keyword: '',
total: 0,
listLoading: false,
query: {
categoryId: '',
keyword: '',
},
treeLoading: false,
visible: false,
multipleSelection: '',
categoryList: [],
selectedData: [],
tagsList: [],
inputWidth: 0,
}
},
watch: {
value(val) {
this.setDefault()
},
selectDisabled() {
this.$nextTick(() => {
this.resetInputHeight();
});
},
},
computed: {
showClose() {
let hasValue = this.multiple
? Array.isArray(this.value) && this.value.length > 0
: this.value !== undefined && this.value !== null && this.value !== '';
let criteria = this.clearable &&
!this.selectDisabled &&
this.inputHovering &&
hasValue;
return criteria;
},
currentPlaceholder() {
if (this.multiple && Array.isArray(this.value) && this.value.length) {
return ''
} else {
return this.placeholder
}
},
selectDisabled() {
return this.disabled || (this.elForm || {}).disabled;
},
_elFormItemSize() {
return (this.elFormItem || {}).elFormItemSize;
},
selectSize() {
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
},
collapseTagSize() {
return ['small', 'mini'].indexOf(this.selectSize) > -1
? 'mini'
: 'small';
},
},
created() {
this.getDictionaryData()
this.setDefault()
},
mounted() {
addResizeListener(this.$el, this.handleResize);
const reference = this.$refs.reference;
if (reference && reference.$el) {
const sizeMap = {
medium: 36,
small: 32,
mini: 28
};
const input = reference.$el.querySelector('input');
this.initialInputHeight = input.getBoundingClientRect().height || sizeMap[this.selectSize];
}
if (this.multiple) {
this.resetInputHeight();
}
this.$nextTick(() => {
if (reference && reference.$el) {
this.inputWidth = reference.$el.getBoundingClientRect().width;
}
});
this.setDefault()
},
beforeDestroy() {
if (this.$el && this.handleResize) removeResizeListener(this.$el, this.handleResize);
},
methods: {
initData() {
this.listLoading = true
this.tableData = []
let query = {
...this.listQuery,
keyword: this.keyword,
category: this.categoryId ? this.categoryId : ""
}
FlowEngineAll(query).then((res) => {
this.tableData = res.data.list
this.total = res.data.pagination.total
if (this.tableData.length && this.selectedData.length) {
let arr = []
this.$nextTick(() => {
this.tableData.forEach(row => { //
this.selectedData.forEach(item => {
if (row.id === item.id) { //
arr.push(row)
this.$refs.multipleTable.$refs.JNPFTable.toggleRowSelection(row, true)
}
})
})
})
}
this.listLoading = false
}).catch(() => {
this.listLoading = false
})
},
setDefault() {
if (!this.value || !this.value.length) {
this.innerValue = ''
this.selectedData = []
this.tagsList = []
return
}
const arr = this.multiple ? this.value : [this.value]
FlowEngineListByIds(arr).then((res) => {
this.selectedData = res.data
if (this.multiple) {
this.innerValue = ''
this.tagsList = JSON.parse(JSON.stringify(this.selectedData))
} else {
this.innerValue = this.selectedData.length ? this.selectedData[0].fullName : ''
}
this.$nextTick(() => {
if (this.multiple) {
this.resetInputHeight();
}
})
})
},
flowSelect() {
this.visible = true
this.reset()
},
getDictionaryData() {
this.$store.dispatch('base/getDictionaryData', { sort: 'WorkFlowCategory' }).then((res) => {
this.categoryList.push({
id: 0,
encode: "all",
fullName: "全部流程",
})
this.categoryList.push(...res)
})
this.categoryId = 0
},
handleNodeClick(data) {
this.categoryId = data.id ? data.id : 0
this.initData()
},
reset() {
this.keyword = ''
this.search()
},
search() {
this.listQuery.currentPage = 1
this.listQuery.pageSize = 20
this.listQuery.sort = 'desc'
this.initData()
},
handleSelection(selection, val) {
const index = this.selectedData.findIndex((item) => {
return item.id == val.id
})
if (index == -1) {
this.selectedData.push(val)
} else {
this.selectedData.splice(index, 1)
}
this.selectedData = [...new Set(this.selectedData)]
},
handleSelectionAll(selection) {
if (selection.length) {
if (this.selectedData.length) {
this.selectedData.forEach((item, index) => {
selection.forEach(it => {
if (item.id != it.id) {
this.selectedData.push(it)
}
});
});
} else {
this.selectedData.push(...selection)
}
} else {
if (this.selectedData.length && this.tableData.length) {
this.tableData.forEach(item => {
const index = this.selectedData.findIndex((it) => {
return item.id == it.id
})
if (index != -1) {
this.selectedData.splice(index, 1)
}
});
}
}
const map = new Map()
this.selectedData = this.selectedData.filter(key => !map.has(key.id) && map.set(key.id))
},
//
confirm() {
if (this.multiple) {
this.innerValue = ''
this.tagsList = JSON.parse(JSON.stringify(this.selectedData))
let selectedIds = this.selectedData.map(o => o.id)
this.$emit('input', selectedIds)
this.$emit('changeName', this.tagsList)
} else {
if (!this.selectedData.length) {
this.innerValue = ''
this.$emit('input', '')
this.$emit('changeName', '', {})
this.visible = false
return
}
this.innerValue = this.selectedData[0].fullName
let selectedIds = this.selectedData.map(o => o.id)
this.$emit('input', selectedIds[0])
this.$emit('changeName', this.tagsList)
}
this.visible = false
},
cancelConfirm() {
this.setDefault()
this.visible = false
},
removeAll() {
this.selectedData = []
},
removeData(index) {
this.selectedData.splice(index, 1)
},
deleteTag(event, index) {
this.selectedData.splice(index, 1)
this.confirm()
event.stopPropagation();
},
handleClearClick(event) {
this.selectedData = []
this.confirm()
event.stopPropagation();
},
resetInputWidth() {
this.inputWidth = this.$refs.reference.$el.getBoundingClientRect().width;
},
handleResize() {
this.resetInputWidth();
if (this.multiple) this.resetInputHeight();
},
resetInputHeight() {
if (this.collapseTags) return;
this.$nextTick(() => {
if (!this.$refs.reference) return;
let inputChildNodes = this.$refs.reference.$el.childNodes;
let input = [].filter.call(inputChildNodes, item => item.tagName === 'INPUT')[0];
const tags = this.$refs.tags;
const tagsHeight = tags ? Math.round(tags.getBoundingClientRect().height) : 0;
const sizeInMap = this.initialInputHeight || 40;
input.style.height = this.selectedData.length === 0
? sizeInMap + 'px'
: Math.max(
tags ? (tagsHeight + (tagsHeight > sizeInMap ? 6 : 0)) : 0,
sizeInMap
) + 'px';
});
},
}
}
</script>
<style lang="scss" scoped>
.flow_category {
height: 30px;
width: 100%;
line-height: 30px;
text-align: center;
border-bottom: 1px solid #dcdfe6;
}
</style>

@ -0,0 +1,75 @@
<template>
<el-dialog :title="setting.title" :close-on-click-modal="false" :visible.sync="visible"
class="JNPF-dialog JNPF-dialog_center" lock-scroll append-to-body width='1000px'
destroy-on-close>
<div class="main" v-loading="loading">
<component :is="currentView" ref="form" :config="setting" @setLoad="setLoad"
@setCandidateLoad="setCandidateLoad" @setPageLoad="setPageLoad" />
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">{{$t('common.cancelButton')}}</el-button>
</span>
</el-dialog>
</template>
<script>
import { FlowBeforeInfo } from '@/api/workFlow/FlowBefore'
export default {
data() {
return {
currentView: '',
setting: {},
flowFormInfo: {},
flowTaskInfo: {},
visible: false,
loading: false,
btnLoading: false,
}
},
methods: {
init(data) {
this.loading = true
this.setting = data
this.visible = true
this.getBeforeInfo(data)
},
getBeforeInfo(data) {
FlowBeforeInfo(data.id, { taskNodeId: data.taskNodeId, flowId: data.flowId }).then(res => {
this.flowFormInfo = res.data.flowFormInfo
this.flowTaskInfo = res.data.flowTaskInfo
data.fullName = this.flowTaskInfo.fullName
this.flowUrgent = this.flowTaskInfo.flowUrgent || 1
data.type = this.flowTaskInfo.type
data.formData = res.data.formData || {}
data.draftData = res.data.draftData || null
const formUrl = this.flowFormInfo.formType == 2 ? 'workFlow/workFlowForm/dynamicForm' : this.flowFormInfo.urlAddress ? this.flowFormInfo.urlAddress.replace(/\s*/g, "") : `workFlow/workFlowForm/${this.flowFormInfo.enCode}`
this.currentView = (resolve) => require([`@/views/${formUrl}`], resolve)
data.formConf = res.data.flowFormInfo.propertyJson
if (data.opType != 1) data.readonly = true
data.formOperates = res.data.formOperates || []
if (data.opType == 0) {
for (let i = 0; i < data.formOperates.length; i++) {
data.formOperates[i].write = false
}
}
this.setting = data
}).catch(() => { this.loading = false })
},
setPageLoad(val) {
this.loading = !!val
},
setCandidateLoad(val) {
this.candidateLoading = !!val
},
setLoad(val) {
this.btnLoading = !!val
}
}
}
</script>
<style lang="scss" scoped>
.JNPF-dialog {
.main {
min-height: 50px;
}
}
</style>

@ -0,0 +1,222 @@
<template>
<el-dialog v-bind="$attrs" :close-on-click-modal="false" :modal-append-to-body="false"
append-to-body v-on="$listeners" @open="onOpen" @close="onClose"
class="JNPF-dialog JNPF-dialog_center" title="加签" width="600px">
<el-form ref="hasFreeApproverForm" :rules="rules" :model="hasFreeApproverForm"
label-width="100px">
<el-form-item label="加签人员" prop="freeApproverUserId">
<JnpfUserSelect v-model="hasFreeApproverForm.freeApproverUserId" placeholder="请选择加签人员" />
</el-form-item>
<el-form-item label="加签类型">
<el-radio-group v-model="hasFreeApproverForm.freeApproverType">
<el-radio-button :label="1">审批前</el-radio-button>
<el-radio-button :label="2">审批后</el-radio-button>
</el-radio-group>
<div v-if='hasFreeApproverForm.freeApproverType==1'>加签后流程先经过加签审批人再由我审批</div>
<div v-else></div>
</el-form-item>
<el-form-item label="分支选择" prop="branchList"
v-if="branchList.length &&hasFreeApproverForm.freeApproverType===2">
<el-select v-model="hasFreeApproverForm.branchList" multiple placeholder="请选择审批分支" clearable
@change="onBranchChange" filterable>
<el-option v-for="item in branchList" :key="item.nodeId" :label="item.nodeName"
:value="item.nodeId" />
</el-select>
</el-form-item>
<div v-if="hasFreeApproverForm.freeApproverType==2">
<el-form-item :label="item.nodeName+item.label" :prop="'candidateList.' + i + '.value'"
v-for="(item,i) in hasFreeApproverForm.candidateList" :key="i" :rules="item.rules">
<candidate-user-select v-model="item.value" multiple :placeholder="'请选择'+item.label"
:taskId="taskId" :formData="formData" :nodeId="item.nodeId" v-if="item.hasCandidates" />
<JnpfUserSelect v-model="item.value" multiple :placeholder="'请选择'+item.label" title="候选人员"
v-else />
</el-form-item>
</div>
<el-form-item label="加签原因" prop="handleOpinion" v-if="properties&&properties.hasOpinion">
<el-input v-model="hasFreeApproverForm.handleOpinion" placeholder="请输入加签原因" type="textarea"
:rows="4" />
</el-form-item>
<el-form-item label="加签附件" prop="fileList" v-if="properties&&properties.hasOpinion">
<JnpfUploadFile v-model="hasFreeApproverForm.fileList" :limit="3" />
</el-form-item>
<el-form-item label="手写签名" required v-if="properties&&properties.hasSign">
<div class="sign-main">
<img :src="signImg" alt="" v-if="signImg" class="sign-img">
<div @click="addSign" class="sign-style">
<i class="icon-ym icon-ym-signature add-sign"></i>
<span class="sign-title" v-if="!signImg"></span>
</div>
</div>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="close">{{formConf.cancelButtonText||' '}}</el-button>
<el-button type="primary" :loading="btnLoading" @click="dataFormSubmit()">
{{$t('common.confirmButton')}}</el-button>
</div>
<SignImgDialog v-if="signVisible" ref="SignImg" :lineWidth='3' :userInfo='userInfo'
:isDefault='1' @close="signDialog" />
</el-dialog>
</template>
<script>
import { Candidates, FreeApprover } from '@/api/workFlow/FlowBefore'
import SignImgDialog from '@/components/SignImgDialog'
import CandidateUserSelect from './CandidateUserSelect'
import { mapGetters } from "vuex"
export default {
components: { CandidateUserSelect, SignImgDialog },
props: ['taskId', 'formData', 'properties'],
data() {
return {
key: +new Date(),
formConf: {},
currentView: '',
hasFreeApproverForm: {
freeApproverUserId: "",
freeApproverType: 1,
handleOpinion: '',
fileList: [],
branchList: [],
candidateList: []
},
branchList: [],
candidateType: 1,
btnLoading: false,
freeApproverUserId: '',
rules: {
freeApproverUserId: [
{ required: true, message: '加签人员不能为空', trigger: 'change' }
],
branchList: [{ required: true, message: `选择分支不能为空`, trigger: 'click' }]
},
signImg: '',
signVisible: false,
}
},
computed: {
...mapGetters(['userInfo'])
},
methods: {
onOpen() {
this.signImg = ""
if (this.properties && this.properties.hasSign) this.signImg = this.userInfo.signImg
Candidates(this.taskId, this.formData).then(res => {
let data = res.data
this.candidateType = data.type
this.candidateLoading = false
this.branchList = []
if (data.type == 1) {
this.branchList = res.data.list.filter(o => o.isBranchFlow)
let list = res.data.list.filter(o => !o.isBranchFlow && o.isCandidates)
this.hasFreeApproverForm.candidateList = list.map(o => ({
...o,
isDefault: true,
label: '审批人',
value: [],
rules: [{ required: true, message: `审批人不能为空`, trigger: 'click' }]
}))
this.$nextTick(() => {
this.$refs['hasFreeApproverForm'].resetFields()
})
} else if (data.type == 2) {
let list = res.data.list.filter(o => o.isCandidates)
this.hasFreeApproverForm.candidateList = list.map(o => ({
...o,
label: '审批人',
value: [],
rules: [{ required: true, message: `审批人不能为空`, trigger: 'click' }]
}))
this.$nextTick(() => {
this.$refs['hasFreeApproverForm'].resetFields()
})
}
})
},
onClose() {
this.$emit('update:visible', false)
},
close(e) {
this.$emit('update:visible', false)
},
onBranchChange(val) {
const defaultList = this.hasFreeApproverForm.candidateList.filter(o => o.isDefault)
if (!val.length) return this.hasFreeApproverForm.candidateList = defaultList
let list = []
for (let i = 0; i < val.length; i++) {
inner: for (let j = 0; j < this.branchList.length; j++) {
let o = this.branchList[j]
if (val[i] === o.nodeId && o.isCandidates) {
list.push({
...o,
label: '审批人',
value: [],
rules: [{ required: true, message: `审批人不能为空`, trigger: 'click' }]
})
break inner
}
}
}
this.hasFreeApproverForm.candidateList = [...defaultList, ...list]
},
dataFormSubmit() {
this.$refs['hasFreeApproverForm'].validate((valid) => {
if (!valid) return
this.btnLoading = true
let query = {
handleOpinion: this.hasFreeApproverForm.handleOpinion,
fileList: this.hasFreeApproverForm.fileList,
freeApproverType: this.hasFreeApproverForm.freeApproverType,
freeApproverUserId: this.hasFreeApproverForm.freeApproverUserId,
branchList: this.hasFreeApproverForm.branchList,
formData: this.formData.formData
}
if (this.hasFreeApproverForm.candidateList.length) {
let candidateList = {}
for (let i = 0; i < this.hasFreeApproverForm.candidateList.length; i++) {
candidateList[this.hasFreeApproverForm.candidateList[i].nodeId] = this.hasFreeApproverForm.candidateList[i].value
}
query.candidateList = candidateList
}
if (this.signImg) {
query.signImg = this.signImg
}
FreeApprover(this.taskId, query).then(res => {
this.$message({
type: 'success',
message: res.msg,
duration: 1000,
onClose: () => {
this.btnLoading = false
this.$emit('close', true)
}
})
}).catch(() => { this.btnLoading = false })
})
},
addSign() {
this.signVisible = true
this.$nextTick(() => {
this.$refs.SignImg.init()
})
},
signDialog(val) {
this.signVisible = false
if (val) {
this.signImg = val
}
},
}
}
</script>
<style lang="scss" scoped>
>>> .el-dialog__body {
padding: 30px 20px;
color: #606266;
font-size: 14px;
word-break: break-all;
}
</style>

@ -0,0 +1,134 @@
<template>
<div class="JNPF-common-layout">
<JNPF-table :data="list" :hasNO="false" class="recordListTable" height="100%">
<el-table-column prop="nodeName" label="节点名称" show-overflow-tooltip width="200"
v-if="opType == 4">
<template slot-scope="scope">
<el-link type="primary" :underline="false" @click="handelNodeDetail(scope.row)">
{{scope.row.nodeName}}</el-link>
</template>
</el-table-column>
<el-table-column prop="nodeName" label="节点名称" width="200" v-else />
<el-table-column prop="userName" label="操作人员" width="150" />
<el-table-column prop="creatorTime" label="接收时间" width="150"
:formatter="jnpf.tableDateFormat" />
<el-table-column prop="handleTime" label="操作时间" width="150"
:formatter="jnpf.tableDateFormat" />
<el-table-column prop="handleStatus" label="执行动作" width="200" show-overflow-tooltip>
<template slot-scope="scope">
<div class="item">
<span :style="{background:colorList[scope.row.handleStatus||0]}"></span>
{{statusList[scope.row.handleStatus||0]}}
<div
v-if="scope.row.handleStatus==5||scope.row.handleStatus==6||scope.row.handleStatus==7||scope.row.handleStatus==10">
{{scope.row.operatorId}}
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="signImg" label="签名" width="120" align="center">
<template slot-scope="scope">
<el-image v-if="scope.row.signImg" style="width: 80px" :src="scope.row.signImg"
:preview-src-list="[scope.row.signImg]">
</el-image>
</template>
</el-table-column>
<el-table-column prop="fileList" label="附件" width="200" align="center">
<template slot-scope="scope">
<div v-for="(file,index) in JSON.parse(scope.row.fileList)" :key="index"
@click="handlePreview(file)">
<el-link type="primary" :underline="false">{{file.name}}</el-link>
</div>
</template>
</el-table-column>
<el-table-column prop="handleOpinion" label="备注" min-width="200" show-overflow-tooltip />
<el-table-column prop="handleOpinion" label="事件日志" width="100" v-if="opType==4">
<template slot-scope="scope">
<template v-if="scope.row.isLog">
<el-link type="primary" :underline="false" @click="handelNodeLog(scope.row)">
查看日志</el-link>
</template>
</template>
</el-table-column>
</JNPF-table>
<Preview :visible.sync="previewVisible" :file="activeFile" :showDownload="true" />
<FormBox v-if="formVisible" ref="FormBox" @close="formVisible = false" />
</div>
</template>
<script>
import Preview from '@/components/Jnpf/Upload/Preview'
import FormBox from '../components/FormBox'
export default {
components: { Preview, FormBox },
props: {
list: { type: Array, default: [] },
endTime: { type: Number, default: 0 },
flowId: { type: String, default: '' },
opType: { type: Number, default: 0 }
},
name: 'recordList',
data() {
return {
colorList: ['rgba(242,68,68,0.39)', 'rgba(35,162,5,0.39)', 'rgba(0,0,255,0.39)', 'rgba(21,21,157,0.39)', 'rgba(186,33,33,0.39)',
'rgba(25,185,185,0.39)', 'rgba(50,191,61,0.39)', 'rgba(49,151,214,0.39)', 'rgba(185,123,6,0.39)', 'rgba(45,94,186,0.39)', 'rgba(50,191,61,0.39)',
'rgba(255,0,0,0.39)', 'rgba(0,128,0,0.39)'],
statusList: ['退回', '同意', '发起', '撤回', '终止', '指派', '后加签', '转办', '变更', '复活', '前加签', '挂起', '恢复'],
previewVisible: false,
formVisible: false,
activeFile: {},
logVisible: false
}
},
methods: {
handlePreview(file) {
this.activeFile = file
this.previewVisible = true
},
handelNodeDetail(item) {
let data = {
id: item.taskId,
taskNodeId: item.taskNodeId,
enCode: item.flowCode,
flowId: this.flowId,
formType: item.formType,
opType: 0,
status: item.status,
title: item.nodeName
}
this.formVisible = true
this.$nextTick(() => {
this.$refs.FormBox.init(data)
})
},
handelNodeLog(item) {
this.$emit('handelNodeLog', item)
}
}
}
</script>
<style lang="scss" scoped>
.recordListTable {
.el-link {
font-size: 12px;
}
.item {
display: flex;
align-items: center;
width: 100%;
span {
width: 7px;
height: 7px;
margin-right: 6px;
margin-bottom: 1px;
border-radius: 50%;
flex-shrink: 0;
}
}
.signImg {
width: 80px;
cursor: pointer;
}
}
</style>

@ -0,0 +1,185 @@
<template>
<el-tabs tab-position="left" style="height: 100%;" v-model="activeTab" class="recordSummary">
<el-tab-pane label="按部门汇总" name="1"></el-tab-pane>
<el-tab-pane label="按岗位汇总" name="3"></el-tab-pane>
<div class="recordSummary-list" v-loading="loading">
<template v-if="list.length">
<el-card class="recordSummary-item" v-for="(item,i) in list" :key="i"
v-show="item.list&&item.list.length">
<div class="recordSummary-item-main">
<div class="cap">{{item.fullName}}意见</div>
<div class="content">
<div class="child-item" v-for="(child,j) in item.list" :key="j">
<div class="child-item-block">
<div class="avatar">
<el-avatar :src="define.comUrl+child.headIcon"></el-avatar>
</div>
<div class="child-item-title">
<div class="child-item-line">
<div class="name">
{{child.userName}}<span> {{child.handleTime | toDate()}}</span>
</div>
<el-tag :type="child.tagType" class="tag">{{child.txt}}</el-tag>
</div>
<div class="child-item-option"
v-if="child.handleOpinion && child.handleStatus!=2">
{{child.handleOpinion}}
</div>
<div class="file-List" v-if="(child.fileList.length && child.handleStatus!=2)">
<JnpfUploadFile v-model="child.fileList" detailed disabled :showIcon='false'>
</JnpfUploadFile>
</div>
</div>
</div>
</div>
</div>
</div>
</el-card>
</template>
<el-empty description="暂无数据" :image-size="120" v-else></el-empty>
</div>
</el-tabs>
</template>
<script>
import { getRecordList } from '@/api/workFlow/FlowBefore'
export default {
name: 'comments',
props: {
id: { type: String, default: '' },
summaryType: { default: '0' },
},
data() {
return {
list: [],
activeTab: '1',
loading: false
}
},
watch: {
activeTab(val) {
this.init()
}
},
methods: {
init() {
this.loading = true
const query = {
category: this.activeTab,
type: this.summaryType
}
getRecordList(this.id, query).then(res => {
this.list = res.data
if (this.list.length) {
this.list.forEach((o, i) => {
o.list.forEach(j => {
j.fileList = j.fileList ? JSON.parse(j.fileList) : []
})
o.list = o.list.map(i => ({
txt: i.handleStatus == 0 ? '退回' : i.handleStatus == 1 ? '同意' : i.handleStatus == 2 ? '发起' : i.handleStatus == 3 ? '撤回' : i.handleStatus == 4 ? '终止' : i.handleStatus == 5 ? '指派' : i.handleStatus == 6 ? '后加签' : i.handleStatus == 10 ? '前加签' : i.handleStatus == 8 ? '变更' : i.handleStatus == 11 ? '挂起' : i.handleStatus == 12 ? '恢复' : '转审',
tagType: i.handleStatus == 0 || i.handleStatus == 11 ? 'danger' : i.handleStatus == 1 || i.handleStatus == 12 ? 'success' : i.handleStatus == 3 || i.handleStatus == 4 || i.handleStatus == 8 ? 'warning' : "",
...i
}))
})
}
this.loading = false
}).catch(() => {
this.loading = false
})
}
}
}
</script>
<style lang="scss" scoped>
.recordSummary {
.el-tab-pane {
height: 0;
}
>>> .el-tabs__content {
height: 100%;
.recordSummary-list {
height: 100%;
overflow: hidden auto;
padding: 50px 100px 0;
.recordSummary-item {
margin-bottom: 20px;
.el-card__body {
padding: 0;
.recordSummary-item-main {
display: flex;
align-items: center;
.cap {
width: 150px;
flex-shrink: 0;
text-align: center;
font-size: 14px;
}
.content {
flex: 1;
padding: 0px 36px;
border-left: 1px solid #e4e7ed;
}
.child-item {
padding: 20px 0;
font-size: 14px;
line-height: 22px;
&:last-child {
margin-bottom: 0;
}
&:nth-child(2n) {
border-top: 1px solid #e1e5eb;
}
.child-item-block {
display: flex;
align-items: flex-start;
flex-direction: row;
.avatar {
width: 40px;
height: 40px;
}
// padding: 10px;
.child-item-title {
flex: 1;
margin-bottom: 2px;
display: flex;
justify-content: space-between;
flex-direction: column;
margin-left: 5px;
.child-item-line {
display: flex;
align-items: center;
flex-direction: row;
justify-content: space-between;
padding-left: 4px;
.name {
font-weight: 600;
span {
font-weight: 400;
font-size: 12px;
}
}
.tag {
float: right;
}
}
.child-item-option {
color: #747579;
padding-left: 4px;
}
.status {
flex-shrink: 0;
.el-link {
cursor: auto !important;
}
}
}
}
}
}
}
}
}
}
}
</style>

@ -0,0 +1,93 @@
<template>
<el-dialog title="挂起流程暂停" :close-on-click-modal="false" width='600px' :visible.sync="visible"
class="JNPF-dialog JNPF-dialog_center" lock-scroll append-to-body>
<el-form ref="dataForm" :model="dataForm" label-width="80px" :rules="fromRules">
<el-form-item label="挂起类型" prop="suspend">
<el-select v-model="dataForm.suspend" placeholder="挂起类型" filterable>
<el-option v-for="item in suspendOptions" :key="item.value" :label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="挂起原因" prop="handleOpinion">
<el-input v-model="dataForm.handleOpinion" placeholder="请输入挂起原因" type="textarea"
:rows="4" />
</el-form-item>
<el-form-item label="挂起附件" prop="fileList">
<JnpfUploadFile v-model="dataForm.fileList" :limit="3" />
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">{{$t('common.cancelButton')}}</el-button>
<el-button type="primary" @click="handleSure()" :loading="btnLoading">
{{$t('common.confirmButton')}}
</el-button>
</span>
</el-dialog>
</template>
<script>
import { suspendType } from '@/api/workFlow/FlowBefore'
export default {
components: {},
props: {
},
data() {
return {
visible: false,
eventType: '',
dataForm: {
suspend: false,
handleOpinion: '',
fileList: [],
},
signVisible: false,
fromRules: {
nodeCode: [
{ required: true, message: '请选择指派节点', trigger: 'change' }
]
},
signImg: '',
btnLoading: false,
title: '',
label: '',
suspendOptions: []
}
},
computed: {
},
methods: {
init(id) {
this.visible = true
this.dataForm.suspend = false
this.dataForm.handleOpinion = ''
this.dataForm.fileList = []
this.suspendOptions = []
this.$nextTick(() => {
this.$refs['dataForm'].resetFields()
suspendType(id).then(res => {
let listOptions = []
if (res.data) {
listOptions = [{ value: false, label: '全部流程挂起' }, { value: true, label: '主流程挂起' }]
} else {
listOptions = [{ value: false, label: '全部流程挂起' }]
}
this.suspendOptions = listOptions
})
})
},
handleSure() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
this.btnLoading = true
this.$emit('submit', this.dataForm)
}
})
},
closeDialog() {
this.btnLoading = false
this.visible = false
},
}
}
</script>

@ -609,7 +609,7 @@
</jnpf-form-tip-item> </jnpf-form-tip-item>
</el-col> </el-col>
<el-col :span="24" v-if="judgeShow('remark')"> <el-col :span="24" v-if="judgeShow('remark')">
<jnpf-form-tip-item label="备注" v-if="judgeShow('remark')" label-width="0px" prop="remark"> <jnpf-form-tip-item label="备注" v-if="judgeShow('remark')" prop="remark">
<JnpfTextarea v-model="dataForm.remark" @change="changeData('remark', -1)" placeholder="请输入" <JnpfTextarea v-model="dataForm.remark" @change="changeData('remark', -1)" placeholder="请输入"
:disabled="judgeWrite('remark')" :style='{ "width": "100%" }' true type="textarea" :disabled="judgeWrite('remark')" :style='{ "width": "100%" }' true type="textarea"
:autosize='{ "minRows": 4, "maxRows": 4 }'> :autosize='{ "minRows": 4, "maxRows": 4 }'>

@ -0,0 +1,383 @@
<template>
<div class="JNPF-common-layout">
<div class="JNPF-common-layout-center">
<el-row class="JNPF-common-search-box" :gutter="16">
<el-form @submit.native.prevent>
<el-col :span="6">
<el-form-item label="关键词">
<el-input v-model="keyword" placeholder="请输入关键词查询" clearable
@keyup.enter.native="search()" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="日期">
<el-date-picker v-model="pickerVal" type="daterange" start-placeholder=""
end-placeholder="结束日期" :picker-options="pickerOptions" value-format="timestamp"
clearable :editable="false">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="分类">
<el-select v-model="flowCategory" placeholder="选择分类" clearable filterable>
<el-option v-for="item in categoryList" :key="item.id" :label="item.fullName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<template v-if="showAll">
<el-col :span="6">
<el-form-item label="所属流程">
<el-select v-model="templateId" placeholder="选择所属流程" clearable
@change="onTemplateIdChange" filterable>
<el-option-group v-for="group in flowEngineList" :key="group.id"
:label="group.fullName+'【'+group.num+'】'">
<el-option v-for="item in group.children" :key="item.id" :label="item.fullName"
:value="item.id">
</el-option>
</el-option-group>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="所属名称">
<el-select v-model="flowId" placeholder="选择所属名称" clearable
@visible-change="visibleFlowChange" filterable>
<el-option v-for="item in flowOptions" :key="item.id" :label="item.fullName"
:value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="发起人员">
<JnpfUserSelect v-model="creatorUserId" placeholder="选择发起人员" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="流程状态">
<el-select v-model="status" placeholder="选择流程状态" clearable filterable>
<el-option v-for="(item,i) in statusList" :key="i" :label="item.fullName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="紧急程度">
<el-select v-model="urgent" placeholder="选择紧急程度" clearable filterable>
<el-option v-for="(item,i) in urgentList" :key="i" :label="item.fullName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
</template>
<el-col :span="6">
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="search()">
{{$t('common.search')}}</el-button>
<el-button icon="el-icon-refresh-right" @click="refresh()">{{$t('common.reset')}}
</el-button>
<el-button type="text" icon="el-icon-arrow-down" @click="showAll=true"
v-if="!showAll">展开</el-button>
<el-button type="text" icon="el-icon-arrow-up" @click="showAll=false" v-else>
收起</el-button>
</el-form-item>
</el-col>
</el-form>
</el-row>
<div class="JNPF-common-layout-main JNPF-flex-main">
<div class="JNPF-common-head">
<div>
<el-button type="danger" @click="handleDel" icon="el-icon-delete">删除</el-button>
</div>
<div class="JNPF-common-head-right">
<el-tooltip effect="dark" :content="$t('common.refresh')" placement="top">
<el-link icon="icon-ym icon-ym-Refresh JNPF-common-head-icon" :underline="false"
@click="initData()" />
</el-tooltip>
</div>
</div>
<JNPF-table v-loading="listLoading" :data="list" hasC @selection-change="handleChange">
<el-table-column prop="fullName" label="流程标题" show-overflow-tooltip min-width="150" />
<el-table-column prop="flowName" label="所属流程" width="130" />
<el-table-column prop="startTime" label="发起时间" width="130"
:formatter="jnpf.tableDateFormat" />
<el-table-column prop="userName" label="发起人员" width="130" />
<el-table-column prop="thisStep" label="审批节点" width="130" />
<el-table-column prop="flowUrgent" label="紧急程度" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.flowUrgent | urgentText() }}
</template>
</el-table-column>
<el-table-column prop="flowVersion" label="版本号" width="70" align="center">
<template slot-scope="scope">
<el-tag type="primary">V:{{scope.row.flowVersion}}</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="流程状态" width="130" align="center">
<template slot-scope="scope">
<el-tag type="primary" v-if="scope.row.status==1"></el-tag>
<el-tag type="success" v-else-if="scope.row.status==2">审核通过</el-tag>
<el-tag type="danger" v-else-if="scope.row.status==3">审核退回</el-tag>
<el-tag type="info" v-else-if="scope.row.status==4">流程撤回</el-tag>
<el-tag type="info" v-else-if="scope.row.status==5">审核终止</el-tag>
<el-tag type="danger" v-else-if="scope.row.status==6">已被挂起</el-tag>
<el-tag v-else type="warning">等待提交</el-tag>
</template>
</el-table-column>
<el-table-column prop="completion" label="流程进度" width="150">
<template slot-scope="scope">
<p class="text-grey" v-if="scope.row.status==5 || scope.row.completion == 0">----</p>
<p v-else-if=" scope.row.completion == 100">已完成</p>
<el-progress :percentage="scope.row.completion" v-else></el-progress>
</template>
</el-table-column>
<el-table-column label="操作" width="50" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" @click="toDetail(scope.row)"></el-button>
</template>
</el-table-column>
</JNPF-table>
<pagination :total="total" :page.sync="listQuery.currentPage"
:limit.sync="listQuery.pageSize" @pagination="initData" />
</div>
</div>
<FlowBox v-if="formVisible" ref="FlowBox" @close="closeForm" />
</div>
</template>
<script>
import { FlowMonitorList, DeleteList } from '@/api/workFlow/FlowMonitor'
import { FlowEngineListAll, getFlowList } from '@/api/workFlow/FlowEngine'
import FlowBox from '../components/FlowBox'
export default {
name: 'workFlow-flowMonitor',
components: { FlowBox },
data() {
return {
list: [],
total: 0,
showAll: false,
listLoading: true,
listQuery: {
currentPage: 1,
pageSize: 20,
sort: 'desc',
sidx: ''
},
formVisible: false,
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
statusList: [{
id: 0,
fullName: '等待提交'
}, {
id: 1,
fullName: '等待审核'
}, {
id: 2,
fullName: '审核通过'
}, {
id: 3,
fullName: '审核退回'
}, {
id: 4,
fullName: '流程撤回'
}, {
id: 5,
fullName: '审核终止'
}, {
id: 6,
fullName: '已被挂起'
}],
urgent: '',
urgentList: [
{
id: 1,
fullName: '普通'
}, {
id: 2,
fullName: '重要'
}, {
id: 3,
fullName: '紧急'
}
],
keyword: '',
pickerVal: [],
startTime: '',
endTime: '',
templateId: '',
flowId: '',
status: '',
flowCategory: '',
creatorUserId: '',
categoryList: [],
flowEngineList: [],
multipleSelection: [],
flowOptions: []
}
},
filters: {
getCategoryText(id, categoryList) {
let item = categoryList.filter(o => o.enCode == id)[0]
return item && item.fullName ? item.fullName : ''
}
},
created() {
this.getDictionaryData()
this.getFlowEngineList()
this.initData()
},
methods: {
search() {
if (this.pickerVal && this.pickerVal.length) {
this.startTime = this.pickerVal[0]
this.endTime = this.pickerVal[1]
} else {
this.startTime = ''
this.endTime = ''
}
this.listQuery = {
currentPage: 1,
pageSize: 20,
sort: 'desc',
sidx: ''
}
this.initData()
},
getFlowEngineList() {
FlowEngineListAll().then((res) => {
this.flowEngineList = res.data.list
})
},
getDictionaryData() {
this.$store.dispatch('base/getDictionaryData', { sort: 'WorkFlowCategory' }).then((res) => {
this.categoryList = res
})
},
initData() {
this.listLoading = true
let query = {
...this.listQuery,
keyword: this.keyword,
startTime: this.startTime,
endTime: this.endTime,
templateId: this.templateId,
flowId: this.flowId,
status: this.status,
flowUrgent: this.urgent,
flowCategory: this.flowCategory,
creatorUserId: this.creatorUserId
}
FlowMonitorList(query).then(async res => {
this.list = res.data.list
this.total = res.data.pagination.total
this.listLoading = false
})
},
toDetail(item) {
let data = {
flowId: item.flowId,
id: item.processId,
taskId: item.id,
status: item.status,
opType: 4,
hasCancel: true,
readonly: true
}
this.formVisible = true
this.$nextTick(() => {
this.$refs.FlowBox.init(data)
})
},
handleChange(val) {
this.multipleSelection = val.map(item => item.id)
},
handleDel() {
if (!this.multipleSelection.length) {
this.$message({ type: 'error', message: '请选择一条数据' });
return
}
let data = { ids: this.multipleSelection.join(',') }
this.$confirm('您确定要删除这些数据吗, 是否继续?', '提示', {
type: 'warning'
}).then(() => {
DeleteList(data).then(res => {
this.$message({
type: 'success',
message: res.msg
});
this.refresh();
})
}).catch(() => { });
},
closeForm(isRefresh) {
this.formVisible = false
if (isRefresh) this.refresh()
},
refresh() {
this.pickerVal = ''
this.startTime = ''
this.endTime = ''
this.keyword = ''
this.templateId = ''
this.flowId = ''
this.status = ''
this.urgent = ''
this.flowCategory = ''
this.creatorUserId = ''
this.flowOptions = []
this.listQuery = {
currentPage: 1,
pageSize: 20,
sort: 'desc',
sidx: ''
}
this.initData()
},
onTemplateIdChange(val) {
this.flowId = ''
this.flowOptions = []
if (!val) return
this.getFlowList()
},
getFlowList() {
getFlowList(this.templateId).then(res => {
this.flowOptions = res.data
})
},
visibleFlowChange(val) {
if (!val) return
if (!this.templateId) this.$message.warning('请先选择所属流程')
},
}
}
</script>

@ -0,0 +1,383 @@
<template>
<div class="JNPF-common-layout">
<div class="JNPF-common-layout-center">
<el-row class="JNPF-common-search-box" :gutter="16">
<el-form @submit.native.prevent>
<el-col :span="6">
<el-form-item label="关键词">
<el-input v-model="keyword" placeholder="请输入关键词查询" clearable
@keyup.enter.native="search()" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="日期">
<el-date-picker v-model="pickerVal" type="daterange" start-placeholder=""
end-placeholder="结束日期" :picker-options="pickerOptions" value-format="timestamp"
clearable :editable="false">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="分类">
<el-select v-model="flowCategory" placeholder="选择分类" clearable filterable>
<el-option v-for="item in categoryList" :key="item.id" :label="item.fullName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<template v-if="showAll">
<el-col :span="6">
<el-form-item label="所属流程">
<el-select v-model="templateId" placeholder="选择所属流程" clearable
@change="onTemplateIdChange" filterable>
<el-option-group v-for="group in flowEngineList" :key="group.id"
:label="group.fullName+'【'+group.num+'】'">
<el-option v-for="item in group.children" :key="item.id" :label="item.fullName"
:value="item.id">
</el-option>
</el-option-group>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="所属名称">
<el-select v-model="flowId" placeholder="选择所属名称" clearable
@visible-change="visibleFlowChange" filterable>
<el-option v-for="item in flowOptions" :key="item.id" :label="item.fullName"
:value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="发起人员">
<JnpfUserSelect v-model="creatorUserId" placeholder="选择发起人员" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="流程状态">
<el-select v-model="status" placeholder="选择流程状态" clearable filterable>
<el-option v-for="(item,i) in statusList" :key="i" :label="item.fullName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="紧急程度">
<el-select v-model="urgent" placeholder="选择紧急程度" clearable filterable>
<el-option v-for="(item,i) in urgentList" :key="i" :label="item.fullName"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
</template>
<el-col :span="6">
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="search()">
{{$t('common.search')}}</el-button>
<el-button icon="el-icon-refresh-right" @click="refresh()">{{$t('common.reset')}}
</el-button>
<el-button type="text" icon="el-icon-arrow-down" @click="showAll=true"
v-if="!showAll">展开</el-button>
<el-button type="text" icon="el-icon-arrow-up" @click="showAll=false" v-else>
收起</el-button>
</el-form-item>
</el-col>
</el-form>
</el-row>
<div class="JNPF-common-layout-main JNPF-flex-main">
<div class="JNPF-common-head">
<div>
<el-button type="danger" @click="handleDel" icon="el-icon-delete">删除</el-button>
</div>
<div class="JNPF-common-head-right">
<el-tooltip effect="dark" :content="$t('common.refresh')" placement="top">
<el-link icon="icon-ym icon-ym-Refresh JNPF-common-head-icon" :underline="false"
@click="initData()" />
</el-tooltip>
</div>
</div>
<JNPF-table v-loading="listLoading" :data="list" hasC @selection-change="handleChange">
<el-table-column prop="fullName" label="流程标题" show-overflow-tooltip min-width="150" />
<el-table-column prop="flowName" label="所属流程" width="130" />
<el-table-column prop="startTime" label="发起时间" width="130"
:formatter="jnpf.tableDateFormat" />
<el-table-column prop="userName" label="发起人员" width="130" />
<el-table-column prop="thisStep" label="审批节点" width="130" />
<el-table-column prop="flowUrgent" label="紧急程度" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.flowUrgent | urgentText() }}
</template>
</el-table-column>
<el-table-column prop="flowVersion" label="版本号" width="70" align="center">
<template slot-scope="scope">
<el-tag type="primary">V:{{scope.row.flowVersion}}</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="流程状态" width="130" align="center">
<template slot-scope="scope">
<el-tag type="primary" v-if="scope.row.status==1"></el-tag>
<el-tag type="success" v-else-if="scope.row.status==2">审核通过</el-tag>
<el-tag type="danger" v-else-if="scope.row.status==3">审核退回</el-tag>
<el-tag type="info" v-else-if="scope.row.status==4">流程撤回</el-tag>
<el-tag type="info" v-else-if="scope.row.status==5">审核终止</el-tag>
<el-tag type="danger" v-else-if="scope.row.status==6">已被挂起</el-tag>
<el-tag v-else type="warning">等待提交</el-tag>
</template>
</el-table-column>
<el-table-column prop="completion" label="流程进度" width="150">
<template slot-scope="scope">
<p class="text-grey" v-if="scope.row.status==5 || scope.row.completion == 0">----</p>
<p v-else-if=" scope.row.completion == 100">已完成</p>
<el-progress :percentage="scope.row.completion" v-else></el-progress>
</template>
</el-table-column>
<el-table-column label="操作" width="50" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" @click="toDetail(scope.row)"></el-button>
</template>
</el-table-column>
</JNPF-table>
<pagination :total="total" :page.sync="listQuery.currentPage"
:limit.sync="listQuery.pageSize" @pagination="initData" />
</div>
</div>
<FlowBox v-if="formVisible" ref="FlowBox" @close="closeForm" />
</div>
</template>
<script>
import { FlowMonitorList, DeleteList } from '@/api/workFlow/FlowMonitor'
import { FlowEngineListAll, getFlowList } from '@/api/workFlow/FlowEngine'
import FlowBox from '../components/FlowBox'
export default {
name: 'workFlow-flowMonitor',
components: { FlowBox },
data() {
return {
list: [],
total: 0,
showAll: false,
listLoading: true,
listQuery: {
currentPage: 1,
pageSize: 20,
sort: 'desc',
sidx: ''
},
formVisible: false,
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
picker.$emit('pick', [start, end]);
}
}]
},
statusList: [{
id: 0,
fullName: '等待提交'
}, {
id: 1,
fullName: '等待审核'
}, {
id: 2,
fullName: '审核通过'
}, {
id: 3,
fullName: '审核退回'
}, {
id: 4,
fullName: '流程撤回'
}, {
id: 5,
fullName: '审核终止'
}, {
id: 6,
fullName: '已被挂起'
}],
urgent: '',
urgentList: [
{
id: 1,
fullName: '普通'
}, {
id: 2,
fullName: '重要'
}, {
id: 3,
fullName: '紧急'
}
],
keyword: '',
pickerVal: [],
startTime: '',
endTime: '',
templateId: '',
flowId: '',
status: '',
flowCategory: '',
creatorUserId: '',
categoryList: [],
flowEngineList: [],
multipleSelection: [],
flowOptions: []
}
},
filters: {
getCategoryText(id, categoryList) {
let item = categoryList.filter(o => o.enCode == id)[0]
return item && item.fullName ? item.fullName : ''
}
},
created() {
this.getDictionaryData()
this.getFlowEngineList()
this.initData()
},
methods: {
search() {
if (this.pickerVal && this.pickerVal.length) {
this.startTime = this.pickerVal[0]
this.endTime = this.pickerVal[1]
} else {
this.startTime = ''
this.endTime = ''
}
this.listQuery = {
currentPage: 1,
pageSize: 20,
sort: 'desc',
sidx: ''
}
this.initData()
},
getFlowEngineList() {
FlowEngineListAll().then((res) => {
this.flowEngineList = res.data.list
})
},
getDictionaryData() {
this.$store.dispatch('base/getDictionaryData', { sort: 'WorkFlowCategory' }).then((res) => {
this.categoryList = res
})
},
initData() {
this.listLoading = true
let query = {
...this.listQuery,
keyword: this.keyword,
startTime: this.startTime,
endTime: this.endTime,
templateId: this.templateId,
flowId: this.flowId,
status: this.status,
flowUrgent: this.urgent,
flowCategory: this.flowCategory,
creatorUserId: this.creatorUserId
}
FlowMonitorList(query).then(async res => {
this.list = res.data.list
this.total = res.data.pagination.total
this.listLoading = false
})
},
toDetail(item) {
let data = {
flowId: item.flowId,
id: item.processId,
taskId: item.id,
status: item.status,
opType: 4,
hasCancel: true,
readonly: true
}
this.formVisible = true
this.$nextTick(() => {
this.$refs.FlowBox.init(data)
})
},
handleChange(val) {
this.multipleSelection = val.map(item => item.id)
},
handleDel() {
if (!this.multipleSelection.length) {
this.$message({ type: 'error', message: '请选择一条数据' });
return
}
let data = { ids: this.multipleSelection.join(',') }
this.$confirm('您确定要删除这些数据吗, 是否继续?', '提示', {
type: 'warning'
}).then(() => {
DeleteList(data).then(res => {
this.$message({
type: 'success',
message: res.msg
});
this.refresh();
})
}).catch(() => { });
},
closeForm(isRefresh) {
this.formVisible = false
if (isRefresh) this.refresh()
},
refresh() {
this.pickerVal = ''
this.startTime = ''
this.endTime = ''
this.keyword = ''
this.templateId = ''
this.flowId = ''
this.status = ''
this.urgent = ''
this.flowCategory = ''
this.creatorUserId = ''
this.flowOptions = []
this.listQuery = {
currentPage: 1,
pageSize: 20,
sort: 'desc',
sidx: ''
}
this.initData()
},
onTemplateIdChange(val) {
this.flowId = ''
this.flowOptions = []
if (!val) return
this.getFlowList()
},
getFlowList() {
getFlowList(this.templateId).then(res => {
this.flowOptions = res.data
})
},
visibleFlowChange(val) {
if (!val) return
if (!this.templateId) this.$message.warning('请先选择所属流程')
},
}
}
</script>
Loading…
Cancel
Save