diff --git a/apps/backend-mock/api/upload.ts b/apps/backend-mock/api/upload.ts new file mode 100644 index 00000000..1bb9e602 --- /dev/null +++ b/apps/backend-mock/api/upload.ts @@ -0,0 +1,13 @@ +import { verifyAccessToken } from '~/utils/jwt-utils'; +import { unAuthorizedResponse } from '~/utils/response'; + +export default eventHandler((event) => { + const userinfo = verifyAccessToken(event); + if (!userinfo) { + return unAuthorizedResponse(event); + } + return useResponseSuccess({ + url: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp', + }); + // return useResponseError("test") +}); diff --git a/apps/backend-mock/routes/[...].ts b/apps/backend-mock/routes/[...].ts index 70c5f7c7..99f544b6 100644 --- a/apps/backend-mock/routes/[...].ts +++ b/apps/backend-mock/routes/[...].ts @@ -7,6 +7,7 @@ export default defineEventHandler(() => {
  • /api/menu/all
  • /api/auth/codes
  • /api/auth/login
  • +
  • /api/upload
  • `; }); diff --git a/docs/src/components/common-ui/vben-modal.md b/docs/src/components/common-ui/vben-modal.md index 56ff6d3c..3c8200f9 100644 --- a/docs/src/components/common-ui/vben-modal.md +++ b/docs/src/components/common-ui/vben-modal.md @@ -59,7 +59,7 @@ Modal 内的内容一般业务中,会比较复杂,所以我们可以将 moda ::: info 注意 - `VbenModal` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenModal参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。 -- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenModal`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onConfirm`,那么以内部的 `onConfirm` 为准。`onOpenChange`事件除外,内外都会触发。 +- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenModal`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onConfirm`,那么以内部的 `onConfirm` 为准。`onOpenChange`事件除外,内外都会触发。另外,如果设置了`destroyOnClose`,内部Modal及其子组件会在被关闭后完全销毁。 - 如果弹窗的默认行为不符合你的预期,可以在`src\bootstrap.ts`中修改`setDefaultModalProps`的参数来设置默认的属性,如默认隐藏全屏按钮,修改默认ZIndex等。 ::: diff --git a/docs/src/guide/project/tailwindcss.md b/docs/src/guide/project/tailwindcss.md index cfab5964..837076cb 100644 --- a/docs/src/guide/project/tailwindcss.md +++ b/docs/src/guide/project/tailwindcss.md @@ -11,3 +11,7 @@ 当前只有对应的包下面存在 `tailwind.config.mjs` 文件才会启用 tailwindcss 的编译,否则不会启用 tailwindcss。如果你是纯粹的 SDK 包,不需要使用 tailwindcss,可以不用创建 `tailwind.config.mjs` 文件。 ::: + +## 提示 + +现`tailwindcss`已至v4.x版本,使用方法与`tailwindcss: ^3.4.17`有差异,v4.0无法与v3.x版本兼容,在开发前请确认`package.json`中的`tailwindcss`版本。 diff --git a/packages/@core/ui-kit/popup-ui/src/alert/alert.vue b/packages/@core/ui-kit/popup-ui/src/alert/alert.vue index a5b4d0da..9b133ee1 100644 --- a/packages/@core/ui-kit/popup-ui/src/alert/alert.vue +++ b/packages/@core/ui-kit/popup-ui/src/alert/alert.vue @@ -91,14 +91,13 @@ const getIconRender = computed(() => { }); function doCancel() { - isConfirm.value = false; + handleCancel(); handleOpenChange(false); } function doConfirm() { - isConfirm.value = true; + handleConfirm(); handleOpenChange(false); - emits('confirm'); } provideAlertContext({ @@ -117,11 +116,13 @@ function handleCancel() { const loading = ref(false); async function handleOpenChange(val: boolean) { + const confirmState = isConfirm.value; + isConfirm.value = false; await nextTick(); if (!val && props.beforeClose) { loading.value = true; try { - const res = await props.beforeClose({ isConfirm: isConfirm.value }); + const res = await props.beforeClose({ isConfirm: confirmState }); if (res !== false) { open.value = false; } diff --git a/packages/@core/ui-kit/popup-ui/src/modal/use-modal.ts b/packages/@core/ui-kit/popup-ui/src/modal/use-modal.ts index 2f40ddea..61965a02 100644 --- a/packages/@core/ui-kit/popup-ui/src/modal/use-modal.ts +++ b/packages/@core/ui-kit/popup-ui/src/modal/use-modal.ts @@ -1,6 +1,14 @@ import type { ExtendedModalApi, ModalApiOptions, ModalProps } from './modal'; -import { defineComponent, h, inject, nextTick, provide, reactive } from 'vue'; +import { + defineComponent, + h, + inject, + nextTick, + provide, + reactive, + ref, +} from 'vue'; import { useStore } from '@vben-core/shared/store'; @@ -24,6 +32,7 @@ export function useVbenModal( const { connectedComponent } = options; if (connectedComponent) { const extendedApi = reactive({}); + const isModalReady = ref(true); const Modal = defineComponent( (props: TParentModalProps, { attrs, slots }) => { provide(USER_MODAL_INJECT_KEY, { @@ -33,6 +42,11 @@ export function useVbenModal( Object.setPrototypeOf(extendedApi, api); }, options, + async reCreateModal() { + isModalReady.value = false; + await nextTick(); + isModalReady.value = true; + }, }); checkProps(extendedApi as ExtendedModalApi, { ...props, @@ -41,7 +55,7 @@ export function useVbenModal( }); return () => h( - connectedComponent, + isModalReady.value ? connectedComponent : 'div', { ...props, ...attrs, @@ -70,6 +84,13 @@ export function useVbenModal( injectData.options?.onOpenChange?.(isOpen); }; + mergedOptions.onClosed = () => { + options.onClosed?.(); + if (options.destroyOnClose) { + injectData.reCreateModal?.(); + } + }; + const api = new ModalApi(mergedOptions); const extendedApi: ExtendedModalApi = api as never; diff --git a/packages/effects/layouts/src/basic/menu/use-mixed-menu.ts b/packages/effects/layouts/src/basic/menu/use-mixed-menu.ts index 6129e9d8..dc727447 100644 --- a/packages/effects/layouts/src/basic/menu/use-mixed-menu.ts +++ b/packages/effects/layouts/src/basic/menu/use-mixed-menu.ts @@ -74,7 +74,7 @@ function useMixedMenu() { */ const headerActive = computed(() => { if (!needSplit.value) { - return route.path; + return route.meta?.activePath ?? route.path; } return rootMenuPath.value; }); diff --git a/playground/src/api/examples/upload.ts b/playground/src/api/examples/upload.ts new file mode 100644 index 00000000..246d4f26 --- /dev/null +++ b/playground/src/api/examples/upload.ts @@ -0,0 +1,25 @@ +import { requestClient } from '#/api/request'; + +interface UploadFileParams { + file: File; + onError?: (error: Error) => void; + onProgress?: (progress: { percent: number }) => void; + onSuccess?: (data: any, file: File) => void; +} +export async function upload_file({ + file, + onError, + onProgress, + onSuccess, +}: UploadFileParams) { + try { + onProgress?.({ percent: 0 }); + + const data = await requestClient.upload('/upload', { file }); + + onProgress?.({ percent: 100 }); + onSuccess?.(data, file); + } catch (error) { + onError?.(error instanceof Error ? error : new Error(String(error))); + } +} diff --git a/playground/src/locales/langs/en-US/examples.json b/playground/src/locales/langs/en-US/examples.json index 1a25a983..9335b28b 100644 --- a/playground/src/locales/langs/en-US/examples.json +++ b/playground/src/locales/langs/en-US/examples.json @@ -18,7 +18,11 @@ "dynamic": "Dynamic Form", "custom": "Custom Component", "api": "Api", - "merge": "Merge Form" + "merge": "Merge Form", + "upload-error": "Partial file upload failed", + "upload-urls": "Urls after file upload", + "file": "file", + "upload-image": "Click to upload image" }, "vxeTable": { "title": "Vxe Table", diff --git a/playground/src/locales/langs/zh-CN/examples.json b/playground/src/locales/langs/zh-CN/examples.json index 8f15d020..ff11d7fd 100644 --- a/playground/src/locales/langs/zh-CN/examples.json +++ b/playground/src/locales/langs/zh-CN/examples.json @@ -21,7 +21,11 @@ "dynamic": "动态表单", "custom": "自定义组件", "api": "Api", - "merge": "合并表单" + "merge": "合并表单", + "upload-error": "部分文件上传失败", + "upload-urls": "文件上传后的网址", + "file": "文件", + "upload-image": "点击上传图片" }, "vxeTable": { "title": "Vxe 表格", diff --git a/playground/src/views/examples/form/basic.vue b/playground/src/views/examples/form/basic.vue index b98ace26..d0e91d33 100644 --- a/playground/src/views/examples/form/basic.vue +++ b/playground/src/views/examples/form/basic.vue @@ -1,5 +1,7 @@