docs: update docs (#4466)
* chore: fixed known issues with form components * docs: add vben form docpull/48/MERGE
parent
dac80703d9
commit
5ce3a18785
|
@ -27,13 +27,13 @@ import {
|
|||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Textarea,
|
||||
TimePicker,
|
||||
TreeSelect,
|
||||
Upload,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
// 业务表单组件适配
|
||||
|
||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
||||
export type FormComponentType =
|
||||
| 'AutoComplete'
|
||||
| 'Checkbox'
|
||||
|
@ -51,6 +51,7 @@ export type FormComponentType =
|
|||
| 'Select'
|
||||
| 'Space'
|
||||
| 'Switch'
|
||||
| 'Textarea'
|
||||
| 'TimePicker'
|
||||
| 'TreeSelect'
|
||||
| 'Upload'
|
||||
|
@ -83,12 +84,16 @@ setupVbenForm<FormComponentType>({
|
|||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Textarea,
|
||||
TimePicker,
|
||||
TreeSelect,
|
||||
Upload,
|
||||
},
|
||||
config: {
|
||||
// ant design vue组件库默认都是 v-model:value
|
||||
baseModelPropName: 'value',
|
||||
|
||||
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
|
@ -97,12 +102,14 @@ setupVbenForm<FormComponentType>({
|
|||
},
|
||||
},
|
||||
defineRules: {
|
||||
// 输入项目必填国际化适配
|
||||
required: (value, _params, ctx) => {
|
||||
if ((!value && value !== 0) || value.length === 0) {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// 选择项目必填国际化适配
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('formRules.selectRequired', [ctx.label]);
|
||||
|
|
|
@ -73,7 +73,7 @@ setupVbenForm<FormComponentType>({
|
|||
},
|
||||
defineRules: {
|
||||
required: (value, _params, ctx) => {
|
||||
if ((!value && value !== 0) || value.length === 0) {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -82,7 +82,7 @@ setupVbenForm<FormComponentType>({
|
|||
},
|
||||
defineRules: {
|
||||
required: (value, _params, ctx) => {
|
||||
if ((!value && value !== 0) || value.length === 0) {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -24,14 +24,16 @@ const parsedFiles = computed(() => {
|
|||
class="not-prose relative w-full overflow-x-auto rounded-t-lg px-4 py-6"
|
||||
>
|
||||
<div class="flex w-full max-w-[700px] px-2">
|
||||
<slot v-if="parsedFiles.length > 0"></slot>
|
||||
<div v-else class="text-destructive text-sm">
|
||||
<span class="bg-destructive text-foreground rounded-sm px-1 py-1">
|
||||
ERROR:
|
||||
</span>
|
||||
The preview directory does not exist. Please check the 'dir'
|
||||
parameter.
|
||||
</div>
|
||||
<ClientOnly>
|
||||
<slot v-if="parsedFiles.length > 0"></slot>
|
||||
<div v-else class="text-destructive text-sm">
|
||||
<span class="bg-destructive text-foreground rounded-sm px-1 py-1">
|
||||
ERROR:
|
||||
</span>
|
||||
The preview directory does not exist. Please check the 'dir'
|
||||
parameter.
|
||||
</div>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</div>
|
||||
<PreviewGroup v-if="parsedFiles.length > 0" :files="parsedFiles">
|
||||
|
|
|
@ -154,15 +154,19 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] {
|
|||
items: [
|
||||
{
|
||||
link: 'common-ui/vben-modal',
|
||||
text: 'Vben Modal 模态框',
|
||||
text: 'Modal 模态框',
|
||||
},
|
||||
{
|
||||
link: 'common-ui/vben-drawer',
|
||||
text: 'Vben Drawer 抽屉',
|
||||
text: 'Drawer 抽屉',
|
||||
},
|
||||
{
|
||||
link: 'common-ui/vben-form',
|
||||
text: 'Vben Form 表单',
|
||||
text: 'Form 表单',
|
||||
},
|
||||
{
|
||||
link: 'common-ui/vben-count-to-animator',
|
||||
text: 'CountToAnimator 数字动画',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -1,30 +1,92 @@
|
|||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted, watch } from 'vue';
|
||||
import {
|
||||
computed,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
watch,
|
||||
} from 'vue';
|
||||
|
||||
// import { useAntdDesignTokens } from '@vben/hooks';
|
||||
// import { initPreferences } from '@vben/preferences';
|
||||
|
||||
import { ConfigProvider, theme } from 'ant-design-vue';
|
||||
import mediumZoom from 'medium-zoom';
|
||||
import { useRoute } from 'vitepress';
|
||||
import DefaultTheme from 'vitepress/theme';
|
||||
|
||||
const { Layout } = DefaultTheme;
|
||||
const route = useRoute();
|
||||
// const { tokens } = useAntdDesignTokens();
|
||||
|
||||
const initZoom = () => {
|
||||
// mediumZoom('[data-zoomable]', { background: 'var(--vp-c-bg)' });
|
||||
mediumZoom('.VPContent img', { background: 'var(--vp-c-bg)' });
|
||||
};
|
||||
|
||||
const isDark = ref(true);
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
() => nextTick(() => initZoom()),
|
||||
);
|
||||
|
||||
// initPreferences({
|
||||
// namespace: 'docs',
|
||||
// });
|
||||
|
||||
onMounted(() => {
|
||||
initZoom();
|
||||
});
|
||||
|
||||
// 使用该函数
|
||||
const observer = watchDarkModeChange((dark) => {
|
||||
isDark.value = dark;
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
observer?.disconnect();
|
||||
});
|
||||
|
||||
function watchDarkModeChange(callback: (isDark: boolean) => void) {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
const htmlElement = document.documentElement;
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
const isDark = htmlElement.classList.contains('dark');
|
||||
callback(isDark);
|
||||
});
|
||||
|
||||
observer.observe(htmlElement, {
|
||||
attributeFilter: ['class'],
|
||||
attributes: true,
|
||||
});
|
||||
|
||||
const initialIsDark = htmlElement.classList.contains('dark');
|
||||
callback(initialIsDark);
|
||||
|
||||
return observer;
|
||||
}
|
||||
|
||||
const tokenTheme = computed(() => {
|
||||
const algorithm = isDark.value
|
||||
? [theme.darkAlgorithm]
|
||||
: [theme.defaultAlgorithm];
|
||||
|
||||
return {
|
||||
algorithm,
|
||||
// token: tokens,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Layout />
|
||||
<ConfigProvider :theme="tokenTheme">
|
||||
<Layout />
|
||||
</ConfigProvider>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -5,3 +5,7 @@ html.dark {
|
|||
.dark .VPContent {
|
||||
/* background-color: #14161a; */
|
||||
}
|
||||
|
||||
.form-valid-error p {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -7,10 +7,17 @@
|
|||
"dev": "vitepress dev",
|
||||
"docs:preview": "vitepress preview"
|
||||
},
|
||||
"imports": {
|
||||
"#/*": "./src/_env/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vben-core/shadcn-ui": "workspace:*",
|
||||
"@vben/common-ui": "workspace:*",
|
||||
"@vben/hooks": "workspace:*",
|
||||
"@vben/locales": "workspace:*",
|
||||
"@vben/preferences": "workspace:*",
|
||||
"@vben/styles": "workspace:*",
|
||||
"ant-design-vue": "catalog:",
|
||||
"lucide-vue-next": "catalog:",
|
||||
"medium-zoom": "catalog:",
|
||||
"radix-vue": "catalog:",
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
import type {
|
||||
BaseFormComponentType,
|
||||
VbenFormSchema as FormSchema,
|
||||
VbenFormProps,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
AutoComplete,
|
||||
Button,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
DatePicker,
|
||||
Divider,
|
||||
Input,
|
||||
InputNumber,
|
||||
InputPassword,
|
||||
Mentions,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
RangePicker,
|
||||
Rate,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Textarea,
|
||||
TimePicker,
|
||||
TreeSelect,
|
||||
Upload,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
||||
export type FormComponentType =
|
||||
| 'AutoComplete'
|
||||
| 'Checkbox'
|
||||
| 'CheckboxGroup'
|
||||
| 'DatePicker'
|
||||
| 'Divider'
|
||||
| 'Input'
|
||||
| 'InputNumber'
|
||||
| 'InputPassword'
|
||||
| 'Mentions'
|
||||
| 'Radio'
|
||||
| 'RadioGroup'
|
||||
| 'RangePicker'
|
||||
| 'Rate'
|
||||
| 'Select'
|
||||
| 'Space'
|
||||
| 'Switch'
|
||||
| 'Textarea'
|
||||
| 'TimePicker'
|
||||
| 'TreeSelect'
|
||||
| 'Upload'
|
||||
| BaseFormComponentType;
|
||||
|
||||
// 初始化表单组件,并注册到form组件内部
|
||||
setupVbenForm<FormComponentType>({
|
||||
components: {
|
||||
AutoComplete,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
DatePicker,
|
||||
// 自定义默认的重置按钮
|
||||
DefaultResetActionButton: (props, { attrs, slots }) => {
|
||||
return h(Button, { ...props, attrs, type: 'default' }, slots);
|
||||
},
|
||||
// 自定义默认的提交按钮
|
||||
DefaultSubmitActionButton: (props, { attrs, slots }) => {
|
||||
return h(Button, { ...props, attrs, type: 'primary' }, slots);
|
||||
},
|
||||
Divider,
|
||||
Input,
|
||||
InputNumber,
|
||||
InputPassword,
|
||||
Mentions,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
RangePicker,
|
||||
Rate,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Textarea,
|
||||
TimePicker,
|
||||
TreeSelect,
|
||||
Upload,
|
||||
},
|
||||
config: {
|
||||
// ant design vue组件库默认都是 v-model:value
|
||||
baseModelPropName: 'value',
|
||||
|
||||
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
Switch: 'checked',
|
||||
Upload: 'fileList',
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
// 输入项目必填国际化适配
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// 选择项目必填国际化适配
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('formRules.selectRequired', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const useVbenForm = useForm<FormComponentType>;
|
||||
|
||||
export { useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<FormComponentType>;
|
||||
export type { VbenFormProps };
|
|
@ -0,0 +1 @@
|
|||
export * from './form';
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Vben CountToAnimator 数字动画
|
||||
|
||||
框架提供的数字动画组件,支持数字动画效果。
|
||||
|
||||
> 如果文档内没有参数说明,可以尝试在在线示例内寻找
|
||||
|
||||
::: info 写在前面
|
||||
|
||||
如果你觉得现有组件的封装不够理想,或者不完全符合你的需求,大可以直接使用原生组件,亦或亲手封装一个适合的组件。框架提供的组件并非束缚,使用与否,完全取决于你的需求与自由。
|
||||
|
||||
:::
|
||||
|
||||
## 基础用法
|
||||
|
||||
通过 `start-val` 和 `end-val`设置数字动画的开始值和结束值, 持续时间`3000`ms。
|
||||
|
||||
<DemoPreview dir="demos/vben-count-to-animator/basic" />
|
||||
|
||||
## 自定义前缀及分隔符
|
||||
|
||||
通过 `prefix` 和 `separator` 设置数字动画的前缀和分隔符。
|
||||
|
||||
<DemoPreview dir="demos/vben-count-to-animator/custom" />
|
||||
|
||||
### Props
|
||||
|
||||
| 属性名 | 描述 | 类型 | 默认值 |
|
||||
| ---------- | -------------- | --------- | -------- |
|
||||
| startVal | 起始值 | `number` | `0` |
|
||||
| endVal | 结束值 | `number` | `2021` |
|
||||
| duration | 动画持续时间 | `number` | `1500` |
|
||||
| autoplay | 自动执行 | `boolean` | `true` |
|
||||
| prefix | 前缀 | `string` | - |
|
||||
| suffix | 后缀 | `string` | - |
|
||||
| separator | 分隔符 | `string` | `,` |
|
||||
| color | 字体颜色 | `string` | - |
|
||||
| useEasing | 是否开启动画 | `boolean` | `true` |
|
||||
| transition | 动画效果 | `string` | `linear` |
|
||||
| decimals | 保留小数点位数 | `number` | `0` |
|
||||
|
||||
### Methods
|
||||
|
||||
以下事件,只有在 `useVbenModal({onCancel:()=>{}})` 中传入才会生效。
|
||||
|
||||
| 事件名 | 描述 | 类型 |
|
||||
| ------ | ------------ | ---------- |
|
||||
| start | 开始执行动画 | `()=>void` |
|
||||
| reset | 重置 | `()=>void` |
|
|
@ -4,8 +4,441 @@ outline: deep
|
|||
|
||||
# Vben Form 表单
|
||||
|
||||
框架提供的表单组件,可适配 `Element Plus`、`Ant Design Vue`、`Naive`UI 框架。
|
||||
框架提供的表单组件,可适配 `Element Plus`、`Ant Design Vue`、`Naive UI` 等框架。
|
||||
|
||||
# 使用
|
||||
> 如果文档内没有参数说明,可以尝试在在线示例内寻找
|
||||
|
||||
TODO
|
||||
::: info 写在前面
|
||||
|
||||
如果你觉得现有组件的封装不够理想,或者不完全符合你的需求,大可以直接使用原生组件,亦或亲手封装一个适合的组件。框架提供的组件并非束缚,使用与否,完全取决于你的需求与自由。
|
||||
|
||||
:::
|
||||
|
||||
## 适配器
|
||||
|
||||
表单底层使用 [vee-validate](https://vee-validate.logaretm.com/v4/) 进行表单验证,所以你可以使用 `vee-validate` 的所有功能。对于不同的 UI 框架,我们提供了适配器,以便更好的适配不同的 UI 框架。
|
||||
|
||||
### 适配器说明
|
||||
|
||||
每个应用都有不同的 UI 框架,所以在应用的 `src/adapter/form` 内部,你可以根据自己的需求,进行组件适配。下面是 `Ant Design Vue` 的适配器示例代码,可根据注释查看说明:
|
||||
|
||||
::: details ant design 适配器
|
||||
|
||||
```ts
|
||||
import type {
|
||||
BaseFormComponentType,
|
||||
VbenFormSchema as FormSchema,
|
||||
VbenFormProps,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import {
|
||||
AutoComplete,
|
||||
Button,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
DatePicker,
|
||||
Divider,
|
||||
Input,
|
||||
InputNumber,
|
||||
InputPassword,
|
||||
Mentions,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
RangePicker,
|
||||
Rate,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Textarea,
|
||||
TimePicker,
|
||||
TreeSelect,
|
||||
Upload,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
||||
export type FormComponentType =
|
||||
| 'AutoComplete'
|
||||
| 'Checkbox'
|
||||
| 'CheckboxGroup'
|
||||
| 'DatePicker'
|
||||
| 'Divider'
|
||||
| 'Input'
|
||||
| 'InputNumber'
|
||||
| 'InputPassword'
|
||||
| 'Mentions'
|
||||
| 'Radio'
|
||||
| 'RadioGroup'
|
||||
| 'RangePicker'
|
||||
| 'Rate'
|
||||
| 'Select'
|
||||
| 'Space'
|
||||
| 'Switch'
|
||||
| 'Textarea'
|
||||
| 'TimePicker'
|
||||
| 'TreeSelect'
|
||||
| 'Upload'
|
||||
| BaseFormComponentType;
|
||||
|
||||
// 初始化表单组件,并注册到form组件内部
|
||||
setupVbenForm<FormComponentType>({
|
||||
components: {
|
||||
AutoComplete,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
DatePicker,
|
||||
// 自定义默认的重置按钮
|
||||
DefaultResetActionButton: (props, { attrs, slots }) => {
|
||||
return h(Button, { ...props, attrs, type: 'default' }, slots);
|
||||
},
|
||||
// 自定义默认的提交按钮
|
||||
DefaultSubmitActionButton: (props, { attrs, slots }) => {
|
||||
return h(Button, { ...props, attrs, type: 'primary' }, slots);
|
||||
},
|
||||
Divider,
|
||||
Input,
|
||||
InputNumber,
|
||||
InputPassword,
|
||||
Mentions,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
RangePicker,
|
||||
Rate,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Textarea,
|
||||
TimePicker,
|
||||
TreeSelect,
|
||||
Upload,
|
||||
},
|
||||
config: {
|
||||
// ant design vue组件库默认都是 v-model:value
|
||||
baseModelPropName: 'value',
|
||||
|
||||
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
Switch: 'checked',
|
||||
Upload: 'fileList',
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
// 输入项目必填国际化适配
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// 选择项目必填国际化适配
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('formRules.selectRequired', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const useVbenForm = useForm<FormComponentType>;
|
||||
|
||||
export { useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<FormComponentType>;
|
||||
export type { VbenFormProps };
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 基础用法
|
||||
|
||||
::: tip README
|
||||
|
||||
下方示例代码中的,存在一些国际化、主题色未适配问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。
|
||||
|
||||
:::
|
||||
|
||||
使用 `useVbenForm` 创建最基础的表单。
|
||||
|
||||
<DemoPreview dir="demos/vben-form/basic" />
|
||||
|
||||
## 查询表单
|
||||
|
||||
查询表单是一种特殊的表单,用于查询数据。查询表单不会触发表单验证,只会触发查询事件。
|
||||
|
||||
<DemoPreview dir="demos/vben-form/query" />
|
||||
|
||||
## 表单校验
|
||||
|
||||
表单校验是一个非常重要的功能,可以通过 `rules` 属性进行校验。
|
||||
|
||||
<DemoPreview dir="demos/vben-form/rules" />
|
||||
|
||||
## 表单联动
|
||||
|
||||
表单联动是一个非常常见的功能,可以通过 `dependencies` 属性进行联动。
|
||||
|
||||
_注意_ 需要指定 `dependencies` 的 `triggerFields` 属性,设置由谁的改动来触发,以便表单组件能够正确的联动。
|
||||
|
||||
<DemoPreview dir="demos/vben-form/dynamic" />
|
||||
|
||||
## 自定义组件
|
||||
|
||||
如果你的业务组件库没有提供某个组件,你可以自行封装一个组件,然后加到表单内部。
|
||||
|
||||
<DemoPreview dir="demos/vben-form/custom" />
|
||||
|
||||
## 操作
|
||||
|
||||
一些常见的表单操作。
|
||||
|
||||
<DemoPreview dir="demos/vben-form/api" />
|
||||
|
||||
## API
|
||||
|
||||
`useVbenForm` 返回一个数组,第一个元素是表单组件,第二个元素是表单的方法。
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { useVbenForm } from '#/adapter';
|
||||
|
||||
// Form 为弹窗组件
|
||||
// formApi 为弹窗的方法
|
||||
const [Form, formApi] = useVbenForm({
|
||||
// 属性
|
||||
// 事件
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form />
|
||||
</template>
|
||||
```
|
||||
|
||||
### FormApi
|
||||
|
||||
useVbenForm 返回的第二个参数,是一个对象,包含了一些表单的方法。
|
||||
|
||||
| 方法名 | 描述 | 类型 |
|
||||
| --- | --- | --- |
|
||||
| submitForm | 提交表单 | `(e:Event)=>Promise<Record<string,any>>` |
|
||||
| resetForm | 重置表单 | `()=>Promise<void>` |
|
||||
| setValues | 设置表单值 | `()=>Promise<Record<string,any>>` |
|
||||
| getValues | 获取表单值 | `(fields:Record<string, any>,shouldValidate: boolean = false)=>Promise<void>` |
|
||||
| validate | 表单校验 | `()=>Promise<void>` |
|
||||
| resetValidate | 重置表单校验 | `()=>Promise<void>` |
|
||||
| updateSchema | 更新formSchema | `(schema:FormSchema[])=>void` |
|
||||
| setFieldValue | 设置字段值 | `(field: string, value: any, shouldValidate?: boolean)=>Promise<void>` |
|
||||
| setState | 设置组件状态(props) | `(stateOrFn:\| ((prev: VbenFormProps) => Partial<VbenFormProps>)\| Partial<VbenFormProps>)=>Promise<void>` |
|
||||
| getState | 获取组件状态(props) | `()=>Promise<VbenFormProps>` |
|
||||
| form | 表单对象实例,可以操作表单,见 [useForm](https://vee-validate.logaretm.com/v4/api/use-form/) | - |
|
||||
|
||||
## Props
|
||||
|
||||
所有属性都可以传入 `useVbenForm` 的第一个参数中。
|
||||
|
||||
| 属性名 | 描述 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| layout | 表单项布局 | `'horizontal' \| 'vertical'` | `horizontal` |
|
||||
| showCollapseButton | 是否显示折叠按钮 | `boolean` | `false` |
|
||||
| wrapperClass | 表单的布局,基于tailwindcss | `any` | - |
|
||||
| actionWrapperClass | 表单操作区域class | `any` | - |
|
||||
| handleReset | 表单重置回调 | `(values: Record<string, any>,) => Promise<void> \| void` | - |
|
||||
| handleSubmit | 表单提交回调 | `(values: Record<string, any>,) => Promise<void> \| void` | - |
|
||||
| resetButtonOptions | 重置按钮组件参数 | `ActionButtonOptions` | - |
|
||||
| submitButtonOptions | 提交按钮组件参数 | `ActionButtonOptions` | - |
|
||||
| showDefaultActions | 是否显示默认操作按钮 | `boolean` | `true` |
|
||||
| collapsed | 是否折叠,在`是否展开,在showCollapseButton=true`时生效 | `boolean` | `false` |
|
||||
| collapsedRows | 折叠时保持的行数 | `number` | `1` |
|
||||
| commonConfig | 表单项的通用配置,每个配置都会传递到每个表单项,表单项可覆盖 | `FormCommonConfig` | - |
|
||||
| schema | 表单项的每一项配置 | `FormSchema` | - |
|
||||
|
||||
### TS 类型说明
|
||||
|
||||
::: details ActionButtonOptions
|
||||
|
||||
```ts
|
||||
export interface ActionButtonOptions {
|
||||
/** 样式 */
|
||||
class?: any;
|
||||
/** 是否禁用 */
|
||||
disabled?: boolean;
|
||||
/** 是否加载中 */
|
||||
loading?: boolean;
|
||||
/** 按钮大小 */
|
||||
size?: ButtonVariantSize;
|
||||
/** 按钮类型 */
|
||||
variant?: ButtonVariants;
|
||||
/** 是否显示 */
|
||||
show?: boolean;
|
||||
/** 按钮文本 */
|
||||
text?: string;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: details FormCommonConfig
|
||||
|
||||
```ts
|
||||
export interface FormCommonConfig {
|
||||
/**
|
||||
* 所有表单项的props
|
||||
*/
|
||||
componentProps?: ComponentProps;
|
||||
/**
|
||||
* 所有表单项的控件样式
|
||||
*/
|
||||
controlClass?: string;
|
||||
/**
|
||||
* 所有表单项的禁用状态
|
||||
* @default false
|
||||
*/
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* 所有表单项的控件样式
|
||||
* @default {}
|
||||
*/
|
||||
formFieldProps?: Partial<typeof Field>;
|
||||
/**
|
||||
* 所有表单项的栅格布局
|
||||
* @default ""
|
||||
*/
|
||||
formItemClass?: string;
|
||||
/**
|
||||
* 隐藏所有表单项label
|
||||
* @default false
|
||||
*/
|
||||
hideLabel?: boolean;
|
||||
/**
|
||||
* 是否隐藏必填标记
|
||||
* @default false
|
||||
*/
|
||||
hideRequiredMark?: boolean;
|
||||
/**
|
||||
* 所有表单项的label样式
|
||||
* @default ""
|
||||
*/
|
||||
labelClass?: string;
|
||||
/**
|
||||
* 所有表单项的label宽度
|
||||
*/
|
||||
labelWidth?: number;
|
||||
/**
|
||||
* 所有表单项的wrapper样式
|
||||
*/
|
||||
wrapperClass?: string;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: details FormSchema
|
||||
|
||||
```ts
|
||||
export interface FormSchema<
|
||||
T extends BaseFormComponentType = BaseFormComponentType,
|
||||
> extends FormCommonConfig {
|
||||
/** 组件 */
|
||||
component: Component | T;
|
||||
/** 组件参数 */
|
||||
componentProps?: ComponentProps;
|
||||
/** 默认值 */
|
||||
defaultValue?: any;
|
||||
/** 依赖 */
|
||||
dependencies?: FormItemDependencies;
|
||||
/** 描述 */
|
||||
description?: string;
|
||||
/** 字段名 */
|
||||
fieldName: string;
|
||||
/** 帮助信息 */
|
||||
help?: string;
|
||||
/** 表单项 */
|
||||
label?: string;
|
||||
// 自定义组件内部渲染
|
||||
renderComponentContent?: RenderComponentContentType;
|
||||
/** 字段规则 */
|
||||
rules?: FormSchemaRuleType;
|
||||
/** 后缀 */
|
||||
suffix?: CustomRenderType;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### 表单联动
|
||||
|
||||
表单联动需要通过 schema 内的 `dependencies` 属性进行联动,允许您添加字段之间的依赖项,以根据其他字段的值控制字段。
|
||||
|
||||
```ts
|
||||
dependencies: {
|
||||
// 只有当 name 字段的值变化时,才会触发联动
|
||||
triggerFields: ['name'],
|
||||
// 动态判断当前字段是否需要显示,不显示则直接销毁
|
||||
if(values,formApi){},
|
||||
// 动态判断当前字段是否需要显示,不显示用css隐藏
|
||||
show(values,formApi){},
|
||||
// 动态判断当前字段是否需要禁用
|
||||
disabled(values,formApi){},
|
||||
// 字段变更时,都会触发该函数
|
||||
trigger(values,formApi){},
|
||||
// 动态rules
|
||||
rules(values,formApi){},
|
||||
// 动态必填
|
||||
required(values,formApi){},
|
||||
// 动态组件参数
|
||||
componentProps(values,formApi){},
|
||||
}
|
||||
```
|
||||
|
||||
### 表单校验
|
||||
|
||||
表单联动需要通过 schema 内的 `rules` 属性进行配置。
|
||||
|
||||
rules的值可以是一个字符串,也可以是一个zod的schema。
|
||||
|
||||
#### 字符串
|
||||
|
||||
```ts
|
||||
// 表示字段必填,默认会根据适配器的required进行国际化
|
||||
{
|
||||
rules: 'required';
|
||||
}
|
||||
|
||||
// 表示字段必填,默认会根据适配器的required进行国际化,用于下拉选择之类
|
||||
{
|
||||
rules: 'selectRequired';
|
||||
}
|
||||
```
|
||||
|
||||
#### zod
|
||||
|
||||
rules也支持 zod 的 schema,可以进行更复杂的校验,zod 的使用请查看 [zod文档](https://zod.dev/)。
|
||||
|
||||
```ts
|
||||
import { z } from '#/adapter';
|
||||
|
||||
// 基础类型
|
||||
{
|
||||
rules: z.string().min(1, { message: '请输入字符串' });
|
||||
}
|
||||
|
||||
// 可选,并且携带默认值
|
||||
{
|
||||
rules: z.string().default('默认值').optional(),
|
||||
}
|
||||
|
||||
// 复杂校验
|
||||
{
|
||||
z.string().min(1, { message: "请输入" })
|
||||
.refine((value) => value === "123", {
|
||||
message: "值必须为123",
|
||||
});
|
||||
}
|
||||
```
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { VbenCountToAnimator } from '@vben/common-ui';
|
||||
</script>
|
||||
<template>
|
||||
<VbenCountToAnimator :duration="3000" :end-val="30000" :start-val="1" />
|
||||
</template>
|
|
@ -0,0 +1,12 @@
|
|||
<script lang="ts" setup>
|
||||
import { VbenCountToAnimator } from '@vben/common-ui';
|
||||
</script>
|
||||
<template>
|
||||
<VbenCountToAnimator
|
||||
:duration="3000"
|
||||
:end-val="2000000"
|
||||
:start-val="1"
|
||||
prefix="$"
|
||||
separator="/"
|
||||
/>
|
||||
</template>
|
|
@ -0,0 +1,236 @@
|
|||
<script lang="ts" setup>
|
||||
import { Button, message, Space } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter';
|
||||
|
||||
const [BaseForm, formApi] = useVbenForm({
|
||||
// 所有表单项共用,可单独在表单内覆盖
|
||||
commonConfig: {
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// 使用 tailwindcss grid布局
|
||||
// 提交函数
|
||||
handleSubmit: onSubmit,
|
||||
// 垂直布局,label和input在不同行,值为vertical
|
||||
layout: 'horizontal',
|
||||
// 水平布局,label和input在同一行
|
||||
schema: [
|
||||
{
|
||||
// 组件需要在 #/adapter.ts内注册,并加上类型
|
||||
component: 'Input',
|
||||
// 对应组件的参数
|
||||
componentProps: {
|
||||
placeholder: '请输入用户名',
|
||||
},
|
||||
// 字段名
|
||||
fieldName: 'field1',
|
||||
// 界面显示的label
|
||||
label: 'field1',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: true,
|
||||
options: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '选项2',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
placeholder: '请选择',
|
||||
showSearch: true,
|
||||
},
|
||||
fieldName: 'fieldOptions',
|
||||
label: '下拉选',
|
||||
},
|
||||
],
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-2',
|
||||
});
|
||||
|
||||
function onSubmit(values: Record<string, any>) {
|
||||
message.success({
|
||||
content: `form values: ${JSON.stringify(values)}`,
|
||||
});
|
||||
}
|
||||
|
||||
function handleClick(
|
||||
action:
|
||||
| 'batchAddSchema'
|
||||
| 'batchDeleteSchema'
|
||||
| 'disabled'
|
||||
| 'hiddenAction'
|
||||
| 'hiddenResetButton'
|
||||
| 'hiddenSubmitButton'
|
||||
| 'labelWidth'
|
||||
| 'resetDisabled'
|
||||
| 'resetLabelWidth'
|
||||
| 'showAction'
|
||||
| 'showResetButton'
|
||||
| 'showSubmitButton'
|
||||
| 'updateActionAlign'
|
||||
| 'updateResetButton'
|
||||
| 'updateSchema'
|
||||
| 'updateSubmitButton',
|
||||
) {
|
||||
switch (action) {
|
||||
case 'updateSchema': {
|
||||
formApi.updateSchema([
|
||||
{
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '选项2',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
label: '选项3',
|
||||
value: '3',
|
||||
},
|
||||
],
|
||||
},
|
||||
fieldName: 'fieldOptions',
|
||||
},
|
||||
]);
|
||||
message.success('字段 `fieldOptions` 下拉选项更新成功。');
|
||||
break;
|
||||
}
|
||||
|
||||
case 'labelWidth': {
|
||||
formApi.setState({
|
||||
commonConfig: {
|
||||
labelWidth: 150,
|
||||
},
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'resetLabelWidth': {
|
||||
formApi.setState({
|
||||
commonConfig: {
|
||||
labelWidth: 100,
|
||||
},
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'disabled': {
|
||||
formApi.setState({ commonConfig: { disabled: true } });
|
||||
break;
|
||||
}
|
||||
case 'resetDisabled': {
|
||||
formApi.setState({ commonConfig: { disabled: false } });
|
||||
break;
|
||||
}
|
||||
case 'hiddenAction': {
|
||||
formApi.setState({ showDefaultActions: false });
|
||||
break;
|
||||
}
|
||||
case 'showAction': {
|
||||
formApi.setState({ showDefaultActions: true });
|
||||
break;
|
||||
}
|
||||
case 'hiddenResetButton': {
|
||||
formApi.setState({ resetButtonOptions: { show: false } });
|
||||
break;
|
||||
}
|
||||
case 'showResetButton': {
|
||||
formApi.setState({ resetButtonOptions: { show: true } });
|
||||
break;
|
||||
}
|
||||
case 'hiddenSubmitButton': {
|
||||
formApi.setState({ submitButtonOptions: { show: false } });
|
||||
break;
|
||||
}
|
||||
case 'showSubmitButton': {
|
||||
formApi.setState({ submitButtonOptions: { show: true } });
|
||||
break;
|
||||
}
|
||||
case 'updateResetButton': {
|
||||
formApi.setState({
|
||||
resetButtonOptions: { disabled: true },
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'updateSubmitButton': {
|
||||
formApi.setState({
|
||||
submitButtonOptions: { loading: true },
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'updateActionAlign': {
|
||||
formApi.setState({
|
||||
// 可以自行调整class
|
||||
actionWrapperClass: 'text-center',
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'batchAddSchema': {
|
||||
formApi.setState((prev) => {
|
||||
const currentSchema = prev?.schema ?? [];
|
||||
const newSchema = [];
|
||||
for (let i = 0; i < 2; i++) {
|
||||
newSchema.push({
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: `field${i}${Date.now()}`,
|
||||
label: `field+`,
|
||||
});
|
||||
}
|
||||
return {
|
||||
schema: [...currentSchema, ...newSchema],
|
||||
};
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'batchDeleteSchema': {
|
||||
formApi.setState((prev) => {
|
||||
const currentSchema = prev?.schema ?? [];
|
||||
return {
|
||||
schema: currentSchema.slice(0, -2),
|
||||
};
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Space class="mb-5 flex-wrap">
|
||||
<Button @click="handleClick('updateSchema')">updateSchema</Button>
|
||||
<Button @click="handleClick('labelWidth')">更改labelWidth</Button>
|
||||
<Button @click="handleClick('resetLabelWidth')">还原labelWidth</Button>
|
||||
<Button @click="handleClick('disabled')">禁用表单</Button>
|
||||
<Button @click="handleClick('resetDisabled')">解除禁用</Button>
|
||||
<Button @click="handleClick('hiddenAction')">隐藏操作按钮</Button>
|
||||
<Button @click="handleClick('showAction')">显示操作按钮</Button>
|
||||
<Button @click="handleClick('hiddenResetButton')">隐藏重置按钮</Button>
|
||||
<Button @click="handleClick('showResetButton')">显示重置按钮</Button>
|
||||
<Button @click="handleClick('hiddenSubmitButton')">隐藏提交按钮</Button>
|
||||
<Button @click="handleClick('showSubmitButton')">显示提交按钮</Button>
|
||||
<Button @click="handleClick('updateResetButton')">修改重置按钮</Button>
|
||||
<Button @click="handleClick('updateSubmitButton')">修改提交按钮</Button>
|
||||
<Button @click="handleClick('updateActionAlign')">
|
||||
调整操作按钮位置
|
||||
</Button>
|
||||
<Button @click="handleClick('batchAddSchema')"> 批量添加表单项 </Button>
|
||||
<Button @click="handleClick('batchDeleteSchema')">
|
||||
批量删除表单项
|
||||
</Button>
|
||||
</Space>
|
||||
<BaseForm />
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,231 @@
|
|||
<script lang="ts" setup>
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter';
|
||||
|
||||
const [BaseForm] = useVbenForm({
|
||||
// 所有表单项共用,可单独在表单内覆盖
|
||||
commonConfig: {
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// 提交函数
|
||||
handleSubmit: onSubmit,
|
||||
// 垂直布局,label和input在不同行,值为vertical
|
||||
// 水平布局,label和input在同一行
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
// 组件需要在 #/adapter.ts内注册,并加上类型
|
||||
component: 'Input',
|
||||
// 对应组件的参数
|
||||
componentProps: {
|
||||
placeholder: '请输入用户名',
|
||||
},
|
||||
// 字段名
|
||||
fieldName: 'username',
|
||||
// 界面显示的label
|
||||
label: '字符串',
|
||||
},
|
||||
{
|
||||
component: 'InputPassword',
|
||||
componentProps: {
|
||||
placeholder: '请输入密码',
|
||||
},
|
||||
fieldName: 'password',
|
||||
label: '密码',
|
||||
},
|
||||
{
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'number',
|
||||
label: '数字(带后缀)',
|
||||
suffix: () => '¥',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: true,
|
||||
options: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '选项2',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
placeholder: '请选择',
|
||||
showSearch: true,
|
||||
},
|
||||
fieldName: 'options',
|
||||
label: '下拉选',
|
||||
},
|
||||
{
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '选项2',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
},
|
||||
fieldName: 'radioGroup',
|
||||
label: '单选组',
|
||||
},
|
||||
{
|
||||
component: 'Radio',
|
||||
fieldName: 'radio',
|
||||
label: '',
|
||||
renderComponentContent: () => {
|
||||
return {
|
||||
default: () => ['Radio'],
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'CheckboxGroup',
|
||||
componentProps: {
|
||||
name: 'cname',
|
||||
options: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '选项2',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
},
|
||||
fieldName: 'checkboxGroup',
|
||||
label: '多选组',
|
||||
},
|
||||
{
|
||||
component: 'Checkbox',
|
||||
fieldName: 'checkbox',
|
||||
label: '',
|
||||
renderComponentContent: () => {
|
||||
return {
|
||||
default: () => ['我已阅读并同意'],
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Mentions',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: 'afc163',
|
||||
value: 'afc163',
|
||||
},
|
||||
{
|
||||
label: 'zombieJ',
|
||||
value: 'zombieJ',
|
||||
},
|
||||
],
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'mentions',
|
||||
label: '提及',
|
||||
},
|
||||
{
|
||||
component: 'Rate',
|
||||
fieldName: 'rate',
|
||||
label: '评分',
|
||||
},
|
||||
{
|
||||
component: 'Switch',
|
||||
componentProps: {
|
||||
class: 'w-auto',
|
||||
},
|
||||
fieldName: 'switch',
|
||||
label: '开关',
|
||||
},
|
||||
{
|
||||
component: 'DatePicker',
|
||||
fieldName: 'datePicker',
|
||||
label: '日期选择框',
|
||||
},
|
||||
{
|
||||
component: 'RangePicker',
|
||||
fieldName: 'rangePicker',
|
||||
label: '范围选择器',
|
||||
},
|
||||
{
|
||||
component: 'TimePicker',
|
||||
fieldName: 'timePicker',
|
||||
label: '时间选择框',
|
||||
},
|
||||
{
|
||||
component: 'TreeSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择',
|
||||
showSearch: true,
|
||||
treeData: [
|
||||
{
|
||||
label: 'root 1',
|
||||
value: 'root 1',
|
||||
children: [
|
||||
{
|
||||
label: 'parent 1',
|
||||
value: 'parent 1',
|
||||
children: [
|
||||
{
|
||||
label: 'parent 1-0',
|
||||
value: 'parent 1-0',
|
||||
children: [
|
||||
{
|
||||
label: 'my leaf',
|
||||
value: 'leaf1',
|
||||
},
|
||||
{
|
||||
label: 'your leaf',
|
||||
value: 'leaf2',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'parent 1-1',
|
||||
value: 'parent 1-1',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'parent 2',
|
||||
value: 'parent 2',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
treeNodeFilterProp: 'label',
|
||||
},
|
||||
fieldName: 'treeSelect',
|
||||
label: '树选择',
|
||||
},
|
||||
],
|
||||
wrapperClass: 'grid-cols-1',
|
||||
});
|
||||
|
||||
function onSubmit(values: Record<string, any>) {
|
||||
message.success({
|
||||
content: `form values: ${JSON.stringify(values)}`,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseForm />
|
||||
</template>
|
|
@ -0,0 +1,68 @@
|
|||
<script lang="ts" setup>
|
||||
import { h } from 'vue';
|
||||
|
||||
import { Input, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter';
|
||||
|
||||
const [Form] = useVbenForm({
|
||||
// 所有表单项共用,可单独在表单内覆盖
|
||||
commonConfig: {
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
labelClass: 'w-2/6',
|
||||
},
|
||||
// 提交函数
|
||||
handleSubmit: onSubmit,
|
||||
// 垂直布局,label和input在不同行,值为vertical
|
||||
// 水平布局,label和input在同一行
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
// 组件需要在 #/adapter.ts内注册,并加上类型
|
||||
component: 'Input',
|
||||
fieldName: 'field',
|
||||
label: '自定义后缀',
|
||||
suffix: () => h('span', { class: 'text-red-600' }, '元'),
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'field1',
|
||||
label: '自定义组件slot',
|
||||
renderComponentContent: () => ({
|
||||
prefix: () => 'prefix',
|
||||
suffix: () => 'suffix',
|
||||
}),
|
||||
},
|
||||
{
|
||||
component: h(Input, { placeholder: '请输入' }),
|
||||
fieldName: 'field2',
|
||||
label: '自定义组件',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'field3',
|
||||
label: '自定义组件(slot)',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
wrapperClass: 'grid-cols-1',
|
||||
});
|
||||
|
||||
function onSubmit(values: Record<string, any>) {
|
||||
message.success({
|
||||
content: `form values: ${JSON.stringify(values)}`,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form>
|
||||
<template #field3="slotProps">
|
||||
<Input placeholder="请输入" v-bind="slotProps" />
|
||||
</template>
|
||||
</Form>
|
||||
</template>
|
|
@ -0,0 +1,168 @@
|
|||
<script lang="ts" setup>
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter';
|
||||
|
||||
const [Form] = useVbenForm({
|
||||
// 提交函数
|
||||
handleSubmit: onSubmit,
|
||||
schema: [
|
||||
{
|
||||
component: 'Input',
|
||||
defaultValue: 'hidden value',
|
||||
dependencies: {
|
||||
show: false,
|
||||
// 随意一个字段改变时,都会触发
|
||||
triggerFields: ['field1Switch'],
|
||||
},
|
||||
fieldName: 'hiddenField',
|
||||
label: '隐藏字段',
|
||||
},
|
||||
{
|
||||
component: 'Switch',
|
||||
defaultValue: true,
|
||||
fieldName: 'field1Switch',
|
||||
help: '通过Dom控制销毁',
|
||||
label: '显示字段1',
|
||||
},
|
||||
{
|
||||
component: 'Switch',
|
||||
defaultValue: true,
|
||||
fieldName: 'field2Switch',
|
||||
help: '通过css控制隐藏',
|
||||
label: '显示字段2',
|
||||
},
|
||||
{
|
||||
component: 'Switch',
|
||||
fieldName: 'field3Switch',
|
||||
label: '禁用字段3',
|
||||
},
|
||||
{
|
||||
component: 'Switch',
|
||||
fieldName: 'field4Switch',
|
||||
label: '字段4必填',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
if(values) {
|
||||
return !!values.field1Switch;
|
||||
},
|
||||
// 只有指定的字段改变时,才会触发
|
||||
triggerFields: ['field1Switch'],
|
||||
},
|
||||
// 字段名
|
||||
fieldName: 'field1',
|
||||
// 界面显示的label
|
||||
label: '字段1',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
show(values) {
|
||||
return !!values.field2Switch;
|
||||
},
|
||||
triggerFields: ['field2Switch'],
|
||||
},
|
||||
fieldName: 'field2',
|
||||
label: '字段2',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
disabled(values) {
|
||||
return !!values.field3Switch;
|
||||
},
|
||||
triggerFields: ['field3Switch'],
|
||||
},
|
||||
fieldName: 'field3',
|
||||
label: '字段3',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
required(values) {
|
||||
return !!values.field4Switch;
|
||||
},
|
||||
triggerFields: ['field4Switch'],
|
||||
},
|
||||
fieldName: 'field4',
|
||||
label: '字段4',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
dependencies: {
|
||||
rules(values) {
|
||||
if (values.field1 === '123') {
|
||||
return 'required';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
triggerFields: ['field1'],
|
||||
},
|
||||
fieldName: 'field5',
|
||||
help: '当字段1的值为`123`时,必填',
|
||||
label: '动态rules',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
class: 'w-full',
|
||||
filterOption: true,
|
||||
options: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '选项2',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
placeholder: '请选择',
|
||||
showSearch: true,
|
||||
},
|
||||
dependencies: {
|
||||
componentProps(values) {
|
||||
if (values.field2 === '123') {
|
||||
return {
|
||||
options: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '选项2',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
label: '选项3',
|
||||
value: '3',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
triggerFields: ['field2'],
|
||||
},
|
||||
fieldName: 'field6',
|
||||
help: '当字段2的值为`123`时,更改下拉选项',
|
||||
label: '动态配置',
|
||||
},
|
||||
],
|
||||
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-2',
|
||||
});
|
||||
|
||||
function onSubmit(values: Record<string, any>) {
|
||||
message.success({
|
||||
content: `form values: ${JSON.stringify(values)}`,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form />
|
||||
</template>
|
|
@ -0,0 +1,94 @@
|
|||
<script lang="ts" setup>
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter';
|
||||
|
||||
const [QueryForm] = useVbenForm({
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
// 所有表单项共用,可单独在表单内覆盖
|
||||
commonConfig: {
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// 提交函数
|
||||
handleSubmit: onSubmit,
|
||||
// 垂直布局,label和input在不同行,值为vertical
|
||||
// 水平布局,label和input在同一行
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
// 组件需要在 #/adapter.ts内注册,并加上类型
|
||||
component: 'Input',
|
||||
// 对应组件的参数
|
||||
componentProps: {
|
||||
placeholder: '请输入用户名',
|
||||
},
|
||||
// 字段名
|
||||
fieldName: 'username',
|
||||
// 界面显示的label
|
||||
label: '字符串',
|
||||
},
|
||||
{
|
||||
component: 'InputPassword',
|
||||
componentProps: {
|
||||
placeholder: '请输入密码',
|
||||
},
|
||||
fieldName: 'password',
|
||||
label: '密码',
|
||||
},
|
||||
{
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'number',
|
||||
label: '数字(带后缀)',
|
||||
suffix: () => '¥',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: true,
|
||||
options: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '选项2',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
placeholder: '请选择',
|
||||
showSearch: true,
|
||||
},
|
||||
fieldName: 'options',
|
||||
label: '下拉选',
|
||||
},
|
||||
{
|
||||
component: 'DatePicker',
|
||||
fieldName: 'datePicker',
|
||||
label: '日期选择框',
|
||||
},
|
||||
],
|
||||
// 是否可展开
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
text: '查询',
|
||||
},
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-2',
|
||||
});
|
||||
function onSubmit(values: Record<string, any>) {
|
||||
message.success({
|
||||
content: `form values: ${JSON.stringify(values)}`,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QueryForm />
|
||||
</template>
|
|
@ -0,0 +1,189 @@
|
|||
<script lang="ts" setup>
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm, z } from '#/adapter';
|
||||
|
||||
const [Form] = useVbenForm({
|
||||
// 所有表单项共用,可单独在表单内覆盖
|
||||
commonConfig: {
|
||||
// 所有表单项
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
// 提交函数
|
||||
handleSubmit: onSubmit,
|
||||
// 垂直布局,label和input在不同行,值为vertical
|
||||
// 水平布局,label和input在同一行
|
||||
layout: 'horizontal',
|
||||
schema: [
|
||||
{
|
||||
// 组件需要在 #/adapter.ts内注册,并加上类型
|
||||
component: 'Input',
|
||||
// 对应组件的参数
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
// 字段名
|
||||
fieldName: 'field1',
|
||||
// 界面显示的label
|
||||
label: '字段1',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
defaultValue: '默认值',
|
||||
fieldName: 'field2',
|
||||
label: '默认值(必填)',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'field3',
|
||||
label: '默认值(非必填)',
|
||||
rules: z.string().default('默认值').optional(),
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'field31',
|
||||
label: '自定义信息',
|
||||
rules: z.string().min(1, { message: '最少输入1个字符' }),
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
// 对应组件的参数
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
// 字段名
|
||||
fieldName: 'field4',
|
||||
// 界面显示的label
|
||||
label: '邮箱',
|
||||
rules: z.string().email('请输入正确的邮箱'),
|
||||
},
|
||||
{
|
||||
component: 'InputNumber',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'number',
|
||||
label: '数字',
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
filterOption: true,
|
||||
options: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '选项2',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
placeholder: '请选择',
|
||||
showSearch: true,
|
||||
},
|
||||
defaultValue: undefined,
|
||||
fieldName: 'options',
|
||||
label: '下拉选',
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '选项2',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
},
|
||||
fieldName: 'radioGroup',
|
||||
label: '单选组',
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
component: 'CheckboxGroup',
|
||||
componentProps: {
|
||||
name: 'cname',
|
||||
options: [
|
||||
{
|
||||
label: '选项1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: '选项2',
|
||||
value: '2',
|
||||
},
|
||||
],
|
||||
},
|
||||
fieldName: 'checkboxGroup',
|
||||
label: '多选组',
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
component: 'Checkbox',
|
||||
fieldName: 'checkbox',
|
||||
label: '',
|
||||
renderComponentContent: () => {
|
||||
return {
|
||||
default: () => ['我已阅读并同意'],
|
||||
};
|
||||
},
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
component: 'DatePicker',
|
||||
defaultValue: undefined,
|
||||
fieldName: 'datePicker',
|
||||
label: '日期选择框',
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
component: 'RangePicker',
|
||||
defaultValue: undefined,
|
||||
fieldName: 'rangePicker',
|
||||
label: '区间选择框',
|
||||
rules: 'selectRequired',
|
||||
},
|
||||
{
|
||||
component: 'InputPassword',
|
||||
componentProps: {
|
||||
placeholder: '请输入',
|
||||
},
|
||||
fieldName: 'password',
|
||||
label: '密码',
|
||||
rules: 'required',
|
||||
},
|
||||
],
|
||||
wrapperClass: 'grid-cols-1',
|
||||
});
|
||||
|
||||
function onSubmit(values: Record<string, any>) {
|
||||
message.success({
|
||||
content: `form values: ${JSON.stringify(values)}`,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form />
|
||||
</template>
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/web.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"#/*": ["./src/_env/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
".vitepress/*.mts",
|
||||
".vitepress/**/*.ts",
|
||||
|
|
|
@ -2,6 +2,7 @@ import { describe, expect, it } from 'vitest';
|
|||
|
||||
import {
|
||||
getFirstNonNullOrUndefined,
|
||||
isBoolean,
|
||||
isEmpty,
|
||||
isHttpUrl,
|
||||
isObject,
|
||||
|
@ -96,6 +97,21 @@ describe('isWindow', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('isBoolean', () => {
|
||||
it('should return true for boolean values', () => {
|
||||
expect(isBoolean(true)).toBe(true);
|
||||
expect(isBoolean(false)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for non-boolean values', () => {
|
||||
expect(isBoolean(null)).toBe(false);
|
||||
expect(isBoolean(42)).toBe(false);
|
||||
expect(isBoolean('string')).toBe(false);
|
||||
expect(isBoolean({})).toBe(false);
|
||||
expect(isBoolean([])).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isObject', () => {
|
||||
it('should return true for objects', () => {
|
||||
expect(isObject({})).toBe(true);
|
||||
|
|
|
@ -10,6 +10,15 @@ function isUndefined(value?: unknown): value is undefined {
|
|||
return value === undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查传入的值是否为boolean
|
||||
* @param value
|
||||
* @returns 如果值是布尔值,返回true,否则返回false。
|
||||
*/
|
||||
function isBoolean(value: unknown): value is boolean {
|
||||
return typeof value === 'boolean';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查传入的值是否为空。
|
||||
*
|
||||
|
@ -141,6 +150,7 @@ function getFirstNonNullOrUndefined<T>(
|
|||
|
||||
export {
|
||||
getFirstNonNullOrUndefined,
|
||||
isBoolean,
|
||||
isEmpty,
|
||||
isFunction,
|
||||
isHttpUrl,
|
||||
|
|
|
@ -219,6 +219,12 @@ export class FormApi {
|
|||
|
||||
async validate(opts?: Partial<ValidationOptions>) {
|
||||
const form = await this.getForm();
|
||||
return await form.validate(opts);
|
||||
|
||||
const validateResult = await form.validate(opts);
|
||||
|
||||
if (Object.keys(validateResult?.errors ?? {}).length > 0) {
|
||||
console.error('validate error', validateResult?.errors);
|
||||
}
|
||||
return validateResult;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import type {
|
|||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { isFunction } from '@vben-core/shared/utils';
|
||||
import { isBoolean, isFunction } from '@vben-core/shared/utils';
|
||||
|
||||
import { useFormValues } from 'vee-validate';
|
||||
|
||||
|
@ -74,12 +74,18 @@ export default function useDependencies(
|
|||
isIf.value = !!(await whenIf(formValues, formApi));
|
||||
// 不渲染
|
||||
if (!isIf.value) return;
|
||||
} else if (isBoolean(whenIf)) {
|
||||
isIf.value = whenIf;
|
||||
if (!isIf.value) return;
|
||||
}
|
||||
|
||||
// 2. 判断show,如果show为false,则隐藏
|
||||
if (isFunction(show)) {
|
||||
isShow.value = !!(await show(formValues, formApi));
|
||||
if (!isShow.value) return;
|
||||
} else if (isBoolean(show)) {
|
||||
isShow.value = show;
|
||||
if (!isShow.value) return;
|
||||
}
|
||||
|
||||
if (isFunction(componentProps)) {
|
||||
|
@ -92,6 +98,8 @@ export default function useDependencies(
|
|||
|
||||
if (isFunction(disabled)) {
|
||||
isDisabled.value = !!(await disabled(formValues, formApi));
|
||||
} else if (isBoolean(disabled)) {
|
||||
isDisabled.value = disabled;
|
||||
}
|
||||
|
||||
if (isFunction(required)) {
|
||||
|
|
|
@ -85,7 +85,15 @@ const currentRules = computed(() => {
|
|||
return dynamicRules.value || rules;
|
||||
});
|
||||
|
||||
const visible = computed(() => {
|
||||
return isIf.value && isShow.value;
|
||||
});
|
||||
|
||||
const shouldRequired = computed(() => {
|
||||
if (!visible.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!currentRules.value) {
|
||||
return isRequired.value;
|
||||
}
|
||||
|
@ -113,6 +121,10 @@ const shouldRequired = computed(() => {
|
|||
});
|
||||
|
||||
const fieldRules = computed(() => {
|
||||
if (!visible.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let rules = currentRules.value;
|
||||
if (!rules) {
|
||||
return isRequired.value ? 'required' : null;
|
||||
|
|
|
@ -88,12 +88,12 @@ export interface FormItemDependencies {
|
|||
* 是否禁用
|
||||
* @returns 是否禁用
|
||||
*/
|
||||
disabled?: FormItemDependenciesCondition;
|
||||
disabled?: boolean | FormItemDependenciesCondition;
|
||||
/**
|
||||
* 是否渲染(删除dom)
|
||||
* @returns 是否渲染
|
||||
*/
|
||||
if?: FormItemDependenciesCondition;
|
||||
if?: boolean | FormItemDependenciesCondition;
|
||||
/**
|
||||
* 是否必填
|
||||
* @returns 是否必填
|
||||
|
@ -107,7 +107,7 @@ export interface FormItemDependencies {
|
|||
* 是否隐藏(Css)
|
||||
* @returns 是否隐藏
|
||||
*/
|
||||
show?: FormItemDependenciesCondition;
|
||||
show?: boolean | FormItemDependenciesCondition;
|
||||
/**
|
||||
* 任意触发都会执行
|
||||
*/
|
||||
|
@ -141,7 +141,7 @@ export interface FormCommonConfig {
|
|||
disabled?: boolean;
|
||||
/**
|
||||
* 所有表单项的控件样式
|
||||
* @default ""
|
||||
* @default {}
|
||||
*/
|
||||
formFieldProps?: Partial<typeof Field>;
|
||||
/**
|
||||
|
@ -161,7 +161,7 @@ export interface FormCommonConfig {
|
|||
hideRequiredMark?: boolean;
|
||||
/**
|
||||
* 所有表单项的label样式
|
||||
* @default "w-[100px]"
|
||||
* @default ""
|
||||
*/
|
||||
labelClass?: string;
|
||||
/**
|
||||
|
@ -295,6 +295,7 @@ export interface VbenFormProps<
|
|||
|
||||
/**
|
||||
* 是否显示默认操作按钮
|
||||
* @default true
|
||||
*/
|
||||
showDefaultActions?: boolean;
|
||||
|
||||
|
|
|
@ -162,11 +162,11 @@ function handleConfirm() {
|
|||
v-if="hintImage"
|
||||
:alt="$t('ui.captcha.alt')"
|
||||
:src="hintImage"
|
||||
class="h-10 w-full rounded border border-solid border-slate-200"
|
||||
class="border-border h-10 w-full rounded border"
|
||||
/>
|
||||
<div
|
||||
v-else-if="hintText"
|
||||
class="flex h-10 w-full items-center justify-center rounded border border-solid border-slate-200"
|
||||
class="border-border flex-center h-10 w-full rounded border"
|
||||
>
|
||||
{{ `${$t('ui.captcha.clickInOrder')}` + `【${hintText}】` }}
|
||||
</div>
|
||||
|
|
|
@ -5,4 +5,11 @@ export * from '@vben-core/form-ui';
|
|||
export * from '@vben-core/popup-ui';
|
||||
|
||||
// 给文档用
|
||||
export { VbenButton } from '@vben-core/shadcn-ui';
|
||||
export {
|
||||
VbenButton,
|
||||
VbenCountToAnimator,
|
||||
VbenInputPassword,
|
||||
VbenLoading,
|
||||
VbenPinInput,
|
||||
VbenSpinner,
|
||||
} from '@vben-core/shadcn-ui';
|
||||
|
|
|
@ -81,6 +81,7 @@ function handleClick(item: NotificationItem) {
|
|||
<div class="flex items-center justify-between p-4 py-3">
|
||||
<div class="text-foreground">{{ $t('widgets.notifications') }}</div>
|
||||
<VbenIconButton
|
||||
:disabled="notifications.length <= 0"
|
||||
:tooltip="$t('widgets.markAllAsRead')"
|
||||
@click="handleMakeAll"
|
||||
>
|
||||
|
@ -131,7 +132,12 @@ function handleClick(item: NotificationItem) {
|
|||
<div
|
||||
class="border-border flex items-center justify-between border-t px-4 py-3"
|
||||
>
|
||||
<VbenButton size="sm" variant="ghost" @click="handleClear">
|
||||
<VbenButton
|
||||
:disabled="notifications.length <= 0"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
@click="handleClear"
|
||||
>
|
||||
{{ $t('widgets.clearNotifications') }}
|
||||
</VbenButton>
|
||||
<VbenButton size="sm" @click="handleViewAll">
|
||||
|
|
|
@ -27,13 +27,13 @@ import {
|
|||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Textarea,
|
||||
TimePicker,
|
||||
TreeSelect,
|
||||
Upload,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
// 业务表单组件适配
|
||||
|
||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
||||
export type FormComponentType =
|
||||
| 'AutoComplete'
|
||||
| 'Checkbox'
|
||||
|
@ -51,6 +51,7 @@ export type FormComponentType =
|
|||
| 'Select'
|
||||
| 'Space'
|
||||
| 'Switch'
|
||||
| 'Textarea'
|
||||
| 'TimePicker'
|
||||
| 'TreeSelect'
|
||||
| 'Upload'
|
||||
|
@ -83,12 +84,16 @@ setupVbenForm<FormComponentType>({
|
|||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Textarea,
|
||||
TimePicker,
|
||||
TreeSelect,
|
||||
Upload,
|
||||
},
|
||||
config: {
|
||||
// ant design vue组件库默认都是 v-model:value
|
||||
baseModelPropName: 'value',
|
||||
|
||||
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
|
@ -97,12 +102,14 @@ setupVbenForm<FormComponentType>({
|
|||
},
|
||||
},
|
||||
defineRules: {
|
||||
// 输入项目必填国际化适配
|
||||
required: (value, _params, ctx) => {
|
||||
if ((!value && value !== 0) || value.length === 0) {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// 选择项目必填国际化适配
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('formRules.selectRequired', [ctx.label]);
|
||||
|
|
|
@ -114,6 +114,7 @@ function handleClick(
|
|||
fieldName: 'fieldOptions',
|
||||
},
|
||||
]);
|
||||
message.success('字段 `fieldOptions` 下拉选项更新成功。');
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Card, Input, message } from 'ant-design-vue';
|
|||
|
||||
import { useVbenForm } from '#/adapter';
|
||||
|
||||
const [BaseForm] = useVbenForm({
|
||||
const [Form] = useVbenForm({
|
||||
// 所有表单项共用,可单独在表单内覆盖
|
||||
commonConfig: {
|
||||
// 所有表单项
|
||||
|
@ -65,11 +65,11 @@ function onSubmit(values: Record<string, any>) {
|
|||
<template>
|
||||
<Page description="表单组件自定义示例" title="表单组件">
|
||||
<Card title="基础示例">
|
||||
<BaseForm>
|
||||
<Form>
|
||||
<template #field3="slotProps">
|
||||
<Input placeholder="请输入" v-bind="slotProps" />
|
||||
</template>
|
||||
</BaseForm>
|
||||
</Form>
|
||||
</Card>
|
||||
</Page>
|
||||
</template>
|
||||
|
|
|
@ -9,6 +9,17 @@ const [Form, formApi] = useVbenForm({
|
|||
// 提交函数
|
||||
handleSubmit: onSubmit,
|
||||
schema: [
|
||||
{
|
||||
component: 'Input',
|
||||
defaultValue: 'hidden value',
|
||||
dependencies: {
|
||||
show: false,
|
||||
// 随意一个字段改变时,都会触发
|
||||
triggerFields: ['field1Switch'],
|
||||
},
|
||||
fieldName: 'hiddenField',
|
||||
label: '隐藏字段',
|
||||
},
|
||||
{
|
||||
component: 'Switch',
|
||||
defaultValue: true,
|
||||
|
|
193
pnpm-lock.yaml
193
pnpm-lock.yaml
|
@ -37,8 +37,8 @@ catalogs:
|
|||
specifier: ^4.1.2
|
||||
version: 4.1.2
|
||||
'@intlify/core-base':
|
||||
specifier: ^10.0.1
|
||||
version: 10.0.1
|
||||
specifier: ^10.0.2
|
||||
version: 10.0.2
|
||||
'@intlify/unplugin-vue-i18n':
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
|
@ -124,11 +124,11 @@ catalogs:
|
|||
specifier: ^4.0.1
|
||||
version: 4.0.1
|
||||
'@vue/reactivity':
|
||||
specifier: ^3.5.7
|
||||
version: 3.5.7
|
||||
specifier: ^3.5.8
|
||||
version: 3.5.8
|
||||
'@vue/shared':
|
||||
specifier: ^3.5.7
|
||||
version: 3.5.7
|
||||
specifier: ^3.5.8
|
||||
version: 3.5.8
|
||||
'@vue/test-utils':
|
||||
specifier: ^2.4.6
|
||||
version: 2.4.6
|
||||
|
@ -280,8 +280,8 @@ catalogs:
|
|||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
jsdom:
|
||||
specifier: ^25.0.0
|
||||
version: 25.0.0
|
||||
specifier: ^25.0.1
|
||||
version: 25.0.1
|
||||
jsonc-eslint-parser:
|
||||
specifier: ^2.4.0
|
||||
version: 2.4.0
|
||||
|
@ -376,8 +376,8 @@ catalogs:
|
|||
specifier: ^16.9.0
|
||||
version: 16.9.0
|
||||
stylelint-config-recess-order:
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
specifier: ^5.1.1
|
||||
version: 5.1.1
|
||||
stylelint-config-recommended:
|
||||
specifier: ^14.0.1
|
||||
version: 14.0.1
|
||||
|
@ -460,8 +460,8 @@ catalogs:
|
|||
specifier: ^9.4.3
|
||||
version: 9.4.3
|
||||
vue-i18n:
|
||||
specifier: ^10.0.1
|
||||
version: 10.0.1
|
||||
specifier: ^10.0.2
|
||||
version: 10.0.2
|
||||
vue-router:
|
||||
specifier: ^4.4.5
|
||||
version: 4.4.5
|
||||
|
@ -553,7 +553,7 @@ importers:
|
|||
version: 3.0.1
|
||||
jsdom:
|
||||
specifier: 'catalog:'
|
||||
version: 25.0.0
|
||||
version: 25.0.1
|
||||
lint-staged:
|
||||
specifier: 'catalog:'
|
||||
version: 15.2.10
|
||||
|
@ -577,7 +577,7 @@ importers:
|
|||
version: 5.4.7(@types/node@22.5.5)(less@4.2.0)(sass@1.79.3)(terser@5.33.0)
|
||||
vitest:
|
||||
specifier: 'catalog:'
|
||||
version: 2.1.1(@types/node@22.5.5)(jsdom@25.0.0)(less@4.2.0)(sass@1.79.3)(terser@5.33.0)
|
||||
version: 2.1.1(@types/node@22.5.5)(jsdom@25.0.1)(less@4.2.0)(sass@1.79.3)(terser@5.33.0)
|
||||
vue:
|
||||
specifier: 3.5.7
|
||||
version: 3.5.7(typescript@5.6.2)
|
||||
|
@ -799,9 +799,21 @@ importers:
|
|||
'@vben/common-ui':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/effects/common-ui
|
||||
'@vben/hooks':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/effects/hooks
|
||||
'@vben/locales':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/locales
|
||||
'@vben/preferences':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/preferences
|
||||
'@vben/styles':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/styles
|
||||
ant-design-vue:
|
||||
specifier: 'catalog:'
|
||||
version: 4.2.5(vue@3.5.7(typescript@5.6.2))
|
||||
lucide-vue-next:
|
||||
specifier: 'catalog:'
|
||||
version: 0.445.0(vue@3.5.7(typescript@5.6.2))
|
||||
|
@ -911,7 +923,7 @@ importers:
|
|||
version: 4.1.4(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.0(jiti@1.21.6))
|
||||
eslint-plugin-vitest:
|
||||
specifier: 'catalog:'
|
||||
version: 0.5.4(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2)(vitest@2.1.1(@types/node@22.5.5)(jsdom@25.0.0)(less@4.2.0)(sass@1.79.3)(terser@5.33.0))
|
||||
version: 0.5.4(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2)(vitest@2.1.1(@types/node@22.5.5)(jsdom@25.0.1)(less@4.2.0)(sass@1.79.3)(terser@5.33.0))
|
||||
eslint-plugin-vue:
|
||||
specifier: 'catalog:'
|
||||
version: 9.28.0(eslint@9.11.0(jiti@1.21.6))
|
||||
|
@ -941,7 +953,7 @@ importers:
|
|||
version: 3.0.1(stylelint@16.9.0(typescript@5.6.2))
|
||||
stylelint-config-recess-order:
|
||||
specifier: 'catalog:'
|
||||
version: 5.1.0(stylelint@16.9.0(typescript@5.6.2))
|
||||
version: 5.1.1(stylelint@16.9.0(typescript@5.6.2))
|
||||
stylelint-scss:
|
||||
specifier: 'catalog:'
|
||||
version: 6.7.0(stylelint@16.9.0(typescript@5.6.2))
|
||||
|
@ -1082,7 +1094,7 @@ importers:
|
|||
dependencies:
|
||||
'@intlify/unplugin-vue-i18n':
|
||||
specifier: 'catalog:'
|
||||
version: 5.0.0(@vue/compiler-dom@3.5.7)(eslint@9.11.0(jiti@1.21.6))(rollup@4.22.4)(typescript@5.6.2)(vue-i18n@10.0.1(vue@3.5.7(typescript@5.6.2)))(vue@3.5.7(typescript@5.6.2))
|
||||
version: 5.0.0(@vue/compiler-dom@3.5.7)(eslint@9.11.0(jiti@1.21.6))(rollup@4.22.4)(typescript@5.6.2)(vue-i18n@10.0.2(vue@3.5.7(typescript@5.6.2)))(vue@3.5.7(typescript@5.6.2))
|
||||
'@jspm/generator':
|
||||
specifier: 'catalog:'
|
||||
version: 2.3.1
|
||||
|
@ -1181,10 +1193,10 @@ importers:
|
|||
version: 0.5.5(vue@3.5.7(typescript@5.6.2))
|
||||
'@vue/reactivity':
|
||||
specifier: 'catalog:'
|
||||
version: 3.5.7
|
||||
version: 3.5.8
|
||||
'@vue/shared':
|
||||
specifier: 'catalog:'
|
||||
version: 3.5.7
|
||||
version: 3.5.8
|
||||
clsx:
|
||||
specifier: 2.1.1
|
||||
version: 2.1.1
|
||||
|
@ -1608,7 +1620,7 @@ importers:
|
|||
dependencies:
|
||||
'@intlify/core-base':
|
||||
specifier: 'catalog:'
|
||||
version: 10.0.1
|
||||
version: 10.0.2
|
||||
'@vben-core/composables':
|
||||
specifier: workspace:*
|
||||
version: link:../@core/composables
|
||||
|
@ -1617,7 +1629,7 @@ importers:
|
|||
version: 3.5.7(typescript@5.6.2)
|
||||
vue-i18n:
|
||||
specifier: 'catalog:'
|
||||
version: 10.0.1(vue@3.5.7(typescript@5.6.2))
|
||||
version: 10.0.2(vue@3.5.7(typescript@5.6.2))
|
||||
|
||||
packages/preferences:
|
||||
dependencies:
|
||||
|
@ -3903,24 +3915,24 @@ packages:
|
|||
vue-i18n:
|
||||
optional: true
|
||||
|
||||
'@intlify/core-base@10.0.1':
|
||||
resolution: {integrity: sha512-6kpRGjhos95ph7QmEtP4tnWFTW102s71CLQAQwfsIGqOAcoJhzcYFpzIQ0gKXzqAIXsMD/hwM5qJ4ewqMHw3gg==}
|
||||
'@intlify/core-base@10.0.2':
|
||||
resolution: {integrity: sha512-0Ewg801c3f5ukktJVi/BVwxPVbX2lvGflK0G6QxTatbWaMt2YA1QheDGTXS2Nz/PupSDPNOabADsTG9LCoKA1A==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/message-compiler@10.0.0':
|
||||
resolution: {integrity: sha512-OcaWc63NC/9p1cMdgoNKBj4d61BH8sUW1Hfs6YijTd9656ZR4rNqXAlRnBrfS5ABq0vjQjpa8VnyvH9hK49yBw==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/message-compiler@10.0.1':
|
||||
resolution: {integrity: sha512-fPeykrcgVT5eOIlshTHiPCN8FV3AZyBOdMS3XaXzfQ6eL5wqfc29I/EdIv5YXVW5X8e/BgYeWjBC0Cuznsl/2g==}
|
||||
'@intlify/message-compiler@10.0.2':
|
||||
resolution: {integrity: sha512-PHFnGFEKknuk+RwcafKAjS537Ln0ptSfqmvVdsHKWBytTbiKqZZFX57pmfio2ln+ZLeHuyudcqbTi1zGJUNIcA==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/shared@10.0.0':
|
||||
resolution: {integrity: sha512-6ngLfI7DOTew2dcF9WMJx+NnMWghMBhIiHbGg+wRvngpzD5KZJZiJVuzMsUQE1a5YebEmtpTEfUrDp/NqVGdiw==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/shared@10.0.1':
|
||||
resolution: {integrity: sha512-b4h7IWdZl710DnAhET8lgfgZ4Y9A2IZx/gbli3Ec/zHtYCoPqLHmiM7kUNBrSZj7d/SSjcMMZHuz5I09x3PYZw==}
|
||||
'@intlify/shared@10.0.2':
|
||||
resolution: {integrity: sha512-sIF9cqB0CwUWLtDb1QDnl4PlTggE4GSnR1aA44thT9Y18rUVdWO2z4w2Ow6O60tsdNzC0WnUd6fU0VXyKQ6WBA==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@intlify/unplugin-vue-i18n@5.0.0':
|
||||
|
@ -4843,6 +4855,9 @@ packages:
|
|||
'@vue/reactivity@3.5.7':
|
||||
resolution: {integrity: sha512-yF0EpokpOHRNXyn/h6abXc9JFIzfdAf0MJHIi92xxCWS0mqrXH6+2aZ+A6EbSrspGzX5MHTd5N8iBA28HnXu9g==}
|
||||
|
||||
'@vue/reactivity@3.5.8':
|
||||
resolution: {integrity: sha512-mlgUyFHLCUZcAYkqvzYnlBRCh0t5ZQfLYit7nukn1GR96gc48Bp4B7OIcSfVSvlG1k3BPfD+p22gi1t2n9tsXg==}
|
||||
|
||||
'@vue/runtime-core@3.5.7':
|
||||
resolution: {integrity: sha512-OzLpBpKbZEaZVSNfd+hQbfBrDKux+b7Yl5hYhhWWWhHD7fEpF+CdI3Brm5k5GsufHEfvMcjruPxwQZuBN6nFYQ==}
|
||||
|
||||
|
@ -4857,6 +4872,9 @@ packages:
|
|||
'@vue/shared@3.5.7':
|
||||
resolution: {integrity: sha512-NBE1PBIvzIedxIc2RZiKXvGbJkrZ2/hLf3h8GlS4/sP9xcXEZMFWOazFkNd6aGeUCMaproe5MHVYB3/4AW9q9g==}
|
||||
|
||||
'@vue/shared@3.5.8':
|
||||
resolution: {integrity: sha512-mJleSWbAGySd2RJdX1RBtcrUBX6snyOc0qHpgk3lGi4l9/P/3ny3ELqFWqYdkXIwwNN/kdm8nD9ky8o6l/Lx2A==}
|
||||
|
||||
'@vue/test-utils@2.4.6':
|
||||
resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==}
|
||||
|
||||
|
@ -7310,8 +7328,8 @@ packages:
|
|||
resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
jsdom@25.0.0:
|
||||
resolution: {integrity: sha512-OhoFVT59T7aEq75TVw9xxEfkXgacpqAhQaYgP9y/fDqWQCMB/b1H66RfmPm/MaeaAIU9nDwMOVTlPN51+ao6CQ==}
|
||||
jsdom@25.0.1:
|
||||
resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
canvas: ^2.11.2
|
||||
|
@ -8778,9 +8796,6 @@ packages:
|
|||
pseudomap@1.0.2:
|
||||
resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
|
||||
|
||||
psl@1.9.0:
|
||||
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
|
||||
|
||||
publint@0.2.11:
|
||||
resolution: {integrity: sha512-/kxbd+sD/uEG515N/ZYpC6gYs8h89cQ4UIsAq1y6VT4qlNh8xmiSwcP2xU2MbzXFl8J0l2IdONKFweLfYoqhcA==}
|
||||
engines: {node: '>=16'}
|
||||
|
@ -8799,9 +8814,6 @@ packages:
|
|||
engines: {node: '>=10.13.0'}
|
||||
hasBin: true
|
||||
|
||||
querystringify@2.2.0:
|
||||
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
|
||||
|
||||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
|
@ -8952,9 +8964,6 @@ packages:
|
|||
require-package-name@2.0.1:
|
||||
resolution: {integrity: sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==}
|
||||
|
||||
requires-port@1.0.0:
|
||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||
|
||||
resize-observer-polyfill@1.5.1:
|
||||
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
|
||||
|
||||
|
@ -9437,8 +9446,8 @@ packages:
|
|||
postcss-html: ^1.0.0
|
||||
stylelint: '>=14.0.0'
|
||||
|
||||
stylelint-config-recess-order@5.1.0:
|
||||
resolution: {integrity: sha512-ddapCF6B/kEtQYIFhQFReQ0dvK1ZdgJDM/SGFtIyeooYDbqaJqcOlGkRRGaVErCQYJY/bPSPsLRS2LdQtLJUVQ==}
|
||||
stylelint-config-recess-order@5.1.1:
|
||||
resolution: {integrity: sha512-eDAHWVBelzDbMbdMj15pSw0Ycykv5eLeriJdbGCp0zd44yvhgZLI+wyVHegzXp5NrstxTPSxl0fuOVKdMm0XLA==}
|
||||
peerDependencies:
|
||||
stylelint: '>=16'
|
||||
|
||||
|
@ -9653,6 +9662,13 @@ packages:
|
|||
resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
tldts-core@6.1.47:
|
||||
resolution: {integrity: sha512-6SWyFMnlst1fEt7GQVAAu16EGgFK0cLouH/2Mk6Ftlwhv3Ol40L0dlpGMcnnNiiOMyD2EV/aF3S+U2nKvvLvrA==}
|
||||
|
||||
tldts@6.1.47:
|
||||
resolution: {integrity: sha512-R/K2tZ5MiY+mVrnSkNJkwqYT2vUv1lcT6wJvd2emGaMJ7PHUGRY4e3tUsdFCXgqxi2QgbHjL3yJgXCo40v9Hxw==}
|
||||
hasBin: true
|
||||
|
||||
tmp@0.0.33:
|
||||
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
|
||||
engines: {node: '>=0.6.0'}
|
||||
|
@ -9673,9 +9689,9 @@ packages:
|
|||
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tough-cookie@4.1.4:
|
||||
resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==}
|
||||
engines: {node: '>=6'}
|
||||
tough-cookie@5.0.0:
|
||||
resolution: {integrity: sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
|
@ -9887,10 +9903,6 @@ packages:
|
|||
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
|
||||
engines: {node: '>= 4.0.0'}
|
||||
|
||||
universalify@0.2.0:
|
||||
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
|
||||
engines: {node: '>= 4.0.0'}
|
||||
|
||||
universalify@2.0.1:
|
||||
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
@ -9983,9 +9995,6 @@ packages:
|
|||
uri-js@4.4.1:
|
||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||
|
||||
url-parse@1.5.10:
|
||||
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
|
||||
|
||||
urlpattern-polyfill@8.0.2:
|
||||
resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==}
|
||||
|
||||
|
@ -10181,8 +10190,8 @@ packages:
|
|||
peerDependencies:
|
||||
eslint: '>=6.0.0'
|
||||
|
||||
vue-i18n@10.0.1:
|
||||
resolution: {integrity: sha512-SQVlSm/1S6AaG1wexvwq3ebXUrrkx75ZHD78UAs4/rYD/X3tsQxfm6ElpT4ZPegJQEgRtOJjGripqSrfqAENtg==}
|
||||
vue-i18n@10.0.2:
|
||||
resolution: {integrity: sha512-osoes79ecpqdzYYhywp/pDLlPMoyQ5MHJvjAitQLmXiaCj/ejC8YIeWIwIsDdqWcvkrVFmROYDLcGgGCVn7g0Q==}
|
||||
engines: {node: '>= 16'}
|
||||
peerDependencies:
|
||||
vue: 3.5.7
|
||||
|
@ -12662,7 +12671,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@swc/helpers': 0.5.13
|
||||
|
||||
'@intlify/bundle-utils@9.0.0-beta.0(vue-i18n@10.0.1(vue@3.5.7(typescript@5.6.2)))':
|
||||
'@intlify/bundle-utils@9.0.0-beta.0(vue-i18n@10.0.2(vue@3.5.7(typescript@5.6.2)))':
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 10.0.0
|
||||
'@intlify/shared': 10.0.0
|
||||
|
@ -12674,33 +12683,33 @@ snapshots:
|
|||
source-map-js: 1.2.1
|
||||
yaml-eslint-parser: 1.2.3
|
||||
optionalDependencies:
|
||||
vue-i18n: 10.0.1(vue@3.5.7(typescript@5.6.2))
|
||||
vue-i18n: 10.0.2(vue@3.5.7(typescript@5.6.2))
|
||||
|
||||
'@intlify/core-base@10.0.1':
|
||||
'@intlify/core-base@10.0.2':
|
||||
dependencies:
|
||||
'@intlify/message-compiler': 10.0.1
|
||||
'@intlify/shared': 10.0.1
|
||||
'@intlify/message-compiler': 10.0.2
|
||||
'@intlify/shared': 10.0.2
|
||||
|
||||
'@intlify/message-compiler@10.0.0':
|
||||
dependencies:
|
||||
'@intlify/shared': 10.0.0
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@intlify/message-compiler@10.0.1':
|
||||
'@intlify/message-compiler@10.0.2':
|
||||
dependencies:
|
||||
'@intlify/shared': 10.0.1
|
||||
'@intlify/shared': 10.0.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@intlify/shared@10.0.0': {}
|
||||
|
||||
'@intlify/shared@10.0.1': {}
|
||||
'@intlify/shared@10.0.2': {}
|
||||
|
||||
'@intlify/unplugin-vue-i18n@5.0.0(@vue/compiler-dom@3.5.7)(eslint@9.11.0(jiti@1.21.6))(rollup@4.22.4)(typescript@5.6.2)(vue-i18n@10.0.1(vue@3.5.7(typescript@5.6.2)))(vue@3.5.7(typescript@5.6.2))':
|
||||
'@intlify/unplugin-vue-i18n@5.0.0(@vue/compiler-dom@3.5.7)(eslint@9.11.0(jiti@1.21.6))(rollup@4.22.4)(typescript@5.6.2)(vue-i18n@10.0.2(vue@3.5.7(typescript@5.6.2)))(vue@3.5.7(typescript@5.6.2))':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@9.11.0(jiti@1.21.6))
|
||||
'@intlify/bundle-utils': 9.0.0-beta.0(vue-i18n@10.0.1(vue@3.5.7(typescript@5.6.2)))
|
||||
'@intlify/bundle-utils': 9.0.0-beta.0(vue-i18n@10.0.2(vue@3.5.7(typescript@5.6.2)))
|
||||
'@intlify/shared': 10.0.0
|
||||
'@intlify/vue-i18n-extensions': 6.2.0(@intlify/shared@10.0.0)(@vue/compiler-dom@3.5.7)(vue-i18n@10.0.1(vue@3.5.7(typescript@5.6.2)))(vue@3.5.7(typescript@5.6.2))
|
||||
'@intlify/vue-i18n-extensions': 6.2.0(@intlify/shared@10.0.0)(@vue/compiler-dom@3.5.7)(vue-i18n@10.0.2(vue@3.5.7(typescript@5.6.2)))(vue@3.5.7(typescript@5.6.2))
|
||||
'@rollup/pluginutils': 5.1.0(rollup@4.22.4)
|
||||
'@typescript-eslint/scope-manager': 7.18.0
|
||||
'@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2)
|
||||
|
@ -12715,7 +12724,7 @@ snapshots:
|
|||
unplugin: 1.14.1
|
||||
vue: 3.5.7(typescript@5.6.2)
|
||||
optionalDependencies:
|
||||
vue-i18n: 10.0.1(vue@3.5.7(typescript@5.6.2))
|
||||
vue-i18n: 10.0.2(vue@3.5.7(typescript@5.6.2))
|
||||
transitivePeerDependencies:
|
||||
- '@vue/compiler-dom'
|
||||
- eslint
|
||||
|
@ -12724,14 +12733,14 @@ snapshots:
|
|||
- typescript
|
||||
- webpack-sources
|
||||
|
||||
'@intlify/vue-i18n-extensions@6.2.0(@intlify/shared@10.0.0)(@vue/compiler-dom@3.5.7)(vue-i18n@10.0.1(vue@3.5.7(typescript@5.6.2)))(vue@3.5.7(typescript@5.6.2))':
|
||||
'@intlify/vue-i18n-extensions@6.2.0(@intlify/shared@10.0.0)(@vue/compiler-dom@3.5.7)(vue-i18n@10.0.2(vue@3.5.7(typescript@5.6.2)))(vue@3.5.7(typescript@5.6.2))':
|
||||
dependencies:
|
||||
'@babel/parser': 7.25.6
|
||||
optionalDependencies:
|
||||
'@intlify/shared': 10.0.0
|
||||
'@vue/compiler-dom': 3.5.7
|
||||
vue: 3.5.7(typescript@5.6.2)
|
||||
vue-i18n: 10.0.1(vue@3.5.7(typescript@5.6.2))
|
||||
vue-i18n: 10.0.2(vue@3.5.7(typescript@5.6.2))
|
||||
|
||||
'@ioredis/commands@1.2.0': {}
|
||||
|
||||
|
@ -13929,6 +13938,10 @@ snapshots:
|
|||
dependencies:
|
||||
'@vue/shared': 3.5.7
|
||||
|
||||
'@vue/reactivity@3.5.8':
|
||||
dependencies:
|
||||
'@vue/shared': 3.5.8
|
||||
|
||||
'@vue/runtime-core@3.5.7':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.7
|
||||
|
@ -13949,6 +13962,8 @@ snapshots:
|
|||
|
||||
'@vue/shared@3.5.7': {}
|
||||
|
||||
'@vue/shared@3.5.8': {}
|
||||
|
||||
'@vue/test-utils@2.4.6':
|
||||
dependencies:
|
||||
js-beautify: 1.15.1
|
||||
|
@ -15713,13 +15728,13 @@ snapshots:
|
|||
optionalDependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2)
|
||||
|
||||
eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2)(vitest@2.1.1(@types/node@22.5.5)(jsdom@25.0.0)(less@4.2.0)(sass@1.79.3)(terser@5.33.0)):
|
||||
eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2)(vitest@2.1.1(@types/node@22.5.5)(jsdom@25.0.1)(less@4.2.0)(sass@1.79.3)(terser@5.33.0)):
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 7.18.0(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2)
|
||||
eslint: 9.11.0(jiti@1.21.6)
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.6.0(@typescript-eslint/parser@8.6.0(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2))(eslint@9.11.0(jiti@1.21.6))(typescript@5.6.2)
|
||||
vitest: 2.1.1(@types/node@22.5.5)(jsdom@25.0.0)(less@4.2.0)(sass@1.79.3)(terser@5.33.0)
|
||||
vitest: 2.1.1(@types/node@22.5.5)(jsdom@25.0.1)(less@4.2.0)(sass@1.79.3)(terser@5.33.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
@ -16738,7 +16753,7 @@ snapshots:
|
|||
|
||||
jsdoc-type-pratt-parser@4.1.0: {}
|
||||
|
||||
jsdom@25.0.0:
|
||||
jsdom@25.0.1:
|
||||
dependencies:
|
||||
cssstyle: 4.1.0
|
||||
data-urls: 5.0.0
|
||||
|
@ -16753,7 +16768,7 @@ snapshots:
|
|||
rrweb-cssom: 0.7.1
|
||||
saxes: 6.0.0
|
||||
symbol-tree: 3.2.4
|
||||
tough-cookie: 4.1.4
|
||||
tough-cookie: 5.0.0
|
||||
w3c-xmlserializer: 5.0.0
|
||||
webidl-conversions: 7.0.0
|
||||
whatwg-encoding: 3.1.1
|
||||
|
@ -18273,8 +18288,6 @@ snapshots:
|
|||
|
||||
pseudomap@1.0.2: {}
|
||||
|
||||
psl@1.9.0: {}
|
||||
|
||||
publint@0.2.11:
|
||||
dependencies:
|
||||
npm-packlist: 5.1.3
|
||||
|
@ -18293,8 +18306,6 @@ snapshots:
|
|||
pngjs: 5.0.0
|
||||
yargs: 15.4.1
|
||||
|
||||
querystringify@2.2.0: {}
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
queue-tick@1.0.1: {}
|
||||
|
@ -18471,8 +18482,6 @@ snapshots:
|
|||
|
||||
require-package-name@2.0.1: {}
|
||||
|
||||
requires-port@1.0.0: {}
|
||||
|
||||
resize-observer-polyfill@1.5.1: {}
|
||||
|
||||
resolve-dir@1.0.1:
|
||||
|
@ -18980,7 +18989,7 @@ snapshots:
|
|||
postcss-html: 1.7.0
|
||||
stylelint: 16.9.0(typescript@5.6.2)
|
||||
|
||||
stylelint-config-recess-order@5.1.0(stylelint@16.9.0(typescript@5.6.2)):
|
||||
stylelint-config-recess-order@5.1.1(stylelint@16.9.0(typescript@5.6.2)):
|
||||
dependencies:
|
||||
stylelint: 16.9.0(typescript@5.6.2)
|
||||
stylelint-order: 6.0.4(stylelint@16.9.0(typescript@5.6.2))
|
||||
|
@ -19260,6 +19269,12 @@ snapshots:
|
|||
|
||||
tinyspy@3.0.2: {}
|
||||
|
||||
tldts-core@6.1.47: {}
|
||||
|
||||
tldts@6.1.47:
|
||||
dependencies:
|
||||
tldts-core: 6.1.47
|
||||
|
||||
tmp@0.0.33:
|
||||
dependencies:
|
||||
os-tmpdir: 1.0.2
|
||||
|
@ -19274,12 +19289,9 @@ snapshots:
|
|||
|
||||
totalist@3.0.1: {}
|
||||
|
||||
tough-cookie@4.1.4:
|
||||
tough-cookie@5.0.0:
|
||||
dependencies:
|
||||
psl: 1.9.0
|
||||
punycode: 2.3.1
|
||||
universalify: 0.2.0
|
||||
url-parse: 1.5.10
|
||||
tldts: 6.1.47
|
||||
|
||||
tr46@0.0.3: {}
|
||||
|
||||
|
@ -19524,8 +19536,6 @@ snapshots:
|
|||
|
||||
universalify@0.1.2: {}
|
||||
|
||||
universalify@0.2.0: {}
|
||||
|
||||
universalify@2.0.1: {}
|
||||
|
||||
unplugin-element-plus@0.8.0(rollup@4.22.4):
|
||||
|
@ -19616,11 +19626,6 @@ snapshots:
|
|||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
url-parse@1.5.10:
|
||||
dependencies:
|
||||
querystringify: 2.2.0
|
||||
requires-port: 1.0.0
|
||||
|
||||
urlpattern-polyfill@8.0.2: {}
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
@ -19849,7 +19854,7 @@ snapshots:
|
|||
- typescript
|
||||
- universal-cookie
|
||||
|
||||
vitest@2.1.1(@types/node@22.5.5)(jsdom@25.0.0)(less@4.2.0)(sass@1.79.3)(terser@5.33.0):
|
||||
vitest@2.1.1(@types/node@22.5.5)(jsdom@25.0.1)(less@4.2.0)(sass@1.79.3)(terser@5.33.0):
|
||||
dependencies:
|
||||
'@vitest/expect': 2.1.1
|
||||
'@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.4.7(@types/node@22.5.5)(less@4.2.0)(sass@1.79.3)(terser@5.33.0))
|
||||
|
@ -19872,7 +19877,7 @@ snapshots:
|
|||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 22.5.5
|
||||
jsdom: 25.0.0
|
||||
jsdom: 25.0.1
|
||||
transitivePeerDependencies:
|
||||
- less
|
||||
- lightningcss
|
||||
|
@ -19912,10 +19917,10 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vue-i18n@10.0.1(vue@3.5.7(typescript@5.6.2)):
|
||||
vue-i18n@10.0.2(vue@3.5.7(typescript@5.6.2)):
|
||||
dependencies:
|
||||
'@intlify/core-base': 10.0.1
|
||||
'@intlify/shared': 10.0.1
|
||||
'@intlify/core-base': 10.0.2
|
||||
'@intlify/shared': 10.0.2
|
||||
'@vue/devtools-api': 6.6.4
|
||||
vue: 3.5.7(typescript@5.6.2)
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ catalog:
|
|||
'@iconify/json': ^2.2.251
|
||||
'@iconify/tailwind': ^1.1.3
|
||||
'@iconify/vue': ^4.1.2
|
||||
'@intlify/core-base': ^10.0.1
|
||||
'@intlify/core-base': ^10.0.2
|
||||
'@intlify/unplugin-vue-i18n': ^5.0.0
|
||||
'@jspm/generator': ^2.3.1
|
||||
'@manypkg/get-packages': ^2.2.2
|
||||
|
@ -53,8 +53,8 @@ catalog:
|
|||
'@vite-pwa/vitepress': ^0.5.3
|
||||
'@vitejs/plugin-vue': ^5.1.4
|
||||
'@vitejs/plugin-vue-jsx': ^4.0.1
|
||||
'@vue/reactivity': ^3.5.7
|
||||
'@vue/shared': ^3.5.7
|
||||
'@vue/reactivity': ^3.5.8
|
||||
'@vue/shared': ^3.5.8
|
||||
'@vue/test-utils': ^2.4.6
|
||||
'@vueuse/core': ^11.1.0
|
||||
'@vueuse/integrations': ^11.1.0
|
||||
|
@ -106,7 +106,7 @@ catalog:
|
|||
html-minifier-terser: ^7.2.0
|
||||
husky: ^9.1.6
|
||||
is-ci: ^3.0.1
|
||||
jsdom: ^25.0.0
|
||||
jsdom: ^25.0.1
|
||||
jsonc-eslint-parser: ^2.4.0
|
||||
jsonwebtoken: ^9.0.2
|
||||
lint-staged: ^15.2.10
|
||||
|
@ -139,7 +139,7 @@ catalog:
|
|||
sass: ^1.79.3
|
||||
sortablejs: ^1.15.3
|
||||
stylelint: ^16.9.0
|
||||
stylelint-config-recess-order: ^5.1.0
|
||||
stylelint-config-recess-order: ^5.1.1
|
||||
stylelint-config-recommended: ^14.0.1
|
||||
stylelint-config-recommended-scss: ^14.1.0
|
||||
stylelint-config-recommended-vue: ^1.5.0
|
||||
|
@ -166,9 +166,9 @@ catalog:
|
|||
vitepress: ^1.3.4
|
||||
vitepress-plugin-group-icons: ^1.2.4
|
||||
vitest: ^2.1.1
|
||||
vue: ^3.5.7
|
||||
vue: ^3.5.8
|
||||
vue-eslint-parser: ^9.4.3
|
||||
vue-i18n: ^10.0.1
|
||||
vue-i18n: ^10.0.2
|
||||
vue-router: ^4.4.5
|
||||
vue-tsc: ^2.1.6
|
||||
watermark-js-plus: ^1.5.6
|
||||
|
|
Loading…
Reference in New Issue