@shwfed/config 2.0.3 → 2.1.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.
- package/dist/module.json +1 -1
- package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/config.d.vue.ts +16 -6
- package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/config.vue +263 -76
- package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/config.vue.d.ts +16 -6
- package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/runtime.d.vue.ts +16 -6
- package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/runtime.vue +104 -12
- package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/runtime.vue.d.ts +16 -6
- package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/schema.d.ts +51 -15
- package/dist/runtime/components/config/blocks/2026-05-17/{com.shwfed.block.chart.line → com.shwfed.block.chart.xy}/schema.js +67 -22
- package/dist/runtime/components/form/ai/fields-button.d.vue.ts +13 -0
- package/dist/runtime/components/form/ai/fields-button.vue +458 -0
- package/dist/runtime/components/form/ai/fields-button.vue.d.ts +13 -0
- package/dist/runtime/components/form/ai/fields-task.md +71 -0
- package/dist/runtime/components/form/config.d.vue.ts +1 -1
- package/dist/runtime/components/form/config.vue +4 -36
- package/dist/runtime/components/form/config.vue.d.ts +1 -1
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.d.vue.ts +18 -18
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.daterange/config.vue.d.ts +18 -18
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetime/config.d.vue.ts +4 -4
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetime/config.vue.d.ts +4 -4
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.d.vue.ts +22 -22
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.datetimerange/config.vue.d.ts +22 -22
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.time/config.d.vue.ts +2 -2
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.time/config.vue.d.ts +2 -2
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.d.vue.ts +4 -4
- package/dist/runtime/components/form/fields/2026-04-27/com.shwfed.form.field.timerange/config.vue.d.ts +4 -4
- package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.d.vue.ts +12 -12
- package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.vue +87 -11
- package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/config.vue.d.ts +12 -12
- package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/runtime.vue +18 -6
- package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/schema.d.ts +1 -1
- package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.numberrange/schema.js +11 -4
- package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.switch/config.d.vue.ts +10 -10
- package/dist/runtime/components/form/fields/2026-04-28/com.shwfed.form.field.switch/config.vue.d.ts +10 -10
- package/dist/runtime/components/form/fields/2026-05-12/com.shwfed.form.field.upload/config.d.vue.ts +12 -12
- package/dist/runtime/components/form/fields/2026-05-12/com.shwfed.form.field.upload/config.vue.d.ts +12 -12
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/config.d.vue.ts +2 -2
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/config.vue +15 -0
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/config.vue.d.ts +2 -2
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.d.vue.ts +1 -0
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.vue +13 -4
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/row.vue.d.ts +1 -0
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/runtime.vue +1 -0
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/schema.d.ts +1 -1
- package/dist/runtime/components/form/fields/2026-05-13/com.shwfed.form.field.list/schema.js +5 -1
- package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/config.d.vue.ts +131 -0
- package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/config.vue +170 -0
- package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/config.vue.d.ts +131 -0
- package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/runtime.d.vue.ts +8 -0
- package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/runtime.vue +52 -0
- package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/runtime.vue.d.ts +8 -0
- package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/schema.d.ts +112 -0
- package/dist/runtime/components/form/fields/2026-05-18/com.shwfed.form.field.table/schema.js +44 -0
- package/dist/runtime/components/form/index.vue +5 -4
- package/dist/runtime/components/form/schema.d.ts +10 -0
- package/dist/runtime/components/form/schema.js +6 -2
- package/dist/runtime/components/form/unit-config.d.vue.ts +16 -0
- package/dist/runtime/components/form/unit-config.vue +30 -3
- package/dist/runtime/components/form/unit-config.vue.d.ts +16 -0
- package/dist/runtime/components/form/utils/cel-scope.d.ts +13 -0
- package/dist/runtime/components/form/utils/cel-scope.js +32 -0
- package/dist/runtime/components/form/utils/schema-meta.d.ts +13 -0
- package/dist/runtime/components/form/utils/schema-meta.js +15 -0
- package/dist/runtime/components/table/ai/columns-task.md +10 -1
- package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.markdown/config.vue +2 -2
- package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.markdown/runtime.vue +14 -4
- package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.markdown/schema.js +3 -2
- package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.text/config.vue +2 -2
- package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.text/runtime.vue +14 -4
- package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.text/schema.js +3 -2
- package/dist/runtime/components/table/config.d.vue.ts +11 -1
- package/dist/runtime/components/table/config.vue +4 -0
- package/dist/runtime/components/table/config.vue.d.ts +11 -1
- package/dist/runtime/components/table/index.d.vue.ts +4 -0
- package/dist/runtime/components/table/index.vue +28 -2
- package/dist/runtime/components/table/index.vue.d.ts +4 -0
- package/dist/runtime/components/table/schema.d.ts +12 -0
- package/dist/runtime/components/table/schema.js +6 -1
- package/dist/runtime/components/table/utils/shared.d.ts +2 -1
- package/dist/runtime/components/ui/date-range-picker/DateRangePickerDateTimePanel.d.vue.ts +1 -1
- package/dist/runtime/components/ui/date-range-picker/DateRangePickerDateTimePanel.vue.d.ts +1 -1
- package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.d.vue.ts +1 -1
- package/dist/runtime/components/ui/date-range-picker/DateRangePickerTimeInput.vue.d.ts +1 -1
- package/dist/runtime/share/expression.d.ts +1 -2
- package/dist/runtime/share/slot-renderer.vue +7 -6
- package/dist/runtime/vendor/cel-js/CLAUDE.md +1 -1
- package/dist/runtime/vendor/cel-js/PROMPT.md +12 -3
- package/dist/runtime/vendor/cel-js/lib/http-builder.d.ts +10 -3
- package/dist/runtime/vendor/cel-js/lib/http-builder.js +28 -5
- package/dist/runtime/vendor/cel-js/lib/http-builtins.d.ts +4 -3
- package/dist/runtime/vendor/cel-js/lib/http-builtins.js +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { Locale } from "../../../../../share/locale.js";
|
|
3
|
+
import { TableConfig, defaultTableConfig } from "../../../../table/schema.js";
|
|
4
|
+
import { commonFieldFields } from "../../../utils/common.js";
|
|
5
|
+
export const type = "com.shwfed.form.field.table";
|
|
6
|
+
export const compatibilityDate = "2026-05-18";
|
|
7
|
+
export const metadata = {
|
|
8
|
+
name: "\u8868\u683C",
|
|
9
|
+
icon: "fluent:table-20-regular",
|
|
10
|
+
w: { initial: 12, min: 6, max: Infinity },
|
|
11
|
+
h: { initial: 10, min: 5, max: Infinity, grow: true }
|
|
12
|
+
};
|
|
13
|
+
export function schema(configure) {
|
|
14
|
+
const Table = Schema.suspend(
|
|
15
|
+
() => TableConfig(configure)
|
|
16
|
+
);
|
|
17
|
+
return Schema.Struct({
|
|
18
|
+
type: Schema.Literal(type),
|
|
19
|
+
compatibilityDate: Schema.Literal(compatibilityDate),
|
|
20
|
+
...commonFieldFields(configure),
|
|
21
|
+
label: Schema.optional(Locale.annotations({
|
|
22
|
+
title: "\u6807\u7B7E",
|
|
23
|
+
description: "\u8868\u683C\u4E0A\u65B9\u5C55\u793A\u7684\u6587\u672C\uFF1B\u7559\u7A7A\u5219\u4E0D\u6E32\u67D3\u6807\u7B7E"
|
|
24
|
+
})),
|
|
25
|
+
tooltip: Schema.optional(Locale.annotations({
|
|
26
|
+
title: "\u63D0\u793A",
|
|
27
|
+
description: "\u9F20\u6807\u60AC\u505C\u5728\u6807\u7B7E\u4E0A\u65F6\u5C55\u793A\u7684\u8BF4\u660E\uFF0C\u652F\u6301 Markdown"
|
|
28
|
+
})),
|
|
29
|
+
binding: Schema.optional(Schema.String.pipe(Schema.minLength(1)).annotations({
|
|
30
|
+
title: "\u7ED1\u5B9A\u8DEF\u5F84",
|
|
31
|
+
description: "\u8868\u683C\u5F53\u524D\u6570\u636E\u5B9E\u65F6\u5199\u5165\u8868\u5355\u72B6\u6001\u7684 `dot-prop` \u8DEF\u5F84\uFF0C\u4F8B\u5982 `rows`\uFF1B\u7559\u7A7A\u5219\u4E0D\u5199\u5165\u8868\u5355\u72B6\u6001"
|
|
32
|
+
})),
|
|
33
|
+
table: Table.annotations({
|
|
34
|
+
title: "\u8868\u683C\u914D\u7F6E",
|
|
35
|
+
description: "\u5185\u5D4C\u8868\u683C\u7684\u5B8C\u6574\u914D\u7F6E\uFF1B\u6570\u636E\u7531\u8868\u683C\u81EA\u5DF1\u7684\u6570\u636E\u6E90\u52A0\u8F7D\uFF0C\u7528\u6237\u7684\u672C\u5730\u589E\u5220\u6539\u5B9E\u65F6\u56DE\u5199\u5230\u300C\u7ED1\u5B9A\u8DEF\u5F84\u300D"
|
|
36
|
+
})
|
|
37
|
+
}).annotations({
|
|
38
|
+
title: "TableField",
|
|
39
|
+
description: "\u628A\u4E00\u4E2A\u53EF\u672C\u5730\u7F16\u8F91\u7684\u8868\u683C\u5D4C\u5165\u8868\u5355\uFF0C\u5176\u5B9E\u65F6\u6570\u636E\u5199\u5165\u7ED1\u5B9A\u8DEF\u5F84"
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
export function defaults() {
|
|
43
|
+
return { table: defaultTableConfig() };
|
|
44
|
+
}
|
|
@@ -167,15 +167,16 @@ const gridStyle = computed(() => {
|
|
|
167
167
|
const hi = Math.min(l.rows, y2 - 1);
|
|
168
168
|
for (let r = lo; r <= hi; r++) grows[r - 1] = true;
|
|
169
169
|
}
|
|
170
|
-
rowTemplate = grows.map((g) => g ? "auto" : "minmax(
|
|
170
|
+
rowTemplate = grows.map((g) => g ? "auto" : "minmax(auto, 1fr)").join(" ");
|
|
171
171
|
}
|
|
172
|
+
const gap = `calc(${l.gap ?? DEFAULT_GAP} * 0.25rem)`;
|
|
173
|
+
const colGap = l.columns > 1 ? `min(${gap}, calc(100% / ${l.columns - 1}))` : gap;
|
|
172
174
|
const parts = [
|
|
173
175
|
"display: grid",
|
|
174
176
|
`grid-template-columns: ${colTemplate}`,
|
|
175
177
|
rowTemplate ? `grid-template-rows: ${rowTemplate}` : "",
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
`gap: calc(${l.gap ?? DEFAULT_GAP} * 0.25rem)`,
|
|
178
|
+
`column-gap: ${colGap}`,
|
|
179
|
+
`row-gap: ${gap}`,
|
|
179
180
|
l.style ?? ""
|
|
180
181
|
].filter(Boolean);
|
|
181
182
|
return parts.join("; ");
|
|
@@ -68,6 +68,16 @@ export declare function FormUnit(configure: (env: Environment) => void): Schema.
|
|
|
68
68
|
}>;
|
|
69
69
|
}>>>;
|
|
70
70
|
}>>;
|
|
71
|
+
/**
|
|
72
|
+
* Register the form-wide `now` / `form` live variables on `env`, skipping any
|
|
73
|
+
* already declared. Idempotent so it composes safely from either side: a
|
|
74
|
+
* `FormConfig` nested inside another (the `table` form field embeds a table
|
|
75
|
+
* whose `query` is its own sub-form, and the embedding `configure` advertises
|
|
76
|
+
* the same pair) would otherwise register `form` twice on one env and the CEL
|
|
77
|
+
* registry throws `'form' is already registered`. Mirrors
|
|
78
|
+
* `registerRowVariablesIfAbsent`.
|
|
79
|
+
*/
|
|
80
|
+
export declare function registerFormVariablesIfAbsent(env: Environment): void;
|
|
71
81
|
export declare function FormConfig(configure: (env: Environment) => void): Schema.refine<{
|
|
72
82
|
readonly initial?: string | undefined;
|
|
73
83
|
readonly kind: "shwfed.component.form";
|
|
@@ -37,10 +37,14 @@ export function FormUnit(configure) {
|
|
|
37
37
|
description: "\u4E00\u7EC4\u8868\u5355\u5B57\u6BB5\u53CA\u5176\u5E03\u5C40"
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
|
+
export function registerFormVariablesIfAbsent(env) {
|
|
41
|
+
const declared = new Set(env.getDefinitions().variables.map((v) => v.name));
|
|
42
|
+
if (!declared.has("now")) env.registerVariable("now", "Date", { description: "\u5F53\u524D\u65E5\u671F/\u65F6\u95F4" });
|
|
43
|
+
if (!declared.has("form")) env.registerVariable("form", "dyn", { description: "\u5F53\u524D\u8868\u5355\u72B6\u6001" });
|
|
44
|
+
}
|
|
40
45
|
export function FormConfig(configure) {
|
|
41
46
|
const formConfigure = (env) => {
|
|
42
|
-
env
|
|
43
|
-
env.registerVariable("form", "dyn", { description: "\u5F53\u524D\u8868\u5355\u72B6\u6001" });
|
|
47
|
+
registerFormVariablesIfAbsent(env);
|
|
44
48
|
configure(env);
|
|
45
49
|
};
|
|
46
50
|
const CelFormInitial = Expression({ configure: formConfigure, resultType: "dyn" });
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type CELContext } from '../../utils/cel-context.js';
|
|
1
2
|
import type { FieldValue, FormUnitValue } from './schema.js';
|
|
3
|
+
import type { Environment } from '../../vendor/cel-js/lib/index.js';
|
|
2
4
|
export type UnitExtra = {
|
|
3
5
|
id: string;
|
|
4
6
|
label: string;
|
|
@@ -17,6 +19,20 @@ type __VLS_Props = {
|
|
|
17
19
|
* v-model — hosts read which extra is active via `selection.value`.
|
|
18
20
|
*/
|
|
19
21
|
extras?: ReadonlyArray<UnitExtra>;
|
|
22
|
+
/**
|
|
23
|
+
* Host-registered CEL variables. Forwarded to the drilled field-list's
|
|
24
|
+
* "AI 生成字段" button so the model can advertise host variables in
|
|
25
|
+
* generated expressions. Absent → only `form` / `now` are in scope.
|
|
26
|
+
*/
|
|
27
|
+
configure?: (env: Environment) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Extra CEL variables in scope for the unit's *field* editors — the list
|
|
30
|
+
* field passes `item` (the current row). Advertised to every drilled field
|
|
31
|
+
* config's ExpressionEditor via `provideCELContext` below; the host's own
|
|
32
|
+
* `extras-pane` slot is excluded, since slotted content injects through the
|
|
33
|
+
* host component, above this provide.
|
|
34
|
+
*/
|
|
35
|
+
fieldCelScope?: CELContext;
|
|
20
36
|
};
|
|
21
37
|
type __VLS_Slots = {
|
|
22
38
|
/** Pane content for the currently-selected extra. Receives `{ id }`. */
|
|
@@ -6,7 +6,8 @@ import { computed, inject, onBeforeUnmount, provide, ref, useTemplateRef, watch
|
|
|
6
6
|
import { toast } from "vue-sonner";
|
|
7
7
|
import {
|
|
8
8
|
celBindings,
|
|
9
|
-
injectCELContext
|
|
9
|
+
injectCELContext,
|
|
10
|
+
provideCELContext
|
|
10
11
|
} from "../../utils/cel-context";
|
|
11
12
|
import { findFreePlacement, normalizeLayoutSet } from "../../share/layout";
|
|
12
13
|
import { getLocalizedText } from "../../share/locale";
|
|
@@ -20,13 +21,16 @@ import { Button } from "../ui/button";
|
|
|
20
21
|
import { ScrollArea } from "../ui/scroll-area";
|
|
21
22
|
import { Separator } from "../ui/separator";
|
|
22
23
|
import { FIELDS, findField } from "./utils/resolve";
|
|
24
|
+
import ShwfedFormAiFieldsButton from "./ai/fields-button.vue";
|
|
23
25
|
defineOptions({ name: "ShwfedFormUnitConfig" });
|
|
24
26
|
const unit = defineModel({ type: Object, ...{ required: true } });
|
|
25
27
|
const selection = defineModel("selection", { type: Object, ...{
|
|
26
28
|
default: () => ({ kind: "layout" })
|
|
27
29
|
} });
|
|
28
|
-
defineProps({
|
|
29
|
-
extras: { type: Array, required: false }
|
|
30
|
+
const props = defineProps({
|
|
31
|
+
extras: { type: Array, required: false },
|
|
32
|
+
configure: { type: Function, required: false },
|
|
33
|
+
fieldCelScope: { type: Object, required: false }
|
|
30
34
|
});
|
|
31
35
|
defineSlots();
|
|
32
36
|
const activeLayoutIndex = ref(0);
|
|
@@ -40,6 +44,7 @@ function popOne() {
|
|
|
40
44
|
const breadcrumbExt = inject(BREADCRUMB_EXTENSION_KEY, null);
|
|
41
45
|
const fullPane = ref(false);
|
|
42
46
|
provide(FORM_FIELD_LAYOUT_KEY, { fullPane });
|
|
47
|
+
provideCELContext(props.fieldCelScope ?? {});
|
|
43
48
|
const takeover = inject(SIDEBAR_TAKEOVER_KEY, null);
|
|
44
49
|
const takeoverTarget = computed(() => takeover?.target.value ?? null);
|
|
45
50
|
const activeFieldId = computed(() => {
|
|
@@ -171,6 +176,16 @@ function removeFields(ids) {
|
|
|
171
176
|
stack.value = stack.value.slice(0, -1);
|
|
172
177
|
}
|
|
173
178
|
}
|
|
179
|
+
function applyAiFields(generated) {
|
|
180
|
+
const valid = generated.filter(
|
|
181
|
+
(f) => !!findField(f.type, f.compatibilityDate)
|
|
182
|
+
);
|
|
183
|
+
if (valid.length === 0) return;
|
|
184
|
+
unit.value = {
|
|
185
|
+
...unit.value,
|
|
186
|
+
fields: [...unit.value.fields, ...valid]
|
|
187
|
+
};
|
|
188
|
+
}
|
|
174
189
|
function updateActiveField(next) {
|
|
175
190
|
const id = activeFieldId.value;
|
|
176
191
|
if (!id) return;
|
|
@@ -311,6 +326,12 @@ function isExtraActive(id) {
|
|
|
311
326
|
</button>
|
|
312
327
|
</div>
|
|
313
328
|
</ScrollArea>
|
|
329
|
+
|
|
330
|
+
<ShwfedFormAiFieldsButton
|
|
331
|
+
:configure="configure"
|
|
332
|
+
:fields="unit.fields"
|
|
333
|
+
@apply="applyAiFields"
|
|
334
|
+
/>
|
|
314
335
|
</template>
|
|
315
336
|
</Teleport>
|
|
316
337
|
|
|
@@ -388,6 +409,12 @@ function isExtraActive(id) {
|
|
|
388
409
|
</button>
|
|
389
410
|
</div>
|
|
390
411
|
</ScrollArea>
|
|
412
|
+
|
|
413
|
+
<ShwfedFormAiFieldsButton
|
|
414
|
+
:configure="configure"
|
|
415
|
+
:fields="unit.fields"
|
|
416
|
+
@apply="applyAiFields"
|
|
417
|
+
/>
|
|
391
418
|
</template>
|
|
392
419
|
</div>
|
|
393
420
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { type CELContext } from '../../utils/cel-context.js';
|
|
1
2
|
import type { FieldValue, FormUnitValue } from './schema.js';
|
|
3
|
+
import type { Environment } from '../../vendor/cel-js/lib/index.js';
|
|
2
4
|
export type UnitExtra = {
|
|
3
5
|
id: string;
|
|
4
6
|
label: string;
|
|
@@ -17,6 +19,20 @@ type __VLS_Props = {
|
|
|
17
19
|
* v-model — hosts read which extra is active via `selection.value`.
|
|
18
20
|
*/
|
|
19
21
|
extras?: ReadonlyArray<UnitExtra>;
|
|
22
|
+
/**
|
|
23
|
+
* Host-registered CEL variables. Forwarded to the drilled field-list's
|
|
24
|
+
* "AI 生成字段" button so the model can advertise host variables in
|
|
25
|
+
* generated expressions. Absent → only `form` / `now` are in scope.
|
|
26
|
+
*/
|
|
27
|
+
configure?: (env: Environment) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Extra CEL variables in scope for the unit's *field* editors — the list
|
|
30
|
+
* field passes `item` (the current row). Advertised to every drilled field
|
|
31
|
+
* config's ExpressionEditor via `provideCELContext` below; the host's own
|
|
32
|
+
* `extras-pane` slot is excluded, since slotted content injects through the
|
|
33
|
+
* host component, above this provide.
|
|
34
|
+
*/
|
|
35
|
+
fieldCelScope?: CELContext;
|
|
20
36
|
};
|
|
21
37
|
type __VLS_Slots = {
|
|
22
38
|
/** Pane content for the currently-selected extra. Receives `{ id }`. */
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Environment } from '../../../vendor/cel-js/lib/index.js';
|
|
2
|
+
import type { CELContext } from '../../../utils/cel-context.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds the CEL variable scope advertised to a form's expression editors and
|
|
5
|
+
* AI assists. Seeds the form-wide `form` / `now` live variables, then probes a
|
|
6
|
+
* throwaway `Environment` with the host's `configure` to surface any
|
|
7
|
+
* host-registered variables (their type / label / description).
|
|
8
|
+
*
|
|
9
|
+
* Values are left `undefined` — this scope is for designer-time advertising
|
|
10
|
+
* and prompt rendering, never evaluation. Extracted from `form/config.vue` so
|
|
11
|
+
* the field-list AI button can reuse the exact same scope.
|
|
12
|
+
*/
|
|
13
|
+
export declare function buildFormCelScope(configure: (env: Environment) => void): CELContext;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Environment } from "../../../vendor/cel-js/lib/index.js";
|
|
2
|
+
export function buildFormCelScope(configure) {
|
|
3
|
+
const probe = new Environment({ unlistedVariablesAreDyn: false });
|
|
4
|
+
const baseline = new Set(probe.getDefinitions().variables.map((v) => v.name));
|
|
5
|
+
configure(probe);
|
|
6
|
+
const out = {
|
|
7
|
+
form: {
|
|
8
|
+
type: "dyn",
|
|
9
|
+
label: "\u8868\u5355\u503C",
|
|
10
|
+
description: "\u5F53\u524D\u8868\u5355\u72B6\u6001",
|
|
11
|
+
value: void 0
|
|
12
|
+
},
|
|
13
|
+
// Mirrors the runtime `now` binding in `form/index.vue`. Editors and
|
|
14
|
+
// prompts never evaluate, so we leave `value` undefined.
|
|
15
|
+
now: {
|
|
16
|
+
type: "Date",
|
|
17
|
+
label: "now",
|
|
18
|
+
description: "\u5F53\u524D\u65E5\u671F/\u65F6\u95F4",
|
|
19
|
+
value: void 0
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
for (const v of probe.getDefinitions().variables) {
|
|
23
|
+
if (baseline.has(v.name)) continue;
|
|
24
|
+
out[v.name] = {
|
|
25
|
+
type: v.type,
|
|
26
|
+
label: v.label ?? v.name,
|
|
27
|
+
description: v.description ?? void 0,
|
|
28
|
+
value: void 0
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
@@ -2,4 +2,17 @@ import { type Schema } from 'effect';
|
|
|
2
2
|
type AnySchema = Schema.Schema<any, any, any>;
|
|
3
3
|
export declare function getStructFieldTitle(schema: AnySchema, fieldName: string): string | undefined;
|
|
4
4
|
export declare function getStructFieldDescription(schema: AnySchema, fieldName: string): string | undefined;
|
|
5
|
+
export type StructFieldInfo = {
|
|
6
|
+
name: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
optional: boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Lists a struct schema's property signatures, pairing each with its `@title` /
|
|
13
|
+
* `@description` annotation and optionality. Used to auto-derive the per-field
|
|
14
|
+
* documentation surfaced to the AI field generator — a new field folder becomes
|
|
15
|
+
* discoverable with zero edits there. Returns `[]` for non-struct schemas.
|
|
16
|
+
*/
|
|
17
|
+
export declare function listStructFields(schema: AnySchema): StructFieldInfo[];
|
|
5
18
|
export {};
|
|
@@ -46,3 +46,18 @@ export function getStructFieldTitle(schema, fieldName) {
|
|
|
46
46
|
export function getStructFieldDescription(schema, fieldName) {
|
|
47
47
|
return getFieldAnnotation(schema, fieldName, SchemaAST.getDescriptionAnnotation);
|
|
48
48
|
}
|
|
49
|
+
export function listStructFields(schema) {
|
|
50
|
+
const tl = getTypeLiteral(schema.ast);
|
|
51
|
+
if (!tl) return [];
|
|
52
|
+
return tl.propertySignatures.map((ps) => {
|
|
53
|
+
const name = String(ps.name);
|
|
54
|
+
const title = getStructFieldTitle(schema, name);
|
|
55
|
+
const description = getStructFieldDescription(schema, name);
|
|
56
|
+
return {
|
|
57
|
+
name,
|
|
58
|
+
...title !== void 0 ? { title } : {},
|
|
59
|
+
...description !== void 0 ? { description } : {},
|
|
60
|
+
optional: ps.isOptional
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
}
|
|
@@ -4,7 +4,7 @@ The `columns` array is a **full replacement**: whatever you emit becomes the ent
|
|
|
4
4
|
|
|
5
5
|
# Hard rules
|
|
6
6
|
|
|
7
|
-
1. **NEVER**
|
|
7
|
+
1. **NEVER add** a new column with `type === "com.shwfed.table.column.actions"`. Action columns are wired by a separate registry — you cannot synthesize one, and a fabricated actions column will be rejected and you will be retried. The **one exception**: if the current configuration already contains an actions column, echo it back verbatim (same `id`, `type`, `compatibilityDate`, and all fields) in its render position. Never invent an actions column the user merely describes when none already exists.
|
|
8
8
|
2. **ONLY** emit a column with `type === "com.shwfed.table.column.markdown"` when the user **explicitly** asks for markdown / rich content / formatted body / 富文本 / Markdown. Otherwise prefer `com.shwfed.table.column.text`.
|
|
9
9
|
3. **NEVER** set `enableSorting: true`. Client-side sorting is rarely the right call in practice (paginated/server-driven data, mixed types, etc.) and the user should opt in manually. Omit the field or set it to `false`.
|
|
10
10
|
4. **NEVER** wrap the `accessor` in type coercions (`string(...)`, `int(...)`, `double(...)`) and **NEVER** supply a default via `.orValue(...)`. Emit the bare optional access — e.g. `row.?name`, `row.?age`. Coercion and defaults make it impossible for the user to distinguish real data from filler, which is actively harmful.
|
|
@@ -41,4 +41,13 @@ Always use optional access (`row.?field`) rather than `row.field` to avoid runti
|
|
|
41
41
|
|
|
42
42
|
If the user gives a sample response, derive accessors directly from its shape. Otherwise, infer field names from the user's prose (e.g. "show name and email" → `row.?name`, `row.?email`).
|
|
43
43
|
|
|
44
|
+
# Rendering recipes
|
|
45
|
+
|
|
46
|
+
**Boolean / `0`-`1` values.** When a field is a boolean or a `0`/`1` flag (e.g. `enabled`, `isActive`, a `0/1` status), render it with `com.shwfed.table.column.icon` rather than `text` — a green check / red cross reads far faster than the literal `true` or `1`. Build both the `accessor` and the `color` as CEL ternaries on the condition:
|
|
47
|
+
|
|
48
|
+
- `accessor`: `<condition> ? 'fluent:checkmark-20-filled' : 'fluent:dismiss-20-filled'`
|
|
49
|
+
- `color`: `<condition> ? '#00c950' : '#fb2c36'`
|
|
50
|
+
|
|
51
|
+
where `<condition>` is a bare optional-access test — `row.?enabled == true` for a boolean field, `row.?status == 1` for a `0/1` field. This is a comparison, not a coercion, so hard rule 4 still holds: do not wrap in `string(...)`/`int(...)` or add `.orValue(...)`.
|
|
52
|
+
|
|
44
53
|
The current configuration (existing data source + columns) is appended to the user prompt under `Current configuration`. Use it only to (a) reuse existing column `id`s where the user's described column clearly corresponds to an existing one, and (b) inform field shapes (e.g. response keys). Do **not** copy columns forward that the user did not describe — the user's prompt is the source of truth for which columns should exist.
|
package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.markdown/config.vue
CHANGED
|
@@ -154,8 +154,8 @@ const copyExpressionModel = computed({
|
|
|
154
154
|
</FieldLabel>
|
|
155
155
|
<ExpressionEditor
|
|
156
156
|
v-model="copyExpressionModel"
|
|
157
|
-
placeholder="如 row.name"
|
|
158
|
-
result-type="string"
|
|
157
|
+
placeholder="如 row.name 或 row.?name"
|
|
158
|
+
:result-type="['string', 'optional']"
|
|
159
159
|
class="min-h-10"
|
|
160
160
|
/>
|
|
161
161
|
</Field>
|
|
@@ -24,12 +24,22 @@ const rendered = computed(() => {
|
|
|
24
24
|
const isEmpty = computed(() => rendered.value.trim().length === 0);
|
|
25
25
|
const align = computed(() => isEmpty.value ? "center" : props.column.align ?? "left");
|
|
26
26
|
const justifyClass = computed(() => JUSTIFY_CLASS[align.value] ?? JUSTIFY_CLASS.left);
|
|
27
|
-
const
|
|
27
|
+
const copyText = computed(() => {
|
|
28
|
+
const { copyExpression } = props.column;
|
|
29
|
+
if (!copyExpression) return void 0;
|
|
30
|
+
try {
|
|
31
|
+
const value = Effect.runSync($cel(copyExpression, celBindings(celContext)));
|
|
32
|
+
return value === void 0 || value === null ? void 0 : String(value);
|
|
33
|
+
} catch (e) {
|
|
34
|
+
console.error("[shwfed-table] markdown copy expression failed", e);
|
|
35
|
+
return void 0;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
const showCopy = computed(() => copyText.value !== void 0 && !isEmpty.value);
|
|
28
39
|
const onCopy = async () => {
|
|
29
|
-
const
|
|
30
|
-
if (
|
|
40
|
+
const text = copyText.value;
|
|
41
|
+
if (text === void 0) return;
|
|
31
42
|
try {
|
|
32
|
-
const text = String(Effect.runSync($cel(column.copyExpression, celBindings(celContext))));
|
|
33
43
|
await navigator.clipboard.writeText(text);
|
|
34
44
|
} catch (e) {
|
|
35
45
|
console.error("[shwfed-table] markdown copy failed", e);
|
package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.markdown/schema.js
CHANGED
|
@@ -7,8 +7,9 @@ export const metadata = {
|
|
|
7
7
|
name: "MD",
|
|
8
8
|
icon: "fluent:markdown-20-regular"
|
|
9
9
|
};
|
|
10
|
+
const isCopyExpressionType = (t) => t === "string" || t === "dyn" || t.startsWith("optional");
|
|
10
11
|
export function schema(configure) {
|
|
11
|
-
const cel = CelRowAccess(configure, { resultType:
|
|
12
|
+
const cel = CelRowAccess(configure, { resultType: isCopyExpressionType });
|
|
12
13
|
const localeMarkdown = LocaleMarkdownWithRow(configure);
|
|
13
14
|
return Schema.Struct({
|
|
14
15
|
type: Schema.Literal(type),
|
|
@@ -34,7 +35,7 @@ export function schema(configure) {
|
|
|
34
35
|
}),
|
|
35
36
|
copyExpression: Schema.optional(cel.annotations({
|
|
36
37
|
title: "\u590D\u5236\u8868\u8FBE\u5F0F",
|
|
37
|
-
description: "\u590D\u5236\u503C\u7684 CEL \u8868\u8FBE\u5F0F"
|
|
38
|
+
description: "\u590D\u5236\u503C\u7684 CEL \u8868\u8FBE\u5F0F\uFF1B\u53EF\u8FD4\u56DE optional\uFF08\u5982 `row.?field`\uFF09\uFF0C\u4E3A\u7A7A\u65F6\u4E0D\u663E\u793A\u590D\u5236\u6309\u94AE"
|
|
38
39
|
}))
|
|
39
40
|
}).annotations({ title: "MarkdownRenderer", description: "MD" });
|
|
40
41
|
}
|
package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.text/config.vue
CHANGED
|
@@ -188,8 +188,8 @@ const copyExpressionModel = computed({
|
|
|
188
188
|
</FieldLabel>
|
|
189
189
|
<ExpressionEditor
|
|
190
190
|
v-model="copyExpressionModel"
|
|
191
|
-
placeholder="如 row.name"
|
|
192
|
-
result-type="string"
|
|
191
|
+
placeholder="如 row.name 或 row.?name"
|
|
192
|
+
:result-type="['string', 'optional']"
|
|
193
193
|
class="min-h-10"
|
|
194
194
|
/>
|
|
195
195
|
</Field>
|
package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.text/runtime.vue
CHANGED
|
@@ -16,12 +16,22 @@ const rawValue = computed(() => props.ctx.cell.getValue());
|
|
|
16
16
|
const isMissing = computed(() => rawValue.value === void 0 || rawValue.value === null);
|
|
17
17
|
const align = computed(() => isMissing.value ? "center" : props.column.align ?? "left");
|
|
18
18
|
const justifyClass = computed(() => JUSTIFY_CLASS[align.value] ?? JUSTIFY_CLASS.left);
|
|
19
|
-
const
|
|
19
|
+
const copyText = computed(() => {
|
|
20
|
+
const { copyExpression } = props.column;
|
|
21
|
+
if (!copyExpression) return void 0;
|
|
22
|
+
try {
|
|
23
|
+
const value = Effect.runSync($cel(copyExpression, celBindings(celContext)));
|
|
24
|
+
return value === void 0 || value === null ? void 0 : String(value);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error("[shwfed-table] text copy expression failed", e);
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const showCopy = computed(() => copyText.value !== void 0 && !isMissing.value && rawValue.value !== "");
|
|
20
31
|
const onCopy = async () => {
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
32
|
+
const text = copyText.value;
|
|
33
|
+
if (text === void 0) return;
|
|
23
34
|
try {
|
|
24
|
-
const text = String(Effect.runSync($cel(column.copyExpression, celBindings(celContext))));
|
|
25
35
|
await navigator.clipboard.writeText(text);
|
|
26
36
|
} catch (e) {
|
|
27
37
|
console.error("[shwfed-table] text copy failed", e);
|
package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.text/schema.js
CHANGED
|
@@ -6,8 +6,9 @@ export const metadata = {
|
|
|
6
6
|
name: "\u6587\u672C",
|
|
7
7
|
icon: "fluent:text-field-20-regular"
|
|
8
8
|
};
|
|
9
|
+
const isCopyExpressionType = (t) => t === "string" || t === "dyn" || t.startsWith("optional");
|
|
9
10
|
export function schema(configure) {
|
|
10
|
-
const cel = CelRowAccess(configure, { resultType:
|
|
11
|
+
const cel = CelRowAccess(configure, { resultType: isCopyExpressionType });
|
|
11
12
|
return Schema.Struct({
|
|
12
13
|
type: Schema.Literal(type),
|
|
13
14
|
compatibilityDate: Schema.Literal(compatibilityDate),
|
|
@@ -22,7 +23,7 @@ export function schema(configure) {
|
|
|
22
23
|
),
|
|
23
24
|
copyExpression: Schema.optional(cel.annotations({
|
|
24
25
|
title: "\u590D\u5236\u8868\u8FBE\u5F0F",
|
|
25
|
-
description: "\u590D\u5236\u503C\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u5B58\u5728\u65F6\u542F\u7528\u590D\u5236\u529F\u80FD"
|
|
26
|
+
description: "\u590D\u5236\u503C\u7684 CEL \u8868\u8FBE\u5F0F\uFF0C\u5B58\u5728\u65F6\u542F\u7528\u590D\u5236\u529F\u80FD\uFF1B\u53EF\u8FD4\u56DE optional\uFF08\u5982 `row.?field`\uFF09\uFF0C\u4E3A\u7A7A\u65F6\u4E0D\u663E\u793A\u590D\u5236\u6309\u94AE"
|
|
26
27
|
}))
|
|
27
28
|
}).annotations({ title: "TextRenderer", description: "\u6587\u672C\u6E32\u67D3\u5668" });
|
|
28
29
|
}
|
|
@@ -7,10 +7,20 @@ type __VLS_ModelProps = {
|
|
|
7
7
|
modelValue: AnyRecord;
|
|
8
8
|
};
|
|
9
9
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
10
|
-
declare
|
|
10
|
+
declare var __VLS_201: {};
|
|
11
|
+
type __VLS_Slots = {} & {
|
|
12
|
+
'general-extra'?: (props: typeof __VLS_201) => any;
|
|
13
|
+
};
|
|
14
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
11
15
|
"update:modelValue": (value: AnyRecord) => any;
|
|
12
16
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
13
17
|
"onUpdate:modelValue"?: ((value: AnyRecord) => any) | undefined;
|
|
14
18
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
15
20
|
declare const _default: typeof __VLS_export;
|
|
16
21
|
export default _default;
|
|
22
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
23
|
+
new (): {
|
|
24
|
+
$slots: S;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
@@ -1251,6 +1251,10 @@ const tableQueryValue = computed({
|
|
|
1251
1251
|
</InputGroup>
|
|
1252
1252
|
</Field>
|
|
1253
1253
|
|
|
1254
|
+
<!-- Host extension slot: a wrapping editor (e.g. the `table` form
|
|
1255
|
+
field config) injects its own fields into the general pane. -->
|
|
1256
|
+
<slot name="general-extra" />
|
|
1257
|
+
|
|
1254
1258
|
<div class="flex items-center gap-2">
|
|
1255
1259
|
<h3 class="text-xs font-medium text-zinc-500">
|
|
1256
1260
|
{{ generalFieldTitle("dataSource") }}
|
|
@@ -7,10 +7,20 @@ type __VLS_ModelProps = {
|
|
|
7
7
|
modelValue: AnyRecord;
|
|
8
8
|
};
|
|
9
9
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
10
|
-
declare
|
|
10
|
+
declare var __VLS_201: {};
|
|
11
|
+
type __VLS_Slots = {} & {
|
|
12
|
+
'general-extra'?: (props: typeof __VLS_201) => any;
|
|
13
|
+
};
|
|
14
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
11
15
|
"update:modelValue": (value: AnyRecord) => any;
|
|
12
16
|
}, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
13
17
|
"onUpdate:modelValue"?: ((value: AnyRecord) => any) | undefined;
|
|
14
18
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
19
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
15
20
|
declare const _default: typeof __VLS_export;
|
|
16
21
|
export default _default;
|
|
22
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
23
|
+
new (): {
|
|
24
|
+
$slots: S;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
@@ -12,6 +12,7 @@ declare const _default: typeof __VLS_export;
|
|
|
12
12
|
export default _default;
|
|
13
13
|
declare const __VLS_export: import("vue").DefineComponent<{
|
|
14
14
|
config?: TableConfigValue;
|
|
15
|
+
rows?: Array<unknown>;
|
|
15
16
|
}, import("@tanstack/vue-table").Table<unknown>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
16
17
|
"update:config": (value: Readonly<{
|
|
17
18
|
kind: "shwfed.component.table";
|
|
@@ -52,8 +53,10 @@ declare const __VLS_export: import("vue").DefineComponent<{
|
|
|
52
53
|
rowSelection: import("effect/Schema").optional<import("effect/Schema").Record$<typeof import("effect/Schema").String, typeof import("effect/Schema").Boolean>>;
|
|
53
54
|
}>>;
|
|
54
55
|
}> | undefined) => any;
|
|
56
|
+
"update:rows": (value: unknown[]) => any;
|
|
55
57
|
}, string, import("vue").PublicProps, Readonly<{
|
|
56
58
|
config?: TableConfigValue;
|
|
59
|
+
rows?: Array<unknown>;
|
|
57
60
|
}> & Readonly<{
|
|
58
61
|
"onUpdate:config"?: ((value: Readonly<{
|
|
59
62
|
kind: "shwfed.component.table";
|
|
@@ -94,4 +97,5 @@ declare const __VLS_export: import("vue").DefineComponent<{
|
|
|
94
97
|
rowSelection: import("effect/Schema").optional<import("effect/Schema").Record$<typeof import("effect/Schema").String, typeof import("effect/Schema").Boolean>>;
|
|
95
98
|
}>>;
|
|
96
99
|
}> | undefined) => any) | undefined;
|
|
100
|
+
"onUpdate:rows"?: ((value: unknown[]) => any) | undefined;
|
|
97
101
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -13,7 +13,7 @@ import { useVirtualizer } from "@tanstack/vue-virtual";
|
|
|
13
13
|
import { Effect, Fiber, Option } from "effect";
|
|
14
14
|
import { Fetch } from "fx-fetch";
|
|
15
15
|
import { Pagination } from "reka-ui/namespaced";
|
|
16
|
-
import { computed, defineComponent, h, onMounted, ref, watch } from "vue";
|
|
16
|
+
import { computed, defineComponent, h, onMounted, ref, toRaw, watch } from "vue";
|
|
17
17
|
import { useI18n } from "vue-i18n";
|
|
18
18
|
import { celBindings, provideCELContext, injectCELContext } from "../../utils/cel-context";
|
|
19
19
|
import { getLocalizedText } from "../../share/locale";
|
|
@@ -29,7 +29,7 @@ import { provideEventTarget } from "../../share/event-bus";
|
|
|
29
29
|
import { findColumn } from "./utils/resolve";
|
|
30
30
|
import { interpolateMarkdown } from "./utils/runtime";
|
|
31
31
|
const config = defineModel("config", { type: Object });
|
|
32
|
-
const rowData =
|
|
32
|
+
const rowData = defineModel("rows", { type: Array, ...{ default: () => [] } });
|
|
33
33
|
const serverTotal = ref(void 0);
|
|
34
34
|
const isFetching = ref(false);
|
|
35
35
|
const queryState = ref({});
|
|
@@ -301,6 +301,14 @@ async function resetQuery() {
|
|
|
301
301
|
queryState.value = {};
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
|
+
function cloneRow(row) {
|
|
305
|
+
if (!row || typeof row !== "object") return row;
|
|
306
|
+
try {
|
|
307
|
+
return structuredClone(toRaw(row));
|
|
308
|
+
} catch {
|
|
309
|
+
return { ...row };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
304
312
|
const tableInstanceId = config.value?.id ?? crypto.randomUUID();
|
|
305
313
|
provideTableInstanceId(tableInstanceId);
|
|
306
314
|
provideEventTarget(tableInstanceId, {
|
|
@@ -312,6 +320,24 @@ provideEventTarget(tableInstanceId, {
|
|
|
312
320
|
"search": () => Effect.promise(fetchDataSource),
|
|
313
321
|
"clear-selection": () => Effect.sync(() => {
|
|
314
322
|
tableApi.resetRowSelection(true);
|
|
323
|
+
}),
|
|
324
|
+
// Local row edits — mutate `rowData` in place, no server round-trip. Each
|
|
325
|
+
// re-assigns the array so the `rows` model re-emits and a host (the
|
|
326
|
+
// `table` form field) sees the live list. Intended for client-side data:
|
|
327
|
+
// with server pagination (`dataSource.total`) `rowData` is only the
|
|
328
|
+
// current page, so a fetch will overwrite these edits.
|
|
329
|
+
"add-row": () => Effect.sync(() => {
|
|
330
|
+
rowData.value = [...rowData.value, {}];
|
|
331
|
+
}),
|
|
332
|
+
"duplicate-selected": () => Effect.sync(() => {
|
|
333
|
+
const clones = tableApi.getSelectedRowModel().rows.map((r) => cloneRow(r.original));
|
|
334
|
+
if (clones.length > 0) rowData.value = [...rowData.value, ...clones];
|
|
335
|
+
}),
|
|
336
|
+
"delete-selected": () => Effect.sync(() => {
|
|
337
|
+
const drop = new Set(tableApi.getSelectedRowModel().rows.map((r) => r.index));
|
|
338
|
+
if (drop.size === 0) return;
|
|
339
|
+
rowData.value = rowData.value.filter((_, i) => !drop.has(i));
|
|
340
|
+
tableApi.resetRowSelection(true);
|
|
315
341
|
})
|
|
316
342
|
});
|
|
317
343
|
const queryRef = ref(null);
|
|
@@ -12,6 +12,7 @@ declare const _default: typeof __VLS_export;
|
|
|
12
12
|
export default _default;
|
|
13
13
|
declare const __VLS_export: import("vue").DefineComponent<{
|
|
14
14
|
config?: TableConfigValue;
|
|
15
|
+
rows?: Array<unknown>;
|
|
15
16
|
}, import("@tanstack/vue-table").Table<unknown>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
16
17
|
"update:config": (value: Readonly<{
|
|
17
18
|
kind: "shwfed.component.table";
|
|
@@ -52,8 +53,10 @@ declare const __VLS_export: import("vue").DefineComponent<{
|
|
|
52
53
|
rowSelection: import("effect/Schema").optional<import("effect/Schema").Record$<typeof import("effect/Schema").String, typeof import("effect/Schema").Boolean>>;
|
|
53
54
|
}>>;
|
|
54
55
|
}> | undefined) => any;
|
|
56
|
+
"update:rows": (value: unknown[]) => any;
|
|
55
57
|
}, string, import("vue").PublicProps, Readonly<{
|
|
56
58
|
config?: TableConfigValue;
|
|
59
|
+
rows?: Array<unknown>;
|
|
57
60
|
}> & Readonly<{
|
|
58
61
|
"onUpdate:config"?: ((value: Readonly<{
|
|
59
62
|
kind: "shwfed.component.table";
|
|
@@ -94,4 +97,5 @@ declare const __VLS_export: import("vue").DefineComponent<{
|
|
|
94
97
|
rowSelection: import("effect/Schema").optional<import("effect/Schema").Record$<typeof import("effect/Schema").String, typeof import("effect/Schema").Boolean>>;
|
|
95
98
|
}>>;
|
|
96
99
|
}> | undefined) => any) | undefined;
|
|
100
|
+
"onUpdate:rows"?: ((value: unknown[]) => any) | undefined;
|
|
97
101
|
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|