update: use components
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
import type { PlatformPermission } from "../../services/api";
|
import type { PlatformPermission } from "../../services/api";
|
||||||
|
|
||||||
|
import { Checkbox } from "~/components/ui/checkbox";
|
||||||
|
|
||||||
/** 模块编码 → 展示名(用于权限分组标题)。 */
|
/** 模块编码 → 展示名(用于权限分组标题)。 */
|
||||||
const MODULE_LABELS: Record<string, string> = {
|
const MODULE_LABELS: Record<string, string> = {
|
||||||
dashboard: "工作台",
|
dashboard: "工作台",
|
||||||
@@ -8,6 +10,10 @@ const MODULE_LABELS: Record<string, string> = {
|
|||||||
system: "系统管理",
|
system: "系统管理",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** 勾选框样式:复刻原生 checkbox 外观(白底、灰边、蓝色勾选),并对齐 `.permission-item input` 的 3px 顶部偏移。 */
|
||||||
|
const CHECKBOX_CLASS =
|
||||||
|
"mt-[3px] size-[16px] rounded-[3px] border-[#cdd9e4] bg-white disabled:opacity-100 data-checked:border-[#1277d3] data-checked:bg-[#1277d3] data-checked:text-white";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 权限勾选列表:按 module_code 分组渲染。
|
* 权限勾选列表:按 module_code 分组渲染。
|
||||||
* - targetRoleCode === "admin" 时 system:view 锁定勾选(后端守卫);
|
* - targetRoleCode === "admin" 时 system:view 锁定勾选(后端守卫);
|
||||||
@@ -58,11 +64,11 @@ export function PermissionCheckboxList({
|
|||||||
className={`permission-item${locked ? " is-locked" : ""}`}
|
className={`permission-item${locked ? " is-locked" : ""}`}
|
||||||
key={code}
|
key={code}
|
||||||
>
|
>
|
||||||
<input
|
<Checkbox
|
||||||
type="checkbox"
|
|
||||||
checked={checked}
|
checked={checked}
|
||||||
disabled={locked}
|
disabled={locked}
|
||||||
onChange={() => onToggle(code)}
|
onCheckedChange={() => onToggle(code)}
|
||||||
|
className={CHECKBOX_CLASS}
|
||||||
/>
|
/>
|
||||||
<span>
|
<span>
|
||||||
<strong>{permission.permission_name}</strong>
|
<strong>{permission.permission_name}</strong>
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
// TODO: split file (>500 lines per CLAUDE.md 3.2.1 budget)
|
||||||
|
import { X } from "lucide-react";
|
||||||
import { ApiRequestError, type Employee, type Workspace, type WorkspaceMember } from "../../services/api";
|
import { ApiRequestError, type Employee, type Workspace, type WorkspaceMember } from "../../services/api";
|
||||||
import { useApi, useAuth } from "../../context/AuthContext";
|
import { useApi, useAuth } from "../../context/AuthContext";
|
||||||
import Icon from "../../components/common/Icon";
|
import Icon from "../../components/common/Icon";
|
||||||
|
import { Button } from "~/components/ui/button";
|
||||||
|
import { Input } from "~/components/ui/input";
|
||||||
|
import { Textarea } from "~/components/ui/textarea";
|
||||||
import { UserMultiSelect } from "./UserMultiSelect";
|
import { UserMultiSelect } from "./UserMultiSelect";
|
||||||
import {
|
import {
|
||||||
formFieldClass,
|
formFieldClass,
|
||||||
formInputClass,
|
|
||||||
formTextareaClass,
|
|
||||||
iconButtonClass,
|
|
||||||
modalBackdropClass,
|
modalBackdropClass,
|
||||||
modalCompactClass,
|
modalCompactClass,
|
||||||
modalEyebrowClass,
|
modalEyebrowClass,
|
||||||
@@ -16,8 +18,6 @@ import {
|
|||||||
modalFormClass,
|
modalFormClass,
|
||||||
modalHeaderClass,
|
modalHeaderClass,
|
||||||
modalTitleClass,
|
modalTitleClass,
|
||||||
primaryButtonClass,
|
|
||||||
secondaryButtonClass,
|
|
||||||
} from "../platform/modalUi";
|
} from "../platform/modalUi";
|
||||||
|
|
||||||
import "../../styles/admin.css";
|
import "../../styles/admin.css";
|
||||||
@@ -29,6 +29,17 @@ const EMPTY_PROJECT_FORM = {
|
|||||||
description: "",
|
description: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CLOSE_BUTTON_CLASS =
|
||||||
|
"size-[34px] rounded-[7px] border border-[#dfe6ed] bg-white hover:bg-[#f7faff] hover:border-[#b9c9da]";
|
||||||
|
const SECONDARY_BUTTON_CLASS =
|
||||||
|
"h-[37px] gap-[7px] rounded-md border-[#d8e1e9] bg-white px-[15px] text-xs font-[650] text-[#56687c]";
|
||||||
|
const PRIMARY_BUTTON_CLASS =
|
||||||
|
"h-[37px] gap-[7px] rounded-md border border-[#126ac3] bg-gradient-to-br from-[#1881e7] to-[#1268c9] px-[15px] text-xs font-[650] text-white shadow-[0_6px_15px_rgb(20_111_202/18%)] enabled:hover:from-[#1175d8] enabled:hover:to-[#0e5bad]";
|
||||||
|
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:shadow-[0_0_0_3px_rgb(38_125_207/8%)] disabled:cursor-not-allowed disabled:opacity-50";
|
||||||
|
const FORM_TEXTAREA_CLASS =
|
||||||
|
"min-h-[92px] w-full resize-y rounded-md border border-[#d6e0e9] bg-white px-[11px] py-2.5 text-xs leading-[1.55] text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:shadow-[0_0_0_3px_rgb(38_125_207/8%)]";
|
||||||
|
|
||||||
export function ProjectManagementPage({
|
export function ProjectManagementPage({
|
||||||
onNotify,
|
onNotify,
|
||||||
onConnectionChange,
|
onConnectionChange,
|
||||||
@@ -253,15 +264,17 @@ export function ProjectManagementPage({
|
|||||||
onChange={(event) => setProjectSearchTerm(event.target.value)}
|
onChange={(event) => setProjectSearchTerm(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<Button
|
||||||
className="primary-button admin-toolbar__button"
|
variant="default"
|
||||||
|
size="sm"
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!canManage}
|
disabled={!canManage}
|
||||||
onClick={openCreateProject}
|
onClick={openCreateProject}
|
||||||
|
className={PRIMARY_BUTTON_CLASS}
|
||||||
>
|
>
|
||||||
<Icon name="plus" size={15} />
|
<Icon name="plus" size={15} />
|
||||||
新建项目
|
新建项目
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!canManage && <div className="admin-readonly">当前为开发人员,只能查看项目列表。</div>}
|
{!canManage && <div className="admin-readonly">当前为开发人员,只能查看项目列表。</div>}
|
||||||
@@ -331,16 +344,23 @@ export function ProjectManagementPage({
|
|||||||
<span className={modalEyebrowClass}>PROJECT</span>
|
<span className={modalEyebrowClass}>PROJECT</span>
|
||||||
<h2 className={modalTitleClass}>{editingProject ? "编辑项目" : "新建项目"}</h2>
|
<h2 className={modalTitleClass}>{editingProject ? "编辑项目" : "新建项目"}</h2>
|
||||||
</div>
|
</div>
|
||||||
<button className={iconButtonClass} type="button" aria-label="关闭" onClick={() => setProjectDialogOpen(false)}>
|
<Button
|
||||||
<Icon name="close" />
|
variant="ghost"
|
||||||
</button>
|
size="icon-sm"
|
||||||
|
type="button"
|
||||||
|
aria-label="关闭"
|
||||||
|
onClick={() => setProjectDialogOpen(false)}
|
||||||
|
className={CLOSE_BUTTON_CLASS}
|
||||||
|
>
|
||||||
|
<X size={16} />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<form className={modalFormClass} onSubmit={(event) => void submitProject(event)}>
|
<form className={modalFormClass} onSubmit={(event) => void submitProject(event)}>
|
||||||
{!editingProject && (
|
{!editingProject && (
|
||||||
<label className={formFieldClass}>
|
<label className={formFieldClass}>
|
||||||
<span>项目编码<span className="text-[#e74c3c]">*</span></span>
|
<span>项目编码<span className="text-[#e74c3c]">*</span></span>
|
||||||
<input
|
<Input
|
||||||
className={formInputClass}
|
className={FORM_INPUT_CLASS}
|
||||||
autoFocus
|
autoFocus
|
||||||
value={projectForm.workspace_code}
|
value={projectForm.workspace_code}
|
||||||
onChange={(event) => setProjectForm({ ...projectForm, workspace_code: event.target.value })}
|
onChange={(event) => setProjectForm({ ...projectForm, workspace_code: event.target.value })}
|
||||||
@@ -350,8 +370,8 @@ export function ProjectManagementPage({
|
|||||||
)}
|
)}
|
||||||
<label className={formFieldClass}>
|
<label className={formFieldClass}>
|
||||||
<span>项目名称<span className="text-[#e74c3c]">*</span></span>
|
<span>项目名称<span className="text-[#e74c3c]">*</span></span>
|
||||||
<input
|
<Input
|
||||||
className={formInputClass}
|
className={FORM_INPUT_CLASS}
|
||||||
value={projectForm.workspace_name}
|
value={projectForm.workspace_name}
|
||||||
onChange={(event) => setProjectForm({ ...projectForm, workspace_name: event.target.value })}
|
onChange={(event) => setProjectForm({ ...projectForm, workspace_name: event.target.value })}
|
||||||
placeholder="请输入项目名称"
|
placeholder="请输入项目名称"
|
||||||
@@ -359,8 +379,8 @@ export function ProjectManagementPage({
|
|||||||
</label>
|
</label>
|
||||||
<label className={formFieldClass}>
|
<label className={formFieldClass}>
|
||||||
<span>配额(字节)</span>
|
<span>配额(字节)</span>
|
||||||
<input
|
<Input
|
||||||
className={formInputClass}
|
className={FORM_INPUT_CLASS}
|
||||||
type="number"
|
type="number"
|
||||||
value={projectForm.quota_bytes}
|
value={projectForm.quota_bytes}
|
||||||
onChange={(event) => setProjectForm({ ...projectForm, quota_bytes: parseInt(event.target.value) || 0 })}
|
onChange={(event) => setProjectForm({ ...projectForm, quota_bytes: parseInt(event.target.value) || 0 })}
|
||||||
@@ -369,8 +389,8 @@ export function ProjectManagementPage({
|
|||||||
</label>
|
</label>
|
||||||
<label className={formFieldClass}>
|
<label className={formFieldClass}>
|
||||||
<span>项目描述</span>
|
<span>项目描述</span>
|
||||||
<textarea
|
<Textarea
|
||||||
className={formTextareaClass}
|
className={FORM_TEXTAREA_CLASS}
|
||||||
value={projectForm.description}
|
value={projectForm.description}
|
||||||
onChange={(event) => setProjectForm({ ...projectForm, description: event.target.value })}
|
onChange={(event) => setProjectForm({ ...projectForm, description: event.target.value })}
|
||||||
placeholder="请输入项目描述(可选)"
|
placeholder="请输入项目描述(可选)"
|
||||||
@@ -378,8 +398,24 @@ export function ProjectManagementPage({
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<div className={modalFooterClass}>
|
<div className={modalFooterClass}>
|
||||||
<button className={secondaryButtonClass} type="button" onClick={() => setProjectDialogOpen(false)}>取消</button>
|
<Button
|
||||||
<button className={primaryButtonClass} type="submit" disabled={saving}>{saving ? "保存中…" : "保存"}</button>
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
onClick={() => setProjectDialogOpen(false)}
|
||||||
|
className={SECONDARY_BUTTON_CLASS}
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
disabled={saving}
|
||||||
|
className={PRIMARY_BUTTON_CLASS}
|
||||||
|
>
|
||||||
|
{saving ? "保存中…" : "保存"}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
@@ -394,9 +430,16 @@ export function ProjectManagementPage({
|
|||||||
<span className={modalEyebrowClass}>IMPORT MEMBER</span>
|
<span className={modalEyebrowClass}>IMPORT MEMBER</span>
|
||||||
<h2 className={modalTitleClass}>导入成员到 {selectedProject?.workspace_name ?? "项目"}</h2>
|
<h2 className={modalTitleClass}>导入成员到 {selectedProject?.workspace_name ?? "项目"}</h2>
|
||||||
</div>
|
</div>
|
||||||
<button className={iconButtonClass} type="button" aria-label="关闭" onClick={() => setImportMemberDialogOpen(false)}>
|
<Button
|
||||||
<Icon name="close" />
|
variant="ghost"
|
||||||
</button>
|
size="icon-sm"
|
||||||
|
type="button"
|
||||||
|
aria-label="关闭"
|
||||||
|
onClick={() => setImportMemberDialogOpen(false)}
|
||||||
|
className={CLOSE_BUTTON_CLASS}
|
||||||
|
>
|
||||||
|
<X size={16} />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<form className={modalFormClass}>
|
<form className={modalFormClass}>
|
||||||
<label className={`${formFieldClass} pb-[120px]`}>
|
<label className={`${formFieldClass} pb-[120px]`}>
|
||||||
@@ -409,10 +452,25 @@ export function ProjectManagementPage({
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<div className={modalFooterClass}>
|
<div className={modalFooterClass}>
|
||||||
<button className={secondaryButtonClass} type="button" onClick={() => setImportMemberDialogOpen(false)}>取消</button>
|
<Button
|
||||||
<button className={primaryButtonClass} type="button" disabled={saving || selectedUserIds.length === 0} onClick={() => void importMember()}>
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
onClick={() => setImportMemberDialogOpen(false)}
|
||||||
|
className={SECONDARY_BUTTON_CLASS}
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
disabled={saving || selectedUserIds.length === 0}
|
||||||
|
onClick={() => void importMember()}
|
||||||
|
className={PRIMARY_BUTTON_CLASS}
|
||||||
|
>
|
||||||
{saving ? "添加中…" : `添加 (${selectedUserIds.length})`}
|
{saving ? "添加中…" : `添加 (${selectedUserIds.length})`}
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { X } from "lucide-react";
|
||||||
import { ApiRequestError, type Employee, type Role } from "../../services/api";
|
import { ApiRequestError, type Employee, type Role } from "../../services/api";
|
||||||
import { useApi, useAuth } from "../../context/AuthContext";
|
import { useApi, useAuth } from "../../context/AuthContext";
|
||||||
import Icon from "../../components/common/Icon";
|
import Icon from "../../components/common/Icon";
|
||||||
|
import { Button } from "~/components/ui/button";
|
||||||
|
import { Input } from "~/components/ui/input";
|
||||||
import {
|
import {
|
||||||
formFieldClass,
|
formFieldClass,
|
||||||
formInputClass,
|
formInputClass,
|
||||||
iconButtonClass,
|
|
||||||
modalBackdropClass,
|
modalBackdropClass,
|
||||||
modalCompactClass,
|
modalCompactClass,
|
||||||
modalEyebrowClass,
|
modalEyebrowClass,
|
||||||
@@ -14,8 +16,6 @@ import {
|
|||||||
modalFormClass,
|
modalFormClass,
|
||||||
modalHeaderClass,
|
modalHeaderClass,
|
||||||
modalTitleClass,
|
modalTitleClass,
|
||||||
primaryButtonClass,
|
|
||||||
secondaryButtonClass,
|
|
||||||
} from "../platform/modalUi";
|
} from "../platform/modalUi";
|
||||||
|
|
||||||
import "../../styles/admin.css";
|
import "../../styles/admin.css";
|
||||||
@@ -29,6 +29,19 @@ const EMPTY_FORM = {
|
|||||||
status: "active" as "active" | "disabled" | "locked",
|
status: "active" as "active" | "disabled" | "locked",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const CLOSE_BUTTON_CLASS =
|
||||||
|
"size-[34px] rounded-[7px] border border-[#dfe6ed] bg-white hover:bg-[#f7faff] hover:border-[#b9c9da]";
|
||||||
|
const SECONDARY_BUTTON_CLASS =
|
||||||
|
"h-[37px] gap-[7px] rounded-md border-[#d8e1e9] bg-white px-[15px] text-xs font-[650] text-[#56687c]";
|
||||||
|
const PRIMARY_BUTTON_CLASS =
|
||||||
|
"h-[37px] gap-[7px] rounded-md border border-[#126ac3] bg-gradient-to-br from-[#1881e7] to-[#1268c9] px-[15px] text-xs font-[650] text-white shadow-[0_6px_15px_rgb(20_111_202/18%)] enabled:hover:from-[#1175d8] enabled:hover:to-[#0e5bad]";
|
||||||
|
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:shadow-[0_0_0_3px_rgb(38_125_207/8%)] disabled:cursor-not-allowed disabled:opacity-50";
|
||||||
|
const ROW_BUTTON_CLASS =
|
||||||
|
"h-6 gap-1 rounded-[4px] border-[#d9e3ec] bg-white px-[9px] text-[11px] text-[#4c6c88] hover:bg-[#f7fafc] disabled:cursor-not-allowed disabled:opacity-[0.45]";
|
||||||
|
const ROW_DANGER_BUTTON_CLASS =
|
||||||
|
"h-6 gap-1 rounded-[4px] border-[#d9e3ec] bg-white px-[9px] text-[11px] text-[#c14a4a] hover:bg-[#fdf2f2] disabled:cursor-not-allowed disabled:opacity-[0.45]";
|
||||||
|
|
||||||
export function UserManagementPage({
|
export function UserManagementPage({
|
||||||
onNotify,
|
onNotify,
|
||||||
onConnectionChange,
|
onConnectionChange,
|
||||||
@@ -185,15 +198,17 @@ export function UserManagementPage({
|
|||||||
onChange={(event) => setUserSearchTerm(event.target.value)}
|
onChange={(event) => setUserSearchTerm(event.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<Button
|
||||||
className="primary-button admin-toolbar__button"
|
variant="default"
|
||||||
|
size="sm"
|
||||||
type="button"
|
type="button"
|
||||||
disabled={!canManage}
|
disabled={!canManage}
|
||||||
onClick={openCreate}
|
onClick={openCreate}
|
||||||
|
className={PRIMARY_BUTTON_CLASS}
|
||||||
>
|
>
|
||||||
<Icon name="plus" size={15} />
|
<Icon name="plus" size={15} />
|
||||||
新建用户
|
新建用户
|
||||||
</button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!canManage && <div className="admin-readonly">当前为开发人员,只能查看用户列表。</div>}
|
{!canManage && <div className="admin-readonly">当前为开发人员,只能查看用户列表。</div>}
|
||||||
@@ -221,8 +236,27 @@ export function UserManagementPage({
|
|||||||
{employee.role_code ? (<span className={`role-pill is-${employee.role_code}`}>{employee.role_name}</span>) : (<span>-</span>)}
|
{employee.role_code ? (<span className={`role-pill is-${employee.role_code}`}>{employee.role_name}</span>) : (<span>-</span>)}
|
||||||
<span className={`status-pill is-${employee.status}`}>{employee.status === "active" ? "正常" : employee.status === "disabled" ? "已停用" : "已锁定"}</span>
|
<span className={`status-pill is-${employee.status}`}>{employee.status === "active" ? "正常" : employee.status === "disabled" ? "已停用" : "已锁定"}</span>
|
||||||
<span className="employee-actions">
|
<span className="employee-actions">
|
||||||
<button type="button" disabled={!canManage} onClick={() => openEdit(employee)}>编辑</button>
|
<Button
|
||||||
<button type="button" className="is-danger" disabled={!canManage || isProtectedAdmin} title={isProtectedAdmin ? "管理员账号不能删除" : "删除"} onClick={() => void remove(employee)}>删除</button>
|
variant="outline"
|
||||||
|
size="xs"
|
||||||
|
type="button"
|
||||||
|
disabled={!canManage}
|
||||||
|
onClick={() => openEdit(employee)}
|
||||||
|
className={ROW_BUTTON_CLASS}
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
size="xs"
|
||||||
|
type="button"
|
||||||
|
disabled={!canManage || isProtectedAdmin}
|
||||||
|
title={isProtectedAdmin ? "管理员账号不能删除" : "删除"}
|
||||||
|
onClick={() => void remove(employee)}
|
||||||
|
className={ROW_DANGER_BUTTON_CLASS}
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -238,15 +272,22 @@ export function UserManagementPage({
|
|||||||
<span className={modalEyebrowClass}>EMPLOYEE</span>
|
<span className={modalEyebrowClass}>EMPLOYEE</span>
|
||||||
<h2 className={modalTitleClass}>{editing ? "编辑用户" : "添加用户"}</h2>
|
<h2 className={modalTitleClass}>{editing ? "编辑用户" : "添加用户"}</h2>
|
||||||
</div>
|
</div>
|
||||||
<button className={iconButtonClass} type="button" aria-label="关闭" onClick={() => setDialogOpen(false)}>
|
<Button
|
||||||
<Icon name="close" />
|
variant="ghost"
|
||||||
</button>
|
size="icon-sm"
|
||||||
|
type="button"
|
||||||
|
aria-label="关闭"
|
||||||
|
onClick={() => setDialogOpen(false)}
|
||||||
|
className={CLOSE_BUTTON_CLASS}
|
||||||
|
>
|
||||||
|
<X size={16} />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<form className={modalFormClass} onSubmit={(event) => void submit(event)}>
|
<form className={modalFormClass} onSubmit={(event) => void submit(event)}>
|
||||||
<label className={formFieldClass}>
|
<label className={formFieldClass}>
|
||||||
<span>姓名<span className="text-[#e74c3c]">*</span></span>
|
<span>姓名<span className="text-[#e74c3c]">*</span></span>
|
||||||
<input
|
<Input
|
||||||
className={formInputClass}
|
className={FORM_INPUT_CLASS}
|
||||||
autoFocus={!editing}
|
autoFocus={!editing}
|
||||||
value={form.display_name}
|
value={form.display_name}
|
||||||
onChange={(event) => setForm({ ...form, display_name: event.target.value })}
|
onChange={(event) => setForm({ ...form, display_name: event.target.value })}
|
||||||
@@ -255,8 +296,8 @@ export function UserManagementPage({
|
|||||||
</label>
|
</label>
|
||||||
<label className={formFieldClass}>
|
<label className={formFieldClass}>
|
||||||
<span>登录账号<span className="text-[#e74c3c]">*</span></span>
|
<span>登录账号<span className="text-[#e74c3c]">*</span></span>
|
||||||
<input
|
<Input
|
||||||
className={formInputClass}
|
className={FORM_INPUT_CLASS}
|
||||||
disabled={Boolean(editing)}
|
disabled={Boolean(editing)}
|
||||||
value={form.username}
|
value={form.username}
|
||||||
onChange={(event) => setForm({ ...form, username: event.target.value })}
|
onChange={(event) => setForm({ ...form, username: event.target.value })}
|
||||||
@@ -267,8 +308,8 @@ export function UserManagementPage({
|
|||||||
{!editing && (
|
{!editing && (
|
||||||
<label className={formFieldClass}>
|
<label className={formFieldClass}>
|
||||||
<span>密码<span className="text-[#e74c3c]">*</span></span>
|
<span>密码<span className="text-[#e74c3c]">*</span></span>
|
||||||
<input
|
<Input
|
||||||
className={formInputClass}
|
className={FORM_INPUT_CLASS}
|
||||||
type="password"
|
type="password"
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
value={form.password}
|
value={form.password}
|
||||||
@@ -279,8 +320,8 @@ export function UserManagementPage({
|
|||||||
)}
|
)}
|
||||||
<label className={formFieldClass}>
|
<label className={formFieldClass}>
|
||||||
<span>邮箱</span>
|
<span>邮箱</span>
|
||||||
<input
|
<Input
|
||||||
className={formInputClass}
|
className={FORM_INPUT_CLASS}
|
||||||
type="email"
|
type="email"
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
value={form.email}
|
value={form.email}
|
||||||
@@ -325,8 +366,24 @@ export function UserManagementPage({
|
|||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
<div className={modalFooterClass}>
|
<div className={modalFooterClass}>
|
||||||
<button className={secondaryButtonClass} type="button" onClick={() => setDialogOpen(false)}>取消</button>
|
<Button
|
||||||
<button className={primaryButtonClass} type="submit" disabled={saving}>{saving ? "保存中…" : "保存"}</button>
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
type="button"
|
||||||
|
onClick={() => setDialogOpen(false)}
|
||||||
|
className={SECONDARY_BUTTON_CLASS}
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
size="sm"
|
||||||
|
type="submit"
|
||||||
|
disabled={saving}
|
||||||
|
className={PRIMARY_BUTTON_CLASS}
|
||||||
|
>
|
||||||
|
{saving ? "保存中…" : "保存"}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user