Files
cn-rdms-web/src/views/system/user/modules/user-org-leader-dialog.vue
dk f4f43814b3 fix(产品需求): 修复产品需求使用状态和终止态字典的问题。
fix(组织): 修复组织编码下拉框的数据显示问题、修复组织编码负责人无法新增的问题。
fix(管理链路): 修复管理链路高度没固定,节点全部收缩等问题。
2026-05-07 17:09:53 +08:00

265 lines
7.7 KiB
Vue

<script setup lang="tsx">
import { computed, ref, watch } from 'vue';
import { ElButton, ElEmpty, ElTag } from 'element-plus';
import dayjs from 'dayjs';
import {
fetchDeleteOrgLeaderRelation,
fetchGetOrgLeaderCandidateUsers,
fetchGetOrgLeaderListByDept,
fetchGetUserPage
} from '@/service/api';
import BusinessFormDialog from '@/components/custom/business-form-dialog.vue';
import BusinessTableActionCell from '@/components/custom/business-table-action-cell';
import { $t } from '@/locales';
import UserOrgLeaderOperateDialog from './user-org-leader-operate-dialog.vue';
defineOptions({ name: 'UserOrgLeaderDialog' });
interface Props {
dept?: Api.SystemManage.Dept | null;
}
const props = defineProps<Props>();
const visible = defineModel<boolean>('visible', {
default: false
});
const loading = ref(false);
const relations = ref<Api.SystemManage.OrgLeaderRelation[]>([]);
const candidateUsers = ref<Api.SystemManage.OrgLeaderCandidateUser[]>([]);
const operateVisible = ref(false);
const operateType = ref<UI.TableOperateType>('add');
const editingData = ref<Api.SystemManage.OrgLeaderRelation | null>(null);
const title = computed(() => {
if (props.dept?.name) {
return `${$t('page.system.user.orgLeaderTitle')} / ${props.dept.name}`;
}
return $t('page.system.user.orgLeaderTitle');
});
const total = computed(() => relations.value.length);
function formatTime(value?: number | null) {
if (!value) {
return '--';
}
return dayjs(value).format('YYYY-MM-DD HH:mm:ss');
}
function isCandidateUser(item: unknown): item is Api.SystemManage.OrgLeaderCandidateUser {
if (!item || typeof item !== 'object') {
return false;
}
const record = item as Record<string, unknown>;
return typeof record.id === 'number' && typeof record.nickname === 'string' && typeof record.deptId === 'number';
}
function mapUsersToCandidateUsers(users: Api.SystemManage.User[]): Api.SystemManage.OrgLeaderCandidateUser[] {
const now = Date.now();
return users
.filter(item => !item.resignedAt || item.resignedAt > now)
.map(item => ({
id: String(item.id),
nickname: item.nickname?.trim() || item.username,
deptId: item.deptId,
deptName: item.deptName ?? null
}));
}
async function loadCandidateUsers(deptId: number) {
const candidateResult = await fetchGetOrgLeaderCandidateUsers(deptId);
if (!candidateResult.error && Array.isArray(candidateResult.data) && candidateResult.data.every(isCandidateUser)) {
return candidateResult.data;
}
const userResult = await fetchGetUserPage({
pageNo: 1,
pageSize: 200,
deptId,
status: 0
});
if (userResult.error) {
return [];
}
return mapUsersToCandidateUsers(userResult.data.list);
}
async function loadData() {
if (!props.dept?.id) {
relations.value = [];
candidateUsers.value = [];
return;
}
loading.value = true;
const [relationResult, candidates] = await Promise.all([
fetchGetOrgLeaderListByDept(props.dept.id),
loadCandidateUsers(props.dept.id)
]);
loading.value = false;
relations.value = relationResult.error ? [] : relationResult.data;
candidateUsers.value = candidates;
}
function openAdd() {
operateType.value = 'add';
editingData.value = null;
operateVisible.value = true;
}
function openEdit(row: Api.SystemManage.OrgLeaderRelation) {
operateType.value = 'edit';
editingData.value = row;
operateVisible.value = true;
}
async function handleDelete(row: Api.SystemManage.OrgLeaderRelation) {
const { error } = await fetchDeleteOrgLeaderRelation(row.id);
if (error) {
return;
}
window.$message?.success($t('common.deleteSuccess'));
await loadData();
}
async function handleDeleteAction(row: Api.SystemManage.OrgLeaderRelation) {
try {
await window.$messageBox?.confirm($t('common.confirmDelete'), $t('common.warning'), {
confirmButtonText: $t('common.confirm'),
cancelButtonText: $t('common.cancel'),
type: 'warning'
});
} catch {
return;
}
await handleDelete(row);
}
async function handleSubmitted() {
operateVisible.value = false;
await loadData();
}
watch(
() => [visible.value, props.dept?.id] as const,
async ([dialogVisible, deptId]) => {
if (!dialogVisible || !deptId) {
return;
}
await loadData();
}
);
</script>
<template>
<BusinessFormDialog v-model="visible" :title="title" preset="lg" :show-footer="false">
<div class="flex-col-stretch gap-16px">
<div class="flex items-center justify-between gap-12px">
<div class="flex items-center gap-8px">
<ElTag type="primary" effect="light">{{ dept?.name || $t('common.noData') }}</ElTag>
<ElTag effect="plain">{{ total }}</ElTag>
</div>
<div class="flex items-center gap-8px">
<ElButton type="primary" plain size="small" @click="openAdd">
<template #icon>
<icon-ic-round-plus class="text-icon" />
</template>
{{ $t('common.add') }}
</ElButton>
<ElButton plain size="small" @click="loadData">
<template #icon>
<icon-mdi-refresh class="text-icon" />
</template>
{{ $t('common.refresh') }}
</ElButton>
</div>
</div>
<div v-loading="loading" class="min-h-260px">
<template v-if="relations.length">
<div class="min-h-260px">
<ElTable border :data="relations" :max-height="420">
<ElTableColumn type="index" :label="$t('common.index')" width="64" />
<ElTableColumn prop="userNickname" :label="$t('page.system.user.orgLeader')" min-width="140" />
<ElTableColumn
prop="effectiveFrom"
:label="$t('page.system.user.effectiveFrom')"
min-width="170"
:formatter="row => formatTime(row.effectiveFrom)"
/>
<ElTableColumn
prop="effectiveUntil"
:label="$t('page.system.user.effectiveUntil')"
min-width="170"
:formatter="row => formatTime(row.effectiveUntil)"
/>
<ElTableColumn
prop="remark"
:label="$t('page.system.user.relationRemark')"
min-width="180"
show-overflow-tooltip
/>
<ElTableColumn
prop="createTime"
:label="$t('page.system.user.createTime')"
min-width="170"
:formatter="row => formatTime(row.createTime)"
/>
<ElTableColumn :label="$t('common.operate')" width="196" align="center" fixed="right">
<template #default="{ row }">
<BusinessTableActionCell
:actions="[
{
key: 'edit',
label: $t('common.edit'),
buttonType: 'primary',
onClick: () => openEdit(row)
},
{
key: 'delete',
label: $t('common.delete'),
buttonType: 'danger',
onClick: () => handleDeleteAction(row)
}
]"
/>
</template>
</ElTableColumn>
</ElTable>
</div>
</template>
<div v-else class="min-h-260px flex items-center justify-center">
<ElEmpty :description="$t('page.system.user.emptyLeader')" />
</div>
</div>
</div>
<UserOrgLeaderOperateDialog
v-model:visible="operateVisible"
:operate-type="operateType"
:row-data="editingData"
:dept="dept"
:candidate-users="candidateUsers"
@submitted="handleSubmitted"
/>
</BusinessFormDialog>
</template>