@shwfed/config 2.12.18 → 3.0.1

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.
Files changed (47) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/actions/buttons/2026-06-25/com.shwfed.actions.button.state.write/config.vue +17 -11
  3. package/dist/runtime/components/config/footer.vue +14 -8
  4. package/dist/runtime/components/config/index.d.vue.ts +1 -0
  5. package/dist/runtime/components/config/index.vue +30 -2
  6. package/dist/runtime/components/config/index.vue.d.ts +1 -0
  7. package/dist/runtime/components/config/use-editor.d.ts +12 -1
  8. package/dist/runtime/components/config/use-editor.js +47 -24
  9. package/dist/runtime/components/config/use-unsaved-guard.d.ts +1 -0
  10. package/dist/runtime/components/config/use-unsaved-guard.js +8 -0
  11. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.d.vue.ts +6 -6
  12. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.vue.d.ts +6 -6
  13. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.d.vue.ts +8 -8
  14. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.vue.d.ts +8 -8
  15. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.d.vue.ts +2 -2
  16. package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.vue.d.ts +2 -2
  17. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.d.vue.ts +6 -6
  18. package/dist/runtime/components/form/fields/2026-05-24/com.shwfed.form.field.monthrange/config.vue.d.ts +6 -6
  19. package/dist/runtime/components/modal.d.vue.ts +8 -8
  20. package/dist/runtime/components/modal.vue +9 -1
  21. package/dist/runtime/components/modal.vue.d.ts +8 -8
  22. package/dist/runtime/components/sidebar/footer.vue +13 -7
  23. package/dist/runtime/components/sidebar/index.d.vue.ts +1 -0
  24. package/dist/runtime/components/sidebar/index.vue +30 -2
  25. package/dist/runtime/components/sidebar/index.vue.d.ts +1 -0
  26. package/dist/runtime/components/sidebar/use-editor.d.ts +11 -1
  27. package/dist/runtime/components/sidebar/use-editor.js +38 -17
  28. package/dist/runtime/components/table/columns/2026-07-12/com.shwfed.table.column.dynamic/config.d.vue.ts +10 -0
  29. package/dist/runtime/components/table/columns/2026-07-12/com.shwfed.table.column.dynamic/config.vue +73 -0
  30. package/dist/runtime/components/table/columns/2026-07-12/com.shwfed.table.column.dynamic/config.vue.d.ts +10 -0
  31. package/dist/runtime/components/table/columns/2026-07-12/com.shwfed.table.column.dynamic/schema.d.ts +63 -0
  32. package/dist/runtime/components/table/columns/2026-07-12/com.shwfed.table.column.dynamic/schema.js +157 -0
  33. package/dist/runtime/components/table/index.vue +86 -12
  34. package/dist/runtime/components/table/utils/resolve.d.ts +42 -0
  35. package/dist/runtime/components/table/utils/resolve.js +11 -3
  36. package/dist/runtime/components/ui/date-picker/DatePickerInput.d.vue.ts +1 -1
  37. package/dist/runtime/components/ui/date-picker/DatePickerInput.vue.d.ts +1 -1
  38. package/dist/runtime/components/ui/date-picker/DatePickerTimeInput.d.vue.ts +1 -1
  39. package/dist/runtime/components/ui/date-picker/DatePickerTimeInput.vue.d.ts +1 -1
  40. package/dist/runtime/components/ui/date-range-picker/DateRangePickerInput.d.vue.ts +1 -1
  41. package/dist/runtime/components/ui/date-range-picker/DateRangePickerInput.vue.d.ts +1 -1
  42. package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.d.vue.ts +2 -2
  43. package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.vue.d.ts +2 -2
  44. package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue +4 -1
  45. package/dist/runtime/share/define-registry.d.ts +11 -0
  46. package/dist/runtime/share/define-registry.js +10 -3
  47. package/package.json +1 -1
@@ -32,10 +32,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
32
32
  readonly mode: "formula" | "prefill";
33
33
  readonly expression: string;
34
34
  } | undefined;
35
- readonly format?: string | undefined;
36
- readonly valueFormat?: string | undefined;
37
- readonly disabledDate?: string | undefined;
38
35
  readonly rangeSeparatorIcon?: string | undefined;
