455 lines
13 KiB
Vue
455 lines
13 KiB
Vue
<template>
|
||
<div class="my-work">
|
||
<div class="my-work__header">
|
||
<h2 class="my-work__title">我的工作</h2>
|
||
<div class="my-work__actions">
|
||
<div class="my-work__tabs">
|
||
<div class="my-work__tab" :class="{ 'my-work__tab--active': activeTab === 'todo' }" @click="activeTab = 'todo'">
|
||
待办
|
||
<span class="my-work__badge my-work__badge--danger">{{ todoTotal }}</span>
|
||
</div>
|
||
<div class="my-work__tab" :class="{ 'my-work__tab--active': activeTab === 'cc' }" @click="activeTab = 'cc'">我的抄送</div>
|
||
</div>
|
||
<ReloadOutlined class="my-work__refresh" :class="{ 'my-work__refresh--spinning': refreshing }" @click="handleRefresh" />
|
||
<a class="my-work__more" @click="goMore">
|
||
更多
|
||
<RightOutlined class="my-work__more-icon" />
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="my-work__body">
|
||
<template v-if="activeTab === 'todo'">
|
||
<BasicTable @register="registerTodoTable">
|
||
<template #headerCell="{ column }">
|
||
<HeaderCell v-if="column.dataIndex !== 'action'" :column="column" />
|
||
<span v-else class="action-header">
|
||
操作
|
||
<Tooltip placement="topLeft" :overlayStyle="{ maxWidth: '300px' }">
|
||
<template #title>
|
||
<div>
|
||
<b>转办</b>:将任务转交他人办理,对方办完后流程继续流转到下一环节,不再回到本人。<br />
|
||
<b>委托</b>:将任务委托他人代办,对方办完后任务会回到本人,由本人继续完成流程。
|
||
</div>
|
||
</template>
|
||
<QuestionCircleOutlined class="action-header__icon" />
|
||
</Tooltip>
|
||
</span>
|
||
</template>
|
||
<template #action="{ record }">
|
||
<TableAction :actions="getTodoTableAction(record)" />
|
||
</template>
|
||
</BasicTable>
|
||
</template>
|
||
<template v-if="activeTab === 'cc'">
|
||
<BasicTable @register="registerCcTable">
|
||
<template #action="{ record }">
|
||
<TableAction :actions="getCcTableAction(record)" />
|
||
</template>
|
||
</BasicTable>
|
||
</template>
|
||
</div>
|
||
|
||
<task-handle-modal @register="registerHandleModal" @success="reloadTodo" />
|
||
<SingleDeptUserModal title="选择委托人" @register="registerEntrusterModal" @selected="selectedEntruster" />
|
||
<SingleDeptUserModal title="选择转办人" @register="registerComplaintModal" @selected="selectedComplaint" />
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { ref, onMounted } from 'vue';
|
||
import { useRouter } from 'vue-router';
|
||
import { BasicTable, TableAction } from '/@/components/Table';
|
||
import HeaderCell from '/@/components/Table/src/components/HeaderCell.vue';
|
||
import { useListPage } from '/@/hooks/system/useListPage';
|
||
import { useModal } from '/@/components/Modal';
|
||
import { RightOutlined, QuestionCircleOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
||
import { Tooltip } from 'ant-design-vue';
|
||
import TaskHandleModal from '/@/views/super/bpm/process/personalOffice/myHandleTask/modal/TaskHandleModal.vue';
|
||
import SingleDeptUserModal from '/@/components/semri/singleDeptUserComponent/SingleDeptUserModal.vue';
|
||
import { getTaskInfoForHistory } from '/@/views/super/bpm/process/personalOffice/myHandleTask/useTaskList';
|
||
import { todoColumns } from '../data/todo.data';
|
||
import { ccColumns } from '../data/cc.data';
|
||
import {
|
||
list as todoList,
|
||
taskNodeInfo,
|
||
taskEntrust,
|
||
taskClaim,
|
||
taskComplaint,
|
||
} from '/@/views/super/bpm/process/personalOffice/myHandleTask/task.handle.api';
|
||
import { list as ccList } from '/@/views/super/bpm/process/personalOffice/myCcTask/task.cc.api';
|
||
|
||
const router = useRouter();
|
||
const activeTab = ref<'todo' | 'cc'>('todo');
|
||
const todoTotal = ref(0);
|
||
const refreshing = ref(false);
|
||
|
||
const emit = defineEmits(['refresh']);
|
||
|
||
const { tableContext: todoContext } = useListPage({
|
||
designScope: 'process-portal-todo',
|
||
pagination: true,
|
||
tableProps: {
|
||
api: (params) => todoList('run', params),
|
||
columns: todoColumns,
|
||
showIndexColumn: true,
|
||
showTableSetting: false,
|
||
canResize: false,
|
||
scroll: { x: 900 },
|
||
actionColumn: {
|
||
dataIndex: 'action',
|
||
fixed: 'right',
|
||
width: 180,
|
||
},
|
||
useSearchForm: false,
|
||
},
|
||
});
|
||
const [registerTodoTable, { reload: reloadTodo }] = todoContext;
|
||
|
||
const { tableContext: ccContext } = useListPage({
|
||
designScope: 'process-portal-cc',
|
||
pagination: true,
|
||
tableProps: {
|
||
api: ccList,
|
||
columns: ccColumns,
|
||
showIndexColumn: true,
|
||
showTableSetting: false,
|
||
canResize: false,
|
||
scroll: { x: 900 },
|
||
actionColumn: { dataIndex: 'action', fixed: 'right', width: 120 },
|
||
useSearchForm: false,
|
||
},
|
||
});
|
||
const [registerCcTable] = ccContext;
|
||
|
||
onMounted(() => {
|
||
fetchTodoTotal();
|
||
});
|
||
|
||
async function fetchTodoTotal() {
|
||
try {
|
||
const res = await todoList('run', { pageNo: 1, pageSize: 1 });
|
||
todoTotal.value = res?.total ?? 0;
|
||
} catch (e) {
|
||
todoTotal.value = 0;
|
||
}
|
||
}
|
||
|
||
async function handleRefresh() {
|
||
refreshing.value = true;
|
||
try {
|
||
reloadTodo();
|
||
await fetchTodoTotal();
|
||
emit('refresh');
|
||
} finally {
|
||
refreshing.value = false;
|
||
}
|
||
}
|
||
|
||
function goMore() {
|
||
if (activeTab.value === 'todo') {
|
||
router.push('/task/myTodoTaskInfo');
|
||
} else {
|
||
router.push('/task/myCcTaskListInfo');
|
||
}
|
||
}
|
||
|
||
const [registerHandleModal, { openModal: openHandleModal }] = useModal();
|
||
const [registerEntrusterModal, { openModal: openEntrusterModal }] = useModal();
|
||
const [registerComplaintModal, { openModal: openComplaintModal }] = useModal();
|
||
|
||
async function handleProcess(record) {
|
||
let { formData, formUrl } = await getTaskNodeInfo(record);
|
||
formData['PROCESS_TAB_TYPE'] = 'run';
|
||
openHandleModal(true, { formData, formUrl, title: '流程办理' });
|
||
}
|
||
|
||
async function getTaskNodeInfo(record) {
|
||
let params = { taskId: record.id };
|
||
const result = await taskNodeInfo('run', params);
|
||
let procInsId = record.processInstanceId || (result.records ? result.records.BPM_INST_ID : '');
|
||
let realDataId = (result.records && result.records.business_id) || result.dataId;
|
||
let realTableName = (result.records && result.records.business_tablename) || result.tableName;
|
||
let formData: any = {
|
||
taskId: record.id,
|
||
taskDefKey: result.taskDefKey,
|
||
procInsId: procInsId,
|
||
dataId: realDataId,
|
||
tableName: realTableName,
|
||
processDataId: result.dataId,
|
||
processTableName: result.tableName,
|
||
permissionList: result.permissionList,
|
||
subPermissionList: result.subPermissionList,
|
||
vars: result.records,
|
||
};
|
||
let tempFormUrl = result.formUrl;
|
||
//节点配置表单URL,VUE组件类型对应的拓展参数
|
||
if (tempFormUrl && tempFormUrl.indexOf('?') != -1 && !isURL(tempFormUrl) && tempFormUrl.indexOf('{{DOMAIN_URL}}') == -1) {
|
||
tempFormUrl = result.formUrl.split('?')[0];
|
||
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 };
|
||
}
|
||
|
||
function isURL(s) {
|
||
return /^http[s]?:\/\/.*/.test(s);
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
async function handleClaim(record) {
|
||
await taskClaim({ taskId: record.id });
|
||
await reloadTodo();
|
||
await fetchTodoTotal();
|
||
}
|
||
|
||
function handleSelectEntruster(record) {
|
||
openEntrusterModal(true, { taskId: record.id });
|
||
}
|
||
|
||
function handleComplaintEntruster(record) {
|
||
openComplaintModal(true, { taskId: record.id });
|
||
}
|
||
|
||
async function selectedEntruster(params) {
|
||
await taskEntrust(params);
|
||
await reloadTodo();
|
||
}
|
||
|
||
async function selectedComplaint(params) {
|
||
await taskComplaint(params);
|
||
await reloadTodo();
|
||
}
|
||
|
||
async function showCcHistory(record) {
|
||
let { formData, formUrl } = await getTaskInfoForHistory(record);
|
||
formData['PROCESS_TAB_TYPE'] = 'history';
|
||
openHandleModal(true, { formData, formUrl, title: '流程历史' });
|
||
}
|
||
|
||
function getTodoTableAction(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;
|
||
}
|
||
|
||
function getCcTableAction(record) {
|
||
return [{ label: '查看详情', onClick: showCcHistory.bind(null, record) }];
|
||
}
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.my-work {
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
border: 1px solid #f0f0f0;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
|
||
margin-bottom: 16px;
|
||
overflow: hidden;
|
||
|
||
&__header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 12px 20px;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
|
||
&__title {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: rgba(0, 0, 0, 0.85);
|
||
margin: 0;
|
||
}
|
||
|
||
&__refresh {
|
||
font-size: 14px;
|
||
color: rgba(0, 0, 0, 0.45);
|
||
cursor: pointer;
|
||
transition: color 0.2s;
|
||
|
||
&:hover {
|
||
color: @primary-color;
|
||
}
|
||
|
||
&--spinning {
|
||
animation: spin 0.8s linear infinite;
|
||
color: @primary-color;
|
||
cursor: default;
|
||
}
|
||
}
|
||
|
||
@keyframes spin {
|
||
from {
|
||
transform: rotate(0deg);
|
||
}
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
&__actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
}
|
||
|
||
&__tabs {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding: 3px;
|
||
background: #f5f5f5;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
&__tab {
|
||
padding: 5px 14px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
color: rgba(0, 0, 0, 0.45);
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
user-select: none;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
|
||
&:hover {
|
||
color: rgba(0, 0, 0, 0.65);
|
||
}
|
||
|
||
&--active {
|
||
background: #fff;
|
||
color: rgba(0, 0, 0, 0.85);
|
||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
|
||
}
|
||
}
|
||
|
||
&__badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 18px;
|
||
height: 18px;
|
||
padding: 0 5px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
border-radius: 9px;
|
||
line-height: 1;
|
||
|
||
&--danger {
|
||
background: #ff4d4f;
|
||
color: #fff;
|
||
}
|
||
}
|
||
|
||
&__more {
|
||
font-size: 13px;
|
||
color: #1890ff;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
white-space: nowrap;
|
||
|
||
&:hover {
|
||
color: #40a9ff;
|
||
}
|
||
}
|
||
|
||
&__more-icon {
|
||
font-size: 10px;
|
||
}
|
||
|
||
&__body {
|
||
padding: 12px 20px;
|
||
|
||
.action-header {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
|
||
&__icon {
|
||
color: rgba(0, 0, 0, 0.45);
|
||
cursor: help;
|
||
font-size: 14px;
|
||
|
||
&:hover {
|
||
color: @primary-color;
|
||
}
|
||
}
|
||
}
|
||
|
||
:deep(.ant-table) {
|
||
table {
|
||
table-layout: fixed;
|
||
width: 100%;
|
||
}
|
||
|
||
.ant-table-thead > tr > th {
|
||
background: #fafafa;
|
||
font-weight: 600;
|
||
font-size: 13px;
|
||
color: rgba(0, 0, 0, 0.65);
|
||
padding: 10px 12px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.ant-table-tbody > tr > td {
|
||
padding: 10px 12px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.ant-table-tbody > tr:hover > td {
|
||
background: #f0f7ff;
|
||
}
|
||
|
||
.ant-table-cell {
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|