@shwfed/config 3.0.3 → 3.0.6
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/actions/buttons/2026-04-21/com.shwfed.actions.button.navigation/runtime.vue +4 -10
- package/dist/runtime/components/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/schema.d.ts +1 -1
- package/dist/runtime/components/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/schema.js +2 -2
- package/dist/runtime/components/actions/buttons/2026-07-13/com.shwfed.actions.button.state.write/runtime.vue +22 -2
- package/dist/runtime/components/actions/buttons/2026-07-13/com.shwfed.actions.button.state.write/schema.d.ts +4 -2
- package/dist/runtime/components/actions/buttons/2026-07-13/com.shwfed.actions.button.state.write/schema.js +1 -1
- package/dist/runtime/components/actions/utils/steps.d.ts +1 -1
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.d.vue.ts +6 -6
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.vue.d.ts +6 -6
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.d.vue.ts +6 -6
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.vue.d.ts +6 -6
- package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/schema.d.ts +3 -3
- package/dist/runtime/components/config/index.vue +25 -0
- package/dist/runtime/components/config/prune-dialog.d.vue.ts +14 -0
- package/dist/runtime/components/config/prune-dialog.vue +72 -0
- package/dist/runtime/components/config/prune-dialog.vue.d.ts +14 -0
- package/dist/runtime/components/config/schema.js +2 -2
- package/dist/runtime/components/config/use-editor.d.ts +9 -0
- package/dist/runtime/components/config/use-editor.js +37 -9
- package/dist/runtime/components/config/utils/linkage.d.ts +8 -0
- package/dist/runtime/components/config/utils/linkage.js +7 -2
- package/dist/runtime/components/config/utils/prune.d.ts +68 -0
- package/dist/runtime/components/config/utils/prune.js +92 -0
- package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/config.d.vue.ts +31 -0
- package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/config.vue +191 -0
- package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/config.vue.d.ts +31 -0
- package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/expand.d.ts +49 -0
- package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/expand.js +87 -0
- package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/runtime.d.vue.ts +8 -0
- package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/runtime.vue +72 -0
- package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/runtime.vue.d.ts +8 -0
- package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/schema.d.ts +59 -0
- package/dist/runtime/components/form/fields/2026-07-13/com.shwfed.form.field.dynamic/schema.js +139 -0
- package/dist/runtime/components/form/utils/form-vars.d.ts +5 -15
- package/dist/runtime/components/form/utils/form-vars.js +12 -6
- package/dist/runtime/components/operations/2026-06-11/com.shwfed.operation.http.request.json/schema.js +2 -2
- package/dist/runtime/components/operations/2026-06-15/com.shwfed.operation.http.download/schema.js +2 -2
- package/dist/runtime/components/operations/2026-06-15/com.shwfed.operation.navigation/runtime.js +5 -11
- package/dist/runtime/components/operations/2026-06-15/com.shwfed.operation.navigation/schema.js +1 -1
- package/dist/runtime/components/operations/utils/resolve.d.ts +1 -1
- package/dist/runtime/components/operations/utils/step-vars.d.ts +4 -2
- package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.select/runtime.vue +1 -21
- package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.select/schema.d.ts +2 -0
- package/dist/runtime/components/table/columns/2026-04-14/com.shwfed.table.column.select/schema.js +16 -1
- package/dist/runtime/components/table/index.vue +15 -0
- package/dist/runtime/components/table/schema.d.ts +6 -6
- package/dist/runtime/components/table/utils/resolve.d.ts +26 -0
- package/dist/runtime/components/table/utils/resolve.js +2 -1
- package/dist/runtime/components/ui/expression-editor/picker-entries.js +1 -1
- package/dist/runtime/components/ui/expression-editor/step-chip-extension.js +1 -1
- package/dist/runtime/components/ui/expression-editor/step-refs.js +1 -1
- package/dist/runtime/share/event-bus.js +2 -2
- package/dist/runtime/share/navigation.d.ts +1 -0
- package/dist/runtime/share/navigation.js +4 -0
- package/dist/runtime/utils/cel-context.d.ts +2 -2
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { Effect } from "effect";
|
|
3
3
|
import { navigateTo, useRouter } from "#app";
|
|
4
|
+
import { isAbsoluteUrl } from "../../../../../share/navigation";
|
|
4
5
|
import { cel as _rawCel } from "../../../../../utils/cel";
|
|
5
6
|
import { celBindings, injectCELContext } from "../../../../../utils/cel-context";
|
|
6
7
|
import ShwfedActionDefinition from "../../../components/definition.vue";
|
|
@@ -12,23 +13,16 @@ const props = defineProps({
|
|
|
12
13
|
const router = useRouter();
|
|
13
14
|
const inherited = injectCELContext();
|
|
14
15
|
const $cel = (expression, context) => _rawCel(expression, { ...celBindings(inherited), ...context });
|
|
15
|
-
function isExternal(url) {
|
|
16
|
-
try {
|
|
17
|
-
return new URL(url, window.location.href).origin !== window.location.origin;
|
|
18
|
-
} catch {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
16
|
const effect = Effect.suspend(
|
|
23
17
|
() => Effect.flatMap(
|
|
24
18
|
$cel(props.config.url),
|
|
25
19
|
(url) => Effect.promise(async () => {
|
|
26
|
-
const
|
|
20
|
+
const absolute = isAbsoluteUrl(url);
|
|
27
21
|
if (props.config.mode === "_blank") {
|
|
28
|
-
window.open(
|
|
22
|
+
window.open(absolute ? url : router.resolve(url).href, "_blank");
|
|
29
23
|
return;
|
|
30
24
|
}
|
|
31
|
-
await navigateTo(url, { external });
|
|
25
|
+
await navigateTo(url, { external: absolute });
|
|
32
26
|
})
|
|
33
27
|
)
|
|
34
28
|
);
|
|
@@ -5,7 +5,7 @@ export declare const compatibilityDate: "2026-05-21";
|
|
|
5
5
|
export declare const metadata: {
|
|
6
6
|
readonly name: "下载文件";
|
|
7
7
|
readonly icon: "fluent:arrow-download-20-regular";
|
|
8
|
-
readonly deprecated: "本按钮已废弃:改用「触发事件」按钮,其触发列表串联页面的「下载文件」操作;原两步式(先取凭据再下载)现为一个「发送请求」行后接一个引用 `steps[
|
|
8
|
+
readonly deprecated: "本按钮已废弃:改用「触发事件」按钮,其触发列表串联页面的「下载文件」操作;原两步式(先取凭据再下载)现为一个「发送请求」行后接一个引用 `steps[?\"<步骤标识>\"]` 的「下载文件」行。";
|
|
9
9
|
};
|
|
10
10
|
export declare const JSON_VAR: {
|
|
11
11
|
readonly type: "dyn";
|
|
@@ -9,8 +9,8 @@ export const metadata = {
|
|
|
9
9
|
icon: "fluent:arrow-download-20-regular",
|
|
10
10
|
// No structured migrate target — designers rebuild downloads by hand. Use a
|
|
11
11
|
// 触发事件 按钮,其触发列表串联页面的「下载文件」操作即可替代本按钮;原来的两步式
|
|
12
|
-
// (先取凭据再下载)现为一个「发送请求」行后接一个引用 `steps["<步骤标识>"]` 的「下载文件」行。
|
|
13
|
-
deprecated: '\u672C\u6309\u94AE\u5DF2\u5E9F\u5F03\uFF1A\u6539\u7528\u300C\u89E6\u53D1\u4E8B\u4EF6\u300D\u6309\u94AE\uFF0C\u5176\u89E6\u53D1\u5217\u8868\u4E32\u8054\u9875\u9762\u7684\u300C\u4E0B\u8F7D\u6587\u4EF6\u300D\u64CD\u4F5C\uFF1B\u539F\u4E24\u6B65\u5F0F\uFF08\u5148\u53D6\u51ED\u636E\u518D\u4E0B\u8F7D\uFF09\u73B0\u4E3A\u4E00\u4E2A\u300C\u53D1\u9001\u8BF7\u6C42\u300D\u884C\u540E\u63A5\u4E00\u4E2A\u5F15\u7528 `steps["<\u6B65\u9AA4\u6807\u8BC6>"]` \u7684\u300C\u4E0B\u8F7D\u6587\u4EF6\u300D\u884C\u3002'
|
|
12
|
+
// (先取凭据再下载)现为一个「发送请求」行后接一个引用 `steps[?"<步骤标识>"]` 的「下载文件」行。
|
|
13
|
+
deprecated: '\u672C\u6309\u94AE\u5DF2\u5E9F\u5F03\uFF1A\u6539\u7528\u300C\u89E6\u53D1\u4E8B\u4EF6\u300D\u6309\u94AE\uFF0C\u5176\u89E6\u53D1\u5217\u8868\u4E32\u8054\u9875\u9762\u7684\u300C\u4E0B\u8F7D\u6587\u4EF6\u300D\u64CD\u4F5C\uFF1B\u539F\u4E24\u6B65\u5F0F\uFF08\u5148\u53D6\u51ED\u636E\u518D\u4E0B\u8F7D\uFF09\u73B0\u4E3A\u4E00\u4E2A\u300C\u53D1\u9001\u8BF7\u6C42\u300D\u884C\u540E\u63A5\u4E00\u4E2A\u5F15\u7528 `steps[?"<\u6B65\u9AA4\u6807\u8BC6>"]` \u7684\u300C\u4E0B\u8F7D\u6587\u4EF6\u300D\u884C\u3002'
|
|
14
14
|
};
|
|
15
15
|
export const JSON_VAR = {
|
|
16
16
|
type: "dyn",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { Effect } from "effect";
|
|
2
|
+
import { Cause, Effect } from "effect";
|
|
3
3
|
import { createDefu } from "defu";
|
|
4
4
|
import { cel as _rawCel } from "../../../../../utils/cel";
|
|
5
5
|
import { celBindings, injectCELContext } from "../../../../../utils/cel-context";
|
|
@@ -51,7 +51,27 @@ const effect = Effect.suspend(() => {
|
|
|
51
51
|
return Effect.map($cel(write.value, { steps }), (result) => {
|
|
52
52
|
const merged = mergeAtPath(formState.getAt(path), result);
|
|
53
53
|
formState.setAt(path, merged);
|
|
54
|
-
})
|
|
54
|
+
}).pipe(
|
|
55
|
+
// A row that blows up here — an expression that parses but throws on
|
|
56
|
+
// this click's data (`steps[?"r1"].json.rate` when the response was
|
|
57
|
+
// null), a path dot-prop cannot descend — is a *config* bug, not a
|
|
58
|
+
// condition the end user can act on. So: no toast, but never silent
|
|
59
|
+
// either. The failure otherwise reaches only the action runner's
|
|
60
|
+
// `catch`, whose `console.error` is `import.meta.dev`-gated — and the
|
|
61
|
+
// designer configures inside the running (production-built) app, so
|
|
62
|
+
// that log is one they can never see. Log ungated, like every other
|
|
63
|
+
// CEL misconfiguration in the runtime (`form/index.vue`,
|
|
64
|
+
// `sidebar/tree.vue`, the field runtimes), naming the row that failed;
|
|
65
|
+
// then re-raise so the click still halts (rows after it, and 写入后,
|
|
66
|
+
// stay skipped). `tapErrorCause` covers a throwing `setAt` (a defect)
|
|
67
|
+
// as well as a failed evaluation.
|
|
68
|
+
Effect.tapErrorCause((cause) => Effect.sync(() => {
|
|
69
|
+
console.error(
|
|
70
|
+
`[shwfed-actions] state.write: failed to write "${path}" for ${props.buttonId}:`,
|
|
71
|
+
Cause.squash(cause)
|
|
72
|
+
);
|
|
73
|
+
}))
|
|
74
|
+
);
|
|
55
75
|
},
|
|
56
76
|
{ discard: true }
|
|
57
77
|
)
|
|
@@ -35,8 +35,10 @@ export type WriteValue = Value['writes'][number];
|
|
|
35
35
|
* Adopts 2026-06-25 values, whose「写入前」was a `requests` list of bare
|
|
36
36
|
* HTTP-request expressions. Each row becomes one「发送请求」op trigger on the
|
|
37
37
|
* page target — the op the button now dispatches instead of fetching itself.
|
|
38
|
-
* The row's `id` is carried over verbatim, so every
|
|
39
|
-
* written into a write value keeps resolving
|
|
38
|
+
* The row's `id` is carried over verbatim, so every step reference already
|
|
39
|
+
* written into a write value keeps resolving — the strict `steps["<id>"]` those
|
|
40
|
+
* values carry as much as the optional `steps[?"<id>"]` the picker writes today,
|
|
41
|
+
* both addressing the same id; the op publishes its parsed
|
|
40
42
|
* response under that same id and halts the chain on failure, which is what the
|
|
41
43
|
* old inline requests did.
|
|
42
44
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { TriggerValue } from '../../../share/event-bus.js';
|
|
2
2
|
/**
|
|
3
3
|
* One row of a trigger list that publishes a chain-step output — the rows a
|
|
4
|
-
* later expression can address as `steps["<id>"]`.
|
|
4
|
+
* later expression can address as `steps[?"<id>"]`.
|
|
5
5
|
*
|
|
6
6
|
* A row qualifies on two counts, neither of them a type check: its operation's
|
|
7
7
|
* registry `metadata.output` marks it as publishing (so a new publishing op
|
package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.d.vue.ts
CHANGED
|
@@ -228,6 +228,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
228
228
|
readonly expanded?: boolean | {
|
|
229
229
|
readonly [x: string]: boolean;
|
|
230
230
|
} | undefined;
|
|
231
|
+
readonly rowSelection?: {
|
|
232
|
+
readonly [x: string]: boolean;
|
|
233
|
+
} | undefined;
|
|
231
234
|
readonly columnVisibility?: {
|
|
232
235
|
readonly [x: string]: boolean;
|
|
233
236
|
} | undefined;
|
|
@@ -248,9 +251,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
248
251
|
readonly columnSizing?: {
|
|
249
252
|
readonly [x: string]: number;
|
|
250
253
|
} | undefined;
|
|
251
|
-
readonly rowSelection?: {
|
|
252
|
-
readonly [x: string]: boolean;
|
|
253
|
-
} | undefined;
|
|
254
254
|
} | undefined;
|
|
255
255
|
};
|
|
256
256
|
readonly compatibilityDate: "2026-05-06";
|
|
@@ -476,6 +476,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
476
476
|
readonly expanded?: boolean | {
|
|
477
477
|
readonly [x: string]: boolean;
|
|
478
478
|
} | undefined;
|
|
479
|
+
readonly rowSelection?: {
|
|
480
|
+
readonly [x: string]: boolean;
|
|
481
|
+
} | undefined;
|
|
479
482
|
readonly columnVisibility?: {
|
|
480
483
|
readonly [x: string]: boolean;
|
|
481
484
|
} | undefined;
|
|
@@ -496,9 +499,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
496
499
|
readonly columnSizing?: {
|
|
497
500
|
readonly [x: string]: number;
|
|
498
501
|
} | undefined;
|
|
499
|
-
readonly rowSelection?: {
|
|
500
|
-
readonly [x: string]: boolean;
|
|
501
|
-
} | undefined;
|
|
502
502
|
} | undefined;
|
|
503
503
|
};
|
|
504
504
|
readonly compatibilityDate: "2026-05-06";
|
package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/config.vue.d.ts
CHANGED
|
@@ -228,6 +228,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
228
228
|
readonly expanded?: boolean | {
|
|
229
229
|
readonly [x: string]: boolean;
|
|
230
230
|
} | undefined;
|
|
231
|
+
readonly rowSelection?: {
|
|
232
|
+
readonly [x: string]: boolean;
|
|
233
|
+
} | undefined;
|
|
231
234
|
readonly columnVisibility?: {
|
|
232
235
|
readonly [x: string]: boolean;
|
|
233
236
|
} | undefined;
|
|
@@ -248,9 +251,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
248
251
|
readonly columnSizing?: {
|
|
249
252
|
readonly [x: string]: number;
|
|
250
253
|
} | undefined;
|
|
251
|
-
readonly rowSelection?: {
|
|
252
|
-
readonly [x: string]: boolean;
|
|
253
|
-
} | undefined;
|
|
254
254
|
} | undefined;
|
|
255
255
|
};
|
|
256
256
|
readonly compatibilityDate: "2026-05-06";
|
|
@@ -476,6 +476,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
476
476
|
readonly expanded?: boolean | {
|
|
477
477
|
readonly [x: string]: boolean;
|
|
478
478
|
} | undefined;
|
|
479
|
+
readonly rowSelection?: {
|
|
480
|
+
readonly [x: string]: boolean;
|
|
481
|
+
} | undefined;
|
|
479
482
|
readonly columnVisibility?: {
|
|
480
483
|
readonly [x: string]: boolean;
|
|
481
484
|
} | undefined;
|
|
@@ -496,9 +499,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {},
|
|
|
496
499
|
readonly columnSizing?: {
|
|
497
500
|
readonly [x: string]: number;
|
|
498
501
|
} | undefined;
|
|
499
|
-
readonly rowSelection?: {
|
|
500
|
-
readonly [x: string]: boolean;
|
|
501
|
-
} | undefined;
|
|
502
502
|
} | undefined;
|
|
503
503
|
};
|
|
504
504
|
readonly compatibilityDate: "2026-05-06";
|
package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.d.vue.ts
CHANGED
|
@@ -223,6 +223,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
223
223
|
readonly expanded?: boolean | {
|
|
224
224
|
readonly [x: string]: boolean;
|
|
225
225
|
} | undefined;
|
|
226
|
+
readonly rowSelection?: {
|
|
227
|
+
readonly [x: string]: boolean;
|
|
228
|
+
} | undefined;
|
|
226
229
|
readonly columnVisibility?: {
|
|
227
230
|
readonly [x: string]: boolean;
|
|
228
231
|
} | undefined;
|
|
@@ -243,9 +246,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
243
246
|
readonly columnSizing?: {
|
|
244
247
|
readonly [x: string]: number;
|
|
245
248
|
} | undefined;
|
|
246
|
-
readonly rowSelection?: {
|
|
247
|
-
readonly [x: string]: boolean;
|
|
248
|
-
} | undefined;
|
|
249
249
|
} | undefined;
|
|
250
250
|
};
|
|
251
251
|
readonly compatibilityDate: "2026-05-06";
|
|
@@ -471,6 +471,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
471
471
|
readonly expanded?: boolean | {
|
|
472
472
|
readonly [x: string]: boolean;
|
|
473
473
|
} | undefined;
|
|
474
|
+
readonly rowSelection?: {
|
|
475
|
+
readonly [x: string]: boolean;
|
|
476
|
+
} | undefined;
|
|
474
477
|
readonly columnVisibility?: {
|
|
475
478
|
readonly [x: string]: boolean;
|
|
476
479
|
} | undefined;
|
|
@@ -491,9 +494,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
491
494
|
readonly columnSizing?: {
|
|
492
495
|
readonly [x: string]: number;
|
|
493
496
|
} | undefined;
|
|
494
|
-
readonly rowSelection?: {
|
|
495
|
-
readonly [x: string]: boolean;
|
|
496
|
-
} | undefined;
|
|
497
497
|
} | undefined;
|
|
498
498
|
};
|
|
499
499
|
readonly compatibilityDate: "2026-05-06";
|
package/dist/runtime/components/config/blocks/2026-05-06/com.shwfed.block.table/runtime.vue.d.ts
CHANGED
|
@@ -223,6 +223,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
223
223
|
readonly expanded?: boolean | {
|
|
224
224
|
readonly [x: string]: boolean;
|
|
225
225
|
} | undefined;
|
|
226
|
+
readonly rowSelection?: {
|
|
227
|
+
readonly [x: string]: boolean;
|
|
228
|
+
} | undefined;
|
|
226
229
|
readonly columnVisibility?: {
|
|
227
230
|
readonly [x: string]: boolean;
|
|
228
231
|
} | undefined;
|
|
@@ -243,9 +246,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
243
246
|
readonly columnSizing?: {
|
|
244
247
|
readonly [x: string]: number;
|
|
245
248
|
} | undefined;
|
|
246
|
-
readonly rowSelection?: {
|
|
247
|
-
readonly [x: string]: boolean;
|
|
248
|
-
} | undefined;
|
|
249
249
|
} | undefined;
|
|
250
250
|
};
|
|
251
251
|
readonly compatibilityDate: "2026-05-06";
|
|
@@ -471,6 +471,9 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
471
471
|
readonly expanded?: boolean | {
|
|
472
472
|
readonly [x: string]: boolean;
|
|
473
473
|
} | undefined;
|
|
474
|
+
readonly rowSelection?: {
|
|
475
|
+
readonly [x: string]: boolean;
|
|
476
|
+
} | undefined;
|
|
474
477
|
readonly columnVisibility?: {
|
|
475
478
|
readonly [x: string]: boolean;
|
|
476
479
|
} | undefined;
|
|
@@ -491,9 +494,6 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {},
|
|
|
491
494
|
readonly columnSizing?: {
|
|
492
495
|
readonly [x: string]: number;
|
|
493
496
|
} | undefined;
|
|
494
|
-
readonly rowSelection?: {
|
|
495
|
-
readonly [x: string]: boolean;
|
|
496
|
-
} | undefined;
|
|
497
497
|
} | undefined;
|
|
498
498
|
};
|
|
499
499
|
readonly compatibilityDate: "2026-05-06";
|
|
@@ -241,6 +241,9 @@ export declare function schema(configure: (env: Environment) => void, _blockRef:
|
|
|
241
241
|
readonly expanded?: boolean | {
|
|
242
242
|
readonly [x: string]: boolean;
|
|
243
243
|
} | undefined;
|
|
244
|
+
readonly rowSelection?: {
|
|
245
|
+
readonly [x: string]: boolean;
|
|
246
|
+
} | undefined;
|
|
244
247
|
readonly columnVisibility?: {
|
|
245
248
|
readonly [x: string]: boolean;
|
|
246
249
|
} | undefined;
|
|
@@ -261,9 +264,6 @@ export declare function schema(configure: (env: Environment) => void, _blockRef:
|
|
|
261
264
|
readonly columnSizing?: {
|
|
262
265
|
readonly [x: string]: number;
|
|
263
266
|
} | undefined;
|
|
264
|
-
readonly rowSelection?: {
|
|
265
|
-
readonly [x: string]: boolean;
|
|
266
|
-
} | undefined;
|
|
267
267
|
} | undefined;
|
|
268
268
|
}, Schema.Struct<{
|
|
269
269
|
kind: Schema.tag<"shwfed.component.table">;
|
|
@@ -13,6 +13,7 @@ import ShwfedModal from "../modal.vue";
|
|
|
13
13
|
import EditorBody from "./config.vue";
|
|
14
14
|
import EditorBreadcrumb from "./breadcrumb.vue";
|
|
15
15
|
import EditorFooter from "./footer.vue";
|
|
16
|
+
import PruneDialog from "./prune-dialog.vue";
|
|
16
17
|
import { provideEventTarget } from "../../share/event-bus";
|
|
17
18
|
import { operationHandlers } from "../operations/utils/resolve";
|
|
18
19
|
import * as alertOperation from "../operations/2026-06-09/com.shwfed.operation.alert/schema";
|
|
@@ -95,12 +96,31 @@ provideEventTarget(PAGE_TARGET_ID, {
|
|
|
95
96
|
)
|
|
96
97
|
});
|
|
97
98
|
const editorOpen = ref(false);
|
|
99
|
+
const pendingPrune = ref(null);
|
|
100
|
+
let settlePrune = null;
|
|
101
|
+
function confirmPrune(items) {
|
|
102
|
+
resolvePrune(false);
|
|
103
|
+
pendingPrune.value = items;
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
settlePrune = resolve;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function resolvePrune(proceed) {
|
|
109
|
+
pendingPrune.value = null;
|
|
110
|
+
const settle = settlePrune;
|
|
111
|
+
settlePrune = null;
|
|
112
|
+
settle?.(proceed);
|
|
113
|
+
}
|
|
114
|
+
onBeforeUnmount(() => {
|
|
115
|
+
if (settlePrune) resolvePrune(false);
|
|
116
|
+
});
|
|
98
117
|
const editorState = useConfigEditor(config, {
|
|
99
118
|
configure: mergedConfigure,
|
|
100
119
|
// Presence is decided once at setup (a host toggling the prop's existence
|
|
101
120
|
// at runtime is not supported); the wrapper reads the current prop per
|
|
102
121
|
// call so a rebound handler identity stays honored.
|
|
103
122
|
save: props.save ? (cfg) => props.save(cfg) : void 0,
|
|
123
|
+
confirmPrune,
|
|
104
124
|
onClose: () => {
|
|
105
125
|
editorOpen.value = false;
|
|
106
126
|
}
|
|
@@ -243,6 +263,11 @@ const isMac = computed(
|
|
|
243
263
|
/>
|
|
244
264
|
</div>
|
|
245
265
|
</div>
|
|
266
|
+
<PruneDialog
|
|
267
|
+
:items="pendingPrune"
|
|
268
|
+
@confirm="resolvePrune(true)"
|
|
269
|
+
@cancel="resolvePrune(false)"
|
|
270
|
+
/>
|
|
246
271
|
<template #footer>
|
|
247
272
|
<EditorFooter
|
|
248
273
|
:state="editorState"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type PruneSummary } from './utils/prune.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
/** Non-null while the gate is open; one entry per item about to be dropped. */
|
|
4
|
+
items: ReadonlyArray<PruneSummary> | null;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
7
|
+
cancel: () => any;
|
|
8
|
+
confirm: () => any;
|
|
9
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
10
|
+
onCancel?: (() => any) | undefined;
|
|
11
|
+
onConfirm?: (() => any) | undefined;
|
|
12
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
13
|
+
declare const _default: typeof __VLS_export;
|
|
14
|
+
export default _default;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { Icon } from "@iconify/vue";
|
|
4
|
+
import {
|
|
5
|
+
AlertDialog,
|
|
6
|
+
AlertDialogAction,
|
|
7
|
+
AlertDialogCancel,
|
|
8
|
+
AlertDialogContent,
|
|
9
|
+
AlertDialogDescription,
|
|
10
|
+
AlertDialogFooter,
|
|
11
|
+
AlertDialogHeader,
|
|
12
|
+
AlertDialogTitle
|
|
13
|
+
} from "../ui/alert-dialog";
|
|
14
|
+
import { pruneKindNoun } from "./utils/prune";
|
|
15
|
+
defineOptions({ name: "ShwfedConfigPruneDialog" });
|
|
16
|
+
const props = defineProps({
|
|
17
|
+
items: { type: [Array, null], required: true }
|
|
18
|
+
});
|
|
19
|
+
const emit = defineEmits(["confirm", "cancel"]);
|
|
20
|
+
const open = computed(() => props.items !== null);
|
|
21
|
+
function onOpenChange(next) {
|
|
22
|
+
if (!next) emit("cancel");
|
|
23
|
+
}
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<AlertDialog
|
|
28
|
+
:open="open"
|
|
29
|
+
@update:open="onOpenChange"
|
|
30
|
+
>
|
|
31
|
+
<AlertDialogContent class="sm:px-12 sm:py-8 flex flex-col gap-8">
|
|
32
|
+
<AlertDialogHeader class="sr-only">
|
|
33
|
+
<AlertDialogTitle>清理未使用的内容</AlertDialogTitle>
|
|
34
|
+
<AlertDialogDescription>Confirmation dialog</AlertDialogDescription>
|
|
35
|
+
</AlertDialogHeader>
|
|
36
|
+
<Icon
|
|
37
|
+
icon="fluent:broom-20-filled"
|
|
38
|
+
class="text-6xl mx-auto text-yellow-500"
|
|
39
|
+
/>
|
|
40
|
+
<div class="flex flex-col gap-3">
|
|
41
|
+
<p class="text-sm text-zinc-900">
|
|
42
|
+
以下内容未放入任何布局,不会在页面上显示。保存时将一并删除:
|
|
43
|
+
</p>
|
|
44
|
+
<ul class="max-h-60 overflow-y-auto flex flex-col gap-1 rounded border border-zinc-200 bg-zinc-50 p-3">
|
|
45
|
+
<li
|
|
46
|
+
v-for="(item, i) in props.items ?? []"
|
|
47
|
+
:key="i"
|
|
48
|
+
class="flex items-baseline gap-2 text-sm text-zinc-700"
|
|
49
|
+
>
|
|
50
|
+
<span class="shrink-0 rounded bg-zinc-200 px-1.5 py-0.5 text-xs text-zinc-600">
|
|
51
|
+
{{ pruneKindNoun(item.kind) }}
|
|
52
|
+
</span>
|
|
53
|
+
<span class="break-all">{{ item.label }}</span>
|
|
54
|
+
</li>
|
|
55
|
+
</ul>
|
|
56
|
+
<p class="text-xs text-zinc-500">
|
|
57
|
+
若仍需保留,请点击取消,并将其放入布局。
|
|
58
|
+
</p>
|
|
59
|
+
</div>
|
|
60
|
+
<AlertDialogFooter>
|
|
61
|
+
<AlertDialogCancel @click="emit('cancel')">
|
|
62
|
+
<Icon icon="fluent:dismiss-20-regular" />
|
|
63
|
+
取消
|
|
64
|
+
</AlertDialogCancel>
|
|
65
|
+
<AlertDialogAction @click="emit('confirm')">
|
|
66
|
+
<Icon icon="fluent:checkmark-20-regular" />
|
|
67
|
+
删除并保存
|
|
68
|
+
</AlertDialogAction>
|
|
69
|
+
</AlertDialogFooter>
|
|
70
|
+
</AlertDialogContent>
|
|
71
|
+
</AlertDialog>
|
|
72
|
+
</template>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type PruneSummary } from './utils/prune.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
/** Non-null while the gate is open; one entry per item about to be dropped. */
|
|
4
|
+
items: ReadonlyArray<PruneSummary> | null;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
7
|
+
cancel: () => any;
|
|
8
|
+
confirm: () => any;
|
|
9
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
10
|
+
onCancel?: (() => any) | undefined;
|
|
11
|
+
onConfirm?: (() => any) | undefined;
|
|
12
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
13
|
+
declare const _default: typeof __VLS_export;
|
|
14
|
+
export default _default;
|
|
@@ -33,7 +33,7 @@ export const metadata = {
|
|
|
33
33
|
icon: httpRequestOperation.metadata.icon
|
|
34
34
|
},
|
|
35
35
|
// Navigation as an op (replaces the standalone navigation *button*): its URL
|
|
36
|
-
// expression can reference `steps["<id>"]`, so a chain can navigate using an
|
|
36
|
+
// expression can reference `steps[?"<id>"]`, so a chain can navigate using an
|
|
37
37
|
// earlier request's response. The page hosts it for the same reason as the
|
|
38
38
|
// request op — navigation acts on nothing instance-specific.
|
|
39
39
|
{
|
|
@@ -43,7 +43,7 @@ export const metadata = {
|
|
|
43
43
|
icon: navigationOperation.metadata.icon
|
|
44
44
|
},
|
|
45
45
|
// Download as an op (replaces the standalone download *button*): its request
|
|
46
|
-
// expression can reference `steps["<id>"]`, so the old two-step「先取凭据再下载」
|
|
46
|
+
// expression can reference `steps[?"<id>"]`, so the old two-step「先取凭据再下载」
|
|
47
47
|
// becomes a 发送请求 row followed by this download row. The page hosts it for
|
|
48
48
|
// the same reason as the request op — the download acts on nothing
|
|
49
49
|
// instance-specific.
|
|
@@ -4,6 +4,7 @@ import { type LayoutSetValue, type PlacementValue, type SlotValue } from '../../
|
|
|
4
4
|
import type { BreadcrumbCrumb, BreadcrumbParentSuffix } from './breadcrumb-extension.js';
|
|
5
5
|
import { type BlockValue, type PageConfigValue } from './schema.js';
|
|
6
6
|
import { type BlockEntry } from './utils/resolve.js';
|
|
7
|
+
import { type PruneSummary } from './utils/prune.js';
|
|
7
8
|
export type PickerEntry = {
|
|
8
9
|
type: string;
|
|
9
10
|
compatibilityDate: string;
|
|
@@ -133,6 +134,14 @@ export interface UseConfigEditorOptions {
|
|
|
133
134
|
* the editor guarantees the session is never lost to a failed save.
|
|
134
135
|
*/
|
|
135
136
|
save?: (config: PageConfigValue) => void | Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Consent gate for the save-time prune of unplaced items (utils/prune.ts).
|
|
139
|
+
* Called only when 确认 would actually drop something, with a breadcrumb per
|
|
140
|
+
* removal; resolving `false` aborts the save with the draft untouched. Hosts
|
|
141
|
+
* wire this to a confirmation dialog — `ShwfedConfig` does. Left unwired, the
|
|
142
|
+
* prune is silent.
|
|
143
|
+
*/
|
|
144
|
+
confirmPrune?: (items: ReadonlyArray<PruneSummary>) => boolean | Promise<boolean>;
|
|
136
145
|
/**
|
|
137
146
|
* Called after a successful 确认 commits the draft to the outer model.
|
|
138
147
|
* Hosts wire this to close the modal; legacy mode emits `close`.
|
|
@@ -7,6 +7,12 @@ import { PageConfig } from "./schema.js";
|
|
|
7
7
|
import { BLOCKS, findBlock } from "./utils/resolve.js";
|
|
8
8
|
import { formatValidationError } from "./utils/validation-error.js";
|
|
9
9
|
import { findDanglingLinkages, formatDanglingLinkages } from "./utils/linkage.js";
|
|
10
|
+
import {
|
|
11
|
+
findPruneConflicts,
|
|
12
|
+
formatPruneConflicts,
|
|
13
|
+
pruneUnplaced,
|
|
14
|
+
summarizePrune
|
|
15
|
+
} from "./utils/prune.js";
|
|
10
16
|
import { createExpressionValidityRegistry, provideExpressionValidity } from "../ui/expression-editor/index.js";
|
|
11
17
|
function deepClone(value) {
|
|
12
18
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -396,20 +402,42 @@ export function useConfigEditor(model, options = {}) {
|
|
|
396
402
|
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";
|
|
397
403
|
return false;
|
|
398
404
|
}
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
405
|
+
const { config: pruned, removed } = pruneUnplaced(decoded);
|
|
406
|
+
const draftBlockLabelAt = (i) => {
|
|
407
|
+
const b = topSlotEditor.blocks.value[i];
|
|
408
|
+
return b ? topSlotEditor.getBlockLabel(b) : void 0;
|
|
409
|
+
};
|
|
410
|
+
const prunedBlockLabelAt = (i) => {
|
|
411
|
+
const b = pruned.slot.blocks[i];
|
|
412
|
+
return b ? topSlotEditor.getBlockLabel(b) : void 0;
|
|
413
|
+
};
|
|
414
|
+
const conflicts = findPruneConflicts(pruned, removed);
|
|
415
|
+
if (conflicts.length > 0) {
|
|
416
|
+
validationError.value = formatPruneConflicts(conflicts, {
|
|
417
|
+
prunedRoot: pruned,
|
|
418
|
+
originalRoot: decoded,
|
|
419
|
+
prunedBlockLabelAt,
|
|
420
|
+
originalBlockLabelAt: draftBlockLabelAt
|
|
404
421
|
});
|
|
405
422
|
return false;
|
|
406
423
|
}
|
|
424
|
+
const dangling = findDanglingLinkages(pruned);
|
|
425
|
+
if (dangling.length > 0) {
|
|
426
|
+
validationError.value = formatDanglingLinkages(dangling, pruned, prunedBlockLabelAt);
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
407
429
|
validationError.value = null;
|
|
430
|
+
if (removed.length > 0 && options.confirmPrune) {
|
|
431
|
+
const proceed = await options.confirmPrune(
|
|
432
|
+
summarizePrune(removed, decoded, draftBlockLabelAt)
|
|
433
|
+
);
|
|
434
|
+
if (!proceed) return false;
|
|
435
|
+
}
|
|
408
436
|
if (options.save) {
|
|
409
437
|
saving.value = true;
|
|
410
438
|
saveError.value = null;
|
|
411
439
|
try {
|
|
412
|
-
await options.save(deepClone(
|
|
440
|
+
await options.save(deepClone(pruned));
|
|
413
441
|
} catch (err) {
|
|
414
442
|
saveError.value = `\u4FDD\u5B58\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`;
|
|
415
443
|
return false;
|
|
@@ -417,9 +445,9 @@ export function useConfigEditor(model, options = {}) {
|
|
|
417
445
|
saving.value = false;
|
|
418
446
|
}
|
|
419
447
|
}
|
|
420
|
-
model.value =
|
|
421
|
-
baseline.value = deepClone(
|
|
422
|
-
draft.value = deepClone(
|
|
448
|
+
model.value = pruned;
|
|
449
|
+
baseline.value = deepClone(pruned);
|
|
450
|
+
draft.value = deepClone(pruned);
|
|
423
451
|
stack.value = [{ kind: "layout" }];
|
|
424
452
|
topSlotEditor.selectedBlockIds.value = [];
|
|
425
453
|
topSlotEditor.activeLayoutIndex.value = 0;
|
|
@@ -11,6 +11,13 @@ export type DanglingLinkage = Readonly<{
|
|
|
11
11
|
*/
|
|
12
12
|
path: ReadonlyArray<PropertyKey>;
|
|
13
13
|
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Every uuid `id` defined under `node`. Exported so the save-time prune
|
|
16
|
+
* (utils/prune.ts) asks what a to-be-removed subtree *defines* with the very
|
|
17
|
+
* same rule this gate uses to decide what is *defined* — the two must agree, or
|
|
18
|
+
* a prune would strand a linkage this walker then fails to flag.
|
|
19
|
+
*/
|
|
20
|
+
export declare function definedIds(node: unknown): Set<string>;
|
|
14
21
|
/**
|
|
15
22
|
* Find every linkage in `config` whose target uuid is not defined anywhere in
|
|
16
23
|
* the tree. `PAGE_TARGET_ID` (the singleton page target sentinel) is always
|
|
@@ -19,6 +26,7 @@ export type DanglingLinkage = Readonly<{
|
|
|
19
26
|
* the caller can render a field-level breadcrumb (see `describePath`).
|
|
20
27
|
*/
|
|
21
28
|
export declare function findDanglingLinkages(config: unknown): DanglingLinkage[];
|
|
29
|
+
export declare const LINKAGE_KIND_LABEL: Record<LinkageKind, string>;
|
|
22
30
|
/**
|
|
23
31
|
* One-line save-footer message for a non-empty dangling list. Renders the
|
|
24
32
|
* breadcrumb to the first offending reference (`「表单 › 确定 › value」`) via the
|
|
@@ -17,6 +17,11 @@ function collectDefinedIds(node, into) {
|
|
|
17
17
|
collectDefinedIds(v, into);
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
export function definedIds(node) {
|
|
21
|
+
const out = /* @__PURE__ */ new Set();
|
|
22
|
+
collectDefinedIds(node, out);
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
20
25
|
function collectRefs(node, path, into) {
|
|
21
26
|
if (Array.isArray(node)) {
|
|
22
27
|
node.forEach((v, i) => collectRefs(v, [...path, i], into));
|
|
@@ -41,7 +46,7 @@ export function findDanglingLinkages(config) {
|
|
|
41
46
|
collectRefs(config, [], refs);
|
|
42
47
|
return refs.filter((r) => r.id !== PAGE_TARGET_ID && !defined.has(r.id));
|
|
43
48
|
}
|
|
44
|
-
const
|
|
49
|
+
export const LINKAGE_KIND_LABEL = {
|
|
45
50
|
operation: "\u89E6\u53D1\u64CD\u4F5C",
|
|
46
51
|
step: "\u6B65\u9AA4\u5F15\u7528",
|
|
47
52
|
scope: "\u8DE8\u5C42\u4F5C\u7528\u57DF\u5F15\u7528"
|
|
@@ -49,7 +54,7 @@ const KIND_LABEL = {
|
|
|
49
54
|
export function formatDanglingLinkages(dangling, rootData, blockLabelAt) {
|
|
50
55
|
const first = dangling[0];
|
|
51
56
|
if (!first) return "";
|
|
52
|
-
const kind =
|
|
57
|
+
const kind = LINKAGE_KIND_LABEL[first.kind];
|
|
53
58
|
const segs = describePath(first.path, rootData, blockLabelAt);
|
|
54
59
|
const where = segs.length > 0 ? `\u300C${segs.join(" \u203A ")}\u300D\u7684${kind}` : kind;
|
|
55
60
|
const suffix = dangling.length > 1 ? `\uFF08\u8FD8\u6709 ${dangling.length - 1} \u4E2A\uFF09` : "";
|