@shwfed/config 2.3.0 → 2.3.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/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/config.d.vue.ts +61 -0
- package/dist/runtime/components/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/config.vue +187 -0
- package/dist/runtime/components/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/config.vue.d.ts +61 -0
- package/dist/runtime/components/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/runtime.d.vue.ts +8 -0
- package/dist/runtime/components/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/runtime.vue +62 -0
- package/dist/runtime/components/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/runtime.vue.d.ts +8 -0
- package/dist/runtime/components/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/schema.d.ts +40 -0
- package/dist/runtime/components/actions/buttons/2026-05-21/com.shwfed.actions.button.http.download/schema.js +87 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/config.d.vue.ts +10 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/config.vue +310 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/config.vue.d.ts +10 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/runtime.d.vue.ts +9 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/runtime.vue +81 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/runtime.vue.d.ts +9 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/schema.d.ts +57 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/schema.js +59 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type Value } from './schema.js';
|
|
2
|
+
type __VLS_ModelProps = {
|
|
3
|
+
modelValue: Value;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
6
|
+
"update:modelValue": (value: {
|
|
7
|
+
readonly template: {
|
|
8
|
+
readonly download?: string | undefined;
|
|
9
|
+
readonly request: string;
|
|
10
|
+
};
|
|
11
|
+
readonly onError?: readonly {
|
|
12
|
+
readonly target: string;
|
|
13
|
+
readonly operation: string;
|
|
14
|
+
}[] | undefined;
|
|
15
|
+
readonly type: "com.shwfed.actions.button.http.download";
|
|
16
|
+
readonly compatibilityDate: "2026-05-21";
|
|
17
|
+
readonly messageExpression?: string | undefined;
|
|
18
|
+
readonly resultExpression?: string | undefined;
|
|
19
|
+
readonly onSuccess?: readonly {
|
|
20
|
+
readonly target: string;
|
|
21
|
+
readonly operation: string;
|
|
22
|
+
}[] | undefined;
|
|
23
|
+
readonly onWarning?: readonly {
|
|
24
|
+
readonly target: string;
|
|
25
|
+
readonly operation: string;
|
|
26
|
+
}[] | undefined;
|
|
27
|
+
readonly onInfo?: readonly {
|
|
28
|
+
readonly target: string;
|
|
29
|
+
readonly operation: string;
|
|
30
|
+
}[] | undefined;
|
|
31
|
+
}) => any;
|
|
32
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
|
|
33
|
+
"onUpdate:modelValue"?: ((value: {
|
|
34
|
+
readonly template: {
|
|
35
|
+
readonly download?: string | undefined;
|
|
36
|
+
readonly request: string;
|
|
37
|
+
};
|
|
38
|
+
readonly onError?: readonly {
|
|
39
|
+
readonly target: string;
|
|
40
|
+
readonly operation: string;
|
|
41
|
+
}[] | undefined;
|
|
42
|
+
readonly type: "com.shwfed.actions.button.http.download";
|
|
43
|
+
readonly compatibilityDate: "2026-05-21";
|
|
44
|
+
readonly messageExpression?: string | undefined;
|
|
45
|
+
readonly resultExpression?: string | undefined;
|
|
46
|
+
readonly onSuccess?: readonly {
|
|
47
|
+
readonly target: string;
|
|
48
|
+
readonly operation: string;
|
|
49
|
+
}[] | undefined;
|
|
50
|
+
readonly onWarning?: readonly {
|
|
51
|
+
readonly target: string;
|
|
52
|
+
readonly operation: string;
|
|
53
|
+
}[] | undefined;
|
|
54
|
+
readonly onInfo?: readonly {
|
|
55
|
+
readonly target: string;
|
|
56
|
+
readonly operation: string;
|
|
57
|
+
}[] | undefined;
|
|
58
|
+
}) => any) | undefined;
|
|
59
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
60
|
+
declare const _default: typeof __VLS_export;
|
|
61
|
+
export default _default;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { Field, FieldLabel } from "../../../../ui/field";
|
|
4
|
+
import { ExpressionEditor } from "../../../../ui/expression-editor";
|
|
5
|
+
import { Markdown } from "../../../../ui/markdown";
|
|
6
|
+
import { getStructFieldDescription, getStructFieldTitle } from "../../../schema";
|
|
7
|
+
import TriggersField from "../../../components/triggers-field.vue";
|
|
8
|
+
import { JSON_VAR, schema } from "./schema";
|
|
9
|
+
defineOptions({ name: "ShwfedHttpDownloadActionConfig" });
|
|
10
|
+
const value = defineModel({ type: null, ...{ required: true } });
|
|
11
|
+
const actionSchema = schema(() => {
|
|
12
|
+
});
|
|
13
|
+
const fieldTitle = (f) => getStructFieldTitle(actionSchema, f) ?? f;
|
|
14
|
+
const fieldDescription = (f) => getStructFieldDescription(actionSchema, f);
|
|
15
|
+
const RESULT_FIELDS = ["onSuccess", "onWarning", "onError", "onInfo"];
|
|
16
|
+
const templateRequestText = computed({
|
|
17
|
+
get: () => value.value.template?.request ?? "",
|
|
18
|
+
set: (next) => {
|
|
19
|
+
value.value = {
|
|
20
|
+
...value.value,
|
|
21
|
+
template: { ...value.value.template, request: next }
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const templateDownloadText = computed({
|
|
26
|
+
get: () => value.value.template?.download ?? "",
|
|
27
|
+
set: (next) => {
|
|
28
|
+
const current = value.value.template ?? { request: "" };
|
|
29
|
+
if (next.length === 0) {
|
|
30
|
+
const { download: _omit, ...rest } = current;
|
|
31
|
+
value.value = { ...value.value, template: rest };
|
|
32
|
+
} else {
|
|
33
|
+
value.value = { ...value.value, template: { ...current, download: next } };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
function updateMessageExpression(v) {
|
|
38
|
+
const next = { ...value.value };
|
|
39
|
+
if (v.length === 0) delete next.messageExpression;
|
|
40
|
+
else next.messageExpression = v;
|
|
41
|
+
value.value = next;
|
|
42
|
+
}
|
|
43
|
+
function updateResultExpression(v) {
|
|
44
|
+
const next = { ...value.value };
|
|
45
|
+
if (v.length === 0) delete next.resultExpression;
|
|
46
|
+
else next.resultExpression = v;
|
|
47
|
+
value.value = next;
|
|
48
|
+
}
|
|
49
|
+
function updateTriggers(key, next) {
|
|
50
|
+
const draft = { ...value.value };
|
|
51
|
+
if (next.length === 0) Reflect.deleteProperty(draft, key);
|
|
52
|
+
else draft[key] = next;
|
|
53
|
+
value.value = draft;
|
|
54
|
+
}
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<template>
|
|
58
|
+
<div class="flex flex-col gap-3">
|
|
59
|
+
<div class="grid grid-cols-2 gap-3">
|
|
60
|
+
<Field orientation="vertical">
|
|
61
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
62
|
+
<template #tooltip>
|
|
63
|
+
<Markdown
|
|
64
|
+
:source="fieldDescription('template') ?? '\u8FD4\u56DE `HttpRequest` \u7684 CEL \u8868\u8FBE\u5F0F\uFF1B\u672A\u914D\u7F6E\u300C\u4E0B\u8F7D\u8BF7\u6C42\u300D\u65F6\u76F4\u63A5\u4E0B\u8F7D\u5176\u54CD\u5E94'"
|
|
65
|
+
block
|
|
66
|
+
class="prose prose-sm prose-zinc"
|
|
67
|
+
/>
|
|
68
|
+
</template>
|
|
69
|
+
请求
|
|
70
|
+
</FieldLabel>
|
|
71
|
+
<ExpressionEditor
|
|
72
|
+
:model-value="templateRequestText"
|
|
73
|
+
multiline
|
|
74
|
+
placeholder="例:http.get('https://api.example.com/files/123')"
|
|
75
|
+
result-type="HttpRequest"
|
|
76
|
+
class="min-h-16"
|
|
77
|
+
@update:model-value="(v) => templateRequestText = v"
|
|
78
|
+
/>
|
|
79
|
+
</Field>
|
|
80
|
+
|
|
81
|
+
<Field orientation="vertical">
|
|
82
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
83
|
+
<template #tooltip>
|
|
84
|
+
<Markdown
|
|
85
|
+
source="可选的第二步:用第一步响应 `json` 中的凭据构造真正的下载请求,例如 `json.data.key`"
|
|
86
|
+
block
|
|
87
|
+
class="prose prose-sm prose-zinc"
|
|
88
|
+
/>
|
|
89
|
+
</template>
|
|
90
|
+
下载请求
|
|
91
|
+
</FieldLabel>
|
|
92
|
+
<ExpressionEditor
|
|
93
|
+
:model-value="templateDownloadText"
|
|
94
|
+
multiline
|
|
95
|
+
placeholder="例:http.get('https://api.example.com/download').query('key', json.data.key)"
|
|
96
|
+
result-type="HttpRequest"
|
|
97
|
+
:extra-vars="{ json: JSON_VAR }"
|
|
98
|
+
class="min-h-16"
|
|
99
|
+
@update:model-value="(v) => templateDownloadText = v"
|
|
100
|
+
/>
|
|
101
|
+
</Field>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div class="flex items-start gap-2">
|
|
105
|
+
<Field
|
|
106
|
+
orientation="vertical"
|
|
107
|
+
class="flex-1 basis-0 min-w-0"
|
|
108
|
+
>
|
|
109
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
110
|
+
<template
|
|
111
|
+
v-if="fieldDescription('messageExpression')"
|
|
112
|
+
#tooltip
|
|
113
|
+
>
|
|
114
|
+
<Markdown
|
|
115
|
+
:source="fieldDescription('messageExpression')"
|
|
116
|
+
block
|
|
117
|
+
class="prose prose-sm prose-zinc"
|
|
118
|
+
/>
|
|
119
|
+
</template>
|
|
120
|
+
{{ fieldTitle("messageExpression") }}
|
|
121
|
+
</FieldLabel>
|
|
122
|
+
<ExpressionEditor
|
|
123
|
+
:model-value="value.messageExpression ?? ''"
|
|
124
|
+
placeholder="例:string(json.message)"
|
|
125
|
+
result-type="string"
|
|
126
|
+
:extra-vars="{ json: JSON_VAR }"
|
|
127
|
+
@update:model-value="updateMessageExpression"
|
|
128
|
+
/>
|
|
129
|
+
</Field>
|
|
130
|
+
|
|
131
|
+
<Field
|
|
132
|
+
orientation="vertical"
|
|
133
|
+
class="flex-1 basis-0 min-w-0"
|
|
134
|
+
>
|
|
135
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
136
|
+
<template
|
|
137
|
+
v-if="fieldDescription('resultExpression')"
|
|
138
|
+
#tooltip
|
|
139
|
+
>
|
|
140
|
+
<Markdown
|
|
141
|
+
:source="fieldDescription('resultExpression')"
|
|
142
|
+
block
|
|
143
|
+
class="prose prose-sm prose-zinc"
|
|
144
|
+
/>
|
|
145
|
+
</template>
|
|
146
|
+
{{ fieldTitle("resultExpression") }}
|
|
147
|
+
</FieldLabel>
|
|
148
|
+
<ExpressionEditor
|
|
149
|
+
:model-value="value.resultExpression ?? ''"
|
|
150
|
+
placeholder="例:json.code == 0 ? 'success' : 'error'"
|
|
151
|
+
result-type="string"
|
|
152
|
+
:extra-vars="{ json: JSON_VAR }"
|
|
153
|
+
@update:model-value="updateResultExpression"
|
|
154
|
+
/>
|
|
155
|
+
</Field>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<!-- One trigger list per 「执行结果」: the request completes, the result
|
|
159
|
+
classifies the outcome, and the matching cell's operations run. -->
|
|
160
|
+
<div class="grid grid-cols-2 gap-3">
|
|
161
|
+
<Field
|
|
162
|
+
v-for="key in RESULT_FIELDS"
|
|
163
|
+
:key="key"
|
|
164
|
+
orientation="vertical"
|
|
165
|
+
class="min-w-0"
|
|
166
|
+
>
|
|
167
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
168
|
+
<template
|
|
169
|
+
v-if="fieldDescription(key)"
|
|
170
|
+
#tooltip
|
|
171
|
+
>
|
|
172
|
+
<Markdown
|
|
173
|
+
:source="fieldDescription(key)"
|
|
174
|
+
block
|
|
175
|
+
class="prose prose-sm prose-zinc"
|
|
176
|
+
/>
|
|
177
|
+
</template>
|
|
178
|
+
{{ fieldTitle(key) }}
|
|
179
|
+
</FieldLabel>
|
|
180
|
+
<TriggersField
|
|
181
|
+
:triggers="value[key] ?? []"
|
|
182
|
+
@update:triggers="(v) => updateTriggers(key, v)"
|
|
183
|
+
/>
|
|
184
|
+
</Field>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
</template>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type Value } from './schema.js';
|
|
2
|
+
type __VLS_ModelProps = {
|
|
3
|
+
modelValue: Value;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_ModelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
6
|
+
"update:modelValue": (value: {
|
|
7
|
+
readonly template: {
|
|
8
|
+
readonly download?: string | undefined;
|
|
9
|
+
readonly request: string;
|
|
10
|
+
};
|
|
11
|
+
readonly onError?: readonly {
|
|
12
|
+
readonly target: string;
|
|
13
|
+
readonly operation: string;
|
|
14
|
+
}[] | undefined;
|
|
15
|
+
readonly type: "com.shwfed.actions.button.http.download";
|
|
16
|
+
readonly compatibilityDate: "2026-05-21";
|
|
17
|
+
readonly messageExpression?: string | undefined;
|
|
18
|
+
readonly resultExpression?: string | undefined;
|
|
19
|
+
readonly onSuccess?: readonly {
|
|
20
|
+
readonly target: string;
|
|
21
|
+
readonly operation: string;
|
|
22
|
+
}[] | undefined;
|
|
23
|
+
readonly onWarning?: readonly {
|
|
24
|
+
readonly target: string;
|
|
25
|
+
readonly operation: string;
|
|
26
|
+
}[] | undefined;
|
|
27
|
+
readonly onInfo?: readonly {
|
|
28
|
+
readonly target: string;
|
|
29
|
+
readonly operation: string;
|
|
30
|
+
}[] | undefined;
|
|
31
|
+
}) => any;
|
|
32
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_ModelProps> & Readonly<{
|
|
33
|
+
"onUpdate:modelValue"?: ((value: {
|
|
34
|
+
readonly template: {
|
|
35
|
+
readonly download?: string | undefined;
|
|
36
|
+
readonly request: string;
|
|
37
|
+
};
|
|
38
|
+
readonly onError?: readonly {
|
|
39
|
+
readonly target: string;
|
|
40
|
+
readonly operation: string;
|
|
41
|
+
}[] | undefined;
|
|
42
|
+
readonly type: "com.shwfed.actions.button.http.download";
|
|
43
|
+
readonly compatibilityDate: "2026-05-21";
|
|
44
|
+
readonly messageExpression?: string | undefined;
|
|
45
|
+
readonly resultExpression?: string | undefined;
|
|
46
|
+
readonly onSuccess?: readonly {
|
|
47
|
+
readonly target: string;
|
|
48
|
+
readonly operation: string;
|
|
49
|
+
}[] | undefined;
|
|
50
|
+
readonly onWarning?: readonly {
|
|
51
|
+
readonly target: string;
|
|
52
|
+
readonly operation: string;
|
|
53
|
+
}[] | undefined;
|
|
54
|
+
readonly onInfo?: readonly {
|
|
55
|
+
readonly target: string;
|
|
56
|
+
readonly operation: string;
|
|
57
|
+
}[] | undefined;
|
|
58
|
+
}) => any) | undefined;
|
|
59
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
60
|
+
declare const _default: typeof __VLS_export;
|
|
61
|
+
export default _default;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Value } from './schema.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
buttonId: string;
|
|
4
|
+
config: Value;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { toast } from "vue-sonner";
|
|
4
|
+
import { cel as _rawCel } from "../../../../../utils/cel";
|
|
5
|
+
import { celBindings, injectCELContext } from "../../../../../utils/cel-context";
|
|
6
|
+
import { dispatchTriggers, useEventChannel } from "../../../../../share/event-bus";
|
|
7
|
+
import ShwfedActionDefinition from "../../../components/definition.vue";
|
|
8
|
+
defineOptions({ name: "ShwfedHttpDownloadActionRuntime" });
|
|
9
|
+
const props = defineProps({
|
|
10
|
+
buttonId: { type: String, required: true },
|
|
11
|
+
config: { type: null, required: true }
|
|
12
|
+
});
|
|
13
|
+
const inherited = injectCELContext();
|
|
14
|
+
const $cel = (expression, context) => _rawCel(expression, { ...celBindings(inherited), ...context });
|
|
15
|
+
const channel = useEventChannel();
|
|
16
|
+
const TOAST_STYLES = /* @__PURE__ */ new Set(["success", "error", "warning", "info"]);
|
|
17
|
+
function saveFile(file) {
|
|
18
|
+
const url = URL.createObjectURL(file);
|
|
19
|
+
const a = document.createElement("a");
|
|
20
|
+
a.href = url;
|
|
21
|
+
a.download = file.name;
|
|
22
|
+
document.body.appendChild(a);
|
|
23
|
+
a.click();
|
|
24
|
+
a.remove();
|
|
25
|
+
URL.revokeObjectURL(url);
|
|
26
|
+
}
|
|
27
|
+
const effect = Effect.suspend(() => Effect.gen(function* () {
|
|
28
|
+
const { template, messageExpression, resultExpression } = props.config;
|
|
29
|
+
const requestBuilder = yield* $cel(template.request);
|
|
30
|
+
if (!template.download) {
|
|
31
|
+
const file = yield* requestBuilder.file();
|
|
32
|
+
yield* Effect.sync(() => saveFile(file));
|
|
33
|
+
return yield* dispatchTriggers(channel, props.config.onSuccess);
|
|
34
|
+
}
|
|
35
|
+
const json = yield* requestBuilder.json();
|
|
36
|
+
const context = { json };
|
|
37
|
+
const kind = resultExpression === void 0 ? "success" : yield* Effect.map($cel(resultExpression, context), (s) => TOAST_STYLES.has(s) ? s : "success");
|
|
38
|
+
if (messageExpression !== void 0) {
|
|
39
|
+
const message = yield* $cel(messageExpression, context);
|
|
40
|
+
yield* Effect.sync(() => toast[kind](message));
|
|
41
|
+
}
|
|
42
|
+
if (kind === "success" || kind === "info") {
|
|
43
|
+
const downloadBuilder = yield* $cel(template.download, context);
|
|
44
|
+
const file = yield* downloadBuilder.file();
|
|
45
|
+
yield* Effect.sync(() => saveFile(file));
|
|
46
|
+
}
|
|
47
|
+
const triggers = {
|
|
48
|
+
success: props.config.onSuccess,
|
|
49
|
+
warning: props.config.onWarning,
|
|
50
|
+
error: props.config.onError,
|
|
51
|
+
info: props.config.onInfo
|
|
52
|
+
};
|
|
53
|
+
return yield* dispatchTriggers(channel, triggers[kind]);
|
|
54
|
+
}));
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<template>
|
|
58
|
+
<ShwfedActionDefinition
|
|
59
|
+
:action-id="buttonId"
|
|
60
|
+
:effect="effect"
|
|
61
|
+
/>
|
|
62
|
+
</template>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Value } from './schema.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
buttonId: string;
|
|
4
|
+
config: Value;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
7
|
+
declare const _default: typeof __VLS_export;
|
|
8
|
+
export default _default;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import type { Environment } from '../../../../../vendor/cel-js/lib/index.js';
|
|
3
|
+
export declare const type: "com.shwfed.actions.button.http.download";
|
|
4
|
+
export declare const compatibilityDate: "2026-05-21";
|
|
5
|
+
export declare const metadata: {
|
|
6
|
+
readonly name: "下载文件";
|
|
7
|
+
readonly icon: "fluent:arrow-download-20-regular";
|
|
8
|
+
};
|
|
9
|
+
export declare const JSON_VAR: {
|
|
10
|
+
readonly type: "dyn";
|
|
11
|
+
readonly label: "HTTP 响应体";
|
|
12
|
+
readonly description: "第一步 `请求` 的响应体(已解析 JSON);用于构造第二步下载请求";
|
|
13
|
+
};
|
|
14
|
+
export declare function schema(configure: (env: Environment) => void): Schema.Struct<{
|
|
15
|
+
type: Schema.Literal<["com.shwfed.actions.button.http.download"]>;
|
|
16
|
+
compatibilityDate: Schema.Literal<["2026-05-21"]>;
|
|
17
|
+
template: Schema.Struct<{
|
|
18
|
+
request: Schema.Schema<string, string, never>;
|
|
19
|
+
download: Schema.optional<Schema.Schema<string, string, never>>;
|
|
20
|
+
}>;
|
|
21
|
+
messageExpression: Schema.optional<Schema.Schema<string, string, never>>;
|
|
22
|
+
resultExpression: Schema.optional<Schema.Schema<string, string, never>>;
|
|
23
|
+
onSuccess: Schema.optional<Schema.Array$<Schema.Struct<{
|
|
24
|
+
target: Schema.refine<string, typeof Schema.String>;
|
|
25
|
+
operation: Schema.SchemaClass<string, string, never>;
|
|
26
|
+
}>>>;
|
|
27
|
+
onWarning: Schema.optional<Schema.Array$<Schema.Struct<{
|
|
28
|
+
target: Schema.refine<string, typeof Schema.String>;
|
|
29
|
+
operation: Schema.SchemaClass<string, string, never>;
|
|
30
|
+
}>>>;
|
|
31
|
+
onError: Schema.optional<Schema.Array$<Schema.Struct<{
|
|
32
|
+
target: Schema.refine<string, typeof Schema.String>;
|
|
33
|
+
operation: Schema.SchemaClass<string, string, never>;
|
|
34
|
+
}>>>;
|
|
35
|
+
onInfo: Schema.optional<Schema.Array$<Schema.Struct<{
|
|
36
|
+
target: Schema.refine<string, typeof Schema.String>;
|
|
37
|
+
operation: Schema.SchemaClass<string, string, never>;
|
|
38
|
+
}>>>;
|
|
39
|
+
}>;
|
|
40
|
+
export type Value = Schema.Schema.Type<ReturnType<typeof schema>>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { Expression } from "../../../../../share/expression.js";
|
|
3
|
+
import { Triggers } from "../../../../../share/event-bus.js";
|
|
4
|
+
import { md } from "../../../../../share/markdown.js";
|
|
5
|
+
export const type = "com.shwfed.actions.button.http.download";
|
|
6
|
+
export const compatibilityDate = "2026-05-21";
|
|
7
|
+
export const metadata = {
|
|
8
|
+
name: "\u4E0B\u8F7D\u6587\u4EF6",
|
|
9
|
+
icon: "fluent:arrow-download-20-regular"
|
|
10
|
+
};
|
|
11
|
+
export const JSON_VAR = {
|
|
12
|
+
type: "dyn",
|
|
13
|
+
label: "HTTP \u54CD\u5E94\u4F53",
|
|
14
|
+
description: "\u7B2C\u4E00\u6B65 `\u8BF7\u6C42` \u7684\u54CD\u5E94\u4F53\uFF08\u5DF2\u89E3\u6790 JSON\uFF09\uFF1B\u7528\u4E8E\u6784\u9020\u7B2C\u4E8C\u6B65\u4E0B\u8F7D\u8BF7\u6C42"
|
|
15
|
+
};
|
|
16
|
+
function configureWithJson(configure) {
|
|
17
|
+
return (env) => {
|
|
18
|
+
configure(env);
|
|
19
|
+
env.registerVariable("json", JSON_VAR.type, { description: JSON_VAR.description });
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function schema(configure) {
|
|
23
|
+
const CelHttpRequest = Expression({ configure, resultType: "HttpRequest" });
|
|
24
|
+
const CelDownloadRequest = Expression({ configure: configureWithJson(configure), resultType: "HttpRequest" });
|
|
25
|
+
const CelMessage = Expression({ configure: configureWithJson(configure), resultType: "string" });
|
|
26
|
+
return Schema.Struct({
|
|
27
|
+
type: Schema.Literal(type),
|
|
28
|
+
compatibilityDate: Schema.Literal(compatibilityDate),
|
|
29
|
+
template: Schema.Struct({
|
|
30
|
+
request: CelHttpRequest.annotations({
|
|
31
|
+
title: "\u8BF7\u6C42",
|
|
32
|
+
description: md`
|
|
33
|
+
返回 \`HttpRequest\` 的 CEL 表达式。未配置「下载请求」时直接作为文件下载请求;
|
|
34
|
+
配置了「下载请求」时作为第一步请求,其响应体(JSON)通过 \`json\` 暴露给「下载请求」与状态表达式
|
|
35
|
+
`
|
|
36
|
+
}),
|
|
37
|
+
download: Schema.optional(CelDownloadRequest.annotations({
|
|
38
|
+
title: "\u4E0B\u8F7D\u8BF7\u6C42",
|
|
39
|
+
description: md`
|
|
40
|
+
可选的第二步:返回 \`HttpRequest\` 的 CEL 表达式,可通过 \`json\` 引用第一步的响应体,
|
|
41
|
+
例如用 \`json.data.key\` 构造真正的下载请求
|
|
42
|
+
`
|
|
43
|
+
}))
|
|
44
|
+
}).annotations({
|
|
45
|
+
title: "\u4E0B\u8F7D",
|
|
46
|
+
description: "\u70B9\u51FB\u540E\u53D1\u8D77 HTTP \u8BF7\u6C42\u5E76\u628A\u54CD\u5E94\u4FDD\u5B58\u4E3A\u672C\u5730\u6587\u4EF6"
|
|
47
|
+
}),
|
|
48
|
+
messageExpression: Schema.optional(CelMessage.annotations({
|
|
49
|
+
title: "\u6D88\u606F",
|
|
50
|
+
description: md`
|
|
51
|
+
仅当配置了「下载请求」时生效;对第一步响应体(\`json\`)求值,返回 \`string\` 作为 toast 显示。
|
|
52
|
+
不配置则不显示。未配置「下载请求」时被忽略。
|
|
53
|
+
`
|
|
54
|
+
})),
|
|
55
|
+
resultExpression: Schema.optional(CelMessage.annotations({
|
|
56
|
+
title: "\u6267\u884C\u7ED3\u679C",
|
|
57
|
+
description: md`
|
|
58
|
+
仅当配置了「下载请求」时生效;对第一步响应体(\`json\`)求值,返回 \`string\`。
|
|
59
|
+
支持 \`'success' | 'warning' | 'error' | 'info'\`;其他值按 \`success\` 处理。不配置则按 \`success\` 处理。
|
|
60
|
+
|
|
61
|
+
\`error\` 与 \`warning\` 会跳过实际的文件下载,只触发对应分支的操作;
|
|
62
|
+
\`success\` 与 \`info\` 会继续执行「下载请求」并保存文件。
|
|
63
|
+
|
|
64
|
+
它同时决定 toast 的样式,以及完成后执行下方哪一组操作。
|
|
65
|
+
`
|
|
66
|
+
})),
|
|
67
|
+
onSuccess: Schema.optional(Triggers.annotations({
|
|
68
|
+
title: "\u6210\u529F",
|
|
69
|
+
description: "\u300C\u6267\u884C\u7ED3\u679C\u300D\u4E3A `success` \u65F6\uFF0C\u4E0B\u8F7D\u5B8C\u6210\u540E\u6267\u884C\u7684\u64CD\u4F5C\uFF1B\u672A\u914D\u7F6E\u300C\u4E0B\u8F7D\u8BF7\u6C42\u300D\u65F6\uFF0CHTTP \u6210\u529F\u5373\u89C6\u4E3A `success`"
|
|
70
|
+
})),
|
|
71
|
+
onWarning: Schema.optional(Triggers.annotations({
|
|
72
|
+
title: "\u8B66\u544A",
|
|
73
|
+
description: "\u300C\u6267\u884C\u7ED3\u679C\u300D\u4E3A `warning` \u65F6\u6267\u884C\u7684\u64CD\u4F5C\uFF1B\u8DF3\u8FC7\u5B9E\u9645\u7684\u6587\u4EF6\u4E0B\u8F7D"
|
|
74
|
+
})),
|
|
75
|
+
onError: Schema.optional(Triggers.annotations({
|
|
76
|
+
title: "\u9519\u8BEF",
|
|
77
|
+
description: "\u300C\u6267\u884C\u7ED3\u679C\u300D\u4E3A `error` \u65F6\u6267\u884C\u7684\u64CD\u4F5C\uFF1B\u8DF3\u8FC7\u5B9E\u9645\u7684\u6587\u4EF6\u4E0B\u8F7D"
|
|
78
|
+
})),
|
|
79
|
+
onInfo: Schema.optional(Triggers.annotations({
|
|
80
|
+
title: "\u4FE1\u606F",
|
|
81
|
+
description: "\u300C\u6267\u884C\u7ED3\u679C\u300D\u4E3A `info` \u65F6\uFF0C\u4E0B\u8F7D\u5B8C\u6210\u540E\u6267\u884C\u7684\u64CD\u4F5C"
|
|
82
|
+
}))
|
|
83
|
+
}).annotations({
|
|
84
|
+
title: "HttpDownloadAction",
|
|
85
|
+
description: "\u70B9\u51FB\u540E\u53D1\u8D77 HTTP \u8BF7\u6C42\u5E76\u628A\u54CD\u5E94\u4FDD\u5B58\u4E3A\u672C\u5730\u6587\u4EF6\uFF0C\u652F\u6301\u4E24\u6B65\u5F0F\uFF08\u5148\u53D6\u51ED\u636E\u518D\u4E0B\u8F7D\uFF09"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -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;
|
package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/config.vue
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { Icon } from "@iconify/vue";
|
|
4
|
+
import { ExpressionEditor } from "../../../../ui/expression-editor";
|
|
5
|
+
import { Switch } from "../../../../ui/switch";
|
|
6
|
+
import { Separator } from "../../../../ui/separator";
|
|
7
|
+
import { Field, FieldLabel } from "../../../../ui/field";
|
|
8
|
+
import { Locale } from "../../../../ui/locale";
|
|
9
|
+
import {
|
|
10
|
+
InputGroup,
|
|
11
|
+
InputGroupAddon,
|
|
12
|
+
InputGroupButton,
|
|
13
|
+
InputGroupInput,
|
|
14
|
+
InputGroupNumberField
|
|
15
|
+
} from "../../../../ui/input-group";
|
|
16
|
+
import { getStructFieldDescription, getStructFieldTitle } from "../../../utils/schema-meta";
|
|
17
|
+
import { Markdown } from "../../../../ui/markdown";
|
|
18
|
+
import DerivedValueEditor from "../../../../form/DerivedValueEditor.vue";
|
|
19
|
+
import { schema } from "./schema";
|
|
20
|
+
defineOptions({ name: "ShwfedTableSwitchRendererConfig" });
|
|
21
|
+
const value = defineModel({ type: Object, ...{ required: true } });
|
|
22
|
+
const fieldSchema = schema(() => {
|
|
23
|
+
});
|
|
24
|
+
const fieldTitle = (field) => getStructFieldTitle(fieldSchema, field) ?? field;
|
|
25
|
+
const fieldDescription = (field) => getStructFieldDescription(fieldSchema, field);
|
|
26
|
+
const ROW_VARS = {
|
|
27
|
+
row: { type: "dyn", label: "\u5F53\u524D\u884C\u6570\u636E" },
|
|
28
|
+
index: { type: "number", label: "\u884C\u7D22\u5F15" }
|
|
29
|
+
};
|
|
30
|
+
const bindingText = computed({
|
|
31
|
+
get: () => value.value.binding ?? "",
|
|
32
|
+
set: (v) => {
|
|
33
|
+
const trimmed = v.trim();
|
|
34
|
+
if (trimmed === "") delete value.value.binding;
|
|
35
|
+
else value.value.binding = trimmed;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
const hiddenModel = computed({
|
|
39
|
+
get: () => value.value.hidden ?? "",
|
|
40
|
+
set: (v) => {
|
|
41
|
+
if (v === "") delete value.value.hidden;
|
|
42
|
+
else value.value.hidden = v;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const disabledModel = computed({
|
|
46
|
+
get: () => value.value.disabled ?? "",
|
|
47
|
+
set: (v) => {
|
|
48
|
+
if (v === "") delete value.value.disabled;
|
|
49
|
+
else value.value.disabled = v;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
const readonlyModel = computed({
|
|
53
|
+
get: () => value.value.readonly ?? "",
|
|
54
|
+
set: (v) => {
|
|
55
|
+
if (v === "") delete value.value.readonly;
|
|
56
|
+
else value.value.readonly = v;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
const derivedModel = computed({
|
|
60
|
+
get: () => value.value.derived,
|
|
61
|
+
set: (v) => {
|
|
62
|
+
if (v == null) delete value.value.derived;
|
|
63
|
+
else value.value.derived = v;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<template>
|
|
69
|
+
<div class="space-y-5">
|
|
70
|
+
<div class="grid grid-cols-2 gap-x-6 gap-y-4">
|
|
71
|
+
<Field orientation="vertical">
|
|
72
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
73
|
+
<template
|
|
74
|
+
v-if="fieldDescription('title')"
|
|
75
|
+
#tooltip
|
|
76
|
+
>
|
|
77
|
+
<Markdown
|
|
78
|
+
:source="fieldDescription('title')"
|
|
79
|
+
block
|
|
80
|
+
class="prose prose-sm prose-zinc"
|
|
81
|
+
/>
|
|
82
|
+
</template>
|
|
83
|
+
{{ fieldTitle("title") }}
|
|
84
|
+
</FieldLabel>
|
|
85
|
+
<Locale v-model="value.title" />
|
|
86
|
+
</Field>
|
|
87
|
+
<Field orientation="vertical">
|
|
88
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
89
|
+
<template
|
|
90
|
+
v-if="fieldDescription('tooltip')"
|
|
91
|
+
#tooltip
|
|
92
|
+
>
|
|
93
|
+
<Markdown
|
|
94
|
+
:source="fieldDescription('tooltip')"
|
|
95
|
+
block
|
|
96
|
+
class="prose prose-sm prose-zinc"
|
|
97
|
+
/>
|
|
98
|
+
</template>
|
|
99
|
+
{{ fieldTitle("tooltip") }}
|
|
100
|
+
</FieldLabel>
|
|
101
|
+
<Locale
|
|
102
|
+
v-model="value.tooltip"
|
|
103
|
+
markdown
|
|
104
|
+
/>
|
|
105
|
+
</Field>
|
|
106
|
+
</div>
|
|
107
|
+
<div class="grid grid-cols-2 gap-x-6 gap-y-4">
|
|
108
|
+
<Field orientation="vertical">
|
|
109
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
110
|
+
<template
|
|
111
|
+
v-if="fieldDescription('binding')"
|
|
112
|
+
#tooltip
|
|
113
|
+
>
|
|
114
|
+
<Markdown
|
|
115
|
+
:source="fieldDescription('binding')"
|
|
116
|
+
block
|
|
117
|
+
class="prose prose-sm prose-zinc"
|
|
118
|
+
/>
|
|
119
|
+
</template>
|
|
120
|
+
{{ fieldTitle("binding") }}
|
|
121
|
+
</FieldLabel>
|
|
122
|
+
<InputGroup>
|
|
123
|
+
<InputGroupInput
|
|
124
|
+
v-model="bindingText"
|
|
125
|
+
placeholder="例:enabled"
|
|
126
|
+
class="font-mono"
|
|
127
|
+
/>
|
|
128
|
+
</InputGroup>
|
|
129
|
+
</Field>
|
|
130
|
+
<Field orientation="vertical">
|
|
131
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
132
|
+
<template
|
|
133
|
+
v-if="fieldDescription('size')"
|
|
134
|
+
#tooltip
|
|
135
|
+
>
|
|
136
|
+
<Markdown
|
|
137
|
+
:source="fieldDescription('size')"
|
|
138
|
+
block
|
|
139
|
+
class="prose prose-sm prose-zinc"
|
|
140
|
+
/>
|
|
141
|
+
</template>
|
|
142
|
+
{{ fieldTitle("size") }}
|
|
143
|
+
</FieldLabel>
|
|
144
|
+
<InputGroup>
|
|
145
|
+
<InputGroupNumberField
|
|
146
|
+
:model-value="value.size"
|
|
147
|
+
:disabled="value.grow"
|
|
148
|
+
:min="0"
|
|
149
|
+
@update:model-value="(v) => value.size = v"
|
|
150
|
+
/>
|
|
151
|
+
<InputGroupAddon align="inline-end">
|
|
152
|
+
<InputGroupButton
|
|
153
|
+
:variant="value.grow ? 'primary' : 'ghost'"
|
|
154
|
+
size="xs"
|
|
155
|
+
@click="value.grow = !value.grow"
|
|
156
|
+
>
|
|
157
|
+
<Icon :icon="value.grow ? 'fluent:lock-closed-20-regular' : 'fluent:arrow-autofit-width-20-regular'" />
|
|
158
|
+
{{ fieldTitle("grow") }}
|
|
159
|
+
</InputGroupButton>
|
|
160
|
+
</InputGroupAddon>
|
|
161
|
+
</InputGroup>
|
|
162
|
+
</Field>
|
|
163
|
+
<Field orientation="vertical">
|
|
164
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
165
|
+
<template
|
|
166
|
+
v-if="fieldDescription('trueLabel')"
|
|
167
|
+
#tooltip
|
|
168
|
+
>
|
|
169
|
+
<Markdown
|
|
170
|
+
:source="fieldDescription('trueLabel')"
|
|
171
|
+
block
|
|
172
|
+
class="prose prose-sm prose-zinc"
|
|
173
|
+
/>
|
|
174
|
+
</template>
|
|
175
|
+
{{ fieldTitle("trueLabel") }}
|
|
176
|
+
</FieldLabel>
|
|
177
|
+
<Locale v-model="value.trueLabel" />
|
|
178
|
+
</Field>
|
|
179
|
+
<Field orientation="vertical">
|
|
180
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
181
|
+
<template
|
|
182
|
+
v-if="fieldDescription('falseLabel')"
|
|
183
|
+
#tooltip
|
|
184
|
+
>
|
|
185
|
+
<Markdown
|
|
186
|
+
:source="fieldDescription('falseLabel')"
|
|
187
|
+
block
|
|
188
|
+
class="prose prose-sm prose-zinc"
|
|
189
|
+
/>
|
|
190
|
+
</template>
|
|
191
|
+
{{ fieldTitle("falseLabel") }}
|
|
192
|
+
</FieldLabel>
|
|
193
|
+
<Locale v-model="value.falseLabel" />
|
|
194
|
+
</Field>
|
|
195
|
+
</div>
|
|
196
|
+
<div class="grid grid-cols-2 gap-x-6 gap-y-4">
|
|
197
|
+
<Field orientation="vertical">
|
|
198
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
199
|
+
<template
|
|
200
|
+
v-if="fieldDescription('hidden')"
|
|
201
|
+
#tooltip
|
|
202
|
+
>
|
|
203
|
+
<Markdown
|
|
204
|
+
:source="fieldDescription('hidden')"
|
|
205
|
+
block
|
|
206
|
+
class="prose prose-sm prose-zinc"
|
|
207
|
+
/>
|
|
208
|
+
</template>
|
|
209
|
+
{{ fieldTitle("hidden") }}
|
|
210
|
+
</FieldLabel>
|
|
211
|
+
<ExpressionEditor
|
|
212
|
+
v-model="hiddenModel"
|
|
213
|
+
placeholder="例:row.archived"
|
|
214
|
+
result-type="bool"
|
|
215
|
+
:extra-vars="ROW_VARS"
|
|
216
|
+
/>
|
|
217
|
+
</Field>
|
|
218
|
+
<Field orientation="vertical">
|
|
219
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
220
|
+
<template
|
|
221
|
+
v-if="fieldDescription('disabled')"
|
|
222
|
+
#tooltip
|
|
223
|
+
>
|
|
224
|
+
<Markdown
|
|
225
|
+
:source="fieldDescription('disabled')"
|
|
226
|
+
block
|
|
227
|
+
class="prose prose-sm prose-zinc"
|
|
228
|
+
/>
|
|
229
|
+
</template>
|
|
230
|
+
{{ fieldTitle("disabled") }}
|
|
231
|
+
</FieldLabel>
|
|
232
|
+
<ExpressionEditor
|
|
233
|
+
v-model="disabledModel"
|
|
234
|
+
placeholder="例:row.locked"
|
|
235
|
+
result-type="bool"
|
|
236
|
+
:extra-vars="ROW_VARS"
|
|
237
|
+
/>
|
|
238
|
+
</Field>
|
|
239
|
+
<Field orientation="vertical">
|
|
240
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
241
|
+
<template
|
|
242
|
+
v-if="fieldDescription('readonly')"
|
|
243
|
+
#tooltip
|
|
244
|
+
>
|
|
245
|
+
<Markdown
|
|
246
|
+
:source="fieldDescription('readonly')"
|
|
247
|
+
block
|
|
248
|
+
class="prose prose-sm prose-zinc"
|
|
249
|
+
/>
|
|
250
|
+
</template>
|
|
251
|
+
{{ fieldTitle("readonly") }}
|
|
252
|
+
</FieldLabel>
|
|
253
|
+
<ExpressionEditor
|
|
254
|
+
v-model="readonlyModel"
|
|
255
|
+
placeholder="例:row.id != null"
|
|
256
|
+
result-type="bool"
|
|
257
|
+
:extra-vars="ROW_VARS"
|
|
258
|
+
/>
|
|
259
|
+
</Field>
|
|
260
|
+
<Field
|
|
261
|
+
orientation="vertical"
|
|
262
|
+
class="col-span-2"
|
|
263
|
+
>
|
|
264
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
265
|
+
<template
|
|
266
|
+
v-if="fieldDescription('derived')"
|
|
267
|
+
#tooltip
|
|
268
|
+
>
|
|
269
|
+
<Markdown
|
|
270
|
+
:source="fieldDescription('derived')"
|
|
271
|
+
block
|
|
272
|
+
class="prose prose-sm prose-zinc"
|
|
273
|
+
/>
|
|
274
|
+
</template>
|
|
275
|
+
{{ fieldTitle("derived") }}
|
|
276
|
+
</FieldLabel>
|
|
277
|
+
<DerivedValueEditor
|
|
278
|
+
v-model="derivedModel"
|
|
279
|
+
result-type="bool"
|
|
280
|
+
placeholder="例:row.quantity > 0"
|
|
281
|
+
/>
|
|
282
|
+
</Field>
|
|
283
|
+
</div>
|
|
284
|
+
<Separator />
|
|
285
|
+
<div class="flex flex-wrap gap-x-8 gap-y-3">
|
|
286
|
+
<Field
|
|
287
|
+
orientation="horizontal"
|
|
288
|
+
class="w-auto gap-2"
|
|
289
|
+
>
|
|
290
|
+
<Switch
|
|
291
|
+
:model-value="value.enableSorting ?? false"
|
|
292
|
+
@update:model-value="(v) => value.enableSorting = v"
|
|
293
|
+
/>
|
|
294
|
+
<FieldLabel class="text-sm text-zinc-600">
|
|
295
|
+
<template
|
|
296
|
+
v-if="fieldDescription('enableSorting')"
|
|
297
|
+
#tooltip
|
|
298
|
+
>
|
|
299
|
+
<Markdown
|
|
300
|
+
:source="fieldDescription('enableSorting')"
|
|
301
|
+
block
|
|
302
|
+
class="prose prose-sm prose-zinc"
|
|
303
|
+
/>
|
|
304
|
+
</template>
|
|
305
|
+
{{ fieldTitle("enableSorting") }}
|
|
306
|
+
</FieldLabel>
|
|
307
|
+
</Field>
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
</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,9 @@
|
|
|
1
|
+
import type { CellContext } from '@tanstack/vue-table';
|
|
2
|
+
import type { Value } from './schema.js';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
column: Value;
|
|
5
|
+
ctx: CellContext<unknown, unknown>;
|
|
6
|
+
};
|
|
7
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
8
|
+
declare const _default: typeof __VLS_export;
|
|
9
|
+
export default _default;
|
package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/runtime.vue
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { computed } from "vue";
|
|
4
|
+
import { useI18n } from "vue-i18n";
|
|
5
|
+
import { cel as _rawCel } from "../../../../../utils/cel";
|
|
6
|
+
import { celBindings, injectCELContext } from "../../../../../utils/cel-context";
|
|
7
|
+
import { getLocalizedText } from "../../../../../share/locale";
|
|
8
|
+
import { Switch } from "../../../../ui/switch";
|
|
9
|
+
import { useFieldValue } from "../../../../form/utils/field-value";
|
|
10
|
+
defineOptions({ name: "ShwfedTableSwitchRendererRuntime" });
|
|
11
|
+
const props = defineProps({
|
|
12
|
+
column: { type: null, required: true },
|
|
13
|
+
ctx: { type: Object, required: true }
|
|
14
|
+
});
|
|
15
|
+
const { locale } = useI18n();
|
|
16
|
+
const inherited = injectCELContext();
|
|
17
|
+
const $cel = (expression, context) => _rawCel(expression, { ...celBindings(inherited), ...context });
|
|
18
|
+
function evalBool(expression, label) {
|
|
19
|
+
if (!expression) return false;
|
|
20
|
+
try {
|
|
21
|
+
return Effect.runSync($cel(expression)) === true;
|
|
22
|
+
} catch (e) {
|
|
23
|
+
console.error(`[shwfed-table] switch ${label} failed`, e);
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const isHidden = computed(() => evalBool(props.column.hidden, "hidden"));
|
|
28
|
+
const isDisabled = computed(() => evalBool(props.column.disabled, "disabled"));
|
|
29
|
+
const isReadonly = computed(() => evalBool(props.column.readonly, "readonly"));
|
|
30
|
+
const effectiveReadonly = computed(
|
|
31
|
+
() => isReadonly.value || props.column.derived?.mode === "formula"
|
|
32
|
+
);
|
|
33
|
+
const trueText = computed(
|
|
34
|
+
() => getLocalizedText(props.column.trueLabel, locale.value) || "\u662F"
|
|
35
|
+
);
|
|
36
|
+
const falseText = computed(
|
|
37
|
+
() => getLocalizedText(props.column.falseLabel, locale.value) || "\u5426"
|
|
38
|
+
);
|
|
39
|
+
const { draft, commit } = useFieldValue({
|
|
40
|
+
binding: () => props.column.binding,
|
|
41
|
+
fromState: (raw) => raw === true,
|
|
42
|
+
toState: (next) => next === true
|
|
43
|
+
});
|
|
44
|
+
function onUpdate(next) {
|
|
45
|
+
draft.value = next;
|
|
46
|
+
commit();
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<!--
|
|
52
|
+
Row budget matches the editable text/number cells: 2px outer inset around
|
|
53
|
+
an `h-7` (28px) row — 32px total. The switch is centered inside that row;
|
|
54
|
+
`sm` size (h-3.5) keeps it visually compact against the surrounding cells.
|
|
55
|
+
Every branch (hidden / readonly / editable) is sized identically so rows
|
|
56
|
+
do not jump when a CEL condition flips.
|
|
57
|
+
-->
|
|
58
|
+
<div class="p-[0.125rem] w-full">
|
|
59
|
+
<span
|
|
60
|
+
v-if="isHidden"
|
|
61
|
+
class="block h-7 w-full"
|
|
62
|
+
/>
|
|
63
|
+
<span
|
|
64
|
+
v-else-if="effectiveReadonly"
|
|
65
|
+
class="flex items-center justify-center h-7 w-full px-2 text-[0.75rem] text-zinc-700 truncate"
|
|
66
|
+
>
|
|
67
|
+
{{ draft ? trueText : falseText }}
|
|
68
|
+
</span>
|
|
69
|
+
<span
|
|
70
|
+
v-else
|
|
71
|
+
class="flex items-center justify-center h-7 w-full"
|
|
72
|
+
>
|
|
73
|
+
<Switch
|
|
74
|
+
size="sm"
|
|
75
|
+
:model-value="draft"
|
|
76
|
+
:disabled="isDisabled"
|
|
77
|
+
@update:model-value="onUpdate"
|
|
78
|
+
/>
|
|
79
|
+
</span>
|
|
80
|
+
</div>
|
|
81
|
+
</template>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CellContext } from '@tanstack/vue-table';
|
|
2
|
+
import type { Value } from './schema.js';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
column: Value;
|
|
5
|
+
ctx: CellContext<unknown, unknown>;
|
|
6
|
+
};
|
|
7
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
8
|
+
declare const _default: typeof __VLS_export;
|
|
9
|
+
export default _default;
|
package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/schema.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import type { ColumnDef } from '@tanstack/vue-table';
|
|
3
|
+
import type { Environment } from '../../../../../vendor/cel-js/lib/index.js';
|
|
4
|
+
import type { ColumnDefDeps } from '../../../utils/resolve.js';
|
|
5
|
+
export declare const type: "com.shwfed.table.column.switch";
|
|
6
|
+
export declare const compatibilityDate: "2026-05-20";
|
|
7
|
+
export declare const metadata: {
|
|
8
|
+
readonly name: "开关";
|
|
9
|
+
readonly icon: "fluent:toggle-left-20-regular";
|
|
10
|
+
};
|
|
11
|
+
export declare function schema(configure: (env: Environment) => void): Schema.Struct<{
|
|
12
|
+
hidden: Schema.optional<Schema.Schema<string, string, never>>;
|
|
13
|
+
disabled: Schema.optional<Schema.Schema<string, string, never>>;
|
|
14
|
+
readonly: Schema.optional<Schema.Schema<string, string, never>>;
|
|
15
|
+
trueLabel: Schema.optional<Schema.TupleType<readonly [Schema.Struct<{
|
|
16
|
+
locale: Schema.Literal<["zh"]>;
|
|
17
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
18
|
+
}>], [Schema.Struct<{
|
|
19
|
+
locale: Schema.Literal<["ja", "en", "ko"]>;
|
|
20
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
21
|
+
}>]>>;
|
|
22
|
+
falseLabel: Schema.optional<Schema.TupleType<readonly [Schema.Struct<{
|
|
23
|
+
locale: Schema.Literal<["zh"]>;
|
|
24
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
25
|
+
}>], [Schema.Struct<{
|
|
26
|
+
locale: Schema.Literal<["ja", "en", "ko"]>;
|
|
27
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
28
|
+
}>]>>;
|
|
29
|
+
derived: Schema.optional<Schema.Struct<{
|
|
30
|
+
mode: Schema.Literal<["formula", "prefill"]>;
|
|
31
|
+
expression: Schema.Schema<string, string, never>;
|
|
32
|
+
}>>;
|
|
33
|
+
title: Schema.TupleType<readonly [Schema.Struct<{
|
|
34
|
+
locale: Schema.Literal<["zh"]>;
|
|
35
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
36
|
+
}>], [Schema.Struct<{
|
|
37
|
+
locale: Schema.Literal<["ja", "en", "ko"]>;
|
|
38
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
39
|
+
}>]>;
|
|
40
|
+
binding: Schema.refine<string, typeof Schema.String>;
|
|
41
|
+
enableSorting: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
|
|
42
|
+
size: Schema.optional<Schema.refine<number, Schema.filter<typeof Schema.Number>>>;
|
|
43
|
+
grow: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
|
|
44
|
+
tooltip: Schema.optional<Schema.TupleType<readonly [Schema.Struct<{
|
|
45
|
+
locale: Schema.Literal<["zh"]>;
|
|
46
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
47
|
+
}>], [Schema.Struct<{
|
|
48
|
+
locale: Schema.Literal<["ja", "en", "ko"]>;
|
|
49
|
+
message: Schema.SchemaClass<string, string, never>;
|
|
50
|
+
}>]>>;
|
|
51
|
+
id: Schema.refine<string, typeof Schema.String>;
|
|
52
|
+
groupId: Schema.optional<typeof Schema.UUID>;
|
|
53
|
+
type: Schema.Literal<["com.shwfed.table.column.switch"]>;
|
|
54
|
+
compatibilityDate: Schema.Literal<["2026-05-20"]>;
|
|
55
|
+
}>;
|
|
56
|
+
export type Value = Schema.Schema.Type<ReturnType<typeof schema>>;
|
|
57
|
+
export declare function toColumnDef(value: Value, { getLocaleText }: ColumnDefDeps): Partial<ColumnDef<unknown, unknown>>;
|
package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch/schema.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { getProperty } from "dot-prop";
|
|
3
|
+
import { Locale } from "../../../../../share/locale.js";
|
|
4
|
+
import { CelRowAccess, derivedRowField, editableColumnFields, editableHeader } from "../../../utils/shared.js";
|
|
5
|
+
export const type = "com.shwfed.table.column.switch";
|
|
6
|
+
export const compatibilityDate = "2026-05-20";
|
|
7
|
+
export const metadata = {
|
|
8
|
+
name: "\u5F00\u5173",
|
|
9
|
+
icon: "fluent:toggle-left-20-regular"
|
|
10
|
+
};
|
|
11
|
+
export function schema(configure) {
|
|
12
|
+
const CelBool = CelRowAccess(configure, { resultType: "bool" });
|
|
13
|
+
return Schema.Struct({
|
|
14
|
+
type: Schema.Literal(type),
|
|
15
|
+
compatibilityDate: Schema.Literal(compatibilityDate),
|
|
16
|
+
...editableColumnFields(),
|
|
17
|
+
hidden: Schema.optional(CelBool.annotations({
|
|
18
|
+
title: "\u9690\u85CF\u6761\u4EF6",
|
|
19
|
+
description: "\u8FD4\u56DE `true` \u65F6\u8BE5\u884C\u7684\u5F00\u5173\u4E0D\u6E32\u67D3\uFF08\u5176\u4F59\u884C\u4E0D\u53D7\u5F71\u54CD\uFF09"
|
|
20
|
+
})),
|
|
21
|
+
disabled: Schema.optional(CelBool.annotations({
|
|
22
|
+
title: "\u7981\u7528\u6761\u4EF6",
|
|
23
|
+
description: "\u8FD4\u56DE `true` \u65F6\u5F00\u5173\u4ECD\u7136\u6E32\u67D3\u4F46\u4E0D\u53EF\u5207\u6362"
|
|
24
|
+
})),
|
|
25
|
+
readonly: Schema.optional(CelBool.annotations({
|
|
26
|
+
title: "\u53EA\u8BFB\u6761\u4EF6",
|
|
27
|
+
description: "\u8FD4\u56DE `true` \u65F6\u4EC5\u4EE5\u7EAF\u6587\u672C\u5C55\u793A\u5F53\u524D\u503C"
|
|
28
|
+
})),
|
|
29
|
+
trueLabel: Schema.optional(Locale.annotations({
|
|
30
|
+
title: "\u5F00\u542F\u6587\u6848",
|
|
31
|
+
description: "\u53EA\u8BFB\u72B6\u6001\u4E0B\uFF0C\u503C\u4E3A `true` \u65F6\u5C55\u793A\u7684\u6587\u672C\uFF1B\u7559\u7A7A\u4F7F\u7528\u9ED8\u8BA4 `\u662F`"
|
|
32
|
+
})),
|
|
33
|
+
falseLabel: Schema.optional(Locale.annotations({
|
|
34
|
+
title: "\u5173\u95ED\u6587\u6848",
|
|
35
|
+
description: "\u53EA\u8BFB\u72B6\u6001\u4E0B\uFF0C\u503C\u4E3A `false` \u65F6\u5C55\u793A\u7684\u6587\u672C\uFF1B\u7559\u7A7A\u4F7F\u7528\u9ED8\u8BA4 `\u5426`"
|
|
36
|
+
})),
|
|
37
|
+
derived: derivedRowField(configure, "bool")
|
|
38
|
+
}).annotations({ title: "SwitchRenderer", description: "\u5F00\u5173\u6E32\u67D3\u5668\uFF08\u53EF\u7F16\u8F91\u3001\u672C\u5730\uFF09" });
|
|
39
|
+
}
|
|
40
|
+
export function toColumnDef(value, { getLocaleText }) {
|
|
41
|
+
return {
|
|
42
|
+
header: editableHeader(getLocaleText(value.title)),
|
|
43
|
+
// `binding` is a dot-prop path (validated non-empty); the cell renderer
|
|
44
|
+
// reads through the same path it writes to. No CEL — accessor is literal.
|
|
45
|
+
accessorFn: (row) => {
|
|
46
|
+
if (!row || typeof row !== "object") return void 0;
|
|
47
|
+
return getProperty(row, value.binding);
|
|
48
|
+
},
|
|
49
|
+
enableSorting: value.enableSorting ?? false,
|
|
50
|
+
// `basic` orders by JS `<` — for booleans that yields `false < true`,
|
|
51
|
+
// which is the obvious grouping. Mirrors the remote switch column.
|
|
52
|
+
sortingFn: "basic",
|
|
53
|
+
size: value.size,
|
|
54
|
+
meta: {
|
|
55
|
+
grow: value.grow ?? false,
|
|
56
|
+
tooltip: getLocaleText(value.tooltip)
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|