fix: 修复 Admin tsconfig 类型校验

This commit is contained in:
sunlei 2026-05-18 22:11:35 +08:00
parent 51464d85d5
commit c82ffa2b6c
19 changed files with 59 additions and 36 deletions

View File

@ -100,6 +100,16 @@ const PreviewGroup = defineAsyncComponent(() =>
import('antdv-next/dist/image/index').then((res) => res.ImagePreviewGroup),
);
type CropperExpose = {
getCropImage: (
format?: 'image/jpeg' | 'image/png',
quality?: number,
outputType?: 'base64' | 'blob',
targetWidth?: number,
targetHeight?: number,
) => Promise<Blob | string | undefined>;
};
const withDefaultPlaceholder = <T extends Component>(
component: T,
type: 'input' | 'select',
@ -293,7 +303,7 @@ const withPreviewUpload = () => {
let objectUrl: null | string = null;
const open = ref<boolean>(true);
const cropperRef = ref<InstanceType<typeof VCropper> | null>(null);
const cropperRef = ref<CropperExpose | null>(null);
const closeModal = () => {
open.value = false;

View File

@ -2,7 +2,6 @@
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web-app.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"#/*": ["./src/*"]
}

View File

@ -1,6 +1,6 @@
import { defineConfig } from '@vben/vite-config';
export default defineConfig(async () => {
const config = defineConfig(async () => {
return {
application: {},
vite: {
@ -17,4 +17,6 @@ export default defineConfig(async () => {
},
},
};
});
}) as unknown;
export default config;

View File

@ -8,7 +8,6 @@
"moduleDetection": "force",
"experimentalDecorators": true,
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",

View File

@ -5,7 +5,6 @@
"compilerOptions": {
"composite": false,
"lib": ["ESNext"],
"baseUrl": "./",
"moduleResolution": "bundler",
"types": ["node"],
"noImplicitAny": true

View File

@ -15,6 +15,7 @@
"base.json",
"library.json",
"node.json",
"types",
"web-app.json",
"web.json"
],

View File

@ -0,0 +1,10 @@
/// <reference types="vite/client" />
declare module '*.vue' {
const component: import('vue').DefineComponent<
Record<string, unknown>,
Record<string, unknown>,
Record<string, unknown>
>;
export default component;
}

View File

@ -3,6 +3,6 @@
"display": "Web Application",
"extends": "./web.json",
"compilerOptions": {
"types": ["vite/client", "@vben/types/global"]
"types": ["@vben/tsconfig/types/vben-web", "@vben/types/global"]
}
}

View File

@ -8,7 +8,7 @@
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"useDefineForClassFields": true,
"moduleResolution": "bundler",
"types": ["vite/client"],
"types": ["@vben/tsconfig/types/vben-web"],
"declaration": false
}
}

View File

@ -35,7 +35,8 @@ async function loadConditionPlugins(conditionPlugins: ConditionPlugin[]) {
const plugins: PluginOption[] = [];
for (const conditionPlugin of conditionPlugins) {
if (conditionPlugin.condition) {
const realPlugins = await conditionPlugin.plugins();
// 第三方插件可能绑定到不同的 Vite 类型实例,运行时统一收敛为 Vite PluginOption。
const realPlugins = (await conditionPlugin.plugins()) as PluginOption[];
plugins.push(...realPlugins);
}
}

View File

@ -14,7 +14,7 @@ async function viteVxeTableImportsPlugin(): Promise<PluginOption> {
}),
],
}),
];
] as unknown as PluginOption;
}
export { viteVxeTableImportsPlugin };

View File

