diff --git a/src/service/api/performance.ts b/src/service/api/performance.ts index 9567369..a666f66 100644 --- a/src/service/api/performance.ts +++ b/src/service/api/performance.ts @@ -67,20 +67,20 @@ type MonthlyResultResponse = Omit & { - totalSheetCount?: number | string | null; + expectedSheetCount?: number | string | null; pendingSendCount?: number | string | null; pendingConfirmCount?: number | string | null; pendingSendUsers?: Array< Omit & { userId: StringIdResponse; - managerUserId: StringIdResponse; + managerUserId?: StringIdResponse | null; sheetId?: StringIdResponse | null; } > | null; @@ -188,13 +188,13 @@ function normalizeMonthlyResult(response: MonthlyResultResponse): Api.Performanc function normalizeTeamSummary(response: TeamSummaryResponse): Api.Performance.Team.Summary { return { ...response, - totalSheetCount: normalizeTotal(response.totalSheetCount), + expectedSheetCount: normalizeTotal(response.expectedSheetCount), pendingSendCount: normalizeTotal(response.pendingSendCount), pendingConfirmCount: normalizeTotal(response.pendingConfirmCount), pendingSendUsers: (response.pendingSendUsers || []).map(item => ({ ...item, userId: normalizeStringId(item.userId), - managerUserId: normalizeStringId(item.managerUserId), + managerUserId: normalizeNullableStringId(item.managerUserId), sheetId: normalizeNullableStringId(item.sheetId), statusCode: item.statusCode ?? null })), diff --git a/src/service/api/work-report.ts b/src/service/api/work-report.ts index f848061..8186ad2 100644 --- a/src/service/api/work-report.ts +++ b/src/service/api/work-report.ts @@ -99,15 +99,19 @@ type ProjectOptionResponse = Omit & { +type TeamReportUnsubmittedReportResponse = Omit< + Api.WorkReport.Common.TeamReportUnsubmittedReport, + 'userId' | 'projectId' +> & { userId: StringIdResponse; + projectId?: StringIdResponse | null; }; type TeamReportSummaryResponse = Omit< Api.WorkReport.Common.TeamReportSummary, - 'unsubmittedUsers' | 'periodStartDate' | 'periodEndDate' + 'unsubmittedReports' | 'periodStartDate' | 'periodEndDate' > & { - unsubmittedUsers?: TeamReportPendingUserResponse[] | null; + unsubmittedReports?: TeamReportUnsubmittedReportResponse[] | null; periodStartDate?: unknown; periodEndDate?: unknown; }; @@ -375,10 +379,16 @@ function normalizeTeamReportSummary(response: TeamReportSummaryResponse): Api.Wo ...response, periodStartDate: normalizeDateText(response.periodStartDate) ?? undefined, periodEndDate: normalizeDateText(response.periodEndDate) ?? undefined, - unsubmittedUsers: - response.unsubmittedUsers?.map(item => ({ + expectedReportCount: response.expectedReportCount ?? 0, + submittedReportCount: response.submittedReportCount ?? 0, + unsubmittedReportCount: response.unsubmittedReportCount ?? 0, + pendingApprovalReportCount: response.pendingApprovalReportCount ?? 0, + unsubmittedReports: + response.unsubmittedReports?.map(item => ({ ...item, - userId: normalizeStringId(item.userId) + userId: normalizeStringId(item.userId), + projectId: normalizeNullableStringId(item.projectId), + projectName: item.projectName ?? null })) ?? [] }; } diff --git a/src/typings/api/performance.d.ts b/src/typings/api/performance.d.ts index bc0f39b..f8b476f 100644 --- a/src/typings/api/performance.d.ts +++ b/src/typings/api/performance.d.ts @@ -181,15 +181,17 @@ declare namespace Api { } interface PendingSendUser { + periodMonth: string; userId: string; userNickname: string; - managerUserId: string; + managerUserId?: string | null; managerName: string; sheetId?: string | null; statusCode?: Common.SheetStatusCode | null; } interface PendingConfirmUser { + periodMonth: string; userId: string; userNickname: string; sheetId: string; @@ -207,7 +209,7 @@ declare namespace Api { interface Summary { periodMonthStart: string; periodMonthEnd: string; - totalSheetCount: number; + expectedSheetCount: number; pendingSendCount: number; pendingConfirmCount: number; confirmedRate: string | number; diff --git a/src/typings/api/work-report.d.ts b/src/typings/api/work-report.d.ts index 88c6e82..ca17469 100644 --- a/src/typings/api/work-report.d.ts +++ b/src/typings/api/work-report.d.ts @@ -72,19 +72,23 @@ declare namespace Api { list: T[]; } - interface TeamReportPendingUser { + interface TeamReportUnsubmittedReport { userId: string; userNickname: string; + periodKey: string; + periodLabel: string; + projectId?: string | null; + projectName?: string | null; } interface TeamReportSummary { periodStartDate?: string | null; periodEndDate?: string | null; - totalShouldSubmit: number; - submittedCount: number; - unsubmittedCount: number; - pendingApprovalCount: number; - unsubmittedUsers: TeamReportPendingUser[]; + expectedReportCount: number; + submittedReportCount: number; + unsubmittedReportCount: number; + pendingApprovalReportCount: number; + unsubmittedReports: TeamReportUnsubmittedReport[]; } interface TeamReportSummaryParams { @@ -98,6 +102,7 @@ declare namespace Api { reportType: ReportType; periodKey: string; userIds?: string[] | null; + projectId?: string | null; } interface TeamReportRemindResult { diff --git a/src/views/infra/log-management/login-log/index.vue b/src/views/infra/log-management/login-log/index.vue index 49f2c17..e884012 100644 --- a/src/views/infra/log-management/login-log/index.vue +++ b/src/views/infra/log-management/login-log/index.vue @@ -4,6 +4,7 @@ import { ElTag } from 'element-plus'; import { SYSTEM_LOGIN_RESULT_DICT_CODE, SYSTEM_LOGIN_TYPE_DICT_CODE } from '@/constants/dict'; import { fetchExportLoginLog, fetchGetLoginLog, fetchGetLoginLogPage } from '@/service/api'; import { useAuth } from '@/hooks/business/auth'; +import { useDict } from '@/hooks/business/dict'; import { useUIPaginatedTable } from '@/hooks/common/table'; import BusinessTableActionCell, { type BusinessTableAction } from '@/components/custom/business-table-action-cell'; import DictText from '@/components/custom/dict-text.vue'; @@ -46,12 +47,19 @@ function transformPageResult(response: LoginLogPageResponse, pageNo: number, pag } const { hasAuth } = useAuth(); +const { getLabel: getLoginResultLabel } = useDict(SYSTEM_LOGIN_RESULT_DICT_CODE); const searchParams = reactive(createSearchParams()); const detailVisible = ref(false); const currentRow = ref(null); const exporting = ref(false); const canExport = computed(() => hasAuth(LogPermission.LoginExport)); +function getLoginResultTagType(result: Api.SystemLog.Login.Log['result']) { + const label = getLoginResultLabel(result, ''); + + return label.includes('成功') ? 'success' : 'danger'; +} + const detailSections: LogDetailSection[] = [ { title: '基础信息', @@ -107,7 +115,7 @@ const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagi label: '登录结果', minWidth: 80, formatter: row => ( - + ) diff --git a/src/views/personal-center/my-performance/index.vue b/src/views/personal-center/my-performance/index.vue index ec9da67..80b4470 100644 --- a/src/views/personal-center/my-performance/index.vue +++ b/src/views/personal-center/my-performance/index.vue @@ -237,8 +237,8 @@ const { columns, columnChecks, data, loading, getDataByPage, mobilePagination, r } baseColumns.push( - { prop: 'employeeDeptName', label: '部门', minWidth: 110, showOverflowTooltip: true }, { prop: 'managerName', label: '直属上级', minWidth: 110, showOverflowTooltip: true }, + { prop: 'employeeDeptName', label: '部门', minWidth: 110, showOverflowTooltip: true }, { prop: 'statusCode', label: '状态', diff --git a/src/views/personal-center/my-performance/modules/performance-summary.vue b/src/views/personal-center/my-performance/modules/performance-summary.vue index 2d4a731..833517f 100644 --- a/src/views/personal-center/my-performance/modules/performance-summary.vue +++ b/src/views/personal-center/my-performance/modules/performance-summary.vue @@ -34,13 +34,21 @@ const periodLabel = computed(() => { }); const cards = computed(() => [ - { label: '绩效表总数', value: props.summary?.totalSheetCount ?? 0 }, + { label: '应有总数', value: props.summary?.expectedSheetCount ?? 0 }, { label: '待发送数', value: props.summary?.pendingSendCount ?? 0, key: 'pending_send' as const }, { label: '待确认数', value: props.summary?.pendingConfirmCount ?? 0, key: 'pending_confirm' as const }, { label: '已确认率', value: `${props.summary?.confirmedRate ?? '0.00'}%` }, { label: '各方向绩效平均分', value: deptOrgAverageCount.value, key: 'dept_org_average' as const } ]); +function getPendingSendStatusText(user: Api.Performance.Team.PendingSendUser) { + if (!user.sheetId || !user.statusCode) { + return '未创建绩效表'; + } + + return getPerformanceStatusLabel(user.statusCode); +} + async function handleRemind(type: Api.Performance.Common.RemindType, userIds?: string[]) { const key = userIds?.length === 1 ? `${type}:${userIds[0]}` : `${type}:all`; remindingKey.value = key; @@ -79,13 +87,14 @@ async function handleRemind(type: Api.Performance.Common.RemindType, userIds?: s
{{ user.userNickname }} - {{ getPerformanceStatusLabel(user.statusCode) }},提醒 {{ user.managerName || '直属上级' }} + {{ user.periodMonth }} · {{ getPendingSendStatusText(user) }},提醒 + {{ user.managerName || '直属上级' }}
{{ user.userNickname }} - 发送时间:{{ formatDateTime(user.sentTime) }} + {{ user.periodMonth }} · 发送时间:{{ formatDateTime(user.sentTime) }}
{
- - - + + + + + +
diff --git a/src/views/personal-center/work-report/shared/components/team-report-summary.vue b/src/views/personal-center/work-report/shared/components/team-report-summary.vue index 634ba36..927f4fc 100644 --- a/src/views/personal-center/work-report/shared/components/team-report-summary.vue +++ b/src/views/personal-center/work-report/shared/components/team-report-summary.vue @@ -3,6 +3,7 @@ import { computed, ref } from 'vue'; import dayjs from 'dayjs'; import { fetchRemindTeamReport } from '@/service/api'; import { formatIsoWeekRangeLabel } from '../utils'; +import { formatWeeklyPeriodLabel } from '../types'; defineOptions({ name: 'TeamReportSummary' }); @@ -26,7 +27,7 @@ const emit = defineEmits<{ }>(); const remindingAll = ref(false); -const remindingUserId = ref(''); +const remindingItemKey = ref(''); const canRemind = computed(() => props.periodKeys.length > 0); function formatSummaryPeriodLabel() { @@ -57,22 +58,54 @@ function formatSummaryPeriodLabel() { const displayPeriodLabel = computed(() => formatSummaryPeriodLabel()); const cards = computed(() => [ - { label: '应填人数', value: props.summary?.totalShouldSubmit ?? 0 }, - { label: '已提交', value: props.summary?.submittedCount ?? 0 }, - { label: '待提交', value: props.summary?.unsubmittedCount ?? 0, key: 'unsubmitted' as const }, - { label: '待审批', value: props.summary?.pendingApprovalCount ?? 0 } + { label: '应填报告数', value: props.summary?.expectedReportCount ?? 0 }, + { label: '已提交报告数', value: props.summary?.submittedReportCount ?? 0 }, + { label: '未提交报告数', value: props.summary?.unsubmittedReportCount ?? 0, key: 'unsubmitted' as const }, + { label: '待审批报告数', value: props.summary?.pendingApprovalReportCount ?? 0 } ]); +function getUnsubmittedReportSecondaryText(item: Api.WorkReport.Common.TeamReportUnsubmittedReport) { + if (props.reportType === 'project') { + return item.projectName ? `${item.projectName} · ${item.periodLabel}` : item.periodLabel; + } + + if (props.reportType === 'weekly') { + const weeklyPeriodMatch = item.periodKey.match(/^weekly-(\d{4}-\d{2}-\d{2})-(\d{4}-\d{2}-\d{2})$/u); + + if (weeklyPeriodMatch) { + return formatWeeklyPeriodLabel(weeklyPeriodMatch[1], weeklyPeriodMatch[2], item.periodLabel); + } + + const weeklyRangeMatch = item.periodLabel.match(/^(\d{4}-\d{2}-\d{2})\s*至\s*(\d{4}-\d{2}-\d{2})$/u); + + if (weeklyRangeMatch) { + return formatWeeklyPeriodLabel(weeklyRangeMatch[1], weeklyRangeMatch[2], item.periodLabel); + } + + return formatWeeklyPeriodLabel(undefined, undefined, item.periodLabel); + } + + return item.periodLabel; +} + +function getUnsubmittedReportKey(item: Api.WorkReport.Common.TeamReportUnsubmittedReport) { + return `${item.userId}-${item.projectId || 'none'}-${item.periodKey}`; +} + +function getSingleRemindSuccessText(remindedCount: number, preciseProjectReminder: boolean) { + if (preciseProjectReminder) { + return `已发送 ${remindedCount} 条催办提醒`; + } + + return `已催办 ${remindedCount} 人`; +} + async function handleRemind(userIds?: string[]) { if (!props.periodKeys.length) return; - const targetUserId = userIds?.length === 1 ? userIds[0] : ''; + const isSingleUserReminder = userIds?.length === 1; - if (targetUserId) { - remindingUserId.value = targetUserId; - } else { - remindingAll.value = true; - } + remindingAll.value = true; const results = await Promise.all( props.periodKeys.map(periodKey => @@ -84,10 +117,7 @@ async function handleRemind(userIds?: string[]) { ) ); - if (!targetUserId) { - remindingAll.value = false; - } - remindingUserId.value = ''; + remindingAll.value = false; const remindedCount = results.reduce((total, result) => { if (result.error) return total; @@ -96,7 +126,33 @@ async function handleRemind(userIds?: string[]) { if (!remindedCount) return; - window.$message?.success(props.periodKeys.length > 1 ? '已按所选区间发送催办提醒' : `已催办 ${remindedCount} 人`); + window.$message?.success( + props.periodKeys.length > 1 + ? '已按所选区间发送催办提醒' + : getSingleRemindSuccessText(remindedCount, props.reportType === 'project' && isSingleUserReminder) + ); + emit('reminded'); +} + +async function handleRemindItem(item: Api.WorkReport.Common.TeamReportUnsubmittedReport) { + const itemKey = getUnsubmittedReportKey(item); + remindingItemKey.value = itemKey; + + const result = await fetchRemindTeamReport({ + reportType: props.reportType, + periodKey: item.periodKey, + userIds: [item.userId], + projectId: props.reportType === 'project' ? item.projectId : undefined + }); + + remindingItemKey.value = ''; + + if (result.error) return; + + const remindedCount = result.data?.remindedCount ?? 0; + if (!remindedCount) return; + + window.$message?.success(getSingleRemindSuccessText(remindedCount, props.reportType === 'project')); emit('reminded'); } @@ -116,26 +172,31 @@ async function handleRemind(userIds?: string[]) {
-
未提交人员
-
+
未提交报告
+
- {{ user.userNickname }} +
+
{{ item.userNickname }}
+
+ {{ getUnsubmittedReportSecondaryText(item) }} +
+
催办
- +