216 lines
6.5 KiB
TypeScript
216 lines
6.5 KiB
TypeScript
import type { Employee, Role, Workspace } from "../../services/api";
|
|
import {
|
|
AppFormDialog,
|
|
dialogPrimaryButtonClass,
|
|
dialogSecondaryButtonClass,
|
|
} from "~/components/common/AppFormDialog";
|
|
import { Button } from "~/components/ui/button";
|
|
import { Input } from "~/components/ui/input";
|
|
import { CreateUserProjectField } from "./CreateUserProjectField";
|
|
import {
|
|
formFieldClass,
|
|
formInputClass,
|
|
modalFormClass,
|
|
} from "../platform/modalUi";
|
|
|
|
export type UserFormState = {
|
|
username: string;
|
|
display_name: string;
|
|
email: string;
|
|
role_code: "admin" | "developer";
|
|
password: string;
|
|
status: "active" | "disabled" | "locked";
|
|
};
|
|
|
|
const FORM_INPUT_CLASS =
|
|
"h-[39px] w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:ring-3 focus:ring-brand/10 disabled:cursor-not-allowed disabled:opacity-50";
|
|
|
|
export function UserFormDialog({
|
|
open,
|
|
editing,
|
|
form,
|
|
saving,
|
|
currentUserId,
|
|
roles,
|
|
createRoleOptions,
|
|
createProjects,
|
|
createProjectsLoading,
|
|
selectedWorkspaceIds,
|
|
onChangeForm,
|
|
onToggleWorkspace,
|
|
onSubmit,
|
|
onClose,
|
|
}: {
|
|
open: boolean;
|
|
editing: Employee | null;
|
|
form: UserFormState;
|
|
saving: boolean;
|
|
currentUserId: string | undefined;
|
|
roles: Role[];
|
|
createRoleOptions: Role[];
|
|
createProjects: Workspace[];
|
|
createProjectsLoading: boolean;
|
|
selectedWorkspaceIds: string[];
|
|
onChangeForm: (next: UserFormState) => void;
|
|
onToggleWorkspace: (workspaceId: string) => void;
|
|
onSubmit: (event: React.FormEvent) => void;
|
|
onClose: () => void;
|
|
}) {
|
|
return (
|
|
<AppFormDialog
|
|
open={open}
|
|
onOpenChange={(nextOpen) => {
|
|
if (!nextOpen) onClose();
|
|
}}
|
|
eyebrow="EMPLOYEE"
|
|
title={editing ? "编辑用户" : "添加用户"}
|
|
>
|
|
<form className={modalFormClass} onSubmit={onSubmit}>
|
|
<label className={formFieldClass}>
|
|
<span>
|
|
姓名<span className="text-danger">*</span>
|
|
</span>
|
|
<Input
|
|
className={FORM_INPUT_CLASS}
|
|
autoFocus={!editing}
|
|
value={form.display_name}
|
|
onChange={(event) => onChangeForm({ ...form, display_name: event.target.value })}
|
|
placeholder="请输入姓名"
|
|
/>
|
|
</label>
|
|
<label className={formFieldClass}>
|
|
<span>
|
|
登录账号<span className="text-danger">*</span>
|
|
</span>
|
|
<Input
|
|
className={FORM_INPUT_CLASS}
|
|
disabled={Boolean(editing)}
|
|
value={form.username}
|
|
onChange={(event) => onChangeForm({ ...form, username: event.target.value })}
|
|
placeholder="请输入登录账号"
|
|
autoComplete="username"
|
|
/>
|
|
</label>
|
|
{!editing && (
|
|
<label className={formFieldClass}>
|
|
<span>
|
|
密码<span className="text-danger">*</span>
|
|
</span>
|
|
<Input
|
|
className={FORM_INPUT_CLASS}
|
|
type="password"
|
|
autoComplete="new-password"
|
|
value={form.password}
|
|
onChange={(event) => onChangeForm({ ...form, password: event.target.value })}
|
|
placeholder="请输入密码(8~72 字符)"
|
|
/>
|
|
</label>
|
|
)}
|
|
<label className={formFieldClass}>
|
|
<span>邮箱</span>
|
|
<Input
|
|
className={FORM_INPUT_CLASS}
|
|
type="email"
|
|
autoComplete="email"
|
|
value={form.email}
|
|
onChange={(event) => onChangeForm({ ...form, email: event.target.value })}
|
|
placeholder="请输入邮箱(可选)"
|
|
/>
|
|
</label>
|
|
<label className={formFieldClass}>
|
|
<span>角色</span>
|
|
<select
|
|
className={formInputClass}
|
|
value={form.role_code}
|
|
disabled={
|
|
editing
|
|
? editing.user_id === currentUserId || roles.length === 0
|
|
: createRoleOptions.length === 0
|
|
}
|
|
onChange={(event) =>
|
|
onChangeForm({
|
|
...form,
|
|
role_code: event.target.value as UserFormState["role_code"],
|
|
})
|
|
}
|
|
>
|
|
{editing ? (
|
|
roles.length === 0 ? (
|
|
<option value="" disabled>
|
|
加载中…
|
|
</option>
|
|
) : (
|
|
roles.map((role) => (
|
|
<option key={role.role_code} value={role.role_code}>
|
|
{role.role_name}
|
|
</option>
|
|
))
|
|
)
|
|
) : createRoleOptions.length === 0 ? (
|
|
<option value="" disabled>
|
|
加载中…
|
|
</option>
|
|
) : (
|
|
createRoleOptions.map((role) => (
|
|
<option key={role.role_code} value={role.role_code}>
|
|
{role.role_name}
|
|
</option>
|
|
))
|
|
)}
|
|
</select>
|
|
</label>
|
|
{!editing && (
|
|
<CreateUserProjectField
|
|
projects={createProjects}
|
|
loading={createProjectsLoading}
|
|
selectedIds={selectedWorkspaceIds}
|
|
onToggle={onToggleWorkspace}
|
|
/>
|
|
)}
|
|
{editing && (
|
|
<label className={formFieldClass}>
|
|
<span>状态</span>
|
|
<select
|
|
className={formInputClass}
|
|
value={form.status}
|
|
disabled={
|
|
editing.user_id === currentUserId || editing.role_code === "admin"
|
|
}
|
|
onChange={(event) =>
|
|
onChangeForm({
|
|
...form,
|
|
status: event.target.value as UserFormState["status"],
|
|
})
|
|
}
|
|
>
|
|
<option value="active">正常</option>
|
|
<option value="disabled">停用</option>
|
|
<option value="locked">锁定</option>
|
|
</select>
|
|
</label>
|
|
)}
|
|
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
type="button"
|
|
onClick={onClose}
|
|
className={dialogSecondaryButtonClass}
|
|
>
|
|
取消
|
|
</Button>
|
|
<Button
|
|
variant="default"
|
|
size="sm"
|
|
type="submit"
|
|
disabled={saving}
|
|
className={dialogPrimaryButtonClass}
|
|
>
|
|
{saving ? "保存中…" : "保存"}
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</AppFormDialog>
|
|
);
|
|
}
|