feat(performance): 新增绩效下签功能支持
- 在月报审批页面增加 sign-off 场景支持 - 添加绩效下签对话框组件实现批量下签功能 - 增加导入绩效压缩包和直接下签两种操作模式 - 实现电子签名预览和日期格式化显示功能 - 更新绩效权限配置增加下签相关权限 - 优化审批对话框标题和按钮文案显示 - 添加导入结果提示和文件验证功能 - 实现团队模式下的下签操作入口 - 完善绩效表格状态和操作权限判断逻辑
This commit is contained in:
@@ -44,6 +44,15 @@ type SheetPageResponse = {
|
||||
list: SheetResponse[];
|
||||
};
|
||||
|
||||
type ImportZipResultResponse = Omit<Api.Performance.Sheet.ImportZipResult, 'messages'> & {
|
||||
workbookCount?: number | string | null;
|
||||
sheetCount?: number | string | null;
|
||||
successCount?: number | string | null;
|
||||
skippedCount?: number | string | null;
|
||||
failedCount?: number | string | null;
|
||||
messages?: string[] | null;
|
||||
};
|
||||
|
||||
type StatusLogResponse = Omit<Api.Performance.Sheet.StatusLog, 'id' | 'sheetId' | 'operatorUserId'> & {
|
||||
id: StringIdResponse;
|
||||
sheetId: StringIdResponse;
|
||||
@@ -144,6 +153,8 @@ function normalizeSheet(response: SheetResponse): Api.Performance.Sheet.Sheet {
|
||||
fileId: normalizeNullableStringId(response.fileId),
|
||||
fileName: response.fileName ?? null,
|
||||
statusName: response.statusName || response.statusCode,
|
||||
signOffStatus: response.signOffStatus ?? null,
|
||||
signOffStatusName: response.signOffStatusName ?? null,
|
||||
actualScoreTotal: response.actualScoreTotal ?? null,
|
||||
baseScoreTotal: response.baseScoreTotal ?? null,
|
||||
extraScoreTotal: response.extraScoreTotal ?? null,
|
||||
@@ -282,6 +293,7 @@ function createSheetQuery(params: Api.Performance.Sheet.SearchParams = {}) {
|
||||
appendValue(query, 'employeeDeptId', params.employeeDeptId);
|
||||
appendValue(query, 'managerName', params.managerName);
|
||||
appendValue(query, 'statusCode', params.statusCode);
|
||||
appendValue(query, 'signOffStatus', params.signOffStatus);
|
||||
|
||||
return query.toString();
|
||||
}
|
||||
@@ -377,6 +389,46 @@ export async function createPerformanceSheet(data: Api.Performance.Sheet.CreateP
|
||||
return mapServiceResult(result as ServiceRequestResult<StringIdResponse>, normalizeStringId);
|
||||
}
|
||||
|
||||
export async function importPerformanceSheetZip(data: Api.Performance.Sheet.ImportZipParams) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', data.file);
|
||||
formData.append('periodMonth', data.periodMonth);
|
||||
data.employeeIds?.forEach(id => {
|
||||
formData.append('employeeIds', id);
|
||||
});
|
||||
|
||||
const result = await request<ImportZipResultResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${SHEET_PREFIX}/import-zip`,
|
||||
method: 'post',
|
||||
data: formData
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<ImportZipResultResponse>, payload => ({
|
||||
workbookCount: normalizeTotal(payload.workbookCount),
|
||||
sheetCount: normalizeTotal(payload.sheetCount),
|
||||
successCount: normalizeTotal(payload.successCount),
|
||||
skippedCount: normalizeTotal(payload.skippedCount),
|
||||
failedCount: normalizeTotal(payload.failedCount),
|
||||
messages: payload.messages ?? []
|
||||
}));
|
||||
}
|
||||
|
||||
export async function signOffPerformanceSheets(data: Api.Performance.Sheet.SignOffParams) {
|
||||
const result = await request<StringIdResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${SHEET_PREFIX}/sign-off`,
|
||||
method: 'post',
|
||||
data: {
|
||||
...data,
|
||||
employeeIds: data.employeeIds && data.employeeIds.length ? data.employeeIds : undefined,
|
||||
remark: data.remark?.trim() || undefined
|
||||
}
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<StringIdResponse>, normalizeStringId);
|
||||
}
|
||||
|
||||
export function updatePerformanceSheetExcel(id: string, data: Api.Performance.Sheet.ExcelUpdateParams) {
|
||||
return request<boolean>({
|
||||
...safeJsonRequestConfig,
|
||||
|
||||
@@ -17,6 +17,7 @@ const DEPT_PREFIX = `${SYSTEM_SERVICE_PREFIX}/dept`;
|
||||
const POST_PREFIX = `${SYSTEM_SERVICE_PREFIX}/post`;
|
||||
const ORG_LEADER_PREFIX = `${SYSTEM_SERVICE_PREFIX}/org-leader`;
|
||||
const USER_MANAGEMENT_RELATION_PREFIX = `${SYSTEM_SERVICE_PREFIX}/user-management-relation`;
|
||||
const USER_SIGNATURE_PREFIX = `${SYSTEM_SERVICE_PREFIX}/user-signatures`;
|
||||
|
||||
function appendQueryValue(query: URLSearchParams, key: string, value: unknown) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
@@ -146,6 +147,12 @@ type MySubordinateTreeNodeResponse = Omit<Api.SystemManage.MySubordinateTreeNode
|
||||
children?: MySubordinateTreeNodeResponse[] | null;
|
||||
};
|
||||
|
||||
type UserSignatureResponse = Omit<Api.SystemManage.UserSignature, 'id' | 'userId' | 'fileId'> & {
|
||||
id: string | number;
|
||||
userId: string | number;
|
||||
fileId: string | number;
|
||||
};
|
||||
|
||||
function normalizeUserSimple(user: UserSimpleResponse): Api.SystemManage.UserSimple {
|
||||
return {
|
||||
...user,
|
||||
@@ -217,6 +224,20 @@ function normalizeMySubordinateTreeNode(node: MySubordinateTreeNodeResponse): Ap
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeUserSignature(signature: UserSignatureResponse): Api.SystemManage.UserSignature {
|
||||
return {
|
||||
...signature,
|
||||
id: normalizeStringId(signature.id),
|
||||
userId: normalizeStringId(signature.userId),
|
||||
fileId: normalizeStringId(signature.fileId),
|
||||
fileName: signature.fileName ?? null,
|
||||
status: signature.status ?? null,
|
||||
remark: signature.remark ?? null,
|
||||
createTime: signature.createTime ?? null,
|
||||
updateTime: signature.updateTime ?? null
|
||||
};
|
||||
}
|
||||
|
||||
/** 获取角色分页 */
|
||||
export async function fetchGetRolePage(params?: Api.SystemManage.RoleSearchParams) {
|
||||
const query = createRolePageQuery(params);
|
||||
@@ -742,6 +763,49 @@ export function fetchAssignUserRoles(data: Api.SystemManage.AssignUserRoleParams
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchGetUserSignature(userId: string) {
|
||||
const result = await request<UserSignatureResponse | null>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${USER_SIGNATURE_PREFIX}/get`,
|
||||
method: 'get',
|
||||
params: { userId }
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<UserSignatureResponse | null>, data =>
|
||||
data ? normalizeUserSignature(data) : null
|
||||
);
|
||||
}
|
||||
|
||||
export function fetchSaveUserSignature(data: Api.SystemManage.SaveUserSignatureParams) {
|
||||
return request<boolean>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${USER_SIGNATURE_PREFIX}/save`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchDeleteUserSignature(userId: string) {
|
||||
return request<boolean>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${USER_SIGNATURE_PREFIX}/delete`,
|
||||
method: 'delete',
|
||||
params: { userId }
|
||||
});
|
||||
}
|
||||
|
||||
export async function fetchGetMyUserSignature() {
|
||||
const result = await request<UserSignatureResponse | null>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${USER_SIGNATURE_PREFIX}/my`,
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<UserSignatureResponse | null>, data =>
|
||||
data ? normalizeUserSignature(data) : null
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 用户管理链路相关 API ====================
|
||||
/**
|
||||
* 获取用户管理链路树形结构
|
||||
|
||||
@@ -88,11 +88,18 @@ type ApprovalRecordResponse = Omit<
|
||||
|
||||
type MonthlyApprovalRecordResponse = Omit<
|
||||
Api.WorkReport.Monthly.MonthlyReportApprovalRecord,
|
||||
'id' | 'statusLogId' | 'auditorUserId'
|
||||
'id' | 'statusLogId' | 'auditorUserId' | 'employeeSignatureFileId' | 'supervisorSignatureFileId'
|
||||
> & {
|
||||
id: StringIdResponse;
|
||||
statusLogId: StringIdResponse;
|
||||
auditorUserId: StringIdResponse;
|
||||
employeeSignatureFileId?: MaybeStringIdResponse;
|
||||
supervisorSignatureFileId?: MaybeStringIdResponse;
|
||||
};
|
||||
|
||||
type MonthlySignOffDetailResponse = {
|
||||
report: MonthlyReportResponse;
|
||||
latestApprovalRecord?: MonthlyApprovalRecordResponse | null;
|
||||
};
|
||||
|
||||
type ProjectOptionResponse = Omit<Api.WorkReport.Project.ProjectReportOwnerProjectOption, 'id'> & {
|
||||
@@ -229,6 +236,7 @@ function createWeeklyPageQuery(params: Api.WorkReport.Weekly.WeeklyReportSearchP
|
||||
function createMonthlyPageQuery(params: Api.WorkReport.Monthly.MonthlyReportSearchParams = {}) {
|
||||
const query = createBasePageQuery(params);
|
||||
appendNullableArrayFlag(query, 'reporterIds', params.reporterIds);
|
||||
appendValue(query, 'signOffStatus', params.signOffStatus);
|
||||
return query.toString();
|
||||
}
|
||||
|
||||
@@ -298,6 +306,8 @@ function normalizeMonthlyReport(response: MonthlyReportResponse): Api.WorkReport
|
||||
reporterDeptName: response.reporterDeptName ?? null,
|
||||
reporterPostName: response.reporterPostName ?? null,
|
||||
statusName: response.statusName || response.statusCode,
|
||||
signOffStatus: response.signOffStatus ?? null,
|
||||
signOffStatusName: response.signOffStatusName ?? null,
|
||||
allowEdit: normalizeBooleanFlag(response.allowEdit),
|
||||
terminal: normalizeBooleanFlag(response.terminal),
|
||||
totalWorkHours: normalizeReportTotalWorkHours(response.totalWorkHours, fallbackTotalWorkHours),
|
||||
@@ -361,7 +371,12 @@ function normalizeMonthlyApprovalRecord(
|
||||
statusLogId: normalizeStringId(response.statusLogId),
|
||||
auditorUserId: normalizeStringId(response.auditorUserId),
|
||||
conclusion: normalizeApprovalConclusion(response.conclusion),
|
||||
opinion: response.opinion ?? null
|
||||
opinion: response.opinion ?? null,
|
||||
meetingDate: normalizeDateText(response.meetingDate) ?? null,
|
||||
employeeSignatureFileId: normalizeNullableStringId(response.employeeSignatureFileId),
|
||||
employeeSignedDate: normalizeDateText(response.employeeSignedDate) ?? null,
|
||||
supervisorSignatureFileId: normalizeNullableStringId(response.supervisorSignatureFileId),
|
||||
supervisorSignedDate: normalizeDateText(response.supervisorSignedDate) ?? null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -697,6 +712,19 @@ export async function fetchGetMonthlyReportApprovalPage(params: Api.WorkReport.M
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetchGetMonthlyReportSignOffPage(params: Api.WorkReport.Monthly.MonthlyReportSearchParams = {}) {
|
||||
const query = createMonthlyPageQuery(params);
|
||||
const result = await request<PageResponse<MonthlyReportResponse>>({
|
||||
...safeJsonRequestConfig,
|
||||
url: query ? `${MONTHLY_PREFIX}/sign-off-page?${query}` : `${MONTHLY_PREFIX}/sign-off-page`,
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<PageResponse<MonthlyReportResponse>>, data =>
|
||||
mapPage(data, normalizeMonthlyReport)
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetchGetMonthlyReportDetail(id: string) {
|
||||
const result = await request<MonthlyReportResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
@@ -799,6 +827,34 @@ export async function fetchGetMonthlyReportApprovalRecords(id: string) {
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetchGetMonthlyReportSignOffDetail(id: string) {
|
||||
const result = await request<MonthlySignOffDetailResponse>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${MONTHLY_PREFIX}/${id}/sign-off-detail`,
|
||||
method: 'get'
|
||||
});
|
||||
|
||||
return mapServiceResult(result as ServiceRequestResult<MonthlySignOffDetailResponse>, data => ({
|
||||
report: normalizeMonthlyReport(data.report),
|
||||
latestApprovalRecord: data.latestApprovalRecord ? normalizeMonthlyApprovalRecord(data.latestApprovalRecord) : null
|
||||
}));
|
||||
}
|
||||
|
||||
export function fetchConfirmMonthlyReportSignOff(
|
||||
id: string,
|
||||
data: Api.WorkReport.Monthly.MonthlyReportSignOffConfirmParams
|
||||
) {
|
||||
return request<boolean>({
|
||||
...safeJsonRequestConfig,
|
||||
url: `${MONTHLY_PREFIX}/${id}/confirm-sign-off`,
|
||||
method: 'post',
|
||||
data: {
|
||||
meetingDate: data.meetingDate,
|
||||
employeeSignedDate: data.employeeSignedDate || undefined
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchExportMonthlyReports(params: Api.WorkReport.Monthly.MonthlyReportSearchParams = {}) {
|
||||
const query = createMonthlyPageQuery(params);
|
||||
return request<Blob, 'blob'>({
|
||||
|
||||
Reference in New Issue
Block a user