@shwfed/nuxt 0.7.9 → 0.7.10
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/app.d.vue.ts +7 -56
- package/dist/runtime/components/app.vue +17 -404
- package/dist/runtime/components/app.vue.d.ts +7 -56
- package/dist/runtime/components/fields.d.vue.ts +154 -132
- package/dist/runtime/components/fields.vue +30 -295
- package/dist/runtime/components/fields.vue.d.ts +154 -132
- package/dist/runtime/components/table.d.vue.ts +63 -59
- package/dist/runtime/components/table.vue +52 -617
- package/dist/runtime/components/table.vue.d.ts +63 -59
- package/dist/runtime/components/ui/app/App.d.vue.ts +86 -0
- package/dist/runtime/components/ui/app/App.vue +414 -0
- package/dist/runtime/components/ui/app/App.vue.d.ts +86 -0
- package/dist/runtime/components/ui/checkbox/Checkbox.vue +6 -2
- package/dist/runtime/components/ui/expression-editor/ExpressionEditor.d.vue.ts +30 -0
- package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue +87 -0
- package/dist/runtime/components/ui/expression-editor/ExpressionEditor.vue.d.ts +30 -0
- package/dist/runtime/components/ui/expression-editor/index.d.ts +1 -0
- package/dist/runtime/components/ui/expression-editor/index.js +1 -0
- package/dist/runtime/components/ui/field/FieldContent.vue +1 -1
- package/dist/runtime/components/ui/field/FieldError.vue +2 -2
- package/dist/runtime/components/ui/fields/Fields.d.vue.ts +376 -0
- package/dist/runtime/components/ui/fields/Fields.vue +441 -0
- package/dist/runtime/components/ui/fields/Fields.vue.d.ts +376 -0
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.d.vue.ts +163 -0
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue +363 -0
- package/dist/runtime/components/ui/fields-configurator/FieldsConfiguratorDialog.vue.d.ts +163 -0
- package/dist/runtime/components/ui/input/Input.d.vue.ts +1 -0
- package/dist/runtime/components/ui/input/Input.vue +2 -0
- package/dist/runtime/components/ui/input/Input.vue.d.ts +1 -0
- package/dist/runtime/components/ui/input-group/InputGroupAddon.vue +4 -1
- package/dist/runtime/components/ui/input-group/InputGroupCombobox.d.vue.ts +8 -3
- package/dist/runtime/components/ui/input-group/InputGroupCombobox.vue +8 -3
- package/dist/runtime/components/ui/input-group/InputGroupCombobox.vue.d.ts +8 -3
- package/dist/runtime/components/ui/input-group/InputGroupComboboxInput.d.vue.ts +8 -1
- package/dist/runtime/components/ui/input-group/InputGroupComboboxInput.vue +10 -1
- package/dist/runtime/components/ui/input-group/InputGroupComboboxInput.vue.d.ts +8 -1
- package/dist/runtime/components/ui/input-group/InputGroupNumberField.d.vue.ts +5 -2
- package/dist/runtime/components/ui/input-group/InputGroupNumberField.vue +9 -3
- package/dist/runtime/components/ui/input-group/InputGroupNumberField.vue.d.ts +5 -2
- package/dist/runtime/components/ui/input-group/index.js +1 -1
- package/dist/runtime/components/ui/locale/Locale.d.vue.ts +20 -0
- package/dist/runtime/components/ui/locale/Locale.vue +291 -0
- package/dist/runtime/components/ui/locale/Locale.vue.d.ts +20 -0
- package/dist/runtime/components/ui/locale/index.d.ts +1 -0
- package/dist/runtime/components/ui/locale/index.js +1 -0
- package/dist/runtime/components/ui/native-select/NativeSelectOption.d.vue.ts +1 -0
- package/dist/runtime/components/ui/native-select/NativeSelectOption.vue +4 -1
- package/dist/runtime/components/ui/native-select/NativeSelectOption.vue.d.ts +1 -0
- package/dist/runtime/components/ui/number-field/NumberFieldInput.vue +1 -1
- package/dist/runtime/components/ui/switch/Switch.vue +1 -1
- package/dist/runtime/components/ui/table/Table.d.vue.ts +81 -0
- package/dist/runtime/components/ui/table/Table.vue +792 -0
- package/dist/runtime/components/ui/table/Table.vue.d.ts +81 -0
- package/dist/runtime/components/ui/table/schema.d.ts +48 -0
- package/dist/runtime/components/ui/table/schema.js +126 -0
- package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.d.vue.ts +62 -0
- package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.vue +2233 -0
- package/dist/runtime/components/ui/table-configurator/TableConfiguratorDialog.vue.d.ts +62 -0
- package/dist/runtime/components/ui/table-configurator/menu.d.ts +37 -0
- package/dist/runtime/components/ui/table-configurator/menu.js +227 -0
- package/dist/runtime/components/ui/tabs/Tabs.d.vue.ts +24 -0
- package/dist/runtime/components/ui/tabs/Tabs.vue +30 -0
- package/dist/runtime/components/ui/tabs/Tabs.vue.d.ts +24 -0
- package/dist/runtime/components/ui/tabs/TabsContent.d.vue.ts +18 -0
- package/dist/runtime/components/ui/tabs/TabsContent.vue +23 -0
- package/dist/runtime/components/ui/tabs/TabsContent.vue.d.ts +18 -0
- package/dist/runtime/components/ui/tabs/TabsList.d.vue.ts +18 -0
- package/dist/runtime/components/ui/tabs/TabsList.vue +25 -0
- package/dist/runtime/components/ui/tabs/TabsList.vue.d.ts +18 -0
- package/dist/runtime/components/ui/tabs/TabsTrigger.d.vue.ts +18 -0
- package/dist/runtime/components/ui/tabs/TabsTrigger.vue +27 -0
- package/dist/runtime/components/ui/tabs/TabsTrigger.vue.d.ts +18 -0
- package/dist/runtime/components/ui/tabs/index.d.ts +4 -0
- package/dist/runtime/components/ui/tabs/index.js +4 -0
- package/dist/runtime/components/ui/textarea/Textarea.d.vue.ts +1 -0
- package/dist/runtime/components/ui/textarea/Textarea.vue +3 -1
- package/dist/runtime/components/ui/textarea/Textarea.vue.d.ts +1 -0
- package/dist/runtime/components/ui/toggle/Toggle.d.vue.ts +34 -0
- package/dist/runtime/components/ui/toggle/Toggle.vue +32 -0
- package/dist/runtime/components/ui/toggle/Toggle.vue.d.ts +34 -0
- package/dist/runtime/components/ui/toggle/index.d.ts +7 -0
- package/dist/runtime/components/ui/toggle/index.js +22 -0
- package/dist/runtime/composables/useTableRenderers.d.ts +2 -1
- package/dist/runtime/composables/useTableRenderers.js +2 -1
- package/dist/runtime/style.css +1 -1
- package/dist/runtime/table-renderers/builtins.js +213 -98
- package/dist/runtime/table-renderers/registry.d.ts +1 -0
- package/dist/runtime/table-renderers/registry.js +3 -0
- package/dist/runtime/utils/coders.d.ts +27 -2
- package/dist/runtime/utils/coders.js +27 -2
- package/package.json +3 -1
- /package/dist/runtime/components/{logo.d.vue.ts → ui/logo/Logo.d.vue.ts} +0 -0
- /package/dist/runtime/components/{logo.vue → ui/logo/Logo.vue} +0 -0
- /package/dist/runtime/components/{logo.vue.d.ts → ui/logo/Logo.vue.d.ts} +0 -0
package/dist/module.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { Scope } from 'effect';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import type { Sidebar as SidebarType } from '../composables/useNavigationTabs.js';
|
|
3
|
+
type CommandEffect = import('effect').Effect.Effect<void>;
|
|
4
|
+
type DslEffect = import('effect').Effect.Effect<Record<string, unknown>, never, Scope.Scope>;
|
|
4
5
|
type ProfileCommandInputItem = Readonly<{
|
|
5
6
|
id: string | number;
|
|
6
7
|
title: string;
|
|
7
8
|
icon?: string;
|
|
8
9
|
hidden?: boolean;
|
|
9
10
|
disabled?: boolean;
|
|
10
|
-
effect?:
|
|
11
|
+
effect?: CommandEffect;
|
|
11
12
|
keywords?: Array<string>;
|
|
12
13
|
}>;
|
|
13
14
|
type ProfileCommandInputGroup = Readonly<{
|
|
@@ -17,63 +18,13 @@ type ProfileCommandInputGroup = Readonly<{
|
|
|
17
18
|
children: Array<ProfileCommandInputItem>;
|
|
18
19
|
}>;
|
|
19
20
|
type __VLS_Props = {
|
|
20
|
-
|
|
21
|
-
* 应用的全局 DSL(CEL)执行上下文。传入一个 Effect,在应用启动时运行一次;
|
|
22
|
-
* 其解析结果(一个 record)会被永久设为全局上下文,供后续所有 CEL 求值(如 `$dsl.evaluate`)使用。
|
|
23
|
-
*
|
|
24
|
-
* 应用会等待该 effect 解析完成后才渲染;在整个应用生命周期内只会执行一次。
|
|
25
|
-
* 若未传入 `dsl`,全局上下文将被设为 `{}`。
|
|
26
|
-
*
|
|
27
|
-
* 适用于需要在首屏前就确定的、会话级稳定数据,例如:当前用户信息、用户所属组、
|
|
28
|
-
* 用户菜单列表、用户权限(或权限模块)等。此处加载的数据在整个应用内可通过 CEL 访问
|
|
29
|
-
*(如 `user`、`menu`、`permissions`)。强调「会话级」稳定即可,不必永久不变。
|
|
30
|
-
*
|
|
31
|
-
* 重要约束:
|
|
32
|
-
* - 加载时间至关重要:在该 effect 解析前,整应用不会加载,用户会看到白屏,因此必须保持非常快
|
|
33
|
-
* (如减少请求、缓存或并行拉取)。
|
|
34
|
-
* - 这是有意为之的权衡:菜单、用户等关键数据必须在此加载,否则可能出现空菜单、无用户等异常表现。
|
|
35
|
-
* - 该 effect 不得抛错(类型为 `never`):任何失败(如网络)应在 effect 内部处理,
|
|
36
|
-
* 返回空对象或安全默认值,保证应用仍能正常启动并得到一致的全局上下文。
|
|
37
|
-
*
|
|
38
|
-
* 返回的 `Record<string, unknown>` 会在通过 `$dsl.evaluate` 求值 CEL 表达式时,
|
|
39
|
-
* 与运行时上下文(如 `now`、`today` 及调用处传入的 context)合并使用。
|
|
40
|
-
*
|
|
41
|
-
* @example 最小示例:返回静态对象,键可在 CEL 中直接使用
|
|
42
|
-
* dsl: Effect.succeed({ hello: 'world' })
|
|
43
|
-
* // 之后在模板或逻辑中:$dsl.evaluate`hello`() 得到 'world'
|
|
44
|
-
*
|
|
45
|
-
* @example 实际场景:拉取会话数据(用户、菜单、权限)并返回,供 CEL 使用
|
|
46
|
-
* dsl: Effect.gen(function* () {
|
|
47
|
-
* const [user, menu, permissions] = yield* Effect.all([
|
|
48
|
-
* fetchUser().pipe(Effect.catchAll(() => Effect.succeed({ name: '', id: '' }))),
|
|
49
|
-
* fetchMenu().pipe(Effect.catchAll(() => Effect.succeed([]))),
|
|
50
|
-
* fetchPermissions().pipe(Effect.catchAll(() => Effect.succeed({ canEdit: false }))),
|
|
51
|
-
* ])
|
|
52
|
-
* return { user, menu, permissions }
|
|
53
|
-
* })
|
|
54
|
-
* // CEL 中可访问 user.name、permissions.canEdit 等;失败时在 effect 内兜底,不抛错
|
|
55
|
-
*/
|
|
56
|
-
dsl?: Effect.Effect<Record<string, unknown>, never, Scope.Scope>;
|
|
57
|
-
/**
|
|
58
|
-
* Sidebar config.
|
|
59
|
-
*
|
|
60
|
-
* `menus` is structured navigation data.
|
|
61
|
-
*/
|
|
21
|
+
dsl?: DslEffect;
|
|
62
22
|
sidebar?: SidebarType;
|
|
63
|
-
/**
|
|
64
|
-
* Profile command config.
|
|
65
|
-
*
|
|
66
|
-
* Structured command entries for profile menu and command palette.
|
|
67
|
-
*/
|
|
68
23
|
commands?: Array<ProfileCommandInputItem | ProfileCommandInputGroup>;
|
|
69
24
|
};
|
|
70
|
-
declare var
|
|
25
|
+
declare var __VLS_10: string, __VLS_11: any;
|
|
71
26
|
type __VLS_Slots = {} & {
|
|
72
|
-
|
|
73
|
-
} & {
|
|
74
|
-
profile?: (props: typeof __VLS_124) => any;
|
|
75
|
-
} & {
|
|
76
|
-
default?: (props: typeof __VLS_347) => any;
|
|
27
|
+
[K in NonNullable<typeof __VLS_10>]?: (props: typeof __VLS_11) => any;
|
|
77
28
|
};
|
|
78
29
|
declare const __VLS_base: 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>;
|
|
79
30
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -1,414 +1,27 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { Toaster } from "vue-sonner";
|
|
7
|
-
import { useMagicKeys, useTimeoutFn, whenever } from "@vueuse/core";
|
|
8
|
-
import { useI18n } from "vue-i18n";
|
|
9
|
-
import { Icon } from "@iconify/vue";
|
|
10
|
-
import { Effect } from "effect";
|
|
11
|
-
import { setGlobalDslContext } from "../plugins/cel/context";
|
|
12
|
-
import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu, SidebarMenuAction, SidebarMenuButton, SidebarMenuItem, SidebarProvider } from "./ui/sidebar";
|
|
13
|
-
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./ui/collapsible";
|
|
14
|
-
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "./ui/dropdown-menu";
|
|
15
|
-
import Logo from "./logo.vue";
|
|
16
|
-
import { useFavorite } from "../composables/useFavorite";
|
|
17
|
-
import { useNavigationTabs } from "../composables/useNavigationTabs";
|
|
18
|
-
const {
|
|
19
|
-
public: {
|
|
20
|
-
shwfed
|
|
21
|
-
}
|
|
22
|
-
} = useRuntimeConfig();
|
|
23
|
-
const { $dsl } = useNuxtApp();
|
|
24
|
-
const { t } = useI18n();
|
|
2
|
+
import UiApp from "./ui/app/App.vue";
|
|
3
|
+
defineOptions({
|
|
4
|
+
inheritAttrs: false
|
|
5
|
+
});
|
|
25
6
|
const props = defineProps({
|
|
26
7
|
dsl: { type: null, required: false },
|
|
27
8
|
sidebar: { type: Array, required: false },
|
|
28
9
|
commands: { type: Array, required: false }
|
|
29
10
|
});
|
|
30
|
-
const { active, tabs, nameOf, close } = useNavigationTabs(() => props.sidebar ?? []);
|
|
31
|
-
const ui = reactive({
|
|
32
|
-
isCommandPaletteOpen: false,
|
|
33
|
-
isProfileDropdownOpen: false
|
|
34
|
-
});
|
|
35
|
-
useHead({
|
|
36
|
-
title: () => active.value && nameOf(active.value),
|
|
37
|
-
bodyAttrs: {
|
|
38
|
-
style: {
|
|
39
|
-
"--primary": "#2DA8BC",
|
|
40
|
-
"--workspace": "#FFFFFF"
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
const { start: startProfileCloseTimer, stop: stopProfileCloseTimer } = useTimeoutFn(() => {
|
|
45
|
-
ui.isProfileDropdownOpen = false;
|
|
46
|
-
}, 150, { immediate: false });
|
|
47
|
-
const { meta_k } = useMagicKeys();
|
|
48
|
-
whenever(() => meta_k?.value, () => ui.isCommandPaletteOpen = !ui.isCommandPaletteOpen);
|
|
49
|
-
setGlobalDslContext(await props.dsl?.pipe(Effect.scoped).pipe(Effect.runPromise) ?? {});
|
|
50
|
-
const {
|
|
51
|
-
isFavorited: isNavigationFavorited,
|
|
52
|
-
canBeFavorited: canBeNavigationFavorited,
|
|
53
|
-
toggle: toggleNavigationFavorite,
|
|
54
|
-
withFavorites: navigations
|
|
55
|
-
} = useFavorite("navigation", () => props.sidebar ?? []);
|
|
56
|
-
const logout = () => {
|
|
57
|
-
if (shwfed.api.logout) {
|
|
58
|
-
window.location.href = shwfed.api.logout;
|
|
59
|
-
}
|
|
60
|
-
return Effect.void;
|
|
61
|
-
};
|
|
62
11
|
</script>
|
|
63
12
|
|
|
64
13
|
<template>
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
/>
|
|
79
|
-
<p class="text-zinc-500">
|
|
80
|
-
{{ t("command-palette-empty") }}
|
|
81
|
-
</p>
|
|
82
|
-
</section>
|
|
83
|
-
</CommandEmpty>
|
|
84
|
-
|
|
85
|
-
<template
|
|
86
|
-
v-for="command in props.commands"
|
|
87
|
-
:key="command.id"
|
|
88
|
-
>
|
|
89
|
-
<CommandGroup
|
|
90
|
-
v-if="'children' in command"
|
|
91
|
-
:heading="command.title"
|
|
92
|
-
>
|
|
93
|
-
<CommandItem
|
|
94
|
-
v-for="child in command.children.filter((child2) => !child2.hidden)"
|
|
95
|
-
:key="child.id"
|
|
96
|
-
:value="child.id"
|
|
97
|
-
:disabled="child.disabled"
|
|
98
|
-
@select="child.effect?.pipe(Effect.tap(() => {
|
|
99
|
-
ui.isCommandPaletteOpen = false;
|
|
100
|
-
})).pipe(Effect.runPromise)"
|
|
101
|
-
>
|
|
102
|
-
<span>{{ child.title }}</span>
|
|
103
|
-
<span class="sr-only">
|
|
104
|
-
{{ child.keywords?.join(" ") }}
|
|
105
|
-
</span>
|
|
106
|
-
</CommandItem>
|
|
107
|
-
</CommandGroup>
|
|
108
|
-
<CommandGroup v-else>
|
|
109
|
-
<CommandItem
|
|
110
|
-
:value="command.id"
|
|
111
|
-
:disabled="command.disabled"
|
|
112
|
-
@select="command.effect?.pipe(Effect.tap(() => {
|
|
113
|
-
ui.isCommandPaletteOpen = false;
|
|
114
|
-
})).pipe(Effect.runPromise)"
|
|
115
|
-
>
|
|
116
|
-
<span>{{ command.title }}</span>
|
|
117
|
-
<span class="sr-only">
|
|
118
|
-
{{ command.keywords?.join(" ") }}
|
|
119
|
-
</span>
|
|
120
|
-
</CommandItem>
|
|
121
|
-
</CommandGroup>
|
|
122
|
-
</template>
|
|
123
|
-
|
|
124
|
-
<template
|
|
125
|
-
v-for="navigation in props.sidebar"
|
|
126
|
-
:key="navigation.id"
|
|
127
|
-
>
|
|
128
|
-
<CommandGroup
|
|
129
|
-
v-if="'children' in navigation"
|
|
130
|
-
:heading="navigation.title"
|
|
131
|
-
>
|
|
132
|
-
<CommandItem
|
|
133
|
-
v-for="menu in navigation.children"
|
|
134
|
-
:key="menu.id"
|
|
135
|
-
:value="menu.id"
|
|
136
|
-
@select="$router.replace(menu.route)"
|
|
137
|
-
>
|
|
138
|
-
<span>{{ menu.title }}</span>
|
|
139
|
-
</CommandItem>
|
|
140
|
-
</CommandGroup>
|
|
141
|
-
<CommandGroup v-else>
|
|
142
|
-
<CommandItem
|
|
143
|
-
:value="navigation.id"
|
|
144
|
-
@select="$router.replace(navigation.route)"
|
|
145
|
-
>
|
|
146
|
-
<span>{{ navigation.title }}</span>
|
|
147
|
-
</CommandItem>
|
|
148
|
-
</CommandGroup>
|
|
149
|
-
</template>
|
|
150
|
-
</CommandList>
|
|
151
|
-
</CommandDialog>
|
|
152
|
-
</ClientOnly>
|
|
153
|
-
|
|
154
|
-
<main class="h-screen w-screen flex flex-col overflow-hidden">
|
|
155
|
-
<header class="bg-(--primary) h-12 w-full flex items-center justify-between px-4 gap-4">
|
|
156
|
-
<Logo />
|
|
157
|
-
<slot name="menu" />
|
|
158
|
-
<span class="flex-1" />
|
|
159
|
-
<DropdownMenu
|
|
160
|
-
v-model:open="ui.isProfileDropdownOpen"
|
|
161
|
-
:modal="false"
|
|
162
|
-
@update:open="ui.isProfileDropdownOpen = $event"
|
|
163
|
-
>
|
|
164
|
-
<DropdownMenuTrigger as-child>
|
|
165
|
-
<button
|
|
166
|
-
type="button"
|
|
167
|
-
class="text-white text-sm cursor-pointer appearance-none outline-none py-2 px-3 rounded hover:bg-white/10"
|
|
168
|
-
@pointerenter="stopProfileCloseTimer();
|
|
169
|
-
ui.isProfileDropdownOpen = true"
|
|
170
|
-
@pointerleave="stopProfileCloseTimer();
|
|
171
|
-
startProfileCloseTimer()"
|
|
172
|
-
@click.prevent="stopProfileCloseTimer();
|
|
173
|
-
ui.isProfileDropdownOpen = true"
|
|
174
|
-
>
|
|
175
|
-
<slot name="profile" />
|
|
176
|
-
</button>
|
|
177
|
-
</DropdownMenuTrigger>
|
|
178
|
-
<DropdownMenuContent
|
|
179
|
-
class="min-w-56"
|
|
180
|
-
align="end"
|
|
181
|
-
@pointerenter="stopProfileCloseTimer()"
|
|
182
|
-
@pointerleave="stopProfileCloseTimer();
|
|
183
|
-
startProfileCloseTimer()"
|
|
184
|
-
>
|
|
185
|
-
<template
|
|
186
|
-
v-for="command in props.commands"
|
|
187
|
-
:key="command.id"
|
|
188
|
-
>
|
|
189
|
-
<template v-if="'children' in command && command.children.some((child2) => !child2.hidden)">
|
|
190
|
-
<DropdownMenuLabel>
|
|
191
|
-
{{ command.title }}
|
|
192
|
-
</DropdownMenuLabel>
|
|
193
|
-
<DropdownMenuGroup>
|
|
194
|
-
<template
|
|
195
|
-
v-for="child in command.children"
|
|
196
|
-
:key="child.id"
|
|
197
|
-
>
|
|
198
|
-
<DropdownMenuItem
|
|
199
|
-
v-if="!child.hidden"
|
|
200
|
-
:disabled="child.disabled"
|
|
201
|
-
@select="child.effect?.pipe(Effect.runPromise)"
|
|
202
|
-
>
|
|
203
|
-
<Icon
|
|
204
|
-
v-if="child.icon"
|
|
205
|
-
:icon="child.icon"
|
|
206
|
-
/>
|
|
207
|
-
{{ child.title }}
|
|
208
|
-
</DropdownMenuItem>
|
|
209
|
-
</template>
|
|
210
|
-
</DropdownMenuGroup>
|
|
211
|
-
<DropdownMenuSeparator />
|
|
212
|
-
</template>
|
|
213
|
-
<DropdownMenuItem
|
|
214
|
-
v-else-if="!('children' in command) && !command.hidden"
|
|
215
|
-
:disabled="command.disabled"
|
|
216
|
-
@select="command.effect?.pipe(Effect.runPromise)"
|
|
217
|
-
>
|
|
218
|
-
<Icon
|
|
219
|
-
v-if="command.icon"
|
|
220
|
-
:icon="command.icon"
|
|
221
|
-
/>
|
|
222
|
-
{{ command.title }}
|
|
223
|
-
</DropdownMenuItem>
|
|
224
|
-
</template>
|
|
225
|
-
<DropdownMenuItem
|
|
226
|
-
@select="logout"
|
|
227
|
-
>
|
|
228
|
-
<Icon
|
|
229
|
-
icon="fluent:sign-out-20-regular"
|
|
230
|
-
/>
|
|
231
|
-
{{ t("logout") }}
|
|
232
|
-
</DropdownMenuItem>
|
|
233
|
-
<DropdownMenuSeparator v-if="props.commands?.some((command2) => 'children' in command2 ? command2.children.some((child2) => !child2.hidden) : !command2.hidden)" />
|
|
234
|
-
<DropdownMenuItem disabled>
|
|
235
|
-
<Icon icon="fluent:history-20-regular" />
|
|
236
|
-
{{ t("build") }}
|
|
237
|
-
<code class="uppercase">{{ $dsl.evaluate`has(git.commit) && git.commit.size() > 0 ? git.commit.slice(0, 7) : 'development'`() }}</code>
|
|
238
|
-
</DropdownMenuItem>
|
|
239
|
-
</DropdownMenuContent>
|
|
240
|
-
</DropdownMenu>
|
|
241
|
-
</header>
|
|
242
|
-
<main class="flex-1 flex w-full overflow-hidden">
|
|
243
|
-
<SidebarProvider
|
|
244
|
-
default-open
|
|
245
|
-
class="w-fit"
|
|
246
|
-
:style="{
|
|
247
|
-
'--sidebar-width': '13rem'
|
|
248
|
-
}"
|
|
249
|
-
>
|
|
250
|
-
<Sidebar
|
|
251
|
-
collapsible="icon"
|
|
252
|
-
class="sticky h-full"
|
|
253
|
-
>
|
|
254
|
-
<SidebarContent class="gap-2 py-1">
|
|
255
|
-
<template
|
|
256
|
-
v-for="group in navigations"
|
|
257
|
-
:key="group.id"
|
|
258
|
-
>
|
|
259
|
-
<Collapsible
|
|
260
|
-
v-if="'children' in group"
|
|
261
|
-
class="group/collapsible"
|
|
262
|
-
>
|
|
263
|
-
<SidebarGroup>
|
|
264
|
-
<SidebarGroupLabel
|
|
265
|
-
as-child
|
|
266
|
-
class="group/label text-sm text-zinc-700 hover:bg-zinc-100 hover:text-zinc-900"
|
|
267
|
-
>
|
|
268
|
-
<CollapsibleTrigger>
|
|
269
|
-
<Icon
|
|
270
|
-
v-if="group.icon"
|
|
271
|
-
:icon="group.icon"
|
|
272
|
-
/>
|
|
273
|
-
{{ group.title }}
|
|
274
|
-
<Icon
|
|
275
|
-
icon="fluent:chevron-right-20-regular"
|
|
276
|
-
class="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-90"
|
|
277
|
-
/>
|
|
278
|
-
</CollapsibleTrigger>
|
|
279
|
-
</SidebarGroupLabel>
|
|
280
|
-
<CollapsibleContent class="overflow-hidden data-[state=open]:overflow-visible data-[state=open]:h-auto">
|
|
281
|
-
<SidebarGroupContent>
|
|
282
|
-
<SidebarMenu class="pl-4">
|
|
283
|
-
<SidebarMenuItem
|
|
284
|
-
v-for="menu in group.children"
|
|
285
|
-
:key="menu.id"
|
|
286
|
-
>
|
|
287
|
-
<SidebarMenuButton
|
|
288
|
-
v-if="'route' in menu"
|
|
289
|
-
as-child
|
|
290
|
-
:is-active="active === menu.route"
|
|
291
|
-
>
|
|
292
|
-
<button
|
|
293
|
-
type="button"
|
|
294
|
-
@click="active = menu.route"
|
|
295
|
-
>
|
|
296
|
-
<Icon
|
|
297
|
-
v-if="menu.icon"
|
|
298
|
-
:icon="menu.icon"
|
|
299
|
-
/>
|
|
300
|
-
{{ menu.title }}
|
|
301
|
-
</button>
|
|
302
|
-
</SidebarMenuButton>
|
|
303
|
-
<SidebarMenuAction
|
|
304
|
-
v-if="canBeNavigationFavorited(menu, group.id)"
|
|
305
|
-
:show-on-hover="!isNavigationFavorited(menu, group.id)"
|
|
306
|
-
class="text-yellow-500!"
|
|
307
|
-
@click.stop="toggleNavigationFavorite(menu, group.id)"
|
|
308
|
-
>
|
|
309
|
-
<Icon
|
|
310
|
-
:icon="isNavigationFavorited(menu, group.id) ? 'fluent:star-20-filled' : 'fluent:star-20-regular'"
|
|
311
|
-
/>
|
|
312
|
-
</SidebarMenuAction>
|
|
313
|
-
</SidebarMenuItem>
|
|
314
|
-
</SidebarMenu>
|
|
315
|
-
</SidebarGroupContent>
|
|
316
|
-
</CollapsibleContent>
|
|
317
|
-
</SidebarGroup>
|
|
318
|
-
</Collapsible>
|
|
319
|
-
<template v-else-if="'route' in group">
|
|
320
|
-
<SidebarGroup>
|
|
321
|
-
<SidebarGroupContent>
|
|
322
|
-
<SidebarMenu>
|
|
323
|
-
<SidebarMenuItem>
|
|
324
|
-
<SidebarMenuButton
|
|
325
|
-
as-child
|
|
326
|
-
:is-active="active === group.route"
|
|
327
|
-
>
|
|
328
|
-
<button
|
|
329
|
-
type="button"
|
|
330
|
-
@click="active = group.route"
|
|
331
|
-
>
|
|
332
|
-
<Icon
|
|
333
|
-
v-if="group.icon"
|
|
334
|
-
:icon="group.icon"
|
|
335
|
-
/>
|
|
336
|
-
{{ group.title }}
|
|
337
|
-
</button>
|
|
338
|
-
</SidebarMenuButton>
|
|
339
|
-
</SidebarMenuItem>
|
|
340
|
-
</SidebarMenu>
|
|
341
|
-
</SidebarGroupContent>
|
|
342
|
-
</SidebarGroup>
|
|
343
|
-
</template>
|
|
344
|
-
</template>
|
|
345
|
-
</SidebarContent>
|
|
346
|
-
</Sidebar>
|
|
347
|
-
</SidebarProvider>
|
|
348
|
-
<main class="flex-1 flex flex-col bg-zinc-100 overflow-hidden">
|
|
349
|
-
<nav class="bg-white p-1 border-b border-zinc-100 h-10 flex items-center justify-start gap-1 overflow-x-auto min-w-0">
|
|
350
|
-
<div
|
|
351
|
-
v-for="tab in tabs"
|
|
352
|
-
:key="tab"
|
|
353
|
-
:class="[
|
|
354
|
-
'h-8 max-w-60 border rounded px-2 text-sm flex items-center gap-2 shrink-0 min-w-0 transition-colors',
|
|
355
|
-
active === tab ? 'text-(--primary) border-(--primary) bg-cyan-50' : 'text-zinc-600 border-zinc-200 hover:text-zinc-900 hover:border-zinc-300'
|
|
356
|
-
]"
|
|
357
|
-
>
|
|
358
|
-
<button
|
|
359
|
-
type="button"
|
|
360
|
-
class="truncate cursor-pointer"
|
|
361
|
-
@click="active = tab"
|
|
362
|
-
>
|
|
363
|
-
{{ nameOf(tab) }}
|
|
364
|
-
</button>
|
|
365
|
-
<button
|
|
366
|
-
v-if="tabs.size !== 1"
|
|
367
|
-
type="button"
|
|
368
|
-
class="cursor-pointer text-zinc-500 hover:text-zinc-900"
|
|
369
|
-
@click.stop="close(tab)"
|
|
370
|
-
>
|
|
371
|
-
<Icon icon="fluent:dismiss-20-regular" />
|
|
372
|
-
</button>
|
|
373
|
-
</div>
|
|
374
|
-
</nav>
|
|
375
|
-
<main class="m-2 flex-1 p-1 bg-(--workspace) border border-zinc-100 rounded overflow-hidden">
|
|
376
|
-
<slot />
|
|
377
|
-
</main>
|
|
378
|
-
</main>
|
|
379
|
-
</main>
|
|
380
|
-
</main>
|
|
381
|
-
</TooltipProvider>
|
|
14
|
+
<UiApp
|
|
15
|
+
v-bind="{ ...props, ...$attrs }"
|
|
16
|
+
>
|
|
17
|
+
<template
|
|
18
|
+
v-for="(_, slotName) in $slots"
|
|
19
|
+
#[slotName]="slotProps"
|
|
20
|
+
>
|
|
21
|
+
<slot
|
|
22
|
+
:name="slotName"
|
|
23
|
+
v-bind="slotProps ?? {}"
|
|
24
|
+
/>
|
|
25
|
+
</template>
|
|
26
|
+
</UiApp>
|
|
382
27
|
</template>
|
|
383
|
-
|
|
384
|
-
<i18n lang="json">
|
|
385
|
-
{
|
|
386
|
-
"zh": {
|
|
387
|
-
"command-palette-empty": "无搜索结果",
|
|
388
|
-
"search": "搜索",
|
|
389
|
-
"logout": "退出登录",
|
|
390
|
-
"build": "构建",
|
|
391
|
-
"navigation": "导航",
|
|
392
|
-
"profile": "个人信息",
|
|
393
|
-
"favorites": "收藏"
|
|
394
|
-
},
|
|
395
|
-
"ja": {
|
|
396
|
-
"command-palette-empty": "結果はありません",
|
|
397
|
-
"search": "検索",
|
|
398
|
-
"logout": "ログアウト",
|
|
399
|
-
"build": "ビルド",
|
|
400
|
-
"navigation": "ナビゲーション",
|
|
401
|
-
"profile": "プロフィール",
|
|
402
|
-
"favorites": "お気に入り"
|
|
403
|
-
},
|
|
404
|
-
"en": {
|
|
405
|
-
"command-palette-empty": "No results",
|
|
406
|
-
"search": "Search",
|
|
407
|
-
"logout": "Logout",
|
|
408
|
-
"build": "Build",
|
|
409
|
-
"navigation": "Navigation",
|
|
410
|
-
"profile": "Profile",
|
|
411
|
-
"favorites": "Favorites"
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
</i18n>
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { Scope } from 'effect';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import type { Sidebar as SidebarType } from '../composables/useNavigationTabs.js';
|
|
3
|
+
type CommandEffect = import('effect').Effect.Effect<void>;
|
|
4
|
+
type DslEffect = import('effect').Effect.Effect<Record<string, unknown>, never, Scope.Scope>;
|
|
4
5
|
type ProfileCommandInputItem = Readonly<{
|
|
5
6
|
id: string | number;
|
|
6
7
|
title: string;
|
|
7
8
|
icon?: string;
|
|
8
9
|
hidden?: boolean;
|
|
9
10
|
disabled?: boolean;
|
|
10
|
-
effect?:
|
|
11
|
+
effect?: CommandEffect;
|
|
11
12
|
keywords?: Array<string>;
|
|
12
13
|
}>;
|
|
13
14
|
type ProfileCommandInputGroup = Readonly<{
|
|
@@ -17,63 +18,13 @@ type ProfileCommandInputGroup = Readonly<{
|
|
|
17
18
|
children: Array<ProfileCommandInputItem>;
|
|
18
19
|
}>;
|
|
19
20
|
type __VLS_Props = {
|
|
20
|
-
|
|
21
|
-
* 应用的全局 DSL(CEL)执行上下文。传入一个 Effect,在应用启动时运行一次;
|
|
22
|
-
* 其解析结果(一个 record)会被永久设为全局上下文,供后续所有 CEL 求值(如 `$dsl.evaluate`)使用。
|
|
23
|
-
*
|
|
24
|
-
* 应用会等待该 effect 解析完成后才渲染;在整个应用生命周期内只会执行一次。
|
|
25
|
-
* 若未传入 `dsl`,全局上下文将被设为 `{}`。
|
|
26
|
-
*
|
|
27
|
-
* 适用于需要在首屏前就确定的、会话级稳定数据,例如:当前用户信息、用户所属组、
|
|
28
|
-
* 用户菜单列表、用户权限(或权限模块)等。此处加载的数据在整个应用内可通过 CEL 访问
|
|
29
|
-
*(如 `user`、`menu`、`permissions`)。强调「会话级」稳定即可,不必永久不变。
|
|
30
|
-
*
|
|
31
|
-
* 重要约束:
|
|
32
|
-
* - 加载时间至关重要:在该 effect 解析前,整应用不会加载,用户会看到白屏,因此必须保持非常快
|
|
33
|
-
* (如减少请求、缓存或并行拉取)。
|
|
34
|
-
* - 这是有意为之的权衡:菜单、用户等关键数据必须在此加载,否则可能出现空菜单、无用户等异常表现。
|
|
35
|
-
* - 该 effect 不得抛错(类型为 `never`):任何失败(如网络)应在 effect 内部处理,
|
|
36
|
-
* 返回空对象或安全默认值,保证应用仍能正常启动并得到一致的全局上下文。
|
|
37
|
-
*
|
|
38
|
-
* 返回的 `Record<string, unknown>` 会在通过 `$dsl.evaluate` 求值 CEL 表达式时,
|
|
39
|
-
* 与运行时上下文(如 `now`、`today` 及调用处传入的 context)合并使用。
|
|
40
|
-
*
|
|
41
|
-
* @example 最小示例:返回静态对象,键可在 CEL 中直接使用
|
|
42
|
-
* dsl: Effect.succeed({ hello: 'world' })
|
|
43
|
-
* // 之后在模板或逻辑中:$dsl.evaluate`hello`() 得到 'world'
|
|
44
|
-
*
|
|
45
|
-
* @example 实际场景:拉取会话数据(用户、菜单、权限)并返回,供 CEL 使用
|
|
46
|
-
* dsl: Effect.gen(function* () {
|
|
47
|
-
* const [user, menu, permissions] = yield* Effect.all([
|
|
48
|
-
* fetchUser().pipe(Effect.catchAll(() => Effect.succeed({ name: '', id: '' }))),
|
|
49
|
-
* fetchMenu().pipe(Effect.catchAll(() => Effect.succeed([]))),
|
|
50
|
-
* fetchPermissions().pipe(Effect.catchAll(() => Effect.succeed({ canEdit: false }))),
|
|
51
|
-
* ])
|
|
52
|
-
* return { user, menu, permissions }
|
|
53
|
-
* })
|
|
54
|
-
* // CEL 中可访问 user.name、permissions.canEdit 等;失败时在 effect 内兜底,不抛错
|
|
55
|
-
*/
|
|
56
|
-
dsl?: Effect.Effect<Record<string, unknown>, never, Scope.Scope>;
|
|
57
|
-
/**
|
|
58
|
-
* Sidebar config.
|
|
59
|
-
*
|
|
60
|
-
* `menus` is structured navigation data.
|
|
61
|
-
*/
|
|
21
|
+
dsl?: DslEffect;
|
|
62
22
|
sidebar?: SidebarType;
|
|
63
|
-
/**
|
|
64
|
-
* Profile command config.
|
|
65
|
-
*
|
|
66
|
-
* Structured command entries for profile menu and command palette.
|
|
67
|
-
*/
|
|
68
23
|
commands?: Array<ProfileCommandInputItem | ProfileCommandInputGroup>;
|
|
69
24
|
};
|
|
70
|
-
declare var
|
|
25
|
+
declare var __VLS_10: string, __VLS_11: any;
|
|
71
26
|
type __VLS_Slots = {} & {
|
|
72
|
-
|
|
73
|
-
} & {
|
|
74
|
-
profile?: (props: typeof __VLS_124) => any;
|
|
75
|
-
} & {
|
|
76
|
-
default?: (props: typeof __VLS_347) => any;
|
|
27
|
+
[K in NonNullable<typeof __VLS_10>]?: (props: typeof __VLS_11) => any;
|
|
77
28
|
};
|
|
78
29
|
declare const __VLS_base: 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>;
|
|
79
30
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|