@supos-grid-table/core 0.1.0-alpha.1 → 0.1.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { n as copyTemplate, r as isComputed, t as TableError } from "./model-BrBCOyRY.mjs";
2
2
  import { a as summarize, i as queryRows, n as groupRows, t as displayValue } from "./query-DDwS9-yU.mjs";
3
- import { t as Grid } from "./grid-DqJTLeBs.mjs";
3
+ import { t as Grid } from "./grid-DeVIC26t.mjs";
4
+ import { At as DialogHeader, Bt as Textarea, C as ComboboxChip, Ct as AlertDialogTitle, D as ComboboxContent, Dt as DialogContent, Et as DialogClose, Ft as Checkbox, Ht as Button, I as ComboboxValue, It as NativeSelect, L as useComboboxAnchor, N as ComboboxList, Nt as DialogTitle, O as ComboboxEmpty, Ot as DialogDescription, Rt as NativeSelectOption, S as Combobox, T as ComboboxChipsInput, Tt as Dialog, Vt as Input, _ as Tabs, _t as AlertDialogDescription, b as TabsTrigger, c as Alert, g as CollapsibleTrigger, gt as AlertDialogContent, h as CollapsibleContent, ht as AlertDialogCancel, j as ComboboxItem, m as Collapsible, mt as AlertDialogAction, pt as AlertDialog, t as Card, v as TabsContent, vt as AlertDialogFooter, w as ComboboxChips, y as TabsList, yt as AlertDialogHeader, zt as Label } from "./ui-esOhDo-4.mjs";
4
5
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
7
  import { AlertTriangle, ArrowDownUp, Braces, CalendarDays, ChartNoAxesCombined, Check, ChevronDown, ClipboardList, Columns3, Database, Download, Filter, Plus, Redo2, Search, SlidersHorizontal, Table2, Trash2, Undo2, X } from "lucide-react";
@@ -153,43 +154,58 @@ function fromCell(field, value) {
153
154
  return raw;
154
155
  }
