feat: 简化字段卡片交互 - 点击直接打开编辑弹窗

- 移除字段卡片选中样式
- 点击字段直接打开编辑弹窗
- 简化 FieldItem 组件,移除 isSelected 属性
- 简化 FieldList 接口,onSelect 改为 onEdit

fix: 优化字段编辑弹窗

- 去除弹窗描述文字
- 字段名称增加字符计数 (x/20)
- 字段描述增加字符计数 (x/50)
- 保持 placeholder 提示

fix: 修复弹窗顶部间距问题

- DialogContent 添加 pt-6 避开关闭按钮
- 内容区域移除顶部 padding,改为 pb-6

fix: 增加弹窗顶部间距 (pt-6 → pt-8)

fix: 优化弹窗尺寸和按钮间距

- 弹窗最大宽度 360→420px 适配标签页
- 增加按钮 padding (px-2→px-3, py-1→py-1.5)
- 添加按钮增加 padding (h-8→h-9, px-3)
This commit is contained in:
雨霖铃
2026-06-06 10:35:30 +08:00
parent 40443c8fc5
commit ea10b27c14
4 changed files with 32 additions and 49 deletions
@@ -73,9 +73,12 @@ export default function FieldEditor({ field, onChange }: FieldEditorProps) {
{/* 基础配置 */} {/* 基础配置 */}
<div className="space-y-4"> <div className="space-y-4">
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium text-foreground"> <div className="flex items-center justify-between">
{t('testDataGenerator_fieldName')} <label className="text-sm font-medium text-foreground">
</label> {t('testDataGenerator_fieldName')}
</label>
<span className="text-xs text-muted-foreground">{field.name.length}/20</span>
</div>
<Input <Input
value={field.name} value={field.name}
onChange={(e) => handleNameChange(e.target.value)} onChange={(e) => handleNameChange(e.target.value)}
@@ -86,9 +89,14 @@ export default function FieldEditor({ field, onChange }: FieldEditorProps) {
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium text-foreground"> <div className="flex items-center justify-between">
{t('testDataGenerator_fieldDescription')} <label className="text-sm font-medium text-foreground">
</label> {t('testDataGenerator_fieldDescription')}
</label>
<span className="text-xs text-muted-foreground">
{(field.description || '').length}/50
</span>
</div>
<Input <Input
value={field.description || ''} value={field.description || ''}
onChange={(e) => handleDescriptionChange(e.target.value)} onChange={(e) => handleDescriptionChange(e.target.value)}
@@ -132,7 +140,7 @@ export default function FieldEditor({ field, onChange }: FieldEditorProps) {
<button <button
key={preset.value} key={preset.value}
onClick={() => handleNullRateChange(preset.value)} onClick={() => handleNullRateChange(preset.value)}
className={`px-2 py-1 text-xs rounded-md transition-colors ${ className={`px-3 py-1.5 text-xs rounded-md transition-colors ${
field.nullRate === preset.value field.nullRate === preset.value
? 'bg-primary text-primary-foreground' ? 'bg-primary text-primary-foreground'
: 'bg-muted text-muted-foreground hover:bg-muted/80' : 'bg-muted text-muted-foreground hover:bg-muted/80'
@@ -11,20 +11,15 @@ import type { FieldConfig } from '@/types/testDataGenerator';
interface FieldItemProps { interface FieldItemProps {
field: FieldConfig; field: FieldConfig;
onClick: () => void; onClick: () => void;
isSelected: boolean;
} }
export default function FieldItem({ field, onClick, isSelected }: FieldItemProps) { export default function FieldItem({ field, onClick }: FieldItemProps) {
const { t } = useI18n('testDataGenerator'); const { t } = useI18n('testDataGenerator');
const generator = getGeneratorById(field.generatorId); const generator = getGeneratorById(field.generatorId);
return ( return (
<div <div
className={`flex-1 min-w-0 cursor-pointer rounded-md transition-all ${ className="flex-1 min-w-0 cursor-pointer rounded-md transition-all hover:bg-muted/50"
isSelected
? 'bg-primary/10 pl-2 border-l-2 border-l-primary'
: 'hover:bg-muted/50 border-l-2 border-l-transparent'
}`}
onClick={onClick} onClick={onClick}
> >
<div <div
@@ -44,21 +44,18 @@ interface FieldListProps {
onUpdate: (index: number, field: FieldConfig) => void; onUpdate: (index: number, field: FieldConfig) => void;
onRemove: (index: number) => void; onRemove: (index: number) => void;
onAdd: () => void; onAdd: () => void;
onSelect: (index: number) => void; onEdit: (index: number) => void;
selectedIndex: number | null;
onReorder: (oldIndex: number, newIndex: number) => void; onReorder: (oldIndex: number, newIndex: number) => void;
} }
/** 可排序的字段项 */ /** 可排序的字段项 */
function SortableFieldItem({ function SortableFieldItem({
field, field,
isSelected, onEdit,
onSelect,
onRemove, onRemove,
}: { }: {
field: FieldConfig; field: FieldConfig;
isSelected: boolean; onEdit: () => void;
onSelect: () => void;
onRemove: () => void; onRemove: () => void;
}) { }) {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
@@ -78,10 +75,8 @@ function SortableFieldItem({
ref={setNodeRef} ref={setNodeRef}
style={style} style={style}
className={`group relative rounded-lg border transition-all ${ className={`group relative rounded-lg border transition-all ${
isDragging ? 'border-primary shadow-lg' : '' isDragging
} ${ ? 'border-primary shadow-lg'
isSelected
? 'border-primary bg-primary/8 shadow-sm ring-1 ring-primary/20'
: 'border-border hover:border-muted-foreground/30 hover:shadow-sm' : 'border-border hover:border-muted-foreground/30 hover:shadow-sm'
}`} }`}
> >
@@ -96,7 +91,7 @@ function SortableFieldItem({
</button> </button>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<FieldItem field={field} onClick={onSelect} isSelected={isSelected} /> <FieldItem field={field} onClick={onEdit} />
</div> </div>
<Button <Button
@@ -117,8 +112,7 @@ export default function FieldList({
onUpdate: _onUpdate, onUpdate: _onUpdate,
onRemove, onRemove,
onAdd, onAdd,
onSelect, onEdit,
selectedIndex,
onReorder, onReorder,
}: FieldListProps) { }: FieldListProps) {
const { t } = useI18n('testDataGenerator'); const { t } = useI18n('testDataGenerator');
@@ -135,14 +129,6 @@ export default function FieldList({
}), }),
); );
// 点击已选中的字段时取消选中
const handleToggleSelect = useCallback(
(index: number) => {
onSelect(selectedIndex === index ? -1 : index);
},
[selectedIndex, onSelect],
);
const handleDragStart = (event: DragStartEvent) => { const handleDragStart = (event: DragStartEvent) => {
setActiveId(String(event.active.id)); setActiveId(String(event.active.id));
}; };
@@ -230,7 +216,7 @@ export default function FieldList({
size="sm" size="sm"
onClick={onAdd} onClick={onAdd}
disabled={isMaxReached} disabled={isMaxReached}
className="h-8 gap-1.5" className="h-9 gap-1.5 px-3"
> >
<Plus className="h-4 w-4" /> <Plus className="h-4 w-4" />
{t('testDataGenerator_addField')} {t('testDataGenerator_addField')}
@@ -284,8 +270,7 @@ export default function FieldList({
> >
<SortableFieldItem <SortableFieldItem
field={field} field={field}
isSelected={selectedIndex === realIndex} onEdit={() => onEdit(realIndex)}
onSelect={() => handleToggleSelect(realIndex)}
onRemove={() => onRemove(realIndex)} onRemove={() => onRemove(realIndex)}
/> />
</div> </div>
@@ -300,8 +285,7 @@ export default function FieldList({
<SortableFieldItem <SortableFieldItem
key={field.id} key={field.id}
field={field} field={field}
isSelected={selectedIndex === index} onEdit={() => onEdit(index)}
onSelect={() => handleToggleSelect(index)}
onRemove={() => onRemove(index)} onRemove={() => onRemove(index)}
/> />
))} ))}
@@ -317,7 +301,7 @@ export default function FieldList({
<GripVertical className="h-4 w-4 text-muted-foreground" /> <GripVertical className="h-4 w-4 text-muted-foreground" />
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<FieldItem field={activeField} isSelected={false} onClick={() => {}} /> <FieldItem field={activeField} onClick={() => {}} />
</div> </div>
<div className="h-8 w-8 shrink-0" /> <div className="h-8 w-8 shrink-0" />
</div> </div>
+4 -8
View File
@@ -9,7 +9,7 @@ import { cn } from '@/lib/utils';
import { useGenerator } from './hooks/useGenerator'; import { useGenerator } from './hooks/useGenerator';
import { getGeneratorById } from '@/lib/generators'; import { getGeneratorById } from '@/lib/generators';
import type { FieldConfig, GenerateResult } from '@/types/testDataGenerator'; import type { FieldConfig, GenerateResult } from '@/types/testDataGenerator';
import { Dialog, DialogContent, DialogHeader, DialogDescription } from '@/components/ui/dialog'; import { Dialog, DialogContent } from '@/components/ui/dialog';
// 生成唯一 ID 的辅助函数 // 生成唯一 ID 的辅助函数
function generateId(): string { function generateId(): string {
@@ -227,8 +227,7 @@ export default function TestDataGeneratorPage() {
onUpdate={handleUpdateField} onUpdate={handleUpdateField}
onRemove={handleRemoveField} onRemove={handleRemoveField}
onAdd={handleAddField} onAdd={handleAddField}
onSelect={handleOpenEditor} onEdit={handleOpenEditor}
selectedIndex={selectedIndex}
onReorder={handleReorder} onReorder={handleReorder}
/> />
</div> </div>
@@ -303,11 +302,8 @@ export default function TestDataGeneratorPage() {
{/* 字段编辑弹窗 */} {/* 字段编辑弹窗 */}
<Dialog open={isEditorOpen} onOpenChange={setIsEditorOpen}> <Dialog open={isEditorOpen} onOpenChange={setIsEditorOpen}>
<DialogContent className="w-[calc(100vw-2rem)] max-w-[360px] max-h-[calc(100vh-4rem)] overflow-hidden p-0"> <DialogContent className="w-[calc(100vw-4rem)] max-w-[420px] max-h-[calc(100vh-4rem)] overflow-hidden p-0 pt-8">
<DialogHeader className="px-6 pt-6 pb-0"> <div className="overflow-y-auto max-h-[calc(100vh-6rem)] px-6 pb-6">
<DialogDescription>{t('testDataGenerator_selectFieldToEdit')}</DialogDescription>
</DialogHeader>
<div className="overflow-y-auto max-h-[calc(100vh-8rem)] px-6 pb-6">
{selectedField && selectedIndex !== null && ( {selectedField && selectedIndex !== null && (
<FieldEditor <FieldEditor
field={selectedField} field={selectedField}