20261007220117
This commit is contained in:
1 parent
9217b3fa42
commit
8b1ec67609
275 files changed
+20026
-49299
No files matched your search
@@ -0,0 +1,137 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface ApiEntry {
|
||||
name: string
|
||||
description: string
|
||||
type: string
|
||||
defaultValue?: string
|
||||
}
|
||||
|
||||
interface ApiDoc {
|
||||
displayName: string
|
||||
description: string
|
||||
props: ApiEntry[]
|
||||
events: ApiEntry[]
|
||||
slots: ApiEntry[]
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
/** 组件名(与 packages/ui/src/<name>/ 目录一致),例如 "button" */
|
||||
name: string
|
||||
}>()
|
||||
|
||||
/**
|
||||
* 数据由 scripts/gen-api.mjs 从组件源码生成,不要手写。
|
||||
* 源码改动后执行 `pnpm gen:api` 即可刷新。
|
||||
*/
|
||||
const data = import.meta.glob('../../../data/api/*.json', { eager: true }) as Record<
|
||||
string,
|
||||
{ default: ApiDoc }
|
||||
>
|
||||
|
||||
const doc = computed<ApiDoc | undefined>(() => {
|
||||
const key = Object.keys(data).find((path) => path.endsWith(`/${props.name}.json`))
|
||||
return key ? data[key].default : undefined
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="doc" class="g3-api">
|
||||
<p v-if="doc.description" class="g3-api__desc">{{ doc.description }}</p>
|
||||
|
||||
<template v-if="doc.props.length">
|
||||
<h3>属性</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>属性</th><th>说明</th><th>类型</th><th>默认值</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in doc.props" :key="item.name">
|
||||
<td><code>{{ item.name }}</code></td>
|
||||
<td>{{ item.description }}</td>
|
||||
<td><code>{{ item.type }}</code></td>
|
||||
<td><code>{{ item.defaultValue }}</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<template v-if="doc.events.length">
|
||||
<h3>事件</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>事件名</th><th>说明</th><th>类型</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in doc.events" :key="item.name">
|
||||
<td><code>{{ item.name }}</code></td>
|
||||
<td>{{ item.description }}</td>
|
||||
<td><code>{{ item.type }}</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<template v-if="doc.slots.length">
|
||||
<h3>插槽</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>插槽名</th><th>说明</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in doc.slots" :key="item.name">
|
||||
<td><code>{{ item.name }}</code></td>
|
||||
<td>{{ item.description }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div v-else class="g3-api__missing">
|
||||
未找到 <code>{{ name }}</code> 的 API 数据,请先执行 <code>pnpm gen:api</code>。
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.g3-api__desc {
|
||||
color: var(--ui-text-muted, #6b7280);
|
||||
}
|
||||
|
||||
.g3-api h3 {
|
||||
margin-top: 28px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.g3-api__missing {
|
||||
padding: 12px 16px;
|
||||
border: 1px dashed var(--ui-border, #e5e7eb);
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
color: var(--ui-text-muted, #6b7280);
|
||||
}
|
||||
|
||||
.g3-api table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.g3-api th,
|
||||
.g3-api td {
|
||||
padding: 10px 14px;
|
||||
border: 1px solid var(--ui-border, #e5e7eb);
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.g3-api th {
|
||||
background: var(--ui-bg-muted, #f9fafb);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.g3-api td code {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,152 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, type Component } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
/** 示例路径,相对 docs/examples 且不含 .vue,例如 "button/basic" */
|
||||
name: string
|
||||
}>()
|
||||
|
||||
/**
|
||||
* 收集所有示例。glob 只认相对路径与项目根绝对路径,这里用相对路径,
|
||||
* key 归一化成 "button/basic" 形式。
|
||||
*/
|
||||
const modules = import.meta.glob('../../../examples/**/*.vue', { eager: true }) as Record<
|
||||
string,
|
||||
{ default: Component }
|
||||
>
|
||||
const sources = import.meta.glob('../../../examples/**/*.vue', {
|
||||
query: '?raw',
|
||||
import: 'default',
|
||||
eager: true,
|
||||
}) as Record<string, string>
|
||||
|
||||
function normalize(path: string): string {
|
||||
return path.replace(/^.*\/examples\//, '').replace(/\.vue$/, '')
|
||||
}
|
||||
|
||||
const registry: Record<string, Component> = {}
|
||||
for (const [path, mod] of Object.entries(modules)) {
|
||||
registry[normalize(path)] = mod.default
|
||||
}
|
||||
|
||||
const sourceMap: Record<string, string> = {}
|
||||
for (const [path, raw] of Object.entries(sources)) {
|
||||
sourceMap[normalize(path)] = raw
|
||||
}
|
||||
|
||||
const demo = computed(() => registry[props.name])
|
||||
const source = computed(() => sourceMap[props.name] ?? '')
|
||||
|
||||
const showCode = ref(false)
|
||||
const copied = ref(false)
|
||||
|
||||
async function copyCode() {
|
||||
if (!source.value) return
|
||||
try {
|
||||
await navigator.clipboard.writeText(source.value)
|
||||
copied.value = true
|
||||
window.setTimeout(() => {
|
||||
copied.value = false
|
||||
}, 1500)
|
||||
} catch {
|
||||
// 非安全上下文(http)下剪贴板不可用,静默忽略
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="g3-demo">
|
||||
<div class="g3-demo__preview">
|
||||
<component :is="demo" v-if="demo" />
|
||||
<p v-else class="g3-demo__missing">未找到示例 docs/examples/{{ name }}.vue</p>
|
||||
</div>
|
||||
|
||||
<div class="g3-demo__toolbar">
|
||||
<button type="button" class="g3-demo__action" @click="showCode = !showCode">
|
||||
{{ showCode ? '收起代码' : '查看代码' }}
|
||||
</button>
|
||||
<button type="button" class="g3-demo__action" @click="copyCode">
|
||||
{{ copied ? '已复制' : '复制代码' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-show="showCode" class="g3-demo__code">
|
||||
<pre><code>{{ source }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/**
|
||||
* 示例卡片样式随组件自身提供,不依赖文档站的全局样式。
|
||||
* 颜色优先取 Nuxt UI 的设计令牌(--ui-*),未定义时退回中性值。
|
||||
*/
|
||||
.g3-demo {
|
||||
margin: 16px 0;
|
||||
border: 1px solid var(--ui-border, #e5e7eb);
|
||||
border-radius: 10px;
|
||||
background: var(--ui-bg, #fff);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.g3-demo__preview {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
padding: 28px 20px;
|
||||
}
|
||||
|
||||
.g3-demo__missing {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.g3-demo__toolbar {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: flex-end;
|
||||
padding: 6px 12px;
|
||||
border-top: 1px solid var(--ui-border, #e5e7eb);
|
||||
background: var(--ui-bg-muted, #f9fafb);
|
||||
}
|
||||
|
||||
.g3-demo__action {
|
||||
padding: 4px 10px;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: var(--ui-text-muted, #6b7280);
|
||||
font-family: inherit;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background 0.2s,
|
||||
color 0.2s;
|
||||
}
|
||||
|
||||
.g3-demo__action:hover {
|
||||
background: var(--ui-bg-elevated, rgba(0, 0, 0, 0.04));
|
||||
color: var(--ui-primary, #1677ff);
|
||||
}
|
||||
|
||||
.g3-demo__code {
|
||||
border-top: 1px solid var(--ui-border, #e5e7eb);
|
||||
background: var(--ui-bg-muted, #f9fafb);
|
||||
max-height: 420px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.g3-demo__code pre {
|
||||
margin: 0;
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
.g3-demo__code code {
|
||||
font-family: var(--g3-font-family-mono, ui-monospace, monospace);
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
white-space: pre;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
title: 快速开始
|
||||
description: 安装组件库并在项目中用起来
|
||||
---
|
||||
|
||||
# 快速开始
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
pnpm add @g3soft/ui @g3soft/tokens
|
||||
```
|
||||
|
||||
`vue` 由宿主项目提供(`peerDependencies`),请确保已安装 Vue 3。
|
||||
|
||||
## 引入设计变量
|
||||
|
||||
在应用入口引入一次,组件样式全部依赖这套变量:
|
||||
|
||||
```ts
|
||||
// main.ts
|
||||
import '@g3soft/tokens'
|
||||
```
|
||||
|
||||
## 使用组件
|
||||
|
||||
按需引入:
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { G3Button } from '@g3soft/ui'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<G3Button type="primary" @click="handleClick">主要按钮</G3Button>
|
||||
</template>
|
||||
```
|
||||
|
||||
或全量注册:
|
||||
|
||||
```ts
|
||||
import { G3UI } from '@g3soft/ui'
|
||||
import { createApp } from 'vue'
|
||||
|
||||
createApp(App).use(G3UI).mount('#app')
|
||||
```
|
||||
|
||||
## 换肤
|
||||
|
||||
组件的颜色与尺寸一律引用 `--g3-*` 变量,定制只需覆盖同名变量,不必改组件代码:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--g3-color-primary: #1677ff;
|
||||
--g3-radius: 6px;
|
||||
}
|
||||
```
|
||||
|
||||
暗色主题由 `html[data-g3-theme='dark']` 触发:
|
||||
|
||||
```ts
|
||||
document.documentElement.dataset.g3Theme = 'dark'
|
||||
```
|
||||
|
||||
## 下一步
|
||||
|
||||
- 浏览[组件参考](/reference/ui/button)
|
||||
- 了解[设计变量](/explain/why-tokens)的设计取舍
|
||||
@@ -0,0 +1,89 @@
|
||||
---
|
||||
seo:
|
||||
title: G3Soft 前端技术文档
|
||||
description: G3Soft 自研 Vue 3 组件库、设计变量与前端工程规范。
|
||||
---
|
||||
|
||||
:::u-page-hero
|
||||
#title
|
||||
自研组件,一套规范
|
||||
|
||||
#description
|
||||
G3Soft 前端技术文档站:组件库 API、设计变量、工程规范与设计决策,全部集中在这里。
|
||||
|
||||
#links
|
||||
:::u-button
|
||||
---
|
||||
color: primary
|
||||
size: xl
|
||||
to: /guide/getting-started
|
||||
trailing-icon: i-lucide-arrow-right
|
||||
---
|
||||
快速开始
|
||||
:::
|
||||
|
||||
:::u-button
|
||||
---
|
||||
color: neutral
|
||||
variant: outline
|
||||
size: xl
|
||||
icon: i-lucide-book-open
|
||||
to: /reference/ui/button
|
||||
---
|
||||
浏览组件
|
||||
:::
|
||||
:::
|
||||
|
||||
:::u-page-section
|
||||
#title
|
||||
四个内容域
|
||||
|
||||
#features
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-rocket
|
||||
to: /guide/getting-started
|
||||
---
|
||||
#title
|
||||
指南
|
||||
|
||||
#description
|
||||
从安装到上手,以及版本升级与迁移说明。
|
||||
:::
|
||||
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-component
|
||||
to: /reference/ui/button
|
||||
---
|
||||
#title
|
||||
参考
|
||||
|
||||
#description
|
||||
组件库 API 自动生成,另有 Hooks 与后端接口文档。
|
||||
:::
|
||||
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-ruler
|
||||
to: /standard/code-style
|
||||
---
|
||||
#title
|
||||
规范
|
||||
|
||||
#description
|
||||
代码风格、命名约定、Git 与发布流程等团队工程规范。
|
||||
:::
|
||||
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-lightbulb
|
||||
to: /explain/why-tokens
|
||||
---
|
||||
#title
|
||||
解释
|
||||
|
||||
#description
|
||||
设计决策背后的取舍与常见问题解答。
|
||||
:::
|
||||
:::
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: Button 按钮
|
||||
description: 常用的操作按钮,支持多种视觉类型与状态
|
||||
---
|
||||
|
||||
# Button 按钮
|
||||
|
||||
常用的操作按钮,支持 7 种视觉类型、3 种尺寸与加载、禁用等状态。
|
||||
|
||||
## 基础用法
|
||||
|
||||
::demo{name="button/basic"}
|
||||
::
|
||||
|
||||
## 视觉类型
|
||||
|
||||
语义化类型用于表达操作的风险等级。
|
||||
|
||||
::demo{name="button/type"}
|
||||
::
|
||||
|
||||
## 尺寸
|
||||
|
||||
::demo{name="button/size"}
|
||||
::
|
||||
|
||||
## 状态
|
||||
|
||||
::demo{name="button/state"}
|
||||
::
|
||||
|
||||
## 图标与撑满
|
||||
|
||||
::demo{name="button/icon"}
|
||||
::
|
||||
|
||||
::demo{name="button/block"}
|
||||
::
|
||||
|
||||
## API
|
||||
|
||||
<ApiTable name="button" />
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"displayName": "Button",
|
||||
"description": "常用的操作按钮。 支持 7 种视觉类型、3 种尺寸,以及加载、禁用、撑满、纯图标等状态。",
|
||||
"props": [
|
||||
{
|
||||
"name": "type",
|
||||
"description": "视觉类型",
|
||||
"type": "ButtonType",
|
||||
"defaultValue": "default"
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"description": "尺寸",
|
||||
"type": "ButtonSize",
|
||||
"defaultValue": "medium"
|
||||
},
|
||||
{
|
||||
"name": "htmlType",
|
||||
"description": "原生 button 的 type 属性",
|
||||
"type": "union",
|
||||
"defaultValue": "button"
|
||||
},
|
||||
{
|
||||
"name": "disabled",
|
||||
"description": "是否禁用",
|
||||
"type": "boolean",
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"name": "loading",
|
||||
"description": "是否加载中:同时禁止点击并展示加载图标",
|
||||
"type": "boolean",
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"name": "block",
|
||||
"description": "是否撑满父容器宽度",
|
||||
"type": "boolean",
|
||||
"defaultValue": "false"
|
||||
},
|
||||
{
|
||||
"name": "iconOnly",
|
||||
"description": "是否仅图标:渲染为正方形按钮",
|
||||
"type": "boolean",
|
||||
"defaultValue": "false"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "click",
|
||||
"description": "",
|
||||
"type": "",
|
||||
"defaultValue": ""
|
||||
}
|
||||
],
|
||||
"slots": [
|
||||
{
|
||||
"name": "icon",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "default",
|
||||
"description": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { G3Button } from '@g3soft/ui'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<G3Button>默认按钮</G3Button>
|
||||
<G3Button type="primary">主要按钮</G3Button>
|
||||
</template>
|
||||
@@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { G3Button } from '@g3soft/ui'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="demo-block">
|
||||
<G3Button type="primary" block>撑满宽度</G3Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.demo-block {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { G3Button } from '@g3soft/ui'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<G3Button type="primary">
|
||||
<template #icon>+</template>
|
||||
新增
|
||||
</G3Button>
|
||||
<G3Button icon-only title="搜索">🔍</G3Button>
|
||||
</template>
|
||||
@@ -0,0 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { G3Button } from '@g3soft/ui'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<G3Button type="primary" size="small">小</G3Button>
|
||||
<G3Button type="primary">中</G3Button>
|
||||
<G3Button type="primary" size="large">大</G3Button>
|
||||
</template>
|
||||
@@ -0,0 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { G3Button } from '@g3soft/ui'
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
function handleAsync() {
|
||||
if (loading.value) return
|
||||
loading.value = true
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1500)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<G3Button type="primary" :loading="loading" @click="handleAsync">点击加载</G3Button>
|
||||
<G3Button disabled>禁用</G3Button>
|
||||
<G3Button type="primary" disabled>禁用(主要)</G3Button>
|
||||
</template>
|
||||
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { G3Button } from '@g3soft/ui'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<G3Button type="success">成功按钮</G3Button>
|
||||
<G3Button type="warning">警告按钮</G3Button>
|
||||
<G3Button type="danger">危险按钮</G3Button>
|
||||
<G3Button type="text">文字按钮</G3Button>
|
||||
<G3Button type="link">链接按钮</G3Button>
|
||||
</template>
|
||||
@@ -0,0 +1,37 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
/**
|
||||
* 文档站(Docus)。
|
||||
*
|
||||
* 几个刻意的选择:
|
||||
* - extends: ['docus'] —— Docus 是 Nuxt Layer,全部能力(导航、搜索、主题)由它提供
|
||||
* - alias 直连 packages 源码 —— 组件库尚未构建也能开发,改组件即时热更
|
||||
*
|
||||
* 部署到子路径时用环境变量 NUXT_APP_BASE_URL=/docs/,不在配置里写死。
|
||||
*/
|
||||
export default defineNuxtConfig({
|
||||
extends: ['docus'],
|
||||
|
||||
// sitemap.xml、llms.txt、OG image 都依赖站点 URL,缺了会导致预渲染 500
|
||||
site: {
|
||||
url: process.env.DOCS_SITE_URL || 'https://ui.g3soft.dev',
|
||||
name: 'G3Soft UI',
|
||||
},
|
||||
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'@g3soft/ui': fileURLToPath(new URL('../packages/ui/src/index.ts', import.meta.url)),
|
||||
'@g3soft/tokens': fileURLToPath(
|
||||
new URL('../packages/tokens/src/index.css', import.meta.url),
|
||||
),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
fs: {
|
||||
// 组件库源码在 docs 目录之外,需要放开读取范围
|
||||
allow: [fileURLToPath(new URL('..', import.meta.url))],
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@g3soft/docs",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "nuxt dev",
|
||||
"build": "nuxt build",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@g3soft/tokens": "workspace:*",
|
||||
"@g3soft/ui": "workspace:*",
|
||||
"better-sqlite3": "^12.11.1",
|
||||
"docus": "^5.14.0",
|
||||
"nuxt": "4.4.8",
|
||||
"vue": "catalog:"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user