first commit

This commit is contained in:
ye1023
2026-04-09 15:09:34 +08:00
commit d8e3a63df1
2246 changed files with 352109 additions and 0 deletions
@@ -0,0 +1,81 @@
<template>
<a-card :class="'jeecg-my-handle-task-info'">
<a-tabs :activeKey="activeKey" tabPosition="left" @tabClick="handleChangePanel">
<a-tab-pane tab="我的任务" key="run">
<task-running-list ref="taskRef" v-if="activeKey==='run'"></task-running-list>
</a-tab-pane>
<a-tab-pane tab="组任务" key="group">
<task-group-list v-if="activeKey==='group'"></task-group-list>
</a-tab-pane>
<a-tab-pane tab="历史任务" key="history">
<task-history-list v-if="activeKey==='history'"></task-history-list>
</a-tab-pane>
</a-tabs>
</a-card>
</template>
<script>
/**
* 我处理的任务信息
* - 正在处理的任务
* - 组任务
* - 历史任务
*/
import { ref, onMounted, nextTick } from 'vue';
import TaskRunningList from './TaskRunningList.vue';
import TaskGroupList from './TaskGroupList.vue';
import TaskHistoryList from './TaskHistoryList.vue';
import { useRouter } from 'vue-router';
import { useAppStore } from '/@/store/modules/app';
export default {
name: 'MyHandleTaskInfo',
components: {
TaskRunningList,
TaskGroupList,
TaskHistoryList,
},
setup() {
const bodyStyle = {
padding: '10px',
};
const activeKey = ref('run');
function handleChangePanel(key) {
activeKey.value = key;
}
// 消息跳转处理页面参数
const taskRef = ref()
const appStore = useAppStore();
onMounted(()=>{
activeKey.value = 'run';
let params = appStore.getMessageHrefParams;
if(params) {
let taskId = params.detailId;
if(taskId){
nextTick(()=>{
taskRef.value.openHrefModal(taskId);
appStore.setMessageHrefParams('');
})
}
}
});
return {
bodyStyle,
activeKey,
handleChangePanel,
taskRef
};
},
};
</script>
<style scoped>
.jeecg-my-handle-task-info {
margin: 10px;
}
</style>
@@ -0,0 +1,79 @@
<template>
<BasicTable @register="registerTable">
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)"></TableAction>
</template>
</BasicTable>
<!-- 办理 -->
<task-handle-modal @register="registerHandleModal" @success="reload"></task-handle-modal>
</template>
<script>
import { BasicTable, TableAction } from '/@/components/Table';
import { useTaskList } from './useTaskList';
import { useModal } from '/@/components/Modal';
import TaskHandleModal from './modal/TaskHandleModal.vue';
import { taskClaim } from './task.handle.api';
export default {
name: 'TaskGroupList',
components: {
BasicTable,
TableAction,
TaskHandleModal,
},
setup() {
const { registerTable, reload, getTaskNodeInfo } = useTaskList('group');
//办理
const [registerHandleModal, { openModal: openHandleModal }] = useModal();
async function handleProcess(record) {
let { formData, formUrl } = await getTaskNodeInfo(record);
formData['PROCESS_TAB_TYPE'] = 'run';
openHandleModal(true, {
formData,
formUrl,
title: '流程办理',
});
}
//签收
async function handleClaim(record) {
let params = { taskId: record.id };
await taskClaim(params);
await reload();
}
function getTableAction(record) {
let arr = [];
if (record.taskAssigneeId && record.taskAssigneeId != '') {
arr.push({
label: '办理',
onClick: handleProcess.bind(null, record),
});
} else {
arr.push({
label: '签收',
popConfirm: {
title: '确定签收吗?',
placement: 'left',
confirm: handleClaim.bind(null, record),
},
});
}
return arr;
}
return {
registerTable,
getTableAction,
registerHandleModal,
reload,
};
},
};
</script>
<style scoped></style>
@@ -0,0 +1,58 @@
<template>
<BasicTable @register="registerTable">
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)"></TableAction>
</template>
</BasicTable>
<task-handle-modal @register="registerModal"></task-handle-modal>
</template>
<script>
import { BasicTable, TableAction } from '/@/components/Table';
import { useTaskList } from './useTaskList';
import { useModal } from '/@/components/Modal';
import TaskHandleModal from './modal/TaskHandleModal.vue';
export default {
name: 'TaskHistoryList',
components: {
BasicTable,
TableAction,
TaskHandleModal,
},
setup() {
const { registerTable, getHistoryTaskInfo } = useTaskList('history');
const [registerModal, { openModal }] = useModal();
async function showHistoryInfo(record) {
let { formData, formUrl } = await getHistoryTaskInfo(record);
formData['PROCESS_TAB_TYPE'] = 'history';
openModal(true, {
formData,
formUrl,
title: '流程历史',
});
}
function getTableAction(record) {
return [
{
label: '历史',
onClick: showHistoryInfo.bind(null, record),
},
];
}
return {
registerTable,
registerModal,
getTableAction,
};
},
};
</script>
<style scoped></style>
@@ -0,0 +1,186 @@
<template>
<BasicTable @register="registerTable">
<template #bpmBizTitle="{ text, record }">
<notification-two-tone v-if="record.taskUrge" title="催办提醒" twoToneColor="#eb2f96" @click="taskNotify(record)" />
<j-ellipsis :value="text" :length="15" />
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)"></TableAction>
</template>
</BasicTable>
<!-- 催办 -->
<task-notify-me-modal @register="registerNotifyModal"></task-notify-me-modal>
<!-- 办理 -->
<task-handle-modal @register="registerHandleModal" @success="reload"></task-handle-modal>
<!-- 委托 -->
<select-entruster-modal @register="registerEntrusterModal" @selected="selectedEntruster"></select-entruster-modal>
<!-- 转办 -->
<select-entruster-modal @register="registerComplaintModal" @selected="selectedComplaint"></select-entruster-modal>
</template>
<script>
import { BasicTable, TableAction } from '/@/components/Table';
import { useTaskList } from './useTaskList';
import JEllipsis from '/@/components/Form/src/jeecg/components/JEllipsis.vue';
import { NotificationTwoTone } from '@ant-design/icons-vue';
import TaskNotifyMeModal from './modal/TaskNotifyMeModal.vue';
import TaskHandleModal from './modal/TaskHandleModal.vue';
import { useModal } from '/@/components/Modal';
import SelectEntrusterModal from './modal/SelectEntrusterModal.vue';
import { taskEntrust, taskClaim, taskComplaint } from './task.handle.api';
import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage';
export default {
name: 'TaskRunningList',
components: {
BasicTable,
TableAction,
JEllipsis,
NotificationTwoTone,
TaskNotifyMeModal,
TaskHandleModal,
SelectEntrusterModal,
},
setup() {
const { registerTable, reload, getTaskNodeInfo } = useTaskList('run');
const userStore = useUserStore();
const { createMessage } = useMessage();
//催办
const [registerNotifyModal, { openModal: openNotifyModal }] = useModal();
function taskNotify(record) {
openNotifyModal(true, {
procInstId: record.processInstanceId,
});
}
//办理
const [registerHandleModal, { openModal: openHandleModal }] = useModal();
async function handleProcess(record) {
let { formData, formUrl } = await getTaskNodeInfo(record);
formData['PROCESS_TAB_TYPE'] = 'run';
openHandleModal(true, {
formData,
formUrl,
title: '流程办理',
});
}
// 委托
const [registerEntrusterModal, { openModal: openEntrusterModal }] = useModal();
// 转办
const [registerComplaintModal, { openModal: openComplaintModal }] = useModal();
// 委托——弹出选择界面
function handleSelectEntruster(record) {
openEntrusterModal(true, {
taskId: record.id,
});
}
// 转办——弹出选择界面
function handleComplaintEntruster(record) {
openComplaintModal(true, {
taskId: record.id,
});
}
// 委托——回调处理
async function selectedEntruster(params) {
console.log('委托', params);
await taskEntrust(params);
await reload();
}
// 转办——回调处理
async function selectedComplaint(params) {
console.log('转办', params);
await taskComplaint(params);
await reload();
}
//签收
async function handleClaim(record) {
let params = { taskId: record.id };
await taskClaim(params);
await reload();
}
function getTableAction(record) {
let arr = [];
if (record.taskAssigneeId && record.taskAssigneeId != '') {
arr.push({
label: '办理',
onClick: handleProcess.bind(null, record),
});
} else {
arr.push({
label: '签收',
popConfirm: {
title: '确定签收吗?',
placement: 'left',
confirm: handleClaim.bind(null, record),
},
});
}
arr.push({
label: '转办',
onClick: handleComplaintEntruster.bind(null, record),
});
arr.push({
label: '委托',
onClick: handleSelectEntruster.bind(null, record),
});
return arr;
}
/**
* 消息跳转时打开表单页面的逻辑
* @param taskId
*/
async function openHrefModal(taskId){
let { formData, formUrl, isSignTask, taskIsHandel, assignee } = await getTaskNodeInfo({id: taskId });
if(taskIsHandel==true){
//如果已经处理过了
return;
}
let username = userStore.getUserInfo.username;
if(assignee && username!=assignee){
//不是你办理的任务
createMessage.warning('任务已被他人签收~')
return;
}
formData['PROCESS_TAB_TYPE'] = 'run';
openHandleModal(true, {
formData,
formUrl,
isSignTask,
title: '流程办理',
});
}
return {
registerTable,
taskNotify,
registerNotifyModal,
registerHandleModal,
getTableAction,
reload,
selectedEntruster,
selectedComplaint,
registerEntrusterModal,
registerComplaintModal,
openHrefModal
};
},
};
</script>
<style scoped></style>
@@ -0,0 +1,502 @@
<template>
<a-card style="margin-top: 10px">
<template #title> <audit-outlined /><span style="margin-left: 10px">我的审批</span> </template>
<a-spin :spinning="loading">
<a-list itemLayout="vertical">
<a-list-item>
<div style="width: 100%">
<div style="margin-bottom: 5px">
处理意见
<a-select
style="width: 300px"
placeholder="常用审批语"
:getPopupContainer="(target) => target.parentNode"
@change="changeReasonSelection"
>
<a-select-option v-for="(item, key) in remarksDictOptions" :key="key" :value="item.value">{{ item.text }}</a-select-option>
</a-select>
</div>
<a-textarea :rows="3" v-model:value="model.reason" />
</div>
</a-list-item>
<a-list-item>
<j-upload text="添加文件" bizPath="bpm" :returnUrl="false" v-model:value="model.fileList"></j-upload>
</a-list-item>
<!-- 选择分支 -->
<a-list-item>
<div style="width: 100%">
<a-radio-group v-model:value="model.processModel">
<a-radio :checked="true" :value="1">单分支模式</a-radio>
<a-radio :value="2">多分支模式</a-radio>
<a-radio :value="3" v-if="historyCount > 0 && allowReject">驳回</a-radio>
</a-radio-group>
<span v-show="model.processModel == 2">
<span style="color: red">多分支模式默认执行所有分支</span>
<template v-for="(item, index) in branchList">
<a-checkbox :checked="true" :value="item.nextnode">{{ item.Transition }}</a-checkbox>
</template>
</span>
<span v-show="model.processModel == 3" v-if="historyCount > 0">
<a-select v-model:value="model.rejectModelNode" :getPopupContainer="(target) => target?.parentNode" style="width: 150px">
<template v-for="(item, index) in historyList">
<a-select-option v-if="item.NAME_ != currentTaskName" :value="item.TASK_DEF_KEY_">{{ item.NAME_ }}</a-select-option>
</template>
</a-select>
</span>
</div>
</a-list-item>
<!-- 下一步操作人 -->
<a-list-item class="flex-center" v-if="buttonStatus.selnextUserStatus || buttonStatus.ccStatus || allowAddSign || allowCounterSignAddUser">
<a-checkbox v-if="buttonStatus.selnextUserStatus" :checked="checkedNext" @change="handleCheckedNext">指定下一步操作人指定下一步会签人员</a-checkbox>
<a-checkbox v-if="buttonStatus.ccStatus" :checked="checkedCc" @change="handleCheckedCc">是否抄送</a-checkbox>
<a-dropdown v-if="allowAddSign || allowCounterSignAddUser" :trigger="['click']" :overlayStyle="{ width: '120px' }" placement="bottomRight">
<div class="dot-more" title="更多操作"> <div></div><div></div><div></div> </div>
<template #overlay>
<a-menu @click="onMoreAction">
<a-menu-item key="add-sign" v-if="allowAddSign">
<div class="flex-center">
<Icon icon="ant-design:sisternode-outlined" size="22" />
<span style="margin-left: 10px">动态加签</span>
</div>
</a-menu-item>
<a-menu-item key="add-user" v-if="allowCounterSignAddUser">
<div class="flex-center">
<Icon icon="ant-design:usergroup-add-outlined" size="22" />
<span style="margin-left: 10px">追加审批人</span>
</div>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-list-item>
<a-list-item style="line-height: 32px" v-show="checkedNext">
<span>指定下一步操作人指定下一步会签人员</span>
<bpm-select-user style="display: inline-block" placeholder="请选择下一步操作人" @change="handleSelectNextUser"></bpm-select-user>
</a-list-item>
<!-- 抄送 -->
<a-list-item style="line-height: 32px" v-show="checkedCc">
<span>抄送给</span>
<bpm-select-user style="display: inline-block" placeholder="请选择抄送人" @change="handleSelectCcUser"></bpm-select-user>
</a-list-item>
</a-list>
<!-- 流转按钮 -->
<div style="margin-top: 20px; text-align: center">
<template v-if="model.processModel == 1">
<template v-for="(item, index) in branchList">
<a-button type="primary" @click="handleProcessComplete(item.nextnode)">{{ item.Transition }}</a-button>
</template>
</template>
<template v-else>
<a-button type="primary" @click="handleManyProcessComplete()">确认提交</a-button>
</template>
</div>
<br />
</a-spin>
</a-card>
<!-- 加签节点弹窗 -->
<add-sign-task-modal @register="registerAddSignTaskModal" @selected="selectedAddSignTask" />
<!-- 审批人选择人员 -->
<user-select-modal :multi="multiSelectUser" @register="registerUserModal" @selected="onSelectedUserOk" />
</template>
<script>
import { ref, reactive, computed, toRaw, unref, watchEffect } from 'vue';
import { initDictOptions } from '/@/utils/dict';
import { useMessage } from '/@/hooks/web/useMessage';
import { taskComplaint, taskComplete, beforeAddSignTask, afterAddSignTask, addMultiInstance} from "../task.handle.api";
import { useModal } from '/@/components/Modal';
import { JSelectUserByDept } from '/@/components/Form';
import { AuditOutlined } from '@ant-design/icons-vue';
import { JUpload } from '/@/components/Form/src/jeecg/components/JUpload';
import BpmSelectUser from '/@/views/super/bpm/process/components/bpmSelectUser/index.vue';
import AddSignTaskModal from '/@/views/super/bpm/process/personalOffice/myHandleTask/modal/AddSignTaskModal.vue';
import UserSelectModal from '/@/components/Form/src/jeecg/components/userSelect/UserSelectModal.vue';
import { useUserStore } from '/@/store/modules/user';
import { isString } from '/@/utils/is';
export default {
name: 'MyHandleContent',
components: {
UserSelectModal,
AddSignTaskModal,
JSelectUserByDept,
JUpload,
AuditOutlined,
BpmSelectUser,
},
props: {
historyList: {
type: Array,
default: () => [],
},
branchList: {
type: Array,
default: () => [],
},
currentTaskName: {
type: String,
default: '',
},
taskId: {
type: String,
default: '',
},
ccStatus:{
type: Boolean,
default: true,
},
allowAddSign:{
type: Boolean,
default: false,
},
allowCounterSignAddUser: {
type: Boolean,
default: false,
},
allowReject: {
type: Boolean,
default: true,
},
turnbackTaskId: {
type: String,
default: '',
},
selnextUserStatus:{
type: Boolean,
default: true,
}
},
emits: ['success'],
setup(props, { emit }) {
console.log('进入MyHandleContent');
const loading = ref(false);
const { createMessage: $message, createConfirm: $confirm } = useMessage();
const userStore = useUserStore();
// 转办
const [registerAddSignTaskModal, { openModal: openAddSignTaskModal }] = useModal();
// 选择用户
const [registerUserModal, { openModal: openUserModal, closeModal: closeUserModal }] = useModal();
/**
* 转审(change-user)和添加审批人(add-user) 都需要选择用户,此处记录不同的类型
*/
const selectUserType = ref('');
const remarksDictOptions = ref([]);
const historyCount = computed(() => {
return props.historyList.length;
});
const buttonStatus = computed(()=>{
return {
ccStatus: props.ccStatus,
selnextUserStatus: props.selnextUserStatus
}
})
const model = reactive({
taskId: '',
nextnode: '',
nextCodeCount: '',
reason: '',
processModel: 1,
rejectModelNode: '',
nextUserName: '',
nextUserId: '',
ccUserIds: '',
ccUserRealNames: '',
fileList: '',
});
//审批人用户是否多选
const multiSelectUser = computed(() => {
return unref(selectUserType) == 'change-user' ? false : true;
});
//监听返回节点变化
watchEffect(() => {
props.turnbackTaskId && (model.rejectModelNode = props.turnbackTaskId);
});
// 选择下一步操作人
const checkedNext = ref(false);
const nextPersonList = ref([]);
function handleCheckedNext(e) {
checkedNext.value = e.target.checked;
nextPersonList.value = [];
}
// 选择抄送人
const checkedCc = ref(false);
const ccPersonList = ref([]);
function handleCheckedCc(e) {
checkedCc.value = e.target.checked;
ccPersonList.value = [];
}
function changeReasonSelection(value) {
model.reason = value;
}
function handleProcessComplete(nextnode) {
if (!model.reason || model.reason.length == 0) {
//update-begin-author:taoyan date:2022-9-5 for: VUEN-2157 2. 意见允许为空
//$message.warning('请填写处理意见');
//return;
//update-end-author:taoyan date:2022-9-5 for: VUEN-2157 2. 意见允许为空
}
if (nextnode) {
model.nextnode = nextnode;
}
//设置下个节点的数量
if (props.branchList) {
model.nextCodeCount = props.branchList.length;
}
$confirm({
title: '提示',
content: '确认提交审批吗?',
onOk: commitTaskHandleInfo,
});
}
function handleManyProcessComplete() {
if (model.processModel == 3) {
if (!model.rejectModelNode || model.rejectModelNode.length == 0) {
$message.warning('请选择驳回节点');
return;
}
}
handleProcessComplete();
}
// 加签——弹出选择界面
function handleAddSignTask() {
openAddSignTaskModal(true, {
taskId: props.taskId
});
}
// 加签——回调处理
async function selectedAddSignTask(params,addSignType) {
console.log('加签', params);
console.log('加签', addSignType);
if(addSignType=='after'){
await afterAddSignTask(params);
}
if(addSignType=='before'){
await beforeAddSignTask(params);
}
//加签完,关闭办理弹窗
emit('success');
}
//================[20230707会签节点增加审批人]begin=============================
/**
* 更多操作
*/
function onMoreAction(e) {
selectUserType.value = 'add-user';
if ('add-user' == e.key) {
//多实例加签(支持串行和并行会签)
onAddHandlePerson();
} else if ('add-sign' == e.key) {
//加签
handleAddSignTask();
}
}
/**
* 添加审批人 事件
*/
function onAddHandlePerson() {
let id = userStore.getUserInfo.id;
selectUserType.value = 'add-user';
openUserModal(true, {
list: [],
excludeUserIdList: [id],
});
}
/**
* 人员选中事件
* @param data
*/
async function onSelectedUserOk(data) {
console.log('onSelectedUserOk', data);
if (data && data.length > 0) {
if (!props.taskId) {
console.error('节点信息不存在');
return;
}
if (selectUserType.value === 'change-user') {
//await doChangeHandlePerson(node.taskId, data)
} else if (selectUserType.value === 'add-user') {
await doAddHandlePerson(props.taskId, data);
} else {
console.error('不识别的类型', selectUserType.value);
}
}
}
/**
* 添加审批人 请求
*/
async function doAddHandlePerson(taskId, userList) {
let usernameList = userList.map((item) => item.username);
let params = {
taskId: taskId,
assignees: usernameList,
//简流模式,串行会签专用变量值
assigneeListKey: 'loopAssigneeCollection',
};
const result = await addMultiInstance(params);
if (result.success) {
closeUserModal();
emit('success');
$message.success('操作成功!');
} else {
$message.warning(result.message);
}
}
//================[20230707会签节点增加审批人]end=============================
/**
* 流程处理提交
* @returns {Promise<void>}
*/
async function commitTaskHandleInfo() {
loading.value = true;
const params = toRaw(model);
params.taskId = props.taskId;
if (params.fileList) {
params.fileList = isString(params.fileList) ? params.fileList : JSON.stringify(params.fileList);
}
try {
console.log('流程提交->params', params);
let json = await taskComplete(params);
console.log('流程提交-result', json);
loading.value = false;
emit('success');
} catch (e) {
console.error('流程处理失败', e);
loading.value = false;
}
}
async function initDict() {
const dictData = await initDictOptions('approval_remarks');
console.log('dic', dictData);
remarksDictOptions.value = dictData;
}
function handleSelectNextUser(arr) {
let { usernames, realnames } = getInfo(arr);
model.nextUserId = usernames.join(',');
model.nextUserName = realnames.join(',');
}
function handleSelectCcUser(arr) {
let { usernames, realnames } = getInfo(arr);
model.ccUserIds = usernames.join(',');
model.ccUserRealNames = realnames.join(',');
}
function getInfo(arr) {
let usernames = [];
let realnames = [];
if (arr && arr.length > 0) {
for (let item of arr) {
usernames.push(item.username);
realnames.push(item.realname);
}
}
return {
usernames,
realnames,
};
}
initDict();
return {
handleSelectNextUser,
handleSelectCcUser,
model,
remarksDictOptions,
changeReasonSelection,
historyCount,
checkedNext,
nextPersonList,
handleCheckedNext,
checkedCc,
ccPersonList,
handleCheckedCc,
loading,
handleManyProcessComplete,
handleProcessComplete,
buttonStatus,
registerAddSignTaskModal,
selectedAddSignTask,
handleAddSignTask,
onMoreAction,
openUserModal,
closeUserModal,
registerUserModal,
onSelectedUserOk,
multiSelectUser
};
},
};
</script>
<style lang="less" scoped>
:deep(.ant-card-head-title) {
padding: 10px 0;
}
:deep(.ant-collapse-header) {
font-size: 16px !important;
}
:deep(.ant-card-head) {
padding-left: 12px;
}
html[data-theme='light'] {
background-color: #fafafa;
}
:deep(.ant-card-body) {
padding-top: 6px;
}
//会签节点增加审批人下拉显示更多样式begin
.flex-center {
display: flex;
justify-content: flex-start;
align-items: center;
}
.dot-more {
display: flex;
flex-direction: row;
height: 30px;
width: 30px;
padding: 0 4px;
border-radius: 2px;
cursor: pointer;
align-items: center;
justify-content: space-between;
margin-left: 8px;
& > div {
width: 4px;
height: 4px;
background-color: #9e9e9e;
margin: 0 1px;
border-radius: 3px;
}
&:hover {
background-color: #f7f7f7;
& > div {
background-color: #0a9fe5;
}
}
}
//会签节点增加审批人下拉显示更多样式end
</style>
@@ -0,0 +1,89 @@
<template>
<a-collapse v-model:activeKey="activeKey" style="margin-top: 10px">
<a-collapse-panel key="1" header="意见信息">
<a-list itemLayout="vertical">
<template v-for="(item, index) in bpmLogList">
<a-list-item>
<a-list-item-meta :description="item.remarks||'无意见信息'">
<template #title>
{{ item.opUserName }}
<span style="color: #ff6d75">[{{ item.taskName }}]</span>
{{ item.opTime }}
</template>
<template #avatar>
<a-avatar :size="36" style="background-color: #51cbff"
><template #icon><UserOutlined /></template
></a-avatar>
</template>
</a-list-item-meta>
<template v-for="(file, index) in item.bpmFiles">
<div class="ant-upload-list ant-upload-list-text">
<div class="ant-upload-list-item ant-upload-list-item-done">
<div class="ant-upload-list-item-info">
<span>
<paper-clip-outlined />
<a
target="_blank"
rel="noopener noreferrer"
:title="file.fileName"
:href="getFileDownloadUrl(file.filePath)"
class="ant-upload-list-item-name"
>{{ file.fileName }}</a
>
</span>
</div>
</div>
</div>
</template>
</a-list-item>
</template>
</a-list>
</a-collapse-panel>
</a-collapse>
</template>
<script>
import { UserOutlined, PaperClipOutlined } from '@ant-design/icons-vue';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { ref, watch } from 'vue';
export default {
name: 'TaskCommentList',
components: {
UserOutlined,
PaperClipOutlined,
},
props: {
bpmLogList: {
type: Array,
default: () => [],
},
},
setup(props) {
const activeKey = ref('1');
watch(
() => props.bpmLogList,
() => {
if (props.bpmLogList.length > 0) {
activeKey.value = '1';
} else {
activeKey.value = '';
}
}
);
function getFileDownloadUrl(path) {
return getFileAccessHttpUrl(path);
}
return {
activeKey,
getFileDownloadUrl,
};
},
};
</script>
<style scoped></style>
@@ -0,0 +1,244 @@
<template>
<div class="jeecg-task-handle-content">
<!-- 横向步骤条显示节点 -->
<task-node-step-list :taskStepList="taskStepList" :stepCount="bpmLogList.length" :currentNode="currentNode"></task-node-step-list>
<!-- 意见信息 -->
<task-comment-list :bpmLogList="bpmLogList"></task-comment-list>
<!-- 填写处理意见并提交 -->
<my-handle-content
v-if="isRunningTask"
@success="handleSuccess"
:taskId="taskId"
:historyList="historyList"
:turnbackTaskId="turnbackTaskId"
:branchList="branchList"
:selnextUserStatus="selnextUserStatus"
:ccStatus="ccStatus"
:allowAddSign="allowAddSign"
:allowCounterSignAddUser="allowCounterSignAddUser"
:allowReject="allowReject"
:currentTaskName="currentNode.taskName">
</my-handle-content>
<!-- 签收 -->
<div v-if="claim" style="width: 100%;text-align: center; padding-top: 10px">
<a-button type="primary" @click="handleClaim" :loading="loading"><AuditOutlined/>确认签收</a-button>
</div>
</div>
</template>
<script lang="ts">
import { taskTransInfo, taskComplete, taskClaim } from '../task.handle.api';
import { ref, watch } from 'vue';
import TaskNodeStepList from './TaskNodeStepList.vue';
import TaskCommentList from './TaskCommentList.vue';
import MyHandleContent from './MyHandleContent.vue';
import { pick } from 'lodash-es';
import { AuditOutlined } from '@ant-design/icons-vue';
export default {
name: 'TaskHandleInnerContent',
components: {
TaskNodeStepList,
TaskCommentList,
MyHandleContent,
AuditOutlined
},
props: {
formData: {
type: Object,
default: () => {},
},
//签收状态
claim:{
type: Boolean,
default: false
}
},
emits: ['success', 'claimSuccess'],
setup(props, { emit }) {
console.log('进入tab panel-content');
const taskId = ref('');
// 任务步骤条
const taskStepList = ref([]);
// 当前任务节点
const currentNode = ref({});
// 审批记录/意见信息
const bpmLogList = ref([]);
// 历史节点
const historyList = ref([]);
// 获取上一步的节点
const turnbackTaskId = ref('');
// 任务分支
const branchList = ref([]);
const isRunningTask = ref(true);
const allowAddSign = ref(false);
//允许驳回
const allowReject = ref(true);
const allowCounterSignAddUser = ref(false);
const loading = ref(false);
// 选择下一步处理人
const selnextUserStatus = ref(true);
// 选择抄送人
const ccStatus = ref(true);
getTaskTransInfo();
async function getTaskTransInfo(type?) {
let taskType = props.formData['PROCESS_TAB_TYPE'];
console.log('taskType>>', taskType)
if(type){
taskType = type;
}
isRunningTask.value = taskType == 'run';
//查询条件-run只需要taskId history只需要procInstId
let params = { taskId: props.formData.taskId, procInstId: props.formData.procInsId };
let data = await taskTransInfo(params, taskType);
console.log('获取流程流转信息', data);
//update-begin-author:taoyan date:2022-7-5 for:
//选择下一步操作人
if(!data.selnextUserStatus || data.selnextUserStatus=='1'){
//如果没有该值 或是该值为1
selnextUserStatus.value = true;
}else{
selnextUserStatus.value = false;
}
// 选择抄送人
if(!data.ccStatus || data.ccStatus=='1'){
//如果没有该值 或是该值为1
ccStatus.value = true;
}else{
ccStatus.value = false;
}
//是否允许加签
allowAddSign.value= data.allowAddSign;
//是否允许驳回
allowReject.value= data?.rejectStatus && data?.rejectStatus =='0'?false:true;
//是否多实例- 会签加签 是否允许会签节点加人0否、1允许
allowCounterSignAddUser.value = (data?.allowCounterSignAddUser && data.allowCounterSignAddUser =='1');
taskStepList.value = data.bpmLogStepList;
bpmLogList.value = data.bpmLogList;
let nodeObject = pick(data, 'taskName', 'taskNameStartTime', 'taskAssigneeName');
if (isRunningTask.value === false) {
// 'taskName', 'taskNameStartTime', 'taskAssigneeName' 是正在运行流程的当前节点信息
// 'currTaskName', 'currTaskNameAssignee', 'currTaskNameStartTime'这三个是历史查看中的节点信息
nodeObject['taskName'] = data['currTaskName'];
nodeObject['taskNameStartTime'] = data['currTaskNameStartTime'];
nodeObject['taskAssigneeName'] = data['currTaskNameAssignee'];
}
currentNode.value = nodeObject;
historyList.value = data.histListNode;
branchList.value = data.transitionList;
turnbackTaskId.value = data.turnbackTaskId;
taskId.value = props.formData.taskId;
}
function handleSuccess() {
emit('success');
}
/**
* 签收调用
*/
async function handleClaim(){
loading.value = true;
let params = { taskId: taskId.value };
await taskClaim(params);
await getTaskTransInfo('run');
emit('claimSuccess')
setTimeout(()=>{
loading.value = false;
}, 200)
}
watch(()=>props.claim, (val)=>{
if(val===true){
// 如果是签收状态 则定义isRunningTask为false
isRunningTask.value = false
}else{
let taskType = props.formData['PROCESS_TAB_TYPE'];
isRunningTask.value = taskType == 'run';
}
}, {immediate: true})
return {
taskId,
bpmLogList,
taskStepList,
currentNode,
historyList,
branchList,
handleSuccess,
isRunningTask,
handleClaim,
loading,
ccStatus,
allowAddSign,
allowCounterSignAddUser,
allowReject,
selnextUserStatus,
turnbackTaskId
};
},
};
</script>
<style lang="less">
.jeecg-task-handle-content {
.task-info {
margin: 20px 0;
}
.task-title {
font-weight: bold;
}
.task-date {
text-overflow: ellipsis;
white-space: nowrap;
}
.task-date span {
/* color: #ff6d75;*/
}
.ant-steps-item-description {
max-width: 200px !important;
}
/** Button按钮间距 */
.ant-btn {
margin-left: 3px;
}
/** 标题和描述对齐 */
.ant-steps-item-content {
text-align: left;
margin-left: 50px;
}
/** 描述的样式 */
.descriptionDiv {
display: flex;
justify-content: left;
align-items: center;
margin-top: 5px;
}
.jee-cust-selector .ant-select-selection__choice {
padding-right: 10px !important;
}
}
</style>
@@ -0,0 +1,124 @@
<template>
<a-card>
<a-steps progressDot :current="stepIndex" style="padding: 10px" size="default">
<template v-if="stepCount > 3">
<a-step>
<template #title>
<div class="task-title">...</div>
</template>
</a-step>
</template>
<template v-for="(item, index) in taskStepList">
<a-step>
<template #title>
<div class="task-title">{{ item.taskName }}</div>
</template>
<template #description>
<div class="descriptionDiv">
<span>
<!--#40a9ff-->
<a-avatar shape="square" style="background-color: #40a9ff"
><template #icon><UserOutlined /></template
></a-avatar>
</span>
<span style="margin-left: 5px">
<div class="task-date" style="text-align: left">
<a-tooltip placement="top">
<template #title>
<span>{{ item.opTime }}</span>
</template>
<span> {{ item.opTime ? item.opTime.substr(0, 10) : item.opTime }}</span>
</a-tooltip>
</div>
<div class="task-user" style="text-align: left">
<span> {{ item.opUserName }}</span>
</div>
</span>
</div>
</template>
</a-step>
</template>
<a-step v-if="currentNode.taskName && currentNode.taskName != ''">
<template #title>
<div class="task-title">{{ currentNode.taskName }}</div>
</template>
<template #description
><!--#faad14eb-->
<div class="descriptionDiv">
<span>
<a-avatar style="background-color: #faad14eb"
><template #icon><UserOutlined /></template
></a-avatar>
</span>
<span style="margin-left: 5px">
<div class="task-date" style="text-align: left">
<a-tooltip placement="top">
<template #title>
<span>{{ currentNode.taskNameStartTime }}</span>
</template>
<span style="color: #ff6d75">
{{ currentNode.taskNameStartTime ? currentNode.taskNameStartTime.substr(0, 10) : currentNode.taskNameStartTime }}
</span>
</a-tooltip>
</div>
<div class="task-user" style="text-align: left">
<span> {{ currentNode.taskAssigneeName }}</span>
</div>
</span>
</div>
</template>
</a-step>
<a-step>
<template #title>
<div class="task-title">...</div>
</template>
</a-step>
</a-steps>
</a-card>
</template>
<script>
/**
* top上节点步骤
*/
import { computed } from 'vue';
import { UserOutlined } from '@ant-design/icons-vue';
export default {
name: 'TaskNodeStepList',
components: {
UserOutlined,
},
props: {
stepCount: {
type: Number,
default: 0,
},
taskStepList: {
type: Array,
default: () => [],
},
currentNode: {
type: Object,
default: () => {},
},
},
setup(props) {
const stepIndex = computed(() => {
if (props.taskStepList.length > 3) {
return props.taskStepList.length + 1;
}
return props.taskStepList.length;
});
return {
stepIndex,
};
},
};
</script>
@@ -0,0 +1,93 @@
<template>
<div>
<div style="margin-bottom: 20px; height: 400px; overflow: hidden; overflow-y: auto; overflow-x: auto">
<bpm-graphic :instanceId="formData.procInsId" @task="getTaskList"></bpm-graphic>
</div>
<a-card title="流程历史跟踪">
<BasicTable @register="registerTable">
<template #remarks="{ record }">
<j-ellipsis :value="getNodeInfo(record)" :length="25" />
</template>
</BasicTable>
</a-card>
</div>
</template>
<script>
/**
* 历史任务跟踪-查看历史节点-流程图
*/
import BpmGraphic from '/@/views/super/bpm/process/components/BpmGraphic.vue';
import { BasicTable } from '/@/components/Table';
import { taskTraceColumns } from '../task.handle.data';
import { taskTraceList } from '../task.handle.api';
import { useListPage } from '/@/hooks/system/useListPage';
import JEllipsis from '/@/components/Form/src/jeecg/components/JEllipsis.vue';
import { ref } from 'vue';
export default {
name: 'TaskTraceContent',
components: {
BpmGraphic,
BasicTable,
JEllipsis,
},
props: {
formData: {
type: Object,
default: () => {},
},
},
setup(props) {
console.log('进入TaskTraceContent>>');
const { tableContext } = useListPage({
designScope: 'task-trace-content',
pagination: true,
tableProps: {
title: '',
api: taskTraceList,
columns: taskTraceColumns,
showIndexColumn: true,
showTableSetting: false,
canResize: false,
showActionColumn: false,
useSearchForm: false,
beforeFetch: (params) => {
return addQueryParams(params);
},
},
});
const [registerTable] = tableContext;
function addQueryParams(params) {
params.processInstanceId = props.formData.procInsId;
return params;
}
const taskList = ref([]);
function getTaskList(arr) {
console.log('aaa', arr);
taskList.value = arr;
}
function getNodeInfo(record) {
let arr = taskList.value;
if (arr && arr.length > 0) {
for (let item of arr) {
if (item.id == record.id) {
return item.remarks;
}
}
}
return '';
}
return {
registerTable,
getTaskList,
getNodeInfo,
};
},
};
</script>
<style scoped></style>
@@ -0,0 +1,88 @@
<template>
<BasicModal title="选择节点审批人" @register="registerModal" :minHeight="80" :width="900" @ok="selected" :canFullscreen="false">
<tr>
<td>
<span> 加签方式</span>
</td>
<td colspan="3" style="width: 600px; margin: 0 auto; padding-top: 25px">
<a-radio-group v-model:value="addSignType">
<a-radio value="before">
<a-tooltip>
<template #title>需要他人核对流程其他人核对完成后回到当前节点处理人手中</template>
向前加签
</a-tooltip>
</a-radio>
<a-radio value="after">
<a-tooltip>
<template #title>需要让他人核对流程其他人核对完成后直接进入下一节点</template>
向后加签
</a-tooltip>
</a-radio>
</a-radio-group>
</td>
</tr>
<tr>
<td>
<span> 选择用户</span>
</td>
<td colspan="3" style="width: 600px; margin: 0 auto; padding-top: 25px">
<j-select-user-by-dept v-model:value="person" button-icon="ant-design:search" placeholder="请选择人员" />
</td>
</tr>
</BasicModal>
</template>
<script>
/**
* 选择加签节点审批人
*/
import { BasicModal, useModalInner } from "/@/components/Modal";
import { ref, nextTick, unref } from "vue";
import { JSelectUserByDept } from "/@/components/Form";
import { useUserStore } from "/@/store/modules/user";
import { useMessage } from "/@/hooks/web/useMessage";
export default {
name: "AddSignTaskModal",
components: {
BasicModal,
JSelectUserByDept
},
emits: ["selected", "register"],
setup(_p, { emit }) {
//useModalInner
const person = ref("");
const taskId = ref("");
const addSignType = ref("after");
const { userInfo } = useUserStore();
const { createMessage } = useMessage();
const [registerModal, { closeModal }] = useModalInner((data) => {
person.value = "";
taskId.value = data.taskId;
});
function selected() {
let signUserIds= unref(person);
if(signUserIds.length==0){
createMessage.warning('请选择审批人')
return;
}
emit("selected", {
currentTaskId: taskId.value,
signUserIds: signUserIds,
userCode: userInfo.username
},addSignType.value);
closeModal();
}
return {
registerModal,
person,
selected,
addSignType
};
}
};
</script>
<style scoped></style>
@@ -0,0 +1,57 @@
<template>
<BasicModal title="选择委托/转办人" @register="registerModal" :minHeight="80" :width="900" @ok="selected" :canFullscreen="false">
<tr>
<td>
<span style="margin-left: 30px"> 选择用户</span>
</td>
<td colspan="3" style="width: 600px; margin: 0 auto; padding-top: 25px">
<j-select-user-by-dept v-model:value="person" button-icon="ant-design:search" :isRadioSelection="true" placeholder="请选择人员" />
</td>
</tr>
</BasicModal>
</template>
<script>
/**
* 选择委托人
*/
import { BasicModal, useModalInner } from '/@/components/Modal';
import { ref, nextTick, unref } from 'vue';
import { JSelectUserByDept } from '/@/components/Form';
export default {
name: 'SelectEntrusterModal',
components: {
BasicModal,
JSelectUserByDept,
},
emits: ['selected', 'register'],
setup(_p, { emit }) {
//useModalInner
const person = ref('');
const taskId = ref('');
const [registerModal, { closeModal }] = useModalInner((data) => {
person.value = '';
taskId.value = data.taskId;
});
function selected() {
let temp = unref(person);
if (temp instanceof Array) {
temp = temp[0];
}
emit('selected', {
taskId: taskId.value,
taskAssignee: temp,
});
closeModal();
}
return {
registerModal,
person,
selected,
};
},
};
</script>
<style scoped></style>
@@ -0,0 +1,133 @@
<template>
<BasicModal
:title="title"
@register="registerModal"
destroyOnClose
wrapClassName="jeecg-online-modal"
style="top: 0px"
:footer="null"
keyboard
defaultFullscreen
:canFullscreen="false"
ref="modalRef"
>
<a-tabs v-model="activeKey" tabPosition="left">
<a-tab-pane key="1">
<template #tab> <file-text-outlined /><span>附加单据</span> </template>
<BpmDynamicForm :path="taskFormUrl" :form-data="taskFormData" :parentNode="parentNode"/>
</a-tab-pane>
<a-tab-pane key="2">
<template #tab> <user-outlined /><span>任务处理</span> </template>
<task-handle-inner-content @success="handleSuccess" :form-data="taskFormData" :claim="claimStatus" @claimSuccess="claimSuccess"></task-handle-inner-content>
</a-tab-pane>
<a-tab-pane key="3">
<template #tab> <partition-outlined /><span>流程图</span> </template>
<task-trace-content :form-data="taskFormData" />
</a-tab-pane>
</a-tabs>
</BasicModal>
</template>
<script lang="ts">
/**
* 处理页面
*/
import { BasicModal, useModalInner } from '/@/components/Modal';
import { ref , nextTick , computed } from 'vue';
import { UserOutlined, PartitionOutlined, FileTextOutlined } from '@ant-design/icons-vue';
import TaskHandleInnerContent from '../content/TaskHandleInnerContent.vue';
import TaskTraceContent from '../content/TaskTraceContent.vue';
import BpmDynamicForm from '/@/views/super/bpm/process/components/BpmDynamicForm.vue';
export default {
name: 'TaskHandleModal',
components: {
BasicModal,
UserOutlined,
FileTextOutlined,
PartitionOutlined,
TaskHandleInnerContent,
TaskTraceContent,
BpmDynamicForm,
},
emits: ['success', 'register'],
setup(_p, { emit }) {
const title = ref('流程办理');
const taskFormData = ref({});
const taskFormUrl = ref('');
//是否需要签收
const claimStatus = ref(false);
//useModalInner
const [registerModal, { closeModal }] = useModalInner((data) => {
console.log('流程办理', data);
taskFormData.value = data.formData;
taskFormUrl.value = data.formUrl;
title.value = data.title;
if(data.isSignTask==true){
claimStatus.value = true
}else{
claimStatus.value = false;
}
});
const activeKey = ref('1');
// desformView 父级滚动条,只有传了此参数才会突破内部弹窗
const modalRef = ref();
const parentNode = computed(() => {
return modalRef.value?.modalWrapperRef?.wrapperRef?.scrollbarRef?.wrap;
});
console.log("parentNode",parentNode)
function handleSuccess() {
emit('success');
closeModal();
}
function claimSuccess(){
claimStatus.value = false;
}
return {
title,
registerModal,
activeKey,
taskFormData,
taskFormUrl,
handleSuccess,
claimStatus,
claimSuccess,
modalRef,
parentNode
};
},
};
</script>
<style lang="less" scoped>
// update-begin--author:liaozhiyang---date:20231220---for:【QQYUN-7670】antd4兼容改造,附加单据页面loading图标偏上
.ant-tabs {
:deep(.ant-spin-nested-loading) {
min-height: 200px;
}
}
// update-end--author:liaozhiyang---date:20231220---for:【QQYUN-7670】antd4兼容改造,附加单据页面loading图标偏上
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-1022】流程办理页面左侧固定不随滚动而滚动
.ant-tabs {
height: 100%;
:deep(.ant-tabs-content-holder) {
height: 100%;
.ant-tabs-content {
height: 100%;
.ant-tabs-tabpane {
height: 100%;
padding-right: 6px;
overflow-y: auto;
}
}
}
}
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-1022】流程办理页面左侧固定不随滚动而滚动
</style>
@@ -0,0 +1,70 @@
<template>
<BasicModal title="催办提醒" @register="registerModal" width="60%">
<a-tabs defaultActiveKey="1" tabPosition="top">
<a-tab-pane key="1">
<template #tab> <alert-outlined /><span>提醒我的</span> </template>
<BasicTable @register="registerTable" />
</a-tab-pane>
</a-tabs>
</BasicModal>
</template>
<script>
import { BasicModal, useModalInner } from '/@/components/Modal';
import { ref, nextTick } from 'vue';
import { AlertOutlined } from '@ant-design/icons-vue';
import { BasicTable } from '/@/components/Table';
import { useListPage } from '/@/hooks/system/useListPage';
import { taskNotifyMeList } from '../task.handle.api';
import { notifyMeColumns } from '../task.handle.data';
export default {
name: 'TaskNotifyMeModal',
components: {
BasicModal,
AlertOutlined,
BasicTable,
},
setup() {
//useModalInner
const procInstId = ref('');
const { tableContext } = useListPage({
designScope: 'notify-me-list',
pagination: true,
tableProps: {
api: taskNotifyMeList,
columns: notifyMeColumns,
immediate: false,
showIndexColumn: true,
showTableSetting: false,
useSearchForm: false,
canResize: false,
showActionColumn: false,
beforeFetch: (params) => {
return addQueryParams(params);
},
},
});
const [registerTable, { reload }] = tableContext;
const [registerModal] = useModalInner((data) => {
console.log(data);
procInstId.value = data.procInstId;
reload();
});
function addQueryParams(params) {
params['procInstId'] = procInstId.value;
return params;
}
return {
registerModal,
registerTable,
};
},
};
</script>
<style scoped></style>
@@ -0,0 +1,103 @@
import { defHttp } from '/@/utils/http/axios';
const URL = {
run: {
list: '/act/task/list',
claim: '/act/task/claim',
taskEntrust: '/act/task/taskEntrust',
getProcessNodeInfo: '/act/process/extActProcessNode/getProcessNodeInfo',
getProcessTaskTransInfo: '/act/task/getProcessTaskTransInfo',
},
history: {
list: '/act/task/taskHistoryList',
getProcessNodeInfo: '/act/process/extActProcessNode/getHisProcessNodeInfo',
getProcessTaskTransInfo: '/act/task/getHisProcessTaskTransInfo',
},
group: {
list: '/act/task/taskGroupList',
claim: '/act/task/claim',
getProcessTaskTransInfo: '/act/task/getProcessTaskTransInfo',
},
processComplete: '/act/task/processComplete',
processHistoryList: '/act/task/processHistoryList',
taskEntrust: '/act/task/taskEntrust',
taskComplaint: '/act/task/taskComplaint',
afterAddSignTask: '/act/task/afterAddSignTask',
beforeAddSignTask: '/act/task/beforeAddSignTask',
claim: '/act/task/claim',
notifyMeList: '/act/process/extActTaskNotification/list',
// 添加审批人
addMultiInstance: '/act/task/addMultiInstance',
};
/**
* 列表
* @param params
*/
export const list = (type, params) => defHttp.get({ url: URL[type].list, params });
/**
* 列表
* @param params
*/
export const taskNodeInfo = (type, params) => defHttp.get({ url: URL[type].getProcessNodeInfo, params });
/**
* 任务流转信息
* @param params
* @param type
*/
export const taskTransInfo = (params, type = 'run') => defHttp.get({ url: URL[type].getProcessTaskTransInfo, params });
/**
* 流程任务办理
* @param params
*/
export const taskComplete = (params) => defHttp.post({ url: URL.processComplete, params });
/**
* 流程历史跟踪
* @param params
*/
export const taskTraceList = (params) => defHttp.get({ url: URL.processHistoryList, params });
/**
* 任务委托
* @param params
*/
export const taskEntrust = (params) => defHttp.put({ url: URL.taskEntrust, params });
/**
* 任务转办
* @param params
*/
export const taskComplaint = (params) => defHttp.put({ url: URL.taskComplaint, params });
/**
* 任务签收
* @param params
*/
export const taskClaim = (params) => defHttp.put({ url: URL.claim, params });
/**
* 催办-提醒我的记录
* @param params
*/
export const taskNotifyMeList = (params) => defHttp.get({ url: URL.notifyMeList, params });
/**
* 任务向后加签
* @param params
*/
export const afterAddSignTask = (params) => defHttp.put({ url: URL.afterAddSignTask, params });
/**
* 任务向前加签
* @param params
*/
export const beforeAddSignTask = (params) => defHttp.put({ url: URL.beforeAddSignTask, params });
/**
* 添加审批人
* @param params
*/
export const addMultiInstance = (params) => defHttp.put({ url: URL.addMultiInstance, params },{isTransformResponse: false});
@@ -0,0 +1,308 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
export enum TaskType {
RUN = 'run',
HIS = 'history',
GROUP = 'group',
}
/**
* 列表 列--running
*/
export const columns: BasicColumn[] = [
{
title: '业务标题',
align: 'center',
dataIndex: 'bpmBizTitle',
slots: { customRender: 'bpmBizTitle' },
},
{
title: '当前环节',
align: 'center',
width: 130,
dataIndex: 'taskName',
},
{
title: '流程名称',
align: 'center',
dataIndex: 'processDefinitionName',
ellipsis: true,
},
{
title: '流程实例',
align: 'center',
dataIndex: 'processInstanceId',
},
{
title: '发起人',
width: 110,
align: 'center',
dataIndex: 'processApplyUserName',
},
{
title: '开始时间',
align: 'center',
dataIndex: 'taskBeginTime',
},
{
title: '流程编号',
align: 'center',
dataIndex: 'processDefinitionId',
ellipsis: true,
},
{
title: '任务ID',
align: 'center',
dataIndex: 'taskId',
},
];
/**
* 列表 列--group
*/
export const columns_group: BasicColumn[] = [
{
title: '业务标题',
align: 'center',
dataIndex: 'bpmBizTitle',
ellipsis: true,
},
{
title: '流程编号',
align: 'center',
dataIndex: 'processDefinitionId',
},
{
title: '流程名称',
align: 'center',
dataIndex: 'processDefinitionName',
},
{
title: '流程实例',
align: 'center',
dataIndex: 'processInstanceId',
},
{
title: '任务ID',
align: 'center',
dataIndex: 'taskId',
},
{
title: '发起人',
align: 'center',
dataIndex: 'processApplyUserName',
},
{
title: '开始时间',
align: 'center',
dataIndex: 'taskBeginTime',
},
{
title: '结束时间',
align: 'center',
dataIndex: 'taskEndTime',
},
{
title: '当前环节',
align: 'center',
dataIndex: 'taskName',
},
];
/**
* 列表 列-history
*/
export const columns_history: BasicColumn[] = [
{
title: '业务标题',
align: 'center',
dataIndex: 'bpmBizTitle',
ellipsis: true,
},
{
title: '流程编号',
align: 'center',
dataIndex: 'processDefinitionId',
ellipsis: true,
},
{
title: '流程名称',
align: 'center',
dataIndex: 'processDefinitionName',
ellipsis: true,
},
{
title: '流程实例',
align: 'center',
width: 100,
dataIndex: 'processInstanceId',
},
{
title: '任务ID',
align: 'center',
dataIndex: 'taskId',
},
{
title: '发起人',
align: 'center',
width: 100,
dataIndex: 'processApplyUserName',
},
{
title: '办理人',
align: 'center',
width: 100,
dataIndex: 'taskAssigneeName',
},
{
title: '开始时间',
align: 'center',
dataIndex: 'taskBeginTime',
},
{
title: '结束时间',
align: 'center',
dataIndex: 'taskEndTime',
},
{
title: '耗时',
align: 'center',
width: 100,
dataIndex: 'durationStr',
},
// {
// title: '当前环节',
// align: 'center',
// width: 120,
// dataIndex: 'taskName',
// },
];
/**
* 查询条件
*/
export const searchFormSchema: FormSchema[] = [
{
label: '业务标题',
field: 'bpmBizTitle',
component: 'Input',
},
{
label: '流程名',
field: 'processDefinitionName',
component: 'Input',
},
{
label: '发起人',
field: 'userName',
component: 'JSelectUserByDept',
componentProps: {
labelKey: 'realname',
rowKey: 'username',
showButton: false,
isRadioSelection: true,
},
buss: 'run',
},
{
label: '流程编号',
field: 'processDefinitionId',
component: 'Input',
},
// {
// label: '应用ID',
// field: 'lowAppId',
// component: 'Input',
// },
];
/**
* 列表 流程历史跟踪
*/
export const taskTraceColumns: BasicColumn[] = [
{
title: '名称',
dataIndex: 'name',
customRender: function ({ text }) {
if (text == 'start1') {
return '开始';
} else if (text == 'end') {
return '结束';
} else {
return text;
}
},
},
{
title: '流程实例ID',
dataIndex: 'processInstanceId',
},
{
title: '开始时间',
dataIndex: 'startTime',
},
{
title: '结束时间',
dataIndex: 'endTime',
},
{
title: '负责人',
dataIndex: 'assigneeName',
},
{
title: '处理结果',
dataIndex: 'deleteReason',
},
{
title: '处理意见',
fixed: 'right',
width: 350,
dataIndex: 'remarks',
slots: { customRender: 'remarks' },
},
];
/**
* 提醒我的列表(催办)
*/
export const notifyMeColumns: BasicColumn[] = [
{
title: '流程名称',
align: 'center',
dataIndex: 'procName',
},
{
title: '任务名称',
align: 'center',
dataIndex: 'taskName',
},
{
title: '催办时间',
align: 'center',
dataIndex: 'opTime',
},
{
title: '催办类型',
align: 'center',
dataIndex: 'notifyType',
customRender: function ({ text }) {
if (!text) {
return '';
}
let srtArr = text.split(',');
let value = '';
if (srtArr.includes('1')) {
value += ',页面通知';
}
if (srtArr.includes('2')) {
value += ',邮件';
}
return value.substring(1);
},
},
{
title: '催办说明',
align: 'center',
dataIndex: 'remarks',
},
];
@@ -0,0 +1,179 @@
import { useListPage } from '/@/hooks/system/useListPage';
import { columns, columns_group, columns_history, TaskType, searchFormSchema } from './task.handle.data';
import { list, taskNodeInfo } from './task.handle.api';
/**
* 用于列表渲染
* @param urlObject
*/
export function useTaskList(type) {
const { tableContext } = useListPage({
designScope: 'process-design',
pagination: true,
tableProps: {
title: '',
api: getDataList,
columns: getColumns(),
showIndexColumn: true,
showTableSetting: false,
canResize: false,
scroll: { x: 1800 },
actionColumn: { dataIndex: 'action', fixed: 'right',width:150 },
useSearchForm: TaskType.GROUP != type,
formConfig: {
schemas: getSearchFormSchema(),
autoAdvancedCol: 3,
baseColProps: { xs: 24, sm: 12, md: 6, lg: 6, xl: 6, xxl: 6 },
actionColOptions: { xs: 24, sm: 12, md: 6, lg: 6, xl: 6, xxl: 6 },
},
},
});
const [registerTable, { reload }] = tableContext;
/**
* 数据请求接口
* @param params
*/
function getDataList(params) {
return list(type, params);
}
/**
* 列
* 不同类型 列表列有少许差别
*/
function getColumns() {
if (TaskType.RUN == type) {
return columns;
} else if (TaskType.GROUP == type) {
return columns_group;
} else {
return columns_history;
}
}
/**
* 查询条件
*/
function getSearchFormSchema() {
return searchFormSchema.filter((item) => !item.buss || item.buss === type);
}
/**
* 获取流程节点信息
*/
async function getTaskNodeInfo(record) {
//查询条件
let params = { taskId: record.id };
const result = await taskNodeInfo(type, params);
console.log('获取流程节点信息', result);
let procInsId = record.processInstanceId || (result.records?result.records.BPM_INST_ID: '');
let formData: any = {
taskId: record.id,
taskDefKey: result.taskDefKey,
procInsId: procInsId,
dataId: result.dataId,
tableName: result.tableName,
permissionList: result.permissionList,
subPermissionList: result.subPermissionList,
vars: result.records,
};
let tempFormUrl = result.formUrl;
console.log('获取流程节点表单URL', tempFormUrl);
//节点配置表单URL,VUE组件类型对应的拓展参数
if (tempFormUrl && tempFormUrl.indexOf('?') != -1 && !isURL(tempFormUrl) && tempFormUrl.indexOf('{{DOMAIN_URL}}') == -1) {
tempFormUrl = result.formUrl.split('?')[0];
console.log('获取流程节点表单URL(去掉参数)', tempFormUrl);
let qv: any = getQueryVariable(result.formUrl);
if (qv.edit == 1) {
formData['disabled'] = false;
}
formData.extendUrlParams = qv;
}
//如果没有taskId参数,程序自动追加,用于设计器表单节点权限
if (tempFormUrl != null && tempFormUrl.indexOf('{{DOMAIN_URL}}/desform/') != -1 && tempFormUrl.indexOf('taskId') == -1) {
tempFormUrl = tempFormUrl.trim();
if (tempFormUrl.endsWith('?')) {
tempFormUrl = tempFormUrl + 'taskId=' + result.taskDefKey;
} else {
tempFormUrl = tempFormUrl + '&taskId=' + result.taskDefKey;
}
}
return {
formData,
formUrl: tempFormUrl,
isSignTask: result.isSignTask,
assignee: result.assignee,
taskIsHandel: result.taskIsHandel||false,
};
}
/**
* 获取历史任务信息
*/
async function getHistoryTaskInfo(record) {
return await getTaskInfoForHistory(record);
}
return {
registerTable,
reload,
getTaskNodeInfo,
getHistoryTaskInfo,
};
}
export async function getTaskInfoForHistory(record) {
//查询条件
let params = { procInstId: record.processInstanceId };
const result = await taskNodeInfo('history', params);
console.log('获取历史任务信息', result);
let formData: any = {
dataId: result.dataId,
taskId: record.id,
taskDefKey: record.taskId,
procInsId: record.processInstanceId,
tableName: result.tableName,
vars: result.records,
};
let tempFormUrl = result.formUrl;
console.log('获取流程节点表单URL', tempFormUrl);
//节点配置表单URL,VUE组件类型对应的拓展参数
if (tempFormUrl && tempFormUrl.indexOf('?') != -1 && !isURL(tempFormUrl) && tempFormUrl.indexOf('{{DOMAIN_URL}}') == -1) {
tempFormUrl = result.formUrl.split('?')[0];
console.log('获取流程节点表单URL(去掉参数)', tempFormUrl);
formData.extendUrlParams = getQueryVariable(result.formUrl);
}
return {
formData,
formUrl: tempFormUrl,
};
}
/**
* 获取URL上参数
* @param url
*/
function getQueryVariable(url) {
if (!url) return;
let t,
n,
r,
i = url.split('?')[1],
s = {};
(t = i.split('&')), (r = null), (n = null);
for (let o in t) {
let u = t[o].indexOf('=');
u !== -1 && ((r = t[o].substr(0, u)), (n = t[o].substr(u + 1)), (s[r] = n));
}
return s;
}
/**
* URL地址
* @param {*} s
*/
function isURL(s) {
return /^http[s]?:\/\/.*/.test(s);
}