fix(加班申请、工作报告、我的绩效): 重构页面样式、修复一系列bug、对不合理的地方进行调整。
This commit is contained in:
@@ -1,19 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
/* eslint-disable no-void */
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import isoWeek from 'dayjs/plugin/isoWeek';
|
||||
import { fetchGetWorkReportStatusDict } from '@/service/api';
|
||||
import type { SearchField } from '@/components/custom/table-search-fields.vue';
|
||||
import TableSearchFields from '@/components/custom/table-search-fields.vue';
|
||||
import { BOOLEAN_TRUE_FALSE_OPTIONS, type WorkReportSearchParams, type WorkReportType } from '../types';
|
||||
import { formatIsoWeekRangeLabel, normalizeWeeklySearchRange } from '../utils';
|
||||
|
||||
dayjs.extend(isoWeek);
|
||||
|
||||
defineOptions({ name: 'WorkReportSearch' });
|
||||
|
||||
interface Props {
|
||||
reportType: WorkReportType;
|
||||
teamMode?: boolean;
|
||||
subordinateOptions?: Array<{ label: string; value: string }>;
|
||||
projectOptions?: Api.WorkReport.Project.ProjectReportOwnerProjectOption[];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
teamMode: false,
|
||||
subordinateOptions: () => [],
|
||||
projectOptions: () => []
|
||||
});
|
||||
|
||||
@@ -35,6 +44,12 @@ const statusOptions = computed(() =>
|
||||
}))
|
||||
);
|
||||
|
||||
const weeklyPeriodPlaceholder = computed(() => {
|
||||
const range = normalizeWeeklySearchRange(model.value.periodStartDate as string[] | undefined);
|
||||
if (!range?.length) return '请选择周报周期';
|
||||
return formatIsoWeekRangeLabel(range[0], range[1]) || '请选择周报周期';
|
||||
});
|
||||
|
||||
const fields = computed<SearchField[]>(() => {
|
||||
const baseFields: SearchField[] = [
|
||||
{ key: 'statusCode', label: '状态', type: 'select', options: statusOptions.value, placeholder: '请选择状态' },
|
||||
@@ -51,8 +66,33 @@ const fields = computed<SearchField[]>(() => {
|
||||
};
|
||||
|
||||
if (props.reportType === 'weekly') {
|
||||
const weeklyPeriodField: SearchField = {
|
||||
key: 'periodStartDate',
|
||||
label: '周期',
|
||||
type: 'dateRange',
|
||||
placeholder: weeklyPeriodPlaceholder.value,
|
||||
format: 'YYYY[年第]ww[周]',
|
||||
rangeSeparator: '至'
|
||||
};
|
||||
|
||||
const teamReporterField: SearchField[] = props.teamMode
|
||||
? [
|
||||
{
|
||||
key: 'reporterIds',
|
||||
label: '提交人',
|
||||
type: 'select',
|
||||
options: props.subordinateOptions,
|
||||
placeholder: '请选择提交人',
|
||||
transformValue: value => (value ? [value] : undefined),
|
||||
resolveValue: value => (Array.isArray(value) ? value[0] : value)
|
||||
}
|
||||
]
|
||||
: [];
|
||||
|
||||
return [
|
||||
...baseFields,
|
||||
baseFields[0],
|
||||
weeklyPeriodField,
|
||||
...teamReporterField,
|
||||
{
|
||||
key: 'isBusinessTrip',
|
||||
label: '是否出差',
|
||||
@@ -81,7 +121,21 @@ const fields = computed<SearchField[]>(() => {
|
||||
}
|
||||
|
||||
if (props.reportType === 'monthly') {
|
||||
return [baseFields[0], monthPeriodField];
|
||||
const teamReporterField: SearchField[] = props.teamMode
|
||||
? [
|
||||
{
|
||||
key: 'reporterIds',
|
||||
label: '提交人',
|
||||
type: 'select',
|
||||
options: props.subordinateOptions,
|
||||
placeholder: '请选择提交人',
|
||||
transformValue: value => (value ? [value] : undefined),
|
||||
resolveValue: value => (Array.isArray(value) ? value[0] : value)
|
||||
}
|
||||
]
|
||||
: [];
|
||||
|
||||
return [baseFields[0], monthPeriodField, ...teamReporterField];
|
||||
}
|
||||
|
||||
return baseFields;
|
||||
@@ -95,6 +149,34 @@ async function loadStatusDict() {
|
||||
onMounted(() => {
|
||||
loadStatusDict();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.reportType,
|
||||
type => {
|
||||
if (type !== 'weekly') return;
|
||||
model.value.periodStartDate = normalizeWeeklySearchRange(model.value.periodStartDate as string[] | undefined);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => model.value.periodStartDate,
|
||||
value => {
|
||||
if (props.reportType !== 'weekly') return;
|
||||
|
||||
const normalizedValue = normalizeWeeklySearchRange(value as string[] | undefined);
|
||||
const currentValue = Array.isArray(value) ? value : [];
|
||||
|
||||
if (
|
||||
normalizedValue?.length === currentValue.length &&
|
||||
normalizedValue?.every((item, index) => item === currentValue[index])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
model.value.periodStartDate = normalizedValue;
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { fetchRemindTeamReport } from '@/service/api';
|
||||
import { formatIsoWeekRangeLabel } from '../utils';
|
||||
|
||||
defineOptions({ name: 'TeamReportSummary' });
|
||||
|
||||
interface Props {
|
||||
reportType: Api.WorkReport.Common.ReportType;
|
||||
periodKey: string;
|
||||
periodKeys?: string[];
|
||||
periodLabel?: string;
|
||||
loading?: boolean;
|
||||
summary?: Api.WorkReport.Common.TeamReportSummary | null;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
periodKeys: () => [],
|
||||
periodLabel: '',
|
||||
loading: false,
|
||||
summary: null
|
||||
@@ -24,6 +27,34 @@ const emit = defineEmits<{
|
||||
|
||||
const remindingAll = ref(false);
|
||||
const remindingUserId = ref('');
|
||||
const canRemind = computed(() => props.periodKeys.length > 0);
|
||||
|
||||
function formatSummaryPeriodLabel() {
|
||||
if (!props.summary?.periodStartDate || !props.summary?.periodEndDate) {
|
||||
return props.periodLabel;
|
||||
}
|
||||
|
||||
const start = dayjs(props.summary.periodStartDate);
|
||||
const end = dayjs(props.summary.periodEndDate);
|
||||
|
||||
if (!start.isValid() || !end.isValid()) {
|
||||
return `${props.summary.periodStartDate} 至 ${props.summary.periodEndDate}`;
|
||||
}
|
||||
|
||||
if (props.reportType === 'monthly' || props.reportType === 'project') {
|
||||
const startMonth = start.format('YYYY-MM');
|
||||
const endMonth = end.format('YYYY-MM');
|
||||
return startMonth === endMonth ? startMonth : `${startMonth} 至 ${endMonth}`;
|
||||
}
|
||||
|
||||
if (props.reportType === 'weekly') {
|
||||
return formatIsoWeekRangeLabel(props.summary.periodStartDate, props.summary.periodEndDate);
|
||||
}
|
||||
|
||||
return `${start.format('YYYY-MM-DD')} 至 ${end.format('YYYY-MM-DD')}`;
|
||||
}
|
||||
|
||||
const displayPeriodLabel = computed(() => formatSummaryPeriodLabel());
|
||||
|
||||
const cards = computed(() => [
|
||||
{ label: '应填人数', value: props.summary?.totalShouldSubmit ?? 0 },
|
||||
@@ -33,6 +64,8 @@ const cards = computed(() => [
|
||||
]);
|
||||
|
||||
async function handleRemind(userIds?: string[]) {
|
||||
if (!props.periodKeys.length) return;
|
||||
|
||||
const targetUserId = userIds?.length === 1 ? userIds[0] : '';
|
||||
|
||||
if (targetUserId) {
|
||||
@@ -41,27 +74,36 @@ async function handleRemind(userIds?: string[]) {
|
||||
remindingAll.value = true;
|
||||
}
|
||||
|
||||
const { error, data } = await fetchRemindTeamReport({
|
||||
reportType: props.reportType,
|
||||
periodKey: props.periodKey,
|
||||
userIds
|
||||
});
|
||||
const results = await Promise.all(
|
||||
props.periodKeys.map(periodKey =>
|
||||
fetchRemindTeamReport({
|
||||
reportType: props.reportType,
|
||||
periodKey,
|
||||
userIds
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
if (!targetUserId) {
|
||||
remindingAll.value = false;
|
||||
}
|
||||
remindingUserId.value = '';
|
||||
|
||||
if (error) return;
|
||||
const remindedCount = results.reduce((total, result) => {
|
||||
if (result.error) return total;
|
||||
return total + (result.data?.remindedCount ?? 0);
|
||||
}, 0);
|
||||
|
||||
window.$message?.success(`已催办 ${data?.remindedCount ?? 0} 人`);
|
||||
if (!remindedCount) return;
|
||||
|
||||
window.$message?.success(props.periodKeys.length > 1 ? '已按所选区间发送催办提醒' : `已催办 ${remindedCount} 人`);
|
||||
emit('reminded');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-loading="props.loading" class="team-report-summary">
|
||||
<div v-if="props.periodLabel" class="team-report-summary__period">{{ props.periodLabel }}</div>
|
||||
<div v-if="displayPeriodLabel" class="team-report-summary__period">{{ displayPeriodLabel }}</div>
|
||||
|
||||
<div class="team-report-summary__grid">
|
||||
<div v-for="card in cards" :key="card.label" class="team-report-summary__item">
|
||||
@@ -83,6 +125,7 @@ async function handleRemind(userIds?: string[]) {
|
||||
>
|
||||
<span class="team-report-summary__user-name">{{ user.userNickname }}</span>
|
||||
<ElButton
|
||||
v-if="canRemind"
|
||||
link
|
||||
type="primary"
|
||||
:loading="remindingUserId === user.userId"
|
||||
@@ -94,7 +137,7 @@ async function handleRemind(userIds?: string[]) {
|
||||
</div>
|
||||
<ElEmpty v-else :image-size="60" description="暂无待提交人员" />
|
||||
|
||||
<div class="team-report-summary__popover-footer">
|
||||
<div v-if="canRemind" class="team-report-summary__popover-footer">
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
|
||||
Reference in New Issue
Block a user