@ -1,7 +1,6 @@
import type { PluginVisualizerOptions } from 'rollup-plugin-visualizer';
import type {
ConfigEnv,
PluginOption,
UserConfig,
UserConfigFnPromise,
} from 'vite';
@ -138,7 +137,7 @@ interface ConditionPlugin {
*
* @description Promise
*/
plugins: () => PluginOption[] | PromiseLike<PluginOption[]>;
plugins: () => PromiseLike<unknown[]> | unknown[];
}
/**

View File

@ -4,6 +4,7 @@ import { FormApi } from '../src/form-api';
describe('formApi', () => {
let formApi: FormApi;
const componentRefMap = () => new Map<string, unknown>();
beforeEach(() => {
formApi = new FormApi();
@ -41,7 +42,7 @@ describe('formApi', () => {
values: { name: 'test' },
};
await formApi.mount(formActions);
formApi.mount(formActions, componentRefMap());
expect(formApi.isMounted).toBe(true);
expect(formApi.form).toEqual(formActions);
});
@ -52,7 +53,7 @@ describe('formApi', () => {
values: { name: 'test' },
};
await formApi.mount(formActions);
formApi.mount(formActions, componentRefMap());
const values = await formApi.getValues();
expect(values).toEqual({ name: 'test' });
});
@ -65,7 +66,7 @@ describe('formApi', () => {
values: { name: 'test' },
};
await formApi.mount(formActions);
formApi.mount(formActions, componentRefMap());
await formApi.setFieldValue('name', 'new value');
expect(setFieldValueMock).toHaveBeenCalledWith(
'name',
@ -82,7 +83,7 @@ describe('formApi', () => {
values: { name: 'test' },
};
await formApi.mount(formActions);
formApi.mount(formActions, componentRefMap());
await formApi.resetForm();
expect(resetFormMock).toHaveBeenCalled();
});
@ -100,7 +101,7 @@ describe('formApi', () => {
};
formApi.setState(state);
await formApi.mount(formActions);
formApi.mount(formActions, componentRefMap());
const result = await formApi.submitForm();
expect(formActions.submitForm).toHaveBeenCalled();
@ -120,7 +121,7 @@ describe('formApi', () => {
validate: validateMock,
};
await formApi.mount(formActions);
formApi.mount(formActions, componentRefMap());
const isValid = await formApi.validate();
expect(validateMock).toHaveBeenCalled();
expect(isValid).toBe(true);

View File

@ -2,7 +2,6 @@
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@vben-core/shadcn-ui/*": ["./src/*"]
}

View File

@ -1,9 +1,8 @@
import type { TabsProps } from './types';
import type { ComponentPublicInstance } from 'vue';
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
import { VbenScrollbar } from '@vben-core/shadcn-ui';
import { useDebounceFn } from '@vueuse/core';
type DomElement = Element | null | undefined;
@ -12,7 +11,7 @@ export function useTabsViewScroll(props: TabsProps) {
let resizeObserver: null | ResizeObserver = null;
let mutationObserver: MutationObserver | null = null;
let tabItemCount = 0;
const scrollbarRef = ref<InstanceType<typeof VbenScrollbar> | null>(null);
const scrollbarRef = ref<ComponentPublicInstance | null>(null);
const scrollViewportEl = ref<DomElement>(null);
const showScrollButton = ref(false);
const scrollIsAtLeft = ref(true);

View File

@ -1,3 +1,5 @@
import type { AxiosResponse } from 'axios';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
@ -92,7 +94,8 @@ describe('requestClient', () => {
mock.onGet('/test/download').reply(200, mockFileContent);
const res = await requestClient.download('/test/download');
const res =
await requestClient.download<AxiosResponse<Blob>>('/test/download');
expect(res.data).toBeInstanceOf(Blob);
});

View File

@ -38,15 +38,15 @@ const mockRoutes = [
describe('hasAuthority', () => {
it('should return true if there is no authority defined', () => {
expect(hasAuthority(mockRoutes[2], ['admin'])).toBe(true);
expect(hasAuthority(mockRoutes[2]!, ['admin'])).toBe(true);
});
it('should return true if the user has the required authority', () => {
expect(hasAuthority(mockRoutes[0], ['admin'])).toBe(true);
expect(hasAuthority(mockRoutes[0]!, ['admin'])).toBe(true);
});
it('should return false if the user does not have the required authority', () => {
expect(hasAuthority(mockRoutes[1], ['user'])).toBe(false);
expect(hasAuthority(mockRoutes[1]!, ['user'])).toBe(false);
});
});

View File

@ -27,12 +27,8 @@ function generateMenus(
// 获取最终的路由路径
const path = finalRoutesMap[route.name as string] ?? route.path ?? '';
const {
meta = {} as RouteMeta,
name: routeName,
redirect,
children = [],
} = route;
const { name: routeName, redirect, children = [] } = route;
const meta = (route.meta ?? {}) as unknown as Partial<RouteMeta>;
const {
activeIcon,
badge,

View File

@ -1,5 +1,7 @@
import type { RouteRecordRaw } from 'vue-router';
import type { RouteMeta } from '@vben-core/typings';
import { filterTree, mapTree } from '@vben-core/shared/utils';
/**
@ -34,7 +36,8 @@ async function generateRoutesByFrontend(
* @param access
*/
function hasAuthority(route: RouteRecordRaw, access: string[]) {
const authority = route.meta?.authority;
const meta = route.meta as Partial<RouteMeta> | undefined;
const authority = meta?.authority;
if (!authority) {
return true;
}
@ -48,10 +51,12 @@ function hasAuthority(route: RouteRecordRaw, access: string[]) {
* @param route
*/
function menuHasVisibleWithForbidden(route: RouteRecordRaw) {
const meta = route.meta as Partial<RouteMeta> | undefined;
return (
!!route.meta?.authority &&
Reflect.has(route.meta || {}, 'menuVisibleWithForbidden') &&
!!route.meta?.menuVisibleWithForbidden
!!meta?.authority &&
Reflect.has(meta || {}, 'menuVisibleWithForbidden') &&
!!meta?.menuVisibleWithForbidden
);
}