@composy/tool-ui
@composy/tool-ui
@composy/tool-ui 是 LDesign 内部工具体系的 Vue
3 基础 UI 包。它把工具壳层、路由导航、主题系统、前后端桥接协议,以及常用业务组件收敛成一套可复用的统一能力,适合构建运维台、数据控制台、工作台、日志面板和命令执行界面。
核心能力
- 统一壳层:
createUI、ToolShell、分组导航、面包屑、主题切换、状态栏。 - 类型安全桥接:
defineUIBridge、defineUIBridgeMethod、createUIBridgeClient、服务端挂载辅助。 - 模块化装配:
defineUIToolModule、composeUIToolModules、createToolHost。 - 组件库:表单、展示、日志、图表、覆盖层、终端、日期选择、树形视图、上传等。
- 样式运行时注入:无需业务工程重复维护基础样式入口。
适用场景
- 需要快速搭建内部工具壳层,并统一导航与视觉风格。
- 需要把前端页面与 Node/服务端能力通过统一协议对接。
- 需要复用一批偏控制台风格的 Vue 3 组件,而不是从零组织样式与交互。
安装
pnpm add @composy/tool-ui vue vue-router
如果在 LDesign monorepo 内使用,通常直接通过 workspace 依赖接入即可。
包结构
src/
bridge/ 桥接定义、客户端与共享路径解析
components/ Tool* 组件集合
composables/ useUITool / useUiBridge
core/ createUI、路由与上下文
dom/ 焦点、滚动锁、浮层定位、环境判断
server/ 桥接挂载与命令桥接服务端实现
styles/ 主题变量与基础样式注入
types/ 统一类型出口
包级 ESLint、TypeScript 与 @composy/pack 主配置位于
.ldesign/;根目录的 ESLint 与 TypeScript 文件是兼容入口。
快速开始
import { Home } from 'lucide-vue-next'
import { defineComponent, h, ref } from 'vue'
import {
ToolButton,
ToolDescriptions,
ToolGrid,
ToolPage,
ToolPanel,
ToolTerminal,
ToolTimeline,
createUI,
toolCommandBridge,
} from '@composy/tool-ui'
const OverviewPage = defineComponent({
name: 'OverviewPage',
setup() {
const command = ref('pnpm -s test -- --run')
return () =>
h(
ToolPage,
{
eyebrow: 'Workspace',
title: '发布控制台',
description: '把元信息、时间线和命令执行放进同一套工具壳层中。',
},
{
actions: () => [
h(ToolButton, { variant: 'secondary' }, () => '刷新'),
h(ToolButton, () => '发布'),
],
default: () => [
h(ToolGrid, { columns: 2, min: 320 }, () => [
h(
ToolPanel,
{ title: '构建信息' },
{
default: () =>
h(ToolDescriptions, {
columns: 2,
items: [
{ label: 'Region', value: 'ap-southeast-1', copyable: true },
{ label: 'Commit', value: 'c1a4d02', copyable: true },
{ label: 'Artifact', value: 'release-surface.tgz', span: 2 },
],
}),
}
),
h(
ToolPanel,
{ title: '流水线时间线' },
{
default: () =>
h(ToolTimeline, {
items: [
{ title: '构建排队', time: '09:41', tone: 'accent' },
{ title: '产物上传', time: '09:44', tone: 'success' },
{ title: '预热待执行', time: '09:46', tone: 'warning' },
],
}),
}
),
h(
ToolPanel,
{ title: '命令终端', padding: 'none' },
{
default: () =>
h(ToolTerminal, {
modelValue: command.value,
bridgeId: 'tool-command',
cwd: '/workspace/app',
'onUpdate:modelValue': (value: string) => {
command.value = value
},
}),
}
),
]),
],
}
)
},
})
createUI({
shell: {
title: 'Workspace Studio',
subtitle: '内部工具统一工作台',
accentColor: '#c56f34',
railBadge: 'Console',
navigationLabel: '工作台',
navigationPlaceholder: '搜索页面或模块',
statusItems: [
{ label: 'ENV', value: 'DEV', tone: 'accent' },
{ label: 'Region', value: 'CN' },
],
},
routes: [
{
path: '/',
component: OverviewPage,
meta: {
title: '概览',
description: '壳层概览与发布控制',
group: '核心',
iconComponent: Home,
},
},
],
backend: {
defaultBridgeId: 'tool-command',
bridges: [toolCommandBridge],
},
})
桥接系统
1. 定义桥接协议
import { defineUIBridge, defineUIBridgeMethod } from '@composy/tool-ui'
export const metricsBridge = defineUIBridge({
id: 'metrics',
methods: {
summary: defineUIBridgeMethod<void, { online: number; errors: number }>({
method: 'GET',
path: 'summary',
}),
},
})
2. 前端调用
import { createUIBridgeClient } from '@composy/tool-ui'
const client = createUIBridgeClient(metricsBridge, {
baseURL: '/api/ui',
timeoutMs: 10_000,
})
const summary = await client.summary()
timeoutMs 会在请求超时后主动 abort,并以 UIBridgeError.code === 'REQUEST_TIMEOUT'
返回;也可以传入 signal 取消一组请求,取消错误的 code 为 REQUEST_ABORTED。两者都会通过
RequestInit.signal 传给自定义 fetcher,并在请求结束后清理计时器和事件监听器。
3. 服务端挂载
import { HttpServer } from '@composy/kit/network'
import { mountUIBridgeToKitServer } from '@composy/tool-ui/server'
const server = new HttpServer({ port: 3001, cors: true })
mountUIBridgeToKitServer(server, metricsBridge, {
async summary() {
return { online: 128, errors: 2 }
},
})
命令桥接
内置的 toolCommandBridge 用于支持 ToolTerminal:
start:启动命令会话poll:轮询新的stdout/stderrstop:终止当前会话
服务端示例:
import { HttpServer } from '@composy/kit/network'
import { mountToolCommandBridgeToKitServer } from '@composy/tool-ui/server'
const server = new HttpServer({
port: 3001,
cors: true,
})
mountToolCommandBridgeToKitServer(server, {
basePath: '/api/ui',
retentionMs: 10 * 60_000,
})
server.start()
模块化装配
如果你的工具由多个独立模块组成,推荐使用模块 API,而不是手动拼 routes 和 bridges:
import { composeUIToolModules, createToolHost, defineUIToolModule } from '@composy/tool-ui'
这套 API 会负责:
- 为模块根路由拼接
basePath - 合并模块级导航元信息
- 去重桥接定义
- 在没有根路由时自动补充首页跳转
主题与样式
默认情况下,createUI 会自动完成两件事:
- 注入基础样式:
ensureToolUIStyles() - 应用主题变量:
setToolUITheme()/setToolUIColorMode()
如果你只复用组件,不通过 createUI 启动壳层,需要手动注入样式:
import { ensureToolUIStyles } from '@composy/tool-ui'
ensureToolUIStyles()
公共入口
- 根入口:
@composy/tool-ui - 桥接入口:
@composy/tool-ui/bridge - 服务端入口:
@composy/tool-ui/server
说明:
- 构建会产出
dist/下的完整模块树,便于内部调试与源码映射。 - 对外稳定公开入口仍以
package.json#exports为准。
Workbench 宿主入口
面向 Workbench 或其他工具容器时,优先使用轻量入口 @composy/tool-ui/host:
import { createToolHost, defineUIToolModule, ToolShell, useUIBridge } from '@composy/tool-ui/host'
该入口包含统一 Shell、模块组合、宿主创建和桥接 composable,不会把全部业务组件索引带入应用首屏。完整组件库仍通过
@composy/tool-ui 提供。
开发命令
pnpm run type-check
pnpm run lint:check
pnpm run format:check
pnpm run build
pnpm run preview:dev
pnpm run preview:check
构建说明
当前包通过 @composy/pack 的共享 tsup 工作流构建,build 与 dev 脚本显式指定
.ldesign/pack.config.ts:
- 构建配置:
.ldesign/pack.config.ts - 入口策略:
src/**/*.ts - 产物格式:ESM + CJS +
.d.ts - 输出目录:
dist/
这意味着源码目录中的 TypeScript 文件会被完整编译为对应的 dist 模块树,同时保留顶层公开入口。
设计原则
- 壳层与业务解耦:业务页面只关心路由与页面内容。
- 桥接协议先行:前后端通过统一类型契约通信。
- 默认值克制:提供完整基础体验,但尽量保留扩展空间。
- 运行时低侵入:样式与主题按需注入,不强依赖额外入口文件。
相关文档
- QUICKSTART.md
- COMPONENTS.md
- README.en.md
.workspace-ui-runtime
通过 @composy/cli 统一接入
- 接入类型:
helper - 统一命令:
ldesign tool-ui - 命令别名:
ui-lib - 包内原生 bin:无独立 bin
当前包通过 helper 方式接入,统一命令会调用 @composy/cli 内置封装。
pnpm add -D @composy/cli
ldesign tool-ui --help
ldesign tools run tool-ui --help
Related Packages
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.0.2 | 3.5.32 | MIT | |
| 0.0.1 | 3.5.39 | MIT |