feat(@vben/web-antd): 用户-项目绑定管理双入口

- 用户管理页:下拉操作新增 "分配项目" 按钮 + assign-project-form.vue 弹窗
  沿用现有 assign-role-form 的交互(多选 + 覆盖写入)
- 项目管理页:行操作新增 "管理成员" 按钮 + assign-user-form.vue 弹窗
  下拉支持搜索用户 nickname/username
- 新建 api/system/user-project/ 封装 4 个接口
- api/system/project 新增 getAllProjectSimpleList:
  顶栏 simple-list 已改为用户授权过滤,管理员分配场景需要全量下拉
- 空集保存二次确认:清空所有分配/成员时弹 AntModal.confirm,防误操作
- 权限点:system:user:assign-project / system:project:assign-user

设计文档:docs/design/2026-04-23-user-project-binding.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
lzh
2026-04-23 14:51:09 +08:00
parent 20251316ae
commit b15b6b4f4d
8 changed files with 376 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
<script lang="ts" setup>
import type { SystemProjectApi } from '#/api/system/project';
import { useVbenModal } from '@vben/common-ui';
import { message, Modal as AntModal } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import {
assignProjectUsers,
getUserIdsByProjectId,
} from '#/api/system/user-project';
import { $t } from '#/locales';
import { useAssignUserFormSchema } from '../data';
const emit = defineEmits(['success']);
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
},
formItemClass: 'col-span-2',
labelWidth: 80,
},
layout: 'horizontal',
schema: useAssignUserFormSchema(),
showDefaultActions: false,
});
const [Modal, modalApi] = useVbenModal({
async onConfirm() {
const { valid } = await formApi.validate();
if (!valid) {
return;
}
const values = await formApi.getValues();
const userIds: number[] = values.userIds ?? [];
// 空集二次确认:清空该项目所有成员是高危操作
if (userIds.length === 0) {
const confirmed = await new Promise<boolean>((resolve) => {
AntModal.confirm({
title: '确认清空项目成员?',
content: `即将清空项目【${values.name}】的所有成员,保存后除超管外所有用户都将无法访问该项目。确认继续?`,
okText: '确认清空',
okType: 'danger',
cancelText: '取消',
onOk: () => resolve(true),
onCancel: () => resolve(false),
});
});
if (!confirmed) {
return;
}
}
modalApi.lock();
try {
await assignProjectUsers({
projectId: values.id,
userIds,
});
await modalApi.close();
emit('success');
message.success($t('ui.actionMessage.operationSuccess'));
} finally {
modalApi.unlock();
}
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
return;
}
const data = modalApi.getData<SystemProjectApi.Project>();
if (!data || !data.id) {
return;
}
modalApi.lock();
try {
const userIds = await getUserIdsByProjectId(data.id);
await formApi.setValues({
...data,
userIds,
});
} finally {
modalApi.unlock();
}
},
});
</script>
<template>
<Modal title="管理成员">
<Form class="mx-4" />
</Modal>
</template>