316 lines
11 KiB
Vue
316 lines
11 KiB
Vue
<template>
|
||
<div class="snaker-task-action" v-if="!loading || taskInfo">
|
||
<!-- 指定下一节点处理人 -->
|
||
<div class="next-handler-section" v-if="supportSpecifyNextHandler && candidates.length > 0">
|
||
<el-checkbox v-model="specifyNextHandler" @change="onSpecifyNextHandlerChange">指定下一节点处理人</el-checkbox>
|
||
<div class="handler-select-wrapper" v-if="specifyNextHandler">
|
||
<el-select v-model="selectedHandler" placeholder="请选择处理人" clearable filterable class="handler-select">
|
||
<el-option v-for="candidate in candidates" :key="candidate.userId" :label="candidate.userName" :value="candidate.userId">
|
||
<span style="float: left">{{ candidate.userName }}({{ candidate.ext?.loginName }})</span>
|
||
<span style="float: right; color: #8492a6; font-size: 13px">{{ candidate.ext?.unitName }}</span>
|
||
</el-option>
|
||
</el-select>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 第一个任务节点或新申请:显示提交、保存草稿、取消 -->
|
||
<!-- <template v-if="isFirstTaskNodeOrNew">-->
|
||
<!-- <el-button type="primary" @click="handleSubmit" :loading="actionLoading" icon="el-icon-upload2">提交</el-button>-->
|
||
<!-- <!– 保存草稿只在流程未发起时显示 –>-->
|
||
<!-- <el-button @click="handleSaveDraft" v-if="!actionLoading && show_save_draft && !instanceId" icon="el-icon-document">保存草稿</el-button>-->
|
||
<!-- <el-button @click="handleCancel" :loading="actionLoading" v-if="show_cancel" icon="el-icon-close">取消</el-button>-->
|
||
<!-- </template>-->
|
||
|
||
<!-- <!– 其他任务节点:显示审批按钮 –>-->
|
||
<!-- <template v-else>-->
|
||
<!-- <!– 会签任务按钮 –>-->
|
||
<!-- <template v-if="is_countersign_task">-->
|
||
<!-- <el-button type="primary" @click="handleAction('AGREE')" :loading="actionLoading" icon="el-icon-check">同意</el-button>-->
|
||
<!-- <el-button type="danger" plain @click="handleAction('COUNTERSIGN_DISAGREE')" :loading="actionLoading" icon="el-icon-close">-->
|
||
<!-- 不同意-->
|
||
<!-- </el-button>-->
|
||
<!-- </template>-->
|
||
|
||
<!-- <!– 普通任务按钮 –>-->
|
||
<!-- <template v-else>-->
|
||
<!-- <el-button type="primary" @click="handleAction('AGREE')" :loading="actionLoading" icon="el-icon-check">同意</el-button>-->
|
||
<!-- <el-button type="danger" plain @click="handleAction('REJECT')" :loading="actionLoading" icon="el-icon-close">拒绝</el-button>-->
|
||
<!-- <el-button @click="handleAction('ROLLBACK')" :loading="actionLoading" icon="el-icon-back">退回上一步</el-button>-->
|
||
<!-- <el-button @click="handleAction('ROLLBACK_TO_OPERATOR')" :loading="actionLoading" icon="el-icon-d-arrow-left">退回发起人</el-button>-->
|
||
<!-- <el-button @click="handleAction('JUMP')" :loading="actionLoading" icon="el-icon-position">跳转</el-button>-->
|
||
<!-- </template>-->
|
||
<!-- </template>-->
|
||
|
||
<!--没有流程实例的时候-->
|
||
<template v-if="!instanceId">
|
||
<el-button type="primary" @click="handleSubmit">提交</el-button>
|
||
<el-button @click="handleSaveDraft">保存</el-button>
|
||
</template>
|
||
|
||
<el-row type="flex" justify="end">
|
||
<el-button v-for="item in buttons" @click="handleAction(item.value)" :type="item.theme" :icon="item.icon" size="medium" :key="item.value">
|
||
{{ item.replacementText || item.originalText }}
|
||
</el-button>
|
||
</el-row>
|
||
</div>
|
||
|
||
<!-- 加载状态 -->
|
||
<div v-else class="snaker-task-action loading-state">
|
||
<el-button loading icon="el-icon-loading">加载中...</el-button>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
module.exports = {
|
||
name: "SnakerFlowTaskFormAction",
|
||
props: {},
|
||
data() {
|
||
return {
|
||
// 加载状态
|
||
loading: false,
|
||
// 操作按钮加载状态
|
||
actionLoading: false,
|
||
// 任务信息
|
||
taskInfo: null,
|
||
// 任务操作枚举
|
||
actionEnum: {
|
||
APPLY: "0", // 发起申请
|
||
AGREE: "1", // 同意
|
||
REJECT: "2", // 拒绝
|
||
COUNTERSIGN_DISAGREE: "20", // 会签不同意 拒绝申请
|
||
ROLLBACK: "3", // 退回上一步
|
||
ROLLBACK_TO_OPERATOR: "6", // 退回发起人
|
||
JUMP: "4", // 跳转
|
||
RE_APPLY: "5", // 重新提交
|
||
SUBMIT: "0" // 发起申请
|
||
},
|
||
// 任务类型枚举
|
||
performTypeEnum: {
|
||
NORMAL: 0, // 普通任务
|
||
COUNTERSIGN: 1 // 会签任务
|
||
},
|
||
// 指定下一节点处理人相关
|
||
specifyNextHandler: false, // 是否指定下一节点处理人
|
||
selectedHandler: null, // 选中的处理人ID
|
||
candidates: [], // 候选人列表
|
||
supportCandidate: false,
|
||
|
||
taskId: GetQueryString("taskId"),
|
||
instanceId: GetQueryString("instanceId"),
|
||
defineKey: GetQueryString("defineKey"),
|
||
defineId: GetQueryString("defineId"),
|
||
businessId: GetQueryString("businessId")
|
||
}
|
||
},
|
||
computed: {
|
||
// 是否为会签任务
|
||
is_countersign_task() {
|
||
return this.taskInfo && this.taskInfo.performType === this.performTypeEnum.COUNTERSIGN
|
||
},
|
||
|
||
// 是否为第一个任务节点或新申请
|
||
isFirstTaskNodeOrNew() {
|
||
// 没有任务ID,说明是新申请
|
||
if (!this.taskId) {
|
||
return true
|
||
}
|
||
|
||
// 有任务ID但是任务信息中标识为第一个节点
|
||
return this.taskInfo && this.taskInfo.ext && this.taskInfo.ext.isFirstTaskNode
|
||
},
|
||
|
||
// 审批按钮配置
|
||
buttons() {
|
||
if (this.taskInfo) {
|
||
return this.taskInfo.taskModel.ext.buttonConfig.filter((v) => v.visible)
|
||
}
|
||
return []
|
||
},
|
||
|
||
// 是否支持选择下一步处理人
|
||
supportSpecifyNextHandler() {
|
||
return this.taskInfo && this.taskInfo.taskModel.ext.enableNextOperator
|
||
}
|
||
},
|
||
mounted() {
|
||
// 只有当有任务ID时才加载任务信息
|
||
if (this.taskId) {
|
||
this.loadTaskInfo()
|
||
this.loadCandidates()
|
||
}
|
||
},
|
||
methods: {
|
||
// 加载任务信息
|
||
loadTaskInfo() {
|
||
if (!this.taskId) return
|
||
|
||
this.loading = true
|
||
this.taskInfo = null
|
||
|
||
$.get("/flow/common/taskInfo", {
|
||
taskId: this.taskId
|
||
})
|
||
.then((res) => {
|
||
if (res.code === 0) {
|
||
this.taskInfo = res.data
|
||
}
|
||
})
|
||
.always(() => {
|
||
this.loading = false
|
||
})
|
||
},
|
||
|
||
// 处理提交操作
|
||
handleSubmit() {
|
||
// 根据是否有流程实例来确定操作类型
|
||
const submitAction = this.instanceId ? this.actionEnum.RE_APPLY : this.actionEnum.APPLY
|
||
|
||
// 触发父组件事件,传递提交操作
|
||
this.$emit("task-action", {
|
||
submitType: submitAction,
|
||
taskId: this.taskId,
|
||
instanceId: this.instanceId,
|
||
defineId: this.defineId,
|
||
defineKey: this.defineKey,
|
||
businessId: this.businessId
|
||
})
|
||
},
|
||
|
||
// 处理任务操作
|
||
handleAction(actionKey) {
|
||
if (this.actionLoading || !this.taskInfo) return
|
||
|
||
// 构建操作数据
|
||
const actionData = {
|
||
submitType: this.actionEnum[actionKey],
|
||
taskId: this.taskId,
|
||
processTaskId: this.taskId,
|
||
instanceId: this.instanceId,
|
||
defineId: this.defineId,
|
||
defineKey: this.defineKey,
|
||
businessId: this.businessId
|
||
}
|
||
|
||
// 如果指定了下一节点处理人,添加到操作数据中
|
||
if (this.specifyNextHandler && this.selectedHandler) {
|
||
actionData.tf_nextNodeOperator = this.selectedHandler
|
||
}
|
||
|
||
// 触发父组件事件,传递操作类型和任务信息
|
||
this.$emit("task-action", actionData)
|
||
},
|
||
|
||
// 保存草稿
|
||
handleSaveDraft() {
|
||
if (this.actionLoading) return
|
||
|
||
this.$emit("save-draft", {
|
||
submitType: this.actionEnum.APPLY,
|
||
defineKey: this.defineKey,
|
||
defineId: this.defineId
|
||
})
|
||
},
|
||
|
||
// 取消操作
|
||
handleCancel() {
|
||
const func = () => window.close()
|
||
this.$emit("cancel", func)
|
||
if (!this.$listeners.cancel) {
|
||
func()
|
||
}
|
||
},
|
||
|
||
// 刷新任务信息
|
||
refresh() {
|
||
if (this.taskId) {
|
||
this.loadTaskInfo()
|
||
}
|
||
},
|
||
|
||
// 处理指定下一节点处理人勾选框变化
|
||
onSpecifyNextHandlerChange(checked) {
|
||
if (!checked) {
|
||
this.selectedHandler = null
|
||
}
|
||
},
|
||
|
||
// 加载候选人列表
|
||
loadCandidates() {
|
||
$.get("/flow/common/candidate", {
|
||
taskId: this.taskId
|
||
}).then((res) => {
|
||
if (res.code === 0) {
|
||
this.candidates = res.data.candidates
|
||
this.supportCandidate = res.data.support
|
||
}
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.snaker-task-action {
|
||
text-align: center;
|
||
padding: 20px;
|
||
background: #fff;
|
||
border-top: 1px solid #eee;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.snaker-task-action .el-button {
|
||
margin: 0 8px;
|
||
min-width: 80px;
|
||
}
|
||
|
||
.loading-state {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
min-height: 60px;
|
||
}
|
||
|
||
/* 指定下一节点处理人样式 */
|
||
.next-handler-section {
|
||
margin-bottom: 20px;
|
||
padding: 15px;
|
||
background: #f8f9fa;
|
||
border: 1px solid #e9ecef;
|
||
border-radius: 4px;
|
||
text-align: left;
|
||
}
|
||
|
||
.handler-select-wrapper {
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.handler-select {
|
||
width: 100%;
|
||
max-width: 300px;
|
||
}
|
||
|
||
/* 响应式 */
|
||
@media (max-width: 768px) {
|
||
.snaker-task-action {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
padding: 15px;
|
||
}
|
||
|
||
.snaker-task-action .el-button {
|
||
width: 100%;
|
||
margin: 0;
|
||
}
|
||
|
||
.next-handler-section {
|
||
margin-bottom: 15px;
|
||
padding: 10px;
|
||
}
|
||
|
||
.handler-select {
|
||
max-width: 100%;
|
||
}
|
||
}
|
||
</style>
|