36
+ readonly format?: string | undefined;
39
37
  readonly startPlaceholder?: readonly [{
40
38
  readonly locale: "zh";
41
39
  readonly message: string;
@@ -50,6 +48,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
50
48
  readonly locale: "en" | "ja" | "ko";
51
49
  readonly message: string;
52
50
  }[]] | undefined;
51
+ readonly valueFormat?: string | undefined;
52
+ readonly disabledDate?: string | undefined;
53
53
  readonly validations?: readonly {
54
54
  readonly message: readonly [{
55
55
  readonly locale: "zh";
@@ -104,10 +104,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
104
104
  readonly mode: "formula" | "prefill";
105
105
  readonly expression: string;
106
106
  } | undefined;
107
- readonly format?: string | undefined;
108
- readonly valueFormat?: string | undefined;
109
- readonly disabledDate?: string | undefined;
110
107
  readonly rangeSeparatorIcon?: string | undefined;
108
+ readonly format?: string | undefined;
111
109
  readonly startPlaceholder?: readonly [{
112
110
  readonly locale: "zh";
113
111
  readonly message: string;
@@ -122,6 +120,8 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
122
120
  readonly locale: "en" | "ja" | "ko";
123
121
  readonly message: string;
124
122
  }[]] | undefined;
123
+ readonly valueFormat?: string | undefined;
124
+ readonly disabledDate?: string | undefined;
125
125
  readonly validations?: readonly {
126
126
  readonly message: readonly [{
127
127
  readonly locale: "zh";
@@ -10,29 +10,29 @@ declare function close(): void;
10
10
  declare var __VLS_20: {
11
11
  close: typeof close;
12
12
  isDesktop: boolean;
13
- }, __VLS_42: {
13
+ }, __VLS_45: {
14
14
  close: typeof close;
15
15
  isDesktop: boolean;
16
- }, __VLS_50: {
16
+ }, __VLS_53: {
17
17
  close: typeof close;
18
18
  isDesktop: boolean;
19
- }, __VLS_57: {
19
+ }, __VLS_60: {
20
20
  close: typeof close;
21
21
  isDesktop: boolean;
22
- }, __VLS_65: {
22
+ }, __VLS_68: {
23
23
  close: typeof close;
24
24
  isDesktop: boolean;
25
25
  };
26
26
  type __VLS_Slots = {} & {
27
27
  trigger?: (props: typeof __VLS_20) => any;
28
28
  } & {
29
- title?: (props: typeof __VLS_42) => any;
29
+ title?: (props: typeof __VLS_45) => any;
30
30
  } & {
31
- description?: (props: typeof __VLS_50) => any;
31
+ description?: (props: typeof __VLS_53) => any;
32
32
  } & {
33
- default?: (props: typeof __VLS_57) => any;
33
+ default?: (props: typeof __VLS_60) => any;
34
34
  } & {
35
- footer?: (props: typeof __VLS_65) => any;
35
+ footer?: (props: typeof __VLS_68) => any;
36
36
  };
37
37
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
38
38
  "update:open": (open: boolean) => any;
@@ -78,6 +78,9 @@ const open = computed({
78
78
  function close() {
79
79
  open.value = false;
80
80
  }
81
+ function onDismissAttempt(event) {
82
+ if (!props.dismissible) event.preventDefault();
83
+ }
81
84
  </script>
82
85
 
83
86
  <template>
@@ -98,12 +101,17 @@ function close() {
98
101
  />
99
102
  </component>
100
103
 
104
+ <!-- The × button is a dismiss affordance too — it hides alongside the
105
+ other dismissal paths while the modal is non-dismissible. -->
101
106
  <component
102
107
  :is="modalComponents.Content"
103
108
  v-bind="attrs"
104
- :show-close-button="props.showCloseButton"
109
+ :show-close-button="props.showCloseButton && props.dismissible"
105
110
  :content-width="props.contentWidth"
106
111
  @after-close="emit('after-close')"
112
+ @escape-key-down="onDismissAttempt"
113
+ @pointer-down-outside="onDismissAttempt"
114
+ @interact-outside="onDismissAttempt"
107
115
  >
108
116
  <component
109
117
  :is="modalComponents.Header"
@@ -10,29 +10,29 @@ declare function close(): void;
10
10
  declare var __VLS_20: {
11
11
  close: typeof close;
12
12
  isDesktop: boolean;
13
- }, __VLS_42: {
13
+ }, __VLS_45: {
14
14
  close: typeof close;
15
15
  isDesktop: boolean;
16
- }, __VLS_50: {
16
+ }, __VLS_53: {
17
17
  close: typeof close;
18
18
  isDesktop: boolean;
19
- }, __VLS_57: {
19
+ }, __VLS_60: {
20
20
  close: typeof close;
21
21
  isDesktop: boolean;
22
- }, __VLS_65: {
22
+ }, __VLS_68: {
23
23
  close: typeof close;
24
24
  isDesktop: boolean;
25
25
  };
26
26
  type __VLS_Slots = {} & {
27
27
  trigger?: (props: typeof __VLS_20) => any;
28
28
  } & {
29
- title?: (props: typeof __VLS_42) => any;
29
+ title?: (props: typeof __VLS_45) => any;
30
30
  } & {
31
- description?: (props: typeof __VLS_50) => any;
31
+ description?: (props: typeof __VLS_53) => any;
32
32
  } & {
33
- default?: (props: typeof __VLS_57) => any;
33
+ default?: (props: typeof __VLS_60) => any;
34
34
  } & {
35
- footer?: (props: typeof __VLS_65) => any;
35
+ footer?: (props: typeof __VLS_68) => any;
36
36
  };
37
37
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
38
38
  "update:open": (open: boolean) => any;
@@ -7,15 +7,12 @@ const props = defineProps({
7
7
  });
8
8
  const emit = defineEmits(["close"]);
9
9
  function onDismiss() {
10
+ if (props.state.saving.value) return;
10
11
  props.state.dismiss();
11
12
  emit("close");
12
13
  }
13
- function onConfirm() {
14
- const errorBefore = props.state.validationError.value;
15
- props.state.confirm();
16
- const errorAfter = props.state.validationError.value;
17
- if (errorAfter && errorAfter !== errorBefore) return;
18
- if (!errorAfter) emit("close");
14
+ async function onConfirm() {
15
+ if (await props.state.confirm()) emit("close");
19
16
  }
20
17
  </script>
21
18
 
@@ -26,11 +23,16 @@ function onConfirm() {
26
23
  v-if="props.state.validationError.value"
27
24
  class="text-red-500 break-all"
28
25
  >{{ props.state.validationError.value }}</span>
26
+ <span
27
+ v-if="props.state.saveError.value"
28
+ class="text-red-500 break-all"
29
+ >{{ props.state.saveError.value }}</span>
29
30
  </div>
30
31
  <Button
31
32
  type="button"
32
33
  variant="default"
33
34
  size="sm"
35
+ :disabled="props.state.saving.value"
34
36
  @click="onDismiss"
35
37
  >
36
38
  <Icon icon="fluent:dismiss-20-regular" />
@@ -40,9 +42,13 @@ function onConfirm() {
40
42
  type="button"
41
43
  variant="primary"
42
44
  size="sm"
45
+ :disabled="props.state.saving.value"
43
46
  @click="onConfirm"
44
47
  >
45
- <Icon icon="fluent:checkmark-20-regular" />
48
+ <Icon
49
+ :icon="props.state.saving.value ? 'fluent:arrow-sync-20-regular' : 'fluent:checkmark-20-regular'"
50
+ :class="props.state.saving.value ? 'animate-spin' : ''"
51
+ />
46
52
  确认
47
53
  </Button>
48
54
  </div>
@@ -3,6 +3,7 @@ import { type SidebarConfigValue } from './schema.js';
3
3
  type __VLS_Props = {
4
4
  configure?: (env: Environment) => void;
5
5
  editable?: boolean;
6
+ save?: (config: SidebarConfigValue) => void | Promise<void>;
6
7
  storageKey?: string;
7
8
  collapsible?: 'offcanvas' | 'icon' | 'none';
8
9
  width?: string;
@@ -1,5 +1,6 @@
1
1
  <script setup>
2
2
  import { computed, ref, watch } from "vue";
3
+ import { Icon } from "@iconify/vue";
3
4
  import { useLocalStorage } from "@vueuse/core";
4
5
  import { useRuntimeConfig } from "#app";
5
6
  import { injectCELContext } from "../../utils/cel-context";
@@ -9,6 +10,7 @@ import SidebarTree from "./tree.vue";
9
10
  import EditorBody from "./config.vue";
10
11
  import EditorFooter from "./footer.vue";
11
12
  import { useSidebarEditor } from "./use-editor";
13
+ import { useUnsavedChangesGuard } from "../config/use-unsaved-guard";
12
14
  import { createSidebarConfig } from "./schema";
13
15
  defineOptions({ name: "ShwfedSidebar" });
14
16
  const configModel = defineModel("config", { type: null });
@@ -21,6 +23,7 @@ const config = computed({
21
23
  const props = defineProps({
22
24
  configure: { type: Function, required: false },
23
25
  editable: { type: Boolean, required: false },
26
+ save: { type: Function, required: false },
24
27
  storageKey: { type: String, required: false },
25
28
  collapsible: { type: String, required: false, default: "none" },
26
29
  width: { type: String, required: false, default: "13rem" }
@@ -83,10 +86,16 @@ const mergedConfigure = (env) => {
83
86
  const editorOpen = ref(false);
84
87
  const editorState = useSidebarEditor(config, {
85
88
  configure: mergedConfigure,
89
+ // Presence decided once at setup; the wrapper reads the current prop per
90
+ // call so a rebound handler identity stays honored (mirrors config/index.vue).
91
+ save: props.save ? (cfg) => props.save(cfg) : void 0,
86
92
  onClose: () => {
87
93
  editorOpen.value = false;
88
94
  }
89
95
  });
96
+ useUnsavedChangesGuard(
97
+ () => props.editable === true && (editorState.isDirty.value || editorState.saving.value)
98
+ );
90
99
  watch(editorOpen, (open, prev) => {
91
100
  if (open && !prev) editorState.reset();
92
101
  });
@@ -137,11 +146,16 @@ function onRootClick(event) {
137
146
  </SidebarProvider>
138
147
  </div>
139
148
 
149
+ <!-- While the draft is dirty (or a save is in flight) the modal refuses
150
+ casual dismissal — Esc, outside-click, and the × button all drop the
151
+ session on the reopen re-anchor above. The explicit footer exits
152
+ (取消 discards, 确认 saves) remain the only ways out of a dirty
153
+ session. Mirrors `config/index.vue`. -->
140
154
  <ShwfedModal
141
155
  v-if="editable"
142
156
  v-model:open="editorOpen"
143
157
  content-width="min(52rem, 92vw)"
144
- :dismissible="true"
158
+ :dismissible="!editorState.isDirty.value && !editorState.saving.value"
145
159
  >
146
160
  <template #title>
147
161
  导航侧栏
@@ -149,7 +163,21 @@ function onRootClick(event) {
149
163
  <template #description>
150
164
  <span class="sr-only">编辑导航侧栏配置</span>
151
165
  </template>
152
- <EditorBody :state="editorState" />
166
+ <!-- The overlay freezes the form while the host save runs: an edit
167
+ landing mid-save would be silently clobbered by the post-commit
168
+ draft re-anchor. -->
169
+ <div class="relative">
170
+ <EditorBody :state="editorState" />
171
+ <div
172
+ v-if="editorState.saving.value"
173
+ class="absolute inset-0 z-20 flex items-center justify-center bg-white/60"
174
+ >
175
+ <Icon
176
+ icon="fluent:arrow-sync-20-regular"
177
+ class="animate-spin text-2xl text-zinc-500"
178
+ />
179
+ </div>
180
+ </div>
153
181
  <template #footer>
154
182
  <EditorFooter
155
183
  :state="editorState"
@@ -3,6 +3,7 @@ import { type SidebarConfigValue } from './schema.js';
3
3
  type __VLS_Props = {
4
4
  configure?: (env: Environment) => void;
5
5
  editable?: boolean;
6
+ save?: (config: SidebarConfigValue) => void | Promise<void>;
6
7
  storageKey?: string;
7
8
  collapsible?: 'offcanvas' | 'icon' | 'none';
8
9
  width?: string;
@@ -4,14 +4,24 @@ import { type SidebarConfigValue } from './schema.js';
4
4
  export interface SidebarEditorState {
5
5
  draft: Ref<SidebarConfigValue>;
6
6
  validationError: Ref<string | null>;
7
+ saveError: Ref<string | null>;
8
+ saving: Ref<boolean>;
7
9
  isDirty: ComputedRef<boolean>;
8
10
  configure: (env: Environment) => void;
9
- confirm: () => void;
11
+ confirm: () => Promise<boolean>;
10
12
  dismiss: () => void;
11
13
  reset: () => void;
12
14
  }
13
15
  export interface UseSidebarEditorOptions {
14
16
  configure?: (env: Environment) => void;
17
+ /**
18
+ * Delegated persistence. When provided, a successful 确认 first awaits this
19
+ * with the validated config; only after it resolves does the draft commit
20
+ * to the outer model and the editor close. A rejection keeps the editor
21
+ * open with the draft intact and surfaces the failure via `saveError` (see
22
+ * config/use-editor.ts — same contract).
23
+ */
24
+ save?: (config: SidebarConfigValue) => void | Promise<void>;
15
25
  /** Called after a successful 确认 commits the draft to the outer model. */
16
26
  onClose?: () => void;
17
27
  }
@@ -16,41 +16,62 @@ export function useSidebarEditor(model, options = {}) {
16
16
  () => JSON.stringify(draft.value) !== JSON.stringify(baseline.value)
17
17
  );
18
18
  const validationError = ref(null);
19
+ const saveError = ref(null);
20
+ const saving = ref(false);
19
21
  const validityRegistry = createExpressionValidityRegistry();
20
22
  provideExpressionValidity(validityRegistry);
21
- function confirm() {
23
+ async function confirm() {
24
+ if (saving.value) return false;
22
25
  const schema = SidebarConfig(configure);
26
+ let decoded;
23
27
  try {
24
- const decoded = Schema.decodeUnknownSync(schema)(draft.value);
25
- if (!validityRegistry.allValid()) {
26
- validationError.value = "\u5B58\u5728\u65E0\u6548\u7684\u8DE8\u5C42\u4F5C\u7528\u57DF\u5F15\u7528\uFF0C\u8BF7\u4FEE\u6B63\u6807\u7EA2\u7684\u8868\u8FBE\u5F0F\u540E\u518D\u4FDD\u5B58";
27
- return;
28
- }
29
- const dangling = findDanglingLinkages(decoded);
30
- if (dangling.length > 0) {
31
- validationError.value = formatDanglingLinkages(dangling, decoded, () => void 0);
32
- return;
33
- }
34
- validationError.value = null;
35
- model.value = decoded;
36
- baseline.value = deepClone(decoded);
37
- draft.value = deepClone(decoded);
38
- options.onClose?.();
28
+ decoded = Schema.decodeUnknownSync(schema)(draft.value);
39
29
  } catch (err) {
40
30
  validationError.value = formatValidationError(err, () => void 0, draft.value);
31
+ return false;
32
+ }
33
+ if (!validityRegistry.allValid()) {
34
+ validationError.value = "\u5B58\u5728\u65E0\u6548\u7684\u8DE8\u5C42\u4F5C\u7528\u57DF\u5F15\u7528\uFF0C\u8BF7\u4FEE\u6B63\u6807\u7EA2\u7684\u8868\u8FBE\u5F0F\u540E\u518D\u4FDD\u5B58";
35
+ return false;
36
+ }
37
+ const dangling = findDanglingLinkages(decoded);
38
+ if (dangling.length > 0) {
39
+ validationError.value = formatDanglingLinkages(dangling, decoded, () => void 0);
40
+ return false;
41
41
  }
42
+ validationError.value = null;
43
+ if (options.save) {
44
+ saving.value = true;
45
+ saveError.value = null;
46
+ try {
47
+ await options.save(deepClone(decoded));
48
+ } catch (err) {
49
+ saveError.value = `\u4FDD\u5B58\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`;
50
+ return false;
51
+ } finally {
52
+ saving.value = false;
53
+ }
54
+ }
55
+ model.value = decoded;
56
+ baseline.value = deepClone(decoded);
57
+ draft.value = deepClone(decoded);
58
+ options.onClose?.();
59
+ return true;
42
60
  }
43
61
  function reset() {
44
62
  baseline.value = deepClone(model.value);
45
63
  draft.value = deepClone(model.value);
46
64
  validationError.value = null;
65
+ saveError.value = null;
47
66
  }
48
67
  function dismiss() {
68
+ if (saving.value) return;
49
69
  reset();
50
70
  options.onClose?.();
51
71
  }
52
72
  watch(draft, () => {
53
73
  if (validationError.value) validationError.value = null;
74
+ if (saveError.value) saveError.value = null;
54
75
  }, { deep: true });
55
- return { draft, validationError, isDirty, configure, confirm, dismiss, reset };
76
+ return { draft, validationError, saveError, saving, isDirty, configure, confirm, dismiss, reset };
56
77
  }
@@ -0,0 +1,10 @@
1
+ type __VLS_ModelProps = {
2
+ modelValue: Record<string, any>;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "update:modelValue": (value: Record<string, any>) => any;
6
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
7
+ "onUpdate:modelValue"?: ((value: Record<string, any>) => any) | undefined;
8
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,73 @@
1
+ <script setup>
2
+ import { ExpressionEditor } from "../../../../ui/expression-editor";
3
+ import { Field, FieldLabel } from "../../../../ui/field";
4
+ import { Markdown } from "../../../../ui/markdown";
5
+ import { getStructFieldDescription, getStructFieldTitle } from "../../../utils/schema-meta";
6
+ import { schema } from "./schema";
7
+ defineOptions({ name: "ShwfedTableDynamicColumnsConfig" });
8
+ const value = defineModel({ type: Object, ...{ required: true } });
9
+ const fieldSchema = schema(() => {
10
+ });
11
+ const fieldTitle = (field) => getStructFieldTitle(fieldSchema, field) ?? field;
12
+ const fieldDescription = (field) => getStructFieldDescription(fieldSchema, field);
13
+ const NON_ROW_EXCLUDE = ["row", "index", "selections", "options"];
14
+ const TRANSFORM_VARS = {
15
+ json: { type: "optional<dyn>", label: "HTTP \u54CD\u5E94\u4F53" },
16
+ query: { type: "dyn", label: "\u641C\u7D22\u6761\u4EF6" }
17
+ };
18
+ </script>
19
+
20
+ <template>
21
+ <div class="space-y-5">
22
+ <Field orientation="vertical">
23
+ <FieldLabel class="text-xs text-zinc-500">
24
+ <template
25
+ v-if="fieldDescription('request')"
26
+ #tooltip
27
+ >
28
+ <Markdown
29
+ :source="fieldDescription('request')"
30
+ block
31
+ class="prose prose-sm prose-zinc"
32
+ />
33
+ </template>
34
+ {{ fieldTitle("request") }}
35
+ </FieldLabel>
36
+ <ExpressionEditor
37
+ :model-value="value.request ?? ''"
38
+ placeholder="如 http.get('/api/columns').query('channel', 'invoice')"
39
+ result-type="HttpRequest"
40
+ multiline
41
+ class="min-h-20"
42
+ :exclude-vars="NON_ROW_EXCLUDE"
43
+ @update:model-value="(v) => value = { ...value, request: v }"
44
+ />
45
+ </Field>
46
+
47
+ <Field orientation="vertical">
48
+ <FieldLabel class="text-xs text-zinc-500">
49
+ <template
50
+ v-if="fieldDescription('transform')"
51
+ #tooltip
52
+ >
53
+ <Markdown
54
+ :source="fieldDescription('transform')"
55
+ block
56
+ class="prose prose-sm prose-zinc"
57
+ />
58
+ </template>
59
+ {{ fieldTitle("transform") }}
60
+ </FieldLabel>
61
+ <ExpressionEditor
62
+ :model-value="value.transform ?? ''"
63
+ placeholder="如 json.orValue([]).map(c, { 'key': c.parameter_name, 'header': c.name, 'kind': 'text', 'accessor': &quot;row.custom_property.filter(p, p.name == '&quot; + c.parameter_name + &quot;')[0].?value&quot; })"
64
+ result-type="list"
65
+ multiline
66
+ class="min-h-32"
67
+ :extra-vars="TRANSFORM_VARS"
68
+ :exclude-vars="NON_ROW_EXCLUDE"
69
+ @update:model-value="(v) => value = { ...value, transform: v }"
70
+ />
71
+ </Field>
72
+ </div>
73
+ </template>
@@ -0,0 +1,10 @@
1
+ type __VLS_ModelProps = {
2
+ modelValue: Record<string, any>;
3
+ };
4
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
5
+ "update:modelValue": (value: Record<string, any>) => any;
6
+ }, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
7
+ "onUpdate:modelValue"?: ((value: Record<string, any>) => any) | undefined;
8
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
+ declare const _default: typeof __VLS_export;
10
+ export default _default;
@@ -0,0 +1,63 @@
1
+ import { Schema } from 'effect';
2
+ import type { Environment } from '../../../../../vendor/cel-js/lib/index.js';
3
+ import type { ColumnExpand } from '../../../utils/resolve.js';
4
+ export declare const type: "com.shwfed.table.column.dynamic";
5
+ export declare const compatibilityDate: "2026-07-12";
6
+ export declare const metadata: {
7
+ readonly name: "动态列";
8
+ readonly icon: "fluent:table-lightning-20-regular";
9
+ readonly description: import("../../../../../share/markdown.js").Markdown;
10
+ };
11
+ declare const DynColumnKind: Schema.Literal<["text", "number", "date"]>;
12
+ export type DynColumnKindValue = Schema.Schema.Type<typeof DynColumnKind>;
13
+ /**
14
+ * The narrow, module-owned DTO the `transform` must emit per column. Keeping
15
+ * this contract closed (vs. exposing the internal column union) means internal
16
+ * schema evolution never breaks app config, the transform stays terse, and dyn
17
+ * cols auto-upgrade to the current renderer. Each element is decoded against
18
+ * this at runtime (partial-drop, loud) — the strongest static guarantee is
19
+ * only that `transform` returns `list<dyn>`.
20
+ */
21
+ export declare const DynColumnDescriptor: Schema.Struct<{
22
+ key: Schema.filter<typeof Schema.String>;
23
+ header: typeof Schema.String;
24
+ kind: Schema.Literal<["text", "number", "date"]>;
25
+ accessor: Schema.filter<typeof Schema.String>;
26
+ /** px column width; defaults to 150. */
27
+ width: Schema.optional<typeof Schema.Number>;
28
+ /** Client-side sort by accessed value. */
29
+ sortable: Schema.optional<typeof Schema.Boolean>;
30
+ /** Backend sort field; only needed for server-side sort. */
31
+ sortKey: Schema.optional<typeof Schema.String>;
32
+ /** Default-hidden; the user can re-show it. */
33
+ hidden: Schema.optional<typeof Schema.Boolean>;
34
+ /** Header hover text; wrapped into a Locale by `expand`. */
35
+ tooltip: Schema.optional<typeof Schema.String>;
36
+ /** Text / number alignment. */
37
+ align: Schema.optional<Schema.Literal<["left", "center", "right"]>>;
38
+ }>;
39
+ export type DynColumnDescriptorValue = Schema.Schema.Type<typeof DynColumnDescriptor>;
40
+ export declare function schema(configure: (env: Environment) => void): Schema.Struct<{
41
+ request: Schema.Schema<string, string, never>;
42
+ transform: Schema.Schema<string, string, never>;
43
+ id: Schema.refine<string, typeof Schema.String>;
44
+ groupId: Schema.optional<typeof Schema.UUID>;
45
+ type: Schema.Literal<["com.shwfed.table.column.dynamic"]>;
46
+ compatibilityDate: Schema.Literal<["2026-07-12"]>;
47
+ }>;
48
+ export type Value = Schema.Schema.Type<ReturnType<typeof schema>>;
49
+ export declare function defaults(): Partial<Value>;
50
+ /**
51
+ * Runtime generator. Evaluates the block's `transform` against the fetched
52
+ * `json`, then decodes each element against `DynColumnDescriptor` — keeping the
53
+ * valid, dropping the invalid / duplicate / colliding (partial-drop, loud).
54
+ * Three failure layers (see the design doc):
55
+ * 1. transform throws / returns a non-list → the whole block yields 0 columns
56
+ * (other columns render normally).
57
+ * 2. individual elements malformed / duplicate `key` / `key` colliding with a
58
+ * static id → drop that element alone, first-wins on keys.
59
+ * 3. per-row accessor eval throws → handled downstream (`translateColumn`'s
60
+ * `accessorFn` try/catches per row) — not here.
61
+ */
62
+ export declare const expand: ColumnExpand;
63
+ export {};