feat(工作报告、加班申请团队视角): 工作报告、加班申请现在可以查看团队视角了(查看下属)。
fix(工作报告): 修复周报在新增/编辑时,不能展示工作日志。
This commit is contained in:
108
src/components/custom/subordinate-selector.vue
Normal file
108
src/components/custom/subordinate-selector.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<script setup lang="ts">
|
||||
defineOptions({ name: 'SubordinateSelector' });
|
||||
|
||||
interface Props {
|
||||
loading?: boolean;
|
||||
data?: Api.SystemManage.MySubordinateTreeNode | null;
|
||||
emptyText?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
loading: false,
|
||||
data: null,
|
||||
emptyText: '暂无下属数据'
|
||||
});
|
||||
|
||||
const selectedUserId = defineModel<string | null>('selectedUserId', {
|
||||
default: null
|
||||
});
|
||||
|
||||
function handleNodeClick(node: Api.SystemManage.MySubordinateTreeNode) {
|
||||
selectedUserId.value = node.userId;
|
||||
}
|
||||
|
||||
function renderNodeLabel(node: Api.SystemManage.MySubordinateTreeNode) {
|
||||
const label = node.isRoot ? '全部下属' : node.userNickname;
|
||||
return `${label}${node.subordinateCount ? `(${node.subordinateCount})` : ''}`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElCard class="subordinate-selector" body-class="subordinate-selector__body">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between gap-12px">
|
||||
<span class="text-14px font-600">团队成员</span>
|
||||
<ElTag v-if="props.data" effect="plain">{{ props.data.subordinateCount }}</ElTag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-loading="props.loading" class="subordinate-selector__content">
|
||||
<ElEmpty v-if="!props.data" :image-size="88" :description="props.emptyText" />
|
||||
<ElTree
|
||||
v-else
|
||||
:data="[props.data]"
|
||||
node-key="userId"
|
||||
:current-node-key="selectedUserId || undefined"
|
||||
:props="{ label: 'userNickname', children: 'children' }"
|
||||
highlight-current
|
||||
default-expand-all
|
||||
expand-on-click-node
|
||||
class="subordinate-selector__tree"
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<template #default="{ data: node }">
|
||||
<span class="subordinate-selector__node-label">{{ renderNodeLabel(node) }}</span>
|
||||
</template>
|
||||
</ElTree>
|
||||
</div>
|
||||
</ElCard>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.subordinate-selector {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:deep(.subordinate-selector__body) {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.subordinate-selector__content {
|
||||
flex: 1;
|
||||
min-height: 240px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.subordinate-selector__tree {
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.subordinate-selector__node-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
:deep(.subordinate-selector__tree .el-tree-node__content) {
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
:deep(.subordinate-selector__tree .el-tree-node__content:hover) {
|
||||
background: var(--el-fill-color-light);
|
||||
}
|
||||
|
||||
:deep(.subordinate-selector__tree .el-tree-node.is-current > .el-tree-node__content) {
|
||||
background: var(--el-color-primary-light-9);
|
||||
}
|
||||
</style>
|
||||
163
src/components/custom/team-context-panel.vue
Normal file
163
src/components/custom/team-context-panel.vue
Normal file
@@ -0,0 +1,163 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import type { TeamViewMode } from '@/views/personal-center/shared/team-dashboard';
|
||||
|
||||
defineOptions({ name: 'TeamContextPanel' });
|
||||
|
||||
interface Props {
|
||||
loading?: boolean;
|
||||
selectedLabel?: string;
|
||||
subordinateCount?: number;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
loading: false,
|
||||
selectedLabel: '',
|
||||
subordinateCount: 0
|
||||
});
|
||||
|
||||
const mode = defineModel<TeamViewMode>('mode', {
|
||||
required: true
|
||||
});
|
||||
|
||||
const scopeOptions = computed(() => [
|
||||
{ label: '个人视角', value: 'self' satisfies TeamViewMode },
|
||||
{ label: '团队视角', value: 'team' satisfies TeamViewMode }
|
||||
]);
|
||||
|
||||
const contextText = computed(() => {
|
||||
if (mode.value === 'self') {
|
||||
return '当前查看我自己的数据。';
|
||||
}
|
||||
|
||||
if (props.selectedLabel) {
|
||||
return `当前范围:${props.selectedLabel}`;
|
||||
}
|
||||
|
||||
return '当前查看团队数据。';
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElCard class="team-context-panel" body-class="team-context-panel__body">
|
||||
<div v-loading="props.loading" class="team-context-panel__layout">
|
||||
<div class="team-context-panel__controls">
|
||||
<ElSegmented v-model="mode" :options="scopeOptions" class="team-context-panel__segmented" />
|
||||
</div>
|
||||
|
||||
<div class="team-context-panel__info">
|
||||
<div class="team-context-panel__info-main">
|
||||
<div class="team-context-panel__info-item">
|
||||
<span class="team-context-panel__info-label">当前范围</span>
|
||||
<strong class="team-context-panel__info-value">
|
||||
{{ props.selectedLabel || (mode === 'self' ? '我自己' : '--') }}
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<div v-if="mode === 'team'" class="team-context-panel__info-item">
|
||||
<span class="team-context-panel__info-label">下属人数</span>
|
||||
<strong class="team-context-panel__info-value">{{ props.subordinateCount }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="team-context-panel__info-desc">{{ contextText }}</p>
|
||||
<div v-if="$slots.default" class="team-context-panel__summary">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ElCard>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.team-context-panel {
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
background: var(--el-fill-color-blank);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:deep(.team-context-panel__body) {
|
||||
padding: 16px 18px;
|
||||
}
|
||||
|
||||
.team-context-panel__layout {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.team-context-panel__controls {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
:deep(.team-context-panel__segmented) {
|
||||
padding: 6px;
|
||||
background: var(--el-fill-color-light);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
:deep(.team-context-panel__segmented .el-segmented__item) {
|
||||
min-width: 96px;
|
||||
min-height: 40px;
|
||||
padding: 0 22px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.team-context-panel__info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-left: 20px;
|
||||
border-left: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.team-context-panel__info-main {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.team-context-panel__info-item {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 10px;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.team-context-panel__info-label {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.team-context-panel__info-value {
|
||||
color: var(--el-text-color-primary);
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.team-context-panel__info-desc {
|
||||
margin: 10px 0 0;
|
||||
color: var(--el-text-color-regular);
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.team-context-panel__summary {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
@media (width <= 1200px) {
|
||||
.team-context-panel__layout {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.team-context-panel__info {
|
||||
padding-left: 0;
|
||||
padding-top: 14px;
|
||||
border-left: none;
|
||||
border-top: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user