Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into warmflow

This commit is contained in:
dap
2025-01-12 10:07:39 +08:00
42 changed files with 848 additions and 131 deletions

View File

@@ -1,6 +1,7 @@
import { createApp, watchEffect } from 'vue';
import { registerAccessDirective } from '@vben/access';
import { initTippy } from '@vben/common-ui';
import { preferences } from '@vben/preferences';
import { initStores } from '@vben/stores';
import '@vben/styles';
@@ -30,6 +31,9 @@ async function bootstrap(namespace: string) {
// 安装权限指令
registerAccessDirective(app);
// 初始化 tippy
initTippy(app);
// 配置路由及路由守卫
app.use(router);

View File

@@ -248,6 +248,15 @@ const routes: RouteRecordRaw[] = [
title: $t('examples.layout.col-page'),
},
},
{
name: 'TippyDemo',
path: '/examples/tippy',
component: () => import('#/views/examples/tippy/index.vue'),
meta: {
icon: 'mdi:message-settings-outline',
title: 'Tippy',
},
},
],
},
];

View File

@@ -7,6 +7,9 @@ const [Drawer, drawerApi] = useVbenDrawer({
onCancel() {
drawerApi.close();
},
onClosed() {
drawerApi.setState({ overlayBlur: 0, placement: 'right' });
},
onConfirm() {
message.info('onConfirm');
// drawerApi.close();

View File

@@ -1,7 +1,8 @@
<script lang="ts" setup>
import type { DrawerPlacement } from '@vben/common-ui';
import type { DrawerPlacement, DrawerState } from '@vben/common-ui';
import { Page, useVbenDrawer } from '@vben/common-ui';
import { Button, Card } from 'ant-design-vue';
import DocButton from '../doc-button.vue';
@@ -41,25 +42,25 @@ const [FormDrawer, formDrawerApi] = useVbenDrawer({
});
function openBaseDrawer(placement: DrawerPlacement = 'right') {
baseDrawerApi.setState({ placement });
baseDrawerApi.open();
baseDrawerApi.setState({ placement }).open();
}
function openBlurDrawer() {
baseDrawerApi.setState({ overlayBlur: 5 }).open();
}
function openInContentDrawer(placement: DrawerPlacement = 'right') {
inContentDrawerApi.setState({ class: '', placement });
const state: Partial<DrawerState> = { class: '', placement };
if (placement === 'top') {
// 页面顶部区域的层级只有200所以设置一个低于200的值抽屉从顶部滑出来的时候才比较合适
inContentDrawerApi.setState({ zIndex: 199 });
} else {
inContentDrawerApi.setState({ zIndex: undefined });
state.zIndex = 199;
}
inContentDrawerApi.open();
inContentDrawerApi.setState(state).open();
}
function openMaxContentDrawer() {
// 这里只是用来演示方便。实际上自己使用的时候可以直接将这些配置卸载Drawer的属性里
inContentDrawerApi.setState({ class: 'w-full', placement: 'right' });
inContentDrawerApi.open();
inContentDrawerApi.setState({ class: 'w-full', placement: 'right' }).open();
}
function openAutoHeightDrawer() {
@@ -71,24 +72,25 @@ function openDynamicDrawer() {
}
function handleUpdateTitle() {
dynamicDrawerApi.setState({ title: '外部动态标题' });
dynamicDrawerApi.open();
dynamicDrawerApi.setState({ title: '外部动态标题' }).open();
}
function openSharedDrawer() {
sharedDrawerApi.setData({
content: '外部传递的数据 content',
payload: '外部传递的数据 payload',
});
sharedDrawerApi.open();
sharedDrawerApi
.setData({
content: '外部传递的数据 content',
payload: '外部传递的数据 payload',
})
.open();
}
function openFormDrawer() {
formDrawerApi.setData({
// 表单值
values: { field1: 'abc', field2: '123' },
});
formDrawerApi.open();
formDrawerApi
.setData({
// 表单值
values: { field1: 'abc', field2: '123' },
})
.open();
}
</script>
@@ -126,6 +128,9 @@ function openFormDrawer() {
<Button class="mb-2 ml-2" type="primary" @click="openBaseDrawer('top')">
顶部打开
</Button>
<Button class="mb-2 ml-2" type="primary" @click="openBlurDrawer">
遮罩层模糊效果
</Button>
</Card>
<Card class="mb-4" title="在内容区域打开">

View File

@@ -0,0 +1,23 @@
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { Slider } from 'ant-design-vue';
const blur = ref(5);
const [Modal, modalApi] = useVbenModal({
overlayBlur: blur.value,
});
watch(blur, (val) => {
modalApi.setState({
overlayBlur: val,
});
});
</script>
<template>
<Modal title="遮罩层模糊">
<p>调整滑块来改变遮罩层模糊程度{{ blur }}</p>
<Slider v-model:value="blur" :max="30" :min="0" />
</Modal>
</template>

View File

@@ -1,15 +1,17 @@
<script lang="ts" setup>
import { Page, useVbenModal } from '@vben/common-ui';
import { Button, Card } from 'ant-design-vue';
import { Button, Card, Flex } from 'ant-design-vue';
import DocButton from '../doc-button.vue';
import AutoHeightDemo from './auto-height-demo.vue';
import BaseDemo from './base-demo.vue';
import BlurDemo from './blur-demo.vue';
import DragDemo from './drag-demo.vue';
import DynamicDemo from './dynamic-demo.vue';
import FormModalDemo from './form-modal-demo.vue';
import InContentModalDemo from './in-content-demo.vue';
import NestedDemo from './nested-demo.vue';
import SharedDataDemo from './shared-data-demo.vue';
const [BaseModal, baseModalApi] = useVbenModal({
@@ -42,6 +44,14 @@ const [FormModal, formModalApi] = useVbenModal({
connectedComponent: FormModalDemo,
});
const [NestedModal, nestedModalApi] = useVbenModal({
connectedComponent: NestedDemo,
});
const [BlurModal, blurModalApi] = useVbenModal({
connectedComponent: BlurDemo,
});
function openBaseModal() {
baseModalApi.open();
}
@@ -63,24 +73,33 @@ function openDynamicModal() {
}
function openSharedModal() {
sharedModalApi.setData({
content: '外部传递的数据 content',
payload: '外部传递的数据 payload',
});
sharedModalApi.open();
sharedModalApi
.setData({
content: '外部传递的数据 content',
payload: '外部传递的数据 payload',
})
.open();
}
function openNestedModal() {
nestedModalApi.open();
}
function openBlurModal() {
blurModalApi.open();
}
function handleUpdateTitle() {
dynamicModalApi.setState({ title: '外部动态标题' });
dynamicModalApi.open();
dynamicModalApi.setState({ title: '外部动态标题' }).open();
}
function openFormModal() {
formModalApi.setData({
// 表单值
values: { field1: 'abc' },
});
formModalApi.open();
formModalApi
.setData({
// 表单值
values: { field1: 'abc' },
})
.open();
}
</script>
@@ -100,44 +119,80 @@ function openFormModal() {
<DynamicModal />
<SharedDataModal />
<FormModal />
<Card class="mb-4" title="基本使用">
<p class="mb-3">一个基础的弹窗示例</p>
<Button type="primary" @click="openBaseModal">打开弹窗</Button>
</Card>
<NestedModal />
<BlurModal />
<Flex wrap="wrap" class="w-full" gap="10">
<Card class="w-[300px]" title="基本使用">
<p>一个基础的弹窗示例</p>
<template #actions>
<Button type="primary" @click="openBaseModal">打开弹窗</Button>
</template>
</Card>
<Card class="mb-4" title="指定容器">
<p class="mb-3">在内容区域打开弹窗的示例</p>
<Button type="primary" @click="openInContentModal">打开弹窗</Button>
</Card>
<Card class="w-[300px]" title="指定容器">
<p>在内容区域打开弹窗的示例</p>
<template #actions>
<Button type="primary" @click="openInContentModal">打开弹窗</Button>
</template>
</Card>
<Card class="mb-4" title="内容高度自适应">
<p class="mb-3">可根据内容并自动调整高度</p>
<Button type="primary" @click="openAutoHeightModal">打开弹窗</Button>
</Card>
<Card class="w-[300px]" title="内容高度自适应">
<p>可根据内容并自动调整高度</p>
<template #actions>
<Button type="primary" @click="openAutoHeightModal">
打开弹窗
</Button>
</template>
</Card>
<Card class="mb-4" title="可拖拽示例">
<p class="mb-3">配置 draggable 可开启拖拽功能</p>
<Button type="primary" @click="openDragModal">打开弹窗</Button>
</Card>
<Card class="w-[300px]" title="可拖拽示例">
<p>配置 draggable 可开启拖拽功能</p>
<template #actions>
<Button type="primary" @click="openDragModal"> 打开弹窗 </Button>
</template>
</Card>
<Card class="mb-4" title="动态配置示例">
<p class="mb-3">通过 setState 动态调整弹窗数据</p>
<Button type="primary" @click="openDynamicModal">打开弹窗</Button>
<Button class="ml-2" type="primary" @click="handleUpdateTitle">
从外部修改标题并打开
</Button>
</Card>
<Card class="w-[300px]" title="动态配置示例">
<p>通过 setState 动态调整弹窗数据</p>
<template #extra>
<Button type="link" @click="openDynamicModal">打开弹窗</Button>
</template>
<template #actions>
<Button type="primary" @click="handleUpdateTitle">
外部修改标题并打开
</Button>
</template>
</Card>
<Card class="mb-4" title="内外数据共享示例">
<p class="mb-3">通过共享 sharedData 来进行数据交互</p>
<Button type="primary" @click="openSharedModal">
打开弹窗并传递数据
</Button>
</Card>
<Card class="w-[300px]" title="内外数据共享示例">
<p>通过共享 sharedData 来进行数据交互</p>
<template #actions>
<Button type="primary" @click="openSharedModal">
打开弹窗并传递数据
</Button>
</template>
</Card>
<Card class="mb-4" title="表单弹窗示例">
<p class="mb-3">弹窗与表单结合</p>
<Button type="primary" @click="openFormModal"> 打开弹窗 </Button>
</Card>
<Card class="w-[300px]" title="表单弹窗示例">
<p>弹窗与表单结合</p>
<template #actions>
<Button type="primary" @click="openFormModal"> 打开表单弹窗 </Button>
</template>
</Card>
<Card class="w-[300px]" title="嵌套弹窗示例">
<p>在已经打开的弹窗中再次打开弹窗</p>
<template #actions>
<Button type="primary" @click="openNestedModal">打开嵌套弹窗</Button>
</template>
</Card>
<Card class="w-[300px]" title="遮罩模糊示例">
<p>遮罩层应用类似毛玻璃的模糊效果</p>
<template #actions>
<Button type="primary" @click="openBlurModal">打开弹窗</Button>
</template>
</Card>
</Flex>
</Page>
</template>

View File

@@ -0,0 +1,24 @@
<script lang="ts" setup>
import { useVbenModal } from '@vben/common-ui';
import { Button } from 'ant-design-vue';
import DragDemo from './drag-demo.vue';
const [Modal] = useVbenModal({
destroyOnClose: true,
});
const [BaseModal, baseModalApi] = useVbenModal({
connectedComponent: DragDemo,
});
function openNestedModal() {
baseModalApi.open();
}
</script>
<template>
<Modal title="嵌套弹窗示例">
<Button @click="openNestedModal" type="primary">打开子弹窗</Button>
<BaseModal />
</Modal>
</template>

View File

@@ -0,0 +1,303 @@
<script lang="ts" setup>
import type { TippyProps } from '@vben/common-ui';
import { reactive } from 'vue';
import { Page, Tippy } from '@vben/common-ui';
import { Button, Card, Flex } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
const tippyProps = reactive<TippyProps>({
animation: 'shift-away',
arrow: true,
content: '这是一个提示',
delay: [200, 200],
duration: 200,
followCursor: false,
hideOnClick: false,
inertia: true,
maxWidth: 'none',
placement: 'top',
theme: 'dark',
trigger: 'mouseenter focusin',
});
function parseBoolean(value: string) {
switch (value) {
case 'false': {
return false;
}
case 'true': {
return true;
}
default: {
return value;
}
}
}
const [Form] = useVbenForm({
handleValuesChange(values) {
Object.assign(tippyProps, {
...values,
delay: [values.delay1, values.delay2],
followCursor: parseBoolean(values.followCursor),
hideOnClick: parseBoolean(values.hideOnClick),
trigger: values.trigger.join(' '),
});
},
schema: [
{
component: 'RadioGroup',
componentProps: {
buttonStyle: 'solid',
class: 'w-full',
options: [
{ label: '自动', value: 'auto' },
{ label: '暗色', value: 'dark' },
{ label: '亮色', value: 'light' },
],
optionType: 'button',
},
defaultValue: tippyProps.theme,
fieldName: 'theme',
label: '主题',
},
{
component: 'Select',
componentProps: {
class: 'w-full',
options: [
{ label: '向上滑入', value: 'shift-away' },
{ label: '向下滑入', value: 'shift-toward' },
{ label: '缩放', value: 'scale' },
{ label: '透视', value: 'perspective' },
{ label: '淡入', value: 'fade' },
],
},
defaultValue: tippyProps.animation,
fieldName: 'animation',
label: '动画类型',
},
{
component: 'RadioGroup',
componentProps: {
buttonStyle: 'solid',
options: [
{ label: '是', value: true },
{ label: '否', value: false },
],
optionType: 'button',
},
defaultValue: tippyProps.inertia,
fieldName: 'inertia',
label: '动画惯性',
},
{
component: 'Select',
componentProps: {
class: 'w-full',
options: [
{ label: '顶部', value: 'top' },
{ label: '顶左', value: 'top-start' },
{ label: '顶右', value: 'top-end' },
{ label: '底部', value: 'bottom' },
{ label: '底左', value: 'bottom-start' },
{ label: '底右', value: 'bottom-end' },
{ label: '左侧', value: 'left' },
{ label: '左上', value: 'left-start' },
{ label: '左下', value: 'left-end' },
{ label: '右侧', value: 'right' },
{ label: '右上', value: 'right-start' },
{ label: '右下', value: 'right-end' },
],
},
defaultValue: tippyProps.placement,
fieldName: 'placement',
label: '位置',
},
{
component: 'InputNumber',
componentProps: {
addonAfter: '毫秒',
},
defaultValue: tippyProps.duration,
fieldName: 'duration',
label: '动画时长',
},
{
component: 'InputNumber',
componentProps: {
addonAfter: '毫秒',
},
defaultValue: 100,
fieldName: 'delay1',
label: '显示延时',
},
{
component: 'InputNumber',
componentProps: {
addonAfter: '毫秒',
},
defaultValue: 100,
fieldName: 'delay2',
label: '隐藏延时',
},
{
component: 'Input',
defaultValue: tippyProps.content,
fieldName: 'content',
label: '内容',
},
{
component: 'RadioGroup',
componentProps: {
buttonStyle: 'solid',
options: [
{ label: '是', value: true },
{ label: '否', value: false },
],
optionType: 'button',
},
defaultValue: tippyProps.arrow,
fieldName: 'arrow',
label: '指示箭头',
},
{
component: 'Select',
componentProps: {
class: 'w-full',
options: [
{ label: '不跟随', value: 'false' },
{ label: '完全跟随', value: 'true' },
{ label: '仅横向', value: 'horizontal' },
{ label: '仅纵向', value: 'vertical' },
{ label: '仅初始', value: 'initial' },
],
},
defaultValue: tippyProps.followCursor?.toString(),
fieldName: 'followCursor',
label: '跟随指针',
},
{
component: 'Select',
componentProps: {
class: 'w-full',
mode: 'multiple',
options: [
{ label: '鼠标移入', value: 'mouseenter' },
{ label: '被点击', value: 'click' },
{ label: '获得焦点', value: 'focusin' },
{ label: '无触发,仅手动', value: 'manual' },
],
},
defaultValue: tippyProps.trigger?.split(' '),
fieldName: 'trigger',
label: '触发方式',
},
{
component: 'Select',
componentProps: {
class: 'w-full',
options: [
{ label: '否', value: 'false' },
{ label: '是', value: 'true' },
{ label: '仅内部', value: 'toggle' },
],
},
defaultValue: tippyProps.hideOnClick?.toString(),
dependencies: {
componentProps(_, formAction) {
return {
disabled: !formAction.values.trigger.includes('click'),
};
},
triggerFields: ['trigger'],
},
fieldName: 'hideOnClick',
help: '只有在触发方式为`click`时才有效',
label: '点击后隐藏',
},
{
component: 'Input',
componentProps: {
allowClear: true,
placeholder: 'none、200px',
},
defaultValue: tippyProps.maxWidth,
fieldName: 'maxWidth',
label: '最大宽度',
},
],
showDefaultActions: false,
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
});
function goDoc() {
window.open('https://atomiks.github.io/tippyjs/v6/all-props/');
}
</script>
<template>
<Page title="Tippy">
<template #description>
<div class="flex items-center">
<p>
Tippy
是一个轻量级的提示工具库它可以用来创建各种交互式提示如工具提示引导提示等
</p>
<Button type="link" size="small" @click="goDoc">查看文档</Button>
</div>
</template>
<Card title="指令形式使用">
<p class="mb-4">
指令形式使用比较简洁直接在需要展示tooltip的组件上用v-tippy传递配置适用于固定内容的工具提示
</p>
<Flex warp="warp" gap="20">
<Button v-tippy="'这是一个提示,使用了默认的配置'">默认配置</Button>
<Button
v-tippy="{ theme: 'light', content: '这是一个提示总是light主题' }"
>
指定主题
</Button>
<Button
v-tippy="{
theme: 'light',
content: '这个提示将在点燃组件100毫秒后激活',
delay: 100,
}"
>
指定延时
</Button>
<Button
v-tippy="{
content: '本提示的动画为`scale`',
animation: 'scale',
}"
>
指定动画
</Button>
</Flex>
</Card>
<Card title="组件形式使用" class="mt-4">
<div class="flex w-full justify-center">
<Tippy v-bind="tippyProps">
<Button>鼠标移到这个组件上来体验效果</Button>
</Tippy>
</div>
<Form class="mt-4" />
<template #actions>
<p
class="text-secondary-foreground hover:text-secondary-foreground cursor-default"
>
更多配置请
<Button type="link" size="small" @click="goDoc">查看文档</Button>
这里只列出了一些常用的配置
</p>
</template>
</Card>
</Page>
</template>