mirror of
https://github.com/fantastic-admin/basic.git
synced 2026-10-06 14:33:11 +08:00
refactor: 重构 components 子包组件目录和导出结构
This commit is contained in:
@@ -45,14 +45,6 @@ const routes: RouteRecordRaw = {
|
|||||||
title: '按钮',
|
title: '按钮',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'button_group',
|
|
||||||
name: 'componentExampleButtonGroup',
|
|
||||||
component: () => import('@/views/component_example/button_group.vue'),
|
|
||||||
meta: {
|
|
||||||
title: '按钮组',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'card',
|
path: 'card',
|
||||||
name: 'componentExampleCard',
|
name: 'componentExampleCard',
|
||||||
@@ -69,14 +61,6 @@ const routes: RouteRecordRaw = {
|
|||||||
title: '复选框',
|
title: '复选框',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'checkbox_group',
|
|
||||||
name: 'componentExampleCheckboxGroup',
|
|
||||||
component: () => import('@/views/component_example/checkbox_group.vue'),
|
|
||||||
meta: {
|
|
||||||
title: '复选框组',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'collapsible',
|
path: 'collapsible',
|
||||||
name: 'componentExampleCollapsible',
|
name: 'componentExampleCollapsible',
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { button } from '@fantastic-admin/components/examples'
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<FaPageHeader title="按钮" description="FaButton" />
|
<FaPageHeader title="按钮" description="FaButton / FaButtonGroup" />
|
||||||
<FaPageMain
|
<FaPageMain
|
||||||
v-for="example in button"
|
v-for="example in button"
|
||||||
:key="example.title"
|
:key="example.title"
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { buttonGroup } from '@fantastic-admin/components/examples'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<FaPageHeader title="按钮组" description="FaButtonGroup" />
|
|
||||||
<FaPageMain
|
|
||||||
v-for="example in buttonGroup"
|
|
||||||
:key="example.title"
|
|
||||||
:code="example.componentRaw"
|
|
||||||
:title="example.title"
|
|
||||||
>
|
|
||||||
<component :is="example.component" />
|
|
||||||
</FaPageMain>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -4,7 +4,7 @@ import { checkbox } from '@fantastic-admin/components/examples'
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<FaPageHeader title="复选框" description="FaCheckbox" />
|
<FaPageHeader title="复选框" description="FaCheckbox / FaCheckboxGroup" />
|
||||||
<FaPageMain
|
<FaPageMain
|
||||||
v-for="example in checkbox"
|
v-for="example in checkbox"
|
||||||
:key="example.title"
|
:key="example.title"
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { checkboxGroup } from '@fantastic-admin/components/examples'
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<FaPageHeader title="复选框组" description="FaCheckboxGroup" />
|
|
||||||
<FaPageMain
|
|
||||||
v-for="example in checkboxGroup"
|
|
||||||
:key="example.title"
|
|
||||||
:code="example.componentRaw"
|
|
||||||
:title="example.title"
|
|
||||||
>
|
|
||||||
<component :is="example.component" />
|
|
||||||
</FaPageMain>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -1,20 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { kbd, kbdGroup } from '@fantastic-admin/components/examples'
|
import { kbd } from '@fantastic-admin/components/examples'
|
||||||
|
|
||||||
const examples = [
|
|
||||||
...kbd,
|
|
||||||
...kbdGroup.map(example => ({
|
|
||||||
...example,
|
|
||||||
title: '组合',
|
|
||||||
})),
|
|
||||||
]
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<FaPageHeader title="键盘" description="FaKbd" />
|
<FaPageHeader title="键盘" description="FaKbd / FaKbdGroup" />
|
||||||
<FaPageMain
|
<FaPageMain
|
||||||
v-for="example in examples"
|
v-for="example in kbd"
|
||||||
:key="example.title"
|
:key="example.title"
|
||||||
:code="example.componentRaw"
|
:code="example.componentRaw"
|
||||||
:title="example.title"
|
:title="example.title"
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Alert } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Avatar } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Badge } from './index.vue'
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# FaButtonGroup 按钮组
|
|
||||||
|
|
||||||
将多个按钮组合在一起的容器组件,支持水平/垂直排列和分割线。
|
|
||||||
|
|
||||||
## 使用场景
|
|
||||||
|
|
||||||
- 将多个按钮组合在一起的容器组件,支持水平/垂直排列和分割线。
|
|
||||||
- 常见用法:垂直排列、带分割线、工具栏按钮组、垂直菜单。
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 |
|
|
||||||
|------|------|------|--------|
|
|
||||||
| orientation | 排列方向 | `'horizontal' \| 'vertical'` | `'horizontal'` |
|
|
||||||
| separator | 是否显示分割线 | `boolean` | `false` |
|
|
||||||
| class | 自定义类名 | `HTMLAttributes['class']` | `undefined` |
|
|
||||||
|
|
||||||
## Slots
|
|
||||||
|
|
||||||
| 名称 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| default | 默认插槽,放置 FaButton 组件 |
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- 无特殊限制,建议按示例中的受控方式接入,并结合业务容器尺寸验证最终展示效果。
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import Basic from './_basic.vue'
|
|
||||||
import BasicRaw from './_basic.vue?raw'
|
|
||||||
import Nested from './_nested.vue'
|
|
||||||
import NestedRaw from './_nested.vue?raw'
|
|
||||||
import Separator from './_separator.vue'
|
|
||||||
import SeparatorRaw from './_separator.vue?raw'
|
|
||||||
import Vertical from './_vertical.vue'
|
|
||||||
import VerticalRaw from './_vertical.vue?raw'
|
|
||||||
import WithComponents from './_with-components.vue'
|
|
||||||
import WithComponentsRaw from './_with-components.vue?raw'
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
title: '基础',
|
|
||||||
component: Basic,
|
|
||||||
componentRaw: BasicRaw,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '垂直',
|
|
||||||
component: Vertical,
|
|
||||||
componentRaw: VerticalRaw,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '分割线',
|
|
||||||
component: Separator,
|
|
||||||
componentRaw: SeparatorRaw,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '嵌套',
|
|
||||||
component: Nested,
|
|
||||||
componentRaw: NestedRaw,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '与其他组件组合',
|
|
||||||
component: WithComponents,
|
|
||||||
componentRaw: WithComponentsRaw,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
@@ -41,3 +41,30 @@
|
|||||||
- `ghost`:幽灵按钮,用于更次要的操作
|
- `ghost`:幽灵按钮,用于更次要的操作
|
||||||
- `link`:链接样式
|
- `link`:链接样式
|
||||||
- `expand`:展开动画效果按钮
|
- `expand`:展开动画效果按钮
|
||||||
|
|
||||||
|
## FaButtonGroup 按钮组
|
||||||
|
|
||||||
|
将多个按钮组合在一起的容器组件,支持水平/垂直排列和分割线。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
- 将多个按钮组合在一起的容器组件,支持水平/垂直排列和分割线。
|
||||||
|
- 常见用法:垂直排列、带分割线、工具栏按钮组、垂直菜单。
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
|------|------|------|--------|
|
||||||
|
| orientation | 排列方向 | `'horizontal' \| 'vertical'` | `'horizontal'` |
|
||||||
|
| separator | 是否显示分割线 | `boolean` | `false` |
|
||||||
|
| class | 自定义类名 | `HTMLAttributes['class']` | `undefined` |
|
||||||
|
|
||||||
|
## Slots
|
||||||
|
|
||||||
|
| 名称 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| default | 默认插槽,放置 FaButton 组件 |
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- 无特殊限制,建议按示例中的受控方式接入,并结合业务容器尺寸验证最终展示效果。
|
||||||
|
|||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// 组件实际使用时无需手动导入,框架会自动导入
|
|
||||||
import FaButton from '../../button/index.vue'
|
|
||||||
import FaIcon from '../../icon/index.vue'
|
import FaIcon from '../../icon/index.vue'
|
||||||
import FaButtonGroup from '../index.vue'
|
import FaButtonGroup from '../ButtonGroup.vue'
|
||||||
|
// 组件实际使用时无需手动导入,框架会自动导入
|
||||||
|
import FaButton from '../index.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// 组件实际使用时无需手动导入,框架会自动导入
|
|
||||||
import FaButton from '../../button/index.vue'
|
|
||||||
import FaIcon from '../../icon/index.vue'
|
import FaIcon from '../../icon/index.vue'
|
||||||
import FaButtonGroup from '../index.vue'
|
import FaButtonGroup from '../ButtonGroup.vue'
|
||||||
|
// 组件实际使用时无需手动导入,框架会自动导入
|
||||||
|
import FaButton from '../index.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// 组件实际使用时无需手动导入,框架会自动导入
|
|
||||||
import FaButton from '../../button/index.vue'
|
|
||||||
import FaIcon from '../../icon/index.vue'
|
import FaIcon from '../../icon/index.vue'
|
||||||
import FaButtonGroup from '../index.vue'
|
import FaButtonGroup from '../ButtonGroup.vue'
|
||||||
|
// 组件实际使用时无需手动导入,框架会自动导入
|
||||||
|
import FaButton from '../index.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
+3
-3
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// 组件实际使用时无需手动导入,框架会自动导入
|
|
||||||
import FaButton from '../../button/index.vue'
|
|
||||||
import FaIcon from '../../icon/index.vue'
|
import FaIcon from '../../icon/index.vue'
|
||||||
import FaButtonGroup from '../index.vue'
|
import FaButtonGroup from '../ButtonGroup.vue'
|
||||||
|
// 组件实际使用时无需手动导入,框架会自动导入
|
||||||
|
import FaButton from '../index.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
+3
-3
@@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { shallowRef } from 'vue'
|
import { shallowRef } from 'vue'
|
||||||
|
|
||||||
// 组件实际使用时无需手动导入,框架会自动导入
|
|
||||||
import FaButton from '../../button/index.vue'
|
|
||||||
import FaDropdown from '../../dropdown/index.vue'
|
import FaDropdown from '../../dropdown/index.vue'
|
||||||
import FaIcon from '../../icon/index.vue'
|
import FaIcon from '../../icon/index.vue'
|
||||||
import FaInput from '../../input/index.vue'
|
import FaInput from '../../input/index.vue'
|
||||||
import FaSelect from '../../select/index.vue'
|
import FaSelect from '../../select/index.vue'
|
||||||
import FaButtonGroup from '../index.vue'
|
import FaButtonGroup from '../ButtonGroup.vue'
|
||||||
|
// 组件实际使用时无需手动导入,框架会自动导入
|
||||||
|
import FaButton from '../index.vue'
|
||||||
|
|
||||||
const currency = shallowRef('CNY')
|
const currency = shallowRef('CNY')
|
||||||
</script>
|
</script>
|
||||||
@@ -2,6 +2,16 @@ import Basic from './_basic.vue'
|
|||||||
import BasicRaw from './_basic.vue?raw'
|
import BasicRaw from './_basic.vue?raw'
|
||||||
import Disabled from './_disabled.vue'
|
import Disabled from './_disabled.vue'
|
||||||
import DisabledRaw from './_disabled.vue?raw'
|
import DisabledRaw from './_disabled.vue?raw'
|
||||||
|
import GroupBasic from './_group-basic.vue'
|
||||||
|
import GroupBasicRaw from './_group-basic.vue?raw'
|
||||||
|
import GroupNested from './_group-nested.vue'
|
||||||
|
import GroupNestedRaw from './_group-nested.vue?raw'
|
||||||
|
import GroupSeparator from './_group-separator.vue'
|
||||||
|
import GroupSeparatorRaw from './_group-separator.vue?raw'
|
||||||
|
import GroupVertical from './_group-vertical.vue'
|
||||||
|
import GroupVerticalRaw from './_group-vertical.vue?raw'
|
||||||
|
import GroupWithComponents from './_group-with-components.vue'
|
||||||
|
import GroupWithComponentsRaw from './_group-with-components.vue?raw'
|
||||||
import Loading from './_loading.vue'
|
import Loading from './_loading.vue'
|
||||||
import LoadingRaw from './_loading.vue?raw'
|
import LoadingRaw from './_loading.vue?raw'
|
||||||
import Size from './_size.vue'
|
import Size from './_size.vue'
|
||||||
@@ -35,4 +45,29 @@ export default [
|
|||||||
component: Loading,
|
component: Loading,
|
||||||
componentRaw: LoadingRaw,
|
componentRaw: LoadingRaw,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '按钮组 - 基础',
|
||||||
|
component: GroupBasic,
|
||||||
|
componentRaw: GroupBasicRaw,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '按钮组 - 垂直',
|
||||||
|
component: GroupVertical,
|
||||||
|
componentRaw: GroupVerticalRaw,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '按钮组 - 分割线',
|
||||||
|
component: GroupSeparator,
|
||||||
|
componentRaw: GroupSeparatorRaw,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '按钮组 - 嵌套',
|
||||||
|
component: GroupNested,
|
||||||
|
componentRaw: GroupNestedRaw,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '按钮组 - 与其他组件组合',
|
||||||
|
component: GroupWithComponents,
|
||||||
|
componentRaw: GroupWithComponentsRaw,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as ButtonGroup } from './ButtonGroup.vue'
|
||||||
|
export { default as Button } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Card } from './index.vue'
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
# FaCheckboxGroup 复选框组
|
|
||||||
|
|
||||||
基于选项数组的复选框组组件,支持描述文案、自定义布局样式和选项插槽扩展。
|
|
||||||
|
|
||||||
## 使用场景
|
|
||||||
|
|
||||||
- 表单中的多选配置项
|
|
||||||
- 多标签偏好选择
|
|
||||||
- 权限点批量选择
|
|
||||||
- 带说明文案的多选列表
|
|
||||||
- 需要卡片式多选交互的场景
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| 属性 | 类型 | 默认值 | 说明 |
|
|
||||||
|------|------|--------|------|
|
|
||||||
| `options` | `CheckboxGroupOption[]` | **必需** | 复选框组选项数据 |
|
|
||||||
| `disabled` | `boolean` | `false` | 是否整体禁用 |
|
|
||||||
| `min` | `number` | - | 最少选中数量,达到后已选项会被禁用 |
|
|
||||||
| `max` | `number` | - | 最多选中数量,达到后未选项会被禁用 |
|
|
||||||
| `dir` | `'ltr' \| 'rtl'` | - | 文本方向 |
|
|
||||||
| `class` | `HTMLAttributes['class']` | - | 组容器类名 |
|
|
||||||
| `optionClass` | `HTMLAttributes['class']` | - | 每个选项外层容器类名 |
|
|
||||||
| `itemClass` | `HTMLAttributes['class']` | - | 复选框本体类名 |
|
|
||||||
| `labelClass` | `HTMLAttributes['class']` | - | 标签内容类名 |
|
|
||||||
|
|
||||||
### CheckboxGroupOption 接口
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export interface CheckboxGroupOption {
|
|
||||||
label: string
|
|
||||||
value: AcceptableValue
|
|
||||||
description?: string
|
|
||||||
disabled?: boolean
|
|
||||||
id?: string
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Slots
|
|
||||||
|
|
||||||
| 名称 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| `option` | 自定义选项内容,slot props: `{ option, checked, disabled, id }`,`option` 会保留 `options` 中的扩展字段类型 |
|
|
||||||
|
|
||||||
## Events
|
|
||||||
|
|
||||||
| 事件名 | 说明 | 回调参数 |
|
|
||||||
|--------|------|----------|
|
|
||||||
| `change` | 选中值变化时触发 | `value: AcceptableValue[]` |
|
|
||||||
|
|
||||||
通过 `v-model` 实现双向绑定。
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **数据驱动**:组选项必须通过 `options` 传入,不再使用多个 `FaCheckbox` 手工拼接。
|
|
||||||
2. **v-model 类型**:组件通过 `v-model` 管理选中值数组。
|
|
||||||
3. **禁用优先级**:组件整体 `disabled` 为 `true` 时,会覆盖单项可用状态。
|
|
||||||
4. **数量限制**:当选中数量小于等于 `min` 时,已选项会被禁用;当选中数量大于等于 `max` 时,未选项会被禁用。
|
|
||||||
5. **布局控制**:通过 `class` 和 `optionClass` 自定义多列或卡片式排列,不再提供 `orientation`。
|
|
||||||
6. **描述文案布局**:存在 `description` 时,选项会自动切换为顶部对齐布局。
|
|
||||||
7. **自定义插槽**:使用 `option` 插槽后,默认复选框本体会隐藏,需要由插槽内容自行承担完整视觉表现。
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import Basic from './_basic.vue'
|
|
||||||
import BasicRaw from './_basic.vue?raw'
|
|
||||||
import CustomOption from './_custom-option.vue'
|
|
||||||
import CustomOptionRaw from './_custom-option.vue?raw'
|
|
||||||
import Description from './_description.vue'
|
|
||||||
import DescriptionRaw from './_description.vue?raw'
|
|
||||||
import Disabled from './_disabled.vue'
|
|
||||||
import DisabledRaw from './_disabled.vue?raw'
|
|
||||||
import Limit from './_limit.vue'
|
|
||||||
import LimitRaw from './_limit.vue?raw'
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
title: '基础',
|
|
||||||
component: Basic,
|
|
||||||
componentRaw: BasicRaw,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '描述',
|
|
||||||
component: Description,
|
|
||||||
componentRaw: DescriptionRaw,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '禁用',
|
|
||||||
component: Disabled,
|
|
||||||
componentRaw: DisabledRaw,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '数量限制',
|
|
||||||
component: Limit,
|
|
||||||
componentRaw: LimitRaw,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '自定义选项内容',
|
|
||||||
component: CustomOption,
|
|
||||||
componentRaw: CustomOptionRaw,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
+1
-1
@@ -4,8 +4,8 @@ import type { HTMLAttributes } from 'vue'
|
|||||||
import { useTextDirection } from '@vueuse/core'
|
import { useTextDirection } from '@vueuse/core'
|
||||||
import { computed, useId, watch } from 'vue'
|
import { computed, useId, watch } from 'vue'
|
||||||
import { cn } from '#utils'
|
import { cn } from '#utils'
|
||||||
import { Checkbox } from '../checkbox/checkbox'
|
|
||||||
import { Label } from '../label/label'
|
import { Label } from '../label/label'
|
||||||
|
import { Checkbox } from './checkbox'
|
||||||
|
|
||||||
export interface CheckboxGroupOption {
|
export interface CheckboxGroupOption {
|
||||||
label: string
|
label: string
|
||||||
@@ -40,3 +40,65 @@
|
|||||||
3. **标签点击**:点击标签文字也能触发复选框状态切换。
|
3. **标签点击**:点击标签文字也能触发复选框状态切换。
|
||||||
4. **空标签隐藏**:当不提供默认插槽内容时,标签元素会自动隐藏。
|
4. **空标签隐藏**:当不提供默认插槽内容时,标签元素会自动隐藏。
|
||||||
5. **禁用状态**:禁用状态下复选框和标签都无法点击。
|
5. **禁用状态**:禁用状态下复选框和标签都无法点击。
|
||||||
|
|
||||||
|
## FaCheckboxGroup 复选框组
|
||||||
|
|
||||||
|
基于选项数组的复选框组组件,支持描述文案、自定义布局样式和选项插槽扩展。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
- 表单中的多选配置项
|
||||||
|
- 多标签偏好选择
|
||||||
|
- 权限点批量选择
|
||||||
|
- 带说明文案的多选列表
|
||||||
|
- 需要卡片式多选交互的场景
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
| 属性 | 类型 | 默认值 | 说明 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| `options` | `CheckboxGroupOption[]` | **必需** | 复选框组选项数据 |
|
||||||
|
| `disabled` | `boolean` | `false` | 是否整体禁用 |
|
||||||
|
| `min` | `number` | - | 最少选中数量,达到后已选项会被禁用 |
|
||||||
|
| `max` | `number` | - | 最多选中数量,达到后未选项会被禁用 |
|
||||||
|
| `dir` | `'ltr' \| 'rtl'` | - | 文本方向 |
|
||||||
|
| `class` | `HTMLAttributes['class']` | - | 组容器类名 |
|
||||||
|
| `optionClass` | `HTMLAttributes['class']` | - | 每个选项外层容器类名 |
|
||||||
|
| `itemClass` | `HTMLAttributes['class']` | - | 复选框本体类名 |
|
||||||
|
| `labelClass` | `HTMLAttributes['class']` | - | 标签内容类名 |
|
||||||
|
|
||||||
|
### CheckboxGroupOption 接口
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export interface CheckboxGroupOption {
|
||||||
|
label: string
|
||||||
|
value: AcceptableValue
|
||||||
|
description?: string
|
||||||
|
disabled?: boolean
|
||||||
|
id?: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Slots
|
||||||
|
|
||||||
|
| 名称 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `option` | 自定义选项内容,slot props: `{ option, checked, disabled, id }`,`option` 会保留 `options` 中的扩展字段类型 |
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
| 事件名 | 说明 | 回调参数 |
|
||||||
|
|--------|------|----------|
|
||||||
|
| `change` | 选中值变化时触发 | `value: AcceptableValue[]` |
|
||||||
|
|
||||||
|
通过 `v-model` 实现双向绑定。
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **数据驱动**:组选项必须通过 `options` 传入,不再使用多个 `FaCheckbox` 手工拼接。
|
||||||
|
2. **v-model 类型**:组件通过 `v-model` 管理选中值数组。
|
||||||
|
3. **禁用优先级**:组件整体 `disabled` 为 `true` 时,会覆盖单项可用状态。
|
||||||
|
4. **数量限制**:当选中数量小于等于 `min` 时,已选项会被禁用;当选中数量大于等于 `max` 时,未选项会被禁用。
|
||||||
|
5. **布局控制**:通过 `class` 和 `optionClass` 自定义多列或卡片式排列,不再提供 `orientation`。
|
||||||
|
6. **描述文案布局**:存在 `description` 时,选项会自动切换为顶部对齐布局。
|
||||||
|
7. **自定义插槽**:使用 `option` 插槽后,默认复选框本体会隐藏,需要由插槽内容自行承担完整视觉表现。
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
import { computed, shallowRef } from 'vue'
|
import { computed, shallowRef } from 'vue'
|
||||||
|
|
||||||
// 组件实际使用时无需手动导入,框架会自动导入
|
// 组件实际使用时无需手动导入,框架会自动导入
|
||||||
import FaCheckboxGroup from '../index.vue'
|
import FaCheckboxGroup from '../CheckboxGroup.vue'
|
||||||
|
|
||||||
const value = shallowRef(['music'])
|
const value = shallowRef(['music'])
|
||||||
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
import { shallowRef } from 'vue'
|
import { shallowRef } from 'vue'
|
||||||
|
|
||||||
// 组件实际使用时无需手动导入,框架会自动导入
|
// 组件实际使用时无需手动导入,框架会自动导入
|
||||||
import FaCheckboxGroup from '../index.vue'
|
import FaCheckboxGroup from '../CheckboxGroup.vue'
|
||||||
|
|
||||||
const value = shallowRef(['focus', 'balanced'])
|
const value = shallowRef(['focus', 'balanced'])
|
||||||
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
import { computed, shallowRef } from 'vue'
|
import { computed, shallowRef } from 'vue'
|
||||||
|
|
||||||
// 组件实际使用时无需手动导入,框架会自动导入
|
// 组件实际使用时无需手动导入,框架会自动导入
|
||||||
import FaCheckboxGroup from '../index.vue'
|
import FaCheckboxGroup from '../CheckboxGroup.vue'
|
||||||
|
|
||||||
const value = shallowRef(['dashboard', 'report'])
|
const value = shallowRef(['dashboard', 'report'])
|
||||||
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
import { computed, shallowRef } from 'vue'
|
import { computed, shallowRef } from 'vue'
|
||||||
|
|
||||||
// 组件实际使用时无需手动导入,框架会自动导入
|
// 组件实际使用时无需手动导入,框架会自动导入
|
||||||
import FaCheckboxGroup from '../index.vue'
|
import FaCheckboxGroup from '../CheckboxGroup.vue'
|
||||||
|
|
||||||
const value = shallowRef(['basic'])
|
const value = shallowRef(['basic'])
|
||||||
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
import { computed, shallowRef } from 'vue'
|
import { computed, shallowRef } from 'vue'
|
||||||
|
|
||||||
// 组件实际使用时无需手动导入,框架会自动导入
|
// 组件实际使用时无需手动导入,框架会自动导入
|
||||||
import FaCheckboxGroup from '../index.vue'
|
import FaCheckboxGroup from '../CheckboxGroup.vue'
|
||||||
|
|
||||||
const value = shallowRef(['dashboard'])
|
const value = shallowRef(['dashboard'])
|
||||||
|
|
||||||
@@ -4,6 +4,16 @@ import CustomStyle from './_custom-style.vue'
|
|||||||
import CustomStyleRaw from './_custom-style.vue?raw'
|
import CustomStyleRaw from './_custom-style.vue?raw'
|
||||||
import Disabled from './_disabled.vue'
|
import Disabled from './_disabled.vue'
|
||||||
import DisabledRaw from './_disabled.vue?raw'
|
import DisabledRaw from './_disabled.vue?raw'
|
||||||
|
import GroupBasic from './_group-basic.vue'
|
||||||
|
import GroupBasicRaw from './_group-basic.vue?raw'
|
||||||
|
import GroupCustomOption from './_group-custom-option.vue'
|
||||||
|
import GroupCustomOptionRaw from './_group-custom-option.vue?raw'
|
||||||
|
import GroupDescription from './_group-description.vue'
|
||||||
|
import GroupDescriptionRaw from './_group-description.vue?raw'
|
||||||
|
import GroupDisabled from './_group-disabled.vue'
|
||||||
|
import GroupDisabledRaw from './_group-disabled.vue?raw'
|
||||||
|
import GroupLimit from './_group-limit.vue'
|
||||||
|
import GroupLimitRaw from './_group-limit.vue?raw'
|
||||||
import Indeterminate from './_indeterminate.vue'
|
import Indeterminate from './_indeterminate.vue'
|
||||||
import IndeterminateRaw from './_indeterminate.vue?raw'
|
import IndeterminateRaw from './_indeterminate.vue?raw'
|
||||||
|
|
||||||
@@ -13,19 +23,44 @@ export default [
|
|||||||
component: Basic,
|
component: Basic,
|
||||||
componentRaw: BasicRaw,
|
componentRaw: BasicRaw,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '半选状态',
|
|
||||||
component: Indeterminate,
|
|
||||||
componentRaw: IndeterminateRaw,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '禁用',
|
title: '禁用',
|
||||||
component: Disabled,
|
component: Disabled,
|
||||||
componentRaw: DisabledRaw,
|
componentRaw: DisabledRaw,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '半选',
|
||||||
|
component: Indeterminate,
|
||||||
|
componentRaw: IndeterminateRaw,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '自定义样式',
|
title: '自定义样式',
|
||||||
component: CustomStyle,
|
component: CustomStyle,
|
||||||
componentRaw: CustomStyleRaw,
|
componentRaw: CustomStyleRaw,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '复选框组 - 基础',
|
||||||
|
component: GroupBasic,
|
||||||
|
componentRaw: GroupBasicRaw,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '复选框组 - 描述',
|
||||||
|
component: GroupDescription,
|
||||||
|
componentRaw: GroupDescriptionRaw,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '复选框组 - 禁用',
|
||||||
|
component: GroupDisabled,
|
||||||
|
componentRaw: GroupDisabledRaw,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '复选框组 - 数量限制',
|
||||||
|
component: GroupLimit,
|
||||||
|
componentRaw: GroupLimitRaw,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '复选框组 - 自定义选项',
|
||||||
|
component: GroupCustomOption,
|
||||||
|
componentRaw: GroupCustomOptionRaw,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as CheckboxGroup } from './CheckboxGroup.vue'
|
||||||
|
export { default as Checkbox } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Collapsible } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as ContextMenu } from './index.vue'
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as Descriptions } from './index.vue'
|
||||||
|
export type { DescriptionDirection, DescriptionItem, DescriptionItemBase, DescriptionSize, DescriptionSlotProps, DescriptionsProps, DescriptionValue } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Divider } from './index.vue'
|
||||||
@@ -1,135 +1,3 @@
|
|||||||
import type { Component, HTMLAttributes, VNode } from 'vue'
|
export { default as Drawer } from './index.vue'
|
||||||
import { createVNode, getCurrentInstance, h, isVNode, onUnmounted, ref, render, shallowRef, watchEffect } from 'vue'
|
export { useDrawer } from './useDrawer'
|
||||||
import Drawer from './index.vue'
|
export type { DrawerEmits, DrawerProps } from './useDrawer'
|
||||||
|
|
||||||
export interface DrawerProps {
|
|
||||||
id?: string
|
|
||||||
modelValue?: boolean
|
|
||||||
zIndex?: number
|
|
||||||
side?: 'top' | 'bottom' | 'left' | 'right'
|
|
||||||
title?: string | (() => string)
|
|
||||||
description?: string | (() => string)
|
|
||||||
loading?: boolean
|
|
||||||
closable?: boolean
|
|
||||||
centered?: boolean
|
|
||||||
bordered?: boolean
|
|
||||||
overlay?: boolean
|
|
||||||
overlayBlur?: boolean
|
|
||||||
showConfirmButton?: boolean
|
|
||||||
showCancelButton?: boolean
|
|
||||||
confirmButtonText?: string | (() => string)
|
|
||||||
cancelButtonText?: string | (() => string)
|
|
||||||
confirmButtonDisabled?: boolean
|
|
||||||
confirmButtonLoading?: boolean
|
|
||||||
beforeClose?: (
|
|
||||||
action: 'confirm' | 'cancel' | 'close',
|
|
||||||
done: () => void,
|
|
||||||
) => void
|
|
||||||
header?: boolean
|
|
||||||
footer?: boolean
|
|
||||||
closeOnClickOverlay?: boolean
|
|
||||||
closeOnPressEscape?: boolean
|
|
||||||
destroyOnClose?: boolean
|
|
||||||
openAutoFocus?: boolean
|
|
||||||
contentClass?: HTMLAttributes['class']
|
|
||||||
headerClass?: HTMLAttributes['class']
|
|
||||||
footerClass?: HTMLAttributes['class']
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DrawerEmits {
|
|
||||||
'update:modelValue': [value: boolean]
|
|
||||||
'open': []
|
|
||||||
'opened': []
|
|
||||||
'close': []
|
|
||||||
'closed': []
|
|
||||||
'confirm': []
|
|
||||||
'cancel': []
|
|
||||||
}
|
|
||||||
|
|
||||||
type BaseOptions = Omit<DrawerProps, 'modelValue'> & {
|
|
||||||
content?: Component | VNode | string
|
|
||||||
onOpen?: () => void
|
|
||||||
onOpened?: () => void
|
|
||||||
onClose?: () => void
|
|
||||||
onClosed?: () => void
|
|
||||||
onConfirm?: () => void
|
|
||||||
onCancel?: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useDrawer() {
|
|
||||||
function create(initialOptions: BaseOptions) {
|
|
||||||
const container = document.createElement('div')
|
|
||||||
const visible = ref(false)
|
|
||||||
const options = shallowRef<BaseOptions>({ ...initialOptions })
|
|
||||||
const instance = getCurrentInstance()
|
|
||||||
let vnode: VNode | null = null
|
|
||||||
|
|
||||||
const updateVNode = () => {
|
|
||||||
vnode = createVNode(Drawer, Object.assign({
|
|
||||||
'id': instance && instance.uid ? `FaDrawer-${instance.uid}` : undefined,
|
|
||||||
'modelValue': visible.value,
|
|
||||||
'onUpdate:modelValue': (val: boolean) => {
|
|
||||||
visible.value = val
|
|
||||||
},
|
|
||||||
...options.value,
|
|
||||||
}), {
|
|
||||||
default: () => {
|
|
||||||
if (typeof options.value.content === 'string') {
|
|
||||||
return options.value.content
|
|
||||||
}
|
|
||||||
else if (isVNode(options.value.content)) {
|
|
||||||
return options.value.content
|
|
||||||
}
|
|
||||||
else if (options.value.content) {
|
|
||||||
return h(options.value.content)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
},
|
|
||||||
})
|
|
||||||
// 继承主应用的上下文
|
|
||||||
if (instance && instance.appContext) {
|
|
||||||
vnode.appContext = instance.appContext
|
|
||||||
}
|
|
||||||
render(vnode, container)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听 visible 和 options 变化,自动重新渲染
|
|
||||||
watchEffect(() => {
|
|
||||||
updateVNode()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 挂载到当前实例
|
|
||||||
instance?.proxy?.$el?.appendChild(container)
|
|
||||||
|
|
||||||
// 监听组件卸载,自动清理
|
|
||||||
if (instance) {
|
|
||||||
onUnmounted(() => {
|
|
||||||
if (vnode) {
|
|
||||||
render(null, container)
|
|
||||||
vnode = null
|
|
||||||
}
|
|
||||||
if (container.parentNode) {
|
|
||||||
container.parentNode.removeChild(container)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const open = () => {
|
|
||||||
visible.value = true
|
|
||||||
}
|
|
||||||
const close = () => {
|
|
||||||
visible.value = false
|
|
||||||
}
|
|
||||||
const update = (newOptions: BaseOptions) => {
|
|
||||||
options.value = { ...options.value, ...newOptions }
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
open,
|
|
||||||
close,
|
|
||||||
update,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
create,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
import type { Component, HTMLAttributes, VNode } from 'vue'
|
||||||
|
import { createVNode, getCurrentInstance, h, isVNode, onUnmounted, ref, render, shallowRef, watchEffect } from 'vue'
|
||||||
|
import Drawer from './index.vue'
|
||||||
|
|
||||||
|
export interface DrawerProps {
|
||||||
|
id?: string
|
||||||
|
modelValue?: boolean
|
||||||
|
zIndex?: number
|
||||||
|
side?: 'top' | 'bottom' | 'left' | 'right'
|
||||||
|
title?: string | (() => string)
|
||||||
|
description?: string | (() => string)
|
||||||
|
loading?: boolean
|
||||||
|
closable?: boolean
|
||||||
|
centered?: boolean
|
||||||
|
bordered?: boolean
|
||||||
|
overlay?: boolean
|
||||||
|
overlayBlur?: boolean
|
||||||
|
showConfirmButton?: boolean
|
||||||
|
showCancelButton?: boolean
|
||||||
|
confirmButtonText?: string | (() => string)
|
||||||
|
cancelButtonText?: string | (() => string)
|
||||||
|
confirmButtonDisabled?: boolean
|
||||||
|
confirmButtonLoading?: boolean
|
||||||
|
beforeClose?: (
|
||||||
|
action: 'confirm' | 'cancel' | 'close',
|
||||||
|
done: () => void,
|
||||||
|
) => void
|
||||||
|
header?: boolean
|
||||||
|
footer?: boolean
|
||||||
|
closeOnClickOverlay?: boolean
|
||||||
|
closeOnPressEscape?: boolean
|
||||||
|
destroyOnClose?: boolean
|
||||||
|
openAutoFocus?: boolean
|
||||||
|
contentClass?: HTMLAttributes['class']
|
||||||
|
headerClass?: HTMLAttributes['class']
|
||||||
|
footerClass?: HTMLAttributes['class']
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DrawerEmits {
|
||||||
|
'update:modelValue': [value: boolean]
|
||||||
|
'open': []
|
||||||
|
'opened': []
|
||||||
|
'close': []
|
||||||
|
'closed': []
|
||||||
|
'confirm': []
|
||||||
|
'cancel': []
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseOptions = Omit<DrawerProps, 'modelValue'> & {
|
||||||
|
content?: Component | VNode | string
|
||||||
|
onOpen?: () => void
|
||||||
|
onOpened?: () => void
|
||||||
|
onClose?: () => void
|
||||||
|
onClosed?: () => void
|
||||||
|
onConfirm?: () => void
|
||||||
|
onCancel?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDrawer() {
|
||||||
|
function create(initialOptions: BaseOptions) {
|
||||||
|
const container = document.createElement('div')
|
||||||
|
const visible = ref(false)
|
||||||
|
const options = shallowRef<BaseOptions>({ ...initialOptions })
|
||||||
|
const instance = getCurrentInstance()
|
||||||
|
let vnode: VNode | null = null
|
||||||
|
|
||||||
|
const updateVNode = () => {
|
||||||
|
vnode = createVNode(Drawer, Object.assign({
|
||||||
|
'id': instance && instance.uid ? `FaDrawer-${instance.uid}` : undefined,
|
||||||
|
'modelValue': visible.value,
|
||||||
|
'onUpdate:modelValue': (val: boolean) => {
|
||||||
|
visible.value = val
|
||||||
|
},
|
||||||
|
...options.value,
|
||||||
|
}), {
|
||||||
|
default: () => {
|
||||||
|
if (typeof options.value.content === 'string') {
|
||||||
|
return options.value.content
|
||||||
|
}
|
||||||
|
else if (isVNode(options.value.content)) {
|
||||||
|
return options.value.content
|
||||||
|
}
|
||||||
|
else if (options.value.content) {
|
||||||
|
return h(options.value.content)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// 继承主应用的上下文
|
||||||
|
if (instance && instance.appContext) {
|
||||||
|
vnode.appContext = instance.appContext
|
||||||
|
}
|
||||||
|
render(vnode, container)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听 visible 和 options 变化,自动重新渲染
|
||||||
|
watchEffect(() => {
|
||||||
|
updateVNode()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 挂载到当前实例
|
||||||
|
instance?.proxy?.$el?.appendChild(container)
|
||||||
|
|
||||||
|
// 监听组件卸载,自动清理
|
||||||
|
if (instance) {
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (vnode) {
|
||||||
|
render(null, container)
|
||||||
|
vnode = null
|
||||||
|
}
|
||||||
|
if (container.parentNode) {
|
||||||
|
container.parentNode.removeChild(container)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
const close = () => {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
const update = (newOptions: BaseOptions) => {
|
||||||
|
options.value = { ...options.value, ...newOptions }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
update,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
create,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Dropdown } from './index.vue'
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as FileUpload } from './index.vue'
|
||||||
|
export type { FileItem, FileUploadRequestOptions } from './index.vue'
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { filesize } from 'filesize'
|
import { filesize } from 'filesize'
|
||||||
import { computed, onMounted, onUnmounted, ref, useTemplateRef } from 'vue'
|
import { computed, onMounted, onUnmounted, ref, useTemplateRef } from 'vue'
|
||||||
import ButtonGroup from '../button-group/index.vue'
|
import ButtonGroup from '../button/ButtonGroup.vue'
|
||||||
import Button from '../button/index.vue'
|
import Button from '../button/index.vue'
|
||||||
import Icon from '../icon/index.vue'
|
import Icon from '../icon/index.vue'
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as FixedBar } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as HoverCard } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Icon } from './index.vue'
|
||||||
@@ -1,22 +1,2 @@
|
|||||||
import { createApp, h } from 'vue'
|
export { default as ImagePreview } from './index.vue'
|
||||||
import Preview from './preview.vue'
|
export { useImagePreview } from './useImagePreview'
|
||||||
|
|
||||||
export function useImagePreview() {
|
|
||||||
function open(src: string | string[], index = 0) {
|
|
||||||
const container = document.createElement('div')
|
|
||||||
const app = createApp({
|
|
||||||
render() {
|
|
||||||
return h(Preview, {
|
|
||||||
modelValue: true,
|
|
||||||
src,
|
|
||||||
index,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
app.mount(container)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
open,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { VisuallyHidden } from 'reka-ui'
|
import { VisuallyHidden } from 'reka-ui'
|
||||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
import ButtonGroup from '../button-group/index.vue'
|
import ButtonGroup from '../button/ButtonGroup.vue'
|
||||||
import Button from '../button/index.vue'
|
import Button from '../button/index.vue'
|
||||||
import Icon from '../icon/index.vue'
|
import Icon from '../icon/index.vue'
|
||||||
import { Dialog, DialogContent, DialogDescription, DialogTitle } from './dialog'
|
import { Dialog, DialogContent, DialogDescription, DialogTitle } from './dialog'
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { createApp, h } from 'vue'
|
||||||
|
import Preview from './preview.vue'
|
||||||
|
|
||||||
|
export function useImagePreview() {
|
||||||
|
function open(src: string | string[], index = 0) {
|
||||||
|
const container = document.createElement('div')
|
||||||
|
const app = createApp({
|
||||||
|
render() {
|
||||||
|
return h(Preview, {
|
||||||
|
modelValue: true,
|
||||||
|
src,
|
||||||
|
index,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
app.mount(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
open,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as ImageUpload } from './index.vue'
|
||||||
|
export type { UploadRequestOptions } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as InputOTP } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Input } from './index.vue'
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# FaKbdGroup 键盘组合
|
|
||||||
|
|
||||||
用于包裹多个 `FaKbd` 组件,展示组合按键的容器组件。
|
|
||||||
|
|
||||||
## 使用场景
|
|
||||||
|
|
||||||
- 快捷键组合展示
|
|
||||||
- 键盘操作说明
|
|
||||||
- 命令面板提示
|
|
||||||
- 帮助文档中的多键组合说明
|
|
||||||
|
|
||||||
## Props
|
|
||||||
|
|
||||||
| 属性 | 类型 | 默认值 | 说明 |
|
|
||||||
|------|------|--------|------|
|
|
||||||
| `class` | `HTMLAttributes['class']` | - | 自定义 CSS 类 |
|
|
||||||
|
|
||||||
## Slots
|
|
||||||
|
|
||||||
| 名称 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| `default` | 包含的 `FaKbd` 组件 |
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **仅用于组合**:此组件仅用于包裹多个 `FaKbd`,不建议单独使用
|
|
||||||
2. **间距自动**:内部的 `FaKbd` 会自动添加适当间距
|
|
||||||
3. **灵活内容**:除了 `FaKbd` 外,也可以放入其他内容(如分隔符)
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import Basic from './_basic.vue'
|
|
||||||
import BasicRaw from './_basic.vue?raw'
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
title: '基础',
|
|
||||||
component: Basic,
|
|
||||||
componentRaw: BasicRaw,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
@@ -34,3 +34,32 @@
|
|||||||
1. **配合使用**:组合按键时请使用 `FaKbdGroup` 包裹多个 `FaKbd`
|
1. **配合使用**:组合按键时请使用 `FaKbdGroup` 包裹多个 `FaKbd`
|
||||||
2. **内容长度**:按键内容不宜过长,适合单个字符或简短单词
|
2. **内容长度**:按键内容不宜过长,适合单个字符或简短单词
|
||||||
3. **图标支持**:可以使用 `FaIcon` 组件显示图标,图标会自动调整为合适尺寸
|
3. **图标支持**:可以使用 `FaIcon` 组件显示图标,图标会自动调整为合适尺寸
|
||||||
|
|
||||||
|
## FaKbdGroup 键盘组合
|
||||||
|
|
||||||
|
用于包裹多个 `FaKbd` 组件,展示组合按键的容器组件。
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
- 快捷键组合展示
|
||||||
|
- 键盘操作说明
|
||||||
|
- 命令面板提示
|
||||||
|
- 帮助文档中的多键组合说明
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
| 属性 | 类型 | 默认值 | 说明 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| `class` | `HTMLAttributes['class']` | - | 自定义 CSS 类 |
|
||||||
|
|
||||||
|
## Slots
|
||||||
|
|
||||||
|
| 名称 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `default` | 包含的 `FaKbd` 组件 |
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **仅用于组合**:此组件仅用于包裹多个 `FaKbd`,不建议单独使用
|
||||||
|
2. **间距自动**:内部的 `FaKbd` 会自动添加适当间距
|
||||||
|
3. **灵活内容**:除了 `FaKbd` 外,也可以放入其他内容(如分隔符)
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// 组件实际使用时无需手动导入,框架会自动导入
|
// 组件实际使用时无需手动导入,框架会自动导入
|
||||||
import FaKbd from '../../kbd/index.vue'
|
import FaKbd from '../index.vue'
|
||||||
import FaKbdGroup from '../index.vue'
|
import FaKbdGroup from '../KbdGroup.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import Basic from './_basic.vue'
|
import Basic from './_basic.vue'
|
||||||
import BasicRaw from './_basic.vue?raw'
|
import BasicRaw from './_basic.vue?raw'
|
||||||
|
import GroupBasic from './_group-basic.vue'
|
||||||
|
import GroupBasicRaw from './_group-basic.vue?raw'
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
@@ -7,4 +9,9 @@ export default [
|
|||||||
component: Basic,
|
component: Basic,
|
||||||
componentRaw: BasicRaw,
|
componentRaw: BasicRaw,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '组合',
|
||||||
|
component: GroupBasic,
|
||||||
|
componentRaw: GroupBasicRaw,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as Kbd } from './index.vue'
|
||||||
|
export { default as KbdGroup } from './KbdGroup.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Label } from './index.vue'
|
||||||
@@ -1,231 +1,3 @@
|
|||||||
import type { Component, HTMLAttributes, VNode } from 'vue'
|
export { default as Modal } from './index.vue'
|
||||||
import { createVNode, getCurrentInstance, h, isVNode, onUnmounted, ref, render, shallowRef, watchEffect } from 'vue'
|
export { useModal } from './useModal'
|
||||||
import Modal from './index.vue'
|
export type { ModalEmits, ModalProps } from './useModal'
|
||||||
|
|
||||||
export interface ModalProps {
|
|
||||||
id?: string
|
|
||||||
modelValue?: boolean
|
|
||||||
zIndex?: number
|
|
||||||
title?: string | (() => string)
|
|
||||||
description?: string | (() => string)
|
|
||||||
icon?: 'info' | 'success' | 'warning' | 'error'
|
|
||||||
loading?: boolean
|
|
||||||
closable?: boolean
|
|
||||||
maximize?: boolean
|
|
||||||
maximizable?: boolean
|
|
||||||
draggable?: boolean
|
|
||||||
center?: boolean
|
|
||||||
border?: boolean
|
|
||||||
alignCenter?: boolean
|
|
||||||
overlay?: boolean
|
|
||||||
overlayBlur?: boolean
|
|
||||||
showConfirmButton?: boolean
|
|
||||||
showCancelButton?: boolean
|
|
||||||
confirmButtonText?: string | (() => string)
|
|
||||||
cancelButtonText?: string | (() => string)
|
|
||||||
confirmButtonDisabled?: boolean
|
|
||||||
confirmButtonLoading?: boolean
|
|
||||||
beforeClose?: (
|
|
||||||
action: 'confirm' | 'cancel' | 'close',
|
|
||||||
done: () => void,
|
|
||||||
) => void
|
|
||||||
header?: boolean
|
|
||||||
footer?: boolean
|
|
||||||
closeOnClickOverlay?: boolean
|
|
||||||
closeOnPressEscape?: boolean
|
|
||||||
destroyOnClose?: boolean
|
|
||||||
openAutoFocus?: boolean
|
|
||||||
class?: HTMLAttributes['class']
|
|
||||||
headerClass?: HTMLAttributes['class']
|
|
||||||
contentClass?: HTMLAttributes['class']
|
|
||||||
footerClass?: HTMLAttributes['class']
|
|
||||||
}
|
|
||||||
export interface ModalEmits {
|
|
||||||
'update:modelValue': [value: boolean]
|
|
||||||
'open': []
|
|
||||||
'opened': []
|
|
||||||
'close': []
|
|
||||||
'closed': []
|
|
||||||
'confirm': []
|
|
||||||
'cancel': []
|
|
||||||
}
|
|
||||||
|
|
||||||
type BaseOptions = Omit<ModalProps, 'modelValue'> & {
|
|
||||||
content?: Component | VNode | string
|
|
||||||
onOpen?: () => void
|
|
||||||
onOpened?: () => void
|
|
||||||
onClose?: () => void
|
|
||||||
onClosed?: () => void
|
|
||||||
onConfirm?: () => void
|
|
||||||
onCancel?: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
type alertOptions = Pick<BaseOptions, 'title' | 'description' | 'icon' | 'alignCenter' | 'overlay' | 'overlayBlur' | 'confirmButtonText' | 'confirmButtonDisabled' | 'confirmButtonLoading' | 'closeOnClickOverlay' | 'closeOnPressEscape' | 'class' | 'headerClass' | 'contentClass' | 'footerClass' | 'content' | 'onConfirm'>
|
|
||||||
|
|
||||||
type confirmOptions = Pick<BaseOptions, 'title' | 'description' | 'alignCenter' | 'overlay' | 'overlayBlur' | 'confirmButtonText' | 'cancelButtonText' | 'confirmButtonDisabled' | 'confirmButtonLoading' | 'beforeClose' | 'closeOnClickOverlay' | 'closeOnPressEscape' | 'class' | 'headerClass' | 'contentClass' | 'footerClass' | 'content' | 'onConfirm' | 'onCancel'>
|
|
||||||
|
|
||||||
export function useModal() {
|
|
||||||
function create(initialOptions: BaseOptions) {
|
|
||||||
const container = document.createElement('div')
|
|
||||||
const visible = ref(false)
|
|
||||||
const options = shallowRef<BaseOptions>({ ...initialOptions })
|
|
||||||
const instance = getCurrentInstance()
|
|
||||||
let vnode: VNode | null = null
|
|
||||||
|
|
||||||
const updateVNode = () => {
|
|
||||||
vnode = createVNode(Modal, Object.assign({
|
|
||||||
'id': instance && instance.uid ? `FaModal-${instance.uid}` : undefined,
|
|
||||||
'modelValue': visible.value,
|
|
||||||
'onUpdate:modelValue': (val: boolean) => {
|
|
||||||
visible.value = val
|
|
||||||
},
|
|
||||||
...options.value,
|
|
||||||
}), {
|
|
||||||
default: () => {
|
|
||||||
if (typeof options.value.content === 'string') {
|
|
||||||
return options.value.content
|
|
||||||
}
|
|
||||||
else if (isVNode(options.value.content)) {
|
|
||||||
return options.value.content
|
|
||||||
}
|
|
||||||
else if (options.value.content) {
|
|
||||||
return h(options.value.content)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
},
|
|
||||||
})
|
|
||||||
// 继承主应用的上下文
|
|
||||||
if (instance && instance.appContext) {
|
|
||||||
vnode.appContext = instance.appContext
|
|
||||||
}
|
|
||||||
render(vnode, container)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听 visible 和 options 变化,自动重新渲染
|
|
||||||
watchEffect(() => {
|
|
||||||
updateVNode()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 挂载到当前实例
|
|
||||||
instance?.proxy?.$el?.appendChild(container)
|
|
||||||
|
|
||||||
// 监听组件卸载,自动清理
|
|
||||||
if (instance) {
|
|
||||||
onUnmounted(() => {
|
|
||||||
if (vnode) {
|
|
||||||
render(null, container)
|
|
||||||
vnode = null
|
|
||||||
}
|
|
||||||
if (container.parentNode) {
|
|
||||||
container.parentNode.removeChild(container)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const open = () => {
|
|
||||||
visible.value = true
|
|
||||||
}
|
|
||||||
const close = () => {
|
|
||||||
visible.value = false
|
|
||||||
}
|
|
||||||
const update = (newOptions: BaseOptions) => {
|
|
||||||
options.value = { ...options.value, ...newOptions }
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
open,
|
|
||||||
close,
|
|
||||||
update,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function info(options: alertOptions) {
|
|
||||||
const defaultOptions: BaseOptions = {
|
|
||||||
icon: 'info',
|
|
||||||
closable: false,
|
|
||||||
border: false,
|
|
||||||
alignCenter: true,
|
|
||||||
closeOnClickOverlay: false,
|
|
||||||
destroyOnClose: true,
|
|
||||||
openAutoFocus: true,
|
|
||||||
class: 'min-h-auto max-w-[calc(100%-2rem)]',
|
|
||||||
contentClass: 'py-0 min-h-auto text-center sm:text-start',
|
|
||||||
footerClass: 'p-4',
|
|
||||||
}
|
|
||||||
const { open } = create(Object.assign(defaultOptions, options))
|
|
||||||
open()
|
|
||||||
}
|
|
||||||
function success(options: alertOptions) {
|
|
||||||
const defaultOptions: BaseOptions = {
|
|
||||||
icon: 'success',
|
|
||||||
closable: false,
|
|
||||||
border: false,
|
|
||||||
alignCenter: true,
|
|
||||||
closeOnClickOverlay: false,
|
|
||||||
destroyOnClose: true,
|
|
||||||
openAutoFocus: true,
|
|
||||||
class: 'min-h-auto max-w-[calc(100%-2rem)]',
|
|
||||||
contentClass: 'py-0 min-h-auto text-center sm:text-start',
|
|
||||||
footerClass: 'p-4',
|
|
||||||
}
|
|
||||||
const { open } = create(Object.assign(defaultOptions, options))
|
|
||||||
open()
|
|
||||||
}
|
|
||||||
function warning(options: alertOptions) {
|
|
||||||
const defaultOptions: BaseOptions = {
|
|
||||||
icon: 'warning',
|
|
||||||
closable: false,
|
|
||||||
border: false,
|
|
||||||
alignCenter: true,
|
|
||||||
closeOnClickOverlay: false,
|
|
||||||
destroyOnClose: true,
|
|
||||||
openAutoFocus: true,
|
|
||||||
class: 'min-h-auto max-w-[calc(100%-2rem)]',
|
|
||||||
contentClass: 'py-0 min-h-auto text-center sm:text-start',
|
|
||||||
footerClass: 'p-4',
|
|
||||||
}
|
|
||||||
const { open } = create(Object.assign(defaultOptions, options))
|
|
||||||
open()
|
|
||||||
}
|
|
||||||
function error(options: alertOptions) {
|
|
||||||
const defaultOptions: BaseOptions = {
|
|
||||||
icon: 'error',
|
|
||||||
closable: false,
|
|
||||||
border: false,
|
|
||||||
alignCenter: true,
|
|
||||||
closeOnClickOverlay: false,
|
|
||||||
destroyOnClose: true,
|
|
||||||
openAutoFocus: true,
|
|
||||||
class: 'min-h-auto max-w-[calc(100%-2rem)]',
|
|
||||||
contentClass: 'py-0 min-h-auto text-center sm:text-start',
|
|
||||||
footerClass: 'p-4',
|
|
||||||
}
|
|
||||||
const { open } = create(Object.assign(defaultOptions, options))
|
|
||||||
open()
|
|
||||||
}
|
|
||||||
function confirm(options: confirmOptions) {
|
|
||||||
const defaultOptions: BaseOptions = {
|
|
||||||
closable: false,
|
|
||||||
border: false,
|
|
||||||
alignCenter: true,
|
|
||||||
showCancelButton: true,
|
|
||||||
closeOnClickOverlay: false,
|
|
||||||
destroyOnClose: true,
|
|
||||||
openAutoFocus: true,
|
|
||||||
class: 'min-h-auto max-w-[calc(100%-2rem)]',
|
|
||||||
contentClass: 'py-0 min-h-auto text-center sm:text-start',
|
|
||||||
footerClass: 'p-4',
|
|
||||||
}
|
|
||||||
const { open, update } = create(Object.assign(defaultOptions, options))
|
|
||||||
open()
|
|
||||||
return {
|
|
||||||
update,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
create,
|
|
||||||
info,
|
|
||||||
success,
|
|
||||||
warning,
|
|
||||||
error,
|
|
||||||
confirm,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,231 @@
|
|||||||
|
import type { Component, HTMLAttributes, VNode } from 'vue'
|
||||||
|
import { createVNode, getCurrentInstance, h, isVNode, onUnmounted, ref, render, shallowRef, watchEffect } from 'vue'
|
||||||
|
import Modal from './index.vue'
|
||||||
|
|
||||||
|
export interface ModalProps {
|
||||||
|
id?: string
|
||||||
|
modelValue?: boolean
|
||||||
|
zIndex?: number
|
||||||
|
title?: string | (() => string)
|
||||||
|
description?: string | (() => string)
|
||||||
|
icon?: 'info' | 'success' | 'warning' | 'error'
|
||||||
|
loading?: boolean
|
||||||
|
closable?: boolean
|
||||||
|
maximize?: boolean
|
||||||
|
maximizable?: boolean
|
||||||
|
draggable?: boolean
|
||||||
|
center?: boolean
|
||||||
|
border?: boolean
|
||||||
|
alignCenter?: boolean
|
||||||
|
overlay?: boolean
|
||||||
|
overlayBlur?: boolean
|
||||||
|
showConfirmButton?: boolean
|
||||||
|
showCancelButton?: boolean
|
||||||
|
confirmButtonText?: string | (() => string)
|
||||||
|
cancelButtonText?: string | (() => string)
|
||||||
|
confirmButtonDisabled?: boolean
|
||||||
|
confirmButtonLoading?: boolean
|
||||||
|
beforeClose?: (
|
||||||
|
action: 'confirm' | 'cancel' | 'close',
|
||||||
|
done: () => void,
|
||||||
|
) => void
|
||||||
|
header?: boolean
|
||||||
|
footer?: boolean
|
||||||
|
closeOnClickOverlay?: boolean
|
||||||
|
closeOnPressEscape?: boolean
|
||||||
|
destroyOnClose?: boolean
|
||||||
|
openAutoFocus?: boolean
|
||||||
|
class?: HTMLAttributes['class']
|
||||||
|
headerClass?: HTMLAttributes['class']
|
||||||
|
contentClass?: HTMLAttributes['class']
|
||||||
|
footerClass?: HTMLAttributes['class']
|
||||||
|
}
|
||||||
|
export interface ModalEmits {
|
||||||
|
'update:modelValue': [value: boolean]
|
||||||
|
'open': []
|
||||||
|
'opened': []
|
||||||
|
'close': []
|
||||||
|
'closed': []
|
||||||
|
'confirm': []
|
||||||
|
'cancel': []
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseOptions = Omit<ModalProps, 'modelValue'> & {
|
||||||
|
content?: Component | VNode | string
|
||||||
|
onOpen?: () => void
|
||||||
|
onOpened?: () => void
|
||||||
|
onClose?: () => void
|
||||||
|
onClosed?: () => void
|
||||||
|
onConfirm?: () => void
|
||||||
|
onCancel?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
type alertOptions = Pick<BaseOptions, 'title' | 'description' | 'icon' | 'alignCenter' | 'overlay' | 'overlayBlur' | 'confirmButtonText' | 'confirmButtonDisabled' | 'confirmButtonLoading' | 'closeOnClickOverlay' | 'closeOnPressEscape' | 'class' | 'headerClass' | 'contentClass' | 'footerClass' | 'content' | 'onConfirm'>
|
||||||
|
|
||||||
|
type confirmOptions = Pick<BaseOptions, 'title' | 'description' | 'alignCenter' | 'overlay' | 'overlayBlur' | 'confirmButtonText' | 'cancelButtonText' | 'confirmButtonDisabled' | 'confirmButtonLoading' | 'beforeClose' | 'closeOnClickOverlay' | 'closeOnPressEscape' | 'class' | 'headerClass' | 'contentClass' | 'footerClass' | 'content' | 'onConfirm' | 'onCancel'>
|
||||||
|
|
||||||
|
export function useModal() {
|
||||||
|
function create(initialOptions: BaseOptions) {
|
||||||
|
const container = document.createElement('div')
|
||||||
|
const visible = ref(false)
|
||||||
|
const options = shallowRef<BaseOptions>({ ...initialOptions })
|
||||||
|
const instance = getCurrentInstance()
|
||||||
|
let vnode: VNode | null = null
|
||||||
|
|
||||||
|
const updateVNode = () => {
|
||||||
|
vnode = createVNode(Modal, Object.assign({
|
||||||
|
'id': instance && instance.uid ? `FaModal-${instance.uid}` : undefined,
|
||||||
|
'modelValue': visible.value,
|
||||||
|
'onUpdate:modelValue': (val: boolean) => {
|
||||||
|
visible.value = val
|
||||||
|
},
|
||||||
|
...options.value,
|
||||||
|
}), {
|
||||||
|
default: () => {
|
||||||
|
if (typeof options.value.content === 'string') {
|
||||||
|
return options.value.content
|
||||||
|
}
|
||||||
|
else if (isVNode(options.value.content)) {
|
||||||
|
return options.value.content
|
||||||
|
}
|
||||||
|
else if (options.value.content) {
|
||||||
|
return h(options.value.content)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// 继承主应用的上下文
|
||||||
|
if (instance && instance.appContext) {
|
||||||
|
vnode.appContext = instance.appContext
|
||||||
|
}
|
||||||
|
render(vnode, container)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听 visible 和 options 变化,自动重新渲染
|
||||||
|
watchEffect(() => {
|
||||||
|
updateVNode()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 挂载到当前实例
|
||||||
|
instance?.proxy?.$el?.appendChild(container)
|
||||||
|
|
||||||
|
// 监听组件卸载,自动清理
|
||||||
|
if (instance) {
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (vnode) {
|
||||||
|
render(null, container)
|
||||||
|
vnode = null
|
||||||
|
}
|
||||||
|
if (container.parentNode) {
|
||||||
|
container.parentNode.removeChild(container)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
const close = () => {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
const update = (newOptions: BaseOptions) => {
|
||||||
|
options.value = { ...options.value, ...newOptions }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
update,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function info(options: alertOptions) {
|
||||||
|
const defaultOptions: BaseOptions = {
|
||||||
|
icon: 'info',
|
||||||
|
closable: false,
|
||||||
|
border: false,
|
||||||
|
alignCenter: true,
|
||||||
|
closeOnClickOverlay: false,
|
||||||
|
destroyOnClose: true,
|
||||||
|
openAutoFocus: true,
|
||||||
|
class: 'min-h-auto max-w-[calc(100%-2rem)]',
|
||||||
|
contentClass: 'py-0 min-h-auto text-center sm:text-start',
|
||||||
|
footerClass: 'p-4',
|
||||||
|
}
|
||||||
|
const { open } = create(Object.assign(defaultOptions, options))
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
function success(options: alertOptions) {
|
||||||
|
const defaultOptions: BaseOptions = {
|
||||||
|
icon: 'success',
|
||||||
|
closable: false,
|
||||||
|
border: false,
|
||||||
|
alignCenter: true,
|
||||||
|
closeOnClickOverlay: false,
|
||||||
|
destroyOnClose: true,
|
||||||
|
openAutoFocus: true,
|
||||||
|
class: 'min-h-auto max-w-[calc(100%-2rem)]',
|
||||||
|
contentClass: 'py-0 min-h-auto text-center sm:text-start',
|
||||||
|
footerClass: 'p-4',
|
||||||
|
}
|
||||||
|
const { open } = create(Object.assign(defaultOptions, options))
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
function warning(options: alertOptions) {
|
||||||
|
const defaultOptions: BaseOptions = {
|
||||||
|
icon: 'warning',
|
||||||
|
closable: false,
|
||||||
|
border: false,
|
||||||
|
alignCenter: true,
|
||||||
|
closeOnClickOverlay: false,
|
||||||
|
destroyOnClose: true,
|
||||||
|
openAutoFocus: true,
|
||||||
|
class: 'min-h-auto max-w-[calc(100%-2rem)]',
|
||||||
|
contentClass: 'py-0 min-h-auto text-center sm:text-start',
|
||||||
|
footerClass: 'p-4',
|
||||||
|
}
|
||||||
|
const { open } = create(Object.assign(defaultOptions, options))
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
function error(options: alertOptions) {
|
||||||
|
const defaultOptions: BaseOptions = {
|
||||||
|
icon: 'error',
|
||||||
|
closable: false,
|
||||||
|
border: false,
|
||||||
|
alignCenter: true,
|
||||||
|
closeOnClickOverlay: false,
|
||||||
|
destroyOnClose: true,
|
||||||
|
openAutoFocus: true,
|
||||||
|
class: 'min-h-auto max-w-[calc(100%-2rem)]',
|
||||||
|
contentClass: 'py-0 min-h-auto text-center sm:text-start',
|
||||||
|
footerClass: 'p-4',
|
||||||
|
}
|
||||||
|
const { open } = create(Object.assign(defaultOptions, options))
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
function confirm(options: confirmOptions) {
|
||||||
|
const defaultOptions: BaseOptions = {
|
||||||
|
closable: false,
|
||||||
|
border: false,
|
||||||
|
alignCenter: true,
|
||||||
|
showCancelButton: true,
|
||||||
|
closeOnClickOverlay: false,
|
||||||
|
destroyOnClose: true,
|
||||||
|
openAutoFocus: true,
|
||||||
|
class: 'min-h-auto max-w-[calc(100%-2rem)]',
|
||||||
|
contentClass: 'py-0 min-h-auto text-center sm:text-start',
|
||||||
|
footerClass: 'p-4',
|
||||||
|
}
|
||||||
|
const { open, update } = create(Object.assign(defaultOptions, options))
|
||||||
|
open()
|
||||||
|
return {
|
||||||
|
update,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
create,
|
||||||
|
info,
|
||||||
|
success,
|
||||||
|
warning,
|
||||||
|
error,
|
||||||
|
confirm,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as NumberField } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as PageHeader } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as PageMain } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Pagination } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as PasswordStrength } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Popover } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Progress } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as RadioGroup } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as ScrollArea } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as SearchBar } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Select } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Slider } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Switch } from './index.vue'
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as Table } from './index.vue'
|
||||||
|
export type { TableAlign, TableCellSlotProps, TableColumn, TableColumnFixed, TableEmptySlotProps, TableExpandedState, TableFooterSlotProps, TableHeaderSlotProps, TableProps, TableSelectionColumn, TableSelectionDisabled, TableSortingState, TableToolbarSlotProps } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Tabs } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Tag } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Textarea } from './index.vue'
|
||||||
@@ -1,5 +1,2 @@
|
|||||||
import { toast } from 'vue-sonner'
|
export { default as Toast } from './index.vue'
|
||||||
|
export { useToast } from './useToast'
|
||||||
export function useToast() {
|
|
||||||
return toast
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { toast } from 'vue-sonner'
|
||||||
|
|
||||||
|
export function useToast() {
|
||||||
|
return toast
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Tooltip } from './index.vue'
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as Trend } from './index.vue'
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
export { default as alert } from './basic/alert/_examples'
|
export { default as alert } from './basic/alert/_examples'
|
||||||
export { default as avatar } from './basic/avatar/_examples'
|
export { default as avatar } from './basic/avatar/_examples'
|
||||||
export { default as badge } from './basic/badge/_examples'
|
export { default as badge } from './basic/badge/_examples'
|
||||||
export { default as buttonGroup } from './basic/button-group/_examples'
|
|
||||||
export { default as button } from './basic/button/_examples'
|
export { default as button } from './basic/button/_examples'
|
||||||
export { default as card } from './basic/card/_examples'
|
export { default as card } from './basic/card/_examples'
|
||||||
export { default as checkboxGroup } from './basic/checkbox-group/_examples'
|
|
||||||
export { default as checkbox } from './basic/checkbox/_examples'
|
export { default as checkbox } from './basic/checkbox/_examples'
|
||||||
export { default as collapsible } from './basic/collapsible/_examples'
|
export { default as collapsible } from './basic/collapsible/_examples'
|
||||||
export { default as contextMenu } from './basic/context-menu/_examples'
|
export { default as contextMenu } from './basic/context-menu/_examples'
|
||||||
@@ -20,7 +18,6 @@ export { default as imagePreview } from './basic/image-preview/_examples'
|
|||||||
export { default as imageUpload } from './basic/image-upload/_examples'
|
export { default as imageUpload } from './basic/image-upload/_examples'
|
||||||
export { default as inputOtp } from './basic/input-otp/_examples'
|
export { default as inputOtp } from './basic/input-otp/_examples'
|
||||||
export { default as input } from './basic/input/_examples'
|
export { default as input } from './basic/input/_examples'
|
||||||
export { default as kbdGroup } from './basic/kbd-group/_examples'
|
|
||||||
export { default as kbd } from './basic/kbd/_examples'
|
export { default as kbd } from './basic/kbd/_examples'
|
||||||
export { default as label } from './basic/label/_examples'
|
export { default as label } from './basic/label/_examples'
|
||||||
export { default as modal } from './basic/modal/_examples'
|
export { default as modal } from './basic/modal/_examples'
|
||||||
|
|||||||
@@ -1,57 +1,54 @@
|
|||||||
// 基础版组件
|
// 基础版组件
|
||||||
export { default as FaAlert } from './basic/alert/index.vue'
|
export { Alert as FaAlert } from './basic/alert'
|
||||||
export { default as FaAvatar } from './basic/avatar/index.vue'
|
export { Avatar as FaAvatar } from './basic/avatar'
|
||||||
export { default as FaBadge } from './basic/badge/index.vue'
|
export { Badge as FaBadge } from './basic/badge'
|
||||||
export { default as FaButtonGroup } from './basic/button-group/index.vue'
|
export { Button as FaButton, ButtonGroup as FaButtonGroup } from './basic/button'
|
||||||
export { default as FaButton } from './basic/button/index.vue'
|
export { Card as FaCard } from './basic/card'
|
||||||
export { default as FaCard } from './basic/card/index.vue'
|
export { Checkbox as FaCheckbox, CheckboxGroup as FaCheckboxGroup } from './basic/checkbox'
|
||||||
export { default as FaCheckboxGroup } from './basic/checkbox-group/index.vue'
|
export { Collapsible as FaCollapsible } from './basic/collapsible'
|
||||||
export { default as FaCheckbox } from './basic/checkbox/index.vue'
|
export { ContextMenu as FaContextMenu } from './basic/context-menu'
|
||||||
export { default as FaCollapsible } from './basic/collapsible/index.vue'
|
export { Descriptions as FaDescriptions } from './basic/descriptions'
|
||||||
export { default as FaContextMenu } from './basic/context-menu/index.vue'
|
export type { DescriptionDirection, DescriptionItem, DescriptionItemBase, DescriptionSize, DescriptionSlotProps, DescriptionsProps, DescriptionValue } from './basic/descriptions'
|
||||||
export { default as FaDescriptions } from './basic/descriptions/index.vue'
|
export { Divider as FaDivider } from './basic/divider'
|
||||||
export type { DescriptionDirection, DescriptionItem, DescriptionItemBase, DescriptionSize, DescriptionSlotProps, DescriptionsProps, DescriptionValue } from './basic/descriptions/index.vue'
|
export { Drawer as FaDrawer } from './basic/drawer'
|
||||||
export { default as FaDivider } from './basic/divider/index.vue'
|
export type { DrawerEmits, DrawerProps } from './basic/drawer'
|
||||||
export type { DrawerEmits, DrawerProps } from './basic/drawer/index'
|
export { useDrawer as useFaDrawer } from './basic/drawer'
|
||||||
export { useDrawer as useFaDrawer } from './basic/drawer/index'
|
export { Dropdown as FaDropdown } from './basic/dropdown'
|
||||||
export { default as FaDrawer } from './basic/drawer/index.vue'
|
export { FileUpload as FaFileUpload } from './basic/file-upload'
|
||||||
export { default as FaDropdown } from './basic/dropdown/index.vue'
|
export type { FileItem, FileUploadRequestOptions } from './basic/file-upload'
|
||||||
export { default as FaFileUpload } from './basic/file-upload/index.vue'
|
export { FixedBar as FaFixedBar } from './basic/fixed-bar'
|
||||||
export type { FileItem, FileUploadRequestOptions } from './basic/file-upload/index.vue'
|
export { HoverCard as FaHoverCard } from './basic/hover-card'
|
||||||
export { default as FaFixedBar } from './basic/fixed-bar/index.vue'
|
export { Icon as FaIcon } from './basic/icon'
|
||||||
export { default as FaHoverCard } from './basic/hover-card/index.vue'
|
export { ImagePreview as FaImagePreview } from './basic/image-preview'
|
||||||
export { default as FaIcon } from './basic/icon/index.vue'
|
export { useImagePreview as useFaImagePreview } from './basic/image-preview'
|
||||||
export { useImagePreview as useFaImagePreview } from './basic/image-preview/index'
|
export { ImageUpload as FaImageUpload } from './basic/image-upload'
|
||||||
export { default as FaImagePreview } from './basic/image-preview/index.vue'
|
export type { UploadRequestOptions } from './basic/image-upload'
|
||||||
export { default as FaImageUpload } from './basic/image-upload/index.vue'
|
export { Input as FaInput } from './basic/input'
|
||||||
export type { UploadRequestOptions } from './basic/image-upload/index.vue'
|
export { InputOTP as FaInputOTP } from './basic/input-otp'
|
||||||
export { default as FaInputOTP } from './basic/input-otp/index.vue'
|
export { Kbd as FaKbd, KbdGroup as FaKbdGroup } from './basic/kbd'
|
||||||
export { default as FaInput } from './basic/input/index.vue'
|
export { Label as FaLabel } from './basic/label'
|
||||||
export { default as FaKbdGroup } from './basic/kbd-group/index.vue'
|
export { Modal as FaModal } from './basic/modal'
|
||||||
export { default as FaKbd } from './basic/kbd/index.vue'
|
export type { ModalEmits, ModalProps } from './basic/modal'
|
||||||
export { default as FaLabel } from './basic/label/index.vue'
|
export { useModal as useFaModal } from './basic/modal'
|
||||||
export type { ModalEmits, ModalProps } from './basic/modal/index'
|
export { NumberField as FaNumberField } from './basic/number-field'
|
||||||
export { useModal as useFaModal } from './basic/modal/index'
|
export { PageHeader as FaPageHeader } from './basic/page-header'
|
||||||
export { default as FaModal } from './basic/modal/index.vue'
|
export { PageMain as FaPageMain } from './basic/page-main'
|
||||||
export { default as FaNumberField } from './basic/number-field/index.vue'
|
export { Pagination as FaPagination } from './basic/pagination'
|
||||||
export { default as FaPageHeader } from './basic/page-header/index.vue'
|
export { PasswordStrength as FaPasswordStrength } from './basic/password-strength'
|
||||||
export { default as FaPageMain } from './basic/page-main/index.vue'
|
export { Popover as FaPopover } from './basic/popover'
|
||||||
export { default as FaPagination } from './basic/pagination/index.vue'
|
export { Progress as FaProgress } from './basic/progress'
|
||||||
export { default as FaPasswordStrength } from './basic/password-strength/index.vue'
|
export { RadioGroup as FaRadioGroup } from './basic/radio-group'
|
||||||
export { default as FaPopover } from './basic/popover/index.vue'
|
export { ScrollArea as FaScrollArea } from './basic/scroll-area'
|
||||||
export { default as FaProgress } from './basic/progress/index.vue'
|
export { SearchBar as FaSearchBar } from './basic/search-bar'
|
||||||
export { default as FaRadioGroup } from './basic/radio-group/index.vue'
|
export { Select as FaSelect } from './basic/select'
|
||||||
export { default as FaScrollArea } from './basic/scroll-area/index.vue'
|
export { Slider as FaSlider } from './basic/slider'
|
||||||
export { default as FaSearchBar } from './basic/search-bar/index.vue'
|
export { Switch as FaSwitch } from './basic/switch'
|
||||||
export { default as FaSelect } from './basic/select/index.vue'
|
export { Table as FaTable } from './basic/table'
|
||||||
export { default as FaSlider } from './basic/slider/index.vue'
|
export type { TableAlign, TableCellSlotProps, TableColumn, TableColumnFixed, TableEmptySlotProps, TableExpandedState, TableFooterSlotProps, TableHeaderSlotProps, TableProps, TableSelectionColumn, TableSelectionDisabled, TableSortingState, TableToolbarSlotProps } from './basic/table'
|
||||||
export { default as FaSwitch } from './basic/switch/index.vue'
|
export { Tabs as FaTabs } from './basic/tabs'
|
||||||
export { default as FaTable } from './basic/table/index.vue'
|
export { Tag as FaTag } from './basic/tag'
|
||||||
export type { TableAlign, TableCellSlotProps, TableColumn, TableColumnFixed, TableEmptySlotProps, TableExpandedState, TableFooterSlotProps, TableHeaderSlotProps, TableProps, TableSelectionColumn, TableSelectionDisabled, TableSortingState, TableToolbarSlotProps } from './basic/table/index.vue'
|
export { Textarea as FaTextarea } from './basic/textarea'
|
||||||
export { default as FaTabs } from './basic/tabs/index.vue'
|
export { Toast as FaToast } from './basic/toast'
|
||||||
export { default as FaTag } from './basic/tag/index.vue'
|
export { useToast as useFaToast } from './basic/toast'
|
||||||
export { default as FaTextarea } from './basic/textarea/index.vue'
|
export { Tooltip as FaTooltip } from './basic/tooltip'
|
||||||
export { useToast as useFaToast } from './basic/toast/index'
|
export { Trend as FaTrend } from './basic/trend'
|
||||||
export { default as FaToast } from './basic/toast/index.vue'
|
|
||||||
export { default as FaTooltip } from './basic/tooltip/index.vue'
|
|
||||||
export { default as FaTrend } from './basic/trend/index.vue'
|
|
||||||
|
|||||||
Reference in New Issue
Block a user