Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev
This commit is contained in:
commit
7f1548b343
@ -3,11 +3,12 @@
|
|||||||
* 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
|
* 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Component, SetupContext } from 'vue';
|
import type { Component } from 'vue';
|
||||||
|
|
||||||
import type { BaseFormComponentType } from '@vben/common-ui';
|
import type { BaseFormComponentType } from '@vben/common-ui';
|
||||||
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
import { h } from 'vue';
|
import { defineComponent, getCurrentInstance, h, ref } from 'vue';
|
||||||
|
|
||||||
import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
|
import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
@ -44,10 +45,30 @@ const withDefaultPlaceholder = <T extends Component>(
|
|||||||
component: T,
|
component: T,
|
||||||
type: 'input' | 'select',
|
type: 'input' | 'select',
|
||||||
) => {
|
) => {
|
||||||
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
|
return defineComponent({
|
||||||
const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
|
inheritAttrs: false,
|
||||||
return h(component, { ...props, ...attrs, placeholder }, slots);
|
name: component.name,
|
||||||
};
|
setup: (props: any, { attrs, expose, slots }) => {
|
||||||
|
const placeholder =
|
||||||
|
props?.placeholder ||
|
||||||
|
attrs?.placeholder ||
|
||||||
|
$t(`ui.placeholder.${type}`);
|
||||||
|
// 透传组件暴露的方法
|
||||||
|
const innerRef = ref();
|
||||||
|
const publicApi: Recordable<any> = {};
|
||||||
|
expose(publicApi);
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
instance?.proxy?.$nextTick(() => {
|
||||||
|
for (const key in innerRef.value) {
|
||||||
|
if (typeof innerRef.value[key] === 'function') {
|
||||||
|
publicApi[key] = innerRef.value[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () =>
|
||||||
|
h(component, { ...props, ...attrs, placeholder, ref: innerRef }, slots);
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
||||||
|
@ -279,22 +279,24 @@ const [Form, formApi] = useVbenForm({
|
|||||||
|
|
||||||
useVbenForm 返回的第二个参数,是一个对象,包含了一些表单的方法。
|
useVbenForm 返回的第二个参数,是一个对象,包含了一些表单的方法。
|
||||||
|
|
||||||
| 方法名 | 描述 | 类型 |
|
| 方法名 | 描述 | 类型 | 版本号 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| submitForm | 提交表单 | `(e:Event)=>Promise<Record<string,any>>` |
|
| submitForm | 提交表单 | `(e:Event)=>Promise<Record<string,any>>` | - |
|
||||||
| validateAndSubmitForm | 提交并校验表单 | `(e:Event)=>Promise<Record<string,any>>` |
|
| validateAndSubmitForm | 提交并校验表单 | `(e:Event)=>Promise<Record<string,any>>` | - |
|
||||||
| resetForm | 重置表单 | `()=>Promise<void>` |
|
| resetForm | 重置表单 | `()=>Promise<void>` | - |
|
||||||
| setValues | 设置表单值, 默认会过滤不在schema中定义的field, 可通过filterFields形参关闭过滤 | `(fields: Record<string, any>, filterFields?: boolean, shouldValidate?: boolean) => Promise<void>` |
|
| setValues | 设置表单值, 默认会过滤不在schema中定义的field, 可通过filterFields形参关闭过滤 | `(fields: Record<string, any>, filterFields?: boolean, shouldValidate?: boolean) => Promise<void>` | - |
|
||||||
| getValues | 获取表单值 | `(fields:Record<string, any>,shouldValidate: boolean = false)=>Promise<void>` |
|
| getValues | 获取表单值 | `(fields:Record<string, any>,shouldValidate: boolean = false)=>Promise<void>` | - |
|
||||||
| validate | 表单校验 | `()=>Promise<void>` |
|
| validate | 表单校验 | `()=>Promise<void>` | - |
|
||||||
| validateField | 校验指定字段 | `(fieldName: string)=>Promise<ValidationResult<unknown>>` |
|
| validateField | 校验指定字段 | `(fieldName: string)=>Promise<ValidationResult<unknown>>` | - |
|
||||||
| isFieldValid | 检查某个字段是否已通过校验 | `(fieldName: string)=>Promise<boolean>` |
|
| isFieldValid | 检查某个字段是否已通过校验 | `(fieldName: string)=>Promise<boolean>` | - |
|
||||||
| resetValidate | 重置表单校验 | `()=>Promise<void>` |
|
| resetValidate | 重置表单校验 | `()=>Promise<void>` | - |
|
||||||
| updateSchema | 更新formSchema | `(schema:FormSchema[])=>void` |
|
| updateSchema | 更新formSchema | `(schema:FormSchema[])=>void` | - |
|
||||||
| setFieldValue | 设置字段值 | `(field: string, value: any, shouldValidate?: boolean)=>Promise<void>` |
|
| setFieldValue | 设置字段值 | `(field: string, value: any, shouldValidate?: boolean)=>Promise<void>` | - |
|
||||||
| setState | 设置组件状态(props) | `(stateOrFn:\| ((prev: VbenFormProps) => Partial<VbenFormProps>)\| Partial<VbenFormProps>)=>Promise<void>` |
|
| setState | 设置组件状态(props) | `(stateOrFn:\| ((prev: VbenFormProps) => Partial<VbenFormProps>)\| Partial<VbenFormProps>)=>Promise<void>` | - |
|
||||||
| getState | 获取组件状态(props) | `()=>Promise<VbenFormProps>` |
|
| getState | 获取组件状态(props) | `()=>Promise<VbenFormProps>` | - |
|
||||||
| form | 表单对象实例,可以操作表单,见 [useForm](https://vee-validate.logaretm.com/v4/api/use-form/) | - |
|
| form | 表单对象实例,可以操作表单,见 [useForm](https://vee-validate.logaretm.com/v4/api/use-form/) | - | - |
|
||||||
|
| getFieldComponentRef | 获取指定字段的组件实例 | `<T=unknown>(fieldName: string)=>T` | >5.5.3 |
|
||||||
|
| getFocusedField | 获取当前已获得焦点的字段 | `()=>string\|undefined` | >5.5.3 |
|
||||||
|
|
||||||
## Props
|
## Props
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ vxeUI.renderer.add('CellLink', {
|
|||||||
|
|
||||||
**表单搜索** 部分采用了`Vben Form 表单`,参考 [Vben Form 表单文档](/components/common-ui/vben-form)。
|
**表单搜索** 部分采用了`Vben Form 表单`,参考 [Vben Form 表单文档](/components/common-ui/vben-form)。
|
||||||
|
|
||||||
当启用了表单搜索时,可以在toolbarConfig中配置`search`为`true`来让表格在工具栏区域显示一个搜索表单控制按钮。
|
当启用了表单搜索时,可以在toolbarConfig中配置`search`为`true`来让表格在工具栏区域显示一个搜索表单控制按钮。表格的所有以`form-`开头的命名插槽都会被传递给搜索表单。
|
||||||
|
|
||||||
<DemoPreview dir="demos/vben-vxe-table/form" />
|
<DemoPreview dir="demos/vben-vxe-table/form" />
|
||||||
|
|
||||||
@ -250,3 +250,9 @@ useVbenVxeGrid 返回的第二个参数,是一个对象,包含了一些表
|
|||||||
| toolbar-actions | 工具栏左侧部分(表格标题附近) |
|
| toolbar-actions | 工具栏左侧部分(表格标题附近) |
|
||||||
| toolbar-tools | 工具栏右侧部分(vxeTable原生工具按钮的左侧) |
|
| toolbar-tools | 工具栏右侧部分(vxeTable原生工具按钮的左侧) |
|
||||||
| table-title | 表格标题插槽 |
|
| table-title | 表格标题插槽 |
|
||||||
|
|
||||||
|
::: info 搜索表单的插槽
|
||||||
|
|
||||||
|
对于使用了搜索表单的表格来说,所有以`form-`开头的命名插槽都会传递给表单。
|
||||||
|
|
||||||
|
:::
|
||||||
|
@ -95,7 +95,7 @@
|
|||||||
"node": ">=20.10.0",
|
"node": ">=20.10.0",
|
||||||
"pnpm": ">=9.12.0"
|
"pnpm": ">=9.12.0"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.15.6",
|
"packageManager": "pnpm@9.15.7",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"peerDependencyRules": {
|
"peerDependencyRules": {
|
||||||
"allowedVersions": {
|
"allowedVersions": {
|
||||||
|
@ -5,6 +5,8 @@ import type {
|
|||||||
ValidationOptions,
|
ValidationOptions,
|
||||||
} from 'vee-validate';
|
} from 'vee-validate';
|
||||||
|
|
||||||
|
import type { ComponentPublicInstance } from 'vue';
|
||||||
|
|
||||||
import type { Recordable } from '@vben-core/typings';
|
import type { Recordable } from '@vben-core/typings';
|
||||||
|
|
||||||
import type { FormActions, FormSchema, VbenFormProps } from './types';
|
import type { FormActions, FormSchema, VbenFormProps } from './types';
|
||||||
@ -56,6 +58,11 @@ export class FormApi {
|
|||||||
|
|
||||||
public store: Store<VbenFormProps>;
|
public store: Store<VbenFormProps>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件实例映射
|
||||||
|
*/
|
||||||
|
private componentRefMap: Map<string, unknown> = new Map();
|
||||||
|
|
||||||
// 最后一次点击提交时的表单值
|
// 最后一次点击提交时的表单值
|
||||||
private latestSubmissionValues: null | Recordable<any> = null;
|
private latestSubmissionValues: null | Recordable<any> = null;
|
||||||
|
|
||||||
@ -85,6 +92,46 @@ export class FormApi {
|
|||||||
bindMethods(this);
|
bindMethods(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字段组件实例
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @returns 组件实例
|
||||||
|
*/
|
||||||
|
getFieldComponentRef<T = ComponentPublicInstance>(
|
||||||
|
fieldName: string,
|
||||||
|
): T | undefined {
|
||||||
|
return this.componentRefMap.has(fieldName)
|
||||||
|
? (this.componentRefMap.get(fieldName) as T)
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前聚焦的字段,如果没有聚焦的字段则返回undefined
|
||||||
|
*/
|
||||||
|
getFocusedField() {
|
||||||
|
for (const fieldName of this.componentRefMap.keys()) {
|
||||||
|
const ref = this.getFieldComponentRef(fieldName);
|
||||||
|
if (ref) {
|
||||||
|
let el: HTMLElement | null = null;
|
||||||
|
if (ref instanceof HTMLElement) {
|
||||||
|
el = ref;
|
||||||
|
} else if (ref.$el instanceof HTMLElement) {
|
||||||
|
el = ref.$el;
|
||||||
|
}
|
||||||
|
if (!el) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
el === document.activeElement ||
|
||||||
|
el.contains(document.activeElement)
|
||||||
|
) {
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
getLatestSubmissionValues() {
|
getLatestSubmissionValues() {
|
||||||
return this.latestSubmissionValues || {};
|
return this.latestSubmissionValues || {};
|
||||||
}
|
}
|
||||||
@ -143,13 +190,14 @@ export class FormApi {
|
|||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
mount(formActions: FormActions) {
|
mount(formActions: FormActions, componentRefMap: Map<string, unknown>) {
|
||||||
if (!this.isMounted) {
|
if (!this.isMounted) {
|
||||||
Object.assign(this.form, formActions);
|
Object.assign(this.form, formActions);
|
||||||
this.stateHandler.setConditionTrue();
|
this.stateHandler.setConditionTrue();
|
||||||
this.setLatestSubmissionValues({
|
this.setLatestSubmissionValues({
|
||||||
...toRaw(this.handleRangeTimeValue(this.form.values)),
|
...toRaw(this.handleRangeTimeValue(this.form.values)),
|
||||||
});
|
});
|
||||||
|
this.componentRefMap = componentRefMap;
|
||||||
this.isMounted = true;
|
this.isMounted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import type { ZodType } from 'zod';
|
|||||||
|
|
||||||
import type { FormSchema, MaybeComponentProps } from '../types';
|
import type { FormSchema, MaybeComponentProps } from '../types';
|
||||||
|
|
||||||
import { computed, nextTick, useTemplateRef, watch } from 'vue';
|
import { computed, nextTick, onUnmounted, useTemplateRef, watch } from 'vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
@ -18,6 +18,7 @@ import { cn, isFunction, isObject, isString } from '@vben-core/shared/utils';
|
|||||||
import { toTypedSchema } from '@vee-validate/zod';
|
import { toTypedSchema } from '@vee-validate/zod';
|
||||||
import { useFieldError, useFormValues } from 'vee-validate';
|
import { useFieldError, useFormValues } from 'vee-validate';
|
||||||
|
|
||||||
|
import { injectComponentRefMap } from '../use-form-context';
|
||||||
import { injectRenderFormProps, useFormContext } from './context';
|
import { injectRenderFormProps, useFormContext } from './context';
|
||||||
import useDependencies from './dependencies';
|
import useDependencies from './dependencies';
|
||||||
import FormLabel from './form-label.vue';
|
import FormLabel from './form-label.vue';
|
||||||
@ -267,6 +268,15 @@ function autofocus() {
|
|||||||
fieldComponentRef.value?.focus?.();
|
fieldComponentRef.value?.focus?.();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const componentRefMap = injectComponentRefMap();
|
||||||
|
watch(fieldComponentRef, (componentRef) => {
|
||||||
|
componentRefMap?.set(fieldName, componentRef);
|
||||||
|
});
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (componentRefMap?.has(fieldName)) {
|
||||||
|
componentRefMap.delete(fieldName);
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -20,6 +20,9 @@ export const [injectFormProps, provideFormProps] =
|
|||||||
'VbenFormProps',
|
'VbenFormProps',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const [injectComponentRefMap, provideComponentRefMap] =
|
||||||
|
createContext<Map<string, unknown>>('ComponentRefMap');
|
||||||
|
|
||||||
export function useFormInitial(
|
export function useFormInitial(
|
||||||
props: ComputedRef<VbenFormProps> | VbenFormProps,
|
props: ComputedRef<VbenFormProps> | VbenFormProps,
|
||||||
) {
|
) {
|
||||||
|
@ -17,7 +17,11 @@ import {
|
|||||||
DEFAULT_FORM_COMMON_CONFIG,
|
DEFAULT_FORM_COMMON_CONFIG,
|
||||||
} from './config';
|
} from './config';
|
||||||
import { Form } from './form-render';
|
import { Form } from './form-render';
|
||||||
import { provideFormProps, useFormInitial } from './use-form-context';
|
import {
|
||||||
|
provideComponentRefMap,
|
||||||
|
provideFormProps,
|
||||||
|
useFormInitial,
|
||||||
|
} from './use-form-context';
|
||||||
// 通过 extends 会导致热更新卡死,所以重复写了一遍
|
// 通过 extends 会导致热更新卡死,所以重复写了一遍
|
||||||
interface Props extends VbenFormProps {
|
interface Props extends VbenFormProps {
|
||||||
formApi: ExtendedFormApi;
|
formApi: ExtendedFormApi;
|
||||||
@ -29,11 +33,14 @@ const state = props.formApi?.useStore?.();
|
|||||||
|
|
||||||
const forward = useForwardPriorityValues(props, state);
|
const forward = useForwardPriorityValues(props, state);
|
||||||
|
|
||||||
|
const componentRefMap = new Map<string, unknown>();
|
||||||
|
|
||||||
const { delegatedSlots, form } = useFormInitial(forward);
|
const { delegatedSlots, form } = useFormInitial(forward);
|
||||||
|
|
||||||
provideFormProps([forward, form]);
|
provideFormProps([forward, form]);
|
||||||
|
provideComponentRefMap(componentRefMap);
|
||||||
|
|
||||||
props.formApi?.mount?.(form);
|
props.formApi?.mount?.(form, componentRefMap);
|
||||||
|
|
||||||
const handleUpdateCollapsed = (value: boolean) => {
|
const handleUpdateCollapsed = (value: boolean) => {
|
||||||
props.formApi?.setState({ collapsed: !!value });
|
props.formApi?.setState({ collapsed: !!value });
|
||||||
|
@ -3,6 +3,8 @@ import type { CSSProperties } from 'vue';
|
|||||||
|
|
||||||
import type { VbenLayoutProps } from './vben-layout';
|
import type { VbenLayoutProps } from './vben-layout';
|
||||||
|
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SCROLL_FIXED_CLASS,
|
SCROLL_FIXED_CLASS,
|
||||||
useLayoutFooterStyle,
|
useLayoutFooterStyle,
|
||||||
@ -11,8 +13,8 @@ import {
|
|||||||
import { Menu } from '@vben-core/icons';
|
import { Menu } from '@vben-core/icons';
|
||||||
import { VbenIconButton } from '@vben-core/shadcn-ui';
|
import { VbenIconButton } from '@vben-core/shadcn-ui';
|
||||||
import { ELEMENT_ID_MAIN_CONTENT } from '@vben-core/shared/constants';
|
import { ELEMENT_ID_MAIN_CONTENT } from '@vben-core/shared/constants';
|
||||||
|
|
||||||
import { useMouse, useScroll, useThrottleFn } from '@vueuse/core';
|
import { useMouse, useScroll, useThrottleFn } from '@vueuse/core';
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
LayoutContent,
|
LayoutContent,
|
||||||
@ -60,10 +62,16 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{ sideMouseLeave: []; toggleSidebar: [] }>();
|
const emit = defineEmits<{ sideMouseLeave: []; toggleSidebar: [] }>();
|
||||||
const sidebarCollapse = defineModel<boolean>('sidebarCollapse');
|
const sidebarCollapse = defineModel<boolean>('sidebarCollapse', {
|
||||||
|
default: false,
|
||||||
|
});
|
||||||
const sidebarExtraVisible = defineModel<boolean>('sidebarExtraVisible');
|
const sidebarExtraVisible = defineModel<boolean>('sidebarExtraVisible');
|
||||||
const sidebarExtraCollapse = defineModel<boolean>('sidebarExtraCollapse');
|
const sidebarExtraCollapse = defineModel<boolean>('sidebarExtraCollapse', {
|
||||||
const sidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover');
|
default: false,
|
||||||
|
});
|
||||||
|
const sidebarExpandOnHover = defineModel<boolean>('sidebarExpandOnHover', {
|
||||||
|
default: false,
|
||||||
|
});
|
||||||
const sidebarEnable = defineModel<boolean>('sidebarEnable', { default: true });
|
const sidebarEnable = defineModel<boolean>('sidebarEnable', { default: true });
|
||||||
|
|
||||||
// side是否处于hover状态展开菜单中
|
// side是否处于hover状态展开菜单中
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
|
import type { Component, DefineComponent } from 'vue';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AccessModeType,
|
AccessModeType,
|
||||||
GenerateMenuAndRoutesOptions,
|
GenerateMenuAndRoutesOptions,
|
||||||
RouteRecordRaw,
|
RouteRecordRaw,
|
||||||
} from '@vben/types';
|
} from '@vben/types';
|
||||||
|
|
||||||
|
import { defineComponent, h } from 'vue';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
cloneDeep,
|
cloneDeep,
|
||||||
generateMenus,
|
generateMenus,
|
||||||
generateRoutesByBackend,
|
generateRoutesByBackend,
|
||||||
generateRoutesByFrontend,
|
generateRoutesByFrontend,
|
||||||
|
isFunction,
|
||||||
|
isString,
|
||||||
mapTree,
|
mapTree,
|
||||||
setObjToUrlParams,
|
setObjToUrlParams,
|
||||||
} from '@vben/utils';
|
} from '@vben/utils';
|
||||||
@ -89,8 +95,31 @@ async function generateRoutes(
|
|||||||
/**
|
/**
|
||||||
* 调整路由树,做以下处理:
|
* 调整路由树,做以下处理:
|
||||||
* 1. 对未添加redirect的路由添加redirect
|
* 1. 对未添加redirect的路由添加redirect
|
||||||
|
* 2. 将懒加载的组件名称修改为当前路由的名称(如果启用了keep-alive的话)
|
||||||
*/
|
*/
|
||||||
resultRoutes = mapTree(resultRoutes, (route) => {
|
resultRoutes = mapTree(resultRoutes, (route) => {
|
||||||
|
// 重新包装component,使用与路由名称相同的name以支持keep-alive的条件缓存。
|
||||||
|
if (
|
||||||
|
route.meta?.keepAlive &&
|
||||||
|
isFunction(route.component) &&
|
||||||
|
route.name &&
|
||||||
|
isString(route.name)
|
||||||
|
) {
|
||||||
|
const originalComponent = route.component as () => Promise<{
|
||||||
|
default: Component | DefineComponent;
|
||||||
|
}>;
|
||||||
|
route.component = async () => {
|
||||||
|
const component = await originalComponent();
|
||||||
|
if (!component.default) return component;
|
||||||
|
return defineComponent({
|
||||||
|
name: route.name as string,
|
||||||
|
setup(props, { attrs, slots }) {
|
||||||
|
return () => h(component.default, { ...props, ...attrs }, slots);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// 如果有redirect或者没有子路由,则直接返回
|
// 如果有redirect或者没有子路由,则直接返回
|
||||||
if (route.redirect || !route.children || route.children.length === 0) {
|
if (route.redirect || !route.children || route.children.length === 0) {
|
||||||
return route;
|
return route;
|
||||||
|
@ -3,11 +3,12 @@
|
|||||||
* 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
|
* 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Component, SetupContext } from 'vue';
|
import type { Component } from 'vue';
|
||||||
|
|
||||||
import type { BaseFormComponentType } from '@vben/common-ui';
|
import type { BaseFormComponentType } from '@vben/common-ui';
|
||||||
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
import { h } from 'vue';
|
import { defineComponent, getCurrentInstance, h, ref } from 'vue';
|
||||||
|
|
||||||
import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
|
import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
@ -41,10 +42,30 @@ const withDefaultPlaceholder = <T extends Component>(
|
|||||||
component: T,
|
component: T,
|
||||||
type: 'input' | 'select',
|
type: 'input' | 'select',
|
||||||
) => {
|
) => {
|
||||||
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
|
return defineComponent({
|
||||||
const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
|
inheritAttrs: false,
|
||||||
return h(component, { ...props, ...attrs, placeholder }, slots);
|
name: component.name,
|
||||||
};
|
setup: (props: any, { attrs, expose, slots }) => {
|
||||||
|
const placeholder =
|
||||||
|
props?.placeholder ||
|
||||||
|
attrs?.placeholder ||
|
||||||
|
$t(`ui.placeholder.${type}`);
|
||||||
|
// 透传组件暴露的方法
|
||||||
|
const innerRef = ref();
|
||||||
|
const publicApi: Recordable<any> = {};
|
||||||
|
expose(publicApi);
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
instance?.proxy?.$nextTick(() => {
|
||||||
|
for (const key in innerRef.value) {
|
||||||
|
if (typeof innerRef.value[key] === 'function') {
|
||||||
|
publicApi[key] = innerRef.value[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () =>
|
||||||
|
h(component, { ...props, ...attrs, placeholder, ref: innerRef }, slots);
|
||||||
|
},
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { VbenFormSchema } from '@vben/common-ui';
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
import type { BasicOption } from '@vben/types';
|
import type { BasicOption, Recordable } from '@vben/types';
|
||||||
|
|
||||||
import { computed, markRaw } from 'vue';
|
import { computed, markRaw, useTemplateRef } from 'vue';
|
||||||
|
|
||||||
import { AuthenticationLogin, SliderCaptcha, z } from '@vben/common-ui';
|
import { AuthenticationLogin, SliderCaptcha, z } from '@vben/common-ui';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
@ -104,12 +104,28 @@ const formSchema = computed((): VbenFormSchema[] => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const loginRef =
|
||||||
|
useTemplateRef<InstanceType<typeof AuthenticationLogin>>('loginRef');
|
||||||
|
|
||||||
|
async function onSubmit(params: Recordable<any>) {
|
||||||
|
authStore.authLogin(params).catch(() => {
|
||||||
|
// 登陆失败,刷新验证码的演示
|
||||||
|
|
||||||
|
// 使用表单API获取验证码组件实例,并调用其resume方法来重置验证码
|
||||||
|
loginRef.value
|
||||||
|
?.getFormApi()
|
||||||
|
?.getFieldComponentRef<InstanceType<typeof SliderCaptcha>>('captcha')
|
||||||
|
?.resume();
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AuthenticationLogin
|
<AuthenticationLogin
|
||||||
|
ref="loginRef"
|
||||||
:form-schema="formSchema"
|
:form-schema="formSchema"
|
||||||
:loading="authStore.loginLoading"
|
:loading="authStore.loginLoading"
|
||||||
@submit="authStore.authLogin"
|
@submit="onSubmit"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { RefSelectProps } from 'ant-design-vue/es/select';
|
||||||
|
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
import { Page } from '@vben/common-ui';
|
import { Page } from '@vben/common-ui';
|
||||||
@ -82,6 +84,7 @@ function handleClick(
|
|||||||
action:
|
action:
|
||||||
| 'batchAddSchema'
|
| 'batchAddSchema'
|
||||||
| 'batchDeleteSchema'
|
| 'batchDeleteSchema'
|
||||||
|
| 'componentRef'
|
||||||
| 'disabled'
|
| 'disabled'
|
||||||
| 'hiddenAction'
|
| 'hiddenAction'
|
||||||
| 'hiddenResetButton'
|
| 'hiddenResetButton'
|
||||||
@ -129,6 +132,11 @@ function handleClick(
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'componentRef': {
|
||||||
|
// 获取下拉组件的实例,并调用它的focus方法
|
||||||
|
formApi.getFieldComponentRef<RefSelectProps>('fieldOptions')?.focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'disabled': {
|
case 'disabled': {
|
||||||
formApi.setState({ commonConfig: { disabled: true } });
|
formApi.setState({ commonConfig: { disabled: true } });
|
||||||
break;
|
break;
|
||||||
@ -182,6 +190,7 @@ function handleClick(
|
|||||||
formApi.setState({ submitButtonOptions: { show: true } });
|
formApi.setState({ submitButtonOptions: { show: true } });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'updateActionAlign': {
|
case 'updateActionAlign': {
|
||||||
formApi.setState({
|
formApi.setState({
|
||||||
// 可以自行调整class
|
// 可以自行调整class
|
||||||
@ -189,7 +198,6 @@ function handleClick(
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'updateResetButton': {
|
case 'updateResetButton': {
|
||||||
formApi.setState({
|
formApi.setState({
|
||||||
resetButtonOptions: { disabled: true },
|
resetButtonOptions: { disabled: true },
|
||||||
@ -257,6 +265,7 @@ function handleClick(
|
|||||||
<Button @click="handleClick('batchDeleteSchema')">
|
<Button @click="handleClick('batchDeleteSchema')">
|
||||||
批量删除表单项
|
批量删除表单项
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button @click="handleClick('componentRef')">下拉组件获取焦点</Button>
|
||||||
</Space>
|
</Space>
|
||||||
<Card title="操作示例">
|
<Card title="操作示例">
|
||||||
<BaseForm />
|
<BaseForm />
|
||||||
|
@ -18,25 +18,25 @@ catalog:
|
|||||||
'@changesets/cli': ^2.28.1
|
'@changesets/cli': ^2.28.1
|
||||||
'@changesets/git': ^3.0.2
|
'@changesets/git': ^3.0.2
|
||||||
'@clack/prompts': ^0.9.1
|
'@clack/prompts': ^0.9.1
|
||||||
'@commitlint/cli': ^19.7.1
|
'@commitlint/cli': ^19.8.0
|
||||||
'@commitlint/config-conventional': ^19.7.1
|
'@commitlint/config-conventional': ^19.8.0
|
||||||
'@ctrl/tinycolor': ^4.1.0
|
'@ctrl/tinycolor': ^4.1.0
|
||||||
'@eslint/js': ^9.21.0
|
'@eslint/js': ^9.22.0
|
||||||
'@faker-js/faker': ^9.5.1
|
'@faker-js/faker': ^9.6.0
|
||||||
'@iconify/json': ^2.2.311
|
'@iconify/json': ^2.2.314
|
||||||
'@iconify/tailwind': ^1.2.0
|
'@iconify/tailwind': ^1.2.0
|
||||||
'@iconify/vue': ^4.3.0
|
'@iconify/vue': ^4.3.0
|
||||||
'@intlify/core-base': ^11.1.1
|
'@intlify/core-base': ^11.1.2
|
||||||
'@intlify/unplugin-vue-i18n': ^6.0.3
|
'@intlify/unplugin-vue-i18n': ^6.0.3
|
||||||
'@jspm/generator': ^2.5.1
|
'@jspm/generator': ^2.5.1
|
||||||
'@manypkg/get-packages': ^2.2.2
|
'@manypkg/get-packages': ^2.2.2
|
||||||
'@nolebase/vitepress-plugin-git-changelog': ^2.15.0
|
'@nolebase/vitepress-plugin-git-changelog': ^2.15.0
|
||||||
'@playwright/test': ^1.50.1
|
'@playwright/test': ^1.51.0
|
||||||
'@pnpm/workspace.read-manifest': ^1000.1.0
|
'@pnpm/workspace.read-manifest': ^1000.1.1
|
||||||
'@stylistic/stylelint-plugin': ^3.1.2
|
'@stylistic/stylelint-plugin': ^3.1.2
|
||||||
'@tailwindcss/nesting': 0.0.0-insiders.565cd3e
|
'@tailwindcss/nesting': 0.0.0-insiders.565cd3e
|
||||||
'@tailwindcss/typography': ^0.5.16
|
'@tailwindcss/typography': ^0.5.16
|
||||||
'@tanstack/vue-query': ^5.66.9
|
'@tanstack/vue-query': ^5.67.2
|
||||||
'@tanstack/vue-store': ^0.7.0
|
'@tanstack/vue-store': ^0.7.0
|
||||||
'@types/archiver': ^6.0.3
|
'@types/archiver': ^6.0.3
|
||||||
'@types/eslint': ^9.6.1
|
'@types/eslint': ^9.6.1
|
||||||
@ -45,13 +45,13 @@ catalog:
|
|||||||
'@types/lodash.clonedeep': ^4.5.9
|
'@types/lodash.clonedeep': ^4.5.9
|
||||||
'@types/lodash.get': ^4.4.9
|
'@types/lodash.get': ^4.4.9
|
||||||
'@types/lodash.isequal': ^4.5.8
|
'@types/lodash.isequal': ^4.5.8
|
||||||
'@types/node': ^22.13.5
|
'@types/node': ^22.13.10
|
||||||
'@types/nprogress': ^0.2.3
|
'@types/nprogress': ^0.2.3
|
||||||
'@types/postcss-import': ^14.0.3
|
'@types/postcss-import': ^14.0.3
|
||||||
'@types/qrcode': ^1.5.5
|
'@types/qrcode': ^1.5.5
|
||||||
'@types/sortablejs': ^1.15.8
|
'@types/sortablejs': ^1.15.8
|
||||||
'@typescript-eslint/eslint-plugin': ^8.25.0
|
'@typescript-eslint/eslint-plugin': ^8.26.0
|
||||||
'@typescript-eslint/parser': ^8.25.0
|
'@typescript-eslint/parser': ^8.26.0
|
||||||
'@vee-validate/zod': ^4.15.0
|
'@vee-validate/zod': ^4.15.0
|
||||||
'@vite-pwa/vitepress': ^0.5.3
|
'@vite-pwa/vitepress': ^0.5.3
|
||||||
'@vitejs/plugin-vue': ^5.2.1
|
'@vitejs/plugin-vue': ^5.2.1
|
||||||
@ -59,13 +59,13 @@ catalog:
|
|||||||
'@vue/reactivity': ^3.5.13
|
'@vue/reactivity': ^3.5.13
|
||||||
'@vue/shared': ^3.5.13
|
'@vue/shared': ^3.5.13
|
||||||
'@vue/test-utils': ^2.4.6
|
'@vue/test-utils': ^2.4.6
|
||||||
'@vueuse/core': ^12.7.0
|
'@vueuse/core': ^12.8.2
|
||||||
'@vueuse/motion': ^2.2.6
|
'@vueuse/motion': ^2.2.6
|
||||||
'@vueuse/integrations': ^12.7.0
|
'@vueuse/integrations': ^12.8.2
|
||||||
ant-design-vue: ^4.2.6
|
ant-design-vue: ^4.2.6
|
||||||
archiver: ^7.0.1
|
archiver: ^7.0.1
|
||||||
autoprefixer: ^10.4.20
|
autoprefixer: ^10.4.20
|
||||||
axios: ^1.8.1
|
axios: ^1.8.2
|
||||||
axios-mock-adapter: ^2.1.0
|
axios-mock-adapter: ^2.1.0
|
||||||
cac: ^6.7.14
|
cac: ^6.7.14
|
||||||
chalk: ^5.4.1
|
chalk: ^5.4.1
|
||||||
@ -78,30 +78,30 @@ catalog:
|
|||||||
cross-env: ^7.0.3
|
cross-env: ^7.0.3
|
||||||
cspell: 8.17.2
|
cspell: 8.17.2
|
||||||
cssnano: ^7.0.6
|
cssnano: ^7.0.6
|
||||||
cz-git: ^1.11.0
|
cz-git: ^1.11.1
|
||||||
czg: ^1.11.0
|
czg: ^1.11.1
|
||||||
dayjs: ^1.11.13
|
dayjs: ^1.11.13
|
||||||
defu: ^6.1.4
|
defu: ^6.1.4
|
||||||
depcheck: ^1.4.7
|
depcheck: ^1.4.7
|
||||||
dotenv: ^16.4.7
|
dotenv: ^16.4.7
|
||||||
echarts: ^5.6.0
|
echarts: ^5.6.0
|
||||||
element-plus: ^2.9.5
|
element-plus: ^2.9.6
|
||||||
eslint: ^9.21.0
|
eslint: ^9.22.0
|
||||||
eslint-config-turbo: ^2.4.4
|
eslint-config-turbo: ^2.4.4
|
||||||
eslint-plugin-command: ^0.2.7
|
eslint-plugin-command: ^0.2.7
|
||||||
eslint-plugin-eslint-comments: ^3.2.0
|
eslint-plugin-eslint-comments: ^3.2.0
|
||||||
eslint-plugin-import-x: ^4.6.1
|
eslint-plugin-import-x: ^4.6.1
|
||||||
eslint-plugin-jsdoc: ^50.6.3
|
eslint-plugin-jsdoc: ^50.6.3
|
||||||
eslint-plugin-jsonc: ^2.19.1
|
eslint-plugin-jsonc: ^2.19.1
|
||||||
eslint-plugin-n: ^17.15.1
|
eslint-plugin-n: ^17.16.2
|
||||||
eslint-plugin-no-only-tests: ^3.3.0
|
eslint-plugin-no-only-tests: ^3.3.0
|
||||||
eslint-plugin-perfectionist: ^4.9.0
|
eslint-plugin-perfectionist: ^4.10.0
|
||||||
eslint-plugin-prettier: ^5.2.3
|
eslint-plugin-prettier: ^5.2.3
|
||||||
eslint-plugin-regexp: ^2.7.0
|
eslint-plugin-regexp: ^2.7.0
|
||||||
eslint-plugin-unicorn: ^56.0.1
|
eslint-plugin-unicorn: ^56.0.1
|
||||||
eslint-plugin-unused-imports: ^4.1.4
|
eslint-plugin-unused-imports: ^4.1.4
|
||||||
eslint-plugin-vitest: ^0.5.4
|
eslint-plugin-vitest: ^0.5.4
|
||||||
eslint-plugin-vue: ^9.32.0
|
eslint-plugin-vue: ^9.33.0
|
||||||
execa: ^9.5.2
|
execa: ^9.5.2
|
||||||
find-up: ^7.0.0
|
find-up: ^7.0.0
|
||||||
get-port: ^7.1.0
|
get-port: ^7.1.0
|
||||||
@ -120,31 +120,31 @@ catalog:
|
|||||||
lucide-vue-next: ^0.469.0
|
lucide-vue-next: ^0.469.0
|
||||||
medium-zoom: ^1.1.0
|
medium-zoom: ^1.1.0
|
||||||
naive-ui: ^2.41.0
|
naive-ui: ^2.41.0
|
||||||
nitropack: ^2.10.4
|
nitropack: ^2.11.6
|
||||||
nprogress: ^0.2.0
|
nprogress: ^0.2.0
|
||||||
ora: ^8.2.0
|
ora: ^8.2.0
|
||||||
pinia: ^2.3.1
|
pinia: ^2.3.1
|
||||||
pinia-plugin-persistedstate: ^4.2.0
|
pinia-plugin-persistedstate: ^4.2.0
|
||||||
pkg-types: ^1.3.1
|
pkg-types: ^1.3.1
|
||||||
playwright: ^1.50.1
|
playwright: ^1.51.0
|
||||||
postcss: ^8.5.3
|
postcss: ^8.5.3
|
||||||
postcss-antd-fixes: ^0.2.0
|
postcss-antd-fixes: ^0.2.0
|
||||||
postcss-html: ^1.8.0
|
postcss-html: ^1.8.0
|
||||||
postcss-import: ^16.1.0
|
postcss-import: ^16.1.0
|
||||||
postcss-preset-env: ^10.1.5
|
postcss-preset-env: ^10.1.5
|
||||||
postcss-scss: ^4.0.9
|
postcss-scss: ^4.0.9
|
||||||
prettier: ^3.5.2
|
prettier: ^3.5.3
|
||||||
prettier-plugin-tailwindcss: ^0.6.11
|
prettier-plugin-tailwindcss: ^0.6.11
|
||||||
publint: ^0.2.12
|
publint: ^0.2.12
|
||||||
qrcode: ^1.5.4
|
qrcode: ^1.5.4
|
||||||
radix-vue: ^1.9.17
|
radix-vue: ^1.9.17
|
||||||
resolve.exports: ^2.0.3
|
resolve.exports: ^2.0.3
|
||||||
rimraf: ^6.0.1
|
rimraf: ^6.0.1
|
||||||
rollup: ^4.34.8
|
rollup: ^4.35.0
|
||||||
rollup-plugin-visualizer: ^5.14.0
|
rollup-plugin-visualizer: ^5.14.0
|
||||||
sass: ^1.85.1
|
sass: ^1.85.1
|
||||||
sortablejs: ^1.15.6
|
sortablejs: ^1.15.6
|
||||||
stylelint: ^16.14.1
|
stylelint: ^16.15.0
|
||||||
stylelint-config-recess-order: ^5.1.1
|
stylelint-config-recess-order: ^5.1.1
|
||||||
stylelint-config-recommended: ^14.0.1
|
stylelint-config-recommended: ^14.0.1
|
||||||
stylelint-config-recommended-scss: ^14.1.0
|
stylelint-config-recommended-scss: ^14.1.0
|
||||||
@ -163,9 +163,9 @@ catalog:
|
|||||||
unbuild: ^3.5.0
|
unbuild: ^3.5.0
|
||||||
unplugin-element-plus: ^0.9.1
|
unplugin-element-plus: ^0.9.1
|
||||||
vee-validate: ^4.15.0
|
vee-validate: ^4.15.0
|
||||||
vite: ^6.2.0
|
vite: ^6.2.1
|
||||||
vite-plugin-compression: ^0.5.1
|
vite-plugin-compression: ^0.5.1
|
||||||
vite-plugin-dts: ^4.5.1
|
vite-plugin-dts: ^4.5.3
|
||||||
vite-plugin-html: ^3.2.2
|
vite-plugin-html: ^3.2.2
|
||||||
vite-plugin-lazy-import: ^1.0.7
|
vite-plugin-lazy-import: ^1.0.7
|
||||||
vite-plugin-pwa: ^0.21.1
|
vite-plugin-pwa: ^0.21.1
|
||||||
@ -175,12 +175,12 @@ catalog:
|
|||||||
vitest: ^2.1.9
|
vitest: ^2.1.9
|
||||||
vue: ^3.5.13
|
vue: ^3.5.13
|
||||||
vue-eslint-parser: ^9.4.3
|
vue-eslint-parser: ^9.4.3
|
||||||
vue-i18n: ^11.1.1
|
vue-i18n: ^11.1.2
|
||||||
vue-json-viewer: ^3.0.4
|
vue-json-viewer: ^3.0.4
|
||||||
vue-router: ^4.5.0
|
vue-router: ^4.5.0
|
||||||
vue-tippy: ^6.6.0
|
vue-tippy: ^6.6.0
|
||||||
vue-tsc: 2.1.10
|
vue-tsc: 2.1.10
|
||||||
vxe-pc-ui: ^4.3.99
|
vxe-pc-ui: ^4.4.8
|
||||||
vxe-table: 4.10.0
|
vxe-table: 4.10.0
|
||||||
watermark-js-plus: ^1.5.8
|
watermark-js-plus: ^1.5.8
|
||||||
zod: ^3.24.2
|
zod: ^3.24.2
|
||||||
|
Loading…
Reference in New Issue
Block a user