From df655015b1228a1d312fbc1a7fdd73293622eda3 Mon Sep 17 00:00:00 2001 From: lighua <877312980@qq.com> Date: Fri, 8 Aug 2025 15:31:31 +0800 Subject: [PATCH 01/14] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E4=B8=8B=E8=BD=BD=E5=99=A8=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../effects/request/src/request-client/modules/downloader.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/effects/request/src/request-client/modules/downloader.ts b/packages/effects/request/src/request-client/modules/downloader.ts index 77e72c6c..f5bd101a 100644 --- a/packages/effects/request/src/request-client/modules/downloader.ts +++ b/packages/effects/request/src/request-client/modules/downloader.ts @@ -28,11 +28,12 @@ class FileDownloader { ): Promise { const finalConfig: DownloadRequestConfig = { responseReturn: 'body', + method: 'GET', ...config, responseType: 'blob', }; - const response = await this.client.get(url, finalConfig); + const response = await this.client.request(url, finalConfig); return response; } From fddfc6d4940aa0a7332b5ff3a33a3fcabf5092eb Mon Sep 17 00:00:00 2001 From: lighua <877312980@qq.com> Date: Thu, 21 Aug 2025 11:13:02 +0800 Subject: [PATCH 02/14] =?UTF-8?q?fix:=20=E5=A2=9E=E5=8A=A0=E5=AF=B9?= =?UTF-8?q?=E4=B8=8D=E6=94=AF=E6=8C=81=E7=9A=84=20HTTP=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E7=9A=84=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request-client/modules/downloader.test.ts | 71 +++++++++++++++++++ .../src/request-client/modules/downloader.ts | 22 +++++- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/packages/effects/request/src/request-client/modules/downloader.test.ts b/packages/effects/request/src/request-client/modules/downloader.test.ts index d44dcbb7..27e8a30c 100644 --- a/packages/effects/request/src/request-client/modules/downloader.test.ts +++ b/packages/effects/request/src/request-client/modules/downloader.test.ts @@ -30,6 +30,7 @@ describe('fileDownloader', () => { expect(result).toBeInstanceOf(Blob); expect(result).toEqual(mockBlob); expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, { + method: 'GET', responseType: 'blob', responseReturn: 'body', }); @@ -51,6 +52,7 @@ describe('fileDownloader', () => { expect(result).toEqual(mockBlob); expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, { ...customConfig, + method: 'GET', responseType: 'blob', responseReturn: 'body', }); @@ -84,3 +86,72 @@ describe('fileDownloader', () => { ); }); }); + +describe('fileDownloader use other method', () => { + let fileDownloader: FileDownloader; + + it('should call request using get', async () => { + const url = 'https://example.com/file'; + const mockBlob = new Blob(['file content'], { type: 'text/plain' }); + const mockResponse: Blob = mockBlob; + + const mockAxiosInstance = { + request: vi.fn(), + } as any; + + fileDownloader = new FileDownloader(mockAxiosInstance); + + mockAxiosInstance.request.mockResolvedValueOnce(mockResponse); + + const result = await fileDownloader.download(url); + + expect(result).toBeInstanceOf(Blob); + expect(result).toEqual(mockBlob); + expect(mockAxiosInstance.request).toHaveBeenCalledWith(url, { + method: 'GET', + responseType: 'blob', + responseReturn: 'body', + }); + }); + + it('should call post', async () => { + const url = 'https://example.com/file'; + + const mockAxiosInstance = { + post: vi.fn(), + } as any; + + fileDownloader = new FileDownloader(mockAxiosInstance); + + const customConfig: AxiosRequestConfig = { + method: 'POST', + data: { name: 'aa' }, + }; + + await fileDownloader.download(url, customConfig); + + expect(mockAxiosInstance.post).toHaveBeenCalledWith( + url, + { name: 'aa' }, + { + method: 'POST', + responseType: 'blob', + responseReturn: 'body', + }, + ); + }); + + it('should handle errors gracefully', async () => { + const url = 'https://example.com/file'; + const mockAxiosInstance = { + post: vi.fn(), + } as any; + + fileDownloader = new FileDownloader(mockAxiosInstance); + await expect(() => + fileDownloader.download(url, { method: 'postt' }), + ).rejects.toThrow( + 'RequestClient does not support method "POSTT". Please ensure the method is properly implemented in your RequestClient instance.', + ); + }); +}); diff --git a/packages/effects/request/src/request-client/modules/downloader.ts b/packages/effects/request/src/request-client/modules/downloader.ts index f5bd101a..6c300058 100644 --- a/packages/effects/request/src/request-client/modules/downloader.ts +++ b/packages/effects/request/src/request-client/modules/downloader.ts @@ -33,9 +33,27 @@ class FileDownloader { responseType: 'blob', }; - const response = await this.client.request(url, finalConfig); + // Prefer a generic request if available; otherwise, dispatch to method-specific calls. + const method = (finalConfig.method || 'GET').toUpperCase(); + const clientAny = this.client as any; - return response; + if (typeof clientAny.request === 'function') { + return await clientAny.request(url, finalConfig); + } + const lower = method.toLowerCase(); + + if (typeof clientAny[lower] === 'function') { + if (['POST', 'PUT'].includes(method)) { + const { data, ...rest } = finalConfig as Record; + return await clientAny[lower](url, data, rest); + } + + return await clientAny[lower](url, finalConfig); + } + + throw new Error( + `RequestClient does not support method "${method}". Please ensure the method is properly implemented in your RequestClient instance.`, + ); } } From 7dc68ed368c59cd1d533bcf202c2a8a37f0cb911 Mon Sep 17 00:00:00 2001 From: xingyu4j Date: Mon, 10 Nov 2025 17:49:58 +0800 Subject: [PATCH 03/14] feat: add profile comps --- packages/effects/common-ui/src/ui/index.ts | 1 + .../common-ui/src/ui/profile/base-setting.vue | 56 +++++++++++++++++ .../effects/common-ui/src/ui/profile/index.ts | 6 ++ .../src/ui/profile/notification-setting.vue | 53 ++++++++++++++++ .../src/ui/profile/password-setting.vue | 56 +++++++++++++++++ .../common-ui/src/ui/profile/profile.vue | 62 +++++++++++++++++++ .../src/ui/profile/security-setting.vue | 53 ++++++++++++++++ .../effects/common-ui/src/ui/profile/types.ts | 22 +++++++ 8 files changed, 309 insertions(+) create mode 100644 packages/effects/common-ui/src/ui/profile/base-setting.vue create mode 100644 packages/effects/common-ui/src/ui/profile/index.ts create mode 100644 packages/effects/common-ui/src/ui/profile/notification-setting.vue create mode 100644 packages/effects/common-ui/src/ui/profile/password-setting.vue create mode 100644 packages/effects/common-ui/src/ui/profile/profile.vue create mode 100644 packages/effects/common-ui/src/ui/profile/security-setting.vue create mode 100644 packages/effects/common-ui/src/ui/profile/types.ts diff --git a/packages/effects/common-ui/src/ui/index.ts b/packages/effects/common-ui/src/ui/index.ts index fb99fdec..8b1db558 100644 --- a/packages/effects/common-ui/src/ui/index.ts +++ b/packages/effects/common-ui/src/ui/index.ts @@ -2,3 +2,4 @@ export * from './about'; export * from './authentication'; export * from './dashboard'; export * from './fallback'; +export * from './profile'; diff --git a/packages/effects/common-ui/src/ui/profile/base-setting.vue b/packages/effects/common-ui/src/ui/profile/base-setting.vue new file mode 100644 index 00000000..1ef1c1a0 --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/base-setting.vue @@ -0,0 +1,56 @@ + + diff --git a/packages/effects/common-ui/src/ui/profile/index.ts b/packages/effects/common-ui/src/ui/profile/index.ts new file mode 100644 index 00000000..209a2e81 --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/index.ts @@ -0,0 +1,6 @@ +export { default as ProfileBaseSetting } from './base-setting.vue'; +export { default as ProfileNotificationSetting } from './notification-setting.vue'; +export { default as ProfilePasswordSetting } from './password-setting.vue'; +export { default as Profile } from './profile.vue'; +export { default as ProfileSecuritySetting } from './security-setting.vue'; +export type * from './types'; diff --git a/packages/effects/common-ui/src/ui/profile/notification-setting.vue b/packages/effects/common-ui/src/ui/profile/notification-setting.vue new file mode 100644 index 00000000..9a3e4882 --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/notification-setting.vue @@ -0,0 +1,53 @@ + + diff --git a/packages/effects/common-ui/src/ui/profile/password-setting.vue b/packages/effects/common-ui/src/ui/profile/password-setting.vue new file mode 100644 index 00000000..1d90e086 --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/password-setting.vue @@ -0,0 +1,56 @@ + + diff --git a/packages/effects/common-ui/src/ui/profile/profile.vue b/packages/effects/common-ui/src/ui/profile/profile.vue new file mode 100644 index 00000000..5ab39e61 --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/profile.vue @@ -0,0 +1,62 @@ + + diff --git a/packages/effects/common-ui/src/ui/profile/security-setting.vue b/packages/effects/common-ui/src/ui/profile/security-setting.vue new file mode 100644 index 00000000..9a3e4882 --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/security-setting.vue @@ -0,0 +1,53 @@ + + diff --git a/packages/effects/common-ui/src/ui/profile/types.ts b/packages/effects/common-ui/src/ui/profile/types.ts new file mode 100644 index 00000000..21caf547 --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/types.ts @@ -0,0 +1,22 @@ +import type { BasicUserInfo } from '@vben/types'; + +export interface Props { + title?: string; + userInfo: BasicUserInfo | null; + tabs: { + label: string; + value: string; + }[]; +} + +export interface FormSchemaItem { + description: string; + fieldName: string; + label: string; + name: string; + value: boolean; +} + +export interface SettingProps { + formSchema: FormSchemaItem[]; +} From 4347fba80ae1b70644a54d2d9a37ff092ff59d86 Mon Sep 17 00:00:00 2001 From: xingyu4j Date: Mon, 10 Nov 2025 17:50:47 +0800 Subject: [PATCH 04/14] feat: antdv add profile --- .../src/views/_core/profile/base-setting.vue | 65 ++++++++++++++++++ .../src/views/_core/profile/index.vue | 49 ++++++++++++++ .../_core/profile/notification-setting.vue | 31 +++++++++ .../views/_core/profile/password-setting.vue | 66 +++++++++++++++++++ .../views/_core/profile/security-setting.vue | 43 ++++++++++++ 5 files changed, 254 insertions(+) create mode 100644 apps/web-antd/src/views/_core/profile/base-setting.vue create mode 100644 apps/web-antd/src/views/_core/profile/index.vue create mode 100644 apps/web-antd/src/views/_core/profile/notification-setting.vue create mode 100644 apps/web-antd/src/views/_core/profile/password-setting.vue create mode 100644 apps/web-antd/src/views/_core/profile/security-setting.vue diff --git a/apps/web-antd/src/views/_core/profile/base-setting.vue b/apps/web-antd/src/views/_core/profile/base-setting.vue new file mode 100644 index 00000000..aa8a4c26 --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/base-setting.vue @@ -0,0 +1,65 @@ + + diff --git a/apps/web-antd/src/views/_core/profile/index.vue b/apps/web-antd/src/views/_core/profile/index.vue new file mode 100644 index 00000000..8740894e --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/index.vue @@ -0,0 +1,49 @@ + + diff --git a/apps/web-antd/src/views/_core/profile/notification-setting.vue b/apps/web-antd/src/views/_core/profile/notification-setting.vue new file mode 100644 index 00000000..324a4b39 --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/notification-setting.vue @@ -0,0 +1,31 @@ + + diff --git a/apps/web-antd/src/views/_core/profile/password-setting.vue b/apps/web-antd/src/views/_core/profile/password-setting.vue new file mode 100644 index 00000000..b246bc37 --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/password-setting.vue @@ -0,0 +1,66 @@ + + diff --git a/apps/web-antd/src/views/_core/profile/security-setting.vue b/apps/web-antd/src/views/_core/profile/security-setting.vue new file mode 100644 index 00000000..be30db58 --- /dev/null +++ b/apps/web-antd/src/views/_core/profile/security-setting.vue @@ -0,0 +1,43 @@ + + From e311cfb8da6bac2412704a3e245edc8c5a377c01 Mon Sep 17 00:00:00 2001 From: xingyu4j Date: Mon, 10 Nov 2025 17:51:10 +0800 Subject: [PATCH 05/14] feat: route add profile --- apps/web-antd/src/layouts/basic.vue | 9 +++++++++ apps/web-antd/src/locales/langs/en-US/page.json | 3 ++- apps/web-antd/src/locales/langs/zh-CN/page.json | 3 ++- apps/web-antd/src/router/routes/modules/vben.ts | 10 ++++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/web-antd/src/layouts/basic.vue b/apps/web-antd/src/layouts/basic.vue index 805b8a73..8c5c663a 100644 --- a/apps/web-antd/src/layouts/basic.vue +++ b/apps/web-antd/src/layouts/basic.vue @@ -2,6 +2,7 @@ import type { NotificationItem } from '@vben/layouts'; import { computed, ref, watch } from 'vue'; +import { useRouter } from 'vue-router'; import { AuthenticationLoginExpiredModal } from '@vben/common-ui'; import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants'; @@ -52,6 +53,7 @@ const notifications = ref([ }, ]); +const router = useRouter(); const userStore = useUserStore(); const authStore = useAuthStore(); const accessStore = useAccessStore(); @@ -61,6 +63,13 @@ const showDot = computed(() => ); const menus = computed(() => [ + { + handler: () => { + router.push({ name: 'Profile' }); + }, + icon: 'lucide:user', + text: $t('page.auth.profile'), + }, { handler: () => { openWindow(VBEN_DOC_URL, { diff --git a/apps/web-antd/src/locales/langs/en-US/page.json b/apps/web-antd/src/locales/langs/en-US/page.json index 618a258c..39f1641c 100644 --- a/apps/web-antd/src/locales/langs/en-US/page.json +++ b/apps/web-antd/src/locales/langs/en-US/page.json @@ -4,7 +4,8 @@ "register": "Register", "codeLogin": "Code Login", "qrcodeLogin": "Qr Code Login", - "forgetPassword": "Forget Password" + "forgetPassword": "Forget Password", + "profile": "Profile" }, "dashboard": { "title": "Dashboard", diff --git a/apps/web-antd/src/locales/langs/zh-CN/page.json b/apps/web-antd/src/locales/langs/zh-CN/page.json index 4cb67081..2192d1d5 100644 --- a/apps/web-antd/src/locales/langs/zh-CN/page.json +++ b/apps/web-antd/src/locales/langs/zh-CN/page.json @@ -4,7 +4,8 @@ "register": "注册", "codeLogin": "验证码登录", "qrcodeLogin": "二维码登录", - "forgetPassword": "忘记密码" + "forgetPassword": "忘记密码", + "profile": "个人中心" }, "dashboard": { "title": "概览", diff --git a/apps/web-antd/src/router/routes/modules/vben.ts b/apps/web-antd/src/router/routes/modules/vben.ts index 4a8cdb0a..631facc0 100644 --- a/apps/web-antd/src/router/routes/modules/vben.ts +++ b/apps/web-antd/src/router/routes/modules/vben.ts @@ -89,6 +89,16 @@ const routes: RouteRecordRaw[] = [ order: 9999, }, }, + { + name: 'Profile', + path: '/profile', + component: () => import('#/views/_core/profile/index.vue'), + meta: { + icon: 'lucide:user', + hideInMenu: true, + title: $t('page.auth.profile'), + }, + }, ]; export default routes; From 48b3d3008830a7ce521bf90cc9722c6280878e70 Mon Sep 17 00:00:00 2001 From: xingyu4j Date: Mon, 10 Nov 2025 17:54:01 +0800 Subject: [PATCH 06/14] fix: types --- packages/effects/common-ui/src/ui/profile/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/effects/common-ui/src/ui/profile/types.ts b/packages/effects/common-ui/src/ui/profile/types.ts index 21caf547..e600bde8 100644 --- a/packages/effects/common-ui/src/ui/profile/types.ts +++ b/packages/effects/common-ui/src/ui/profile/types.ts @@ -13,7 +13,6 @@ export interface FormSchemaItem { description: string; fieldName: string; label: string; - name: string; value: boolean; } From 226d9bd1ad74122935f2c5a3a74dd8be9acb405f Mon Sep 17 00:00:00 2001 From: xingyu4j Date: Mon, 10 Nov 2025 17:59:33 +0800 Subject: [PATCH 07/14] feat: ele add profile --- apps/web-ele/src/layouts/basic.vue | 9 +++ .../web-ele/src/locales/langs/en-US/page.json | 3 +- .../web-ele/src/locales/langs/zh-CN/page.json | 3 +- .../web-ele/src/router/routes/modules/vben.ts | 10 +++ .../src/views/_core/profile/base-setting.vue | 65 ++++++++++++++++++ .../web-ele/src/views/_core/profile/index.vue | 49 ++++++++++++++ .../_core/profile/notification-setting.vue | 31 +++++++++ .../views/_core/profile/password-setting.vue | 66 +++++++++++++++++++ .../views/_core/profile/security-setting.vue | 43 ++++++++++++ 9 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 apps/web-ele/src/views/_core/profile/base-setting.vue create mode 100644 apps/web-ele/src/views/_core/profile/index.vue create mode 100644 apps/web-ele/src/views/_core/profile/notification-setting.vue create mode 100644 apps/web-ele/src/views/_core/profile/password-setting.vue create mode 100644 apps/web-ele/src/views/_core/profile/security-setting.vue diff --git a/apps/web-ele/src/layouts/basic.vue b/apps/web-ele/src/layouts/basic.vue index 805b8a73..8c5c663a 100644 --- a/apps/web-ele/src/layouts/basic.vue +++ b/apps/web-ele/src/layouts/basic.vue @@ -2,6 +2,7 @@ import type { NotificationItem } from '@vben/layouts'; import { computed, ref, watch } from 'vue'; +import { useRouter } from 'vue-router'; import { AuthenticationLoginExpiredModal } from '@vben/common-ui'; import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants'; @@ -52,6 +53,7 @@ const notifications = ref([ }, ]); +const router = useRouter(); const userStore = useUserStore(); const authStore = useAuthStore(); const accessStore = useAccessStore(); @@ -61,6 +63,13 @@ const showDot = computed(() => ); const menus = computed(() => [ + { + handler: () => { + router.push({ name: 'Profile' }); + }, + icon: 'lucide:user', + text: $t('page.auth.profile'), + }, { handler: () => { openWindow(VBEN_DOC_URL, { diff --git a/apps/web-ele/src/locales/langs/en-US/page.json b/apps/web-ele/src/locales/langs/en-US/page.json index 618a258c..39f1641c 100644 --- a/apps/web-ele/src/locales/langs/en-US/page.json +++ b/apps/web-ele/src/locales/langs/en-US/page.json @@ -4,7 +4,8 @@ "register": "Register", "codeLogin": "Code Login", "qrcodeLogin": "Qr Code Login", - "forgetPassword": "Forget Password" + "forgetPassword": "Forget Password", + "profile": "Profile" }, "dashboard": { "title": "Dashboard", diff --git a/apps/web-ele/src/locales/langs/zh-CN/page.json b/apps/web-ele/src/locales/langs/zh-CN/page.json index 4cb67081..2192d1d5 100644 --- a/apps/web-ele/src/locales/langs/zh-CN/page.json +++ b/apps/web-ele/src/locales/langs/zh-CN/page.json @@ -4,7 +4,8 @@ "register": "注册", "codeLogin": "验证码登录", "qrcodeLogin": "二维码登录", - "forgetPassword": "忘记密码" + "forgetPassword": "忘记密码", + "profile": "个人中心" }, "dashboard": { "title": "概览", diff --git a/apps/web-ele/src/router/routes/modules/vben.ts b/apps/web-ele/src/router/routes/modules/vben.ts index 98bdaf32..5c522f30 100644 --- a/apps/web-ele/src/router/routes/modules/vben.ts +++ b/apps/web-ele/src/router/routes/modules/vben.ts @@ -89,6 +89,16 @@ const routes: RouteRecordRaw[] = [ order: 9999, }, }, + { + name: 'Profile', + path: '/profile', + component: () => import('#/views/_core/profile/index.vue'), + meta: { + icon: 'lucide:user', + hideInMenu: true, + title: $t('page.auth.profile'), + }, + }, ]; export default routes; diff --git a/apps/web-ele/src/views/_core/profile/base-setting.vue b/apps/web-ele/src/views/_core/profile/base-setting.vue new file mode 100644 index 00000000..aa8a4c26 --- /dev/null +++ b/apps/web-ele/src/views/_core/profile/base-setting.vue @@ -0,0 +1,65 @@ + + diff --git a/apps/web-ele/src/views/_core/profile/index.vue b/apps/web-ele/src/views/_core/profile/index.vue new file mode 100644 index 00000000..8740894e --- /dev/null +++ b/apps/web-ele/src/views/_core/profile/index.vue @@ -0,0 +1,49 @@ + + diff --git a/apps/web-ele/src/views/_core/profile/notification-setting.vue b/apps/web-ele/src/views/_core/profile/notification-setting.vue new file mode 100644 index 00000000..324a4b39 --- /dev/null +++ b/apps/web-ele/src/views/_core/profile/notification-setting.vue @@ -0,0 +1,31 @@ + + diff --git a/apps/web-ele/src/views/_core/profile/password-setting.vue b/apps/web-ele/src/views/_core/profile/password-setting.vue new file mode 100644 index 00000000..4d80d74c --- /dev/null +++ b/apps/web-ele/src/views/_core/profile/password-setting.vue @@ -0,0 +1,66 @@ + + diff --git a/apps/web-ele/src/views/_core/profile/security-setting.vue b/apps/web-ele/src/views/_core/profile/security-setting.vue new file mode 100644 index 00000000..be30db58 --- /dev/null +++ b/apps/web-ele/src/views/_core/profile/security-setting.vue @@ -0,0 +1,43 @@ + + From cbf2a0287719af5bfc426b3c09171cfc82652d41 Mon Sep 17 00:00:00 2001 From: xingyu4j Date: Mon, 10 Nov 2025 17:59:58 +0800 Subject: [PATCH 08/14] feat: naive add profile --- apps/web-naive/src/layouts/basic.vue | 9 +++ .../src/locales/langs/en-US/page.json | 3 +- .../src/locales/langs/zh-CN/page.json | 3 +- .../src/router/routes/modules/vben.ts | 10 +++ .../src/views/_core/profile/base-setting.vue | 65 ++++++++++++++++++ .../src/views/_core/profile/index.vue | 49 ++++++++++++++ .../_core/profile/notification-setting.vue | 31 +++++++++ .../views/_core/profile/password-setting.vue | 66 +++++++++++++++++++ .../views/_core/profile/security-setting.vue | 43 ++++++++++++ 9 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 apps/web-naive/src/views/_core/profile/base-setting.vue create mode 100644 apps/web-naive/src/views/_core/profile/index.vue create mode 100644 apps/web-naive/src/views/_core/profile/notification-setting.vue create mode 100644 apps/web-naive/src/views/_core/profile/password-setting.vue create mode 100644 apps/web-naive/src/views/_core/profile/security-setting.vue diff --git a/apps/web-naive/src/layouts/basic.vue b/apps/web-naive/src/layouts/basic.vue index 0e9747be..f752b941 100644 --- a/apps/web-naive/src/layouts/basic.vue +++ b/apps/web-naive/src/layouts/basic.vue @@ -2,6 +2,7 @@ import type { NotificationItem } from '@vben/layouts'; import { computed, ref, watch } from 'vue'; +import { useRouter } from 'vue-router'; import { AuthenticationLoginExpiredModal } from '@vben/common-ui'; import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants'; @@ -52,6 +53,7 @@ const notifications = ref([ }, ]); +const router = useRouter(); const userStore = useUserStore(); const authStore = useAuthStore(); const accessStore = useAccessStore(); @@ -61,6 +63,13 @@ const showDot = computed(() => ); const menus = computed(() => [ + { + handler: () => { + router.push({ name: 'Profile' }); + }, + icon: 'lucide:user', + text: $t('page.auth.profile'), + }, { handler: () => { openWindow(VBEN_DOC_URL, { diff --git a/apps/web-naive/src/locales/langs/en-US/page.json b/apps/web-naive/src/locales/langs/en-US/page.json index 618a258c..39f1641c 100644 --- a/apps/web-naive/src/locales/langs/en-US/page.json +++ b/apps/web-naive/src/locales/langs/en-US/page.json @@ -4,7 +4,8 @@ "register": "Register", "codeLogin": "Code Login", "qrcodeLogin": "Qr Code Login", - "forgetPassword": "Forget Password" + "forgetPassword": "Forget Password", + "profile": "Profile" }, "dashboard": { "title": "Dashboard", diff --git a/apps/web-naive/src/locales/langs/zh-CN/page.json b/apps/web-naive/src/locales/langs/zh-CN/page.json index 4cb67081..2192d1d5 100644 --- a/apps/web-naive/src/locales/langs/zh-CN/page.json +++ b/apps/web-naive/src/locales/langs/zh-CN/page.json @@ -4,7 +4,8 @@ "register": "注册", "codeLogin": "验证码登录", "qrcodeLogin": "二维码登录", - "forgetPassword": "忘记密码" + "forgetPassword": "忘记密码", + "profile": "个人中心" }, "dashboard": { "title": "概览", diff --git a/apps/web-naive/src/router/routes/modules/vben.ts b/apps/web-naive/src/router/routes/modules/vben.ts index 8f8803b3..32c21ca4 100644 --- a/apps/web-naive/src/router/routes/modules/vben.ts +++ b/apps/web-naive/src/router/routes/modules/vben.ts @@ -89,6 +89,16 @@ const routes: RouteRecordRaw[] = [ order: 9999, }, }, + { + name: 'Profile', + path: '/profile', + component: () => import('#/views/_core/profile/index.vue'), + meta: { + icon: 'lucide:user', + hideInMenu: true, + title: $t('page.auth.profile'), + }, + }, ]; export default routes; diff --git a/apps/web-naive/src/views/_core/profile/base-setting.vue b/apps/web-naive/src/views/_core/profile/base-setting.vue new file mode 100644 index 00000000..aa8a4c26 --- /dev/null +++ b/apps/web-naive/src/views/_core/profile/base-setting.vue @@ -0,0 +1,65 @@ + + diff --git a/apps/web-naive/src/views/_core/profile/index.vue b/apps/web-naive/src/views/_core/profile/index.vue new file mode 100644 index 00000000..8740894e --- /dev/null +++ b/apps/web-naive/src/views/_core/profile/index.vue @@ -0,0 +1,49 @@ + + diff --git a/apps/web-naive/src/views/_core/profile/notification-setting.vue b/apps/web-naive/src/views/_core/profile/notification-setting.vue new file mode 100644 index 00000000..324a4b39 --- /dev/null +++ b/apps/web-naive/src/views/_core/profile/notification-setting.vue @@ -0,0 +1,31 @@ + + diff --git a/apps/web-naive/src/views/_core/profile/password-setting.vue b/apps/web-naive/src/views/_core/profile/password-setting.vue new file mode 100644 index 00000000..1132a51f --- /dev/null +++ b/apps/web-naive/src/views/_core/profile/password-setting.vue @@ -0,0 +1,66 @@ + + diff --git a/apps/web-naive/src/views/_core/profile/security-setting.vue b/apps/web-naive/src/views/_core/profile/security-setting.vue new file mode 100644 index 00000000..be30db58 --- /dev/null +++ b/apps/web-naive/src/views/_core/profile/security-setting.vue @@ -0,0 +1,43 @@ + + From acf99f2441d06a35259308c434c5e98c0a6ba09d Mon Sep 17 00:00:00 2001 From: xingyu4j Date: Mon, 10 Nov 2025 18:00:25 +0800 Subject: [PATCH 09/14] feat: tdesign add profile --- apps/web-tdesign/src/layouts/basic.vue | 9 +++ .../src/locales/langs/en-US/page.json | 3 +- .../src/locales/langs/zh-CN/page.json | 3 +- .../src/router/routes/modules/vben.ts | 10 +++ .../src/views/_core/profile/base-setting.vue | 65 ++++++++++++++++++ .../src/views/_core/profile/index.vue | 49 ++++++++++++++ .../_core/profile/notification-setting.vue | 31 +++++++++ .../views/_core/profile/password-setting.vue | 66 +++++++++++++++++++ .../views/_core/profile/security-setting.vue | 43 ++++++++++++ 9 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 apps/web-tdesign/src/views/_core/profile/base-setting.vue create mode 100644 apps/web-tdesign/src/views/_core/profile/index.vue create mode 100644 apps/web-tdesign/src/views/_core/profile/notification-setting.vue create mode 100644 apps/web-tdesign/src/views/_core/profile/password-setting.vue create mode 100644 apps/web-tdesign/src/views/_core/profile/security-setting.vue diff --git a/apps/web-tdesign/src/layouts/basic.vue b/apps/web-tdesign/src/layouts/basic.vue index 805b8a73..8c5c663a 100644 --- a/apps/web-tdesign/src/layouts/basic.vue +++ b/apps/web-tdesign/src/layouts/basic.vue @@ -2,6 +2,7 @@ import type { NotificationItem } from '@vben/layouts'; import { computed, ref, watch } from 'vue'; +import { useRouter } from 'vue-router'; import { AuthenticationLoginExpiredModal } from '@vben/common-ui'; import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants'; @@ -52,6 +53,7 @@ const notifications = ref([ }, ]); +const router = useRouter(); const userStore = useUserStore(); const authStore = useAuthStore(); const accessStore = useAccessStore(); @@ -61,6 +63,13 @@ const showDot = computed(() => ); const menus = computed(() => [ + { + handler: () => { + router.push({ name: 'Profile' }); + }, + icon: 'lucide:user', + text: $t('page.auth.profile'), + }, { handler: () => { openWindow(VBEN_DOC_URL, { diff --git a/apps/web-tdesign/src/locales/langs/en-US/page.json b/apps/web-tdesign/src/locales/langs/en-US/page.json index 618a258c..39f1641c 100644 --- a/apps/web-tdesign/src/locales/langs/en-US/page.json +++ b/apps/web-tdesign/src/locales/langs/en-US/page.json @@ -4,7 +4,8 @@ "register": "Register", "codeLogin": "Code Login", "qrcodeLogin": "Qr Code Login", - "forgetPassword": "Forget Password" + "forgetPassword": "Forget Password", + "profile": "Profile" }, "dashboard": { "title": "Dashboard", diff --git a/apps/web-tdesign/src/locales/langs/zh-CN/page.json b/apps/web-tdesign/src/locales/langs/zh-CN/page.json index 4cb67081..2192d1d5 100644 --- a/apps/web-tdesign/src/locales/langs/zh-CN/page.json +++ b/apps/web-tdesign/src/locales/langs/zh-CN/page.json @@ -4,7 +4,8 @@ "register": "注册", "codeLogin": "验证码登录", "qrcodeLogin": "二维码登录", - "forgetPassword": "忘记密码" + "forgetPassword": "忘记密码", + "profile": "个人中心" }, "dashboard": { "title": "概览", diff --git a/apps/web-tdesign/src/router/routes/modules/vben.ts b/apps/web-tdesign/src/router/routes/modules/vben.ts index b1a3537c..a5f0aa4d 100644 --- a/apps/web-tdesign/src/router/routes/modules/vben.ts +++ b/apps/web-tdesign/src/router/routes/modules/vben.ts @@ -89,6 +89,16 @@ const routes: RouteRecordRaw[] = [ order: 9999, }, }, + { + name: 'Profile', + path: '/profile', + component: () => import('#/views/_core/profile/index.vue'), + meta: { + icon: 'lucide:user', + hideInMenu: true, + title: $t('page.auth.profile'), + }, + }, ]; export default routes; diff --git a/apps/web-tdesign/src/views/_core/profile/base-setting.vue b/apps/web-tdesign/src/views/_core/profile/base-setting.vue new file mode 100644 index 00000000..aa8a4c26 --- /dev/null +++ b/apps/web-tdesign/src/views/_core/profile/base-setting.vue @@ -0,0 +1,65 @@ + + diff --git a/apps/web-tdesign/src/views/_core/profile/index.vue b/apps/web-tdesign/src/views/_core/profile/index.vue new file mode 100644 index 00000000..8740894e --- /dev/null +++ b/apps/web-tdesign/src/views/_core/profile/index.vue @@ -0,0 +1,49 @@ + + diff --git a/apps/web-tdesign/src/views/_core/profile/notification-setting.vue b/apps/web-tdesign/src/views/_core/profile/notification-setting.vue new file mode 100644 index 00000000..324a4b39 --- /dev/null +++ b/apps/web-tdesign/src/views/_core/profile/notification-setting.vue @@ -0,0 +1,31 @@ + + diff --git a/apps/web-tdesign/src/views/_core/profile/password-setting.vue b/apps/web-tdesign/src/views/_core/profile/password-setting.vue new file mode 100644 index 00000000..7e1c0f08 --- /dev/null +++ b/apps/web-tdesign/src/views/_core/profile/password-setting.vue @@ -0,0 +1,66 @@ + + diff --git a/apps/web-tdesign/src/views/_core/profile/security-setting.vue b/apps/web-tdesign/src/views/_core/profile/security-setting.vue new file mode 100644 index 00000000..be30db58 --- /dev/null +++ b/apps/web-tdesign/src/views/_core/profile/security-setting.vue @@ -0,0 +1,43 @@ + + From 0319604863891ce918cf720471a796335eb69129 Mon Sep 17 00:00:00 2001 From: xingyu4j Date: Mon, 10 Nov 2025 18:00:50 +0800 Subject: [PATCH 10/14] feat: playground add profile --- playground/src/layouts/basic.vue | 9 +++ playground/src/locales/langs/en-US/page.json | 3 +- playground/src/locales/langs/zh-CN/page.json | 3 +- playground/src/router/routes/modules/vben.ts | 10 +++ .../src/views/_core/profile/base-setting.vue | 65 ++++++++++++++++++ playground/src/views/_core/profile/index.vue | 49 ++++++++++++++ .../_core/profile/notification-setting.vue | 31 +++++++++ .../views/_core/profile/password-setting.vue | 66 +++++++++++++++++++ .../views/_core/profile/security-setting.vue | 43 ++++++++++++ 9 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 playground/src/views/_core/profile/base-setting.vue create mode 100644 playground/src/views/_core/profile/index.vue create mode 100644 playground/src/views/_core/profile/notification-setting.vue create mode 100644 playground/src/views/_core/profile/password-setting.vue create mode 100644 playground/src/views/_core/profile/security-setting.vue diff --git a/playground/src/layouts/basic.vue b/playground/src/layouts/basic.vue index 05787651..35f877ff 100644 --- a/playground/src/layouts/basic.vue +++ b/playground/src/layouts/basic.vue @@ -2,6 +2,7 @@ import type { NotificationItem } from '@vben/layouts'; import { computed, onBeforeMount, ref, watch } from 'vue'; +import { useRouter } from 'vue-router'; import { AuthenticationLoginExpiredModal } from '@vben/common-ui'; import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants'; @@ -65,6 +66,7 @@ const notifications = ref([ }, ]); +const router = useRouter(); const userStore = useUserStore(); const authStore = useAuthStore(); const accessStore = useAccessStore(); @@ -74,6 +76,13 @@ const showDot = computed(() => ); const menus = computed(() => [ + { + handler: () => { + router.push({ name: 'Profile' }); + }, + icon: 'lucide:user', + text: $t('page.auth.profile'), + }, { handler: () => { openWindow(VBEN_DOC_URL, { diff --git a/playground/src/locales/langs/en-US/page.json b/playground/src/locales/langs/en-US/page.json index 50f19dc4..32e323c2 100644 --- a/playground/src/locales/langs/en-US/page.json +++ b/playground/src/locales/langs/en-US/page.json @@ -6,7 +6,8 @@ "qrcodeLogin": "Qr Code Login", "forgetPassword": "Forget Password", "sendingCode": "SMS Code is sending...", - "codeSentTo": "Code has been sent to {0}" + "codeSentTo": "Code has been sent to {0}", + "profile": "Profile" }, "dashboard": { "title": "Dashboard", diff --git a/playground/src/locales/langs/zh-CN/page.json b/playground/src/locales/langs/zh-CN/page.json index 666db5b7..437068c9 100644 --- a/playground/src/locales/langs/zh-CN/page.json +++ b/playground/src/locales/langs/zh-CN/page.json @@ -6,7 +6,8 @@ "qrcodeLogin": "二维码登录", "forgetPassword": "忘记密码", "sendingCode": "正在发送验证码", - "codeSentTo": "验证码已发送至{0}" + "codeSentTo": "验证码已发送至{0}", + "profile": "个人中心" }, "dashboard": { "title": "概览", diff --git a/playground/src/router/routes/modules/vben.ts b/playground/src/router/routes/modules/vben.ts index 1c177798..6b621e9e 100644 --- a/playground/src/router/routes/modules/vben.ts +++ b/playground/src/router/routes/modules/vben.ts @@ -101,6 +101,16 @@ const routes: RouteRecordRaw[] = [ name: 'VbenAbout', path: '/vben-admin/about', }, + { + name: 'Profile', + path: '/profile', + component: () => import('#/views/_core/profile/index.vue'), + meta: { + icon: 'lucide:user', + hideInMenu: true, + title: $t('page.auth.profile'), + }, + }, ]; export default routes; diff --git a/playground/src/views/_core/profile/base-setting.vue b/playground/src/views/_core/profile/base-setting.vue new file mode 100644 index 00000000..aa8a4c26 --- /dev/null +++ b/playground/src/views/_core/profile/base-setting.vue @@ -0,0 +1,65 @@ + + diff --git a/playground/src/views/_core/profile/index.vue b/playground/src/views/_core/profile/index.vue new file mode 100644 index 00000000..8740894e --- /dev/null +++ b/playground/src/views/_core/profile/index.vue @@ -0,0 +1,49 @@ + + diff --git a/playground/src/views/_core/profile/notification-setting.vue b/playground/src/views/_core/profile/notification-setting.vue new file mode 100644 index 00000000..324a4b39 --- /dev/null +++ b/playground/src/views/_core/profile/notification-setting.vue @@ -0,0 +1,31 @@ + + diff --git a/playground/src/views/_core/profile/password-setting.vue b/playground/src/views/_core/profile/password-setting.vue new file mode 100644 index 00000000..b246bc37 --- /dev/null +++ b/playground/src/views/_core/profile/password-setting.vue @@ -0,0 +1,66 @@ + + diff --git a/playground/src/views/_core/profile/security-setting.vue b/playground/src/views/_core/profile/security-setting.vue new file mode 100644 index 00000000..be30db58 --- /dev/null +++ b/playground/src/views/_core/profile/security-setting.vue @@ -0,0 +1,43 @@ + + From dbc5ea32aeec769cce362629228feadc537b536b Mon Sep 17 00:00:00 2001 From: shixi <2375768084@qq.com> Date: Tue, 11 Nov 2025 23:41:52 +0800 Subject: [PATCH 11/14] feat: add read and delete for each notification --- apps/web-antd/src/layouts/basic.vue | 17 +++++++++++ apps/web-ele/src/layouts/basic.vue | 17 +++++++++++ apps/web-naive/src/layouts/basic.vue | 17 +++++++++++ .../src/widgets/notification/notification.vue | 30 +++++++++++++++++-- .../layouts/src/widgets/notification/types.ts | 1 + 5 files changed, 79 insertions(+), 3 deletions(-) diff --git a/apps/web-antd/src/layouts/basic.vue b/apps/web-antd/src/layouts/basic.vue index 805b8a73..3f977e1f 100644 --- a/apps/web-antd/src/layouts/basic.vue +++ b/apps/web-antd/src/layouts/basic.vue @@ -23,6 +23,7 @@ import LoginForm from '#/views/_core/authentication/login.vue'; const notifications = ref([ { + id: 1, avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB', date: '3小时前', isRead: true, @@ -30,6 +31,7 @@ const notifications = ref([ title: '收到了 14 份新周报', }, { + id: 2, avatar: 'https://avatar.vercel.sh/1', date: '刚刚', isRead: false, @@ -37,6 +39,7 @@ const notifications = ref([ title: '朱偏右 回复了你', }, { + id: 3, avatar: 'https://avatar.vercel.sh/1', date: '2024-01-01', isRead: false, @@ -44,6 +47,7 @@ const notifications = ref([ title: '曲丽丽 评论了你', }, { + id: 4, avatar: 'https://avatar.vercel.sh/satori', date: '1天前', isRead: false, @@ -102,6 +106,17 @@ function handleNoticeClear() { notifications.value = []; } +function markRead(id: number | string) { + const item = notifications.value.find((item) => item.id === id); + if (item) { + item.isRead = true; + } +} + +function remove(id: number | string) { + notifications.value = notifications.value.filter((item) => item.id !== id); +} + function handleMakeAll() { notifications.value.forEach((item) => (item.isRead = true)); } @@ -144,6 +159,8 @@ watch( :dot="showDot" :notifications="notifications" @clear="handleNoticeClear" + @read="(item) => item.id && markRead(item.id)" + @remove="(item) => item.id && remove(item.id)" @make-all="handleMakeAll" /> diff --git a/apps/web-ele/src/layouts/basic.vue b/apps/web-ele/src/layouts/basic.vue index 805b8a73..3f977e1f 100644 --- a/apps/web-ele/src/layouts/basic.vue +++ b/apps/web-ele/src/layouts/basic.vue @@ -23,6 +23,7 @@ import LoginForm from '#/views/_core/authentication/login.vue'; const notifications = ref([ { + id: 1, avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB', date: '3小时前', isRead: true, @@ -30,6 +31,7 @@ const notifications = ref([ title: '收到了 14 份新周报', }, { + id: 2, avatar: 'https://avatar.vercel.sh/1', date: '刚刚', isRead: false, @@ -37,6 +39,7 @@ const notifications = ref([ title: '朱偏右 回复了你', }, { + id: 3, avatar: 'https://avatar.vercel.sh/1', date: '2024-01-01', isRead: false, @@ -44,6 +47,7 @@ const notifications = ref([ title: '曲丽丽 评论了你', }, { + id: 4, avatar: 'https://avatar.vercel.sh/satori', date: '1天前', isRead: false, @@ -102,6 +106,17 @@ function handleNoticeClear() { notifications.value = []; } +function markRead(id: number | string) { + const item = notifications.value.find((item) => item.id === id); + if (item) { + item.isRead = true; + } +} + +function remove(id: number | string) { + notifications.value = notifications.value.filter((item) => item.id !== id); +} + function handleMakeAll() { notifications.value.forEach((item) => (item.isRead = true)); } @@ -144,6 +159,8 @@ watch( :dot="showDot" :notifications="notifications" @clear="handleNoticeClear" + @read="(item) => item.id && markRead(item.id)" + @remove="(item) => item.id && remove(item.id)" @make-all="handleMakeAll" /> diff --git a/apps/web-naive/src/layouts/basic.vue b/apps/web-naive/src/layouts/basic.vue index 0e9747be..1339a841 100644 --- a/apps/web-naive/src/layouts/basic.vue +++ b/apps/web-naive/src/layouts/basic.vue @@ -23,6 +23,7 @@ import LoginForm from '#/views/_core/authentication/login.vue'; const notifications = ref([ { + id: 1, avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB', date: '3小时前', isRead: true, @@ -30,6 +31,7 @@ const notifications = ref([ title: '收到了 14 份新周报', }, { + id: 2, avatar: 'https://avatar.vercel.sh/1', date: '刚刚', isRead: false, @@ -37,6 +39,7 @@ const notifications = ref([ title: '朱偏右 回复了你', }, { + id: 3, avatar: 'https://avatar.vercel.sh/1', date: '2024-01-01', isRead: false, @@ -44,6 +47,7 @@ const notifications = ref([ title: '曲丽丽 评论了你', }, { + id: 4, avatar: 'https://avatar.vercel.sh/satori', date: '1天前', isRead: false, @@ -102,6 +106,17 @@ function handleNoticeClear() { notifications.value = []; } +function markRead(id: number | string) { + const item = notifications.value.find((item) => item.id === id); + if (item) { + item.isRead = true; + } +} + +function remove(id: number | string) { + notifications.value = notifications.value.filter((item) => item.id !== id); +} + function handleMakeAll() { notifications.value.forEach((item) => (item.isRead = true)); } @@ -145,6 +160,8 @@ watch( :dot="showDot" :notifications="notifications" @clear="handleNoticeClear" + @read="(item) => item.id && markRead(item.id)" + @remove="(item) => item.id && remove(item.id)" @make-all="handleMakeAll" /> diff --git a/packages/effects/layouts/src/widgets/notification/notification.vue b/packages/effects/layouts/src/widgets/notification/notification.vue index e15f9b0e..c9a798aa 100644 --- a/packages/effects/layouts/src/widgets/notification/notification.vue +++ b/packages/effects/layouts/src/widgets/notification/notification.vue @@ -1,7 +1,7 @@ From 05e9d65251e5ddca6d5b32fb22dc8463d321114e Mon Sep 17 00:00:00 2001 From: shixi <2375768084@qq.com> Date: Wed, 12 Nov 2025 01:14:28 +0800 Subject: [PATCH 13/14] fix: for tdesign --- apps/web-tdesign/src/layouts/basic.vue | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/apps/web-tdesign/src/layouts/basic.vue b/apps/web-tdesign/src/layouts/basic.vue index 805b8a73..dee44325 100644 --- a/apps/web-tdesign/src/layouts/basic.vue +++ b/apps/web-tdesign/src/layouts/basic.vue @@ -23,6 +23,7 @@ import LoginForm from '#/views/_core/authentication/login.vue'; const notifications = ref([ { + id: 1, avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB', date: '3小时前', isRead: true, @@ -30,6 +31,7 @@ const notifications = ref([ title: '收到了 14 份新周报', }, { + id: 2, avatar: 'https://avatar.vercel.sh/1', date: '刚刚', isRead: false, @@ -37,6 +39,7 @@ const notifications = ref([ title: '朱偏右 回复了你', }, { + id: 3, avatar: 'https://avatar.vercel.sh/1', date: '2024-01-01', isRead: false, @@ -44,12 +47,31 @@ const notifications = ref([ title: '曲丽丽 评论了你', }, { + id: 4, avatar: 'https://avatar.vercel.sh/satori', date: '1天前', isRead: false, message: '描述信息描述信息描述信息', title: '代办提醒', }, + { + id: 5, + avatar: 'https://avatar.vercel.sh/satori', + date: '1天前', + isRead: false, + message: '描述信息描述信息描述信息', + title: '跳转Workspace示例', + link: '/workspace', + }, + { + id: 6, + avatar: 'https://avatar.vercel.sh/satori', + date: '1天前', + isRead: false, + message: '描述信息描述信息描述信息', + title: '跳转外部链接示例', + link: 'https://doc.vben.pro', + }, ]); const userStore = useUserStore(); @@ -102,6 +124,17 @@ function handleNoticeClear() { notifications.value = []; } +function markRead(id: number | string) { + const item = notifications.value.find((item) => item.id === id); + if (item) { + item.isRead = true; + } +} + +function remove(id: number | string) { + notifications.value = notifications.value.filter((item) => item.id !== id); +} + function handleMakeAll() { notifications.value.forEach((item) => (item.isRead = true)); } @@ -144,6 +177,8 @@ watch( :dot="showDot" :notifications="notifications" @clear="handleNoticeClear" + @read="(item) => item.id && markRead(item.id)" + @remove="(item) => item.id && remove(item.id)" @make-all="handleMakeAll" /> From 573637222d9167ef50bf4eb4fb7d8ffe4b983ea3 Mon Sep 17 00:00:00 2001 From: panda7 Date: Wed, 12 Nov 2025 02:03:12 +0800 Subject: [PATCH 14/14] feat: add form handleCollapsedChange event (#6893) * feat: add form handleCollapsedChange event * fix: ts lint * fix: ts error --------- Co-authored-by: sqchen --- docs/src/components/common-ui/vben-form.md | 1 + .../form-ui/src/components/form-actions.vue | 4 ++-- packages/@core/ui-kit/form-ui/src/form-api.ts | 1 + packages/@core/ui-kit/form-ui/src/types.ts | 4 ++++ .../ui-kit/form-ui/src/use-form-context.ts | 2 +- .../@core/ui-kit/form-ui/src/vben-form.vue | 4 +++- .../ui-kit/form-ui/src/vben-use-form.vue | 21 +++++++++++-------- 7 files changed, 24 insertions(+), 13 deletions(-) diff --git a/docs/src/components/common-ui/vben-form.md b/docs/src/components/common-ui/vben-form.md index 48772bfa..30cbec44 100644 --- a/docs/src/components/common-ui/vben-form.md +++ b/docs/src/components/common-ui/vben-form.md @@ -335,6 +335,7 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单 | handleReset | 表单重置回调 | `(values: Record,) => Promise \| void` | - | | handleSubmit | 表单提交回调 | `(values: Record,) => Promise \| void` | - | | handleValuesChange | 表单值变化回调 | `(values: Record, fieldsChanged: string[]) => void` | - | +| handleCollapsedChange | 表单收起展开状态变化回调 | `(collapsed: boolean) => void` | - | | actionButtonsReverse | 调换操作按钮位置 | `boolean` | `false` | | resetButtonOptions | 重置按钮组件参数 | `ActionButtonOptions` | - | | submitButtonOptions | 提交按钮组件参数 | `ActionButtonOptions` | - | diff --git a/packages/@core/ui-kit/form-ui/src/components/form-actions.vue b/packages/@core/ui-kit/form-ui/src/components/form-actions.vue index 42101c12..36b1caa0 100644 --- a/packages/@core/ui-kit/form-ui/src/components/form-actions.vue +++ b/packages/@core/ui-kit/form-ui/src/components/form-actions.vue @@ -47,7 +47,7 @@ async function handleSubmit(e: Event) { return; } - const values = toRaw(await props.formApi.getValues()); + const values = toRaw(await props.formApi.getValues()) ?? {}; await props.handleSubmit?.(values); } @@ -56,7 +56,7 @@ async function handleReset(e: Event) { e?.stopPropagation(); const props = unref(rootProps); - const values = toRaw(await props.formApi?.getValues()); + const values = toRaw(await props.formApi?.getValues()) ?? {}; if (isFunction(props.handleReset)) { await props.handleReset?.(values); diff --git a/packages/@core/ui-kit/form-ui/src/form-api.ts b/packages/@core/ui-kit/form-ui/src/form-api.ts index 401748c3..f5f353dc 100644 --- a/packages/@core/ui-kit/form-ui/src/form-api.ts +++ b/packages/@core/ui-kit/form-ui/src/form-api.ts @@ -36,6 +36,7 @@ function getDefaultState(): VbenFormProps { handleReset: undefined, handleSubmit: undefined, handleValuesChange: undefined, + handleCollapsedChange: undefined, layout: 'horizontal', resetButtonOptions: {}, schema: [], diff --git a/packages/@core/ui-kit/form-ui/src/types.ts b/packages/@core/ui-kit/form-ui/src/types.ts index 824b7a44..6d704145 100644 --- a/packages/@core/ui-kit/form-ui/src/types.ts +++ b/packages/@core/ui-kit/form-ui/src/types.ts @@ -379,6 +379,10 @@ export interface VbenFormProps< * 表单字段映射 */ fieldMappingTime?: FieldMappingTime; + /** + * 表单收起展开状态变化回调 + */ + handleCollapsedChange?: (collapsed: boolean) => void; /** * 表单重置回调 */ diff --git a/packages/@core/ui-kit/form-ui/src/use-form-context.ts b/packages/@core/ui-kit/form-ui/src/use-form-context.ts index 4ef182ed..e95c87b9 100644 --- a/packages/@core/ui-kit/form-ui/src/use-form-context.ts +++ b/packages/@core/ui-kit/form-ui/src/use-form-context.ts @@ -13,7 +13,7 @@ import { useForm } from 'vee-validate'; import { object, ZodIntersection, ZodNumber, ZodObject, ZodString } from 'zod'; import { getDefaultsForSchema } from 'zod-defaults'; -type ExtendFormProps = VbenFormProps & { formApi: ExtendedFormApi }; +type ExtendFormProps = VbenFormProps & { formApi?: ExtendedFormApi }; export const [injectFormProps, provideFormProps] = createContext<[ComputedRef | ExtendFormProps, FormActions]>( diff --git a/packages/@core/ui-kit/form-ui/src/vben-form.vue b/packages/@core/ui-kit/form-ui/src/vben-form.vue index 260e75cd..f3ba37f0 100644 --- a/packages/@core/ui-kit/form-ui/src/vben-form.vue +++ b/packages/@core/ui-kit/form-ui/src/vben-form.vue @@ -40,7 +40,9 @@ const { delegatedSlots, form } = useFormInitial(props); provideFormProps([props, form]); const handleUpdateCollapsed = (value: boolean) => { - currentCollapsed.value = !!value; + currentCollapsed.value = value; + // 触发收起展开状态变化回调 + props.handleCollapsedChange?.(value); }; watchEffect(() => { diff --git a/packages/@core/ui-kit/form-ui/src/vben-use-form.vue b/packages/@core/ui-kit/form-ui/src/vben-use-form.vue index 3e7b00b6..e9386725 100644 --- a/packages/@core/ui-kit/form-ui/src/vben-use-form.vue +++ b/packages/@core/ui-kit/form-ui/src/vben-use-form.vue @@ -25,7 +25,7 @@ import { } from './use-form-context'; // 通过 extends 会导致热更新卡死,所以重复写了一遍 interface Props extends VbenFormProps { - formApi: ExtendedFormApi; + formApi?: ExtendedFormApi; } const props = defineProps(); @@ -44,11 +44,13 @@ provideComponentRefMap(componentRefMap); props.formApi?.mount?.(form, componentRefMap); const handleUpdateCollapsed = (value: boolean) => { - props.formApi?.setState({ collapsed: !!value }); + props.formApi?.setState({ collapsed: value }); + // 触发收起展开状态变化回调 + forward.value.handleCollapsedChange?.(value); }; function handleKeyDownEnter(event: KeyboardEvent) { - if (!state.value.submitOnEnter || !forward.value.formApi?.isMounted) { + if (!state?.value.submitOnEnter || !forward.value.formApi?.isMounted) { return; } // 如果是 textarea 不阻止默认行为,否则会导致无法换行。 @@ -58,11 +60,11 @@ function handleKeyDownEnter(event: KeyboardEvent) { } event.preventDefault(); - forward.value.formApi.validateAndSubmitForm(); + forward.value.formApi?.validateAndSubmitForm(); } const handleValuesChangeDebounced = useDebounceFn(async () => { - state.value.submitOnChange && forward.value.formApi?.validateAndSubmitForm(); + state?.value.submitOnChange && forward.value.formApi?.validateAndSubmitForm(); }, 300); const valuesCache: Recordable = {}; @@ -74,7 +76,7 @@ onMounted(async () => { () => form.values, async (newVal) => { if (forward.value.handleValuesChange) { - const fields = state.value.schema?.map((item) => { + const fields = state?.value.schema?.map((item) => { return item.fieldName; }); @@ -91,8 +93,9 @@ onMounted(async () => { if (changedFields.length > 0) { // 调用handleValuesChange回调,传入所有表单值的深拷贝和变更的字段列表 + const values = await forward.value.formApi?.getValues(); forward.value.handleValuesChange( - cloneDeep(await forward.value.formApi.getValues()), + cloneDeep(values ?? {}) as Record, changedFields, ); } @@ -109,7 +112,7 @@ onMounted(async () => {
{