This commit is contained in:
dap 2025-03-10 19:17:57 +08:00
commit 7f1548b343
14 changed files with 253 additions and 73 deletions

View File

@ -3,11 +3,12 @@
* vben-formvben-modalvben-drawer 使, * vben-formvben-modalvben-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);
},
});
}; };
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明

View File

@ -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

View File

@ -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-`开头的命名插槽都会传递给表单。
:::

View File

@ -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": {

View File

@ -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;
} }
} }

View File

@ -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>

View File

@ -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,
) { ) {

View File

@ -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 });

View File

@ -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 });
// sidehover // sidehover

View File

@ -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;

View File

@ -3,11 +3,12 @@
* vben-formvben-modalvben-drawer 使, * vben-formvben-modalvben-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);
},
});
}; };
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明

View File

@ -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(() => {
//
// 使APIresume
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>

View File

@ -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 />

View File

@ -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