|
|
|
|
@ -0,0 +1,577 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div class="p-2">
|
|
|
|
|
<!-- 1. 顶部操作栏 -->
|
|
|
|
|
<el-card shadow="never" style="margin-top: 0" class="mb-2">
|
|
|
|
|
<approvalButton
|
|
|
|
|
@submitForm="submitForm"
|
|
|
|
|
@approvalVerifyOpen="approvalVerifyOpen"
|
|
|
|
|
@handleApprovalRecord="handleApprovalRecord"
|
|
|
|
|
:buttonLoading="buttonLoading"
|
|
|
|
|
:id="form.timesheetId"
|
|
|
|
|
:status="form.flowStatus"
|
|
|
|
|
:pageType="routeParams.type"
|
|
|
|
|
:mode="false"
|
|
|
|
|
/>
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
<!-- 2. 主表单 -->
|
|
|
|
|
<el-form
|
|
|
|
|
ref="timesheetFormRef"
|
|
|
|
|
:model="form"
|
|
|
|
|
:rules="rules"
|
|
|
|
|
label-width="120px"
|
|
|
|
|
:disabled="routeParams.type === 'view' || routeParams.type === 'approval'"
|
|
|
|
|
>
|
|
|
|
|
<el-card shadow="never" class="mb-2">
|
|
|
|
|
<template #header>
|
|
|
|
|
<span class="card-title">{{ form.timesheetId ? '修改工时填报' : '添加工时填报' }}</span>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="工时单号" prop="timesheetCode">
|
|
|
|
|
<el-input v-model="form.timesheetCode" placeholder="自动生成工时单号" disabled>
|
|
|
|
|
<template #append>
|
|
|
|
|
<el-button
|
|
|
|
|
type="primary"
|
|
|
|
|
@click="generateCode"
|
|
|
|
|
:disabled="isCodeGenerated || routeParams.type === 'view' || routeParams.type === 'approval'"
|
|
|
|
|
>
|
|
|
|
|
生成编号
|
|
|
|
|
</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-input>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="人员" prop="userId">
|
|
|
|
|
<!-- 显示当前登录人名字 -->
|
|
|
|
|
<el-input :model-value="userStore.nickname" disabled />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="起始日期" prop="startTime">
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="form.startTime"
|
|
|
|
|
type="date"
|
|
|
|
|
value-format="YYYY-MM-DD"
|
|
|
|
|
placeholder="本周一"
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
@change="handleStartTimeChange"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="结束日期" prop="endTime">
|
|
|
|
|
<el-date-picker v-model="form.endTime" type="date" value-format="YYYY-MM-DD" placeholder="本周日" style="width: 100%" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
<el-form-item label="部门工时" prop="deptHours">
|
|
|
|
|
<el-input-number v-model="form.deptHours" :min="0" :precision="1" disabled style="width: 100%" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
<el-form-item label="项目工时" prop="projectHours">
|
|
|
|
|
<el-input-number v-model="form.projectHours" :min="0" :precision="1" disabled style="width: 100%" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
<el-form-item label="总工时" prop="totalHours">
|
|
|
|
|
<el-input-number v-model="form.totalHours" :min="0" :precision="1" disabled style="width: 100%" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
|
|
|
|
|
<el-col :span="24">
|
|
|
|
|
<el-form-item label="备注" prop="remark">
|
|
|
|
|
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
</el-card>
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
<!-- 3. 子表 A:部门工作 -->
|
|
|
|
|
<el-card shadow="never" class="mt-2">
|
|
|
|
|
<template #header>
|
|
|
|
|
<el-row :gutter="10">
|
|
|
|
|
<el-col :span="1.5"><span class="card-title">部门工作</span></el-col>
|
|
|
|
|
<el-col :span="1.5" v-if="!isReadOnly">
|
|
|
|
|
<el-button type="primary" plain icon="Plus" size="small" @click="handleAddDeptRow">新增</el-button>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="1.5" v-if="!isReadOnly">
|
|
|
|
|
<el-button type="danger" plain icon="Delete" size="small" @click="handleDeleteDeptRow">删除</el-button>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
</template>
|
|
|
|
|
<el-table
|
|
|
|
|
:data="form.timesheetDeptList"
|
|
|
|
|
border
|
|
|
|
|
show-summary
|
|
|
|
|
:summary-method="getSummary"
|
|
|
|
|
@selection-change="(selection) => (checkedDept = selection)"
|
|
|
|
|
>
|
|
|
|
|
<el-table-column type="selection" width="50" align="center" />
|
|
|
|
|
<el-table-column label="序号" type="index" width="55" align="center" />
|
|
|
|
|
|
|
|
|
|
<el-table-column label="部门工作描述" prop="workDescription" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-input v-model="scope.row.workDescription" placeholder="请输入工作内容" :disabled="isReadOnly" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
|
|
<el-table-column label="部门" prop="deptId" width="200" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="scope.row.deptId"
|
|
|
|
|
placeholder="请选择部门"
|
|
|
|
|
filterable
|
|
|
|
|
clearable
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
:disabled="isReadOnly"
|
|
|
|
|
@change="(val) => handleDeptChange(val, scope.row)"
|
|
|
|
|
>
|
|
|
|
|
<el-option v-for="item in deptOptions" :key="item.deptId" :label="item.deptName" :value="item.deptId" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
|
|
<el-table-column label="部门直线经理" prop="deptManagerId" width="150" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-input :model-value="getUserName(scope.row.deptManagerId)" placeholder="自动关联" disabled />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
|
|
<el-table-column label="工时(H)" prop="hours" width="150" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-input-number v-model="scope.row.hours" :min="0" :precision="1" :step="0.5" style="width: 100%" :disabled="isReadOnly" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
<!-- 4. 子表 B:项目工作 -->
|
|
|
|
|
<el-card shadow="never" class="mt-2">
|
|
|
|
|
<template #header>
|
|
|
|
|
<el-row :gutter="10">
|
|
|
|
|
<el-col :span="1.5"><span class="card-title">项目工作</span></el-col>
|
|
|
|
|
<el-col :span="1.5" v-if="!isReadOnly">
|
|
|
|
|
<el-button type="primary" plain icon="Plus" size="small" @click="handleAddProjectRow">新增</el-button>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="1.5" v-if="!isReadOnly">
|
|
|
|
|
<el-button type="danger" plain icon="Delete" size="small" @click="handleDeleteProjectRow">删除</el-button>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
</template>
|
|
|
|
|
<el-table
|
|
|
|
|
:data="form.timesheetProjectList"
|
|
|
|
|
border
|
|
|
|
|
show-summary
|
|
|
|
|
:summary-method="getSummary"
|
|
|
|
|
@selection-change="(selection) => (checkedProject = selection)"
|
|
|
|
|
>
|
|
|
|
|
<el-table-column type="selection" width="50" align="center" />
|
|
|
|
|
<el-table-column label="序号" type="index" width="55" align="center" />
|
|
|
|
|
|
|
|
|
|
<!-- 项目名称 -->
|
|
|
|
|
<el-table-column label="项目名称" prop="projectId" min-width="250" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="scope.row.projectId"
|
|
|
|
|
placeholder="请选择项目"
|
|
|
|
|
filterable
|
|
|
|
|
clearable
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
:disabled="isReadOnly"
|
|
|
|
|
@change="(val) => handleProjectWorkChange(val, scope.row)"
|
|
|
|
|
>
|
|
|
|
|
<el-option v-for="item in projectOptions" :key="item.projectId" :label="item.projectName" :value="item.projectId" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
|
|
<el-table-column label="项目号" prop="projectCode" width="150" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-input v-model="scope.row.projectCode" disabled placeholder="自动关联" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
|
|
<el-table-column label="部门" prop="deptId" width="150" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-input :model-value="getDeptName(scope.row.deptId)" disabled placeholder="自动关联" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
|
|
<el-table-column label="项目经理" prop="projectManagerId" width="120" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-input :model-value="getUserName(scope.row.projectManagerId)" disabled placeholder="自动关联" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
|
|
<el-table-column label="工时(H)" prop="hours" width="150" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-input-number v-model="scope.row.hours" :min="0" :precision="1" :step="0.5" style="width: 100%" :disabled="isReadOnly" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
|
|
|
|
|
<approvalRecord ref="approvalRecordRef" />
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup name="TimesheetEdit" lang="ts">
|
|
|
|
|
import { ref, reactive, toRefs, onMounted, getCurrentInstance, watch, computed, nextTick } from 'vue';
|
|
|
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
|
|
|
import { ElMessage } from 'element-plus';
|
|
|
|
|
|
|
|
|
|
// API
|
|
|
|
|
import { getTimesheetInfo, addTimesheetInfo, updateTimesheetInfo, submitTimesheetAndFlowStart } from '@/api/oa/erp/timesheetInfo';
|
|
|
|
|
import { listProjectInfo } from '@/api/oa/erp/projectInfo';
|
|
|
|
|
import { getRuleGenerateCode } from '@/api/system/codeRule';
|
|
|
|
|
|
|
|
|
|
// Components & Store & Enums
|
|
|
|
|
import SubmitVerify from '@/components/Process/submitVerify.vue';
|
|
|
|
|
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
|
|
|
|
|
import ApprovalButton from '@/components/Process/approvalButton.vue';
|
|
|
|
|
import { useUserStore } from '@/store/modules/user';
|
|
|
|
|
import { CodeRuleEnum, FlowCodeEnum } from '@/enums/OAEnum';
|
|
|
|
|
import { getInfo } from '@/api/login';
|
|
|
|
|
import { listDept } from '@/api/system/dept';
|
|
|
|
|
import { listUser } from '@/api/system/user';
|
|
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
|
|
const route = useRoute();
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
const userStore = useUserStore();
|
|
|
|
|
|
|
|
|
|
// Vars
|
|
|
|
|
const routeParams = ref<any>({});
|
|
|
|
|
const buttonLoading = ref(false);
|
|
|
|
|
const timesheetFormRef = ref<ElFormInstance>();
|
|
|
|
|
const isCodeGenerated = ref(false);
|
|
|
|
|
|
|
|
|
|
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
|
|
|
|
|
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
|
|
|
|
|
const taskVariables = ref<Record<string, any>>({});
|
|
|
|
|
|
|
|
|
|
// Data Source
|
|
|
|
|
const projectOptions = ref<any[]>([]);
|
|
|
|
|
const userList = ref<any[]>([]);
|
|
|
|
|
const checkedDept = ref<any[]>([]);
|
|
|
|
|
const checkedProject = ref<any[]>([]);
|
|
|
|
|
const deptOptions = ref<any[]>([]);
|
|
|
|
|
|
|
|
|
|
const data = reactive({
|
|
|
|
|
form: {
|
|
|
|
|
timesheetId: undefined,
|
|
|
|
|
timesheetCode: undefined,
|
|
|
|
|
userId: undefined,
|
|
|
|
|
deptId: undefined,
|
|
|
|
|
chargeId: undefined,
|
|
|
|
|
startTime: undefined,
|
|
|
|
|
endTime: undefined,
|
|
|
|
|
totalHours: 0,
|
|
|
|
|
deptHours: 0,
|
|
|
|
|
projectHours: 0,
|
|
|
|
|
timesheetStatus: '0',
|
|
|
|
|
flowStatus: 'draft',
|
|
|
|
|
remark: undefined,
|
|
|
|
|
// 子表
|
|
|
|
|
timesheetDeptList: [] as any[],
|
|
|
|
|
timesheetProjectList: [] as any[]
|
|
|
|
|
},
|
|
|
|
|
rules: {
|
|
|
|
|
timesheetCode: [{ required: true, message: '编号不能为空', trigger: 'blur' }],
|
|
|
|
|
startTime: [{ required: true, message: '请选择起始日期', trigger: 'change' }],
|
|
|
|
|
endTime: [{ required: true, message: '请选择结束日期', trigger: 'change' }]
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const { form, rules } = toRefs(data);
|
|
|
|
|
|
|
|
|
|
const isReadOnly = computed(() => routeParams.value.type === 'view' || routeParams.value.type === 'approval');
|
|
|
|
|
|
|
|
|
|
/** 初始化 */
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
nextTick(async () => {
|
|
|
|
|
routeParams.value = route.query;
|
|
|
|
|
const id = routeParams.value.id;
|
|
|
|
|
|
|
|
|
|
// 加载基础数据
|
|
|
|
|
loadBaseOptions();
|
|
|
|
|
|
|
|
|
|
if (id && ['update', 'view', 'approval'].includes(routeParams.value.type)) {
|
|
|
|
|
const res: any = await getTimesheetInfo(id);
|
|
|
|
|
form.value = res.data;
|
|
|
|
|
if (!form.value.timesheetDeptList) form.value.timesheetDeptList = [];
|
|
|
|
|
if (!form.value.timesheetProjectList) form.value.timesheetProjectList = [];
|
|
|
|
|
if (form.value.timesheetCode) isCodeGenerated.value = true;
|
|
|
|
|
} else {
|
|
|
|
|
setWeekDates();
|
|
|
|
|
form.value.timesheetDeptList = [];
|
|
|
|
|
form.value.timesheetProjectList = [];
|
|
|
|
|
|
|
|
|
|
// 获取登录用户信息并自动赋值
|
|
|
|
|
try {
|
|
|
|
|
const infoRes = await getInfo();
|
|
|
|
|
const user = infoRes.data.user; // 获取用户对象
|
|
|
|
|
if (user) {
|
|
|
|
|
form.value.userId = user.userId;
|
|
|
|
|
form.value.deptId = user.deptId;
|
|
|
|
|
form.value.chargeId = user.chargeId;
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取用户信息失败', error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/** 辅助:设置本周日期 */
|
|
|
|
|
const setWeekDates = () => {
|
|
|
|
|
const now = new Date();
|
|
|
|
|
const day = now.getDay() || 7; // 周日是0,改为7
|
|
|
|
|
const monday = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1 - day);
|
|
|
|
|
const sunday = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7 - day);
|
|
|
|
|
form.value.startTime = proxy.parseTime(monday, '{y}-{m}-{d}');
|
|
|
|
|
form.value.endTime = proxy.parseTime(sunday, '{y}-{m}-{d}');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 起始日期改变联动
|
|
|
|
|
*/
|
|
|
|
|
const handleStartTimeChange = (val: any) => {
|
|
|
|
|
if (!val) {
|
|
|
|
|
form.value.endTime = undefined;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const startDate = new Date(val.replace(/-/g, '/'));
|
|
|
|
|
const dayOfWeek = startDate.getDay() || 7;
|
|
|
|
|
const daysUntilSunday = 7 - dayOfWeek;
|
|
|
|
|
const endDate = new Date(startDate);
|
|
|
|
|
endDate.setDate(startDate.getDate() + daysUntilSunday);
|
|
|
|
|
form.value.endTime = proxy.parseTime(endDate, '{y}-{m}-{d}');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 加载基础下拉数据
|
|
|
|
|
function loadBaseOptions() {
|
|
|
|
|
listProjectInfo({ pageNum: 1, pageSize: 1000 }).then((res: any) => {
|
|
|
|
|
projectOptions.value = res.rows;
|
|
|
|
|
});
|
|
|
|
|
listUser({ pageNum: 1, pageSize: 1000 }).then((res: any) => {
|
|
|
|
|
userList.value = res.rows;
|
|
|
|
|
});
|
|
|
|
|
listDept({ pageNum: 1, pageSize: 1000 }).then((res: any) => {
|
|
|
|
|
deptOptions.value = res.data;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 部门选择联动
|
|
|
|
|
*/
|
|
|
|
|
const handleDeptChange = (val: any, row: any) => {
|
|
|
|
|
if (!val) {
|
|
|
|
|
row.deptManagerId = undefined;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const dept = deptOptions.value.find((d: any) => d.deptId === val);
|
|
|
|
|
|
|
|
|
|
if (dept && dept.leader) {
|
|
|
|
|
row.deptManagerId = dept.leader;
|
|
|
|
|
} else {
|
|
|
|
|
row.deptManagerId = undefined;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 项目选择联动
|
|
|
|
|
*/
|
|
|
|
|
const handleProjectWorkChange = (val: any, row: any) => {
|
|
|
|
|
if (!val) {
|
|
|
|
|
row.projectCode = '';
|
|
|
|
|
row.deptId = undefined;
|
|
|
|
|
row.projectManagerId = undefined;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 在项目列表中找到选中的项目
|
|
|
|
|
const project = projectOptions.value.find((p: any) => p.projectId === val);
|
|
|
|
|
|
|
|
|
|
if (project) {
|
|
|
|
|
// 自动回填关联信息
|
|
|
|
|
row.projectName = project.projectName;
|
|
|
|
|
row.projectCode = project.projectCode;
|
|
|
|
|
row.deptId = project.deptId;
|
|
|
|
|
row.projectManagerId = project.managerId;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据 UserID 获取用户昵称
|
|
|
|
|
*/
|
|
|
|
|
const getUserName = (userId: any) => {
|
|
|
|
|
if (!userId) return '';
|
|
|
|
|
const user = userList.value.find((u: any) => u.userId == userId);
|
|
|
|
|
return user ? user.nickName : '';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据 DeptID 获取部门名称
|
|
|
|
|
*/
|
|
|
|
|
const getDeptName = (deptId: any) => {
|
|
|
|
|
if (!deptId) return '';
|
|
|
|
|
const dept = deptOptions.value.find((d: any) => d.deptId == deptId);
|
|
|
|
|
return dept ? dept.deptName : '';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 生成编号 */
|
|
|
|
|
const generateCode = async () => {
|
|
|
|
|
if (isCodeGenerated.value) return;
|
|
|
|
|
try {
|
|
|
|
|
const res = await getRuleGenerateCode({ codeRuleCode: CodeRuleEnum.TIMESHEET } as any);
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
form.value.timesheetCode = res.msg;
|
|
|
|
|
isCodeGenerated.value = true;
|
|
|
|
|
proxy?.$modal.msgSuccess('生成成功');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 自动计算工时 */
|
|
|
|
|
const calculateHours = () => {
|
|
|
|
|
const deptList = form.value.timesheetDeptList || [];
|
|
|
|
|
const projectList = form.value.timesheetProjectList || [];
|
|
|
|
|
|
|
|
|
|
const deptTotal = deptList.reduce((sum: number, item: any) => sum + (Number(item.hours) || 0), 0);
|
|
|
|
|
const projectTotal = projectList.reduce((sum: number, item: any) => sum + (Number(item.hours) || 0), 0);
|
|
|
|
|
|
|
|
|
|
form.value.deptHours = deptTotal;
|
|
|
|
|
form.value.projectHours = projectTotal;
|
|
|
|
|
form.value.totalHours = deptTotal + projectTotal;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
watch([() => form.value.timesheetDeptList, () => form.value.timesheetProjectList], () => calculateHours(), { deep: true });
|
|
|
|
|
|
|
|
|
|
/** 子表逻辑 */
|
|
|
|
|
const handleAddDeptRow = () => {
|
|
|
|
|
form.value.timesheetDeptList.push({ workDescription: '', hours: 0 });
|
|
|
|
|
};
|
|
|
|
|
const handleDeleteDeptRow = () => {
|
|
|
|
|
if (!checkedDept.value.length) return proxy.$modal.msgError('请选择数据');
|
|
|
|
|
form.value.timesheetDeptList = form.value.timesheetDeptList.filter((item: any) => !checkedDept.value.includes(item));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleAddProjectRow = () => {
|
|
|
|
|
form.value.timesheetProjectList.push({ projectId: undefined, hours: 0 });
|
|
|
|
|
};
|
|
|
|
|
const handleDeleteProjectRow = () => {
|
|
|
|
|
if (!checkedProject.value.length) return proxy.$modal.msgError('请选择数据');
|
|
|
|
|
form.value.timesheetProjectList = form.value.timesheetProjectList.filter((item: any) => !checkedProject.value.includes(item));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 项目联动回填
|
|
|
|
|
const handleProjectChange = (val: any, row: any) => {
|
|
|
|
|
const proj = projectOptions.value.find((p: any) => p.projectId === val);
|
|
|
|
|
if (proj) {
|
|
|
|
|
row.projectName = proj.projectName;
|
|
|
|
|
row.projectCode = proj.projectCode;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 表格合计
|
|
|
|
|
const getSummary = (param: any) => {
|
|
|
|
|
const { columns, data } = param;
|
|
|
|
|
const sums: string[] = [];
|
|
|
|
|
columns.forEach((column: any, index: number) => {
|
|
|
|
|
if (index === 0) return;
|
|
|
|
|
if (index === 1) {
|
|
|
|
|
sums[index] = '合计';
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (column.property === 'hours') {
|
|
|
|
|
const values = data.map((item: any) => Number(item[column.property]));
|
|
|
|
|
const total = values.reduce((prev: any, curr: any) => (!Number.isNaN(curr) ? prev + curr : prev), 0);
|
|
|
|
|
sums[index] = total.toFixed(1) + ' H';
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return sums;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 提交按钮 */
|
|
|
|
|
const submitForm = (status: string) => {
|
|
|
|
|
timesheetFormRef.value?.validate(async (valid: boolean) => {
|
|
|
|
|
if (valid) {
|
|
|
|
|
buttonLoading.value = true;
|
|
|
|
|
try {
|
|
|
|
|
const submitData: any = { ...form.value };
|
|
|
|
|
|
|
|
|
|
if (status !== 'draft') {
|
|
|
|
|
// 提交审批
|
|
|
|
|
submitData.flowCode = FlowCodeEnum.TIMESHEET_KEY;
|
|
|
|
|
submitData.variables = {
|
|
|
|
|
startUserId: userStore.userId,
|
|
|
|
|
totalHours: submitData.totalHours
|
|
|
|
|
};
|
|
|
|
|
submitData.bizExt = {
|
|
|
|
|
businessTitle: `工时填报:${userStore.nickname} (${submitData.startTime})`,
|
|
|
|
|
businessCode: submitData.timesheetCode
|
|
|
|
|
};
|
|
|
|
|
submitData.timesheetStatus = '2'; // 已提交
|
|
|
|
|
submitData.flowStatus = 'waiting';
|
|
|
|
|
|
|
|
|
|
const res = await submitTimesheetAndFlowStart(submitData);
|
|
|
|
|
if (res?.data) form.value = res.data;
|
|
|
|
|
proxy.$modal.msgSuccess('提交成功');
|
|
|
|
|
} else {
|
|
|
|
|
// 暂存
|
|
|
|
|
submitData.timesheetStatus = '1'; // 草稿
|
|
|
|
|
submitData.flowStatus = 'draft';
|
|
|
|
|
if (submitData.timesheetId) await updateTimesheetInfo(submitData);
|
|
|
|
|
else await addTimesheetInfo(submitData);
|
|
|
|
|
proxy.$modal.msgSuccess('暂存成功');
|
|
|
|
|
}
|
|
|
|
|
proxy.$tab.closePage(route);
|
|
|
|
|
router.go(-1);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(e);
|
|
|
|
|
} finally {
|
|
|
|
|
buttonLoading.value = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleApprovalRecord = () => {
|
|
|
|
|
if (form.value.timesheetId) approvalRecordRef.value?.init(form.value.timesheetId);
|
|
|
|
|
};
|
|
|
|
|
const approvalVerifyOpen = async () => {
|
|
|
|
|
if (submitVerifyRef.value) await submitVerifyRef.value.openDialog(routeParams.value.taskId);
|
|
|
|
|
};
|
|
|
|
|
const submitCallback = async () => {
|
|
|
|
|
await proxy.$tab.closePage(route);
|
|
|
|
|
router.go(-1);
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
.card-title {
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
color: #303133;
|
|
|
|
|
}
|
|
|
|
|
.mt-2 {
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
}
|
|
|
|
|
:deep(.el-table__footer-wrapper tbody td.el-table__cell) {
|
|
|
|
|
background-color: #ffffff !important;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
</style>
|