Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev
This commit is contained in:
@@ -3,11 +3,15 @@
|
||||
* 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
|
||||
*/
|
||||
|
||||
import type { BaseFormComponentType } from '@vben/common-ui';
|
||||
import type { Component, SetupContext } from 'vue';
|
||||
|
||||
import type { BaseFormComponentType } from '@vben/common-ui';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
AutoComplete,
|
||||
Button,
|
||||
@@ -32,7 +36,6 @@ import {
|
||||
TreeSelect,
|
||||
Upload,
|
||||
} from 'ant-design-vue';
|
||||
import { h } from 'vue';
|
||||
|
||||
const withDefaultPlaceholder = <T extends Component>(
|
||||
component: T,
|
||||
@@ -123,7 +126,13 @@ async function initComponentAdapter() {
|
||||
IconPicker: (props, { attrs, slots }) => {
|
||||
return h(
|
||||
IconPicker,
|
||||
{ iconSlot: 'addonAfter', inputComponent: Input, ...props, ...attrs },
|
||||
{
|
||||
iconSlot: 'addonAfter',
|
||||
inputComponent: Input,
|
||||
modelValueProp: 'value',
|
||||
...props,
|
||||
...attrs,
|
||||
},
|
||||
slots,
|
||||
);
|
||||
},
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { createApp, watchEffect } from 'vue';
|
||||
|
||||
import { registerAccessDirective } from '@vben/access';
|
||||
import { initTippy } from '@vben/common-ui';
|
||||
import { initTippy, registerLoadingDirective } from '@vben/common-ui';
|
||||
import { MotionPlugin } from '@vben/plugins/motion';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { initStores } from '@vben/stores';
|
||||
@@ -32,6 +32,12 @@ async function bootstrap(namespace: string) {
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
// 注册v-loading指令
|
||||
registerLoadingDirective(app, {
|
||||
loading: 'loading', // 在这里可以自定义指令名称,也可以明确提供false表示不注册这个指令
|
||||
spinning: 'spinning',
|
||||
});
|
||||
|
||||
// 国际化 i18n 配置
|
||||
await setupI18n(app);
|
||||
|
||||
|
@@ -12,6 +12,7 @@
|
||||
"form": {
|
||||
"title": "Form",
|
||||
"basic": "Basic Form",
|
||||
"layout": "Custom Layout",
|
||||
"query": "Query Form",
|
||||
"rules": "Form Rules",
|
||||
"dynamic": "Dynamic Form",
|
||||
@@ -62,5 +63,8 @@
|
||||
},
|
||||
"layout": {
|
||||
"col-page": "ColPage Layout"
|
||||
},
|
||||
"button-group": {
|
||||
"title": "Button Group"
|
||||
}
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@
|
||||
"form": {
|
||||
"title": "表单",
|
||||
"basic": "基础表单",
|
||||
"layout": "自定义布局",
|
||||
"query": "查询表单",
|
||||
"rules": "表单校验",
|
||||
"dynamic": "动态表单",
|
||||
@@ -62,5 +63,8 @@
|
||||
},
|
||||
"layout": {
|
||||
"col-page": "双列布局"
|
||||
},
|
||||
"button-group": {
|
||||
"title": "按钮组"
|
||||
}
|
||||
}
|
||||
|
@@ -53,6 +53,14 @@ const routes: RouteRecordRaw[] = [
|
||||
title: $t('examples.form.dynamic'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FormLayoutExample',
|
||||
path: '/examples/form/custom-layout',
|
||||
component: () => import('#/views/examples/form/custom-layout.vue'),
|
||||
meta: {
|
||||
title: $t('examples.form.layout'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FormCustomExample',
|
||||
path: '/examples/form/custom',
|
||||
@@ -282,6 +290,24 @@ const routes: RouteRecordRaw[] = [
|
||||
title: 'CountTo',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Loading',
|
||||
path: '/examples/loading',
|
||||
component: () => import('#/views/examples/loading/index.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:circle-double',
|
||||
title: 'Loading',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'ButtonGroup',
|
||||
path: '/examples/button-group',
|
||||
component: () => import('#/views/examples/button-group/index.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:check-circle',
|
||||
title: $t('examples.button-group.title'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
194
playground/src/views/examples/button-group/index.vue
Normal file
194
playground/src/views/examples/button-group/index.vue
Normal file
@@ -0,0 +1,194 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { reactive, ref } from 'vue';
|
||||
|
||||
import {
|
||||
Page,
|
||||
VbenButton,
|
||||
VbenButtonGroup,
|
||||
VbenCheckButtonGroup,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import { Button, Card, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
const radioValue = ref<string | undefined>('a');
|
||||
const checkValue = ref(['a', 'b']);
|
||||
|
||||
const options = [
|
||||
{ label: '选项1', value: 'a' },
|
||||
{ label: '选项2', value: 'b' },
|
||||
{ label: '选项3', value: 'c' },
|
||||
{ label: '选项4', value: 'd' },
|
||||
{ label: '选项5', value: 'e' },
|
||||
{ label: '选项6', value: 'f' },
|
||||
];
|
||||
|
||||
function resetValues() {
|
||||
radioValue.value = undefined;
|
||||
checkValue.value = [];
|
||||
}
|
||||
|
||||
function beforeChange(v: any, isChecked: boolean) {
|
||||
return new Promise((resolve) => {
|
||||
message.loading({
|
||||
content: `正在设置${v}为${isChecked ? '选中' : '未选中'}...`,
|
||||
duration: 0,
|
||||
key: 'beforeChange',
|
||||
});
|
||||
setTimeout(() => {
|
||||
message.success({ content: `${v} 已设置成功`, key: 'beforeChange' });
|
||||
resolve(true);
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
const compProps = reactive({
|
||||
beforeChange: undefined,
|
||||
disabled: false,
|
||||
gap: 0,
|
||||
showIcon: true,
|
||||
size: 'middle',
|
||||
} as Recordable<any>);
|
||||
|
||||
const [Form] = useVbenForm({
|
||||
handleValuesChange(values) {
|
||||
Object.keys(values).forEach((k) => {
|
||||
if (k === 'beforeChange') {
|
||||
compProps[k] = values[k] ? beforeChange : undefined;
|
||||
} else {
|
||||
compProps[k] = values[k];
|
||||
}
|
||||
});
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '大', value: 'large' },
|
||||
{ label: '中', value: 'middle' },
|
||||
{ label: '小', value: 'small' },
|
||||
],
|
||||
},
|
||||
defaultValue: compProps.size,
|
||||
fieldName: 'size',
|
||||
label: '尺寸',
|
||||
},
|
||||
{
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{ label: '无', value: 0 },
|
||||
{ label: '小', value: 5 },
|
||||
{ label: '中', value: 15 },
|
||||
{ label: '大', value: 30 },
|
||||
],
|
||||
},
|
||||
defaultValue: compProps.gap,
|
||||
fieldName: 'gap',
|
||||
label: '间距',
|
||||
},
|
||||
{
|
||||
component: 'Switch',
|
||||
defaultValue: compProps.showIcon,
|
||||
fieldName: 'showIcon',
|
||||
label: '显示图标',
|
||||
},
|
||||
{
|
||||
component: 'Switch',
|
||||
defaultValue: compProps.disabled,
|
||||
fieldName: 'disabled',
|
||||
label: '禁用',
|
||||
},
|
||||
{
|
||||
component: 'Switch',
|
||||
defaultValue: false,
|
||||
fieldName: 'beforeChange',
|
||||
label: '前置回调',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
submitOnChange: true,
|
||||
});
|
||||
|
||||
function onBtnClick(value: any) {
|
||||
const opt = options.find((o) => o.value === value);
|
||||
if (opt) {
|
||||
message.success(`点击了按钮${opt.label},value = ${value}`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<Page
|
||||
title="VbenButtonGroup 按钮组"
|
||||
description="VbenButtonGroup是一个按钮容器,用于包裹一组按钮,协调整体样式。VbenCheckButtonGroup则可以作为一个表单组件,提供单选或多选功能"
|
||||
>
|
||||
<Card title="基本用法">
|
||||
<template #extra>
|
||||
<Button type="primary" @click="resetValues">清空值</Button>
|
||||
</template>
|
||||
<p class="mt-4">按钮组:</p>
|
||||
<div class="mt-2 flex flex-col gap-2">
|
||||
<VbenButtonGroup v-bind="compProps" border>
|
||||
<VbenButton
|
||||
v-for="btn in options"
|
||||
:key="btn.value"
|
||||
variant="link"
|
||||
@click="onBtnClick(btn.value)"
|
||||
>
|
||||
{{ btn.label }}
|
||||
</VbenButton>
|
||||
</VbenButtonGroup>
|
||||
<VbenButtonGroup v-bind="compProps" border>
|
||||
<VbenButton
|
||||
v-for="btn in options"
|
||||
:key="btn.value"
|
||||
variant="outline"
|
||||
@click="onBtnClick(btn.value)"
|
||||
>
|
||||
{{ btn.label }}
|
||||
</VbenButton>
|
||||
</VbenButtonGroup>
|
||||
</div>
|
||||
<p class="mt-4">单选:{{ radioValue }}</p>
|
||||
<div class="mt-2 flex flex-col gap-2">
|
||||
<VbenCheckButtonGroup
|
||||
v-model="radioValue"
|
||||
:options="options"
|
||||
v-bind="compProps"
|
||||
/>
|
||||
</div>
|
||||
<p class="mt-4">单选插槽:{{ radioValue }}</p>
|
||||
<div class="mt-2 flex flex-col gap-2">
|
||||
<VbenCheckButtonGroup
|
||||
v-model="radioValue"
|
||||
:options="options"
|
||||
v-bind="compProps"
|
||||
>
|
||||
<template #option="{ label, value }">
|
||||
<div class="flex items-center">
|
||||
<span>{{ label }}</span>
|
||||
<span class="ml-2 text-gray-400">{{ value }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</VbenCheckButtonGroup>
|
||||
</div>
|
||||
<p class="mt-4">多选{{ checkValue }}</p>
|
||||
<div class="mt-2 flex flex-col gap-2">
|
||||
<VbenCheckButtonGroup
|
||||
v-model="checkValue"
|
||||
multiple
|
||||
:options="options"
|
||||
v-bind="compProps"
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card title="设置" class="mt-4">
|
||||
<Form />
|
||||
</Card>
|
||||
</Page>
|
||||
</template>
|
@@ -1,15 +1,17 @@
|
||||
<script lang="ts" setup>
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { getAllMenusApi } from '#/api';
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
import { Button, Card, message, Spin, TabPane, Tabs } from 'ant-design-vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
import { Button, Card, message, Spin, Tag } from 'ant-design-vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { useVbenForm, z } from '#/adapter/form';
|
||||
import { getAllMenusApi } from '#/api';
|
||||
|
||||
import DocButton from '../doc-button.vue';
|
||||
|
||||
const activeTab = ref('basic');
|
||||
const keyword = ref('');
|
||||
const fetching = ref(false);
|
||||
// 模拟远程获取数据
|
||||
@@ -108,6 +110,7 @@ const [BaseForm, baseFormApi] = useVbenForm({
|
||||
notFoundContent: fetching.value ? h(Spin) : undefined,
|
||||
};
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
component: 'ApiTreeSelect',
|
||||
@@ -115,10 +118,10 @@ const [BaseForm, baseFormApi] = useVbenForm({
|
||||
componentProps: {
|
||||
// 菜单接口
|
||||
api: getAllMenusApi,
|
||||
childrenField: 'children',
|
||||
// 菜单接口转options格式
|
||||
labelField: 'name',
|
||||
valueField: 'path',
|
||||
childrenField: 'children',
|
||||
},
|
||||
// 字段名
|
||||
fieldName: 'apiTree',
|
||||
@@ -148,6 +151,7 @@ const [BaseForm, baseFormApi] = useVbenForm({
|
||||
label: '图标',
|
||||
},
|
||||
{
|
||||
colon: false,
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
@@ -166,7 +170,7 @@ const [BaseForm, baseFormApi] = useVbenForm({
|
||||
showSearch: true,
|
||||
},
|
||||
fieldName: 'options',
|
||||
label: '下拉选',
|
||||
label: () => h(Tag, { color: 'warning' }, () => '😎自定义:'),
|
||||
},
|
||||
{
|
||||
component: 'RadioGroup',
|
||||
@@ -222,6 +226,9 @@ const [BaseForm, baseFormApi] = useVbenForm({
|
||||
default: () => ['我已阅读并同意'],
|
||||
};
|
||||
},
|
||||
rules: z
|
||||
.boolean()
|
||||
.refine((v) => v, { message: '为什么不同意?勾上它!' }),
|
||||
},
|
||||
{
|
||||
component: 'Mentions',
|
||||
@@ -252,6 +259,8 @@ const [BaseForm, baseFormApi] = useVbenForm({
|
||||
class: 'w-auto',
|
||||
},
|
||||
fieldName: 'switch',
|
||||
help: () =>
|
||||
['这是一个多行帮助信息', '第二行', '第三行'].map((v) => h('p', v)),
|
||||
label: '开关',
|
||||
},
|
||||
{
|
||||
@@ -321,75 +330,6 @@ const [BaseForm, baseFormApi] = useVbenForm({
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
|
||||
});
|
||||
|
||||
const [CustomLayoutForm] = useVbenForm({
|
||||
// 所有表单项共用,可单独在表单内覆盖
|
||||
commonConfig: {
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'Select',
|
||||
fieldName: 'field1',
|
||||
label: '字符串',
|
||||
},
|
||||
{
|
||||
component: 'TreeSelect',
|
||||
fieldName: 'field2',
|
||||
label: '字符串',
|
||||
},
|
||||
{
|
||||
component: 'Mentions',
|
||||
fieldName: 'field3',
|
||||
label: '字符串',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'field4',
|
||||
label: '字符串',
|
||||
},
|
||||
{
|
||||
component: 'InputNumber',
|
||||
fieldName: 'field5',
|
||||
// 从第三列开始 相当于中间空了一列
|
||||
formItemClass: 'col-start-3',
|
||||
label: '前面空了一列',
|
||||
},
|
||||
{
|
||||
component: 'Textarea',
|
||||
fieldName: 'field6',
|
||||
// 占满三列空间 基线对齐
|
||||
formItemClass: 'col-span-3 items-baseline',
|
||||
label: '占满三列',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'field7',
|
||||
// 占满2列空间 从第二列开始 相当于前面空了一列
|
||||
formItemClass: 'col-span-2 col-start-2',
|
||||
label: '占满2列',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'field8',
|
||||
// 左右留空
|
||||
formItemClass: 'col-start-2',
|
||||
label: '左右留空',
|
||||
},
|
||||
{
|
||||
component: 'InputPassword',
|
||||
fieldName: 'field9',
|
||||
formItemClass: 'col-start-1',
|
||||
label: '字符串',
|
||||
},
|
||||
],
|
||||
// 一共三列
|
||||
wrapperClass: 'grid-cols-3',
|
||||
});
|
||||
|
||||
function onSubmit(values: Record<string, any>) {
|
||||
message.success({
|
||||
content: `form values: ${JSON.stringify(values)}`,
|
||||
@@ -425,7 +365,6 @@ function handleSetFormValue() {
|
||||
<Page
|
||||
content-class="flex flex-col gap-4"
|
||||
description="表单组件基础示例,请注意,该页面用到的参数代码会添加一些简单注释,方便理解,请仔细查看。"
|
||||
header-class="pb-0"
|
||||
title="表单组件"
|
||||
>
|
||||
<template #description>
|
||||
@@ -434,22 +373,15 @@ function handleSetFormValue() {
|
||||
表单组件基础示例,请注意,该页面用到的参数代码会添加一些简单注释,方便理解,请仔细查看。
|
||||
</p>
|
||||
</div>
|
||||
<Tabs v-model:active-key="activeTab" :tab-bar-style="{ marginBottom: 0 }">
|
||||
<TabPane key="basic" tab="基础示例" />
|
||||
<TabPane key="layout" tab="自定义布局" />
|
||||
</Tabs>
|
||||
</template>
|
||||
<template #extra>
|
||||
<DocButton class="mb-2" path="/components/common-ui/vben-form" />
|
||||
</template>
|
||||
<Card v-show="activeTab === 'basic'" title="基础示例">
|
||||
<Card title="基础示例">
|
||||
<template #extra>
|
||||
<Button type="primary" @click="handleSetFormValue">设置表单值</Button>
|
||||
</template>
|
||||
<BaseForm />
|
||||
</Card>
|
||||
<Card v-show="activeTab === 'layout'" title="使用tailwind自定义布局">
|
||||
<CustomLayoutForm />
|
||||
</Card>
|
||||
</Page>
|
||||
</template>
|
||||
|
111
playground/src/views/examples/form/custom-layout.vue
Normal file
111
playground/src/views/examples/form/custom-layout.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<script lang="ts" setup>
|
||||
import { h } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
|
||||
import { Card } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
import DocButton from '../doc-button.vue';
|
||||
|
||||
const [CustomLayoutForm] = useVbenForm({
|
||||
// 所有表单项共用,可单独在表单内覆盖
|
||||
commonConfig: {
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
component: 'Select',
|
||||
fieldName: 'field1',
|
||||
label: '字符串',
|
||||
},
|
||||
{
|
||||
component: 'TreeSelect',
|
||||
fieldName: 'field2',
|
||||
label: '字符串',
|
||||
},
|
||||
{
|
||||
component: 'Mentions',
|
||||
fieldName: 'field3',
|
||||
label: '字符串',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'field4',
|
||||
label: '字符串',
|
||||
},
|
||||
{
|
||||
component: 'InputNumber',
|
||||
fieldName: 'field5',
|
||||
// 从第三列开始 相当于中间空了一列
|
||||
formItemClass: 'col-start-3',
|
||||
label: '前面空了一列',
|
||||
},
|
||||
{
|
||||
component: 'Divider',
|
||||
fieldName: '_divider',
|
||||
formItemClass: 'col-span-3',
|
||||
hideLabel: true,
|
||||
renderComponentContent: () => {
|
||||
return {
|
||||
default: () => h('div', '分割线'),
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Textarea',
|
||||
fieldName: 'field6',
|
||||
// 占满三列空间 基线对齐
|
||||
formItemClass: 'col-span-3 items-baseline',
|
||||
label: '占满三列',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'field7',
|
||||
// 占满2列空间 从第二列开始 相当于前面空了一列
|
||||
formItemClass: 'col-span-2 col-start-2',
|
||||
label: '占满2列',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'field8',
|
||||
// 左右留空
|
||||
formItemClass: 'col-start-2',
|
||||
label: '左右留空',
|
||||
},
|
||||
{
|
||||
component: 'InputPassword',
|
||||
fieldName: 'field9',
|
||||
formItemClass: 'col-start-1',
|
||||
label: '字符串',
|
||||
},
|
||||
],
|
||||
// 一共三列
|
||||
wrapperClass: 'grid-cols-3',
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page
|
||||
content-class="flex flex-col gap-4"
|
||||
description="使用tailwind自定义表单项的布局"
|
||||
title="表单自定义布局"
|
||||
>
|
||||
<template #description>
|
||||
<div class="text-muted-foreground">
|
||||
<p>使用tailwind自定义表单项的布局,使用Divider分割表单。</p>
|
||||
</div>
|
||||
</template>
|
||||
<template #extra>
|
||||
<DocButton class="mb-2" path="/components/common-ui/vben-form" />
|
||||
</template>
|
||||
<Card title="使用tailwind自定义布局">
|
||||
<CustomLayoutForm />
|
||||
</Card>
|
||||
</Page>
|
||||
</template>
|
@@ -150,7 +150,9 @@ const [Form, formApi] = useVbenForm({
|
||||
default: () => ['我已阅读并同意'],
|
||||
};
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
rules: z.boolean().refine((value) => value, {
|
||||
message: '请勾选',
|
||||
}),
|
||||
},
|
||||
{
|
||||
component: 'DatePicker',
|
||||
|
101
playground/src/views/examples/loading/index.vue
Normal file
101
playground/src/views/examples/loading/index.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<script lang="ts" setup>
|
||||
import { Loading, Page, Spinner } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { refAutoReset } from '@vueuse/core';
|
||||
import { Button, Card, Spin } from 'ant-design-vue';
|
||||
|
||||
const spinning = refAutoReset(false, 3000);
|
||||
const loading = refAutoReset(false, 3000);
|
||||
|
||||
const spinningV = refAutoReset(false, 3000);
|
||||
const loadingV = refAutoReset(false, 3000);
|
||||
</script>
|
||||
<template>
|
||||
<Page
|
||||
title="Vben Loading"
|
||||
description="加载中状态组件。这个组件可以为其它作为容器的组件添加一个加载中的遮罩层。使用它们时,容器需要relative定位。"
|
||||
>
|
||||
<Card title="Antd Spin">
|
||||
<template #actions>这是Antd 组件库自带的Spin组件演示</template>
|
||||
<Spin :spinning="spinning" tip="加载中...">
|
||||
<Button type="primary" @click="spinning = true">显示Spin</Button>
|
||||
</Spin>
|
||||
</Card>
|
||||
|
||||
<Card title="Vben Loading" v-loading="loadingV" class="mt-4">
|
||||
<template #extra>
|
||||
<Button type="primary" @click="loadingV = true">
|
||||
v-loading 指令
|
||||
</Button>
|
||||
</template>
|
||||
<template #actions>
|
||||
Loading组件可以设置文字,并且也提供了icon插槽用于替换加载图标。
|
||||
</template>
|
||||
<div class="flex gap-4">
|
||||
<div class="size-40">
|
||||
<Loading
|
||||
:spinning="loading"
|
||||
text="正在加载..."
|
||||
class="flex h-full w-full items-center justify-center"
|
||||
>
|
||||
<Button type="primary" @click="loading = true">默认动画</Button>
|
||||
</Loading>
|
||||
</div>
|
||||
<div class="size-40">
|
||||
<Loading
|
||||
:spinning="loading"
|
||||
class="flex h-full w-full items-center justify-center"
|
||||
>
|
||||
<Button type="primary" @click="loading = true">自定义动画1</Button>
|
||||
<template #icon>
|
||||
<IconifyIcon
|
||||
icon="svg-spinners:ring-resize"
|
||||
class="text-primary size-10"
|
||||
/>
|
||||
</template>
|
||||
</Loading>
|
||||
</div>
|
||||
<div class="size-40">
|
||||
<Loading
|
||||
:spinning="loading"
|
||||
class="flex h-full w-full items-center justify-center"
|
||||
>
|
||||
<Button type="primary" @click="loading = true">自定义动画2</Button>
|
||||
<template #icon>
|
||||
<IconifyIcon
|
||||
icon="svg-spinners:bars-scale"
|
||||
class="text-primary size-10"
|
||||
/>
|
||||
</template>
|
||||
</Loading>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Vben Spinner"
|
||||
v-spinning="spinningV"
|
||||
class="mt-4 overflow-hidden"
|
||||
:body-style="{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
}"
|
||||
>
|
||||
<template #extra>
|
||||
<Button type="primary" @click="spinningV = true">
|
||||
v-spinning 指令
|
||||
</Button>
|
||||
</template>
|
||||
<template #actions>
|
||||
Spinner组件是Loading组件的一个特例,只有一个固定的统一样式。
|
||||
</template>
|
||||
<Spinner
|
||||
:spinning="spinning"
|
||||
class="flex size-40 items-center justify-center"
|
||||
>
|
||||
<Button type="primary" @click="spinning = true">显示Spinner</Button>
|
||||
</Spinner>
|
||||
</Card>
|
||||
</Page>
|
||||
</template>
|
Reference in New Issue
Block a user