@shwfed/config 2.3.9 → 2.3.11
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/mcp.mjs +1064 -475
- package/dist/module.json +1 -1
- package/dist/preview/assets/config-57-v4VXo.js +1 -0
- package/dist/preview/assets/config-BZahzuEc.js +1 -0
- package/dist/preview/assets/config-Be-2ZA2R.js +1 -0
- package/dist/preview/assets/config-BnZQO-Sp.js +1 -0
- package/dist/preview/assets/config-Bp91DUdU.js +1 -0
- package/dist/preview/assets/config-DNokxY7M.js +1 -0
- package/dist/preview/assets/config-_msO_f2R.js +1 -0
- package/dist/preview/assets/config-_uPI8qV-.js +1 -0
- package/dist/preview/assets/definition.vue_vue_type_script_setup_true_lang-BtScXbs1.js +1 -0
- package/dist/preview/assets/index-DDbl2Atj.js +1 -0
- package/dist/preview/assets/index-DGa3Oj3y.js +1075 -0
- package/dist/preview/assets/index-mbGtsgdv.css +1 -0
- package/dist/preview/assets/runtime-B9u14qqB.js +1 -0
- package/dist/preview/assets/runtime-BBms4myv.js +1 -0
- package/dist/preview/assets/runtime-CfR7ZAND.js +1 -0
- package/dist/preview/assets/runtime-D3EyeiyA.js +1 -0
- package/dist/preview/assets/runtime-Dk9u-Ybw.js +1 -0
- package/dist/preview/assets/runtime-XXqIAt53.js +1 -0
- package/dist/preview/assets/runtime-cKWSGFod.js +1 -0
- package/dist/preview/assets/runtime-w7V-p3t1.js +1 -0
- package/dist/preview/index.html +19 -0
- package/dist/runtime/components/actions/buttons/2026-05-15/com.shwfed.actions.button.event.dispatch/schema.js +3 -1
- package/dist/runtime/components/form/ai/fields-button.vue +3 -1
- package/dist/runtime/components/form/unit-config.vue +8 -4
- package/dist/runtime/components/form/utils/resolve.d.ts +48 -9
- package/dist/runtime/components/form/utils/resolve.js +59 -5
- package/dist/runtime/components/table/ai/columns-button.vue +1 -0
- package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/config.d.vue.ts +10 -0
- package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/config.vue +305 -0
- package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/config.vue.d.ts +10 -0
- package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/runtime.d.vue.ts +9 -0
- package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/runtime.vue +81 -0
- package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/runtime.vue.d.ts +9 -0
- package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/schema.d.ts +55 -0
- package/dist/runtime/components/table/columns/2026-05-13/com.shwfed.table.column.switch.remote/schema.js +82 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/config.d.vue.ts +10 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/config.vue +310 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/config.vue.d.ts +10 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/runtime.d.vue.ts +9 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/runtime.vue +81 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/runtime.vue.d.ts +9 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/schema.d.ts +66 -0
- package/dist/runtime/components/table/columns/2026-05-20/com.shwfed.table.column.switch.local/schema.js +71 -0
- package/dist/runtime/components/table/config.vue +2 -1
- package/dist/runtime/components/table/utils/resolve.d.ts +53 -0
- package/dist/runtime/components/table/utils/resolve.js +66 -1
- package/package.json +2 -2
|
@@ -11,28 +11,76 @@ const runtimeModules = import.meta.glob(
|
|
|
11
11
|
"../fields/*/*/runtime.vue",
|
|
12
12
|
{ eager: true, import: "default", exhaustive: true }
|
|
13
13
|
);
|
|
14
|
+
const sourceKey = (type, compatibilityDate) => `${type}@${compatibilityDate}`;
|
|
14
15
|
const registry = defineRegistry({
|
|
15
16
|
hostName: "shwfed-form",
|
|
16
17
|
dirSegment: "fields",
|
|
17
18
|
schemaModules,
|
|
18
19
|
configModules,
|
|
19
20
|
runtimeModules,
|
|
20
|
-
// An entry is deprecated
|
|
21
|
-
//
|
|
22
|
-
//
|
|
21
|
+
// An entry is deprecated by one of two rules:
|
|
22
|
+
// (a) another entry shares its `type` with a strictly greater
|
|
23
|
+
// `compatibilityDate` — date-versioned supersession;
|
|
24
|
+
// (b) another entry declares this one in its `migrateFrom` — explicit
|
|
25
|
+
// cross-type supersession (rename, split, merge).
|
|
26
|
+
// Rule (a) wins on overlap, since the date-versioned rule is the implicit
|
|
27
|
+
// convention; an explicit `migrateFrom` against a still-current entry is
|
|
28
|
+
// almost always a mistake, but we let it slide as long as no two targets
|
|
29
|
+
// claim the same source (that collision throws below).
|
|
23
30
|
deriveEntryExtras: (drafts) => {
|
|
24
31
|
const latestByType = /* @__PURE__ */ new Map();
|
|
25
32
|
for (const d of drafts) {
|
|
26
33
|
const prev = latestByType.get(d.type);
|
|
27
34
|
if (!prev || d.compatibilityDate > prev) latestByType.set(d.type, d.compatibilityDate);
|
|
28
35
|
}
|
|
36
|
+
const migrationTargets = /* @__PURE__ */ new Map();
|
|
37
|
+
for (const d of drafts) {
|
|
38
|
+
const sources = d.module.migrateFrom;
|
|
39
|
+
if (!sources || sources.length === 0) continue;
|
|
40
|
+
for (const src of sources) {
|
|
41
|
+
const key = sourceKey(src.type, src.compatibilityDate);
|
|
42
|
+
const existing = migrationTargets.get(key);
|
|
43
|
+
if (existing) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`[shwfed-form] migrateFrom collision: source "${key}" is claimed by both "${sourceKey(existing.type, existing.compatibilityDate)}" and "${sourceKey(d.type, d.compatibilityDate)}"`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
migrationTargets.set(key, { type: d.type, compatibilityDate: d.compatibilityDate });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
29
51
|
return (draft) => {
|
|
52
|
+
const declared = migrationTargets.get(sourceKey(draft.type, draft.compatibilityDate));
|
|
53
|
+
if (declared) {
|
|
54
|
+
return { deprecated: true, supersededBy: declared };
|
|
55
|
+
}
|
|
30
56
|
const latest = latestByType.get(draft.type);
|
|
31
|
-
|
|
32
|
-
|
|
57
|
+
if (draft.compatibilityDate < latest) {
|
|
58
|
+
return {
|
|
59
|
+
deprecated: true,
|
|
60
|
+
supersededBy: { type: draft.type, compatibilityDate: latest }
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return { deprecated: false };
|
|
33
64
|
};
|
|
34
65
|
}
|
|
35
66
|
});
|
|
67
|
+
let _migrationTargets = null;
|
|
68
|
+
function getMigrationTargets() {
|
|
69
|
+
if (_migrationTargets) return _migrationTargets;
|
|
70
|
+
const map = /* @__PURE__ */ new Map();
|
|
71
|
+
for (const entry of registry.ENTRIES) {
|
|
72
|
+
const sources = entry.module.migrateFrom;
|
|
73
|
+
if (!sources || sources.length === 0) continue;
|
|
74
|
+
for (const src of sources) {
|
|
75
|
+
map.set(sourceKey(src.type, src.compatibilityDate), {
|
|
76
|
+
type: entry.type,
|
|
77
|
+
compatibilityDate: entry.compatibilityDate
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
_migrationTargets = map;
|
|
82
|
+
return map;
|
|
83
|
+
}
|
|
36
84
|
let _fields = null;
|
|
37
85
|
function getFields() {
|
|
38
86
|
if (_fields) return _fields;
|
|
@@ -46,6 +94,7 @@ function getFields() {
|
|
|
46
94
|
deprecated: e.deprecated,
|
|
47
95
|
...e.supersededBy !== void 0 ? { supersededBy: e.supersededBy } : {},
|
|
48
96
|
migrate: e.module.migrate,
|
|
97
|
+
...e.module.migrateFrom !== void 0 ? { migrateFrom: e.module.migrateFrom } : {},
|
|
49
98
|
defaults: e.module.defaults
|
|
50
99
|
}));
|
|
51
100
|
return _fields;
|
|
@@ -72,3 +121,8 @@ export function allFieldSchemas(configure) {
|
|
|
72
121
|
export function findField(type, compatibilityDate) {
|
|
73
122
|
return getFields().find((f) => f.type === type && f.compatibilityDate === compatibilityDate);
|
|
74
123
|
}
|
|
124
|
+
export function findMigrationTarget(type, compatibilityDate) {
|
|
125
|
+
const target = getMigrationTargets().get(sourceKey(type, compatibilityDate));
|
|
126
|
+
if (!target) return void 0;
|
|
127
|
+
return findField(target.type, target.compatibilityDate);
|
|
128
|
+
}
|
|
@@ -80,6 +80,7 @@ function renderRegisterables() {
|
|
|
80
80
|
lines.push("");
|
|
81
81
|
for (const entry of COLUMNS) {
|
|
82
82
|
if (entry.type === PROHIBITED_TYPE) continue;
|
|
83
|
+
if (entry.deprecated) continue;
|
|
83
84
|
const fields = describeColumnFields(entry.type);
|
|
84
85
|
lines.push(`## \`${entry.type}\` (compatibilityDate \`${entry.compatibilityDate}\`)`);
|
|
85
86
|
lines.push("");
|
|
@@ -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,305 @@
|
|
|
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
|
+
InputGroupNumberField
|
|
14
|
+
} from "../../../../ui/input-group";
|
|
15
|
+
import {
|
|
16
|
+
DropdownMenu,
|
|
17
|
+
DropdownMenuContent,
|
|
18
|
+
DropdownMenuItem,
|
|
19
|
+
DropdownMenuTrigger
|
|
20
|
+
} from "../../../../ui/dropdown-menu";
|
|
21
|
+
import { getStructFieldDescription, getStructFieldTitle } from "../../../utils/schema-meta";
|
|
22
|
+
import { Markdown } from "../../../../ui/markdown";
|
|
23
|
+
import TriggersField from "../../../../actions/components/triggers-field.vue";
|
|
24
|
+
import { schema } from "./schema";
|
|
25
|
+
defineOptions({ name: "ShwfedTableSwitchRendererConfig" });
|
|
26
|
+
const value = defineModel({ type: Object, ...{ required: true } });
|
|
27
|
+
const fieldSchema = schema(() => {
|
|
28
|
+
});
|
|
29
|
+
const fieldTitle = (field) => getStructFieldTitle(fieldSchema, field) ?? field;
|
|
30
|
+
const fieldDescription = (field) => getStructFieldDescription(fieldSchema, field);
|
|
31
|
+
const ALIGN_OPTIONS = [
|
|
32
|
+
{ value: "left", label: "\u5DE6\u5BF9\u9F50", icon: "fluent:text-align-left-20-regular" },
|
|
33
|
+
{ value: "center", label: "\u5C45\u4E2D", icon: "fluent:text-align-center-20-regular" },
|
|
34
|
+
{ value: "right", label: "\u53F3\u5BF9\u9F50", icon: "fluent:text-align-right-20-regular" }
|
|
35
|
+
];
|
|
36
|
+
const currentAlignIcon = computed(
|
|
37
|
+
() => (ALIGN_OPTIONS.find((o) => o.value === (value.value.align ?? "center")) ?? ALIGN_OPTIONS[1]).icon
|
|
38
|
+
);
|
|
39
|
+
const ROW_VARS = {
|
|
40
|
+
row: { type: "dyn", label: "\u5F53\u524D\u884C\u6570\u636E" },
|
|
41
|
+
index: { type: "number", label: "\u884C\u7D22\u5F15" }
|
|
42
|
+
};
|
|
43
|
+
const ROW_VALUE_VARS = {
|
|
44
|
+
...ROW_VARS,
|
|
45
|
+
value: { type: "bool", label: "\u5207\u6362\u540E\u7684\u65B0\u503C" }
|
|
46
|
+
};
|
|
47
|
+
const JSON_VARS = {
|
|
48
|
+
json: { type: "dyn", label: "HTTP \u54CD\u5E94\u4F53" }
|
|
49
|
+
};
|
|
50
|
+
const disabledModel = computed({
|
|
51
|
+
get: () => value.value.disabled ?? "",
|
|
52
|
+
set: (v) => {
|
|
53
|
+
if (v === "") delete value.value.disabled;
|
|
54
|
+
else value.value.disabled = v;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const onChangeModel = computed({
|
|
58
|
+
get: () => value.value.onChange ?? "",
|
|
59
|
+
set: (v) => {
|
|
60
|
+
if (v === "") delete value.value.onChange;
|
|
61
|
+
else value.value.onChange = v;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const successMessageModel = computed({
|
|
65
|
+
get: () => value.value.successMessage ?? "",
|
|
66
|
+
set: (v) => {
|
|
67
|
+
if (v === "") delete value.value.successMessage;
|
|
68
|
+
else value.value.successMessage = v;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
const triggers = computed(() => value.value.triggers ?? []);
|
|
72
|
+
function updateTriggers(next) {
|
|
73
|
+
if (next.length === 0) delete value.value.triggers;
|
|
74
|
+
else value.value.triggers = next;
|
|
75
|
+
}
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<template>
|
|
79
|
+
<div class="space-y-5">
|
|
80
|
+
<div class="grid grid-cols-2 gap-x-6 gap-y-4">
|
|
81
|
+
<Field orientation="vertical">
|
|
82
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
83
|
+
<template
|
|
84
|
+
v-if="fieldDescription('title')"
|
|
85
|
+
#tooltip
|
|
86
|
+
>
|
|
87
|
+
<Markdown
|
|
88
|
+
:source="fieldDescription('title')"
|
|
89
|
+
block
|
|
90
|
+
class="prose prose-sm prose-zinc"
|
|
91
|
+
/>
|
|
92
|
+
</template>
|
|
93
|
+
{{ fieldTitle("title") }}
|
|
94
|
+
</FieldLabel>
|
|
95
|
+
<Locale v-model="value.title" />
|
|
96
|
+
</Field>
|
|
97
|
+
<Field orientation="vertical">
|
|
98
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
99
|
+
<template
|
|
100
|
+
v-if="fieldDescription('tooltip')"
|
|
101
|
+
#tooltip
|
|
102
|
+
>
|
|
103
|
+
<Markdown
|
|
104
|
+
:source="fieldDescription('tooltip')"
|
|
105
|
+
block
|
|
106
|
+
class="prose prose-sm prose-zinc"
|
|
107
|
+
/>
|
|
108
|
+
</template>
|
|
109
|
+
{{ fieldTitle("tooltip") }}
|
|
110
|
+
</FieldLabel>
|
|
111
|
+
<Locale
|
|
112
|
+
v-model="value.tooltip"
|
|
113
|
+
markdown
|
|
114
|
+
/>
|
|
115
|
+
</Field>
|
|
116
|
+
</div>
|
|
117
|
+
<div class="grid grid-cols-2 gap-x-6 gap-y-4">
|
|
118
|
+
<Field orientation="vertical">
|
|
119
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
120
|
+
<template
|
|
121
|
+
v-if="fieldDescription('accessor')"
|
|
122
|
+
#tooltip
|
|
123
|
+
>
|
|
124
|
+
<Markdown
|
|
125
|
+
:source="fieldDescription('accessor')"
|
|
126
|
+
block
|
|
127
|
+
class="prose prose-sm prose-zinc"
|
|
128
|
+
/>
|
|
129
|
+
</template>
|
|
130
|
+
{{ fieldTitle("accessor") }}
|
|
131
|
+
</FieldLabel>
|
|
132
|
+
<ExpressionEditor
|
|
133
|
+
v-model="value.accessor"
|
|
134
|
+
placeholder="如 row.enabled"
|
|
135
|
+
result-type="bool"
|
|
136
|
+
:extra-vars="ROW_VARS"
|
|
137
|
+
/>
|
|
138
|
+
</Field>
|
|
139
|
+
<Field orientation="vertical">
|
|
140
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
141
|
+
<template
|
|
142
|
+
v-if="fieldDescription('size')"
|
|
143
|
+
#tooltip
|
|
144
|
+
>
|
|
145
|
+
<Markdown
|
|
146
|
+
:source="fieldDescription('size')"
|
|
147
|
+
block
|
|
148
|
+
class="prose prose-sm prose-zinc"
|
|
149
|
+
/>
|
|
150
|
+
</template>
|
|
151
|
+
{{ fieldTitle("size") }}
|
|
152
|
+
</FieldLabel>
|
|
153
|
+
<InputGroup>
|
|
154
|
+
<InputGroupAddon align="inline-start">
|
|
155
|
+
<DropdownMenu>
|
|
156
|
+
<DropdownMenuTrigger as-child>
|
|
157
|
+
<InputGroupButton
|
|
158
|
+
variant="ghost"
|
|
159
|
+
size="xs"
|
|
160
|
+
:title="fieldTitle('align')"
|
|
161
|
+
>
|
|
162
|
+
<Icon :icon="currentAlignIcon" />
|
|
163
|
+
</InputGroupButton>
|
|
164
|
+
</DropdownMenuTrigger>
|
|
165
|
+
<DropdownMenuContent align="start">
|
|
166
|
+
<DropdownMenuItem
|
|
167
|
+
v-for="opt in ALIGN_OPTIONS"
|
|
168
|
+
:key="opt.value"
|
|
169
|
+
@select="value.align = opt.value"
|
|
170
|
+
>
|
|
171
|
+
<Icon :icon="opt.icon" />
|
|
172
|
+
{{ opt.label }}
|
|
173
|
+
</DropdownMenuItem>
|
|
174
|
+
</DropdownMenuContent>
|
|
175
|
+
</DropdownMenu>
|
|
176
|
+
</InputGroupAddon>
|
|
177
|
+
<InputGroupNumberField
|
|
178
|
+
:model-value="value.size"
|
|
179
|
+
:disabled="value.grow"
|
|
180
|
+
:min="0"
|
|
181
|
+
@update:model-value="(v) => value.size = v"
|
|
182
|
+
/>
|
|
183
|
+
<InputGroupAddon align="inline-end">
|
|
184
|
+
<InputGroupButton
|
|
185
|
+
:variant="value.grow ? 'primary' : 'ghost'"
|
|
186
|
+
size="xs"
|
|
187
|
+
@click="value.grow = !value.grow"
|
|
188
|
+
>
|
|
189
|
+
<Icon :icon="value.grow ? 'fluent:lock-closed-20-regular' : 'fluent:arrow-autofit-width-20-regular'" />
|
|
190
|
+
{{ fieldTitle("grow") }}
|
|
191
|
+
</InputGroupButton>
|
|
192
|
+
</InputGroupAddon>
|
|
193
|
+
</InputGroup>
|
|
194
|
+
</Field>
|
|
195
|
+
<Field orientation="vertical">
|
|
196
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
197
|
+
<template
|
|
198
|
+
v-if="fieldDescription('disabled')"
|
|
199
|
+
#tooltip
|
|
200
|
+
>
|
|
201
|
+
<Markdown
|
|
202
|
+
:source="fieldDescription('disabled')"
|
|
203
|
+
block
|
|
204
|
+
class="prose prose-sm prose-zinc"
|
|
205
|
+
/>
|
|
206
|
+
</template>
|
|
207
|
+
{{ fieldTitle("disabled") }}
|
|
208
|
+
</FieldLabel>
|
|
209
|
+
<ExpressionEditor
|
|
210
|
+
v-model="disabledModel"
|
|
211
|
+
placeholder="如 row.locked"
|
|
212
|
+
result-type="bool"
|
|
213
|
+
:extra-vars="ROW_VARS"
|
|
214
|
+
/>
|
|
215
|
+
</Field>
|
|
216
|
+
<Field orientation="vertical">
|
|
217
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
218
|
+
<template
|
|
219
|
+
v-if="fieldDescription('onChange')"
|
|
220
|
+
#tooltip
|
|
221
|
+
>
|
|
222
|
+
<Markdown
|
|
223
|
+
:source="fieldDescription('onChange')"
|
|
224
|
+
block
|
|
225
|
+
class="prose prose-sm prose-zinc"
|
|
226
|
+
/>
|
|
227
|
+
</template>
|
|
228
|
+
{{ fieldTitle("onChange") }}
|
|
229
|
+
</FieldLabel>
|
|
230
|
+
<ExpressionEditor
|
|
231
|
+
v-model="onChangeModel"
|
|
232
|
+
placeholder="如 http.post('/api/items/' + row.id).body({ enabled: value })"
|
|
233
|
+
result-type="HttpRequest"
|
|
234
|
+
:extra-vars="ROW_VALUE_VARS"
|
|
235
|
+
multiline
|
|
236
|
+
/>
|
|
237
|
+
</Field>
|
|
238
|
+
<Field orientation="vertical">
|
|
239
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
240
|
+
<template
|
|
241
|
+
v-if="fieldDescription('successMessage')"
|
|
242
|
+
#tooltip
|
|
243
|
+
>
|
|
244
|
+
<Markdown
|
|
245
|
+
:source="fieldDescription('successMessage')"
|
|
246
|
+
block
|
|
247
|
+
class="prose prose-sm prose-zinc"
|
|
248
|
+
/>
|
|
249
|
+
</template>
|
|
250
|
+
{{ fieldTitle("successMessage") }}
|
|
251
|
+
</FieldLabel>
|
|
252
|
+
<ExpressionEditor
|
|
253
|
+
v-model="successMessageModel"
|
|
254
|
+
placeholder="如 '已更新'"
|
|
255
|
+
result-type="string"
|
|
256
|
+
:extra-vars="JSON_VARS"
|
|
257
|
+
/>
|
|
258
|
+
</Field>
|
|
259
|
+
<Field orientation="vertical">
|
|
260
|
+
<FieldLabel class="text-xs text-zinc-500">
|
|
261
|
+
<template
|
|
262
|
+
v-if="fieldDescription('triggers')"
|
|
263
|
+
#tooltip
|
|
264
|
+
>
|
|
265
|
+
<Markdown
|
|
266
|
+
:source="fieldDescription('triggers')"
|
|
267
|
+
block
|
|
268
|
+
class="prose prose-sm prose-zinc"
|
|
269
|
+
/>
|
|
270
|
+
</template>
|
|
271
|
+
{{ fieldTitle("triggers") }}
|
|
272
|
+
</FieldLabel>
|
|
273
|
+
<TriggersField
|
|
274
|
+
:triggers="triggers"
|
|
275
|
+
@update:triggers="updateTriggers"
|
|
276
|
+
/>
|
|
277
|
+
</Field>
|
|
278
|
+
</div>
|
|
279
|
+
<Separator />
|
|
280
|
+
<div class="flex flex-wrap gap-x-8 gap-y-3">
|
|
281
|
+
<Field
|
|
282
|
+
orientation="horizontal"
|
|
283
|
+
class="w-auto gap-2"
|
|
284
|
+
>
|
|
285
|
+
<Switch
|
|
286
|
+
:model-value="value.enableSorting ?? false"
|
|
287
|
+
@update:model-value="(v) => value.enableSorting = v"
|
|
288
|
+
/>
|
|
289
|
+
<FieldLabel class="text-sm text-zinc-600">
|
|
290
|
+
<template
|
|
291
|
+
v-if="fieldDescription('enableSorting')"
|
|
292
|
+
#tooltip
|
|
293
|
+
>
|
|
294
|
+
<Markdown
|
|
295
|
+
:source="fieldDescription('enableSorting')"
|
|
296
|
+
block
|
|
297
|
+
class="prose prose-sm prose-zinc"
|
|
298
|
+
/>
|
|
299
|
+
</template>
|
|
300
|
+
{{ fieldTitle("enableSorting") }}
|
|
301
|
+
</FieldLabel>
|
|
302
|
+
</Field>
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
</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;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, ref } from "vue";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import { Fetch } from "fx-fetch";
|
|
5
|
+
import { toast } from "vue-sonner";
|
|
6
|
+
import { cel as $cel } from "../../../../../utils/cel";
|
|
7
|
+
import { Switch } from "../../../../ui/switch";
|
|
8
|
+
import { JUSTIFY_CLASS } from "../../../utils/runtime";
|
|
9
|
+
import { celBindings, injectCELContext } from "../../../../../utils/cel-context";
|
|
10
|
+
import { dispatchTriggers, useEventChannel } from "../../../../../share/event-bus";
|
|
11
|
+
defineOptions({ name: "ShwfedTableSwitchRendererRuntime" });
|
|
12
|
+
const props = defineProps({
|
|
13
|
+
column: { type: null, required: true },
|
|
14
|
+
ctx: { type: Object, required: true }
|
|
15
|
+
});
|
|
16
|
+
const celContext = injectCELContext();
|
|
17
|
+
const eventChannel = useEventChannel();
|
|
18
|
+
const rawValue = computed(() => props.ctx.cell.getValue());
|
|
19
|
+
const isMissing = computed(() => rawValue.value === void 0 || rawValue.value === null);
|
|
20
|
+
const checked = computed(() => rawValue.value === true);
|
|
21
|
+
const justifyClass = computed(
|
|
22
|
+
() => JUSTIFY_CLASS[props.column.align ?? "center"] ?? JUSTIFY_CLASS.center
|
|
23
|
+
);
|
|
24
|
+
const pending = ref(false);
|
|
25
|
+
const isDisabledByConfig = computed(() => {
|
|
26
|
+
if (!props.column.disabled) return false;
|
|
27
|
+
try {
|
|
28
|
+
return Effect.runSync($cel(props.column.disabled, celBindings(celContext))) === true;
|
|
29
|
+
} catch (e) {
|
|
30
|
+
console.error("[shwfed-table] switch disabled failed", e);
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const isInteractive = computed(() => !!props.column.onChange);
|
|
35
|
+
const isDisabled = computed(() => !isInteractive.value || isDisabledByConfig.value || pending.value);
|
|
36
|
+
async function onUpdate(next) {
|
|
37
|
+
const { onChange, successMessage } = props.column;
|
|
38
|
+
if (!onChange) return;
|
|
39
|
+
pending.value = true;
|
|
40
|
+
const program = Effect.gen(function* () {
|
|
41
|
+
const builder = yield* $cel(onChange, {
|
|
42
|
+
...celBindings(celContext),
|
|
43
|
+
value: next
|
|
44
|
+
});
|
|
45
|
+
return yield* builder.json();
|
|
46
|
+
});
|
|
47
|
+
try {
|
|
48
|
+
const body = await Effect.runPromise(Effect.provide(program, Fetch.layer));
|
|
49
|
+
if (successMessage) {
|
|
50
|
+
try {
|
|
51
|
+
const message = Effect.runSync($cel(successMessage, { json: body }));
|
|
52
|
+
if (message) toast.success(message);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.error("[shwfed-table] switch successMessage failed", e);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
await Effect.runPromise(dispatchTriggers(eventChannel, props.column.triggers));
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.error("[shwfed-table] switch onChange failed", e);
|
|
60
|
+
toast.error("\u8BF7\u6C42\u5931\u8D25");
|
|
61
|
+
} finally {
|
|
62
|
+
pending.value = false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<template>
|
|
68
|
+
<span :class="['w-full py-2 px-1 flex items-center text-xs', justifyClass]">
|
|
69
|
+
<span
|
|
70
|
+
v-if="isMissing"
|
|
71
|
+
class="font-mono text-zinc-300 select-none"
|
|
72
|
+
>-</span>
|
|
73
|
+
<Switch
|
|
74
|
+
v-else
|
|
75
|
+
size="sm"
|
|
76
|
+
:model-value="checked"
|
|
77
|
+
:disabled="isDisabled"
|
|
78
|
+
@update:model-value="(v) => onUpdate(v)"
|
|
79
|
+
/>
|
|
80
|
+
</span>
|
|
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;
|
|
@@ -0,0 +1,55 @@
|
|
|
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, MigrateFn, MigrateSource } from '../../../utils/resolve.js';
|
|
5
|
+
export declare const type: "com.shwfed.table.column.switch.remote";
|
|
6
|
+
export declare const compatibilityDate: "2026-05-13";
|
|
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
|
+
align: Schema.optionalWith<Schema.Literal<["left", "center", "right"]>, {
|
|
13
|
+
default: () => "center";
|
|
14
|
+
}>;
|
|
15
|
+
disabled: Schema.optional<Schema.Schema<string, string, never>>;
|
|
16
|
+
onChange: Schema.optional<Schema.Schema<string, string, never>>;
|
|
17
|
+
successMessage: Schema.optional<Schema.Schema<string, string, never>>;
|
|
18
|
+
triggers: Schema.optional<Schema.Array$<Schema.Struct<{
|
|
19
|
+
target: Schema.refine<string, typeof Schema.String>;
|
|
20
|
+
operation: Schema.SchemaClass<string, string, never>;
|
|
21
|
+
}>>>;
|
|
22
|
+
title: 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
|
+
accessor: Schema.Schema<string, string, never>;
|
|
30
|
+
enableSorting: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
|
|
31
|
+
size: Schema.optional<Schema.refine<number, Schema.filter<typeof Schema.Number>>>;
|
|
32
|
+
grow: Schema.optional<Schema.SchemaClass<boolean, boolean, never>>;
|
|
33
|
+
tooltip: Schema.optional<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
|
+
id: Schema.refine<string, typeof Schema.String>;
|
|
41
|
+
groupId: Schema.optional<typeof Schema.UUID>;
|
|
42
|
+
type: Schema.Literal<["com.shwfed.table.column.switch.remote"]>;
|
|
43
|
+
compatibilityDate: Schema.Literal<["2026-05-13"]>;
|
|
44
|
+
}>;
|
|
45
|
+
export type Value = Schema.Schema.Type<ReturnType<typeof schema>>;
|
|
46
|
+
/**
|
|
47
|
+
* Adopts values that were stored under the pre-split `com.shwfed.table.column.switch`
|
|
48
|
+
* type at the same `compatibilityDate`. The field shape is byte-identical to
|
|
49
|
+
* this entry's schema — only the discriminator changes. Migration declarations
|
|
50
|
+
* live exclusively on this (new) entry; the old folder was removed and is
|
|
51
|
+
* never touched by this rewrite.
|
|
52
|
+
*/
|
|
53
|
+
export declare const migrateFrom: ReadonlyArray<MigrateSource>;
|
|
54
|
+
export declare const migrate: MigrateFn;
|
|
55
|
+
export declare function toColumnDef(value: Value, { getLocaleText, $cel, inheritedContext }: ColumnDefDeps): Partial<ColumnDef<unknown, unknown>>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { Expression } from "../../../../../share/expression.js";
|
|
3
|
+
import { Triggers } from "../../../../../share/event-bus.js";
|
|
4
|
+
import { Align, CelRowAccess, columnFields, registerRowVariablesIfAbsent } from "../../../utils/shared.js";
|
|
5
|
+
export const type = "com.shwfed.table.column.switch.remote";
|
|
6
|
+
export const compatibilityDate = "2026-05-13";
|
|
7
|
+
export const metadata = {
|
|
8
|
+
name: "\u5F00\u5173\uFF08\u8FDC\u7A0B\uFF09",
|
|
9
|
+
icon: "fluent:toggle-left-20-regular"
|
|
10
|
+
};
|
|
11
|
+
export function schema(configure) {
|
|
12
|
+
const CelBool = CelRowAccess(configure, { resultType: "bool" });
|
|
13
|
+
const CelOnChange = Expression({
|
|
14
|
+
configure: (env) => {
|
|
15
|
+
configure(env);
|
|
16
|
+
registerRowVariablesIfAbsent(env);
|
|
17
|
+
env.registerVariable("value", "bool", { description: "\u5207\u6362\u540E\u7684\u65B0\u503C" });
|
|
18
|
+
},
|
|
19
|
+
resultType: "HttpRequest"
|
|
20
|
+
});
|
|
21
|
+
const CelMessage = Expression({
|
|
22
|
+
configure: (env) => {
|
|
23
|
+
configure(env);
|
|
24
|
+
env.registerVariable("json", "dyn", { description: "HTTP \u54CD\u5E94\u4F53\uFF08\u5DF2\u89E3\u6790 JSON\uFF09" });
|
|
25
|
+
},
|
|
26
|
+
resultType: "string"
|
|
27
|
+
});
|
|
28
|
+
return Schema.Struct({
|
|
29
|
+
type: Schema.Literal(type),
|
|
30
|
+
compatibilityDate: Schema.Literal(compatibilityDate),
|
|
31
|
+
...columnFields(configure),
|
|
32
|
+
align: Schema.optionalWith(Align.annotations({ title: "\u5BF9\u9F50" }), { default: () => "center" }),
|
|
33
|
+
disabled: Schema.optional(CelBool.annotations({
|
|
34
|
+
title: "\u7981\u7528\u6761\u4EF6",
|
|
35
|
+
description: "\u8FD4\u56DE `true` \u65F6\u5F00\u5173\u4ECD\u7136\u6E32\u67D3\u4F46\u4E0D\u53EF\u5207\u6362"
|
|
36
|
+
})),
|
|
37
|
+
onChange: Schema.optional(CelOnChange.annotations({
|
|
38
|
+
title: "\u5207\u6362\u8BF7\u6C42",
|
|
39
|
+
description: "\u7528\u6237\u5207\u6362\u540E\u7ACB\u5373\u6C42\u503C\u5E76\u53D1\u8D77\u7684 HTTP \u8BF7\u6C42\uFF1B`value` \u4E3A\u5207\u6362\u540E\u7684\u65B0\u503C\u3002\u672A\u914D\u7F6E\u65F6\u5F00\u5173\u53EA\u8BFB"
|
|
40
|
+
})),
|
|
41
|
+
successMessage: Schema.optional(CelMessage.annotations({
|
|
42
|
+
title: "\u6210\u529F\u6D88\u606F",
|
|
43
|
+
description: "\u8BF7\u6C42\u6210\u529F\u540E\u5F39\u51FA\u7684 toast \u5185\u5BB9\uFF1B\u672A\u914D\u7F6E\u5219\u9759\u9ED8"
|
|
44
|
+
})),
|
|
45
|
+
triggers: Schema.optional(Triggers.annotations({
|
|
46
|
+
title: "\u6210\u529F\u540E\u89E6\u53D1",
|
|
47
|
+
description: "\u8BF7\u6C42\u6210\u529F\u540E\u6309\u5217\u8868\u987A\u5E8F\u5192\u6CE1\u7684\u64CD\u4F5C\u8BF7\u6C42\uFF1B\u672A\u914D\u7F6E\u5219\u4EC0\u4E48\u90FD\u4E0D\u505A"
|
|
48
|
+
}))
|
|
49
|
+
}).annotations({ title: "SwitchRenderer", description: "\u5F00\u5173\u6E32\u67D3\u5668\uFF08\u53EF\u7F16\u8F91\uFF09" });
|
|
50
|
+
}
|
|
51
|
+
export const migrateFrom = [
|
|
52
|
+
{ type: "com.shwfed.table.column.switch", compatibilityDate }
|
|
53
|
+
];
|
|
54
|
+
export const migrate = (prev) => Effect.try({
|
|
55
|
+
try: () => {
|
|
56
|
+
if (!prev || typeof prev !== "object") {
|
|
57
|
+
throw new Error("\u5F00\u5173\uFF08\u8FDC\u7A0B\uFF09\u8FC1\u79FB\u5931\u8D25\uFF1A\u539F\u503C\u4E0D\u662F\u5BF9\u8C61");
|
|
58
|
+
}
|
|
59
|
+
return { ...prev, type, compatibilityDate };
|
|
60
|
+
},
|
|
61
|
+
catch: (e) => e instanceof Error ? e : new Error(String(e))
|
|
62
|
+
});
|
|
63
|
+
export function toColumnDef(value, { getLocaleText, $cel, inheritedContext }) {
|
|
64
|
+
return {
|
|
65
|
+
header: getLocaleText(value.title),
|
|
66
|
+
accessorFn: (row, index) => {
|
|
67
|
+
try {
|
|
68
|
+
return Effect.runSync($cel(value.accessor, { ...inheritedContext, row, index }));
|
|
69
|
+
} catch (e) {
|
|
70
|
+
console.error("[shwfed-table] switch accessor failed", e);
|
|
71
|
+
return void 0;
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
enableSorting: value.enableSorting ?? false,
|
|
75
|
+
sortingFn: "basic",
|
|
76
|
+
size: value.size,
|
|
77
|
+
meta: {
|
|
78
|
+
grow: value.grow ?? false,
|
|
79
|
+
tooltip: getLocaleText(value.tooltip)
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|