create-windy 0.6.7 → 0.6.9
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/README.md +2 -2
- package/dist/cli.js +158 -95
- package/package.json +1 -1
- package/template/.windy-template.json +2 -2
- package/template/README.md +4 -2
- package/template/apps/agent-server/README.md +4 -4
- package/template/apps/server/src/data-access/drizzle-predicate.ts +16 -6
- package/template/apps/server/src/runtime-development.ts +1 -0
- package/template/apps/web/src/layout/AppSidebar.vue +1 -0
- package/template/apps/web/src/pages/ui/UiComponentPage.vue +7 -0
- package/template/apps/web/src/pages/ui/UiComponentPage.webtest.ts +94 -0
- package/template/apps/web/src/pages/ui/component-catalog.ts +248 -0
- package/template/apps/web/src/pages/ui/components/ComponentPreviewCard.vue +50 -0
- package/template/apps/web/src/pages/ui/components/DataComponentShowcases.vue +131 -0
- package/template/apps/web/src/pages/ui/components/FeedbackComponentShowcases.vue +158 -0
- package/template/apps/web/src/pages/ui/components/FoundationComponentShowcases.vue +129 -0
- package/template/apps/web/src/pages/ui/components/NavigationComponentShowcases.vue +155 -0
- package/template/apps/web/src/pages/ui/components/ProjectComponentShowcases.vue +31 -0
- package/template/apps/web/src/pages/ui/components/UiComponentGallery.vue +238 -0
- package/template/apps/web/src/router/index.ts +2 -0
- package/template/apps/web/src/router/manifest-routes.webtest.ts +1 -0
- package/template/apps/web/src/router/platform-admin-route-bindings.ts +3 -1
- package/template/docs/architecture/ai-egress.md +1 -1
- package/template/docs/architecture/ai-runtime-readiness.md +2 -2
- package/template/docs/architecture/ai-runtime.md +15 -15
- package/template/docs/architecture/module-secret-resolution.md +4 -4
- package/template/docs/architecture/report-staging.md +1 -1
- package/template/docs/development/business-web-routes.md +3 -1
- package/template/docs/development/custom-login-page.md +3 -3
- package/template/docs/development/ui-component-gallery.md +44 -0
- package/template/docs/license/license-offline-activation-code.md +2 -2
- package/template/docs/operations/incremental-delivery.md +10 -9
- package/template/docs/platform/agent-runtime.md +4 -4
- package/template/docs/platform/document-ai-runtime.md +3 -3
- package/template/docs/platform/speech-ai-runtime.md +1 -1
- package/template/docs/platform/vision-ai-runtime.md +1 -1
- package/template/docs/product/built-in-roles.md +9 -1
- package/template/packages/modules/package.json +1 -1
- package/template/packages/modules/src/system-admin-routes.ts +8 -0
- package/template/packages/modules/src/system-admin-ui-catalog.ts +1 -1
- package/template/packages/modules/src/system-features.ts +5 -0
- package/template/packages/modules/src/system-module-catalog.ts +2 -1
- package/template/packages/modules/src/system-modules.ts +11 -0
- package/template/packages/modules/src/system-permissions.ts +2 -0
- package/template/packages/modules/src/system-ui-components.test.ts +36 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ArrowRightIcon, PlusIcon, Trash2Icon } from "@lucide/vue";
|
|
3
|
+
import { shallowRef } from "vue";
|
|
4
|
+
import { Badge } from "@/components/ui/badge";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import { Checkbox } from "@/components/ui/checkbox";
|
|
7
|
+
import { Input } from "@/components/ui/input";
|
|
8
|
+
import { Separator } from "@/components/ui/separator";
|
|
9
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
10
|
+
import { Switch } from "@/components/ui/switch";
|
|
11
|
+
import { componentCatalogById, type ComponentId } from "../component-catalog";
|
|
12
|
+
import ComponentPreviewCard from "./ComponentPreviewCard.vue";
|
|
13
|
+
|
|
14
|
+
const props = defineProps<{
|
|
15
|
+
visibleIds: readonly ComponentId[];
|
|
16
|
+
}>();
|
|
17
|
+
|
|
18
|
+
const query = shallowRef("");
|
|
19
|
+
const selected = shallowRef(true);
|
|
20
|
+
const enabled = shallowRef(true);
|
|
21
|
+
|
|
22
|
+
function isVisible(id: ComponentId) {
|
|
23
|
+
return props.visibleIds.includes(id);
|
|
24
|
+
}
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<template>
|
|
28
|
+
<ComponentPreviewCard
|
|
29
|
+
v-if="isVisible('button')"
|
|
30
|
+
:item="componentCatalogById.button"
|
|
31
|
+
>
|
|
32
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
33
|
+
<Button><PlusIcon />新建记录</Button>
|
|
34
|
+
<Button variant="outline">取消</Button>
|
|
35
|
+
<Button variant="secondary">保存草稿</Button>
|
|
36
|
+
<Button variant="destructive"><Trash2Icon />删除</Button>
|
|
37
|
+
<Button variant="link">查看规范<ArrowRightIcon /></Button>
|
|
38
|
+
</div>
|
|
39
|
+
</ComponentPreviewCard>
|
|
40
|
+
|
|
41
|
+
<ComponentPreviewCard
|
|
42
|
+
v-if="isVisible('badge')"
|
|
43
|
+
:item="componentCatalogById.badge"
|
|
44
|
+
>
|
|
45
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
46
|
+
<Badge>已启用</Badge>
|
|
47
|
+
<Badge variant="secondary">草稿</Badge>
|
|
48
|
+
<Badge variant="outline">待处理</Badge>
|
|
49
|
+
<Badge variant="destructive">异常</Badge>
|
|
50
|
+
</div>
|
|
51
|
+
</ComponentPreviewCard>
|
|
52
|
+
|
|
53
|
+
<ComponentPreviewCard
|
|
54
|
+
v-if="isVisible('input')"
|
|
55
|
+
:item="componentCatalogById.input"
|
|
56
|
+
>
|
|
57
|
+
<label class="grid w-full max-w-sm gap-2 text-sm font-medium">
|
|
58
|
+
搜索组件
|
|
59
|
+
<Input v-model="query" placeholder="输入名称或用途" />
|
|
60
|
+
<span class="font-normal text-muted-foreground">
|
|
61
|
+
当前输入:{{ query || "暂无" }}
|
|
62
|
+
</span>
|
|
63
|
+
</label>
|
|
64
|
+
</ComponentPreviewCard>
|
|
65
|
+
|
|
66
|
+
<ComponentPreviewCard
|
|
67
|
+
v-if="isVisible('checkbox')"
|
|
68
|
+
:item="componentCatalogById.checkbox"
|
|
69
|
+
>
|
|
70
|
+
<label class="flex items-center gap-3 text-sm">
|
|
71
|
+
<Checkbox v-model="selected" aria-label="包含已停用组件" />
|
|
72
|
+
<span>
|
|
73
|
+
<span class="block font-medium">包含已停用组件</span>
|
|
74
|
+
<span class="text-muted-foreground">
|
|
75
|
+
{{ selected ? "已包含" : "仅显示可用组件" }}
|
|
76
|
+
</span>
|
|
77
|
+
</span>
|
|
78
|
+
</label>
|
|
79
|
+
</ComponentPreviewCard>
|
|
80
|
+
|
|
81
|
+
<ComponentPreviewCard
|
|
82
|
+
v-if="isVisible('switch')"
|
|
83
|
+
:item="componentCatalogById.switch"
|
|
84
|
+
>
|
|
85
|
+
<div
|
|
86
|
+
class="flex w-full items-center justify-between gap-4 rounded-lg border bg-background p-3"
|
|
87
|
+
>
|
|
88
|
+
<div>
|
|
89
|
+
<p class="text-sm font-medium">组件预览</p>
|
|
90
|
+
<p class="text-xs text-muted-foreground">
|
|
91
|
+
{{ enabled ? "预览已启用" : "预览已暂停" }}
|
|
92
|
+
</p>
|
|
93
|
+
</div>
|
|
94
|
+
<Switch v-model="enabled" aria-label="切换组件预览" />
|
|
95
|
+
</div>
|
|
96
|
+
</ComponentPreviewCard>
|
|
97
|
+
|
|
98
|
+
<ComponentPreviewCard
|
|
99
|
+
v-if="isVisible('separator')"
|
|
100
|
+
:item="componentCatalogById.separator"
|
|
101
|
+
>
|
|
102
|
+
<div class="w-full max-w-sm text-sm">
|
|
103
|
+
<p class="font-medium">组件信息</p>
|
|
104
|
+
<p class="mt-1 text-muted-foreground">由 Windy Web 统一维护。</p>
|
|
105
|
+
<Separator class="my-4" />
|
|
106
|
+
<div class="flex h-5 items-center gap-3">
|
|
107
|
+
<span>稳定</span>
|
|
108
|
+
<Separator orientation="vertical" />
|
|
109
|
+
<span>可访问</span>
|
|
110
|
+
<Separator orientation="vertical" />
|
|
111
|
+
<span>可复用</span>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</ComponentPreviewCard>
|
|
115
|
+
|
|
116
|
+
<ComponentPreviewCard
|
|
117
|
+
v-if="isVisible('skeleton')"
|
|
118
|
+
:item="componentCatalogById.skeleton"
|
|
119
|
+
>
|
|
120
|
+
<div class="flex w-full max-w-sm items-center gap-3">
|
|
121
|
+
<Skeleton class="size-10 rounded-full" />
|
|
122
|
+
<div class="grid flex-1 gap-2">
|
|
123
|
+
<Skeleton class="h-3 w-1/3" />
|
|
124
|
+
<Skeleton class="h-3 w-full" />
|
|
125
|
+
<Skeleton class="h-3 w-4/5" />
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</ComponentPreviewCard>
|
|
129
|
+
</template>
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
BellIcon,
|
|
4
|
+
ChevronDownIcon,
|
|
5
|
+
LayoutDashboardIcon,
|
|
6
|
+
SettingsIcon,
|
|
7
|
+
} from "@lucide/vue";
|
|
8
|
+
import { shallowRef } from "vue";
|
|
9
|
+
import {
|
|
10
|
+
Breadcrumb,
|
|
11
|
+
BreadcrumbItem,
|
|
12
|
+
BreadcrumbLink,
|
|
13
|
+
BreadcrumbList,
|
|
14
|
+
BreadcrumbPage,
|
|
15
|
+
BreadcrumbSeparator,
|
|
16
|
+
} from "@/components/ui/breadcrumb";
|
|
17
|
+
import { Button } from "@/components/ui/button";
|
|
18
|
+
import {
|
|
19
|
+
DropdownMenu,
|
|
20
|
+
DropdownMenuCheckboxItem,
|
|
21
|
+
DropdownMenuContent,
|
|
22
|
+
DropdownMenuItem,
|
|
23
|
+
DropdownMenuLabel,
|
|
24
|
+
DropdownMenuSeparator,
|
|
25
|
+
DropdownMenuShortcut,
|
|
26
|
+
DropdownMenuTrigger,
|
|
27
|
+
} from "@/components/ui/dropdown-menu";
|
|
28
|
+
import {
|
|
29
|
+
SidebarGroup,
|
|
30
|
+
SidebarGroupContent,
|
|
31
|
+
SidebarGroupLabel,
|
|
32
|
+
SidebarMenu,
|
|
33
|
+
SidebarMenuBadge,
|
|
34
|
+
SidebarMenuItem,
|
|
35
|
+
} from "@/components/ui/sidebar";
|
|
36
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
37
|
+
import { componentCatalogById, type ComponentId } from "../component-catalog";
|
|
38
|
+
import ComponentPreviewCard from "./ComponentPreviewCard.vue";
|
|
39
|
+
|
|
40
|
+
const props = defineProps<{
|
|
41
|
+
visibleIds: readonly ComponentId[];
|
|
42
|
+
}>();
|
|
43
|
+
|
|
44
|
+
const compactMode = shallowRef(false);
|
|
45
|
+
|
|
46
|
+
function isVisible(id: ComponentId) {
|
|
47
|
+
return props.visibleIds.includes(id);
|
|
48
|
+
}
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<ComponentPreviewCard
|
|
53
|
+
v-if="isVisible('breadcrumb')"
|
|
54
|
+
:item="componentCatalogById.breadcrumb"
|
|
55
|
+
>
|
|
56
|
+
<Breadcrumb>
|
|
57
|
+
<BreadcrumbList>
|
|
58
|
+
<BreadcrumbItem>
|
|
59
|
+
<BreadcrumbLink href="/admin">工作台</BreadcrumbLink>
|
|
60
|
+
</BreadcrumbItem>
|
|
61
|
+
<BreadcrumbSeparator />
|
|
62
|
+
<BreadcrumbItem>
|
|
63
|
+
<BreadcrumbLink href="/ui/component">UI 组件</BreadcrumbLink>
|
|
64
|
+
</BreadcrumbItem>
|
|
65
|
+
<BreadcrumbSeparator />
|
|
66
|
+
<BreadcrumbItem>
|
|
67
|
+
<BreadcrumbPage>面包屑</BreadcrumbPage>
|
|
68
|
+
</BreadcrumbItem>
|
|
69
|
+
</BreadcrumbList>
|
|
70
|
+
</Breadcrumb>
|
|
71
|
+
</ComponentPreviewCard>
|
|
72
|
+
|
|
73
|
+
<ComponentPreviewCard
|
|
74
|
+
v-if="isVisible('tabs')"
|
|
75
|
+
:item="componentCatalogById.tabs"
|
|
76
|
+
>
|
|
77
|
+
<Tabs default-value="preview" class="w-full">
|
|
78
|
+
<TabsList aria-label="组件页签示例">
|
|
79
|
+
<TabsTrigger value="preview">预览</TabsTrigger>
|
|
80
|
+
<TabsTrigger value="usage">使用说明</TabsTrigger>
|
|
81
|
+
</TabsList>
|
|
82
|
+
<TabsContent
|
|
83
|
+
value="preview"
|
|
84
|
+
class="rounded-lg border bg-background p-3 text-sm"
|
|
85
|
+
>
|
|
86
|
+
在真实主题和间距系统中检查组件。
|
|
87
|
+
</TabsContent>
|
|
88
|
+
<TabsContent
|
|
89
|
+
value="usage"
|
|
90
|
+
class="rounded-lg border bg-background p-3 text-sm"
|
|
91
|
+
>
|
|
92
|
+
从组件目录导入,不复制基础组件源码。
|
|
93
|
+
</TabsContent>
|
|
94
|
+
</Tabs>
|
|
95
|
+
</ComponentPreviewCard>
|
|
96
|
+
|
|
97
|
+
<ComponentPreviewCard
|
|
98
|
+
v-if="isVisible('dropdown-menu')"
|
|
99
|
+
:item="componentCatalogById['dropdown-menu']"
|
|
100
|
+
>
|
|
101
|
+
<DropdownMenu>
|
|
102
|
+
<DropdownMenuTrigger as-child>
|
|
103
|
+
<Button variant="outline">页面操作<ChevronDownIcon /></Button>
|
|
104
|
+
</DropdownMenuTrigger>
|
|
105
|
+
<DropdownMenuContent align="start" class="w-52">
|
|
106
|
+
<DropdownMenuLabel>显示设置</DropdownMenuLabel>
|
|
107
|
+
<DropdownMenuCheckboxItem v-model="compactMode">
|
|
108
|
+
紧凑模式
|
|
109
|
+
</DropdownMenuCheckboxItem>
|
|
110
|
+
<DropdownMenuSeparator />
|
|
111
|
+
<DropdownMenuItem>
|
|
112
|
+
刷新组件
|
|
113
|
+
<DropdownMenuShortcut>⌘R</DropdownMenuShortcut>
|
|
114
|
+
</DropdownMenuItem>
|
|
115
|
+
</DropdownMenuContent>
|
|
116
|
+
</DropdownMenu>
|
|
117
|
+
</ComponentPreviewCard>
|
|
118
|
+
|
|
119
|
+
<ComponentPreviewCard
|
|
120
|
+
v-if="isVisible('sidebar')"
|
|
121
|
+
:item="componentCatalogById.sidebar"
|
|
122
|
+
>
|
|
123
|
+
<div
|
|
124
|
+
class="w-full max-w-xs overflow-hidden rounded-lg border bg-sidebar text-sidebar-foreground"
|
|
125
|
+
>
|
|
126
|
+
<SidebarGroup>
|
|
127
|
+
<SidebarGroupLabel>组件导航</SidebarGroupLabel>
|
|
128
|
+
<SidebarGroupContent>
|
|
129
|
+
<SidebarMenu>
|
|
130
|
+
<SidebarMenuItem>
|
|
131
|
+
<Button
|
|
132
|
+
variant="ghost"
|
|
133
|
+
class="w-full justify-start bg-sidebar-accent"
|
|
134
|
+
>
|
|
135
|
+
<LayoutDashboardIcon />总览
|
|
136
|
+
</Button>
|
|
137
|
+
</SidebarMenuItem>
|
|
138
|
+
<SidebarMenuItem>
|
|
139
|
+
<Button variant="ghost" class="w-full justify-start">
|
|
140
|
+
<SettingsIcon />表单组件
|
|
141
|
+
</Button>
|
|
142
|
+
<SidebarMenuBadge class="top-1.5">7</SidebarMenuBadge>
|
|
143
|
+
</SidebarMenuItem>
|
|
144
|
+
<SidebarMenuItem>
|
|
145
|
+
<Button variant="ghost" class="w-full justify-start">
|
|
146
|
+
<BellIcon />反馈组件
|
|
147
|
+
</Button>
|
|
148
|
+
<SidebarMenuBadge class="top-1.5">5</SidebarMenuBadge>
|
|
149
|
+
</SidebarMenuItem>
|
|
150
|
+
</SidebarMenu>
|
|
151
|
+
</SidebarGroupContent>
|
|
152
|
+
</SidebarGroup>
|
|
153
|
+
</div>
|
|
154
|
+
</ComponentPreviewCard>
|
|
155
|
+
</template>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Badge } from "@/components/ui/badge";
|
|
3
|
+
import type { ComponentCatalogItem } from "../component-catalog";
|
|
4
|
+
import ComponentPreviewCard from "./ComponentPreviewCard.vue";
|
|
5
|
+
|
|
6
|
+
defineProps<{
|
|
7
|
+
items: readonly ComponentCatalogItem[];
|
|
8
|
+
}>();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<ComponentPreviewCard v-for="item in items" :key="item.id" :item="item">
|
|
13
|
+
<div class="grid w-full gap-3">
|
|
14
|
+
<p class="text-sm text-muted-foreground">
|
|
15
|
+
已从当前项目的组件入口自动发现。交互预览可在项目内按需补充。
|
|
16
|
+
</p>
|
|
17
|
+
<div class="flex flex-wrap gap-2">
|
|
18
|
+
<Badge
|
|
19
|
+
v-for="exportName in item.exports"
|
|
20
|
+
:key="exportName"
|
|
21
|
+
variant="outline"
|
|
22
|
+
>
|
|
23
|
+
{{ exportName }}
|
|
24
|
+
</Badge>
|
|
25
|
+
<Badge v-if="item.exports.length === 0" variant="secondary">
|
|
26
|
+
暂无具名导出
|
|
27
|
+
</Badge>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</ComponentPreviewCard>
|
|
31
|
+
</template>
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { BlocksIcon, SearchIcon, XIcon } from "@lucide/vue";
|
|
3
|
+
import { computed, shallowRef } from "vue";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import { Input } from "@/components/ui/input";
|
|
6
|
+
import {
|
|
7
|
+
componentCatalog,
|
|
8
|
+
componentCategories,
|
|
9
|
+
type ComponentCategoryId,
|
|
10
|
+
} from "../component-catalog";
|
|
11
|
+
import DataComponentShowcases from "./DataComponentShowcases.vue";
|
|
12
|
+
import FeedbackComponentShowcases from "./FeedbackComponentShowcases.vue";
|
|
13
|
+
import FoundationComponentShowcases from "./FoundationComponentShowcases.vue";
|
|
14
|
+
import NavigationComponentShowcases from "./NavigationComponentShowcases.vue";
|
|
15
|
+
import ProjectComponentShowcases from "./ProjectComponentShowcases.vue";
|
|
16
|
+
|
|
17
|
+
type CategoryFilter = "all" | ComponentCategoryId;
|
|
18
|
+
|
|
19
|
+
const searchQuery = shallowRef("");
|
|
20
|
+
const activeCategory = shallowRef<CategoryFilter>("all");
|
|
21
|
+
const normalizedQuery = computed(() =>
|
|
22
|
+
searchQuery.value.trim().toLocaleLowerCase(),
|
|
23
|
+
);
|
|
24
|
+
const visibleComponents = computed(() =>
|
|
25
|
+
componentCatalog.filter((component) => {
|
|
26
|
+
if (
|
|
27
|
+
activeCategory.value !== "all" &&
|
|
28
|
+
component.category !== activeCategory.value
|
|
29
|
+
) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (!normalizedQuery.value) return true;
|
|
33
|
+
return [
|
|
34
|
+
component.name,
|
|
35
|
+
component.label,
|
|
36
|
+
component.description,
|
|
37
|
+
component.exportPath,
|
|
38
|
+
...component.keywords,
|
|
39
|
+
].some((value) =>
|
|
40
|
+
value.toLocaleLowerCase().includes(normalizedQuery.value),
|
|
41
|
+
);
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
const visibleIds = computed(() => visibleComponents.value.map(({ id }) => id));
|
|
45
|
+
const visibleProjectComponents = computed(() =>
|
|
46
|
+
visibleComponents.value.filter(({ category }) => category === "project"),
|
|
47
|
+
);
|
|
48
|
+
const visibleCategories = computed(() =>
|
|
49
|
+
componentCategories.filter((category) =>
|
|
50
|
+
visibleComponents.value.some(
|
|
51
|
+
(component) => component.category === category.id,
|
|
52
|
+
),
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
const categoryCounts = Object.fromEntries(
|
|
56
|
+
componentCategories.map((category) => [
|
|
57
|
+
category.id,
|
|
58
|
+
componentCatalog.filter((component) => component.category === category.id)
|
|
59
|
+
.length,
|
|
60
|
+
]),
|
|
61
|
+
) as Readonly<Record<ComponentCategoryId, number>>;
|
|
62
|
+
|
|
63
|
+
function selectCategory(category: CategoryFilter) {
|
|
64
|
+
activeCategory.value = category;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function clearFilters() {
|
|
68
|
+
searchQuery.value = "";
|
|
69
|
+
activeCategory.value = "all";
|
|
70
|
+
}
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<template>
|
|
74
|
+
<div class="mx-auto grid w-full max-w-7xl gap-6">
|
|
75
|
+
<header
|
|
76
|
+
class="overflow-hidden rounded-2xl border bg-card text-card-foreground"
|
|
77
|
+
>
|
|
78
|
+
<div
|
|
79
|
+
class="grid gap-6 p-6 lg:grid-cols-[minmax(0,1fr)_minmax(24rem,0.72fr)] lg:p-8"
|
|
80
|
+
>
|
|
81
|
+
<div class="max-w-2xl">
|
|
82
|
+
<div
|
|
83
|
+
class="mb-4 flex size-11 items-center justify-center rounded-xl bg-primary text-primary-foreground"
|
|
84
|
+
>
|
|
85
|
+
<BlocksIcon class="size-5" />
|
|
86
|
+
</div>
|
|
87
|
+
<p
|
|
88
|
+
class="text-xs font-semibold tracking-[0.18em] text-muted-foreground uppercase"
|
|
89
|
+
>
|
|
90
|
+
Windy UI Registry
|
|
91
|
+
</p>
|
|
92
|
+
<h1 class="mt-2 text-3xl font-semibold tracking-tight">组件总览</h1>
|
|
93
|
+
<p class="mt-3 max-w-xl text-sm leading-6 text-muted-foreground">
|
|
94
|
+
在真实主题、间距和交互上下文中检查 Web 基础组件。每个示例均直接引用
|
|
95
|
+
<code class="rounded bg-muted px-1 py-0.5">components/ui</code>
|
|
96
|
+
中的封装。
|
|
97
|
+
</p>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div class="grid content-start gap-2 rounded-xl border bg-muted/20 p-4">
|
|
101
|
+
<div class="flex items-center justify-between gap-4 border-b pb-3">
|
|
102
|
+
<span class="text-sm font-medium">注册清单</span>
|
|
103
|
+
<code class="text-xs text-muted-foreground">
|
|
104
|
+
{{ componentCatalog.length }} 组件族 ·
|
|
105
|
+
{{ componentCategories.length }} 类
|
|
106
|
+
</code>
|
|
107
|
+
</div>
|
|
108
|
+
<div
|
|
109
|
+
v-for="category in componentCategories"
|
|
110
|
+
:key="category.id"
|
|
111
|
+
class="grid grid-cols-[6rem_1fr_auto] items-center gap-3 py-1 text-xs"
|
|
112
|
+
>
|
|
113
|
+
<code class="text-muted-foreground">{{ category.id }}</code>
|
|
114
|
+
<span>{{ category.label }}</span>
|
|
115
|
+
<span class="tabular-nums text-muted-foreground">
|
|
116
|
+
{{ categoryCounts[category.id] }}
|
|
117
|
+
</span>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</header>
|
|
122
|
+
|
|
123
|
+
<section
|
|
124
|
+
aria-label="组件筛选"
|
|
125
|
+
class="sticky top-0 z-10 rounded-xl border bg-background/95 p-3 shadow-sm backdrop-blur"
|
|
126
|
+
>
|
|
127
|
+
<div
|
|
128
|
+
class="flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between"
|
|
129
|
+
>
|
|
130
|
+
<div class="relative w-full xl:max-w-sm">
|
|
131
|
+
<SearchIcon
|
|
132
|
+
class="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
|
|
133
|
+
/>
|
|
134
|
+
<Input
|
|
135
|
+
v-model="searchQuery"
|
|
136
|
+
type="search"
|
|
137
|
+
aria-label="搜索组件"
|
|
138
|
+
placeholder="搜索名称、用途或导出路径"
|
|
139
|
+
class="pl-8"
|
|
140
|
+
/>
|
|
141
|
+
</div>
|
|
142
|
+
<div class="flex flex-wrap gap-2" aria-label="按分类筛选">
|
|
143
|
+
<Button
|
|
144
|
+
size="sm"
|
|
145
|
+
:variant="activeCategory === 'all' ? 'default' : 'outline'"
|
|
146
|
+
data-category-id="all"
|
|
147
|
+
@click="selectCategory('all')"
|
|
148
|
+
>
|
|
149
|
+
全部
|
|
150
|
+
<span class="tabular-nums opacity-70">{{
|
|
151
|
+
componentCatalog.length
|
|
152
|
+
}}</span>
|
|
153
|
+
</Button>
|
|
154
|
+
<Button
|
|
155
|
+
v-for="category in componentCategories"
|
|
156
|
+
:key="category.id"
|
|
157
|
+
size="sm"
|
|
158
|
+
:variant="activeCategory === category.id ? 'default' : 'outline'"
|
|
159
|
+
:data-category-id="category.id"
|
|
160
|
+
@click="selectCategory(category.id)"
|
|
161
|
+
>
|
|
162
|
+
{{ category.label }}
|
|
163
|
+
<span class="tabular-nums opacity-70">{{
|
|
164
|
+
categoryCounts[category.id]
|
|
165
|
+
}}</span>
|
|
166
|
+
</Button>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
<p class="mt-2 text-xs text-muted-foreground" aria-live="polite">
|
|
170
|
+
当前显示 {{ visibleComponents.length }} /
|
|
171
|
+
{{ componentCatalog.length }} 个组件族
|
|
172
|
+
</p>
|
|
173
|
+
</section>
|
|
174
|
+
|
|
175
|
+
<template v-if="visibleComponents.length">
|
|
176
|
+
<section
|
|
177
|
+
v-for="category in visibleCategories"
|
|
178
|
+
:key="category.id"
|
|
179
|
+
:aria-labelledby="`category-${category.id}`"
|
|
180
|
+
class="grid gap-4"
|
|
181
|
+
>
|
|
182
|
+
<header
|
|
183
|
+
class="flex flex-wrap items-end justify-between gap-2 border-b pb-3"
|
|
184
|
+
>
|
|
185
|
+
<div>
|
|
186
|
+
<p class="text-xs font-medium text-muted-foreground">
|
|
187
|
+
{{ category.id }}
|
|
188
|
+
</p>
|
|
189
|
+
<h2
|
|
190
|
+
:id="`category-${category.id}`"
|
|
191
|
+
class="mt-1 text-xl font-semibold"
|
|
192
|
+
>
|
|
193
|
+
{{ category.label }}
|
|
194
|
+
</h2>
|
|
195
|
+
</div>
|
|
196
|
+
<p class="text-sm text-muted-foreground">
|
|
197
|
+
{{ category.description }}
|
|
198
|
+
</p>
|
|
199
|
+
</header>
|
|
200
|
+
|
|
201
|
+
<div class="grid items-start gap-4 lg:grid-cols-2">
|
|
202
|
+
<FoundationComponentShowcases
|
|
203
|
+
v-if="category.id === 'foundation'"
|
|
204
|
+
:visible-ids="visibleIds"
|
|
205
|
+
/>
|
|
206
|
+
<NavigationComponentShowcases
|
|
207
|
+
v-else-if="category.id === 'navigation'"
|
|
208
|
+
:visible-ids="visibleIds"
|
|
209
|
+
/>
|
|
210
|
+
<FeedbackComponentShowcases
|
|
211
|
+
v-else-if="category.id === 'feedback'"
|
|
212
|
+
:visible-ids="visibleIds"
|
|
213
|
+
/>
|
|
214
|
+
<DataComponentShowcases
|
|
215
|
+
v-else-if="category.id === 'data'"
|
|
216
|
+
:visible-ids="visibleIds"
|
|
217
|
+
/>
|
|
218
|
+
<ProjectComponentShowcases v-else :items="visibleProjectComponents" />
|
|
219
|
+
</div>
|
|
220
|
+
</section>
|
|
221
|
+
</template>
|
|
222
|
+
|
|
223
|
+
<section
|
|
224
|
+
v-else
|
|
225
|
+
class="grid min-h-64 place-items-center rounded-xl border border-dashed p-8 text-center"
|
|
226
|
+
>
|
|
227
|
+
<div>
|
|
228
|
+
<p class="font-medium">没有匹配的组件</p>
|
|
229
|
+
<p class="mt-2 text-sm text-muted-foreground">
|
|
230
|
+
调整关键词或清除分类后重试。
|
|
231
|
+
</p>
|
|
232
|
+
<Button variant="outline" class="mt-4" @click="clearFilters">
|
|
233
|
+
<XIcon />清除筛选
|
|
234
|
+
</Button>
|
|
235
|
+
</div>
|
|
236
|
+
</section>
|
|
237
|
+
</div>
|
|
238
|
+
</template>
|
|
@@ -29,6 +29,7 @@ import SystemResourceRoutePage from "@/pages/system/SystemResourceRoutePage.vue"
|
|
|
29
29
|
import GlobalSettingsPage from "@/pages/settings/GlobalSettingsPage.vue";
|
|
30
30
|
// @windy-module system.settings end
|
|
31
31
|
import PersonalSettingsPage from "@/pages/profile/PersonalSettingsPage.vue";
|
|
32
|
+
import UiComponentPage from "@/pages/ui/UiComponentPage.vue";
|
|
32
33
|
// @windy-module system.configuration begin
|
|
33
34
|
import ModuleConfigurationPage from "@/pages/configuration/ModuleConfigurationPage.vue";
|
|
34
35
|
// @windy-module system.configuration end
|
|
@@ -84,6 +85,7 @@ const platformAdminPageComponents: Partial<
|
|
|
84
85
|
> = {
|
|
85
86
|
resource: SystemResourceRoutePage,
|
|
86
87
|
profile: PersonalSettingsPage,
|
|
88
|
+
"ui-components": UiComponentPage,
|
|
87
89
|
// @windy-module system.settings begin
|
|
88
90
|
settings: GlobalSettingsPage,
|
|
89
91
|
// @windy-module system.settings end
|
|
@@ -47,6 +47,7 @@ describe("Manifest 菜单与 Router 契约", () => {
|
|
|
47
47
|
expect(definitions.some(({ path }) => path === "/admin/profile")).toBe(
|
|
48
48
|
true,
|
|
49
49
|
);
|
|
50
|
+
expect(definitions.some(({ path }) => path === "/ui/component")).toBe(true);
|
|
50
51
|
expect(definitions.some(({ path }) => path === "/admin/work-orders")).toBe(
|
|
51
52
|
false,
|
|
52
53
|
);
|
|
@@ -8,7 +8,8 @@ export type PlatformAdminPageKey =
|
|
|
8
8
|
| "notification"
|
|
9
9
|
| "operations"
|
|
10
10
|
| "ai-runtime"
|
|
11
|
-
| "configuration"
|
|
11
|
+
| "configuration"
|
|
12
|
+
| "ui-components";
|
|
12
13
|
|
|
13
14
|
export interface PlatformAdminRouteBinding {
|
|
14
15
|
readonly routeKey: string;
|
|
@@ -37,6 +38,7 @@ export const platformAdminRouteBindings: readonly PlatformAdminRouteBinding[] =
|
|
|
37
38
|
resourceKey: "dictionaries",
|
|
38
39
|
},
|
|
39
40
|
{ routeKey: "system.feature", page: "resource", resourceKey: "features" },
|
|
41
|
+
{ routeKey: "system.ui-components", page: "ui-components" },
|
|
40
42
|
// @windy-module system.license begin
|
|
41
43
|
{
|
|
42
44
|
routeKey: "system.license",
|
|
@@ -72,7 +72,7 @@ OpenAI-compatible Adapter 只使用 Agent Server 启动配置的一个 Endpoint
|
|
|
72
72
|
配置还必须通过 `AI_PROVIDER_HTTPS_HOST_ALLOWLIST` 的精确 Host 匹配;不支持通配符、
|
|
73
73
|
scheme、port 或重复 Host。内网 HTTP 继续受既有私有地址校验约束。
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
Windy Server 向 Agent Server 只发送 Operation 的 policy mode 与 Deployment
|
|
76
76
|
Key。Agent Server 在发出模型请求前核对:
|
|
77
77
|
|
|
78
78
|
1. policy 必须是 `configured-endpoint`;
|
|
@@ -12,7 +12,7 @@ deployment pin 和真实 `/health/ready` snapshot 投影新版 Operation;未
|
|
|
12
12
|
|
|
13
13
|
## 目标
|
|
14
14
|
|
|
15
|
-
AI Runtime Readiness V1 为
|
|
15
|
+
AI Runtime Readiness V1 为 Windy Server、Module Operation Host 和各独立
|
|
16
16
|
Runtime 提供同一套失败关闭契约。它回答的是:
|
|
17
17
|
|
|
18
18
|
> 当前这个 Operation 能否在一个已经过真实探测、身份和 release 均未漂移的
|
|
@@ -440,7 +440,7 @@ Readiness 不替代以下权威检查:
|
|
|
440
440
|
- Provider egress、DLP 和 Tool network policy;
|
|
441
441
|
- Run 幂等、取消、审计和输出校验。
|
|
442
442
|
|
|
443
|
-
这些 Guard 仍由
|
|
443
|
+
这些 Guard 仍由 Windy Server 在每次执行的相应阶段重新检查。对未授权调用方,
|
|
444
444
|
Host 不应通过 readiness 暴露跨模块或跨租户的 Deployment 细节。
|
|
445
445
|
|
|
446
446
|
Readiness 也不是 GPU、CPU、显存、队列或并发槽位的资源预留。resource limits 描述该
|