fix(ui): 统一设计语言——颜色、字号、间距、控件样式
UI 修正清单: ┌────────────────────────────────────────────────────────────┐ │ 问题 │ 修复 │ ├──────────────────────────┼────────────────────────────────┤ │ 登录页 blue → 不一致 │ 统一改为 indigo 系 │ │ text-[10px] 标签难读 │ → text-xs (设置页所有标签) │ │ text-[9px] 徽章太小 │ → text-xs (角色/租户/权限) │ │ text-[14px] 输入框不统一 │ → text-sm (编辑弹窗) │ │ py-3 vs py-3.5 不一致 │ → py-3 统一 (所有输入框) │ │ 表格头 py-4 过大 │ → py-3 │ │ 编辑弹窗 max-w-md 太窄 │ → max-w-lg (更宽松) │ │ 操作列 opacity-0 隐藏 │ → opacity-60 始终可见 │ │ 原生 checkbox 不匹配 │ → 自定义圆角 checkbox + Check │ │ 登录页 rounded-lg │ → rounded-xl 统一 │ │ 登录按钮缺阴影 │ → 加 shadow-lg │ └────────────────────────────────────────────────────────────┘ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -35,7 +35,7 @@ const LoginPage: React.FC<LoginPageProps> = ({ onLoginSuccess }) => {
|
||||
<div className="min-h-screen bg-slate-50 flex flex-col items-center justify-center p-4">
|
||||
<div className="max-w-md w-full bg-white rounded-2xl shadow-xl p-8 border border-slate-100">
|
||||
<div className="flex justify-center mb-6">
|
||||
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center text-blue-600">
|
||||
<div className="w-16 h-16 bg-indigo-100 rounded-full flex items-center justify-center text-indigo-600">
|
||||
<ShieldCheck className="w-8 h-8" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -56,7 +56,7 @@ const LoginPage: React.FC<LoginPageProps> = ({ onLoginSuccess }) => {
|
||||
setUsername(e.target.value);
|
||||
setError('');
|
||||
}}
|
||||
className="w-full px-4 py-3 rounded-lg border border-slate-300 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all"
|
||||
className="w-full px-4 py-3 rounded-xl border border-slate-300 focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-500 outline-none transition-all"
|
||||
placeholder={t('usernamePlaceholder') || 'Username'}
|
||||
/>
|
||||
</div>
|
||||
@@ -68,7 +68,7 @@ const LoginPage: React.FC<LoginPageProps> = ({ onLoginSuccess }) => {
|
||||
setPassword(e.target.value);
|
||||
setError('');
|
||||
}}
|
||||
className="w-full px-4 py-3 rounded-lg border border-slate-300 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all"
|
||||
className="w-full px-4 py-3 rounded-xl border border-slate-300 focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-500 outline-none transition-all"
|
||||
placeholder={t('passwordPlaceholder') || 'Password'}
|
||||
/>
|
||||
{error && <p className="text-red-500 text-sm mt-1 ml-1">{error}</p>}
|
||||
@@ -76,7 +76,7 @@ const LoginPage: React.FC<LoginPageProps> = ({ onLoginSuccess }) => {
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full bg-blue-600 hover:bg-blue-700 text-white font-semibold py-3 rounded-lg flex items-center justify-center gap-2 transition-transform active:scale-95"
|
||||
className="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-semibold py-3 rounded-xl flex items-center justify-center gap-2 transition-transform active:scale-95 shadow-lg shadow-indigo-200"
|
||||
>
|
||||
{t('loginButton')}
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
|
||||
@@ -257,7 +257,7 @@ export const PermissionSettingsView: React.FC = () => {
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<span className="truncate">{role.name}</span>
|
||||
{role.isSystem && (
|
||||
<span className="text-[9px] font-black text-indigo-400 bg-indigo-100/50 px-1.5 py-0.5 rounded uppercase tracking-wider shrink-0">
|
||||
<span className="text-xs font-black text-indigo-400 bg-indigo-100/50 px-1.5 py-0.5 rounded uppercase tracking-wider shrink-0">
|
||||
系统
|
||||
</span>
|
||||
)}
|
||||
@@ -413,18 +413,28 @@ export const PermissionSettingsView: React.FC = () => {
|
||||
!selectedRole.isSystem ? 'cursor-pointer' : 'cursor-not-allowed opacity-60',
|
||||
)}
|
||||
>
|
||||
<div className={cn(
|
||||
'w-5 h-5 rounded-md flex items-center justify-center border-2 transition-all shrink-0',
|
||||
editingPermissions.has(perm.key)
|
||||
? 'bg-indigo-600 border-indigo-600 text-white'
|
||||
: 'border-slate-300 bg-white',
|
||||
selectedRole.isSystem ? 'opacity-40' : '',
|
||||
)}>
|
||||
{editingPermissions.has(perm.key) && <Check size={14} strokeWidth={3} />}
|
||||
</div>
|
||||
{/* hidden native checkbox for a11y */}
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={editingPermissions.has(perm.key)}
|
||||
onChange={() => togglePermission(perm.key)}
|
||||
disabled={selectedRole.isSystem}
|
||||
className="w-4 h-4 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500/20 cursor-pointer disabled:cursor-not-allowed"
|
||||
className="sr-only"
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-sm font-bold text-slate-800">{perm.label}</div>
|
||||
<div className="text-xs text-slate-400">{perm.description}</div>
|
||||
</div>
|
||||
<code className="text-[10px] text-slate-300 font-mono shrink-0">{perm.key}</code>
|
||||
<code className="text-xs text-slate-300 font-mono shrink-0">{perm.key}</code>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1015,7 +1015,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
|
||||
initial={{ scale: 0.95, opacity: 0 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
exit={{ scale: 0.95, opacity: 0 }}
|
||||
className="bg-white rounded-3xl p-10 w-full max-w-md shadow-2xl border border-white/20"
|
||||
className="bg-white rounded-3xl p-10 w-full max-w-lg shadow-2xl border border-white/20"
|
||||
>
|
||||
<div className="flex items-center justify-between mb-8">
|
||||
<h3 className="text-xl font-black text-slate-900 tracking-tight">{t('editUser')}</h3>
|
||||
@@ -1026,35 +1026,35 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
|
||||
|
||||
<form onSubmit={(e) => { e.preventDefault(); handleUpdateUser(); }} className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2 px-1">
|
||||
<label className="block text-xs font-black text-slate-400 uppercase tracking-wider mb-2 px-1">
|
||||
{t('username')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={editUserData.username}
|
||||
onChange={(e) => setEditUserData({ ...editUserData, username: e.target.value })}
|
||||
className="w-full px-4 py-3.5 bg-slate-50 border border-slate-200 rounded-2xl text-[14px] font-medium transition-all focus:outline-none focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/50"
|
||||
className="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-medium transition-all focus:outline-none focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/50"
|
||||
placeholder={t('usernamePlaceholder')}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2 px-1">
|
||||
<label className="block text-xs font-black text-slate-400 uppercase tracking-wider mb-2 px-1">
|
||||
{t('displayName') || t('name')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={editUserData.displayName}
|
||||
onChange={(e) => setEditUserData({ ...editUserData, displayName: e.target.value })}
|
||||
className="w-full px-4 py-3.5 bg-slate-50 border border-slate-200 rounded-2xl text-[14px] font-medium transition-all focus:outline-none focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/50"
|
||||
className="w-full px-4 py-3 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-medium transition-all focus:outline-none focus:ring-4 focus:ring-indigo-500/10 focus:border-indigo-500/50"
|
||||
placeholder={t('displayNamePlaceholder') || t('namePlaceholder')}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{/* 角色选择 */}
|
||||
<div>
|
||||
<label className="block text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2 px-1">
|
||||
<label className="block text-xs font-black text-slate-400 uppercase tracking-wider mb-2 px-1">
|
||||
角色
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
@@ -1089,23 +1089,23 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
|
||||
|
||||
{/* 权限预览 */}
|
||||
<div className="py-3 px-4 bg-slate-50 rounded-2xl border border-slate-100">
|
||||
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-2">
|
||||
<p className="text-xs font-black text-slate-400 uppercase tracking-wider mb-2">
|
||||
该角色的权限 ({editUserData.role === 'SUPER_ADMIN' ? '26项' : editUserData.role === 'TENANT_ADMIN' ? '21项' : '5项'})
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{editUserData.role === 'SUPER_ADMIN' && (
|
||||
['全部权限:用户管理、租户管理、知识库、考核评估、模型配置、插件管理、系统设置'].map(p => (
|
||||
<span key={p} className="px-2 py-0.5 bg-indigo-50 text-indigo-600 text-[9px] font-bold rounded-md">{p}</span>
|
||||
<span key={p} className="px-2 py-0.5 bg-indigo-50 text-indigo-600 text-xs font-bold rounded-md">{p}</span>
|
||||
))
|
||||
)}
|
||||
{editUserData.role === 'TENANT_ADMIN' && (
|
||||
['查看用户','创建用户','编辑用户','重置密码','管理知识库','管理考核','管理模型','管理插件'].map(p => (
|
||||
<span key={p} className="px-2 py-0.5 bg-indigo-50 text-indigo-600 text-[9px] font-bold rounded-md">{p}</span>
|
||||
<span key={p} className="px-2 py-0.5 bg-indigo-50 text-indigo-600 text-xs font-bold rounded-md">{p}</span>
|
||||
))
|
||||
)}
|
||||
{editUserData.role === 'USER' && (
|
||||
['使用知识库','参与考核'].map(p => (
|
||||
<span key={p} className="px-2 py-0.5 bg-slate-100 text-slate-500 text-[9px] font-bold rounded-md">{p}</span>
|
||||
<span key={p} className="px-2 py-0.5 bg-slate-100 text-slate-500 text-xs font-bold rounded-md">{p}</span>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
@@ -1127,12 +1127,12 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
|
||||
<table className="w-full border-collapse text-left">
|
||||
<thead>
|
||||
<tr className="bg-slate-50/50 border-b border-slate-200/50">
|
||||
<th className="px-6 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest">{t('username')}</th>
|
||||
<th className="px-6 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest">{t('displayName') || t('name')}</th>
|
||||
<th className="px-6 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest">{t('organizations')}</th>
|
||||
<th className="px-6 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest">角色</th>
|
||||
<th className="px-6 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest">{t('createdAt')}</th>
|
||||
<th className="px-6 py-4 text-[10px] font-black text-slate-400 uppercase tracking-widest text-right">{t('actions')}</th>
|
||||
<th className="px-6 py-3 text-xs font-black text-slate-400 uppercase tracking-wider">{t('username')}</th>
|
||||
<th className="px-6 py-3 text-xs font-black text-slate-400 uppercase tracking-wider">{t('displayName') || t('name')}</th>
|
||||
<th className="px-6 py-3 text-xs font-black text-slate-400 uppercase tracking-wider">{t('organizations')}</th>
|
||||
<th className="px-6 py-3 text-xs font-black text-slate-400 uppercase tracking-wider">角色</th>
|
||||
<th className="px-6 py-3 text-xs font-black text-slate-400 uppercase tracking-wider">{t('createdAt')}</th>
|
||||
<th className="px-6 py-3 text-xs font-black text-slate-400 uppercase tracking-wider text-right">{t('actions')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-100">
|
||||
@@ -1174,7 +1174,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
|
||||
.map((m: any) => (
|
||||
<span
|
||||
key={m.tenantId}
|
||||
className="inline-flex items-center gap-1 px-2 py-0.5 bg-emerald-50 text-emerald-700 text-[9px] font-black rounded-md uppercase tracking-wider border border-emerald-100"
|
||||
className="inline-flex items-center gap-1 px-2 py-0.5 bg-emerald-50 text-emerald-700 text-xs font-black rounded-md uppercase tracking-wider border border-emerald-100"
|
||||
>
|
||||
<Building size={8} />
|
||||
{m.tenant?.name || m.tenantId}
|
||||
@@ -1195,7 +1195,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
|
||||
USER: 'bg-slate-50 text-slate-500 border-slate-100',
|
||||
};
|
||||
return (
|
||||
<span className={`inline-flex items-center gap-1 px-2 py-0.5 text-[9px] font-black rounded-md uppercase tracking-wider border ${colors[role] || colors.USER}`}>
|
||||
<span className={`inline-flex items-center gap-1 px-2 py-0.5 text-xs font-black rounded-md uppercase tracking-wider border ${colors[role] || colors.USER}`}>
|
||||
{role === 'SUPER_ADMIN' ? '超级管理员' : role === 'TENANT_ADMIN' ? '管理员' : '用户'}
|
||||
</span>
|
||||
);
|
||||
@@ -1207,7 +1207,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
|
||||
</p>
|
||||
</td>
|
||||
<td className="px-6 py-4 text-right">
|
||||
<div className="flex items-center justify-end gap-1 opacity-0 group-hover:opacity-100 transition-all">
|
||||
<div className="flex items-center justify-end gap-1 opacity-60 group-hover:opacity-100 transition-all">
|
||||
{user.username !== 'admin' && (
|
||||
<>
|
||||
<button
|
||||
|
||||
Reference in New Issue
Block a user