diff --git a/src/api/oa/erp/timesheetPreAlloc/index.ts b/src/api/oa/erp/timesheetPreAlloc/index.ts index d3a5ac5..11887f6 100644 --- a/src/api/oa/erp/timesheetPreAlloc/index.ts +++ b/src/api/oa/erp/timesheetPreAlloc/index.ts @@ -30,9 +30,9 @@ export const getTimesheetPreAlloc = (allocId: string | number): AxiosPromise
=> {
+export const getAllocDetails = (query: { monthCode: string; projectId: string | number }): AxiosPromise => {
return request({
- url: '/oa/erp/timesheetPreAlloc/getStaffAllocDetails',
+ url: '/oa/erp/timesheetPreAlloc/getAllocDetails',
method: 'get',
params: query
});
@@ -89,10 +89,10 @@ export const delTimesheetPreAlloc = (allocId: string | number | Array
-
+
@@ -62,7 +58,7 @@
type="danger"
plain
icon="Delete"
- :disabled="multiple || hasAppliedSelected"
+ :disabled="multiple"
@click="handleDelete()"
v-hasPermi="['oa/erp:timesheetPreAlloc:remove']"
>
@@ -78,9 +74,9 @@
-
-
-
+
+
+
{{ scope.row.projectCode }}
@@ -88,40 +84,26 @@
-
+
{{ formatHours(scope.row.sourceTotalHours) }}
-
+
{{ formatHours(scope.row.allocatedTotalHours) }}
-
-
+
+
-
-
- {{ parseTime(scope.row.applyTime, '{y}-{m}-{d} {h}:{i}:{s}') }}
-
-
-
-
-
-
+
+
@@ -130,7 +112,7 @@
-
+
@@ -156,7 +138,7 @@
:disabled="Boolean(form.allocId)"
placeholder="请选择来源预投项目"
class="full-width"
- @change="loadStaffAllocDetails"
+ @change="loadAllocDetails"
>
-
-
-
- {{ formatHours(scope.row.sourceHours) }}
-
-
- {{ formatHours(calcStaffAllocated(scope.row)) }}
-
-
-
- {{ formatHours(calcStaffRemaining(scope.row)) }}
-
-
-
-
-
-
-
-
-
-
-
-
- 添加去向
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -233,8 +212,8 @@
@@ -248,14 +227,13 @@ import {
delTimesheetPreAlloc,
addTimesheetPreAlloc,
updateTimesheetPreAlloc,
- getStaffAllocDetails,
+ getAllocDetails,
listAvailableSourceProjects
} from '@/api/oa/erp/timesheetPreAlloc';
import {
TimesheetPreAllocVO,
TimesheetPreAllocQuery,
TimesheetPreAllocForm,
- PreAllocStaffAlloc,
PreAllocTarget,
PreAllocDetailVO
} from '@/api/oa/erp/timesheetPreAlloc/types';
@@ -294,15 +272,14 @@ const dialog = reactive({
});
const columns = ref([
- { key: 0, label: `月份`, visible: true },
- { key: 1, label: `来源预投项目`, visible: true },
- { key: 2, label: `来源工时`, visible: true },
- { key: 3, label: `已分配工时`, visible: true },
- { key: 4, label: `涉及人数`, visible: true },
- { key: 5, label: `分配状态`, visible: true },
- { key: 6, label: `回写时间`, visible: true },
- { key: 7, label: `备注`, visible: true },
- { key: 8, label: `分配单编号`, visible: true }
+ { key: 0, label: `分配单编号`, visible: true },
+ { key: 1, label: `月份`, visible: true },
+ { key: 2, label: `来源预投项目`, visible: true },
+ { key: 3, label: `来源工时`, visible: true },
+ { key: 4, label: `已分配工时`, visible: true },
+ { key: 5, label: `涉及人数`, visible: true },
+ { key: 6, label: `分配状态`, visible: true },
+ { key: 7, label: `备注`, visible: true }
]);
const getCurrentMonthCode = () => {
@@ -318,9 +295,8 @@ const initFormData = (): TimesheetPreAllocForm => ({
allocatedTotalHours: 0,
staffCount: 0,
allocStatus: '0',
- appliedFlag: '0',
remark: undefined,
- staffAllocList: []
+ allocItems: []
});
const data = reactive>({
@@ -362,37 +338,50 @@ const toNumber = (value?: number | string | null) => {
const formatHours = (value?: number | string | null) => toNumber(value).toFixed(2);
-const calcStaffAllocated = (staff: PreAllocStaffAlloc) => (staff.allocItems || []).reduce((sum, item) => sum + toNumber(item.allocHours), 0);
+const normalizeTarget = (item: PreAllocTarget): PreAllocTarget => ({
+ targetProjectId: item.targetProjectId ? String(item.targetProjectId) : undefined,
+ targetProjectCode: item.targetProjectCode,
+ targetProjectName: item.targetProjectName,
+ allocHours: toNumber(item.allocHours)
+});
-const calcStaffRemaining = (staff: PreAllocStaffAlloc) => toNumber(staff.sourceHours) - calcStaffAllocated(staff);
-
-const sourceTotalHours = computed(() => (form.value.staffAllocList || []).reduce((sum, staff) => sum + toNumber(staff.sourceHours), 0));
-const allocatedTotalHours = computed(() => (form.value.staffAllocList || []).reduce((sum, staff) => sum + calcStaffAllocated(staff), 0));
+const sourceTotalHours = computed(() => toNumber(form.value.sourceTotalHours));
+const allocatedTotalHours = computed(() => (form.value.allocItems || []).reduce((sum, item) => sum + toNumber(item.allocHours), 0));
const remainingTotalHours = computed(() => sourceTotalHours.value - allocatedTotalHours.value);
-const staffCount = computed(() => (form.value.staffAllocList || []).filter((staff) => toNumber(staff.sourceHours) > 0).length);
+const staffCount = computed(() => toNumber(form.value.staffCount));
const currentAllocStatus = computed(() => {
if (allocatedTotalHours.value <= 0) {
return '0';
}
return allocatedTotalHours.value < sourceTotalHours.value ? '1' : '2';
});
-const hasAppliedSelected = computed(() => selectedRows.value.some((row) => row.appliedFlag === '1'));
-const loadProjectOptions = async () => {
- if (querySourceProjectOptions.value.length === 0) {
- const sourceRes: any = await getErpProjectInfoList({ projectCategory: '4' });
- querySourceProjectOptions.value = (sourceRes.data || []).map(normalizeProject);
+const loadTargetProjectOptions = async () => {
+ if (targetProjectOptions.value.length > 0) {
+ return;
}
- if (targetProjectOptions.value.length === 0) {
- const [logisticsRes, spareRes]: any[] = await Promise.all([
- getErpProjectInfoList({ projectCategory: '1' }),
- getErpProjectInfoList({ projectCategory: '2' })
- ]);
- const merged = [...(logisticsRes.data || []), ...(spareRes.data || [])].map(normalizeProject);
- const uniqueMap = new Map();
- merged.forEach((project) => uniqueMap.set(project.projectId, project));
- targetProjectOptions.value = Array.from(uniqueMap.values());
+ const [logisticsRes, spareRes]: any[] = await Promise.all([
+ getErpProjectInfoList({ projectCategory: '1' }),
+ getErpProjectInfoList({ projectCategory: '2' })
+ ]);
+ const merged = [...(logisticsRes.data || []), ...(spareRes.data || [])].map(normalizeProject);
+ const uniqueMap = new Map();
+ merged.forEach((project) => uniqueMap.set(project.projectId, project));
+ targetProjectOptions.value = Array.from(uniqueMap.values());
+};
+
+const loadAllPreProjectOptions = async () => {
+ const res: any = await getErpProjectInfoList({ projectCategory: '4' });
+ querySourceProjectOptions.value = (res.data || []).map(normalizeProject);
+};
+
+const loadQuerySourceProjectOptions = async (monthCode?: string) => {
+ if (!monthCode) {
+ await loadAllPreProjectOptions();
+ return;
}
+ const res: any = await listAvailableSourceProjects({ monthCode });
+ querySourceProjectOptions.value = (res.data || []).map(normalizeProject);
};
const loadSourceProjectOptions = async (monthCode?: string) => {
@@ -404,19 +393,18 @@ const loadSourceProjectOptions = async (monthCode?: string) => {
sourceProjectOptions.value = (res.data || []).map(normalizeProject);
};
-const normalizeStaffAlloc = (staff: PreAllocStaffAlloc): PreAllocStaffAlloc => ({
- staffUserId: staff.staffUserId,
- staffName: staff.staffName,
- sourceHours: toNumber(staff.sourceHours),
- allocatedHours: toNumber(staff.allocatedHours),
- remainingHours: toNumber(staff.remainingHours),
- allocItems: (staff.allocItems || []).map((item) => ({
- targetProjectId: item.targetProjectId ? String(item.targetProjectId) : undefined,
- targetProjectCode: item.targetProjectCode,
- targetProjectName: item.targetProjectName,
- allocHours: toNumber(item.allocHours)
- }))
-});
+const appendMissingTargetOptions = (items: PreAllocTarget[]) => {
+ items.forEach((item) => {
+ const targetProjectId = item.targetProjectId ? String(item.targetProjectId) : undefined;
+ if (targetProjectId && !targetProjectOptions.value.some((project) => project.projectId === targetProjectId)) {
+ targetProjectOptions.value.push({
+ projectId: targetProjectId,
+ projectCode: item.targetProjectCode,
+ projectName: item.targetProjectName
+ });
+ }
+ });
+};
const applyDetailToForm = (detail: PreAllocDetailVO) => {
const sourceProjectId = detail.projectId ? String(detail.projectId) : undefined;
@@ -428,6 +416,10 @@ const applyDetailToForm = (detail: PreAllocDetailVO) => {
projectCategory: '4'
});
}
+
+ const allocItems = (detail.allocItems || []).map(normalizeTarget);
+ appendMissingTargetOptions(allocItems);
+
form.value.allocId = detail.allocId;
form.value.allocCode = detail.allocCode;
form.value.monthCode = detail.monthCode;
@@ -439,33 +431,26 @@ const applyDetailToForm = (detail: PreAllocDetailVO) => {
form.value.allocatedTotalHours = toNumber(detail.allocatedTotalHours);
form.value.staffCount = detail.staffCount;
form.value.allocStatus = detail.allocStatus;
- form.value.appliedFlag = detail.appliedFlag;
- form.value.summaryId = detail.summaryId;
- form.value.applyTime = detail.applyTime;
form.value.remark = detail.remark;
- form.value.staffAllocList = (detail.staffAllocList || []).map(normalizeStaffAlloc);
- form.value.staffAllocList.forEach((staff) => {
- (staff.allocItems || []).forEach((item) => {
- const targetProjectId = item.targetProjectId ? String(item.targetProjectId) : undefined;
- if (targetProjectId && !targetProjectOptions.value.some((project) => project.projectId === targetProjectId)) {
- targetProjectOptions.value.push({
- projectId: targetProjectId,
- projectCode: item.targetProjectCode,
- projectName: item.targetProjectName
- });
- }
- });
- });
+ form.value.allocItems = allocItems;
};
-const loadStaffAllocDetails = async () => {
+const resetSourceTotals = () => {
+ form.value.sourceTotalHours = 0;
+ form.value.allocatedTotalHours = 0;
+ form.value.staffCount = 0;
+ form.value.allocStatus = '0';
+ form.value.allocItems = [];
+};
+
+const loadAllocDetails = async () => {
if (!form.value.monthCode || !form.value.projectId) {
- form.value.staffAllocList = [];
+ resetSourceTotals();
return;
}
detailLoading.value = true;
try {
- const res: any = await getStaffAllocDetails({
+ const res: any = await getAllocDetails({
monthCode: form.value.monthCode,
projectId: form.value.projectId
});
@@ -479,20 +464,28 @@ const handleMonthChange = async () => {
form.value.projectId = undefined;
form.value.projectCode = undefined;
form.value.projectName = undefined;
- form.value.staffAllocList = [];
+ resetSourceTotals();
await loadSourceProjectOptions(form.value.monthCode);
};
+const handleQueryMonthChange = async () => {
+ queryParams.value.projectId = undefined;
+ await loadQuerySourceProjectOptions(queryParams.value.monthCode);
+};
+
const getList = async () => {
loading.value = true;
- const params = {
- ...queryParams.value,
- projectId: queryParams.value.projectId || undefined
- };
- const res: any = await listTimesheetPreAlloc(params);
- timesheetPreAllocList.value = res.rows;
- total.value = res.total;
- loading.value = false;
+ try {
+ const params = {
+ ...queryParams.value,
+ projectId: queryParams.value.projectId || undefined
+ };
+ const res: any = await listTimesheetPreAlloc(params);
+ timesheetPreAllocList.value = res.rows;
+ total.value = res.total;
+ } finally {
+ loading.value = false;
+ }
};
const cancel = () => {
@@ -510,8 +503,9 @@ const handleQuery = () => {
getList();
};
-const resetQuery = () => {
+const resetQuery = async () => {
queryFormRef.value?.resetFields();
+ await loadQuerySourceProjectOptions();
handleQuery();
};
@@ -524,7 +518,7 @@ const handleSelectionChange = (selection: TimesheetPreAllocVO[]) => {
const handleAdd = async () => {
reset();
- await loadProjectOptions();
+ await loadTargetProjectOptions();
await loadSourceProjectOptions(form.value.monthCode);
dialog.visible = true;
dialog.title = '预投工时分配';
@@ -532,23 +526,24 @@ const handleAdd = async () => {
const handleUpdate = async (row?: TimesheetPreAllocVO) => {
reset();
- await loadProjectOptions();
+ await loadTargetProjectOptions();
const allocId = row?.allocId || ids.value[0];
const res: any = await getTimesheetPreAlloc(allocId);
+ await loadSourceProjectOptions(res.data?.monthCode);
applyDetailToForm(res.data);
dialog.visible = true;
dialog.title = '预投工时分配';
};
-const addTarget = (staff: PreAllocStaffAlloc) => {
- if (!staff.allocItems) {
- staff.allocItems = [];
+const addTarget = () => {
+ if (!form.value.allocItems) {
+ form.value.allocItems = [];
}
- staff.allocItems.push({ targetProjectId: undefined, allocHours: 0 });
+ form.value.allocItems.push({ targetProjectId: undefined, allocHours: 0 });
};
-const removeTarget = (staff: PreAllocStaffAlloc, index: number) => {
- staff.allocItems?.splice(index, 1);
+const removeTarget = (index: number) => {
+ form.value.allocItems?.splice(index, 1);
};
const validateAllocation = () => {
@@ -556,28 +551,26 @@ const validateAllocation = () => {
proxy?.$modal.msgError('本月该预投项目没有可分配工时');
return false;
}
- for (const staff of form.value.staffAllocList || []) {
- const usedTargets = new Set();
- for (const item of staff.allocItems || []) {
- if (!item.targetProjectId) {
- proxy?.$modal.msgError(`员工[${staff.staffName}]存在未选择目标项目的分配行`);
- return false;
- }
- const targetProjectId = String(item.targetProjectId);
- if (usedTargets.has(targetProjectId)) {
- proxy?.$modal.msgError(`员工[${staff.staffName}]不能重复选择同一目标项目`);
- return false;
- }
- usedTargets.add(targetProjectId);
- if (toNumber(item.allocHours) <= 0) {
- proxy?.$modal.msgError(`员工[${staff.staffName}]分配工时必须大于0`);
- return false;
- }
- }
- if (calcStaffRemaining(staff) < -0.000001) {
- proxy?.$modal.msgError(`员工[${staff.staffName}]分配工时不能超过原预投工时`);
+ const usedTargets = new Set();
+ for (const item of form.value.allocItems || []) {
+ if (!item.targetProjectId) {
+ proxy?.$modal.msgError('存在未选择目标项目的分配行');
return false;
}
+ const targetProjectId = String(item.targetProjectId);
+ if (usedTargets.has(targetProjectId)) {
+ proxy?.$modal.msgError('同一张分配单不能重复选择同一目标项目');
+ return false;
+ }
+ usedTargets.add(targetProjectId);
+ if (toNumber(item.allocHours) <= 0) {
+ proxy?.$modal.msgError('分配工时必须大于0');
+ return false;
+ }
+ }
+ if (allocatedTotalHours.value - sourceTotalHours.value > 0.000001) {
+ proxy?.$modal.msgError('分配工时合计不能超过来源预投工时');
+ return false;
}
return true;
};
@@ -591,14 +584,9 @@ const buildSubmitPayload = (): TimesheetPreAllocForm => ({
staffCount: staffCount.value,
allocStatus: currentAllocStatus.value,
remark: form.value.remark,
- staffAllocList: (form.value.staffAllocList || []).map((staff) => ({
- staffUserId: staff.staffUserId,
- staffName: staff.staffName,
- sourceHours: toNumber(staff.sourceHours),
- allocItems: (staff.allocItems || []).map((item: PreAllocTarget) => ({
- targetProjectId: item.targetProjectId,
- allocHours: toNumber(item.allocHours)
- }))
+ allocItems: (form.value.allocItems || []).map((item) => ({
+ targetProjectId: item.targetProjectId,
+ allocHours: toNumber(item.allocHours)
}))
});
@@ -625,11 +613,6 @@ const submitForm = () => {
};
const handleDelete = async (row?: TimesheetPreAllocVO) => {
- const deleteRows = row ? [row] : selectedRows.value;
- if (deleteRows.some((item) => item.appliedFlag === '1')) {
- proxy?.$modal.msgError('已回写月汇总的预投工时分配单不允许删除');
- return;
- }
const allocIds = row?.allocId || ids.value;
await proxy?.$modal.confirm(`是否确认删除预投工时分配编号为"${allocIds}"的数据项?`).finally(() => (loading.value = false));
await delTimesheetPreAlloc(allocIds);
@@ -641,14 +624,16 @@ const handleExport = () => {
proxy?.download(
'oa/erp/timesheetPreAlloc/export',
{
- ...queryParams.value
+ ...queryParams.value,
+ // 如果选中了数据,则只导出选中的数据
+ allocIds: ids.value.length > 0 ? ids.value.join(',') : undefined
},
`timesheetPreAlloc_${new Date().getTime()}.xlsx`
);
};
onMounted(async () => {
- await loadProjectOptions();
+ await Promise.all([loadTargetProjectOptions(), loadQuerySourceProjectOptions()]);
await getList();
});
@@ -695,25 +680,19 @@ onMounted(async () => {
font-size: 16px;
}
-.alloc-table {
- width: 100%;
+.alloc-section {
+ min-height: 120px;
}
-.target-list {
+.target-toolbar {
display: flex;
- flex-direction: column;
- gap: 8px;
- padding: 4px 0;
-}
-
-.target-row {
- display: grid;
- grid-template-columns: minmax(260px, 1fr) 150px 32px;
- gap: 8px;
align-items: center;
+ justify-content: space-between;
+ margin-bottom: 8px;
+ font-weight: 600;
}
-.target-project-select {
+.alloc-table {
width: 100%;
}
@@ -726,9 +705,5 @@ onMounted(async () => {
.summary-strip {
grid-template-columns: repeat(2, minmax(120px, 1fr));
}
-
- .target-row {
- grid-template-columns: 1fr;
- }
}