feat(工作报告、加班申请团队视角): 工作报告、加班申请现在可以查看团队视角了(查看下属)。

fix(工作报告): 修复周报在新增/编辑时,不能展示工作日志。
This commit is contained in:
dk
2026-06-14 23:57:42 +08:00
parent 17690283f6
commit 3c1cf6c7fa
19 changed files with 1618 additions and 94 deletions

View File

@@ -0,0 +1,217 @@
<script setup lang="ts">
import { computed, ref } from 'vue';
import { fetchRemindTeamReport } from '@/service/api';
defineOptions({ name: 'TeamReportSummary' });
interface Props {
reportType: Api.WorkReport.Common.ReportType;
periodKey: string;
periodLabel?: string;
loading?: boolean;
summary?: Api.WorkReport.Common.TeamReportSummary | null;
}
const props = withDefaults(defineProps<Props>(), {
periodLabel: '',
loading: false,
summary: null
});
const emit = defineEmits<{
reminded: [];
}>();
const remindingAll = ref(false);
const remindingUserId = ref('');
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 }
]);
async function handleRemind(userIds?: string[]) {
const targetUserId = userIds?.length === 1 ? userIds[0] : '';
if (targetUserId) {
remindingUserId.value = targetUserId;
} else {
remindingAll.value = true;
}
const { error, data } = await fetchRemindTeamReport({
reportType: props.reportType,
periodKey: props.periodKey,
userIds
});
if (!targetUserId) {
remindingAll.value = false;
}
remindingUserId.value = '';
if (error) return;
window.$message?.success(`已催办 ${data?.remindedCount ?? 0}`);
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 class="team-report-summary__grid">
<div v-for="card in cards" :key="card.label" class="team-report-summary__item">
<div class="team-report-summary__label">{{ card.label }}</div>
<div class="team-report-summary__value">
<template v-if="card.key === 'unsubmitted'">
<ElPopover placement="bottom" :width="300" trigger="hover">
<template #reference>
<button type="button" class="team-report-summary__link-button">{{ card.value }}</button>
</template>
<div class="team-report-summary__popover">
<div class="team-report-summary__popover-title">未提交人员</div>
<div v-if="props.summary?.unsubmittedUsers?.length" class="team-report-summary__user-list">
<div
v-for="user in props.summary.unsubmittedUsers"
:key="user.userId"
class="team-report-summary__user-item"
>
<span class="team-report-summary__user-name">{{ user.userNickname }}</span>
<ElButton
link
type="primary"
:loading="remindingUserId === user.userId"
@click="handleRemind([user.userId])"
>
催办
</ElButton>
</div>
</div>
<ElEmpty v-else :image-size="60" description="暂无待提交人员" />
<div class="team-report-summary__popover-footer">
<ElButton
size="small"
type="primary"
plain
:loading="remindingAll"
:disabled="!props.summary?.unsubmittedUsers?.length"
@click="handleRemind()"
>
一键催办全部
</ElButton>
</div>
</div>
</ElPopover>
</template>
<template v-else>
{{ card.value }}
</template>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.team-report-summary {
display: grid;
gap: 12px;
}
.team-report-summary__period {
color: var(--el-text-color-secondary);
font-size: 12px;
}
.team-report-summary__grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
}
.team-report-summary__item {
display: grid;
gap: 8px;
padding: 14px 16px;
border: 1px solid var(--el-border-color-light);
border-radius: 8px;
background: var(--el-fill-color-blank);
}
.team-report-summary__label {
color: var(--el-text-color-secondary);
font-size: 12px;
}
.team-report-summary__value {
color: var(--el-text-color-primary);
font-size: 22px;
font-weight: 600;
line-height: 1.2;
}
.team-report-summary__link-button {
padding: 0;
border: none;
background: transparent;
color: var(--el-color-primary);
font: inherit;
cursor: pointer;
}
.team-report-summary__popover {
display: grid;
gap: 10px;
}
.team-report-summary__popover-title {
font-size: 14px;
font-weight: 600;
color: var(--el-text-color-primary);
}
.team-report-summary__user-list {
display: grid;
gap: 8px;
max-height: 240px;
overflow: auto;
}
.team-report-summary__user-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 8px 10px;
border-radius: 8px;
background: var(--el-fill-color-light);
}
.team-report-summary__user-name {
min-width: 0;
color: var(--el-text-color-regular);
}
.team-report-summary__popover-footer {
display: flex;
justify-content: flex-end;
}
@media (width <= 1200px) {
.team-report-summary__grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (width <= 768px) {
.team-report-summary__grid {
grid-template-columns: 1fr;
}
}
</style>