155
156
  function Modal({ title, close, children, error }) {
156
- const ref = useRef(null);
157
- useEffect(() => {
158
- ref.current?.showModal();
159
- return () => ref.current?.close();
160
- }, []);
161
- return /* @__PURE__ */ jsxs("dialog", {
162
- className: "sg-modal",
163
- ref,
164
- onCancel: (event) => {
165
- event.preventDefault();
166
- close();
157
+ return /* @__PURE__ */ jsx(Dialog, {
158
+ open: true,
159
+ disablePointerDismissal: true,
160
+ onOpenChange: (open, details) => {
161
+ const target = details.event.target;
162
+ if (!open && details.reason === "escape-key" && target instanceof Element && target.closest("[role=\"combobox\"]")) {
163
+ details.cancel();
164
+ return;
165
+ }
166
+ if (!open) close();
167
167
  },
168
- children: [
169
- /* @__PURE__ */ jsxs("header", { children: [/* @__PURE__ */ jsx("h2", { children: title }), /* @__PURE__ */ jsx("button", {
170
- type: "button",
171
- "aria-label": "关闭",
172
- onClick: close,
173
- children: /* @__PURE__ */ jsx(X, { size: 18 })
174
- })] }),
175
- error && /* @__PURE__ */ jsx("p", {
176
- className: "sg-error",
177
- role: "alert",
178
- children: error
179
- }),
180
- children
181
- ]
168
+ children: /* @__PURE__ */ jsxs(DialogContent, {
169
+ className: "sg-portal sg-modal",
170
+ showCloseButton: false,
171
+ children: [
172
+ /* @__PURE__ */ jsxs(DialogHeader, {
173
+ className: "sg-modal-header",
174
+ children: [
175
+ /* @__PURE__ */ jsx(DialogTitle, { children: title }),
176
+ /* @__PURE__ */ jsx(DialogDescription, {
177
+ className: "sr-only",
178
+ children: "填写并保存本次修改,按 Escape 或关闭按钮返回。"
179
+ }),
180
+ /* @__PURE__ */ jsx(DialogClose, {
181
+ render: /* @__PURE__ */ jsx(Button, {
182
+ variant: "ghost",
183
+ size: "icon-sm",
184
+ "aria-label": "关闭"
185
+ }),
186
+ children: /* @__PURE__ */ jsx(X, { size: 18 })
187
+ })
188
+ ]
189
+ }),
190
+ error && /* @__PURE__ */ jsx(Alert, {
191
+ className: "sg-error",
192
+ role: "alert",
193
+ children: error
194
+ }),
195
+ children
196
+ ]
197
+ })
182
198
  });
183
199
  }
184
200
  function FieldInput({ field, value, set, snapshot }) {
201
+ const anchor = useComboboxAnchor();
185
202
  if (isComputed(field) || field.readonly) return /* @__PURE__ */ jsx("output", { children: displayValue(value) || "保存后计算" });
186
- if (field.type === "boolean") return /* @__PURE__ */ jsx("input", {
203
+ if (field.type === "boolean") return /* @__PURE__ */ jsx(Checkbox, {
187
204
  "aria-label": field.name,
188
- type: "checkbox",
189
205
  checked: value === true,
190
- onChange: (e) => set(e.target.checked)
206
+ onCheckedChange: (checked) => set(checked === true)
191
207
  });
192
- if (field.type === "textarea") return /* @__PURE__ */ jsx("textarea", {
208
+ if (field.type === "textarea") return /* @__PURE__ */ jsx(Textarea, {
193
209
  "aria-label": field.name,
194
210
  rows: 3,
195
211
  value: String(value ?? ""),
@@ -205,22 +221,52 @@ function FieldInput({ field, value, set, snapshot }) {
205
221
  id: o,
206
222
  label: o
207
223
  }));
208
- return /* @__PURE__ */ jsxs("select", {
224
+ if (multiple) return /* @__PURE__ */ jsxs(Combobox, {
225
+ multiple: true,
226
+ items: options.map((option) => option.id),
227
+ value: Array.isArray(value) ? value.map(String) : [],
228
+ onValueChange: (next, details) => {
229
+ if (details.reason === "escape-key") {
230
+ details.cancel();
231
+ return;
232
+ }
233
+ set(next);
234
+ },
235
+ itemToStringLabel: (id) => options.find((option) => option.id === id)?.label ?? id,
236
+ children: [/* @__PURE__ */ jsxs(ComboboxChips, {
237
+ ref: anchor,
238
+ children: [/* @__PURE__ */ jsx(ComboboxValue, { children: (selected) => selected.map((id) => /* @__PURE__ */ jsx(ComboboxChip, {
239
+ "aria-label": `移除 ${options.find((option) => option.id === id)?.label ?? id}`,
240
+ children: options.find((option) => option.id === id)?.label ?? id
241
+ }, id)) }), /* @__PURE__ */ jsx(ComboboxChipsInput, {
242
+ "aria-label": field.name,
243
+ placeholder: "搜索并选择,可多选"
244
+ })]
245
+ }), /* @__PURE__ */ jsxs(ComboboxContent, {
246
+ className: "sg-portal",
247
+ anchor,
248
+ children: [/* @__PURE__ */ jsx(ComboboxEmpty, { children: "没有匹配的记录或选项" }), /* @__PURE__ */ jsx(ComboboxList, { children: (id) => /* @__PURE__ */ jsx(ComboboxItem, {
249
+ value: id,
250
+ children: options.find((option) => option.id === id)?.label ?? id
251
+ }, id) })]
252
+ })]
253
+ });
254
+ return /* @__PURE__ */ jsxs(NativeSelect, {
209
255
  "aria-label": field.name,
210
256
  multiple: !!multiple,
211
257
  required: field.required,
212
258
  value: multiple ? Array.isArray(value) ? value.map(String) : [] : Array.isArray(value) ? String(value[0] ?? "") : String(value ?? ""),
213
259
  onChange: (e) => set(multiple ? Array.from(e.target.selectedOptions, (o) => o.value) : field.type === "relation" ? e.target.value ? [e.target.value] : [] : e.target.value || null),
214
- children: [!multiple && /* @__PURE__ */ jsx("option", {
260
+ children: [!multiple && /* @__PURE__ */ jsx(NativeSelectOption, {
215
261
  value: "",
216
262
  children: "请选择"
217
- }), options.map((o) => /* @__PURE__ */ jsx("option", {
263
+ }), options.map((o) => /* @__PURE__ */ jsx(NativeSelectOption, {
218
264
  value: o.id,
219
265
  children: o.label
220
266
  }, o.id))]
221
267
  });
222
268
  }
223
- return /* @__PURE__ */ jsx("input", {
269
+ return /* @__PURE__ */ jsx(Input, {
224
270
  "aria-label": field.name,
225
271
  type: field.type === "number" ? "number" : field.type === "date" ? "date" : "text",
226
272
  step: field.type === "number" ? "any" : void 0,
@@ -261,12 +307,12 @@ function RecordForm({ table, snapshot, row, busy, onSave, onDelete, error }) {
261
307
  className: "sg-muted",
262
308
  children: row ? "修改将校验打开记录时的版本,其他人的更新会提示冲突。" : "按数据模型填写,保存成功后可在所有视图查看。"
263
309
  }),
264
- error && /* @__PURE__ */ jsxs("p", {
310
+ error && /* @__PURE__ */ jsxs(Alert, {
265
311
  role: "alert",
266
312
  className: "sg-error",
267
313
  children: [error, "。待提交内容已保留。"]
268
314
  }),
269
- table.fields.map((field) => /* @__PURE__ */ jsxs("label", { children: [
315
+ table.fields.map((field) => /* @__PURE__ */ jsxs(Label, { children: [
270
316
  /* @__PURE__ */ jsxs("span", { children: [
271
317
  field.name,
272
318
  field.required && /* @__PURE__ */ jsx("b", {
@@ -287,7 +333,9 @@ function RecordForm({ table, snapshot, row, busy, onSave, onDelete, error }) {
287
333
  },
288
334
  snapshot: draftSnapshot
289
335
  }),
290
- field.type === "relation" && !field.readonly && /* @__PURE__ */ jsxs("button", {
336
+ field.type === "relation" && !field.readonly && /* @__PURE__ */ jsxs(Button, {
337
+ variant: "ghost",
338
+ size: "sm",
291
339
  type: "button",
292
340
  disabled: !snapshot.capabilities.atomicBatch || busy,
293
341
  onClick: () => setRelationDraft(field),
@@ -295,7 +343,9 @@ function RecordForm({ table, snapshot, row, busy, onSave, onDelete, error }) {
295
343
  })
296
344
  ] }, field.id)),
297
345
  /* @__PURE__ */ jsxs("footer", { children: [
298
- onDelete && /* @__PURE__ */ jsxs("button", {
346
+ onDelete && /* @__PURE__ */ jsxs(Button, {
347
+ variant: "ghost",
348
+ size: "sm",
299
349
  type: "button",
300
350
  className: "sg-danger",
301
351
  disabled: busy,
@@ -307,7 +357,9 @@ function RecordForm({ table, snapshot, row, busy, onSave, onDelete, error }) {
307
357
  className: "sg-success",
308
358
  children: [/* @__PURE__ */ jsx(Check, { size: 15 }), "已保存"]
309
359
  }),
310
- /* @__PURE__ */ jsx("button", {
360
+ /* @__PURE__ */ jsx(Button, {
361
+ variant: "default",
362
+ size: "sm",
311
363
  className: "sg-primary",
312
364
  type: "submit",
313
365
  disabled: busy || !snapshot.capabilities.write,
@@ -346,15 +398,16 @@ function RecordForm({ table, snapshot, row, busy, onSave, onDelete, error }) {
346
398
  }
347
399
  function SchemaEditor({ snapshot, table, save, close, error }) {
348
400
  const [base, setBase] = useState(() => structuredClone(snapshot.base));
401
+ const [deleteField, setDeleteField] = useState();
349
402
  const draft = base.tables.find((t) => t.id === table.id);
350
403
  function update() {
351
404
  setBase(structuredClone(base));
352
405
  }
353
- return /* @__PURE__ */ jsx(Modal, {
406
+ return /* @__PURE__ */ jsxs(Modal, {
354
407
  title: "字段与 UNS 绑定",
355
408
  close,
356
409
  error,
357
- children: /* @__PURE__ */ jsxs("form", {
410
+ children: [/* @__PURE__ */ jsxs("form", {
358
411
  className: "sg-schema",
359
412
  onSubmit: async (e) => {
360
413
  e.preventDefault();
@@ -365,7 +418,7 @@ function SchemaEditor({ snapshot, table, save, close, error }) {
365
418
  className: "sg-muted",
366
419
  children: "字段引用使用稳定 ID。绑定仅配置映射,实际读写由已接入的 UNS 适配器执行。"
367
420
  }),
368
- /* @__PURE__ */ jsxs("label", { children: ["数据表名称", /* @__PURE__ */ jsx("input", {
421
+ /* @__PURE__ */ jsxs(Label, { children: ["数据表名称", /* @__PURE__ */ jsx(Input, {
369
422
  required: true,
370
423
  value: draft.name,
371
424
  onChange: (e) => {
@@ -378,18 +431,16 @@ function SchemaEditor({ snapshot, table, save, close, error }) {
378
431
  children: draft.fields.map((field) => /* @__PURE__ */ jsxs("fieldset", { children: [
379
432
  /* @__PURE__ */ jsxs("div", {
380
433
  className: "sg-field-heading",
381
- children: [/* @__PURE__ */ jsx("code", { children: field.id }), /* @__PURE__ */ jsx("button", {
434
+ children: [/* @__PURE__ */ jsx("code", { children: field.id }), /* @__PURE__ */ jsx(Button, {
435
+ variant: "ghost",
436
+ size: "sm",
382
437
  type: "button",
383
438
  title: `删除字段 ${field.name}`,
384
- onClick: () => {
385
- if (!confirm(`删除字段“${field.name}”的应用配置?关联公式和视图也需要调整。`)) return;
386
- draft.fields = draft.fields.filter((f) => f.id !== field.id);
387
- update();
388
- },
439
+ onClick: () => setDeleteField(field),
389
440
  children: /* @__PURE__ */ jsx(Trash2, { size: 14 })
390
441
  })]
391
442
  }),
392
- /* @__PURE__ */ jsxs("label", { children: ["名称", /* @__PURE__ */ jsx("input", {
443
+ /* @__PURE__ */ jsxs(Label, { children: ["名称", /* @__PURE__ */ jsx(Input, {
393
444
  required: true,
394
445
  value: field.name,
395
446
  onChange: (e) => {
@@ -397,7 +448,7 @@ function SchemaEditor({ snapshot, table, save, close, error }) {
397
448
  update();
398
449
  }
399
450
  })] }),
400
- /* @__PURE__ */ jsxs("label", { children: ["类型", /* @__PURE__ */ jsx("select", {
451
+ /* @__PURE__ */ jsxs(Label, { children: ["类型", /* @__PURE__ */ jsx(NativeSelect, {
401
452
  value: field.type,
402
453
  onChange: (e) => {
403
454
  field.type = e.target.value;
@@ -416,38 +467,36 @@ function SchemaEditor({ snapshot, table, save, close, error }) {
416
467
  "formula",
417
468
  "lookup",
418
469
  "rollup"
419
- ].map((kind) => /* @__PURE__ */ jsx("option", { children: kind }, kind))
470
+ ].map((kind) => /* @__PURE__ */ jsx(NativeSelectOption, { children: kind }, kind))
420
471
  })] }),
421
- /* @__PURE__ */ jsxs("label", {
472
+ /* @__PURE__ */ jsxs(Label, {
422
473
  className: "sg-check",
423
- children: [/* @__PURE__ */ jsx("input", {
424
- type: "checkbox",
474
+ children: [/* @__PURE__ */ jsx(Checkbox, {
425
475
  checked: !!field.required,
426
- onChange: (e) => {
427
- field.required = e.target.checked;
476
+ onCheckedChange: (checked) => {
477
+ field.required = checked === true;
428
478
  update();
429
479
  }
430
480
  }), "必填"]
431
481
  }),
432
- /* @__PURE__ */ jsxs("label", {
482
+ /* @__PURE__ */ jsxs(Label, {
433
483
  className: "sg-check",
434
- children: [/* @__PURE__ */ jsx("input", {
435
- type: "checkbox",
484
+ children: [/* @__PURE__ */ jsx(Checkbox, {
436
485
  checked: !!field.readonly,
437
- onChange: (e) => {
438
- field.readonly = e.target.checked;
486
+ onCheckedChange: (checked) => {
487
+ field.readonly = checked === true;
439
488
  update();
440
489
  }
441
490
  }), "只读"]
442
491
  }),
443
- (field.type === "select" || field.type === "multiSelect") && /* @__PURE__ */ jsxs("label", { children: ["选项(每行一个)", /* @__PURE__ */ jsx("textarea", {
492
+ (field.type === "select" || field.type === "multiSelect") && /* @__PURE__ */ jsxs(Label, { children: ["选项(每行一个)", /* @__PURE__ */ jsx(Textarea, {
444
493
  value: (field.options ?? []).join("\n"),
445
494
  onChange: (e) => {
446
495
  field.options = e.target.value.split("\n");
447
496
  update();
448
497
  }
449
498
  })] }),
450
- field.type === "formula" && /* @__PURE__ */ jsxs("label", { children: ["公式", /* @__PURE__ */ jsx("textarea", {
499
+ field.type === "formula" && /* @__PURE__ */ jsxs(Label, { children: ["公式", /* @__PURE__ */ jsx(Textarea, {
451
500
  placeholder: "IF({status} = '已过账', {quantity}, 0)",
452
501
  value: field.formula ?? "",
453
502
  onChange: (e) => {
@@ -455,7 +504,7 @@ function SchemaEditor({ snapshot, table, save, close, error }) {
455
504
  update();
456
505
  }
457
506
  })] }),
458
- field.type === "relation" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("label", { children: ["关联表", /* @__PURE__ */ jsxs("select", {
507
+ field.type === "relation" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Label, { children: ["关联表", /* @__PURE__ */ jsxs(NativeSelect, {
459
508
  value: field.relation?.tableId ?? "",
460
509
  onChange: (e) => {
461
510
  field.relation = {
@@ -464,29 +513,28 @@ function SchemaEditor({ snapshot, table, save, close, error }) {
464
513
  };
465
514
  update();
466
515
  },
467
- children: [/* @__PURE__ */ jsx("option", {
516
+ children: [/* @__PURE__ */ jsx(NativeSelectOption, {
468
517
  value: "",
469
518
  children: "请选择"
470
- }), base.tables.map((t) => /* @__PURE__ */ jsx("option", {
519
+ }), base.tables.map((t) => /* @__PURE__ */ jsx(NativeSelectOption, {
471
520
  value: t.id,
472
521
  children: t.name
473
522
  }, t.id))]
474
- })] }), /* @__PURE__ */ jsxs("label", {
523
+ })] }), /* @__PURE__ */ jsxs(Label, {
475
524
  className: "sg-check",
476
- children: [/* @__PURE__ */ jsx("input", {
477
- type: "checkbox",
525
+ children: [/* @__PURE__ */ jsx(Checkbox, {
478
526
  checked: field.relation?.multiple ?? true,
479
- onChange: (e) => {
527
+ onCheckedChange: (checked) => {
480
528
  field.relation = {
481
529
  tableId: field.relation?.tableId ?? "",
482
- multiple: e.target.checked
530
+ multiple: checked === true
483
531
  };
484
532
  update();
485
533
  }
486
534
  }), "允许关联多个记录"]
487
535
  })] }),
488
536
  (field.type === "lookup" || field.type === "rollup") && /* @__PURE__ */ jsxs(Fragment, { children: [
489
- /* @__PURE__ */ jsxs("label", { children: ["关联字段 ID", /* @__PURE__ */ jsx("input", {
537
+ /* @__PURE__ */ jsxs(Label, { children: ["关联字段 ID", /* @__PURE__ */ jsx(Input, {
490
538
  value: field.lookup?.relationFieldId ?? "",
491
539
  onChange: (e) => {
492
540
  field.lookup = {
@@ -497,7 +545,7 @@ function SchemaEditor({ snapshot, table, save, close, error }) {
497
545
  update();
498
546
  }
499
547
  })] }),
500
- /* @__PURE__ */ jsxs("label", { children: ["目标字段 ID", /* @__PURE__ */ jsx("input", {
548
+ /* @__PURE__ */ jsxs(Label, { children: ["目标字段 ID", /* @__PURE__ */ jsx(Input, {
501
549
  value: field.lookup?.targetFieldId ?? "",
502
550
  onChange: (e) => {
503
551
  field.lookup = {
@@ -508,7 +556,7 @@ function SchemaEditor({ snapshot, table, save, close, error }) {
508
556
  update();
509
557
  }
510
558
  })] }),
511
- field.type === "rollup" && /* @__PURE__ */ jsxs("label", { children: ["汇总", /* @__PURE__ */ jsx("select", {
559
+ field.type === "rollup" && /* @__PURE__ */ jsxs(Label, { children: ["汇总", /* @__PURE__ */ jsx(NativeSelect, {
512
560
  value: field.lookup?.aggregate ?? "sum",
513
561
  onChange: (e) => {
514
562
  field.lookup = {
@@ -523,12 +571,14 @@ function SchemaEditor({ snapshot, table, save, close, error }) {
523
571
  "min",
524
572
  "max",
525
573
  "average"
526
- ].map((s) => /* @__PURE__ */ jsx("option", { children: s }, s))
574
+ ].map((s) => /* @__PURE__ */ jsx(NativeSelectOption, { children: s }, s))
527
575
  })] })
528
576
  ] })
529
577
  ] }, field.id))
530
578
  }),
531
- /* @__PURE__ */ jsxs("button", {
579
+ /* @__PURE__ */ jsxs(Button, {
580
+ variant: "ghost",
581
+ size: "sm",
532
582
  type: "button",
533
583
  onClick: () => {
534
584
  draft.fields.push({
@@ -540,39 +590,63 @@ function SchemaEditor({ snapshot, table, save, close, error }) {
540
590
  },
541
591
  children: [/* @__PURE__ */ jsx(Plus, { size: 16 }), "新增字段"]
542
592
  }),
543
- /* @__PURE__ */ jsxs("details", { children: [
544
- /* @__PURE__ */ jsx("summary", { children: "UNS 数据绑定" }),
545
- /* @__PURE__ */ jsxs("label", { children: ["UNS alias", /* @__PURE__ */ jsx("input", {
546
- value: draft.binding?.alias ?? "",
547
- onChange: (e) => {
548
- draft.binding = {
549
- alias: e.target.value,
550
- fieldMap: draft.binding?.fieldMap ?? {}
551
- };
552
- update();
553
- }
554
- })] }),
555
- draft.fields.filter((f) => !isComputed(f)).map((f) => /* @__PURE__ */ jsxs("label", { children: [f.name, /* @__PURE__ */ jsx("input", {
556
- placeholder: "UNS 字段名",
557
- value: draft.binding?.fieldMap[f.id] ?? "",
558
- onChange: (e) => {
559
- draft.binding ??= {
560
- alias: "",
561
- fieldMap: {}
562
- };
563
- if (e.target.value) draft.binding.fieldMap[f.id] = e.target.value;
564
- else delete draft.binding.fieldMap[f.id];
565
- update();
566
- }
567
- })] }, f.id))
568
- ] }),
569
- /* @__PURE__ */ jsx("footer", { children: /* @__PURE__ */ jsx("button", {
593
+ /* @__PURE__ */ jsxs(Collapsible, { children: [/* @__PURE__ */ jsx(CollapsibleTrigger, {
594
+ render: /* @__PURE__ */ jsx(Button, {
595
+ variant: "ghost",
596
+ type: "button"
597
+ }),
598
+ children: "UNS 数据绑定"
599
+ }), /* @__PURE__ */ jsxs(CollapsibleContent, { children: [/* @__PURE__ */ jsxs(Label, { children: ["UNS alias", /* @__PURE__ */ jsx(Input, {
600
+ value: draft.binding?.alias ?? "",
601
+ onChange: (e) => {
602
+ draft.binding = {
603
+ alias: e.target.value,
604
+ fieldMap: draft.binding?.fieldMap ?? {}
605
+ };
606
+ update();
607
+ }
608
+ })] }), draft.fields.filter((f) => !isComputed(f)).map((f) => /* @__PURE__ */ jsxs(Label, { children: [f.name, /* @__PURE__ */ jsx(Input, {
609
+ placeholder: "UNS 字段名",
610
+ value: draft.binding?.fieldMap[f.id] ?? "",
611
+ onChange: (e) => {
612
+ draft.binding ??= {
613
+ alias: "",
614
+ fieldMap: {}
615
+ };
616
+ if (e.target.value) draft.binding.fieldMap[f.id] = e.target.value;
617
+ else delete draft.binding.fieldMap[f.id];
618
+ update();
619
+ }
620
+ })] }, f.id))] })] }),
621
+ /* @__PURE__ */ jsx("footer", { children: /* @__PURE__ */ jsx(Button, {
622
+ variant: "default",
623
+ size: "sm",
570
624
  type: "submit",
571
625
  className: "sg-primary",
572
626
  children: "保存配置"
573
627
  }) })
574
628
  ]
575
- })
629
+ }), /* @__PURE__ */ jsx(AlertDialog, {
630
+ open: !!deleteField,
631
+ onOpenChange: (open) => {
632
+ if (!open) setDeleteField(void 0);
633
+ },
634
+ children: /* @__PURE__ */ jsxs(AlertDialogContent, {
635
+ className: "sg-portal",
636
+ children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: "删除字段配置" }), /* @__PURE__ */ jsxs(AlertDialogDescription, { children: [
637
+ "确认删除“",
638
+ deleteField?.name,
639
+ "”的应用配置?关联公式和视图也需要调整。"
640
+ ] })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, { children: "取消" }), /* @__PURE__ */ jsx(AlertDialogAction, {
641
+ onClick: () => {
642
+ draft.fields = draft.fields.filter((field) => field.id !== deleteField?.id);
643
+ update();
644
+ setDeleteField(void 0);
645
+ },
646
+ children: "确认删除"
647
+ })] })]
648
+ })
649
+ })]
576
650
  });
577
651
  }
578
652
  function ViewEditor({ view, table, save, close, error }) {
@@ -588,7 +662,7 @@ function ViewEditor({ view, table, save, close, error }) {
588
662
  if (await save(draft)) close();
589
663
  },
590
664
  children: [
591
- /* @__PURE__ */ jsxs("label", { children: ["名称", /* @__PURE__ */ jsx("input", {
665
+ /* @__PURE__ */ jsxs(Label, { children: ["名称", /* @__PURE__ */ jsx(Input, {
592
666
  required: true,
593
667
  value: draft.name,
594
668
  onChange: (e) => setDraft({
@@ -596,46 +670,46 @@ function ViewEditor({ view, table, save, close, error }) {
596
670
  name: e.target.value
597
671
  })
598
672
  })] }),
599
- /* @__PURE__ */ jsxs("label", { children: ["视图类型", /* @__PURE__ */ jsx("select", {
673
+ /* @__PURE__ */ jsxs(Label, { children: ["视图类型", /* @__PURE__ */ jsx(NativeSelect, {
600
674
  value: draft.type,
601
675
  onChange: (e) => setDraft({
602
676
  ...draft,
603
677
  type: e.target.value
604
678
  }),
605
- children: Object.entries(viewNames).map(([id, name]) => /* @__PURE__ */ jsx("option", {
679
+ children: Object.entries(viewNames).map(([id, name]) => /* @__PURE__ */ jsx(NativeSelectOption, {
606
680
  value: id,
607
681
  children: name
608
682
  }, id))
609
683
  })] }),
610
- /* @__PURE__ */ jsxs("label", { children: ["分组字段", /* @__PURE__ */ jsxs("select", {
684
+ /* @__PURE__ */ jsxs(Label, { children: ["分组字段", /* @__PURE__ */ jsxs(NativeSelect, {
611
685
  value: draft.groupBy ?? "",
612
686
  onChange: (e) => setDraft({
613
687
  ...draft,
614
688
  groupBy: e.target.value || void 0
615
689
  }),
616
- children: [/* @__PURE__ */ jsx("option", {
690
+ children: [/* @__PURE__ */ jsx(NativeSelectOption, {
617
691
  value: "",
618
692
  children: "不分组"
619
- }), table.fields.map((f) => /* @__PURE__ */ jsx("option", {
693
+ }), table.fields.map((f) => /* @__PURE__ */ jsx(NativeSelectOption, {
620
694
  value: f.id,
621
695
  children: f.name
622
696
  }, f.id))]
623
697
  })] }),
624
- /* @__PURE__ */ jsxs("label", { children: ["日历日期字段", /* @__PURE__ */ jsxs("select", {
698
+ /* @__PURE__ */ jsxs(Label, { children: ["日历日期字段", /* @__PURE__ */ jsxs(NativeSelect, {
625
699
  value: draft.dateFieldId ?? "",
626
700
  onChange: (e) => setDraft({
627
701
  ...draft,
628
702
  dateFieldId: e.target.value || void 0
629
703
  }),
630
- children: [/* @__PURE__ */ jsx("option", {
704
+ children: [/* @__PURE__ */ jsx(NativeSelectOption, {
631
705
  value: "",
632
706
  children: "请选择"
633
- }), table.fields.filter((f) => f.type === "date").map((f) => /* @__PURE__ */ jsx("option", {
707
+ }), table.fields.filter((f) => f.type === "date").map((f) => /* @__PURE__ */ jsx(NativeSelectOption, {
634
708
  value: f.id,
635
709
  children: f.name
636
710
  }, f.id))]
637
711
  })] }),
638
- /* @__PURE__ */ jsxs("label", { children: ["冻结列数", /* @__PURE__ */ jsx("input", {
712
+ /* @__PURE__ */ jsxs(Label, { children: ["冻结列数", /* @__PURE__ */ jsx(Input, {
639
713
  type: "number",
640
714
  min: 0,
641
715
  max: table.fields.length,
@@ -645,14 +719,13 @@ function ViewEditor({ view, table, save, close, error }) {
645
719
  freezeColumns: Number(e.target.value)
646
720
  })
647
721
  })] }),
648
- /* @__PURE__ */ jsxs("fieldset", { children: [/* @__PURE__ */ jsx("legend", { children: "显示字段" }), table.fields.map((f) => /* @__PURE__ */ jsxs("label", {
722
+ /* @__PURE__ */ jsxs("fieldset", { children: [/* @__PURE__ */ jsx("legend", { children: "显示字段" }), table.fields.map((f) => /* @__PURE__ */ jsxs(Label, {
649
723
  className: "sg-check",
650
- children: [/* @__PURE__ */ jsx("input", {
651
- type: "checkbox",
724
+ children: [/* @__PURE__ */ jsx(Checkbox, {
652
725
  checked: !draft.hiddenFields?.includes(f.id),
653
- onChange: (e) => setDraft({
726
+ onCheckedChange: (checked) => setDraft({
654
727
  ...draft,
655
- hiddenFields: e.target.checked ? (draft.hiddenFields ?? []).filter((id) => id !== f.id) : [...draft.hiddenFields ?? [], f.id]
728
+ hiddenFields: checked === true ? (draft.hiddenFields ?? []).filter((id) => id !== f.id) : [...draft.hiddenFields ?? [], f.id]
656
729
  })
657
730
  }), f.name]
658
731
  }, f.id))] }),
@@ -661,19 +734,19 @@ function ViewEditor({ view, table, save, close, error }) {
661
734
  (draft.filters ?? []).map((filter, index) => /* @__PURE__ */ jsxs("div", {
662
735
  className: "sg-filter-line",
663
736
  children: [
664
- /* @__PURE__ */ jsx("select", {
737
+ /* @__PURE__ */ jsx(NativeSelect, {
665
738
  "aria-label": "筛选字段",
666
739
  value: filter.fieldId,
667
740
  onChange: (e) => {
668
741
  draft.filters[index].fieldId = e.target.value;
669
742
  setDraft(structuredClone(draft));
670
743
  },
671
- children: table.fields.map((f) => /* @__PURE__ */ jsx("option", {
744
+ children: table.fields.map((f) => /* @__PURE__ */ jsx(NativeSelectOption, {
672
745
  value: f.id,
673
746
  children: f.name
674
747
  }, f.id))
675
748
  }),
676
- /* @__PURE__ */ jsx("select", {
749
+ /* @__PURE__ */ jsx(NativeSelect, {
677
750
  "aria-label": "筛选方式",
678
751
  value: filter.op,
679
752
  onChange: (e) => {
@@ -689,9 +762,9 @@ function ViewEditor({ view, table, save, close, error }) {
689
762
  "lt",
690
763
  "lte",
691
764
  "empty"
692
- ].map((op) => /* @__PURE__ */ jsx("option", { children: op }, op))
765
+ ].map((op) => /* @__PURE__ */ jsx(NativeSelectOption, { children: op }, op))
693
766
  }),
694
- /* @__PURE__ */ jsx("input", {
767
+ /* @__PURE__ */ jsx(Input, {
695
768
  "aria-label": "筛选值",
696
769
  value: displayValue(filter.value),
697
770
  onChange: (e) => {
@@ -700,7 +773,9 @@ function ViewEditor({ view, table, save, close, error }) {
700
773
  setDraft(structuredClone(draft));
701
774
  }
702
775
  }),
703
- /* @__PURE__ */ jsx("button", {
776
+ /* @__PURE__ */ jsx(Button, {
777
+ variant: "ghost",
778
+ size: "sm",
704
779
  type: "button",
705
780
  "aria-label": "移除筛选",
706
781
  onClick: () => setDraft({
@@ -711,7 +786,9 @@ function ViewEditor({ view, table, save, close, error }) {
711
786
  })
712
787
  ]
713
788
  }, index)),
714
- /* @__PURE__ */ jsx("button", {
789
+ /* @__PURE__ */ jsx(Button, {
790
+ variant: "ghost",
791
+ size: "sm",
715
792
  type: "button",
716
793
  onClick: () => setDraft({
717
794
  ...draft,
@@ -724,7 +801,7 @@ function ViewEditor({ view, table, save, close, error }) {
724
801
  children: "添加条件"
725
802
  })
726
803
  ] }),
727
- /* @__PURE__ */ jsxs("label", { children: ["排序字段", /* @__PURE__ */ jsxs("select", {
804
+ /* @__PURE__ */ jsxs(Label, { children: ["排序字段", /* @__PURE__ */ jsxs(NativeSelect, {
728
805
  value: draft.sorts?.[0]?.fieldId ?? "",
729
806
  onChange: (e) => setDraft({
730
807
  ...draft,
@@ -733,15 +810,15 @@ function ViewEditor({ view, table, save, close, error }) {
733
810
  direction: draft.sorts?.[0]?.direction ?? "asc"
734
811
  }] : []
735
812
  }),
736
- children: [/* @__PURE__ */ jsx("option", {
813
+ children: [/* @__PURE__ */ jsx(NativeSelectOption, {
737
814
  value: "",
738
815
  children: "默认顺序"
739
- }), table.fields.map((f) => /* @__PURE__ */ jsx("option", {
816
+ }), table.fields.map((f) => /* @__PURE__ */ jsx(NativeSelectOption, {
740
817
  value: f.id,
741
818
  children: f.name
742
819
  }, f.id))]
743
820
  })] }),
744
- /* @__PURE__ */ jsxs("label", { children: ["方向", /* @__PURE__ */ jsxs("select", {
821
+ /* @__PURE__ */ jsxs(Label, { children: ["方向", /* @__PURE__ */ jsxs(NativeSelect, {
745
822
  value: draft.sorts?.[0]?.direction ?? "asc",
746
823
  onChange: (e) => setDraft({
747
824
  ...draft,
@@ -750,15 +827,17 @@ function ViewEditor({ view, table, save, close, error }) {
750
827
  direction: e.target.value
751
828
  }))
752
829
  }),
753
- children: [/* @__PURE__ */ jsx("option", {
830
+ children: [/* @__PURE__ */ jsx(NativeSelectOption, {
754
831
  value: "asc",
755
832
  children: "升序"
756
- }), /* @__PURE__ */ jsx("option", {
833
+ }), /* @__PURE__ */ jsx(NativeSelectOption, {
757
834
  value: "desc",
758
835
  children: "降序"
759
836
  })]
760
837
  })] }),
761
- /* @__PURE__ */ jsx("footer", { children: /* @__PURE__ */ jsx("button", {
838
+ /* @__PURE__ */ jsx("footer", { children: /* @__PURE__ */ jsx(Button, {
839
+ variant: "default",
840
+ size: "sm",
762
841
  className: "sg-primary",
763
842
  type: "submit",
764
843
  children: "保存视图"
@@ -779,7 +858,7 @@ function CalendarView({ rows, view, table, open }) {
779
858
  const padding = new Date(Date.UTC(year, mm - 1, 1)).getUTCDay();
780
859
  return /* @__PURE__ */ jsxs("section", {
781
860
  className: "sg-calendar",
782
- children: [/* @__PURE__ */ jsxs("label", { children: ["月份", /* @__PURE__ */ jsx("input", {
861
+ children: [/* @__PURE__ */ jsxs(Label, { children: ["月份", /* @__PURE__ */ jsx(Input, {
783
862
  "aria-label": "日历月份",
784
863
  type: "month",
785
864
  value: month,
@@ -803,7 +882,10 @@ function CalendarView({ rows, view, table, open }) {
803
882
  const date = `${month}-${String(i + 1).padStart(2, "0")}`;
804
883
  return /* @__PURE__ */ jsxs("div", {
805
884
  className: "sg-calendar-day",
806
- children: [/* @__PURE__ */ jsx("span", { children: i + 1 }), rows.filter((r) => r.values[dateField] === date).map((r) => /* @__PURE__ */ jsx("button", {
885
+ children: [/* @__PURE__ */ jsx("span", { children: i + 1 }), rows.filter((r) => r.values[dateField] === date).map((r) => /* @__PURE__ */ jsx(Button, {
886
+ variant: "ghost",
887
+ size: "sm",
888
+ type: "button",
807
889
  onClick: () => open(r),
808
890
  children: displayValue(r.values[table.fields[0].id]) || r.id
809
891
  }, r.id))]
@@ -821,6 +903,8 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
821
903
  const [busy, setBusy] = useState(false), [connected, setConnected] = useState(false);
822
904
  const [online, setOnline] = useState([]);
823
905
  const [record, setRecord] = useState();
906
+ const [newTable, setNewTable] = useState(false), [newTableName, setNewTableName] = useState("");
907
+ const [confirmation, setConfirmation] = useState();
824
908
  const [schemaOpen, setSchemaOpen] = useState(false), [viewDraft, setViewDraft] = useState();
825
909
  const [pending, setPending] = useState();
826
910
  const [undo, setUndo] = useState([]), [redo, setRedo] = useState([]);
@@ -924,7 +1008,10 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
924
1008
  children: [
925
1009
  /* @__PURE__ */ jsx(Database, { size: 32 }),
926
1010
  /* @__PURE__ */ jsx("p", { children: error || "正在加载多维表格…" }),
927
- error && /* @__PURE__ */ jsx("button", {
1011
+ error && /* @__PURE__ */ jsx(Button, {
1012
+ variant: "ghost",
1013
+ size: "sm",
1014
+ type: "button",
928
1015
  onClick: () => void refresh(),
929
1016
  children: "重新加载"
930
1017
  })
@@ -1003,7 +1090,10 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1003
1090
  /* @__PURE__ */ jsx("i", {}),
1004
1091
  name,
1005
1092
  /* @__PURE__ */ jsx("small", { children: records.length })
1006
- ] }), records.map((row) => /* @__PURE__ */ jsxs("button", {
1093
+ ] }), records.map((row) => /* @__PURE__ */ jsxs(Button, {
1094
+ variant: "ghost",
1095
+ size: "sm",
1096
+ type: "button",
1007
1097
  className: "sg-card",
1008
1098
  draggable: !readonly && groupingField?.type === "select",
1009
1099
  onDragStart: (e) => e.dataTransfer.setData("text/plain", row.id),
@@ -1152,6 +1242,22 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1152
1242
  })]
1153
1243
  }, groupName ?? "all");
1154
1244
  }
1245
+ const confirmationDialog = /* @__PURE__ */ jsx(AlertDialog, {
1246
+ open: !!confirmation,
1247
+ onOpenChange: (open) => {
1248
+ if (!open) setConfirmation(void 0);
1249
+ },
1250
+ children: /* @__PURE__ */ jsxs(AlertDialogContent, {
1251
+ className: "sg-portal",
1252
+ children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: confirmation?.title }), /* @__PURE__ */ jsx(AlertDialogDescription, { children: confirmation?.description })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, { children: "取消" }), /* @__PURE__ */ jsx(AlertDialogAction, {
1253
+ onClick: () => {
1254
+ confirmation?.action();
1255
+ setConfirmation(void 0);
1256
+ },
1257
+ children: "确认"
1258
+ })] })]
1259
+ })
1260
+ });
1155
1261
  return /* @__PURE__ */ jsxs("main", {
1156
1262
  className: "sg-app",
1157
1263
  children: [
@@ -1171,7 +1277,10 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1171
1277
  }),
1172
1278
  /* @__PURE__ */ jsx("nav", {
1173
1279
  "aria-label": "数据表",
1174
- children: snapshot.base.tables.map((t) => /* @__PURE__ */ jsxs("button", {
1280
+ children: snapshot.base.tables.map((t) => /* @__PURE__ */ jsxs(Button, {
1281
+ variant: "ghost",
1282
+ size: "sm",
1283
+ type: "button",
1175
1284
  className: t.id === table.id ? "active" : "",
1176
1285
  onClick: () => {
1177
1286
  setTableId(t.id);
@@ -1186,28 +1295,14 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1186
1295
  ]
1187
1296
  }, t.id))
1188
1297
  }),
1189
- /* @__PURE__ */ jsxs("button", {
1298
+ /* @__PURE__ */ jsxs(Button, {
1299
+ variant: "ghost",
1300
+ size: "sm",
1301
+ type: "button",
1190
1302
  className: "sg-add-table",
1191
- onClick: async () => {
1192
- const name = prompt("新数据表名称");
1193
- if (!name?.trim()) return;
1194
- const id = `t_${crypto.randomUUID().slice(0, 8)}`, base = structuredClone(snapshot.base);
1195
- base.tables.push({
1196
- id,
1197
- name,
1198
- fields: [{
1199
- id: "name",
1200
- name: "名称",
1201
- type: "text",
1202
- required: true
1203
- }],
1204
- views: [{
1205
- id: "grid",
1206
- name: "全部记录",
1207
- type: "grid"
1208
- }]
1209
- });
1210
- if (await configure(base)) setTableId(id);
1303
+ onClick: () => {
1304
+ setNewTableName("");
1305
+ setNewTable(true);
1211
1306
  },
1212
1307
  children: [/* @__PURE__ */ jsx(Plus, { size: 15 }), "添加数据表"]
1213
1308
  }),
@@ -1216,7 +1311,10 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1216
1311
  children: [
1217
1312
  /* @__PURE__ */ jsx("span", { className: `sg-connection ${connected ? "connected" : ""}` }),
1218
1313
  connected ? "协同已连接" : "正在重连,输入会保留",
1219
- /* @__PURE__ */ jsx("button", {
1314
+ /* @__PURE__ */ jsx(Button, {
1315
+ variant: "ghost",
1316
+ size: "sm",
1317
+ type: "button",
1220
1318
  title: "导出不含数据和绑定的应用模板",
1221
1319
  onClick: () => download(`${snapshot.base.name}.template.json`, JSON.stringify(copyTemplate(snapshot.base), null, 2), "application/json"),
1222
1320
  children: /* @__PURE__ */ jsx(Download, { size: 15 })
@@ -1225,8 +1323,11 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1225
1323
  })
1226
1324
  ]
1227
1325
  }),
1228
- /* @__PURE__ */ jsxs("section", {
1326
+ /* @__PURE__ */ jsxs(Tabs, {
1327
+ value: view.id,
1328
+ onValueChange: (value) => setViewId(String(value)),
1229
1329
  className: "sg-workspace",
1330
+ render: /* @__PURE__ */ jsx("section", {}),
1230
1331
  children: [
1231
1332
  /* @__PURE__ */ jsxs("header", {
1232
1333
  className: "sg-topbar",
@@ -1242,7 +1343,10 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1242
1343
  title: `${p.name}${p.fieldId ? ` · ${p.fieldId}` : ""}`,
1243
1344
  children: p.name.slice(0, 1)
1244
1345
  }, `${p.userId}/${p.clientId}`))
1245
- }), /* @__PURE__ */ jsxs("button", {
1346
+ }), /* @__PURE__ */ jsxs(Button, {
1347
+ variant: "ghost",
1348
+ size: "sm",
1349
+ type: "button",
1246
1350
  onClick: () => setSchemaOpen(true),
1247
1351
  children: [/* @__PURE__ */ jsx(SlidersHorizontal, { size: 16 }), "字段与绑定"]
1248
1352
  })]
@@ -1251,14 +1355,21 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1251
1355
  /* @__PURE__ */ jsxs("nav", {
1252
1356
  className: "sg-view-tabs",
1253
1357
  "aria-label": "视图",
1254
- children: [table.views.map((v) => {
1255
- const Icon = viewIcons[v.type];
1256
- return /* @__PURE__ */ jsxs("button", {
1257
- className: v.id === view.id ? "active" : "",
1258
- onClick: () => setViewId(v.id),
1259
- children: [/* @__PURE__ */ jsx(Icon, { size: 15 }), v.name]
1260
- }, v.id);
1261
- }), /* @__PURE__ */ jsx("button", {
1358
+ children: [/* @__PURE__ */ jsx(TabsList, {
1359
+ variant: "line",
1360
+ "aria-label": "视图",
1361
+ children: table.views.map((v) => {
1362
+ const Icon = viewIcons[v.type];
1363
+ return /* @__PURE__ */ jsxs(TabsTrigger, {
1364
+ value: v.id,
1365
+ className: v.id === view.id ? "active" : "",
1366
+ children: [/* @__PURE__ */ jsx(Icon, { size: 15 }), v.name]
1367
+ }, v.id);
1368
+ })
1369
+ }), /* @__PURE__ */ jsx(Button, {
1370
+ variant: "ghost",
1371
+ size: "sm",
1372
+ type: "button",
1262
1373
  "aria-label": "新增视图",
1263
1374
  onClick: () => setViewDraft({
1264
1375
  id: `v_${crypto.randomUUID().slice(0, 8)}`,
@@ -1271,13 +1382,19 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1271
1382
  /* @__PURE__ */ jsxs("div", {
1272
1383
  className: "sg-toolbar",
1273
1384
  children: [/* @__PURE__ */ jsxs("div", { children: [
1274
- /* @__PURE__ */ jsxs("button", {
1385
+ /* @__PURE__ */ jsxs(Button, {
1386
+ variant: "default",
1387
+ size: "sm",
1388
+ type: "button",
1275
1389
  className: "sg-primary",
1276
1390
  disabled: readonly || busy,
1277
1391
  onClick: () => setRecord("new"),
1278
1392
  children: [/* @__PURE__ */ jsx(Plus, { size: 16 }), "新增记录"]
1279
1393
  }),
1280
- /* @__PURE__ */ jsxs("button", {
1394
+ /* @__PURE__ */ jsxs(Button, {
1395
+ variant: "ghost",
1396
+ size: "sm",
1397
+ type: "button",
1281
1398
  onClick: () => setViewDraft(view),
1282
1399
  children: [
1283
1400
  /* @__PURE__ */ jsx(Filter, { size: 15 }),
@@ -1285,35 +1402,50 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1285
1402
  view.filters?.length ? ` ${view.filters.length}` : ""
1286
1403
  ]
1287
1404
  }),
1288
- /* @__PURE__ */ jsxs("button", {
1405
+ /* @__PURE__ */ jsxs(Button, {
1406
+ variant: "ghost",
1407
+ size: "sm",
1408
+ type: "button",
1289
1409
  onClick: () => setViewDraft(view),
1290
1410
  children: [/* @__PURE__ */ jsx(ArrowDownUp, { size: 15 }), "排序"]
1291
1411
  }),
1292
- /* @__PURE__ */ jsxs("button", {
1412
+ /* @__PURE__ */ jsxs(Button, {
1413
+ variant: "ghost",
1414
+ size: "sm",
1415
+ type: "button",
1293
1416
  onClick: () => setViewDraft(view),
1294
1417
  children: [/* @__PURE__ */ jsx(SlidersHorizontal, { size: 15 }), "视图设置"]
1295
1418
  })
1296
1419
  ] }), /* @__PURE__ */ jsxs("div", { children: [
1297
- /* @__PURE__ */ jsx("button", {
1420
+ /* @__PURE__ */ jsx(Button, {
1421
+ variant: "ghost",
1422
+ size: "sm",
1423
+ type: "button",
1298
1424
  "aria-label": "撤销",
1299
1425
  disabled: !undo.length || busy,
1300
1426
  onClick: () => void commit(undo[undo.length - 1], "undo"),
1301
1427
  children: /* @__PURE__ */ jsx(Undo2, { size: 16 })
1302
1428
  }),
1303
- /* @__PURE__ */ jsx("button", {
1429
+ /* @__PURE__ */ jsx(Button, {
1430
+ variant: "ghost",
1431
+ size: "sm",
1432
+ type: "button",
1304
1433
  "aria-label": "重做",
1305
1434
  disabled: !redo.length || busy,
1306
1435
  onClick: () => void commit(redo[redo.length - 1], "redo"),
1307
1436
  children: /* @__PURE__ */ jsx(Redo2, { size: 16 })
1308
1437
  }),
1309
- /* @__PURE__ */ jsx("button", {
1438
+ /* @__PURE__ */ jsx(Button, {
1439
+ variant: "ghost",
1440
+ size: "sm",
1441
+ type: "button",
1310
1442
  "aria-label": "导出 CSV",
1311
1443
  onClick: exportCsv,
1312
1444
  children: /* @__PURE__ */ jsx(Download, { size: 16 })
1313
1445
  }),
1314
- /* @__PURE__ */ jsxs("label", {
1446
+ /* @__PURE__ */ jsxs(Label, {
1315
1447
  className: "sg-search",
1316
- children: [/* @__PURE__ */ jsx(Search, { size: 15 }), /* @__PURE__ */ jsx("input", {
1448
+ children: [/* @__PURE__ */ jsx(Search, { size: 15 }), /* @__PURE__ */ jsx(Input, {
1317
1449
  placeholder: "搜索当前表",
1318
1450
  value: search,
1319
1451
  onChange: (e) => setSearch(e.target.value)
@@ -1321,74 +1453,105 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1321
1453
  })
1322
1454
  ] })]
1323
1455
  }),
1324
- snapshot.dataError && /* @__PURE__ */ jsxs("div", {
1456
+ snapshot.dataError && /* @__PURE__ */ jsxs(Alert, {
1325
1457
  className: "sg-error",
1326
1458
  role: "alert",
1327
1459
  children: [
1328
1460
  /* @__PURE__ */ jsx(AlertTriangle, { size: 16 }),
1329
1461
  /* @__PURE__ */ jsx("span", { children: snapshot.dataError }),
1330
- /* @__PURE__ */ jsx("button", {
1462
+ /* @__PURE__ */ jsx(Button, {
1463
+ variant: "ghost",
1464
+ size: "sm",
1465
+ type: "button",
1331
1466
  onClick: () => setSchemaOpen(true),
1332
1467
  children: "检查字段与绑定"
1333
1468
  }),
1334
- /* @__PURE__ */ jsx("button", {
1469
+ /* @__PURE__ */ jsx(Button, {
1470
+ variant: "ghost",
1471
+ size: "sm",
1472
+ type: "button",
1335
1473
  onClick: () => void refresh(),
1336
1474
  children: "重新连接"
1337
1475
  })
1338
1476
  ]
1339
1477
  }),
1340
- error && /* @__PURE__ */ jsxs("div", {
1478
+ error && /* @__PURE__ */ jsxs(Alert, {
1341
1479
  className: "sg-error",
1342
1480
  role: "alert",
1343
1481
  children: [
1344
1482
  /* @__PURE__ */ jsx(AlertTriangle, { size: 16 }),
1345
1483
  /* @__PURE__ */ jsxs("span", { children: [error, pending && "。待提交内容已保留。"] }),
1346
- pending && /* @__PURE__ */ jsxs("details", { children: [/* @__PURE__ */ jsx("summary", { children: "查看保留的修改" }), pending.map((mutation) => /* @__PURE__ */ jsxs("p", { children: [
1484
+ pending && /* @__PURE__ */ jsxs(Collapsible, { children: [/* @__PURE__ */ jsx(CollapsibleTrigger, {
1485
+ render: /* @__PURE__ */ jsx(Button, {
1486
+ variant: "ghost",
1487
+ type: "button"
1488
+ }),
1489
+ children: "查看保留的修改"
1490
+ }), /* @__PURE__ */ jsx(CollapsibleContent, { children: pending.map((mutation) => /* @__PURE__ */ jsxs("p", { children: [
1347
1491
  snapshot.base.tables.find((t) => t.id === mutation.tableId)?.name,
1348
- " · ",
1492
+ " ·",
1493
+ " ",
1349
1494
  mutation.type === "delete" ? "删除记录" : Object.entries(mutation.values).map(([id, value]) => `${snapshot.base.tables.find((t) => t.id === mutation.tableId)?.fields.find((f) => f.id === id)?.name ?? id}:${displayValue(value)}`).join(";")
1350
- ] }, `${mutation.tableId}/${mutation.id}`))] }),
1351
- /* @__PURE__ */ jsx("button", {
1495
+ ] }, `${mutation.tableId}/${mutation.id}`)) })] }),
1496
+ /* @__PURE__ */ jsx(Button, {
1497
+ variant: "ghost",
1498
+ size: "sm",
1499
+ type: "button",
1352
1500
  onClick: () => void refresh(),
1353
1501
  children: "刷新数据"
1354
1502
  }),
1355
- pending && /* @__PURE__ */ jsx("button", {
1503
+ pending && /* @__PURE__ */ jsx(Button, {
1504
+ variant: "ghost",
1505
+ size: "sm",
1506
+ type: "button",
1356
1507
  onClick: () => {
1357
- const latest = snapshotRef.current;
1358
- if (!confirm("请确认已核对其他人的修改。将把保留的字段值提交到当前最新版本。")) return;
1359
- commit(pending.map((m) => m.type === "create" ? m : {
1360
- ...m,
1361
- expectedVersion: latest.records[m.tableId]?.find((r) => r.id === m.id)?.version ?? m.expectedVersion
1362
- }));
1508
+ setConfirmation({
1509
+ title: "核对后重新提交",
1510
+ description: "请确认已核对其他人的修改。将把保留的字段值提交到当前最新版本。",
1511
+ action: () => {
1512
+ const latest = snapshotRef.current;
1513
+ commit(pending.map((m) => m.type === "create" ? m : {
1514
+ ...m,
1515
+ expectedVersion: latest.records[m.tableId]?.find((r) => r.id === m.id)?.version ?? m.expectedVersion
1516
+ }));
1517
+ }
1518
+ });
1363
1519
  },
1364
1520
  children: "核对后重新提交"
1365
1521
  }),
1366
- /* @__PURE__ */ jsx("button", {
1522
+ /* @__PURE__ */ jsx(Button, {
1523
+ variant: "ghost",
1524
+ size: "sm",
1525
+ type: "button",
1367
1526
  "aria-label": "关闭错误提示",
1368
1527
  onClick: () => setError(""),
1369
1528
  children: /* @__PURE__ */ jsx(X, { size: 15 })
1370
1529
  })
1371
1530
  ]
1372
1531
  }),
1373
- Object.keys(snapshot.errors).length > 0 && /* @__PURE__ */ jsxs("div", {
1532
+ Object.keys(snapshot.errors).length > 0 && /* @__PURE__ */ jsxs(Alert, {
1374
1533
  className: "sg-error",
1375
1534
  children: [
1376
1535
  /* @__PURE__ */ jsx(Braces, { size: 16 }),
1377
1536
  "有 ",
1378
1537
  Object.keys(snapshot.errors).length,
1379
1538
  " 个计算字段错误,请检查公式与引用。",
1380
- /* @__PURE__ */ jsx("button", {
1539
+ /* @__PURE__ */ jsx(Button, {
1540
+ variant: "ghost",
1541
+ size: "sm",
1542
+ type: "button",
1381
1543
  onClick: () => setSchemaOpen(true),
1382
1544
  children: "检查字段"
1383
1545
  })
1384
1546
  ]
1385
1547
  }),
1386
- /* @__PURE__ */ jsxs("div", {
1548
+ /* @__PURE__ */ jsxs(TabsContent, {
1549
+ value: view.id,
1387
1550
  className: `sg-content sg-content-${view.type}`,
1388
1551
  children: [
1389
1552
  view.type === "grid" && (view.groupBy ? [...groups].map(([name, records]) => grid(records, name)) : grid(rows)),
1390
1553
  view.type === "kanban" && board(),
1391
- view.type === "form" && /* @__PURE__ */ jsxs("div", {
1554
+ view.type === "form" && /* @__PURE__ */ jsxs(Card, {
1392
1555
  className: "sg-form-page",
1393
1556
  children: [
1394
1557
  /* @__PURE__ */ jsx("span", {
@@ -1418,7 +1581,7 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1418
1581
  }),
1419
1582
  view.type === "dashboard" && /* @__PURE__ */ jsxs("div", {
1420
1583
  className: "sg-dashboard",
1421
- children: [/* @__PURE__ */ jsxs("div", {
1584
+ children: [/* @__PURE__ */ jsxs(Card, {
1422
1585
  className: "sg-metric",
1423
1586
  children: [
1424
1587
  /* @__PURE__ */ jsx("small", { children: "匹配记录" }),
@@ -1427,7 +1590,7 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1427
1590
  ]
1428
1591
  }), table.fields.filter((f) => f.type === "number" || f.type === "rollup" || f.type === "formula").map((f) => {
1429
1592
  const stats = summarize(rows, f.id);
1430
- return /* @__PURE__ */ jsxs("div", {
1593
+ return /* @__PURE__ */ jsxs(Card, {
1431
1594
  className: "sg-metric",
1432
1595
  children: [
1433
1596
  /* @__PURE__ */ jsxs("small", { children: [f.name, " · 合计"] }),
@@ -1463,10 +1626,54 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1463
1626
  })
1464
1627
  ]
1465
1628
  }),
1466
- record && /* @__PURE__ */ jsx(Modal, {
1629
+ newTable && /* @__PURE__ */ jsx(Modal, {
1630
+ title: "新增数据表",
1631
+ close: () => setNewTable(false),
1632
+ error,
1633
+ children: /* @__PURE__ */ jsxs("form", {
1634
+ className: "sg-record-form",
1635
+ onSubmit: async (event) => {
1636
+ event.preventDefault();
1637
+ if (!newTableName.trim()) return;
1638
+ const id = `t_${crypto.randomUUID().slice(0, 8)}`, base = structuredClone(snapshot.base);
1639
+ base.tables.push({
1640
+ id,
1641
+ name: newTableName.trim(),
1642
+ fields: [{
1643
+ id: "name",
1644
+ name: "名称",
1645
+ type: "text",
1646
+ required: true
1647
+ }],
1648
+ views: [{
1649
+ id: "grid",
1650
+ name: "全部记录",
1651
+ type: "grid"
1652
+ }]
1653
+ });
1654
+ if (await configure(base)) {
1655
+ setTableId(id);
1656
+ setNewTable(false);
1657
+ }
1658
+ },
1659
+ children: [/* @__PURE__ */ jsxs(Label, { children: ["数据表名称", /* @__PURE__ */ jsx(Input, {
1660
+ "aria-label": "数据表名称",
1661
+ required: true,
1662
+ maxLength: 200,
1663
+ value: newTableName,
1664
+ onChange: (event) => setNewTableName(event.target.value)
1665
+ })] }), /* @__PURE__ */ jsx("footer", { children: /* @__PURE__ */ jsx(Button, {
1666
+ type: "submit",
1667
+ disabled: busy,
1668
+ children: "创建数据表"
1669
+ }) })]
1670
+ })
1671
+ }),
1672
+ !record && confirmationDialog,
1673
+ record && /* @__PURE__ */ jsxs(Modal, {
1467
1674
  title: record === "new" ? `新增${table.name}记录` : "记录详情",
1468
1675
  close: () => setRecord(void 0),
1469
- children: /* @__PURE__ */ jsx(RecordForm, {
1676
+ children: [/* @__PURE__ */ jsx(RecordForm, {
1470
1677
  table,
1471
1678
  row: record === "new" ? void 0 : record,
1472
1679
  snapshot,
@@ -1489,16 +1696,22 @@ function MultiTableApp({ endpoint = "/api/multitable", client: suppliedClient })
1489
1696
  return success;
1490
1697
  },
1491
1698
  onDelete: record === "new" ? void 0 : () => {
1492
- if (confirm("确认删除此记录?被其他记录引用时会拒绝删除。")) commit([{
1493
- type: "delete",
1494
- tableId: table.id,
1495
- id: record.id,
1496
- expectedVersion: record.version
1497
- }]).then((ok) => {
1498
- if (ok) setRecord(void 0);
1699
+ setConfirmation({
1700
+ title: "删除记录",
1701
+ description: "确认删除此记录?被其他记录引用时会拒绝删除。",
1702
+ action: () => {
1703
+ commit([{
1704
+ type: "delete",
1705
+ tableId: table.id,
1706
+ id: record.id,
1707
+ expectedVersion: record.version
1708
+ }]).then((ok) => {
1709
+ if (ok) setRecord(void 0);
1710
+ });
1711
+ }
1499
1712
  });
1500
1713
  }
1501
- }, record === "new" ? "new" : record.id)
1714
+ }, record === "new" ? "new" : record.id), confirmationDialog]
1502
1715
  }),
1503
1716
  schemaOpen && /* @__PURE__ */ jsx(SchemaEditor, {
1504
1717
  snapshot,