import { createApp, reactive, ref } from 'vue' import { Plus, Search, Settings } from '@lucide/vue' import { G3Button, G3ButtonGroup, G3ButtonGroupSeparator, G3Checkbox, G3Col, G3DatePicker, G3Drawer, G3Dropdown, G3DropdownItem, G3Form, G3FormGroup, G3FormItem, G3Input, G3Message, G3Modal, G3Notification, G3Pagination, G3Popover, G3Radio, G3RadioGroup, G3RangePicker, G3Row, G3Scrollbar, G3Segmented, G3Select, G3Space, G3Spin, G3Splitter, G3SplitterPanel, G3Switch, G3TabPane, G3Tabs, G3Tag, G3TimeSelect, G3Tooltip, G3Tree, } from '@g3soft/ui' import '@g3soft/tokens' /** * playground —— 组件在真实浏览器里的落脚点。 * * 约定:所有 E2E 与人工手测都通过 `data-testid` 找元素, * 不要依赖文本或层级结构(文案会改、结构会调,testid 保持稳定)。 */ const ROLE_OPTIONS = [ { label: '管理员', value: 1 }, { label: '会员', value: 2 }, { label: '访客', value: 3 }, ] const TREE_DATA = [ { key: '1', title: '前端', children: [ { key: '1-1', title: 'Vue', children: [{ key: '1-1-1', title: '组合式 API' }] }, { key: '1-2', title: 'React' }, ], }, { key: '2', title: '后端', children: [{ key: '2-1', title: 'Node.js' }] }, ] const LONG_TEXT = Array.from({ length: 40 }, (_, index) => `第 ${index + 1} 行内容`).join('\n') const App = { components: { G3Button, G3ButtonGroup, G3ButtonGroupSeparator, G3Checkbox, G3Col, G3DatePicker, G3Drawer, G3Dropdown, G3DropdownItem, G3Form, G3FormGroup, G3FormItem, G3Input, G3Modal, G3Pagination, G3Popover, G3Radio, G3RadioGroup, G3RangePicker, G3Row, G3Scrollbar, G3Segmented, G3Select, G3Space, G3Spin, G3Splitter, G3SplitterPanel, G3Switch, G3TabPane, G3Tabs, G3Tag, G3TimeSelect, G3Tooltip, G3Tree, Plus, Search, Settings, }, setup() { const model = reactive({ name: '张三', intro: '这是多行文本', role: 1 as number | undefined, city: [] as number[], agree: true, level: 2, gender: 'male', date: '2026-10-08 09:30:00', range: ['2026-10-01', '2026-10-08'] as string[], }) const tab = ref('a') // 选项是中文原始值,初始值必须对上,否则没有选中项 const seg = ref('日') const time = ref(new Date(2026, 9, 8, 9, 30, 0)) const modalOpen = ref(false) const modalConfirmOpen = ref(false) const drawerOpen = ref(false) // 四个方向都要能验:上下面板的宽度必须撑满视口(曾因用 flex 对齐而缩到内容宽) const drawerPlacement = ref<'left' | 'right' | 'top' | 'bottom'>('right') // 挂载即展开的浮层:验证「初始 open」也能拿到 z-index const popoverAOpen = ref(true) const popoverBOpen = ref(true) // 扁平树:两个节点都不是父节点,用来验「叶子不该留展开箭头占位」 const flatTreeData = [ { key: 'flat-1', title: '节点一(无子节点)' }, { key: 'flat-2', title: '节点二(无子节点)' }, ] const treeMenuItems = [ { key: 'add', label: '新增子节点' }, { key: 'rename', label: '重命名' }, { key: 'delete', label: '删除', danger: true }, ] function toast(placement?: string) { G3Message.success(`操作成功`, placement ? { placement: placement as never } : undefined) } function notify() { G3Notification.info({ title: '定时任务完成', description: '本次共同步 128 条数据。' }) } function openDrawer(placement: 'left' | 'right' | 'top' | 'bottom') { drawerPlacement.value = placement drawerOpen.value = true } function openCommandConfirm() { G3Modal.confirm({ title: '删除确认', content: '确定要删除这条记录吗?', onConfirm: () => new Promise((resolve) => setTimeout(resolve, 600)), }) } return { model, tab, seg, time, modalOpen, modalConfirmOpen, drawerOpen, drawerPlacement, openDrawer, popoverAOpen, popoverBOpen, flatTreeData, treeMenuItems, roleOptions: ROLE_OPTIONS, treeData: TREE_DATA, longText: LONG_TEXT, toast, notify, openCommandConfirm, } }, template: `

基础:Button / Tag / Spin / Segmented / Space

主要 次要 描边 幽灵 危险 危险描边 链接 小 大 加载中 禁用
左 中 右 第一 第二 第三 上 中 下 default secondary outline success danger ghost 可关闭
间距测试

Input(含 textarea 与拖拽手柄)

Form / Select / Date / Switch / Radio / Checkbox

男 女 已选中 未选中 禁用

Tabs / Tree / Pagination / Splitter / Scrollbar / TimeSelect

标签一的内容 标签二的内容 不可见
左侧标签页内容 第二个面板 右侧标签页内容 第二个面板
左
右
{{ longText }}

Dropdown / Tooltip / Popover(首两个挂载即展开,用于层级回归)

编辑 复制 删除 悬停提示

Modal / Drawer / Message / Notification

打开 Modal 右侧 Drawer 左侧 Drawer 上方 Drawer 下方 Drawer Modal.confirm Message 默认 Message 左上 Message 右下 Notification

第一段内容。

第 {{ i }} 段内容,用于验证缩放时内容区是否内部滚动。

抽屉内容

`, } createApp(App).mount('#app')