refactor(ui): unify all dialogs on radix base per guide ch.6/7
- base dialog: z raised to 300/301 (was below sidebar z-200 - dialogs appeared sunken under navigation), overlay bg-black/45 + 2px blur, unified close button with hover/focus-visible states, footer pt-4 - FormDialog normalized: width presets sm/md/lg/xl + optional description, single scrollable body (header/footer fixed), unified top-right close from base - ModalOverlay rewritten on radix kernel, same props (+title/noScroll): portal/focus trap/scroll lock/ESC with proper nesting (fixes chain-close when ticket detail opens quick-review), 200ms animation, z-300 layering - was a bare div with no a11y, no animation, ESC via document listener that leaked to nested dialogs - killed box-in-box: all 14 ModalOverlay callers stripped their inner card wrapper (double border/shadow/radius/padding gone) and duplicate scroll containers; EditInstanceDialog (4th standalone system) migrated to ModalOverlay, gains ESC/animation/portal - visual unification per guide: dialog title text-base semibold (was 4 sizes), close button one form (was 5), footer buttons on dg-btn system (secondary/primary/danger, was 4 systems), primary color var(--dg-blue) everywhere (yellow/emerald/blue-500/600 mixed before), widths 4 presets (was 7 arbitrary values) - dead code removed: 3 orphaned LDAP dialog components, leftover create-OU state/handler in BatchImportModal, unused icon imports verified in browser: users create dialog shows single card (0 inner cards), 12px radius, z-301/300, overlay blur, sidebar correctly covered; tsc/build/audit gates green
This commit is contained in:
1 parent
36724bba94
commit
48c8fddbe9
20 files changed
+1645
-1258
No files matched your search
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { XCircle } from 'lucide-react';
|
||||
import { userApi } from '@/api/user';
|
||||
import { toast } from '@/hooks/useToast';
|
||||
import { ModalOverlay } from '@/components/ModalOverlay';
|
||||
@@ -44,18 +43,20 @@ export function CreateUserModal({ onClose, onCreated }: CreateUserModalProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalOverlay open={true} onClose={onClose} width="max-w-md">
|
||||
<div className="w-full bg-[var(--dg-bg-secondary)] border border-[var(--dg-border)] rounded-xl p-6 shadow-xl">
|
||||
<ModalOverlay open={true} onClose={onClose} width="md">
|
||||
<div className="w-full">
|
||||
<div className="flex items-center justify-between mb-5">
|
||||
<h2 className="text-lg font-semibold">新建用户</h2>
|
||||
<button onClick={onClose} className="text-[var(--dg-text-muted)] hover:text-[var(--dg-text-primary)] transition-colors"><XCircle className="w-5 h-5" /></button>
|
||||
<h2 className="text-base font-semibold">新建用户</h2>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">用户名 *</label>
|
||||
<input
|
||||
value={username}
|
||||
onChange={e => { setUsername(e.target.value); if (errors.username) setErrors(p => ({ ...p, username: undefined })); }}
|
||||
onChange={(e) => {
|
||||
setUsername(e.target.value);
|
||||
if (errors.username) setErrors((p) => ({ ...p, username: undefined }));
|
||||
}}
|
||||
className={`w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border rounded-lg text-sm focus:border-blue-400 focus:outline-none ${errors.username ? 'border-red-400' : 'border-[var(--dg-border)]'}`}
|
||||
placeholder="3-30位字母、数字或下划线"
|
||||
/>
|
||||
@@ -63,14 +64,22 @@ export function CreateUserModal({ onClose, onCreated }: CreateUserModalProps) {
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">显示名称</label>
|
||||
<input value={displayName} onChange={e => setDisplayName(e.target.value)} className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none" placeholder="用户的显示名称" />
|
||||
<input
|
||||
value={displayName}
|
||||
onChange={(e) => setDisplayName(e.target.value)}
|
||||
className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none"
|
||||
placeholder="用户的显示名称"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">密码 *</label>
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={e => { setPassword(e.target.value); if (errors.password) setErrors(p => ({ ...p, password: undefined })); }}
|
||||
onChange={(e) => {
|
||||
setPassword(e.target.value);
|
||||
if (errors.password) setErrors((p) => ({ ...p, password: undefined }));
|
||||
}}
|
||||
className={`w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border rounded-lg text-sm focus:border-blue-400 focus:outline-none ${errors.password ? 'border-red-400' : 'border-[var(--dg-border)]'}`}
|
||||
placeholder="至少6位"
|
||||
/>
|
||||
@@ -81,7 +90,10 @@ export function CreateUserModal({ onClose, onCreated }: CreateUserModalProps) {
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={e => { setEmail(e.target.value); if (errors.email) setErrors(p => ({ ...p, email: undefined })); }}
|
||||
onChange={(e) => {
|
||||
setEmail(e.target.value);
|
||||
if (errors.email) setErrors((p) => ({ ...p, email: undefined }));
|
||||
}}
|
||||
className={`w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border rounded-lg text-sm focus:border-blue-400 focus:outline-none ${errors.email ? 'border-red-400' : 'border-[var(--dg-border)]'}`}
|
||||
placeholder="example@company.com"
|
||||
/>
|
||||
@@ -89,7 +101,11 @@ export function CreateUserModal({ onClose, onCreated }: CreateUserModalProps) {
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">角色</label>
|
||||
<select value={role} onChange={e => setRole(e.target.value)} className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none">
|
||||
<select
|
||||
value={role}
|
||||
onChange={(e) => setRole(e.target.value)}
|
||||
className="w-full px-3 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none"
|
||||
>
|
||||
<option value="admin">超级管理员</option>
|
||||
<option value="dba">数据库管理员</option>
|
||||
<option value="developer">开发人员</option>
|
||||
@@ -97,8 +113,10 @@ export function CreateUserModal({ onClose, onCreated }: CreateUserModalProps) {
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex justify-end gap-3 pt-2">
|
||||
<button onClick={onClose} className="px-4 py-2 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm hover:bg-[var(--dg-bg-hover)] transition-colors">取消</button>
|
||||
<button onClick={handleSubmit} disabled={submitting} className="px-4 py-2 bg-blue-500 text-white rounded-lg text-sm hover:opacity-90 disabled:opacity-50 transition-opacity">
|
||||
<button onClick={onClose} className="dg-btn dg-btn-secondary dg-btn-sm">
|
||||
取消
|
||||
</button>
|
||||
<button onClick={handleSubmit} disabled={submitting} className="dg-btn dg-btn-primary dg-btn-sm">
|
||||
{submitting ? '创建中...' : '创建'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user