20260812221200
This commit is contained in:
1 parent
87b2d3c482
commit
4bd730f649
47 files changed
+6286
-1881
No files matched your search
@@ -0,0 +1,151 @@
|
||||
import { useState, useCallback, useRef } from "react";
|
||||
import type { ModuleField, FieldGroup, EditMode, FieldDataSource } from "./types";
|
||||
import { defaultFields, defaultGroups, makeField } from "./data/moduleData";
|
||||
import ModuleSettings from "./components/ModuleSettings";
|
||||
import FieldConfigList from "./components/FieldConfigList";
|
||||
import DataSourceDrawer from "./components/DataSourceDrawer";
|
||||
|
||||
export default function App() {
|
||||
const [fields, setFields] = useState<ModuleField[]>(defaultFields);
|
||||
const [groups, setGroups] = useState<FieldGroup[]>(defaultGroups);
|
||||
const [editMode, setEditMode] = useState<EditMode>("form");
|
||||
const [activeId, setActiveId] = useState<number | null>(null);
|
||||
const [dsDrawerFieldId, setDsDrawerFieldId] = useState<number | null>(null);
|
||||
const nextGroupId = useRef(10);
|
||||
|
||||
const dsDrawerField = dsDrawerFieldId != null ? fields.find((f) => f.id === dsDrawerFieldId) ?? null : null;
|
||||
|
||||
// ======== Field mutations ========
|
||||
const updateField = useCallback((id: number, key: string, value: string | number) => {
|
||||
setFields((prev) => prev.map((f) => (f.id === id ? { ...f, [key]: value } : f)));
|
||||
}, []);
|
||||
|
||||
const toggleField = useCallback((id: number, key: string) => {
|
||||
setFields((prev) => prev.map((f) => (f.id === id ? { ...f, [key]: !(f as any)[key] } : f)));
|
||||
}, []);
|
||||
|
||||
const onTypeChange = useCallback((id: number, newType: string) => {
|
||||
setFields((prev) =>
|
||||
prev.map((f) => {
|
||||
if (f.id !== id) return f;
|
||||
let ds = f.ds;
|
||||
if (newType === "select" && !ds) {
|
||||
ds = { type: "view", view: "", vf: "b_id", lf: "b_name" };
|
||||
}
|
||||
if (newType !== "select") ds = null;
|
||||
return { ...f, type: newType, ds };
|
||||
})
|
||||
);
|
||||
}, []);
|
||||
|
||||
const addField = useCallback(() => {
|
||||
const firstGroup = groups[0]?.id ?? null;
|
||||
const order = (fields.length + 1) * 10;
|
||||
const f = makeField(firstGroup, "input", "新字段");
|
||||
f.order = order;
|
||||
setFields((prev) => [...prev, f]);
|
||||
setActiveId(f.id);
|
||||
}, [fields.length, groups]);
|
||||
|
||||
const deleteField = useCallback(
|
||||
(id: number) => {
|
||||
const f = fields.find((f) => f.id === id);
|
||||
if (!f || !window.confirm(`删除「${f.label}」(${f.code})?`)) return;
|
||||
setFields((prev) => prev.filter((f) => f.id !== id));
|
||||
if (activeId === id) setActiveId(null);
|
||||
},
|
||||
[fields, activeId]
|
||||
);
|
||||
|
||||
// ======== Drag reorder ========
|
||||
const reorderFields = useCallback((activeId: number, overId: number) => {
|
||||
setFields((prev) => {
|
||||
const src = prev.find((f) => f.id === activeId);
|
||||
const tgt = prev.find((f) => f.id === overId);
|
||||
if (!src || !tgt) return prev;
|
||||
const tmp = src.order;
|
||||
return prev.map((f) => {
|
||||
if (f.id === activeId) return { ...f, order: tgt.order };
|
||||
if (f.id === overId) return { ...f, order: tmp };
|
||||
return f;
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
// ======== Data source ========
|
||||
const updateDs = useCallback((ds: FieldDataSource | null) => {
|
||||
if (dsDrawerFieldId == null) return;
|
||||
setFields((prev) => prev.map((f) => (f.id === dsDrawerFieldId ? { ...f, ds } : f)));
|
||||
}, [dsDrawerFieldId]);
|
||||
|
||||
// ======== Group ========
|
||||
const addGroup = useCallback(() => {
|
||||
const name = window.prompt("分组名称:");
|
||||
if (!name) return;
|
||||
const id = `g${nextGroupId.current++}`;
|
||||
setGroups((prev) => [...prev, { id, name }]);
|
||||
}, []);
|
||||
|
||||
const renameGroup = useCallback((id: string) => {
|
||||
const g = groups.find((g) => g.id === id);
|
||||
if (!g) return;
|
||||
const name = window.prompt("新名称:", g.name);
|
||||
if (name) {
|
||||
setGroups((prev) => prev.map((g) => (g.id === id ? { ...g, name } : g)));
|
||||
}
|
||||
}, [groups]);
|
||||
|
||||
const activateField = useCallback(
|
||||
(id: number) => {
|
||||
setActiveId((prev) => (prev === id ? null : id));
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col">
|
||||
<div className="flex-1 flex flex-col max-w-[1280px] mx-auto w-full p-5 gap-4">
|
||||
{/* Module Settings */}
|
||||
<ModuleSettings editMode={editMode} onEditModeChange={setEditMode} />
|
||||
|
||||
{/* Grouped field table (single column) */}
|
||||
<div className="card flex flex-col min-h-0 flex-1">
|
||||
<div className="flex items-center justify-between px-4 py-2.5 border-b border-slate-100 shrink-0">
|
||||
<span className="text-xs font-semibold text-slate-500 uppercase tracking-wide">
|
||||
字段配置 <span className="text-slate-300 font-normal ml-1">{fields.length}</span>
|
||||
</span>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<button className="btn btn-primary text-[10px] py-1 px-2.5" onClick={addField}>
|
||||
+ 新增
|
||||
</button>
|
||||
<button className="btn btn-ghost text-[10px] py-1 px-2" onClick={addGroup}>
|
||||
+ 分组
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<FieldConfigList
|
||||
fields={fields}
|
||||
groups={groups}
|
||||
activeId={activeId}
|
||||
onActivate={activateField}
|
||||
onToggle={toggleField}
|
||||
onUpdate={updateField}
|
||||
onTypeChange={onTypeChange}
|
||||
onReorder={reorderFields}
|
||||
onGroupRename={renameGroup}
|
||||
onDelete={deleteField}
|
||||
onOpenDs={setDsDrawerFieldId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Data Source Drawer */}
|
||||
<DataSourceDrawer
|
||||
field={dsDrawerField}
|
||||
open={dsDrawerFieldId != null}
|
||||
onClose={() => setDsDrawerFieldId(null)}
|
||||
onUpdate={updateDs}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user