fix(工作报告、我的绩效、产品/项目、消息通知): 修复已知的一些问题。
This commit is contained in:
@@ -67,20 +67,20 @@ type MonthlyResultResponse = Omit<Api.Performance.Sheet.MonthlyResult, 'sheetId'
|
|||||||
|
|
||||||
type TeamSummaryResponse = Omit<
|
type TeamSummaryResponse = Omit<
|
||||||
Api.Performance.Team.Summary,
|
Api.Performance.Team.Summary,
|
||||||
| 'totalSheetCount'
|
| 'expectedSheetCount'
|
||||||
| 'pendingSendCount'
|
| 'pendingSendCount'
|
||||||
| 'pendingConfirmCount'
|
| 'pendingConfirmCount'
|
||||||
| 'pendingSendUsers'
|
| 'pendingSendUsers'
|
||||||
| 'pendingConfirmUsers'
|
| 'pendingConfirmUsers'
|
||||||
| 'deptOrgAverages'
|
| 'deptOrgAverages'
|
||||||
> & {
|
> & {
|
||||||
totalSheetCount?: number | string | null;
|
expectedSheetCount?: number | string | null;
|
||||||
pendingSendCount?: number | string | null;
|
pendingSendCount?: number | string | null;
|
||||||
pendingConfirmCount?: number | string | null;
|
pendingConfirmCount?: number | string | null;
|
||||||
pendingSendUsers?: Array<
|
pendingSendUsers?: Array<
|
||||||
Omit<Api.Performance.Team.PendingSendUser, 'userId' | 'managerUserId' | 'sheetId'> & {
|
Omit<Api.Performance.Team.PendingSendUser, 'userId' | 'managerUserId' | 'sheetId'> & {
|
||||||
userId: StringIdResponse;
|
userId: StringIdResponse;
|
||||||
managerUserId: StringIdResponse;
|
managerUserId?: StringIdResponse | null;
|
||||||
sheetId?: StringIdResponse | null;
|
sheetId?: StringIdResponse | null;
|
||||||
}
|
}
|
||||||
> | null;
|
> | null;
|
||||||
@@ -188,13 +188,13 @@ function normalizeMonthlyResult(response: MonthlyResultResponse): Api.Performanc
|
|||||||
function normalizeTeamSummary(response: TeamSummaryResponse): Api.Performance.Team.Summary {
|
function normalizeTeamSummary(response: TeamSummaryResponse): Api.Performance.Team.Summary {
|
||||||
return {
|
return {
|
||||||
...response,
|
...response,
|
||||||
totalSheetCount: normalizeTotal(response.totalSheetCount),
|
expectedSheetCount: normalizeTotal(response.expectedSheetCount),
|
||||||
pendingSendCount: normalizeTotal(response.pendingSendCount),
|
pendingSendCount: normalizeTotal(response.pendingSendCount),
|
||||||
pendingConfirmCount: normalizeTotal(response.pendingConfirmCount),
|
pendingConfirmCount: normalizeTotal(response.pendingConfirmCount),
|
||||||
pendingSendUsers: (response.pendingSendUsers || []).map(item => ({
|
pendingSendUsers: (response.pendingSendUsers || []).map(item => ({
|
||||||
...item,
|
...item,
|
||||||
userId: normalizeStringId(item.userId),
|
userId: normalizeStringId(item.userId),
|
||||||
managerUserId: normalizeStringId(item.managerUserId),
|
managerUserId: normalizeNullableStringId(item.managerUserId),
|
||||||
sheetId: normalizeNullableStringId(item.sheetId),
|
sheetId: normalizeNullableStringId(item.sheetId),
|
||||||
statusCode: item.statusCode ?? null
|
statusCode: item.statusCode ?? null
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -99,15 +99,19 @@ type ProjectOptionResponse = Omit<Api.WorkReport.Project.ProjectReportOwnerProje
|
|||||||
id: StringIdResponse;
|
id: StringIdResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TeamReportPendingUserResponse = Omit<Api.WorkReport.Common.TeamReportPendingUser, 'userId'> & {
|
type TeamReportUnsubmittedReportResponse = Omit<
|
||||||
|
Api.WorkReport.Common.TeamReportUnsubmittedReport,
|
||||||
|
'userId' | 'projectId'
|
||||||
|
> & {
|
||||||
userId: StringIdResponse;
|
userId: StringIdResponse;
|
||||||
|
projectId?: StringIdResponse | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TeamReportSummaryResponse = Omit<
|
type TeamReportSummaryResponse = Omit<
|
||||||
Api.WorkReport.Common.TeamReportSummary,
|
Api.WorkReport.Common.TeamReportSummary,
|
||||||
'unsubmittedUsers' | 'periodStartDate' | 'periodEndDate'
|
'unsubmittedReports' | 'periodStartDate' | 'periodEndDate'
|
||||||
> & {
|
> & {
|
||||||
unsubmittedUsers?: TeamReportPendingUserResponse[] | null;
|
unsubmittedReports?: TeamReportUnsubmittedReportResponse[] | null;
|
||||||
periodStartDate?: unknown;
|
periodStartDate?: unknown;
|
||||||
periodEndDate?: unknown;
|
periodEndDate?: unknown;
|
||||||
};
|
};
|
||||||
@@ -375,10 +379,16 @@ function normalizeTeamReportSummary(response: TeamReportSummaryResponse): Api.Wo
|
|||||||
...response,
|
...response,
|
||||||
periodStartDate: normalizeDateText(response.periodStartDate) ?? undefined,
|
periodStartDate: normalizeDateText(response.periodStartDate) ?? undefined,
|
||||||
periodEndDate: normalizeDateText(response.periodEndDate) ?? undefined,
|
periodEndDate: normalizeDateText(response.periodEndDate) ?? undefined,
|
||||||
unsubmittedUsers:
|
expectedReportCount: response.expectedReportCount ?? 0,
|
||||||
response.unsubmittedUsers?.map(item => ({
|
submittedReportCount: response.submittedReportCount ?? 0,
|
||||||
|
unsubmittedReportCount: response.unsubmittedReportCount ?? 0,
|
||||||
|
pendingApprovalReportCount: response.pendingApprovalReportCount ?? 0,
|
||||||
|
unsubmittedReports:
|
||||||
|
response.unsubmittedReports?.map(item => ({
|
||||||
...item,
|
...item,
|
||||||
userId: normalizeStringId(item.userId)
|
userId: normalizeStringId(item.userId),
|
||||||
|
projectId: normalizeNullableStringId(item.projectId),
|
||||||
|
projectName: item.projectName ?? null
|
||||||
})) ?? []
|
})) ?? []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/typings/api/performance.d.ts
vendored
6
src/typings/api/performance.d.ts
vendored
@@ -181,15 +181,17 @@ declare namespace Api {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface PendingSendUser {
|
interface PendingSendUser {
|
||||||
|
periodMonth: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
userNickname: string;
|
userNickname: string;
|
||||||
managerUserId: string;
|
managerUserId?: string | null;
|
||||||
managerName: string;
|
managerName: string;
|
||||||
sheetId?: string | null;
|
sheetId?: string | null;
|
||||||
statusCode?: Common.SheetStatusCode | null;
|
statusCode?: Common.SheetStatusCode | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PendingConfirmUser {
|
interface PendingConfirmUser {
|
||||||
|
periodMonth: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
userNickname: string;
|
userNickname: string;
|
||||||
sheetId: string;
|
sheetId: string;
|
||||||
@@ -207,7 +209,7 @@ declare namespace Api {
|
|||||||
interface Summary {
|
interface Summary {
|
||||||
periodMonthStart: string;
|
periodMonthStart: string;
|
||||||
periodMonthEnd: string;
|
periodMonthEnd: string;
|
||||||
totalSheetCount: number;
|
expectedSheetCount: number;
|
||||||
pendingSendCount: number;
|
pendingSendCount: number;
|
||||||
pendingConfirmCount: number;
|
pendingConfirmCount: number;
|
||||||
confirmedRate: string | number;
|
confirmedRate: string | number;
|
||||||
|
|||||||
17
src/typings/api/work-report.d.ts
vendored
17
src/typings/api/work-report.d.ts
vendored
@@ -72,19 +72,23 @@ declare namespace Api {
|
|||||||
list: T[];
|
list: T[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TeamReportPendingUser {
|
interface TeamReportUnsubmittedReport {
|
||||||
userId: string;
|
userId: string;
|
||||||
userNickname: string;
|
userNickname: string;
|
||||||
|
periodKey: string;
|
||||||
|
periodLabel: string;
|
||||||
|
projectId?: string | null;
|
||||||
|
projectName?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TeamReportSummary {
|
interface TeamReportSummary {
|
||||||
periodStartDate?: string | null;
|
periodStartDate?: string | null;
|
||||||
periodEndDate?: string | null;
|
periodEndDate?: string | null;
|
||||||
totalShouldSubmit: number;
|
expectedReportCount: number;
|
||||||
submittedCount: number;
|
submittedReportCount: number;
|
||||||
unsubmittedCount: number;
|
unsubmittedReportCount: number;
|
||||||
pendingApprovalCount: number;
|
pendingApprovalReportCount: number;
|
||||||
unsubmittedUsers: TeamReportPendingUser[];
|
unsubmittedReports: TeamReportUnsubmittedReport[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TeamReportSummaryParams {
|
interface TeamReportSummaryParams {
|
||||||
@@ -98,6 +102,7 @@ declare namespace Api {
|
|||||||
reportType: ReportType;
|
reportType: ReportType;
|
||||||
periodKey: string;
|
periodKey: string;
|
||||||
userIds?: string[] | null;
|
userIds?: string[] | null;
|
||||||
|
projectId?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TeamReportRemindResult {
|
interface TeamReportRemindResult {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { ElTag } from 'element-plus';
|
|||||||
import { SYSTEM_LOGIN_RESULT_DICT_CODE, SYSTEM_LOGIN_TYPE_DICT_CODE } from '@/constants/dict';
|
import { SYSTEM_LOGIN_RESULT_DICT_CODE, SYSTEM_LOGIN_TYPE_DICT_CODE } from '@/constants/dict';
|
||||||
import { fetchExportLoginLog, fetchGetLoginLog, fetchGetLoginLogPage } from '@/service/api';
|
import { fetchExportLoginLog, fetchGetLoginLog, fetchGetLoginLogPage } from '@/service/api';
|
||||||
import { useAuth } from '@/hooks/business/auth';
|
import { useAuth } from '@/hooks/business/auth';
|
||||||
|
import { useDict } from '@/hooks/business/dict';
|
||||||
import { useUIPaginatedTable } from '@/hooks/common/table';
|
import { useUIPaginatedTable } from '@/hooks/common/table';
|
||||||
import BusinessTableActionCell, { type BusinessTableAction } from '@/components/custom/business-table-action-cell';
|
import BusinessTableActionCell, { type BusinessTableAction } from '@/components/custom/business-table-action-cell';
|
||||||
import DictText from '@/components/custom/dict-text.vue';
|
import DictText from '@/components/custom/dict-text.vue';
|
||||||
@@ -46,12 +47,19 @@ function transformPageResult(response: LoginLogPageResponse, pageNo: number, pag
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { hasAuth } = useAuth();
|
const { hasAuth } = useAuth();
|
||||||
|
const { getLabel: getLoginResultLabel } = useDict(SYSTEM_LOGIN_RESULT_DICT_CODE);
|
||||||
const searchParams = reactive(createSearchParams());
|
const searchParams = reactive(createSearchParams());
|
||||||
const detailVisible = ref(false);
|
const detailVisible = ref(false);
|
||||||
const currentRow = ref<Api.SystemLog.Login.Log | null>(null);
|
const currentRow = ref<Api.SystemLog.Login.Log | null>(null);
|
||||||
const exporting = ref(false);
|
const exporting = ref(false);
|
||||||
const canExport = computed(() => hasAuth(LogPermission.LoginExport));
|
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[] = [
|
const detailSections: LogDetailSection[] = [
|
||||||
{
|
{
|
||||||
title: '基础信息',
|
title: '基础信息',
|
||||||
@@ -107,7 +115,7 @@ const { columns, columnChecks, data, loading, getData, getDataByPage, mobilePagi
|
|||||||
label: '登录结果',
|
label: '登录结果',
|
||||||
minWidth: 80,
|
minWidth: 80,
|
||||||
formatter: row => (
|
formatter: row => (
|
||||||
<ElTag type={row.result === 1 ? 'success' : 'danger'}>
|
<ElTag type={getLoginResultTagType(row.result)}>
|
||||||
<DictText dictCode={SYSTEM_LOGIN_RESULT_DICT_CODE} value={row.result} />
|
<DictText dictCode={SYSTEM_LOGIN_RESULT_DICT_CODE} value={row.result} />
|
||||||
</ElTag>
|
</ElTag>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -237,8 +237,8 @@ const { columns, columnChecks, data, loading, getDataByPage, mobilePagination, r
|
|||||||
}
|
}
|
||||||
|
|
||||||
baseColumns.push(
|
baseColumns.push(
|
||||||
{ prop: 'employeeDeptName', label: '部门', minWidth: 110, showOverflowTooltip: true },
|
|
||||||
{ prop: 'managerName', label: '直属上级', minWidth: 110, showOverflowTooltip: true },
|
{ prop: 'managerName', label: '直属上级', minWidth: 110, showOverflowTooltip: true },
|
||||||
|
{ prop: 'employeeDeptName', label: '部门', minWidth: 110, showOverflowTooltip: true },
|
||||||
{
|
{
|
||||||
prop: 'statusCode',
|
prop: 'statusCode',
|
||||||
label: '状态',
|
label: '状态',
|
||||||
|
|||||||
@@ -34,13 +34,21 @@ const periodLabel = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const cards = 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?.pendingSendCount ?? 0, key: 'pending_send' as const },
|
||||||
{ label: '待确认数', value: props.summary?.pendingConfirmCount ?? 0, key: 'pending_confirm' as const },
|
{ label: '待确认数', value: props.summary?.pendingConfirmCount ?? 0, key: 'pending_confirm' as const },
|
||||||
{ label: '已确认率', value: `${props.summary?.confirmedRate ?? '0.00'}%` },
|
{ label: '已确认率', value: `${props.summary?.confirmedRate ?? '0.00'}%` },
|
||||||
{ label: '各方向绩效平均分', value: deptOrgAverageCount.value, key: 'dept_org_average' as const }
|
{ 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[]) {
|
async function handleRemind(type: Api.Performance.Common.RemindType, userIds?: string[]) {
|
||||||
const key = userIds?.length === 1 ? `${type}:${userIds[0]}` : `${type}:all`;
|
const key = userIds?.length === 1 ? `${type}:${userIds[0]}` : `${type}:all`;
|
||||||
remindingKey.value = key;
|
remindingKey.value = key;
|
||||||
@@ -79,13 +87,14 @@ async function handleRemind(type: Api.Performance.Common.RemindType, userIds?: s
|
|||||||
<div v-if="props.summary?.pendingSendUsers?.length" class="performance-summary__user-list">
|
<div v-if="props.summary?.pendingSendUsers?.length" class="performance-summary__user-list">
|
||||||
<div
|
<div
|
||||||
v-for="user in props.summary.pendingSendUsers"
|
v-for="user in props.summary.pendingSendUsers"
|
||||||
:key="`${user.userId}-${user.sheetId || 'none'}`"
|
:key="`${user.userId}-${user.periodMonth}`"
|
||||||
class="performance-summary__user-item"
|
class="performance-summary__user-item"
|
||||||
>
|
>
|
||||||
<div class="performance-summary__user-main">
|
<div class="performance-summary__user-main">
|
||||||
<span>{{ user.userNickname }}</span>
|
<span>{{ user.userNickname }}</span>
|
||||||
<small>
|
<small>
|
||||||
{{ getPerformanceStatusLabel(user.statusCode) }},提醒 {{ user.managerName || '直属上级' }}
|
{{ user.periodMonth }} · {{ getPendingSendStatusText(user) }},提醒
|
||||||
|
{{ user.managerName || '直属上级' }}
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<ElButton
|
<ElButton
|
||||||
@@ -127,12 +136,12 @@ async function handleRemind(type: Api.Performance.Common.RemindType, userIds?: s
|
|||||||
<div v-if="props.summary?.pendingConfirmUsers?.length" class="performance-summary__user-list">
|
<div v-if="props.summary?.pendingConfirmUsers?.length" class="performance-summary__user-list">
|
||||||
<div
|
<div
|
||||||
v-for="user in props.summary.pendingConfirmUsers"
|
v-for="user in props.summary.pendingConfirmUsers"
|
||||||
:key="user.sheetId"
|
:key="`${user.userId}-${user.periodMonth}`"
|
||||||
class="performance-summary__user-item"
|
class="performance-summary__user-item"
|
||||||
>
|
>
|
||||||
<div class="performance-summary__user-main">
|
<div class="performance-summary__user-main">
|
||||||
<span>{{ user.userNickname }}</span>
|
<span>{{ user.userNickname }}</span>
|
||||||
<small>发送时间:{{ formatDateTime(user.sentTime) }}</small>
|
<small>{{ user.periodMonth }} · 发送时间:{{ formatDateTime(user.sentTime) }}</small>
|
||||||
</div>
|
</div>
|
||||||
<ElButton
|
<ElButton
|
||||||
link
|
link
|
||||||
|
|||||||
@@ -273,12 +273,12 @@ watch(visible, isVisible => {
|
|||||||
</div>
|
</div>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<ElFormItem
|
<!-- <ElFormItem-->
|
||||||
label="备注"
|
<!-- label="备注"-->
|
||||||
class="performance-template-dialog__field performance-template-dialog__field--full"
|
<!-- class="performance-template-dialog__field performance-template-dialog__field--full"-->
|
||||||
>
|
<!-- >-->
|
||||||
<ElInput v-model="uploadForm.remark" type="textarea" :rows="3" maxlength="500" show-word-limit />
|
<!-- <ElInput v-model="uploadForm.remark" type="textarea" :rows="3" maxlength="500" show-word-limit />-->
|
||||||
</ElFormItem>
|
<!-- </ElFormItem>-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="performance-template-dialog__actions">
|
<div class="performance-template-dialog__actions">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { computed, ref } from 'vue';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { fetchRemindTeamReport } from '@/service/api';
|
import { fetchRemindTeamReport } from '@/service/api';
|
||||||
import { formatIsoWeekRangeLabel } from '../utils';
|
import { formatIsoWeekRangeLabel } from '../utils';
|
||||||
|
import { formatWeeklyPeriodLabel } from '../types';
|
||||||
|
|
||||||
defineOptions({ name: 'TeamReportSummary' });
|
defineOptions({ name: 'TeamReportSummary' });
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ const emit = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const remindingAll = ref(false);
|
const remindingAll = ref(false);
|
||||||
const remindingUserId = ref('');
|
const remindingItemKey = ref('');
|
||||||
const canRemind = computed(() => props.periodKeys.length > 0);
|
const canRemind = computed(() => props.periodKeys.length > 0);
|
||||||
|
|
||||||
function formatSummaryPeriodLabel() {
|
function formatSummaryPeriodLabel() {
|
||||||
@@ -57,22 +58,54 @@ function formatSummaryPeriodLabel() {
|
|||||||
const displayPeriodLabel = computed(() => formatSummaryPeriodLabel());
|
const displayPeriodLabel = computed(() => formatSummaryPeriodLabel());
|
||||||
|
|
||||||
const cards = computed(() => [
|
const cards = computed(() => [
|
||||||
{ label: '应填人数', value: props.summary?.totalShouldSubmit ?? 0 },
|
{ label: '应填报告数', value: props.summary?.expectedReportCount ?? 0 },
|
||||||
{ label: '已提交', value: props.summary?.submittedCount ?? 0 },
|
{ label: '已提交报告数', value: props.summary?.submittedReportCount ?? 0 },
|
||||||
{ label: '待提交', value: props.summary?.unsubmittedCount ?? 0, key: 'unsubmitted' as const },
|
{ label: '未提交报告数', value: props.summary?.unsubmittedReportCount ?? 0, key: 'unsubmitted' as const },
|
||||||
{ label: '待审批', value: props.summary?.pendingApprovalCount ?? 0 }
|
{ 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[]) {
|
async function handleRemind(userIds?: string[]) {
|
||||||
if (!props.periodKeys.length) return;
|
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(
|
const results = await Promise.all(
|
||||||
props.periodKeys.map(periodKey =>
|
props.periodKeys.map(periodKey =>
|
||||||
@@ -84,10 +117,7 @@ async function handleRemind(userIds?: string[]) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!targetUserId) {
|
|
||||||
remindingAll.value = false;
|
remindingAll.value = false;
|
||||||
}
|
|
||||||
remindingUserId.value = '';
|
|
||||||
|
|
||||||
const remindedCount = results.reduce((total, result) => {
|
const remindedCount = results.reduce((total, result) => {
|
||||||
if (result.error) return total;
|
if (result.error) return total;
|
||||||
@@ -96,7 +126,33 @@ async function handleRemind(userIds?: string[]) {
|
|||||||
|
|
||||||
if (!remindedCount) return;
|
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');
|
emit('reminded');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -116,26 +172,31 @@ async function handleRemind(userIds?: string[]) {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="team-report-summary__popover">
|
<div class="team-report-summary__popover">
|
||||||
<div class="team-report-summary__popover-title">未提交人员</div>
|
<div class="team-report-summary__popover-title">未提交报告</div>
|
||||||
<div v-if="props.summary?.unsubmittedUsers?.length" class="team-report-summary__user-list">
|
<div v-if="props.summary?.unsubmittedReports?.length" class="team-report-summary__user-list">
|
||||||
<div
|
<div
|
||||||
v-for="user in props.summary.unsubmittedUsers"
|
v-for="item in props.summary.unsubmittedReports"
|
||||||
:key="user.userId"
|
:key="getUnsubmittedReportKey(item)"
|
||||||
class="team-report-summary__user-item"
|
class="team-report-summary__user-item"
|
||||||
>
|
>
|
||||||
<span class="team-report-summary__user-name">{{ user.userNickname }}</span>
|
<div class="team-report-summary__user-content">
|
||||||
|
<div class="team-report-summary__user-name">{{ item.userNickname }}</div>
|
||||||
|
<div class="team-report-summary__user-meta">
|
||||||
|
{{ getUnsubmittedReportSecondaryText(item) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<ElButton
|
<ElButton
|
||||||
v-if="canRemind"
|
v-if="canRemind"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
:loading="remindingUserId === user.userId"
|
:loading="remindingItemKey === getUnsubmittedReportKey(item)"
|
||||||
@click="handleRemind([user.userId])"
|
@click="handleRemindItem(item)"
|
||||||
>
|
>
|
||||||
催办
|
催办
|
||||||
</ElButton>
|
</ElButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ElEmpty v-else :image-size="60" description="暂无待提交人员" />
|
<ElEmpty v-else :image-size="60" description="暂无未提交报告" />
|
||||||
|
|
||||||
<div v-if="canRemind" class="team-report-summary__popover-footer">
|
<div v-if="canRemind" class="team-report-summary__popover-footer">
|
||||||
<ElButton
|
<ElButton
|
||||||
@@ -143,7 +204,7 @@ async function handleRemind(userIds?: string[]) {
|
|||||||
type="primary"
|
type="primary"
|
||||||
plain
|
plain
|
||||||
:loading="remindingAll"
|
:loading="remindingAll"
|
||||||
:disabled="!props.summary?.unsubmittedUsers?.length"
|
:disabled="!props.summary?.unsubmittedReports?.length"
|
||||||
@click="handleRemind()"
|
@click="handleRemind()"
|
||||||
>
|
>
|
||||||
一键催办全部
|
一键催办全部
|
||||||
@@ -239,6 +300,21 @@ async function handleRemind(userIds?: string[]) {
|
|||||||
.team-report-summary__user-name {
|
.team-report-summary__user-name {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
color: var(--el-text-color-regular);
|
color: var(--el-text-color-regular);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-report-summary__user-content {
|
||||||
|
min-width: 0;
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-report-summary__user-meta {
|
||||||
|
min-width: 0;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.team-report-summary__popover-footer {
|
.team-report-summary__popover-footer {
|
||||||
|
|||||||
@@ -31,6 +31,27 @@ const plannedEndDate = computed(() => props.task?.plannedEndDate ?? null);
|
|||||||
const priority = computed(() => props.task?.priority ?? null);
|
const priority = computed(() => props.task?.priority ?? null);
|
||||||
const attachments = computed(() => props.task?.attachments ?? []);
|
const attachments = computed(() => props.task?.attachments ?? []);
|
||||||
|
|
||||||
|
const ownerOptions = computed(() => {
|
||||||
|
const currentOwnerId = ownerId.value;
|
||||||
|
|
||||||
|
if (!currentOwnerId) {
|
||||||
|
return props.userOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasOwner = props.userOptions.some(item => item.id === currentOwnerId);
|
||||||
|
if (hasOwner) {
|
||||||
|
return props.userOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
...props.userOptions,
|
||||||
|
{
|
||||||
|
id: currentOwnerId,
|
||||||
|
nickname: props.task?.ownerNickname?.trim() || currentOwnerId
|
||||||
|
}
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
const assigneeIds = computed(() => props.task?.assignees?.map(a => a.userId) ?? []);
|
const assigneeIds = computed(() => props.task?.assignees?.map(a => a.userId) ?? []);
|
||||||
const assigneeOptions = computed(() => props.task?.assignees ?? []);
|
const assigneeOptions = computed(() => props.task?.assignees ?? []);
|
||||||
|
|
||||||
@@ -69,7 +90,7 @@ const parentTaskOptions = computed(() => {
|
|||||||
/>
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem label="负责人">
|
<ElFormItem label="负责人">
|
||||||
<BusinessUserSelect :model-value="ownerId" :options="userOptions" disabled placeholder="--" />
|
<BusinessUserSelect :model-value="ownerId" :options="ownerOptions" disabled placeholder="--" />
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem label="协办人">
|
<ElFormItem label="协办人">
|
||||||
<ElSelect
|
<ElSelect
|
||||||
|
|||||||
Reference in New Issue
Block a user