feat: 更新请假申请表单,添加流程类型选择和流程启动功能

This commit is contained in:
AN
2025-05-31 23:48:36 +08:00
parent ab1d3a237e
commit 49521b667d
9 changed files with 345 additions and 15 deletions

View File

@ -37,11 +37,11 @@ const value = defineModel<CommonType.IdType[]>('value', { required: false, defau
let fileNum = 0;
const fileList = ref<UploadFileInfo[]>([]);
const needRelaodData = defineModel<boolean>('needRelaodData', {
default: false
});
const needRelaodData = ref<boolean>(false);
defineExpose({
refreshList: needRelaodData
refreshList: needRelaodData,
fileList
});
watch(
() => fileList.value,

View File

@ -0,0 +1,103 @@
<script setup lang="ts">
import { reactive, ref, watch } from 'vue';
import { messageTypeOptions } from '@/constants/workflow';
import { fetchCompleteTask, fetchGetTask } from '@/service/api/workflow';
import FileUpload from '@/components/custom/file-upload.vue';
defineOptions({
name: 'WorkflowTaskApplyModal'
});
interface Props {
/** the task id */
taskId: CommonType.IdType;
/** the task variables */
taskVariables: { [key: string]: any };
}
const props = defineProps<Props>();
interface Emits {
(e: 'finished'): void;
}
const emit = defineEmits<Emits>();
const visible = defineModel<boolean>('visible', {
default: false
});
const title = defineModel<string>('title', {
default: '流程发起'
});
const fileUploadRef = ref<InstanceType<typeof FileUpload> | null>(null);
const accept = ref<string>('.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.pdf,.jpg,.jpeg,.png,.gif,.bmp,.webp');
type Model = Api.Workflow.CompleteTaskOperateParams;
const task = ref<Api.Workflow.Task>();
const model: Model = reactive(createDefaultModel());
function createDefaultModel(): Model {
return {
taskId: null,
fileId: null,
flowCopyList: [],
messageType: ['1'],
taskVariables: {},
variables: {},
assigneeMap: {}
};
}
async function getTask() {
const { error, data } = await fetchGetTask(props.taskId);
if (error) return;
task.value = data;
}
async function handleSubmit() {
const fileList = fileUploadRef.value?.fileList;
if (fileList?.length) {
const fileIds = fileList.map(item => item.id);
model.fileId = fileIds.join(',');
}
model.taskId = props.taskId;
model.taskVariables = props.taskVariables;
const { error } = await fetchCompleteTask(model);
if (error) return;
window.$message?.success('提交成功');
visible.value = false;
emit('finished');
}
watch(visible, () => {
if (visible.value) {
getTask();
Object.assign(model, createDefaultModel());
}
});
</script>
<template>
<NModal v-model:show="visible" :title="title" :native-scrollbar="false" closable>
<NForm :model="model">
<NFormItem label="通知方式" path="messageType">
<NCheckboxGroup v-model:value="model.messageType">
<NSpace item-style="display: flex;">
<NCheckbox v-for="item in messageTypeOptions" :key="item.value" :value="item.value" :label="item.label" />
</NSpace>
</NCheckboxGroup>
</NFormItem>
<NFormItem label="附件" path="fileId">
<FileUpload ref="fileUploadRef" :file-size="20" :max="20" upload-type="file" :accept="accept" />
</NFormItem>
</NForm>
<template #footer>
<NButton @click="visible = false">取消</NButton>
<NButton type="primary" @click="handleSubmit">提交</NButton>
</template>
</NModal>
</template>

View File

@ -1,5 +1,24 @@
import { transformRecordToOption } from '@/utils/common';
export const messageTypeRecord: Record<Api.Workflow.MessageType, string> = {
'1': '站内信',
'2': '邮件',
'3': '短信'
};
export const messageTypeOptions = transformRecordToOption(messageTypeRecord);
export const flowCodeTypeRecord: Record<Api.Workflow.FlowCodeType, string> = {
leave1: '请假申请-普通',
leave2: '请假申请-排他网关',
leave3: '请假申请-并行网关',
leave4: '请假申请-会签',
leave5: '请假申请-并行会签网关',
leave6: '请假申请-排他并行会签'
};
export const flowCodeTypeOptions = transformRecordToOption(flowCodeTypeRecord);
/** leave type */
export const leaveTypeRecord: Record<Api.Workflow.LeaveType, string> = {
'1': '事假',

View File

@ -2,3 +2,4 @@ export * from './category';
export * from './leave';
export * from './instance';
export * from './definition';
export * from './task';

View File

@ -18,7 +18,7 @@ export function fetchGetLeaveDetail(id: CommonType.IdType) {
/** 新增请假申请 */
export function fetchCreateLeave(data: Api.Workflow.LeaveOperateParams) {
return request<boolean>({
return request<Api.Workflow.Leave>({
url: '/workflow/leave',
method: 'post',
data
@ -27,7 +27,7 @@ export function fetchCreateLeave(data: Api.Workflow.LeaveOperateParams) {
/** 修改请假申请 */
export function fetchUpdateLeave(data: Api.Workflow.LeaveOperateParams) {
return request<boolean>({
return request<Api.Workflow.Leave>({
url: '/workflow/leave',
method: 'put',
data

View File

@ -0,0 +1,27 @@
import { request } from '@/service/request';
/** 启动任务 */
export function fetchStartWorkflow(data: Api.Workflow.StartWorkflowOperateParams) {
return request<Api.Workflow.StartWorkflowResult>({
url: '/workflow/task/startWorkFlow',
method: 'post',
data
});
}
/** 获取任务 */
export function fetchGetTask(taskId: CommonType.IdType) {
return request<Api.Workflow.Task>({
url: `/workflow/task/getTask/${taskId}`,
method: 'get'
});
}
/** 完成任务 */
export function fetchCompleteTask(data: Api.Workflow.CompleteTaskOperateParams) {
return request<Api.Workflow.Task>({
url: '/workflow/task/completeTask',
method: 'post',
data
});
}

View File

@ -10,6 +10,8 @@ declare namespace Api {
* backend api module: "Workflow"
*/
namespace Workflow {
/** 流程类型 */
type FlowCodeType = 'leave1' | 'leave2' | 'leave3' | 'leave4' | 'leave5' | 'leave6';
/** 请假状态 */
type LeaveType = '1' | '2' | '3' | '4';
/** leave */
@ -198,5 +200,127 @@ declare namespace Api {
/** 作废原因 */
comment: string;
}>;
/** 启动流程操作参数 */
type StartWorkflowOperateParams = CommonType.RecordNullable<{
/** 流程定义ID */
flowCode: string;
/** 业务ID */
businessId: CommonType.IdType;
/** 变量 */
variables: { [key: string]: any };
}>;
/** 启动流程结果 */
type StartWorkflowResult = CommonType.RecordNullable<{
/** 流程实例ID */
processInstanceId: CommonType.IdType;
/** 任务ID */
taskId: CommonType.IdType;
}>;
/** 抄送人 */
type FlowCopy = CommonType.RecordNullable<{
/** 用户ID */
userId: CommonType.IdType;
/** 用户名称 */
userName: string;
}>;
/** 按钮权限 */
type ButtonPermission = CommonType.RecordNullable<{
/** 唯一编码 */
code: CommonType.IdType;
/** 选项值 */
value: string;
/** 是否显示 */
show: boolean;
}>;
/** 任务详情 */
type Task = Common.CommonRecord<{
/** 任务ID */
id: CommonType.IdType;
/** 租户编号 */
tenantId: CommonType.IdType;
/** 删除标志 */
delFlag: number;
/** 流程定义ID */
definitionId: CommonType.IdType;
/** 流程实例ID */
instanceId: CommonType.IdType;
/** 业务ID */
businessId: CommonType.IdType;
/** 节点编码 */
nodeCode: string;
/** 节点类型 */
nodeType: WorkflowNodeType;
/** 权限列表 */
permissionList: string[];
/** 用户列表 */
userList: any[];
/** 审批表单是否自定义 */
formCustom: Api.Common.YesOrNoStatus;
/** 审批表单路径 */
formPath: string;
/** 流程状态 */
flowStatus: string;
/** 流程状态名称 */
flowStatusName: string;
/** 分类ID */
category: CommonType.IdType;
/** 分类名称 */
categoryName: string;
/** 类型 */
type: string;
/** 审批人 */
assigneeIds: string;
/** 审批人名称 */
assigneeNames: string;
/** 审批人 */
processedBy: string;
/** 审批人名称 */
processedByName: string;
/** 流程签署比例值 大于0为票签会签 */
nodeRatio: string;
/** 创建人名称 */
createByName: string;
/** 是否为申请人节点 */
applyNode: string;
/** 按钮列表 */
buttonList: ButtonPermission[];
/** 节点名称 */
nodeName: string;
/** 流程定义名称 */
flowName: string;
/** 流程定义编码 */
flowCode: string;
/** 流程版本号 */
version: string;
}>;
/** 消息类型 */
type MessageType = '1' | '2' | '3';
/** 完成任务操作参数 */
type CompleteTaskOperateParams = CommonType.RecordNullable<{
/** 任务ID */
taskId: CommonType.IdType;
/** 文件ID */
fileId: CommonType.IdType;
/** 抄送人 */
flowCopyList: FlowCopy[];
/** 消息类型 */
messageType: string[];
/** 消息 */
message: string;
/** 通知 */
notice: string;
/** 任务变量 */
taskVariables: { [key: string]: any };
/** 变量 */
variables: { [key: string]: any };
/** 审批人 */
assigneeMap: { [key: string]: string };
/** 扩展字段 */
ext: string;
}>;
}
}

View File

@ -55,6 +55,7 @@ declare module 'vue' {
IconUilSearch: typeof import('~icons/uil/search')['default']
JsonPreview: typeof import('./../components/custom/json-preview.vue')['default']
LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
LeaveForm: typeof import('../components/custom/workflow-leave-form.vue')['default']
LookForward: typeof import('./../components/custom/look-forward.vue')['default']
MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
MenuTree: typeof import('./../components/custom/menu-tree.vue')['default']
@ -68,6 +69,7 @@ declare module 'vue' {
NButton: typeof import('naive-ui')['NButton']
NCard: typeof import('naive-ui')['NCard']
NCheckbox: typeof import('naive-ui')['NCheckbox']
NCheckboxGroup: typeof import('naive-ui')['NCheckboxGroup']
NCode: typeof import('naive-ui')['NCode']
NCollapse: typeof import('naive-ui')['NCollapse']
NCollapseItem: typeof import('naive-ui')['NCollapseItem']
@ -147,5 +149,7 @@ declare module 'vue' {
UserSelect: typeof import('./../components/custom/user-select.vue')['default']
WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
WorkflowCategorySelect: typeof import('./../components/custom/workflow-category-select.vue')['default']
WorkflowLeaveForm: typeof import('./../components/custom/workflow-leave-form.vue')['default']
WorkflowTaskApplyModal: typeof import('./../components/custom/workflow-task-apply-modal.vue')['default']
}
}

View File

@ -1,8 +1,9 @@
<script setup lang="ts">
import { computed, reactive, watch } from 'vue';
import { computed, reactive, ref, watch } from 'vue';
import dayjs from 'dayjs';
import { leaveTypeOptions } from '@/constants/workflow';
import { fetchCreateLeave, fetchUpdateLeave } from '@/service/api/workflow';
import { useBoolean } from '@sa/hooks';
import { flowCodeTypeOptions, leaveTypeOptions } from '@/constants/workflow';
import { fetchCreateLeave, fetchStartWorkflow, fetchUpdateLeave } from '@/service/api/workflow';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
@ -28,7 +29,7 @@ const emit = defineEmits<Emits>();
const visible = defineModel<boolean>('visible', {
default: false
});
const { bool: taskApplyVisible, setTrue: setTaskApplyVisible } = useBoolean();
const { formRef, validate, restoreValidation } = useNaiveForm();
const { createRequiredRule } = useFormRules();
@ -40,12 +41,21 @@ const title = computed(() => {
return titles[props.operateType];
});
type Model = Api.Workflow.LeaveOperateParams;
const respLeave = ref<Api.Workflow.Leave>();
const startWorkflowResult = ref<Api.Workflow.StartWorkflowResult>();
type Model = Api.Workflow.LeaveOperateParams & {
flowCode: Api.Workflow.FlowCodeType;
};
type StartWorkflowModel = Api.Workflow.StartWorkflowOperateParams;
const model: Model = reactive(createDefaultModel());
const startWorkflowModel: StartWorkflowModel = reactive(createDefaultStartWorkflowModel());
function createDefaultModel(): Model {
return {
flowCode: 'leave1',
leaveType: null,
startDate: null,
endDate: null,
@ -53,6 +63,15 @@ function createDefaultModel(): Model {
remark: ''
};
}
function createDefaultStartWorkflowModel(): StartWorkflowModel {
return {
flowCode: null,
businessId: null,
variables: {}
};
}
const dateRange = computed<[string, string] | null>({
get: () => {
if (!model.startDate || !model.endDate) return null;
@ -74,10 +93,11 @@ const dateRange = computed<[string, string] | null>({
}
});
type RuleKey = Extract<keyof Model, 'id' | 'leaveType' | 'leaveDays' | 'startDate' | 'endDate'>;
type RuleKey = Extract<keyof Model, 'id' | 'leaveType' | 'leaveDays' | 'startDate' | 'endDate'> | 'flowCode';
const rules: Record<RuleKey, App.Global.FormRule> = {
id: createRequiredRule('id不能为空'),
flowCode: createRequiredRule('流程类型不能为空'),
leaveType: createRequiredRule('请假类型不能为空'),
startDate: createRequiredRule('请假时间不能为空'),
endDate: createRequiredRule('结束时间不能为空'),
@ -99,23 +119,45 @@ function closeDrawer() {
visible.value = false;
}
async function handleSubmit() {
async function handleSaveDraft() {
await validate();
// request
if (props.operateType === 'add') {
const { leaveType, startDate, endDate, leaveDays, remark } = model;
const { error } = await fetchCreateLeave({ leaveType, startDate, endDate, leaveDays, remark });
const { error, data } = await fetchCreateLeave({ leaveType, startDate, endDate, leaveDays, remark });
if (error) return;
respLeave.value = data;
}
if (props.operateType === 'edit') {
const { id, leaveType, startDate, endDate, leaveDays, remark } = model;
const { error } = await fetchUpdateLeave({ id, leaveType, startDate, endDate, leaveDays, remark });
const { error, data } = await fetchUpdateLeave({ id, leaveType, startDate, endDate, leaveDays, remark });
if (error) return;
respLeave.value = data;
}
}
const taskVariables = ref<{ [key: string]: any }>({});
async function handleSubmit() {
await handleSaveDraft();
// 提交流程
startWorkflowModel.businessId = respLeave.value?.id;
startWorkflowModel.flowCode = model.flowCode;
taskVariables.value = {
leaveDays: respLeave.value?.leaveDays,
userList: ['1', '3', '4']
};
startWorkflowModel.variables = taskVariables.value;
const { error, data } = await fetchStartWorkflow(startWorkflowModel);
if (error) return;
startWorkflowResult.value = data;
window.$message?.success($t('common.updateSuccess'));
setTaskApplyVisible();
}
function handleTaskFinished() {
closeDrawer();
emit('submitted');
}
@ -132,6 +174,9 @@ watch(visible, () => {
<NDrawer v-model:show="visible" :title="title" display-directive="show" :width="800" class="max-w-90%">
<NDrawerContent :title="title" :native-scrollbar="false" closable>
<NForm ref="formRef" :model="model" :rules="rules">
<NFormItem label="流程类型" path="flowCode">
<NSelect v-model:value="model.flowCode" placeholder="请输入流程类型" :options="flowCodeTypeOptions" />
</NFormItem>
<NFormItem label="请假类型" path="leaveType">
<NSelect v-model:value="model.leaveType" placeholder="请输入请假类型" :options="leaveTypeOptions" />
</NFormItem>
@ -154,9 +199,16 @@ watch(visible, () => {
<template #footer>
<NSpace :size="16">
<NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
<NButton @click="handleSaveDraft">暂存</NButton>
<NButton type="primary" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
</NSpace>
</template>
<WorkflowTaskApplyModal
v-model:visible="taskApplyVisible"
:task-id="startWorkflowResult?.taskId || ''"
:task-variables="taskVariables"
@finished="handleTaskFinished"
/>
</NDrawerContent>
</NDrawer>
</template>