Files
cn-rdms-web/src/components/custom/subordinate-selector.vue
dk 570f284230 feat(日志管理): 开发日志管理功能。
fix(项目任务): 1、任务完成后需要依然能够修改工作日志,但是只能修改工作内容和上传附件。2、任务完成后,协办人的工作日志不应该能删除、所有任务里的成员不能新增工作日志,前端不显示新增、删除按钮。3、团队成员的面板,在成员排序时,让有下属的成员提前。4、在任务弹出框有个快速用执行的信息填充的icon。
2026-06-25 21:34:23 +08:00

146 lines
3.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { computed } from 'vue';
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 sortSubordinateNodes(
nodes: Api.SystemManage.MySubordinateTreeNode[] | null | undefined
): Api.SystemManage.MySubordinateTreeNode[] | null {
if (!nodes?.length) return null;
return nodes
.map((node, index) => ({
...node,
children: sortSubordinateNodes(node.children),
originalIndex: index
}))
.sort((left, right) => {
const leftHasChildren = (left.children?.length ?? 0) > 0 ? 1 : 0;
const rightHasChildren = (right.children?.length ?? 0) > 0 ? 1 : 0;
if (leftHasChildren !== rightHasChildren) {
return rightHasChildren - leftHasChildren;
}
return left.originalIndex - right.originalIndex;
})
.map(({ originalIndex: _ignored, ...node }) => node);
}
const treeData = computed<Api.SystemManage.MySubordinateTreeNode[] | null>(() => {
if (!props.data) return null;
return [
{
...props.data,
children: sortSubordinateNodes(props.data.children)
}
];
});
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="treeData || []"
node-key="userId"
:current-node-key="selectedUserId || undefined"
:props="{ label: 'userNickname', children: 'children' }"
highlight-current
:default-expanded-keys="[props.data.userId]"
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>