|
|
|
|
@ -7,14 +7,20 @@
|
|
|
|
|
<el-form-item label="汇总编号" prop="summaryCode">
|
|
|
|
|
<el-input v-model="queryParams.summaryCode" placeholder="请输入汇总编号" clearable @keyup.enter="handleQuery" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="月份编码" prop="monthCode">
|
|
|
|
|
<el-input v-model="queryParams.monthCode" placeholder="请输入月份编码(YYYYMM)" clearable @keyup.enter="handleQuery" />
|
|
|
|
|
<el-form-item label="月份" prop="monthCode">
|
|
|
|
|
<el-date-picker
|
|
|
|
|
v-model="queryParams.monthCode"
|
|
|
|
|
type="month"
|
|
|
|
|
value-format="YYYYMM"
|
|
|
|
|
placeholder="请选择月份"
|
|
|
|
|
clearable
|
|
|
|
|
@change="handleQuery"
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="部门ID" prop="deptId">
|
|
|
|
|
<el-input v-model="queryParams.deptId" placeholder="请输入部门ID" clearable @keyup.enter="handleQuery" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="部门负责人ID" prop="userId">
|
|
|
|
|
<el-input v-model="queryParams.userId" placeholder="请输入部门负责人ID" clearable @keyup.enter="handleQuery" />
|
|
|
|
|
<el-form-item label="部门" prop="deptId">
|
|
|
|
|
<el-select v-model="queryParams.deptId" placeholder="请选择部门" clearable filterable style="width: 200px" @change="handleQuery">
|
|
|
|
|
<el-option v-for="dept in deptList" :key="dept.deptId" :label="dept.deptName" :value="dept.deptId" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="月标准工时" prop="standardDays">
|
|
|
|
|
<el-input v-model="queryParams.standardDays" placeholder="请输入月标准工时天数" clearable @keyup.enter="handleQuery" />
|
|
|
|
|
@ -31,14 +37,14 @@
|
|
|
|
|
<el-card shadow="never">
|
|
|
|
|
<template #header>
|
|
|
|
|
<el-row :gutter="10" class="mb8">
|
|
|
|
|
<el-col :span="1.5">
|
|
|
|
|
<!-- <el-col :span="1.5">
|
|
|
|
|
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['oa/erp:timesheetSummary:add']">新增</el-button>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="1.5">
|
|
|
|
|
</el-col> -->
|
|
|
|
|
<!-- <el-col :span="1.5">
|
|
|
|
|
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['oa/erp:timesheetSummary:edit']"
|
|
|
|
|
>修改</el-button
|
|
|
|
|
>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-col> -->
|
|
|
|
|
<el-col :span="1.5">
|
|
|
|
|
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['oa/erp:timesheetSummary:remove']"
|
|
|
|
|
>删除</el-button
|
|
|
|
|
@ -56,8 +62,8 @@
|
|
|
|
|
<el-table-column label="汇总工时ID" align="center" prop="summaryId" v-if="columns[0].visible" />
|
|
|
|
|
<el-table-column label="汇总编号" align="center" prop="summaryCode" v-if="columns[2].visible" />
|
|
|
|
|
<el-table-column label="月份编码" align="center" prop="monthCode" v-if="columns[3].visible" />
|
|
|
|
|
<el-table-column label="部门ID" align="center" prop="deptId" v-if="columns[4].visible" />
|
|
|
|
|
<el-table-column label="部门负责人ID" align="center" prop="userId" v-if="columns[5].visible" />
|
|
|
|
|
<el-table-column label="部门" align="center" prop="deptName" v-if="columns[4].visible" />
|
|
|
|
|
<el-table-column label="部门负责人" align="center" prop="leaderName" v-if="columns[5].visible" />
|
|
|
|
|
<el-table-column label="关联月标准工时ID" align="center" prop="standardMonthId" v-if="columns[6].visible" />
|
|
|
|
|
<el-table-column label="月标准工时" align="center" prop="standardDays" v-if="columns[7].visible" />
|
|
|
|
|
<el-table-column label="项目工时合计" align="center" prop="totalProjectHours" v-if="columns[8].visible" />
|
|
|
|
|
@ -67,8 +73,14 @@
|
|
|
|
|
<el-table-column label="备注" align="center" prop="remark" v-if="columns[12].visible" />
|
|
|
|
|
<el-table-column label="操作" align="center" fixed="right" class-name="small-padding fixed-width">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-tooltip content="修改" placement="top">
|
|
|
|
|
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['oa/erp:timesheetSummary:edit']"></el-button>
|
|
|
|
|
<el-tooltip content="调整明细" placement="top">
|
|
|
|
|
<el-button
|
|
|
|
|
link
|
|
|
|
|
type="success"
|
|
|
|
|
icon="Operation"
|
|
|
|
|
@click="handleAdjust(scope.row)"
|
|
|
|
|
v-hasPermi="['oa/erp:timesheetSummary:edit']"
|
|
|
|
|
></el-button>
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
<el-tooltip content="删除" placement="top">
|
|
|
|
|
<el-button
|
|
|
|
|
@ -85,6 +97,79 @@
|
|
|
|
|
|
|
|
|
|
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
<!-- 调整汇总工时对话框 -->
|
|
|
|
|
<el-dialog :title="adjustDialog.title" v-model="adjustDialog.visible" width="1000px" append-to-body>
|
|
|
|
|
<div v-if="adjustData" class="mt-4">
|
|
|
|
|
<el-descriptions title="汇总统计" :column="4" border class="mb-4">
|
|
|
|
|
<el-descriptions-item label="项目总工时">{{ adjustData.totalProjectHours }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="部门总工时">{{ adjustData.totalDeptHours }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="总工时">{{ adjustData.totalHours }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="总人数">{{ adjustData.staffCount }}</el-descriptions-item>
|
|
|
|
|
<el-descriptions-item label="标准天数">{{ adjustData.standardDays }}</el-descriptions-item>
|
|
|
|
|
</el-descriptions>
|
|
|
|
|
|
|
|
|
|
<div class="mb-4 flex items-center">
|
|
|
|
|
<span class="mr-2 text-sm font-medium">人员搜索:</span>
|
|
|
|
|
<el-input v-model="staffSearchName" placeholder="输入姓名搜索..." clearable style="width: 250px" prefix-icon="Search" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<el-table :data="filteredDetailList" border max-height="400">
|
|
|
|
|
<el-table-column label="序号" type="index" width="55" align="center" />
|
|
|
|
|
<el-table-column label="人员" prop="staffName" align="center" />
|
|
|
|
|
<el-table-column label="类型" align="center">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-tag :type="scope.row.isProject === '1' ? 'success' : 'info'">
|
|
|
|
|
{{ scope.row.isProject === '1' ? '项目' : '部门' }}
|
|
|
|
|
</el-tag>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="原始项目" prop="originalProjectName" align="center" show-overflow-tooltip />
|
|
|
|
|
<el-table-column label="原始工时" prop="originalHours" align="center" />
|
|
|
|
|
<el-table-column label="调整项目" align="center" width="200">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="scope.row.adjustedProjectId"
|
|
|
|
|
filterable
|
|
|
|
|
clearable
|
|
|
|
|
placeholder="请选择项目"
|
|
|
|
|
v-if="scope.row.isProject === '1'"
|
|
|
|
|
@change="(val) => handleProjectChange(val, scope.row)"
|
|
|
|
|
>
|
|
|
|
|
<el-option v-for="item in projectList" :key="item.projectId" :label="item.projectName" :value="item.projectId" />
|
|
|
|
|
</el-select>
|
|
|
|
|
<span v-else>-</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="调整工时" align="center" width="150">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-input-number
|
|
|
|
|
v-model="scope.row.adjustedHours"
|
|
|
|
|
:precision="1"
|
|
|
|
|
:step="0.5"
|
|
|
|
|
:min="0"
|
|
|
|
|
size="small"
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
@change="calculateTotals"
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column label="操作" align="center" width="100">
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
<el-button link type="primary" icon="Plus" @click="handleAddRow(scope.row)" v-if="scope.row.isProject === '1'"></el-button>
|
|
|
|
|
<el-button link type="danger" icon="Minus" @click="handleRemoveRow(scope.row)"></el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
<template #footer>
|
|
|
|
|
<div class="dialog-footer">
|
|
|
|
|
<el-button type="primary" @click="submitAdjust" :loading="buttonLoading">确 定</el-button>
|
|
|
|
|
<el-button @click="adjustDialog.visible = false">取 消</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
<!-- 添加或修改月汇总工时信息对话框 -->
|
|
|
|
|
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
|
|
|
|
<el-form ref="timesheetSummaryFormRef" :model="form" :rules="rules" label-width="120px">
|
|
|
|
|
@ -141,6 +226,8 @@ import {
|
|
|
|
|
updateTimesheetSummary
|
|
|
|
|
} from '@/api/oa/erp/timesheetSummary';
|
|
|
|
|
import { TimesheetSummaryVO, TimesheetSummaryQuery, TimesheetSummaryForm } from '@/api/oa/erp/timesheetSummary/types';
|
|
|
|
|
import { getErpProjectInfoList } from '@/api/oa/erp/projectInfo';
|
|
|
|
|
import { allListDept } from '@/api/system/dept';
|
|
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
|
|
|
|
|
|
|
@ -161,6 +248,14 @@ const dialog = reactive<DialogOption>({
|
|
|
|
|
title: ''
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const deptList = ref<any[]>([]);
|
|
|
|
|
|
|
|
|
|
/** 查询部门列表 */
|
|
|
|
|
const getDeptList = async () => {
|
|
|
|
|
const res = await allListDept({ deptCategory: '03' } as any);
|
|
|
|
|
deptList.value = res.data;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 列显隐信息
|
|
|
|
|
const columns = ref<FieldOption[]>([
|
|
|
|
|
{ key: 0, label: `汇总工时ID`, visible: false },
|
|
|
|
|
@ -321,6 +416,148 @@ const handleExport = () => {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
getDeptList();
|
|
|
|
|
getList();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 调整汇总明细逻辑
|
|
|
|
|
const adjustDialog = reactive({
|
|
|
|
|
visible: false,
|
|
|
|
|
title: '调整汇总工时明细'
|
|
|
|
|
});
|
|
|
|
|
const adjustData = ref<TimesheetSummaryVO | null>(null);
|
|
|
|
|
const projectList = ref<any[]>([]);
|
|
|
|
|
const staffSearchName = ref('');
|
|
|
|
|
|
|
|
|
|
const filteredDetailList = computed(() => {
|
|
|
|
|
if (!adjustData.value || !adjustData.value.summaryDetailList) return [];
|
|
|
|
|
if (!staffSearchName.value) return adjustData.value.summaryDetailList;
|
|
|
|
|
return adjustData.value.summaryDetailList.filter(
|
|
|
|
|
(item) => item.staffName && item.staffName.toLowerCase().includes(staffSearchName.value.toLowerCase())
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const loadProjectList = async () => {
|
|
|
|
|
if (projectList.value.length === 0) {
|
|
|
|
|
const res = await getErpProjectInfoList({});
|
|
|
|
|
projectList.value = res.data.map((p: any) => ({
|
|
|
|
|
...p,
|
|
|
|
|
projectId: String(p.projectId)
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleAdjust = async (row: TimesheetSummaryVO) => {
|
|
|
|
|
const summaryId = row.summaryId;
|
|
|
|
|
const res = await getTimesheetSummary(summaryId);
|
|
|
|
|
adjustData.value = res.data;
|
|
|
|
|
staffSearchName.value = '';
|
|
|
|
|
|
|
|
|
|
await loadProjectList();
|
|
|
|
|
|
|
|
|
|
// 统一 ID 为字符串
|
|
|
|
|
if (adjustData.value?.summaryDetailList) {
|
|
|
|
|
adjustData.value.summaryDetailList.forEach((detail) => {
|
|
|
|
|
if (detail.adjustedProjectId) {
|
|
|
|
|
detail.adjustedProjectId = String(detail.adjustedProjectId);
|
|
|
|
|
}
|
|
|
|
|
if (detail.originalProjectId) {
|
|
|
|
|
detail.originalProjectId = String(detail.originalProjectId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (detail.isProject === '1' && !detail.adjustedProjectId && detail.originalProjectId) {
|
|
|
|
|
detail.adjustedProjectId = detail.originalProjectId;
|
|
|
|
|
detail.adjustedProjectName = detail.originalProjectName;
|
|
|
|
|
detail.adjustedProjectCode = detail.originalProjectCode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 核心修复:如果当前行选中的项目 ID 不在下拉列表中,手动补齐,防止 el-select 显示 ID
|
|
|
|
|
if (detail.isProject === '1' && detail.adjustedProjectId) {
|
|
|
|
|
const pId = String(detail.adjustedProjectId);
|
|
|
|
|
const isExist = projectList.value.some((p) => String(p.projectId) === pId);
|
|
|
|
|
if (!isExist) {
|
|
|
|
|
projectList.value.push({
|
|
|
|
|
projectId: pId,
|
|
|
|
|
projectName: detail.adjustedProjectName || detail.originalProjectName || pId,
|
|
|
|
|
projectCode: detail.adjustedProjectCode || detail.originalProjectCode
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
adjustDialog.visible = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleProjectChange = (val: string | number, row: any) => {
|
|
|
|
|
const project = projectList.value.find((p) => String(p.projectId) === String(val));
|
|
|
|
|
if (project) {
|
|
|
|
|
row.adjustedProjectName = project.projectName;
|
|
|
|
|
row.adjustedProjectCode = project.projectCode;
|
|
|
|
|
} else {
|
|
|
|
|
row.adjustedProjectName = undefined;
|
|
|
|
|
row.adjustedProjectCode = undefined;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleAddRow = (row: any) => {
|
|
|
|
|
if (!adjustData.value || !adjustData.value.summaryDetailList) return;
|
|
|
|
|
const index = adjustData.value.summaryDetailList.indexOf(row);
|
|
|
|
|
if (index === -1) return;
|
|
|
|
|
|
|
|
|
|
const newRow = { ...row };
|
|
|
|
|
newRow.summaryDetailId = undefined; // 新增行
|
|
|
|
|
newRow.isGenerated = '0'; // 手动新增的行标识为 0
|
|
|
|
|
|
|
|
|
|
// 保留原始项目信息作为参考,仅重置调整后的工时和项目
|
|
|
|
|
newRow.adjustedHours = 0;
|
|
|
|
|
newRow.adjustedProjectId = undefined;
|
|
|
|
|
newRow.adjustedProjectName = undefined;
|
|
|
|
|
newRow.adjustedProjectCode = undefined;
|
|
|
|
|
|
|
|
|
|
adjustData.value.summaryDetailList.splice(index + 1, 0, newRow);
|
|
|
|
|
calculateTotals();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleRemoveRow = (row: any) => {
|
|
|
|
|
if (!adjustData.value || !adjustData.value.summaryDetailList) return;
|
|
|
|
|
const index = adjustData.value.summaryDetailList.indexOf(row);
|
|
|
|
|
if (index === -1) return;
|
|
|
|
|
adjustData.value.summaryDetailList.splice(index, 1);
|
|
|
|
|
calculateTotals();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const calculateTotals = () => {
|
|
|
|
|
if (!adjustData.value || !adjustData.value.summaryDetailList) return;
|
|
|
|
|
|
|
|
|
|
let totalProject = 0;
|
|
|
|
|
let totalDept = 0;
|
|
|
|
|
|
|
|
|
|
adjustData.value.summaryDetailList.forEach((item) => {
|
|
|
|
|
const hours = Number(item.adjustedHours) || 0;
|
|
|
|
|
if (item.isProject === '1') {
|
|
|
|
|
totalProject += hours;
|
|
|
|
|
} else {
|
|
|
|
|
totalDept += hours;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
adjustData.value.totalProjectHours = totalProject;
|
|
|
|
|
adjustData.value.totalDeptHours = totalDept;
|
|
|
|
|
adjustData.value.totalHours = totalProject + totalDept;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const submitAdjust = async () => {
|
|
|
|
|
if (!adjustData.value) return;
|
|
|
|
|
|
|
|
|
|
buttonLoading.value = true;
|
|
|
|
|
try {
|
|
|
|
|
await updateTimesheetSummary(adjustData.value as any);
|
|
|
|
|
proxy?.$modal.msgSuccess('调整成功');
|
|
|
|
|
adjustDialog.visible = false;
|
|
|
|
|
await getList();
|
|
|
|
|
} finally {
|
|
|
|
|
buttonLoading.value = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|