diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 000000000..951380ecb --- /dev/null +++ b/.cursorrules @@ -0,0 +1,135 @@ +You are an expert in TypeScript, Node.js, Vite, Vue.js, Vue Router, Pinia, VueUse, and UI frameworks (Ant Design Vue, Element Plus, Naive UI), with a deep understanding of best practices and performance optimization techniques in these technologies. + +When analyzing and modifying code, you should: + +Package Structure Understanding +- Core Package (@core): + - Analyze base/ for core utilities and implementations + - Review ui-kit/ for framework-agnostic components + - Examine composables/ for shared Vue hooks + - Check preferences/ for app-wide settings +- Utility Packages: + - Understand utils/ for common functions + - Review types/ for shared type definitions + - Check constants/ for shared constants + - Examine effects/ for shared animations +- Feature Packages: + - Analyze stores/ for Pinia store implementations + - Review locales/ for i18n resources + - Check icons/ for icon components + - Examine styles/ for theme implementations + +Framework Adaptation Patterns +- UI Framework Handling: + - Identify the target UI framework in apps/ (Ant Design Vue, Element Plus, Naive UI) + - Identify the target UI framework in packages/ (packages/@core/ui-kit/**) + - Use framework-specific component patterns + - Maintain consistent APIs across frameworks + - Implement proper component props + - Handle framework-specific events + - Follow framework-specific styling + +Component Development Rules +- Base Components: + - Use framework-agnostic design where possible + - Implement proper type definitions + - Include accessibility features + - Handle component composition + - Manage component state + - Document component APIs +- Framework Components: + - Follow framework conventions + - Use framework-specific features + - Implement proper slots + - Handle framework events + - Use framework themes + - Document framework specifics + +State and Store Patterns +- Store Implementation: + - Use Pinia store patterns + - Implement proper typing + - Handle state persistence + - Manage store modules + - Handle store reset + - Document store usage +- State Management: + - Use composition store helpers + - Implement state watchers + - Handle state updates + - Manage side effects + - Document state flow + - Test store functionality + +Composable Development +- Hook Patterns: + - Create reusable hooks + - Implement proper typing + - Handle lifecycle + - Manage dependencies + - Document usage + - Test hook behavior +- Utility Functions: + - Create pure functions + - Use proper typing + - Handle edge cases + - Document parameters + - Test functionality + - Consider performance + +Style and Theme Handling +- Theme Implementation: + - Support dark mode + - Handle framework themes + - Use CSS variables + - Implement transitions + - Handle responsive styles + - Document theme usage +- Style Management: + - Use framework classes + - Handle style conflicts + - Implement utilities + - Manage overrides + - Document style usage + - Test theme switching + +Code Quality Standards +- TypeScript Usage: + - Use strict mode + - Implement interfaces + - Handle type guards + - Document types + - Test type safety + - Maintain type files +- Testing Requirements: + - Write unit tests + - Test components + - Test utilities + - Test hooks + - Document testing + - Maintain coverage + +Response Guidelines +- Code Changes: + - Explain modifications + - Show code examples + - Document impacts + - Suggest alternatives + - Consider performance + - Note security implications +- Documentation: + - Update comments + - Provide examples + - Document APIs + - Note changes + - Include references + - Maintain clarity + +When making improvements: +- Follow existing patterns +- Maintain consistency +- Consider compatibility +- Think about scaling +- Document changes +- Test thoroughly + diff --git a/apps/web-antd/src/adapter/component/index.ts b/apps/web-antd/src/adapter/component/index.ts index 7ff499c2f..05d158488 100644 --- a/apps/web-antd/src/adapter/component/index.ts +++ b/apps/web-antd/src/adapter/component/index.ts @@ -5,8 +5,6 @@ import type { BaseFormComponentType } from '@vben/common-ui'; -import type { CustomComponentType } from '#/components/form/types'; - import type { Component, SetupContext } from 'vue'; import { h } from 'vue'; @@ -38,8 +36,6 @@ import { Upload, } from 'ant-design-vue'; -import { registerComponent as registerCustomFormComponent } from '#/components/form/component-map'; - const withDefaultPlaceholder = ( component: T, type: 'input' | 'select', @@ -52,6 +48,7 @@ const withDefaultPlaceholder = ( // 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 export type ComponentType = + | 'ApiCheckbox' | 'ApiSelect' | 'ApiTreeSelect' | 'AutoComplete' @@ -77,14 +74,26 @@ export type ComponentType = | 'TimePicker' | 'TreeSelect' | 'Upload' - | BaseFormComponentType - | CustomComponentType; + | BaseFormComponentType; async function initComponentAdapter() { const components: Partial> = { // 如果你的组件体积比较大,可以使用异步加载 // Button: () => // import('xxx').then((res) => res.Button), + + ApiCheckbox: (props, { attrs, slots }) => { + return h( + ApiComponent, + { + ...props, + ...attrs, + component: CheckboxGroup, + modelPropName: 'value', + }, + slots, + ); + }, ApiSelect: (props, { attrs, slots }) => { return h( ApiComponent, @@ -94,8 +103,8 @@ async function initComponentAdapter() { ...attrs, component: Select, loadingSlot: 'suffixIcon', - visibleEvent: 'onDropdownVisibleChange', modelPropName: 'value', + visibleEvent: 'onVisibleChange', }, slots, ); @@ -154,9 +163,6 @@ async function initComponentAdapter() { Upload, }; - // 注册自定义组件 - registerCustomFormComponent(components); - // 将组件注册到全局共享状态中 globalShareState.setComponents(components); diff --git a/apps/web-antd/src/api/infra/file/index.ts b/apps/web-antd/src/api/infra/file/index.ts new file mode 100644 index 000000000..3acf7ebea --- /dev/null +++ b/apps/web-antd/src/api/infra/file/index.ts @@ -0,0 +1,46 @@ +import { type PageParam, requestClient } from '#/api/request'; + +export namespace FileApi { + export interface FilePageReqVO extends PageParam { + path?: string; + type?: string; + createTime?: Date[]; + } + + // 文件预签名地址 Response VO + export interface FilePresignedUrlRespVO { + // 文件配置编号 + configId: number; + // 文件上传 URL + uploadUrl: string; + // 文件 URL + url: string; + } +} + +// 查询文件列表 +export function getFilePage(params: FileApi.FilePageReqVO) { + return requestClient.get('/infra/file/page', { params }); +} + +// 删除文件 +export function deleteFile(id: number) { + return requestClient.delete(`/infra/file/delete?id=${id}`); +} + +// 获取文件预签名地址 +export function getFilePresignedUrl(path: string) { + return requestClient.get( + '/infra/file/presigned-url', + { params: { path } }, + ); +} + +export function createFile(data: any) { + return requestClient.post('/infra/file/create', data); +} + +// 上传文件 +export function uploadFile(data: any) { + return requestClient.upload('/infra/file/upload', data); +} diff --git a/apps/web-antd/src/components/description/description.vue b/apps/web-antd/src/components/description/description.vue deleted file mode 100644 index ec5e220ed..000000000 --- a/apps/web-antd/src/components/description/description.vue +++ /dev/null @@ -1,77 +0,0 @@ - - diff --git a/apps/web-antd/src/components/description/index.ts b/apps/web-antd/src/components/description/index.ts deleted file mode 100644 index 745e7550a..000000000 --- a/apps/web-antd/src/components/description/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as Description } from './description.vue'; -export type * from './types'; diff --git a/apps/web-antd/src/components/description/types.d.ts b/apps/web-antd/src/components/description/types.d.ts deleted file mode 100644 index c5120b8e8..000000000 --- a/apps/web-antd/src/components/description/types.d.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { CollapseContainerOptions } from '@/components/Container'; -import type { DescriptionsProps } from 'ant-design-vue/es/descriptions'; - -import type { CSSProperties, VNode } from 'vue'; - -export interface DescItem { - labelMinWidth?: number; - contentMinWidth?: number; - labelStyle?: CSSProperties; - field: string; - label: JSX.Element | string | VNode; - // Merge column - span?: number; - show?: (...arg: any) => boolean; - // render - render?: ( - val: any, - data: Recordable, - ) => Element | JSX.Element | number | string | undefined | VNode; - component: string; - componentProps?: any; - children?: DescItem[]; -} - -export interface DescriptionProps extends DescriptionsProps { - // Whether to include the collapse component - useCollapse?: boolean; - /** - * item configuration - * @type DescItem - */ - schema: DescItem[]; - /** - * 数据 - * @type object - */ - data: Recordable; - /** - * Built-in CollapseContainer component configuration - * @type CollapseContainerOptions - */ - collapseOptions?: CollapseContainerOptions; -} - -export interface DescInstance { - setDescProps(descProps: Partial): void; -} - -export type Register = (descInstance: DescInstance) => void; - -/** - * @description: - */ -export type UseDescReturnType = [Register, DescInstance]; diff --git a/apps/web-antd/src/components/form/component-map.ts b/apps/web-antd/src/components/form/component-map.ts deleted file mode 100644 index ecaf9cd36..000000000 --- a/apps/web-antd/src/components/form/component-map.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { CustomComponentType } from './types'; - -import type { Component } from 'vue'; - -import { capitalizeFirstLetter, kebabToCamelCase } from '@vben/utils'; - -const componentMap = new Map(); -// import.meta.glob() 直接引入所有的模块 Vite 独有的功能 -const modules = import.meta.glob('./components/**/*.vue', { eager: true }); -// 加入到路由集合中 -Object.keys(modules).forEach((key) => { - if (!key.includes('-ignore')) { - const mod = (modules as any)[key].default || {}; - // ./components/ApiDict.vue - // 获取ApiDict - const compName = key.replace('./components/', '').replace('.vue', ''); - componentMap.set(capitalizeFirstLetter(kebabToCamelCase(compName)), mod); - } -}); - -export function add(compName: string, component: Component) { - componentMap.set(compName, component); -} - -export function del(compName: string) { - componentMap.delete(compName); -} -/** - * 注册组件 - * @param components - */ -export const registerComponent = (components: any) => { - componentMap.forEach((value, key) => { - components[key] = value as Component; - }); -}; -export { componentMap }; diff --git a/apps/web-antd/src/components/form/components/api-checkbox-group.vue b/apps/web-antd/src/components/form/components/api-checkbox-group.vue deleted file mode 100644 index 41e53679a..000000000 --- a/apps/web-antd/src/components/form/components/api-checkbox-group.vue +++ /dev/null @@ -1,147 +0,0 @@ - - - diff --git a/apps/web-antd/src/components/form/components/api-dict.vue b/apps/web-antd/src/components/form/components/api-dict.vue deleted file mode 100644 index 109760acd..000000000 --- a/apps/web-antd/src/components/form/components/api-dict.vue +++ /dev/null @@ -1,54 +0,0 @@ - - diff --git a/apps/web-antd/src/components/form/components/api-radio-group.vue b/apps/web-antd/src/components/form/components/api-radio-group.vue deleted file mode 100644 index da2ace231..000000000 --- a/apps/web-antd/src/components/form/components/api-radio-group.vue +++ /dev/null @@ -1,156 +0,0 @@ - - - diff --git a/apps/web-antd/src/components/form/components/api-select.vue b/apps/web-antd/src/components/form/components/api-select.vue deleted file mode 100644 index ee02d1b77..000000000 --- a/apps/web-antd/src/components/form/components/api-select.vue +++ /dev/null @@ -1,163 +0,0 @@ - - - diff --git a/apps/web-antd/src/components/form/components/api-tree-select.vue b/apps/web-antd/src/components/form/components/api-tree-select.vue deleted file mode 100644 index 776ce7720..000000000 --- a/apps/web-antd/src/components/form/components/api-tree-select.vue +++ /dev/null @@ -1,140 +0,0 @@ - - - diff --git a/apps/web-antd/src/components/form/index.ts b/apps/web-antd/src/components/form/index.ts deleted file mode 100644 index 750820af1..000000000 --- a/apps/web-antd/src/components/form/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default as ApiCheckboxGroup } from './components/api-checkbox-group.vue'; -export { default as ApiDict } from './components/api-dict.vue'; -export { default as ApiRadioGroup } from './components/api-radio-group.vue'; -export { default as ApiSelect } from './components/api-select.vue'; -export { default as ApiTreeSelect } from './components/api-tree-select.vue'; diff --git a/apps/web-antd/src/components/form/types/index.d.ts b/apps/web-antd/src/components/form/types/index.d.ts deleted file mode 100644 index e60e17e0f..000000000 --- a/apps/web-antd/src/components/form/types/index.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type CustomComponentType = - | 'ApiCheckboxGroup' - | 'ApiDict' - | 'ApiRadioGroup' - | 'ApiSelect' - | 'ApiTreeSelect'; diff --git a/apps/web-antd/src/components/view/component-map.ts b/apps/web-antd/src/components/view/component-map.ts deleted file mode 100644 index f0a1b2a86..000000000 --- a/apps/web-antd/src/components/view/component-map.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { Component } from 'vue'; - -import { toPascalCase } from '#/util/tool'; - -const componentMap = new Map(); -// import.meta.glob() 直接引入所有的模块 Vite 独有的功能 -const modules = import.meta.glob('./components/**/*.vue', { eager: true }); -// 加入到路由集合中 -Object.keys(modules).forEach((key) => { - if (!key.includes('-ignore')) { - const mod = (modules as any)[key].default || {}; - // ./components/ApiDict.vue - // 获取ApiDict - const compName = key.replace('./components/', '').replace('.vue', ''); - componentMap.set(toPascalCase(compName), mod); - } -}); - -export function add(compName: string, component: Component) { - componentMap.set(compName, component); -} - -export function del(compName: string) { - componentMap.delete(compName); -} - -export { componentMap }; diff --git a/apps/web-antd/src/components/view/components/api-checkbox-group.vue b/apps/web-antd/src/components/view/components/api-checkbox-group.vue deleted file mode 100644 index dbca93f94..000000000 --- a/apps/web-antd/src/components/view/components/api-checkbox-group.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/apps/web-antd/src/components/view/components/api-dict.vue b/apps/web-antd/src/components/view/components/api-dict.vue deleted file mode 100644 index d4ec0df61..000000000 --- a/apps/web-antd/src/components/view/components/api-dict.vue +++ /dev/null @@ -1,64 +0,0 @@ - - - diff --git a/apps/web-antd/src/components/view/components/api-radio-group.vue b/apps/web-antd/src/components/view/components/api-radio-group.vue deleted file mode 100644 index dbca93f94..000000000 --- a/apps/web-antd/src/components/view/components/api-radio-group.vue +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/apps/web-antd/src/components/view/components/api-select.vue b/apps/web-antd/src/components/view/components/api-select.vue deleted file mode 100644 index de4177471..000000000 --- a/apps/web-antd/src/components/view/components/api-select.vue +++ /dev/null @@ -1,154 +0,0 @@ - - - diff --git a/apps/web-antd/src/components/view/components/api-tree-select.vue b/apps/web-antd/src/components/view/components/api-tree-select.vue deleted file mode 100644 index c581a06e6..000000000 --- a/apps/web-antd/src/components/view/components/api-tree-select.vue +++ /dev/null @@ -1,95 +0,0 @@ - - - diff --git a/apps/web-antd/src/views/infra/codegen/codegen.data.ts b/apps/web-antd/src/views/infra/codegen/codegen.data.ts index bc10fe249..a93bb5f46 100644 --- a/apps/web-antd/src/views/infra/codegen/codegen.data.ts +++ b/apps/web-antd/src/views/infra/codegen/codegen.data.ts @@ -2,7 +2,7 @@ import type { VxeGridProps } from '#/adapter/vxe-table'; import type { CodegenApi } from '#/api/infra/codegen'; import { type VbenFormProps, z } from '@vben/common-ui'; -import { useUserStore } from '@vben/stores'; +import { useDictStore, useUserStore } from '@vben/stores'; import { getDataSourceConfigList } from '#/api/infra/data-source-config'; import { $t } from '#/locales'; @@ -88,21 +88,11 @@ export namespace CodegenImportTableModalData { fieldName: 'dataSourceConfigId', component: 'ApiSelect', componentProps: { - defaultSelectedFirst: true, - allowClear: true, placeholder: '请选择数据源', api: getDataSourceConfigList, labelField: 'name', valueField: 'id', }, - componentEvents: (events, formApi) => { - return { - optionsChange: (value: any) => { - // 设置默认选中第一个 - formApi.setFieldValue('dataSourceConfigId', value[0].id); - }, - }; - }, }, { label: '表名称', @@ -169,37 +159,47 @@ export namespace CodegenOptionsModalData { { label: '生成模版', fieldName: 'template', - component: 'ApiDict', + component: 'ApiSelect', componentProps: { - code: DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE, class: 'w-full', placeholder: '请选择生成模版', + api: () => { + return useDictStore().getDictOptions( + DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE, + ); + }, }, - rules: z.string().min(1, { message: '生成模版不能为空' }), + rules: 'required', defaultValue: '1', }, { label: '前端类型', fieldName: 'frontType', - component: 'ApiDict', + component: 'ApiSelect', componentProps: { - code: DICT_TYPE.INFRA_CODEGEN_FRONT_TYPE, class: 'w-full', placeholder: '请选择前端类型', + api: () => { + return useDictStore().getDictOptions( + DICT_TYPE.INFRA_CODEGEN_FRONT_TYPE, + ); + }, }, - rules: z.string().min(1, { message: '前端类型不能为空' }), + rules: 'required', defaultValue: '31', }, { label: '生成场景', fieldName: 'scene', - component: 'ApiDict', + component: 'ApiSelect', componentProps: { - code: DICT_TYPE.INFRA_CODEGEN_SCENE, class: 'w-full', placeholder: '请选择生成场景', + api: () => { + return useDictStore().getDictOptions(DICT_TYPE.INFRA_CODEGEN_SCENE); + }, }, - rules: z.string().min(1, { message: '生成场景不能为空' }), + rules: 'required', defaultValue: '1', }, { @@ -214,16 +214,17 @@ export namespace CodegenOptionsModalData { }, labelField: 'name', valueField: 'id', + childrenField: 'children', placeholder: '请选择上级菜单', }, - rules: z.number().min(1, { message: '上级菜单不能为空' }), + rules: 'required', defaultValue: null, }, { label: '模块名', fieldName: 'moduleName', component: 'Input', - rules: z.string().min(1, { message: '模块名不能为空' }), + rules: 'required', componentProps: { placeholder: '请输入模块名', }, @@ -232,22 +233,19 @@ export namespace CodegenOptionsModalData { label: '业务名', fieldName: 'businessName', component: 'Input', - rules: z.string().min(1, { message: '业务名不能为空' }), + rules: 'required', componentProps: { placeholder: '请输入业务名', }, }, - { - label: '类名', - fieldName: 'className', - component: 'Input', - rules: z.string().min(1, { message: '类名不能为空' }), - }, { label: '类描述', fieldName: 'classComment', component: 'Input', - rules: z.string().min(1, { message: '类描述不能为空' }), + rules: 'required', + componentProps: { + placeholder: '请输入类描述', + }, }, ]; } diff --git a/apps/web-antd/src/views/infra/codegen/components/import-table-modal.vue b/apps/web-antd/src/views/infra/codegen/components/import-table-modal.vue index 93d644e9f..5276a7edd 100644 --- a/apps/web-antd/src/views/infra/codegen/components/import-table-modal.vue +++ b/apps/web-antd/src/views/infra/codegen/components/import-table-modal.vue @@ -85,9 +85,6 @@ const [Modal, modalApi] = useVbenModal({ confirmLoading: confirmLoading.value, closeOnClickModal: false, closeOnPressEscape: false, - onOpened: async () => { - gridApi.reload(await gridApi.formApi.getValues()); - }, onConfirm: async () => { modalApi.setState({ confirmLoading: true }); const formValues = await gridApi.formApi.getValues(); diff --git a/apps/web-antd/src/views/infra/file-config/file-config.data.ts b/apps/web-antd/src/views/infra/file-config/file-config.data.ts index 53e98d521..72156e54c 100644 --- a/apps/web-antd/src/views/infra/file-config/file-config.data.ts +++ b/apps/web-antd/src/views/infra/file-config/file-config.data.ts @@ -3,6 +3,8 @@ import type { FileConfigApi } from '#/api/infra/file-config'; import { h } from 'vue'; +import { useDictStore } from '@vben/stores'; + import { Tag } from 'ant-design-vue'; import { type VbenFormProps, z } from '#/adapter/form'; @@ -24,11 +26,13 @@ export const formSchema: VbenFormProps['schema'] = [ { fieldName: 'storage', label: '储存器', - component: 'ApiDict', + component: 'ApiSelect', componentProps: { - code: DICT_TYPE.INFRA_FILE_STORAGE, class: 'w-full', placeholder: '请选择储存器', + api: () => { + return useDictStore().getDictOptions(DICT_TYPE.INFRA_FILE_STORAGE); + }, }, }, { @@ -118,14 +122,15 @@ export const editFormSchema: VbenFormProps['schema'] = [ { fieldName: 'storage', label: '储存器', - component: 'ApiDict', + component: 'ApiSelect', componentProps: { - code: DICT_TYPE.INFRA_FILE_STORAGE, class: 'w-full', placeholder: '请选择储存器', - numberToString: true, + api: () => { + return useDictStore().getDictOptions(DICT_TYPE.INFRA_FILE_STORAGE); + }, }, - rules: z.string().min(1, '请选择储存器').or(z.number()), + rules: 'required', defaultValue: '1', }, { @@ -135,7 +140,7 @@ export const editFormSchema: VbenFormProps['schema'] = [ componentProps: { placeholder: '请输入基础路径', }, - rules: z.string().min(1, '请输入基础路径'), + rules: 'required', dependencies: { triggerFields: ['storage'], if: (values: Record) => { @@ -150,7 +155,7 @@ export const editFormSchema: VbenFormProps['schema'] = [ componentProps: { placeholder: '请输入主机地址', }, - rules: z.string().min(1, '请输入主机地址'), + rules: 'required', dependencies: { triggerFields: ['storage'], if: (values: Record) => [11, 12].includes(values.storage), @@ -163,7 +168,7 @@ export const editFormSchema: VbenFormProps['schema'] = [ componentProps: { placeholder: '请输入主机端口', }, - rules: z.string().min(1, '请输入主机端口'), + rules: 'required', dependencies: { triggerFields: ['storage'], if: (values: Record) => [11, 12].includes(values.storage), diff --git a/apps/web-antd/src/views/infra/file/components/upload-form.vue b/apps/web-antd/src/views/infra/file/components/upload-form.vue new file mode 100644 index 000000000..54d83c24e --- /dev/null +++ b/apps/web-antd/src/views/infra/file/components/upload-form.vue @@ -0,0 +1,22 @@ + + + diff --git a/apps/web-antd/src/views/infra/file/file.data.ts b/apps/web-antd/src/views/infra/file/file.data.ts new file mode 100644 index 000000000..5a8915a12 --- /dev/null +++ b/apps/web-antd/src/views/infra/file/file.data.ts @@ -0,0 +1,52 @@ +import type { VxeGridProps } from '@vben/plugins/vxe-table'; + +import type { VbenFormProps } from '#/adapter/form'; + +import { type FileApi, uploadFile } from '#/api/infra/file'; + +/** + * 文件 表格查询表单配置 + */ +export const formSchema: VbenFormProps['schema'] = []; + +/** + * 文件 表格配置 + */ +export const tableColumns: VxeGridProps['columns'] = [ + { + fixed: 'left', + type: 'checkbox', + width: 50, + }, + { + fixed: 'left', + type: 'seq', + width: 50, + }, + { field: 'name', title: '文件名称' }, + { field: 'path', title: '文件路径' }, + { field: 'url', title: '文件 URL' }, + { field: 'type', title: '文件类型' }, + { field: 'size', title: '文件大小' }, + { field: 'createTime', title: '创建时间' }, +]; + +/** + * 文件 编辑表单配置 + */ +export const editFormSchema: VbenFormProps['schema'] = []; + +/** + * 文件 上传表单配置 + */ +export const uploadFormSchema: VbenFormProps['schema'] = [ + { + fieldName: 'file', + label: '文件', + component: 'ApiUpload', + componentProps: { + api: uploadFile, + uploadMode: 'file', + }, + }, +]; diff --git a/apps/web-antd/src/views/infra/file/index.vue b/apps/web-antd/src/views/infra/file/index.vue new file mode 100644 index 000000000..8049f1d5f --- /dev/null +++ b/apps/web-antd/src/views/infra/file/index.vue @@ -0,0 +1,107 @@ + + + diff --git a/packages/effects/common-ui/src/components/api-component/api-component.vue b/packages/effects/common-ui/src/components/api-component/api-component.vue index d1d42ad7c..5a40513d0 100644 --- a/packages/effects/common-ui/src/components/api-component/api-component.vue +++ b/packages/effects/common-ui/src/components/api-component/api-component.vue @@ -81,6 +81,7 @@ const emit = defineEmits<{ const modelValue = defineModel({ default: '' }); const attrs = useAttrs(); +const { class: className, style }: Record = attrs; const refOptions = ref([]); const loading = ref(false); @@ -188,7 +189,7 @@ function emitChange() { }