feat:工作流前端进度 - 95%

This commit is contained in:
weifs 2023-04-19 17:19:28 +08:00
parent 596594d8b3
commit 09f48c32c7
10 changed files with 238 additions and 53 deletions

View File

@ -2,16 +2,16 @@
namespace App\Http\Controllers\Api;
use Cache;
use Request;
use Carbon\Carbon;
use App\Models\User;
use App\Module\Base;
use App\Module\Ihttp;
use App\Tasks\PushTask;
use App\Models\WebSocketDialog;
use App\Models\WorkflowProcMsg;
use App\Exceptions\ApiException;
use App\Models\WebSocketDialogMsg;
use Hhxsv5\LaravelS\Swoole\Task\Task;
/**
* @apiDefine workflow
@ -47,6 +47,28 @@ class WorkflowController extends AbstractController
}
}
/**
* @api {get} api/workflow/user/department 02. 获取当前用户部门
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup workflow
* @apiName user__department
*
* @apiQuery {Number} id 流程ID
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function user__department()
{
// User::auth();
// $data['id'] = intval(Request::input('id'));
// $workflow = $this->getProcessById($data['id']);
// return Base::retSuccess('success', $workflow);
}
/**
* @api {post} api/workflow/procdef/all 02. 查询流程定义
*
@ -211,7 +233,22 @@ class WorkflowController extends AbstractController
if($process['is_finished'] == true) {
$dialog = WebSocketDialog::checkUserDialog($botUser, $process['start_user_id']);
$this->workflowMsg('workflow_submitter', $dialog, $botUser, ['userid' => $data['userid']], $process, $pass);
}else if ($process['candidate']) {
// 下个审批人
$userid = explode(',', $process['candidate']);
$toUser = User::whereIn('userid', $userid)->get()->toArray();
foreach ($toUser as $val) {
if ($val['bot']) {
continue;
}
$dialog = WebSocketDialog::checkUserDialog($botUser, $val['userid']);
if (empty($dialog)) {
continue;
}
$this->workflowMsg('workflow_reviewer', $dialog, $botUser, $val, $process,'start');
}
}
// 抄送人
$notifier = $this->handleProcessNode($process, $task['step']);
if ($notifier && $pass == 'pass') {
@ -686,6 +723,20 @@ class WorkflowController extends AbstractController
$proc_msg->userid = $toUser['userid'];
$proc_msg->save();
}
// 更新工作报告 未读数量
if($type == 'workflow_reviewer' && $toUser['userid']){
$params = [
'userid' => [ $toUser['userid'], User::auth()->userid() ],
'msg' => [
'type' => 'workflow',
'action' => 'backlog',
'userid' => $toUser['userid'],
]
];
Task::deliver(new PushTask($params, false));
}
return true;
}
@ -710,6 +761,9 @@ class WorkflowController extends AbstractController
}
$val['node_user_list'][$k]['userimg'] = User::getAvatar($info->userid, $info->userimg, $info->email, $info->nickname);
}
}else if($val['aprover_id']){
$info = User::whereUserid($val['aprover_id'])->first();
$val['userimg'] = $info ? User::getAvatar($info->userid, $info->userimg, $info->email, $info->nickname) : '';
}
}
$info = User::whereUserid($res['start_user_id'])->first();

View File

@ -11,7 +11,7 @@
<UserAvatar :userid="userId" :size="36" tooltipDisabled/>
</div>
<span>{{userInfo.nickname}}</span>
<Badge v-if="reportUnreadNumber > 0" class="manage-box-top-report" :overflow-count="999" :count="reportUnreadNumber"/>
<Badge v-if="(reportUnreadNumber + backlogUnreadNumber) > 0" class="manage-box-top-report" :overflow-count="999" :count="reportUnreadNumber + backlogUnreadNumber"/>
<Badge v-else-if="!!clientNewVersion" class="manage-box-top-report" dot/>
<div class="manage-box-arrow">
<Icon type="ios-arrow-up" />
@ -90,6 +90,10 @@
v-else-if="item.path === 'workReport' && reportUnreadNumber > 0"
class="manage-menu-report-badge"
:count="reportUnreadNumber"/>
<Badge
v-else-if="item.path === 'review' && backlogUnreadNumber > 0"
class="manage-menu-report-badge"
:count="backlogUnreadNumber"/>
</div>
</DropdownItem>
</template>
@ -396,6 +400,7 @@ export default {
this.$store.dispatch("getUserInfo").catch(_ => {})
this.$store.dispatch("getTaskPriority").catch(_ => {})
this.$store.dispatch("getReportUnread", 0)
this.$store.dispatch("getBacklogUnread", 0)
//
this.$store.dispatch("needHome").then(_ => {
this.needStartHome = true
@ -441,6 +446,7 @@ export default {
'dialogIns',
'reportUnreadNumber',
'backlogUnreadNumber',
]),
...mapGetters(['dashboardTask']),
@ -686,6 +692,11 @@ export default {
this.$store.dispatch("getReportUnread", 1000)
}
break;
case 'workflow':
if (action == 'backlog') {
this.$store.dispatch("getBacklogUnread", 1000)
}
break;
}
},
deep: true,

View File

@ -2374,7 +2374,7 @@ export default {
let domAudits = $(target).parents(".open-review-details")
if( domAudits.length > 0 ){
let dataId = domAudits[0].getAttribute("data-id")
if( window.innerWidth < 425 ){
if( window.innerWidth < 426 ){
this.goForward({name: 'manage-review-details', query: { id: domAudits[0].getAttribute("data-id") } });
}else{
this.approveDetailsShow = true;

View File

@ -45,7 +45,7 @@
<p class="review-process-state">{{$L('已提交')}}</p>
</div>
<div class="review-process-right">
<p>{{ getTimeAgo(item.claim_time) }}</p>
<p v-if="parseInt(getTimeAgo(item.claim_time)) < showTimeNum">{{ getTimeAgo(item.claim_time) }}</p>
<p>{{item.claim_time?.substr(0,16)}}</p>
</div>
</div>
@ -56,7 +56,7 @@
>
<p class="timeline-title">{{$L('审批')}}</p>
<div style="display: flex;">
<Avatar :src="item.node_user_list && item.node_user_list[0]?.userimg" size="38"/>
<Avatar :src="(item.node_user_list && item.node_user_list[0]?.userimg) || item.userimg" size="38"/>
<div style="margin-left: 10px;flex: 1;">
<p class="review-process-name">{{item.approver}}</p>
<p class="review-process-state" style="color: #6d6d6d;" v-if="!item.identitylink">待审批</p>
@ -68,7 +68,7 @@
</p>
</div>
<div class="review-process-right">
<p>
<p v-if="parseInt(getTimeAgo(item.claim_time)) < showTimeNum">
{{ item.identitylink?.state==0 ?
($L('已等待') + " " + getTimeAgo( datas.node_infos[key-1].claim_time,2)) :
(item.claim_time ? getTimeAgo(item.claim_time) : '')
@ -77,6 +77,7 @@
<p>{{item.claim_time?.substr(0,16)}}</p>
</div>
</div>
<p class="comment" v-if="item.identitylink?.state==2">{{ datas.latest_comment }}</p>
</TimelineItem>
<!-- 抄送 -->
<TimelineItem v-for="(item,key) in datas.node_infos" :key="key" :color="item.is_finished ? 'green' : '#ccc'" v-if="item.type == 'notifier' && item._show">
@ -111,7 +112,7 @@
<div style="flex: 1;"></div>
<Button type="success" v-if="(datas.candidate || '').split(',').indexOf(userId + '') != -1" @click="approve(1)">{{$L('同意')}}</Button>
<Button type="error" v-if="(datas.candidate || '').split(',').indexOf(userId + '') != -1" @click="approve(2)">{{$L('拒绝')}}</Button>
<Button type="warning" v-if="userId == datas.start_user_id" @click="revocation">{{$L('撤销')}}</Button>
<Button type="warning" v-if="isShowWarningBtn" @click="revocation">{{$L('撤销')}}</Button>
</div>
</div>
</template>
@ -131,9 +132,8 @@ export default {
data() {
return {
modalTransferIndex:window.modalTransferIndex,
datas:{
}
datas:{},
showTimeNum:24
}
},
watch: {
@ -144,7 +144,6 @@ export default {
},
data: {
handler(newValue,oldValue) {
console.log(newValue)
if(newValue.id){
this.getInfo()
}
@ -152,6 +151,17 @@ export default {
deep: true
},
},
computed: {
isShowWarningBtn(){
let is = this.userId == this.datas.start_user_id;
(this.datas.node_infos || []).map(h=>{
if(h.type != 'starter' && h.is_finished == true) {
is = false;
}
})
return is;
},
},
mounted() {
this.init()
},
@ -230,7 +240,7 @@ export default {
type:"textarea",
okText: type == 1 ? "同意" : "拒绝",
onOk: (desc) => {
if (!desc) {
if (type !=1 && !desc) {
return `请输入审批意见`
}
this.$store.dispatch("call", {
@ -269,7 +279,7 @@ export default {
this.$emit('revocation')
}).catch(({msg}) => {
$A.modalError(msg);
reject(msg);
resolve();
});
return false
})

View File

@ -7,10 +7,10 @@
<div class="review-nav">
<h1>{{$L('审批中心')}}</h1>
</div>
<Button v-for="item in procdefList" :loading="loadIng > 0" type="primary" @click="initiate(item)" style="margin-right:10px;">{{item.name}}</Button>
<Button v-for="(item,key) in procdefList" :loading="loadIng > 0" :key="key" type="primary" @click="initiate(item)" style="margin-right:10px;">{{item.name}}</Button>
</div>
<Tabs :value="tabsValue" @on-click="tabsClick" style="margin: 0 20px;height: 100%;">
<Tabs :value="tabsValue" @on-click="tabsClick" style="margin: 0 20px;height: 100%;" size="small">
<TabPane :label="$L('待办') + (backlogTotal > 0 ? ('('+backlogTotal+')') : '')" name="backlog" style="height: 100%;">
<div class="review-main-search">
<div style="display: flex;gap: 10px;">
@ -29,7 +29,7 @@
</div>
</div>
<div class="review-main-right">
<listDetails v-if="!detailsShow" :data="details" @approve="tabsClick" @revocation="tabsClick"></listDetails>
<listDetails v-if="!detailsShow && tabsValue=='backlog'" :data="details" @approve="tabsClick" @revocation="tabsClick"></listDetails>
</div>
</div>
</TabPane>
@ -51,7 +51,7 @@
</div>
</div>
<div class="review-main-right">
<listDetails v-if="!detailsShow" :data="details" @approve="tabsClick" @revocation="tabsClick"></listDetails>
<listDetails v-if="!detailsShow && tabsValue=='done'" :data="details" @approve="tabsClick" @revocation="tabsClick"></listDetails>
</div>
</div>
</TabPane>
@ -75,7 +75,7 @@
</div>
</div>
<div class="review-main-right">
<listDetails v-if="!detailsShow" :data="details" @approve="tabsClick" @revocation="tabsClick"></listDetails>
<listDetails v-if="!detailsShow && tabsValue=='notify'" :data="details" @approve="tabsClick" @revocation="tabsClick"></listDetails>
</div>
</div>
</TabPane>
@ -100,7 +100,7 @@
</div>
</div>
<div class="review-main-right">
<listDetails v-if="!detailsShow" :data="details" @approve="tabsClick" @revocation="tabsClick"></listDetails>
<listDetails v-if="!detailsShow && tabsValue=='initiated'" :data="details" @approve="tabsClick" @revocation="tabsClick"></listDetails>
</div>
</div>
</TabPane>
@ -116,8 +116,13 @@
<!--发起-->
<Modal v-model="addShow" :title="$L(addTitle)" :mask-closable="false">
<Form ref="initiateRef" :model="addData" :rules="addRule" label-width="auto" @submit.native.prevent>
<FormItem v-if="departmentList.length>1" prop="department_id" :label="$L('选择部门')">
<Select v-model="addData.department_id" :placeholder="$L('请选择部门')">
<Option v-for="(item, index) in departmentList" :value="item.id" :key="index">{{ item.name }}</Option>
</Select>
</FormItem>
<FormItem v-if="(addTitle || '').indexOf('班') == -1" prop="type" :label="$L('假期类型')">
<Select v-model="addData.type" :placeholder="$L('请选择')">
<Select v-model="addData.type" :placeholder="$L('请选择假期类型')">
<Option v-for="(item, index) in selectTypes" :value="item" :key="index">{{ item }}</Option>
</Select>
</FormItem>
@ -146,6 +151,7 @@
<Button type="primary" :loading="loadIng > 0" @click="onInitiate">{{$L('确认')}}</Button>
</div>
</Modal>
</div>
</template>
@ -153,6 +159,7 @@
import list from "./list.vue";
import listDetails from "./details.vue";
import DrawerOverlay from "../../../components/DrawerOverlay";
import { mapState } from 'vuex'
export default {
components:{list,listDetails,DrawerOverlay},
name: "review",
@ -195,29 +202,61 @@ export default {
addTitle:'',
addShow:false,
addData: {
department_id:0,
type: '',
startTime:"",
endTime:"",
},
addRule: {
department_id:{ type: 'number',required: true, message: this.$L('请选择部门!'), trigger: 'change' },
type: { type: 'string',required: true, message: this.$L('请选择假期类型!'), trigger: 'change' },
startTime: { type: 'string',required: true, message: this.$L('请选择开始时间!'), trigger: 'change' },
endTime:{ type: 'string',required: true, message: this.$L('请选择结束时间!'), trigger: 'change' },
description:{ type: 'string',required: true, message: this.$L('请选择结束时间!'), trigger: 'change' },
},
selectTypes:["年假","事假","病假","调休","产假","陪产假","婚假","丧假","哺乳假"]
selectTypes:["年假","事假","病假","调休","产假","陪产假","婚假","丧假","哺乳假"],
}
},
computed: {
...mapState([ 'wsMsg','userInfo','userIsAdmin' ]),
departmentList(){
let departmentNames = (this.userInfo.department_name || '').split(',');
return (this.userInfo.department || []).map((h,index)=>{
return {
id:h,
name:departmentNames[index]
};
})
}
},
watch: {
wsMsg: {
handler(info) {
const {type, action} = info;
switch (type) {
case 'workflow':
if (action == 'backlog') {
this.tabsClick()
}
break;
}
},
deep: true,
},
},
mounted() {
this.tabsValue = "initiated"
this.tabsValue = "backlog"
this.tabsClick()
this.getProcdef()
this.getBacklogList()
this.addData.department_id = this.userInfo.department[0] || 0;
},
methods:{
// tab
tabsClick(val){
this.__tabsClick && clearTimeout(this.__tabsClick)
this.__tabsClick = setTimeout(() => {
this.tabsValue = val || this.tabsValue
if(val!=""){
this.approvalType = this.searchState = "all"
@ -234,6 +273,7 @@ export default {
if(this.tabsValue == 'initiated'){
this.getInitiatedList();
}
}, 200)
},
//
@ -244,7 +284,7 @@ export default {
this.initiatedList.map(h=>{ h._active = false; })
item._active = true;
//
if( window.innerWidth < 425 ){
if( window.innerWidth < 426 ){
this.goForward({name: 'manage-review-details', query: { id: item.id } });
return;
}
@ -398,7 +438,7 @@ export default {
url: 'workflow/process/start',
data: {
proc_name:this.addTitle,
department_id:1,
department_id:this.addData.department_id,
var: JSON.stringify(this.addData)
},
method: 'post',
@ -421,8 +461,21 @@ export default {
}
</script>
<style scoped>
.review-details{
<style lang="scss">
.page-review .review-details{
border-radius: 8px;
}
.page-review .ivu-tabs-nav {
display: flex;
width: 350px;
@media (max-width: 1010px) {
width: 100%;
}
.ivu-tabs-tab{
font-size: 15px;
flex:1;
text-align: center;
}
}
</style>

View File

@ -457,6 +457,7 @@ export default {
dispatch("getProjects").catch(() => {});
dispatch("getDialogs").catch(() => {});
dispatch("getReportUnread", 1000);
dispatch("getBacklogUnread", 1000);
dispatch("getTaskForDashboard");
dispatch("dialogMsgRead");
//
@ -485,6 +486,32 @@ export default {
}, typeof timeout === "number" ? timeout : 1000)
},
/**
* 获取审批待办未读数量
* @param state
* @param dispatch
* @param timeout
*/
getBacklogUnread({state, dispatch}, timeout) {
window.__getBacklogUnread && clearTimeout(window.__getBacklogUnread)
window.__getBacklogUnread = setTimeout(() => {
if (state.userId === 0) {
state.backlogUnreadNumber = 0;
} else {
dispatch("call", {
url: 'workflow/process/findTask',
data: {
page:1,
page_size: 500,
}
}).then(({data}) => {
state.backlogUnreadNumber = data.total || 0;
}).catch(_ => {});
}
}, typeof timeout === "number" ? timeout : 1000)
},
/**
* 获取/更新会员信息
* @param dispatch

View File

@ -179,4 +179,7 @@ export default {
apiKeyData: {},
localKeyPair: {},
localKeyLock: false,
// 审批待办未读数量
backlogUnreadNumber: 0,
};

View File

@ -699,6 +699,25 @@
background-color: $primary-color;
}
}
.open-review-details{
.cause{
border-bottom: 1px solid #e3e3e3;
border-top: 1px solid #e3e3e3;
padding-bottom: 10px;
margin-top: 10px;
>span:first-child{
display: inline-block;
padding: 15px 0;
}
}
.btn-raw{
display: flex;
text-align: center;
gap: 10px;
padding: 10px 0 5px 0;
}
}
}
}

View File

@ -220,4 +220,12 @@
align-items: center;
gap: 10px;
}
.comment{
border-radius:5px;
background-color: #eaeaea;
padding: 3px 10px;
margin-top: 5px;
margin-left: 45px;
}
}

View File

@ -109,15 +109,15 @@
@elseif ($type === 'notice')
{{$notice}}
@elseif ($type === 'workflow_reviewer')
<span class="open-review-details" data-id="{{$data->id}}"><b style="border-bottom: 1px solid #e3e3e3;padding-bottom: 10px;">{{$data->nickname}}提交的「{{$data->proc_def_name}}」待你审批</b>
<div style="border-bottom: 1px solid #e3e3e3;padding: 10px 0;"><span style="display: inline-block;padding:15px 0;">申请人:<span style="color:#84c56a">{{'@'}}{{$data->nickname}}</span> {{$data->department}}</span>
<span class="open-review-details" data-id="{{$data->id}}"><b>{{$data->nickname}}提交的「{{$data->proc_def_name}}」待你审批</b>
<div class="cause"><span>申请人:<span style="color:#84c56a">{{'@'}}{{$data->nickname}}</span> {{$data->department}}</span>
<b>审批事由</b>
@if ($data->type)
<span>假期类型:{{$data->type}}</span>
@endif
<span>开始时间:{{$data->start_time}}</span>
<span>结束时间:{{$data->end_time}}</span>
</div><div style="display: flex;text-align: center;gap: 10px;padding: 10px 0 5px 0;">
</div><div class="btn-raw">
@if ($action === 'pass')
<Button type="button" class="ivu-btn ivu-btn-small" style="flex: 1;">已同意</Button>
@elseif ($action === 'refuse')
@ -130,27 +130,27 @@
@endif
</div></span>
@elseif ($type === 'workflow_notifier')
<span class="open-review-details" data-id="{{$data->id}}"><b style="border-bottom: 1px solid #e3e3e3;padding-bottom: 10px;">抄送{{$data->nickname}}提交的「{{$data->proc_def_name}}」记录</b>
<div style="border-bottom: 1px solid #e3e3e3;padding: 10px 0;"><span style="display: inline-block;padding:15px 0;">申请人:<span style="color:#84c56a">{{'@'}}{{$data->nickname}}</span> {{$data->department}}</span>
<span class="open-review-details" data-id="{{$data->id}}"><b>抄送{{$data->nickname}}提交的「{{$data->proc_def_name}}」记录</b>
<div class="cause"><span>申请人:<span style="color:#84c56a">{{'@'}}{{$data->nickname}}</span> {{$data->department}}</span>
<b>审批事由</b>
@if ($data->type)
<span>假期类型:{{$data->type}}</span>
@endif
<span>开始时间:{{$data->start_time}}</span>
<span>结束时间:{{$data->end_time}}</span>
</div><div style="display: flex;text-align: center;gap: 10px;padding: 10px 0 5px 0;">
</div><div class="btn-raw">
<Button type="button" class="ivu-btn ivu-btn-primary ivu-btn-small" style="flex: 1;">查看详情</Button>
</div></span>
@elseif ($type === 'workflow_submitter')
<span class="open-review-details" data-id="{{$data->id}}"><b style="border-bottom: 1px solid #e3e3e3;padding-bottom: 10px;"> @if ($action === 'pass')您发起的「{{$data->proc_def_name}}」已通过 @else您发起的「{{$data->proc_def_name}}」被{{$data->nickname}}拒绝 @endif</b>
<div style="border-bottom: 1px solid #e3e3e3;padding: 10px 0;"><span style="display: inline-block;padding:15px 0;">申请人:<span style="color:#84c56a">{{'@'}}{{$data->nickname}}</span> {{$data->department}}</span>
<span class="open-review-details" data-id="{{$data->id}}"><b> @if ($action === 'pass')您发起的「{{$data->proc_def_name}}」已通过 @else您发起的「{{$data->proc_def_name}}」被{{$data->nickname}}拒绝 @endif</b>
<div class="cause"><span>申请人:<span style="color:#84c56a">{{'@'}}{{$data->nickname}}</span> {{$data->department}}</span>
<b>审批事由</b>
@if ($data->type)
<span>假期类型:{{$data->type}}</span>
@endif
<span>开始时间:{{$data->start_time}}</span>
<span>结束时间:{{$data->end_time}}</span>
</div><div style="display: flex;text-align: center;gap: 10px;padding: 10px 0 5px 0;">
</div><div class="btn-raw">
<Button type="button" class="ivu-btn ivu-btn-primary ivu-btn-small" style="flex: 1;">查看详情</Button>
</div></span>
@else