From 0f756503ff4cb327eaaf4261b7cb0c7cc26cdcc4 Mon Sep 17 00:00:00 2001 From: Netfan Date: Mon, 16 Dec 2024 12:44:07 +0800 Subject: [PATCH 1/7] feat: add demo for modify menu badge data --- packages/@core/ui-kit/menu-ui/src/index.ts | 1 + packages/stores/src/modules/access.ts | 19 ++++ playground/package.json | 1 + playground/src/views/demos/badge/index.vue | 113 ++++++++++++++++++++- pnpm-lock.yaml | 3 + 5 files changed, 135 insertions(+), 2 deletions(-) diff --git a/packages/@core/ui-kit/menu-ui/src/index.ts b/packages/@core/ui-kit/menu-ui/src/index.ts index 575a2330..1e3bc143 100644 --- a/packages/@core/ui-kit/menu-ui/src/index.ts +++ b/packages/@core/ui-kit/menu-ui/src/index.ts @@ -1,3 +1,4 @@ +export { default as MenuBadge } from './components/menu-badge.vue'; export * from './components/normal-menu'; export { default as Menu } from './menu.vue'; export type * from './types'; diff --git a/packages/stores/src/modules/access.ts b/packages/stores/src/modules/access.ts index ee8641a7..6b445207 100644 --- a/packages/stores/src/modules/access.ts +++ b/packages/stores/src/modules/access.ts @@ -41,6 +41,25 @@ interface AccessState { */ export const useAccessStore = defineStore('core-access', { actions: { + getMenuByPath(path: string) { + function findMenu( + menus: MenuRecordRaw[], + path: string, + ): MenuRecordRaw | undefined { + for (const menu of menus) { + if (menu.path === path) { + return menu; + } + if (menu.children) { + const matched = findMenu(menu.children, path); + if (matched) { + return matched; + } + } + } + } + return findMenu(this.accessMenus, path); + }, setAccessCodes(codes: string[]) { this.accessCodes = codes; }, diff --git a/playground/package.json b/playground/package.json index 5518c17f..720622e0 100644 --- a/playground/package.json +++ b/playground/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "@tanstack/vue-query": "catalog:", + "@vben-core/menu-ui": "workspace:*", "@vben/access": "workspace:*", "@vben/common-ui": "workspace:*", "@vben/constants": "workspace:*", diff --git a/playground/src/views/demos/badge/index.vue b/playground/src/views/demos/badge/index.vue index 92d70c7e..8976c6fe 100644 --- a/playground/src/views/demos/badge/index.vue +++ b/playground/src/views/demos/badge/index.vue @@ -1,7 +1,116 @@ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ce181fe1..2de4ab5b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1768,6 +1768,9 @@ importers: '@tanstack/vue-query': specifier: 'catalog:' version: 5.62.7(vue@3.5.13(typescript@5.7.2)) + '@vben-core/menu-ui': + specifier: workspace:* + version: link:../packages/@core/ui-kit/menu-ui '@vben/access': specifier: workspace:* version: link:../packages/effects/access From 38805a0e1fc0d35cf3e3f0a4773d7e2ea1f61760 Mon Sep 17 00:00:00 2001 From: Netfan Date: Mon, 16 Dec 2024 20:48:51 +0800 Subject: [PATCH 2/7] feat: improve code login demo (#5154) * feat: add some method in formApi * fix: VbenPinInput style with small screen * chore: improve code login demo --- docs/src/components/common-ui/vben-form.md | 2 + packages/@core/ui-kit/form-ui/src/form-api.ts | 15 +++++++ .../ui-kit/shadcn-ui/src/ui/button/button.ts | 4 +- .../src/ui/pin-input/PinInputInput.vue | 2 +- playground/src/locales/langs/en-US/page.json | 4 +- playground/src/locales/langs/zh-CN/page.json | 4 +- .../views/_core/authentication/code-login.vue | 44 ++++++++++++++++++- 7 files changed, 68 insertions(+), 7 deletions(-) diff --git a/docs/src/components/common-ui/vben-form.md b/docs/src/components/common-ui/vben-form.md index 618e3d3a..d7b4c098 100644 --- a/docs/src/components/common-ui/vben-form.md +++ b/docs/src/components/common-ui/vben-form.md @@ -287,6 +287,8 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单 | setValues | 设置表单值, 默认会过滤不在schema中定义的field, 可通过filterFields形参关闭过滤 | `(fields: Record, filterFields?: boolean, shouldValidate?: boolean) => Promise` | | getValues | 获取表单值 | `(fields:Record,shouldValidate: boolean = false)=>Promise` | | validate | 表单校验 | `()=>Promise` | +| validateField | 校验指定字段 | `(fieldName: string)=>Promise>` | +| isFieldValid | 检查某个字段是否已通过校验 | `(fieldName: string)=>Promise` | | resetValidate | 重置表单校验 | `()=>Promise` | | updateSchema | 更新formSchema | `(schema:FormSchema[])=>void` | | setFieldValue | 设置字段值 | `(field: string, value: any, shouldValidate?: boolean)=>Promise` | 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 585afaff..8de5c405 100644 --- a/packages/@core/ui-kit/form-ui/src/form-api.ts +++ b/packages/@core/ui-kit/form-ui/src/form-api.ts @@ -130,6 +130,11 @@ export class FormApi { return form.values; } + async isFieldValid(fieldName: string) { + const form = await this.getForm(); + return form.isFieldValid(fieldName); + } + merge(formApi: FormApi) { const chain = [this, formApi]; const proxy = new Proxy(formApi, { @@ -348,4 +353,14 @@ export class FormApi { } return await this.submitForm(); } + + async validateField(fieldName: string, opts?: Partial) { + const form = await this.getForm(); + const validateResult = await form.validateField(fieldName, opts); + + if (Object.keys(validateResult?.errors ?? {}).length > 0) { + console.error('validate error', validateResult?.errors); + } + return validateResult; + } } diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/button/button.ts b/packages/@core/ui-kit/shadcn-ui/src/ui/button/button.ts index 3ca9b4b4..d0287751 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/button/button.ts +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/button/button.ts @@ -11,8 +11,8 @@ export const buttonVariants = cva( size: { default: 'h-9 px-4 py-2', icon: 'h-8 w-8 rounded-sm px-1 text-lg', - lg: 'h-10 rounded-md px-8', - sm: 'h-8 rounded-md px-3 text-xs', + lg: 'h-10 rounded-md px-4', + sm: 'h-8 rounded-md px-2 text-xs', xs: 'h-8 w-8 rounded-sm px-1 text-xs', }, variant: { diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputInput.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputInput.vue index 6971f257..ee74d768 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputInput.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputInput.vue @@ -24,7 +24,7 @@ const forwardedProps = useForwardProps(delegatedProps); v-bind="forwardedProps" :class=" cn( - 'border-input bg-background relative flex h-10 w-10 items-center justify-center border-y border-r text-center text-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md focus:relative focus:z-10 focus:outline-none focus:ring-2', + 'border-input bg-background relative flex h-10 w-8 items-center justify-center border-y border-r text-center text-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md focus:relative focus:z-10 focus:outline-none focus:ring-2 md:w-10', props.class, ) " diff --git a/playground/src/locales/langs/en-US/page.json b/playground/src/locales/langs/en-US/page.json index 618a258c..50f19dc4 100644 --- a/playground/src/locales/langs/en-US/page.json +++ b/playground/src/locales/langs/en-US/page.json @@ -4,7 +4,9 @@ "register": "Register", "codeLogin": "Code Login", "qrcodeLogin": "Qr Code Login", - "forgetPassword": "Forget Password" + "forgetPassword": "Forget Password", + "sendingCode": "SMS Code is sending...", + "codeSentTo": "Code has been sent to {0}" }, "dashboard": { "title": "Dashboard", diff --git a/playground/src/locales/langs/zh-CN/page.json b/playground/src/locales/langs/zh-CN/page.json index 4cb67081..666db5b7 100644 --- a/playground/src/locales/langs/zh-CN/page.json +++ b/playground/src/locales/langs/zh-CN/page.json @@ -4,7 +4,9 @@ "register": "注册", "codeLogin": "验证码登录", "qrcodeLogin": "二维码登录", - "forgetPassword": "忘记密码" + "forgetPassword": "忘记密码", + "sendingCode": "正在发送验证码", + "codeSentTo": "验证码已发送至{0}" }, "dashboard": { "title": "概览", diff --git a/playground/src/views/_core/authentication/code-login.vue b/playground/src/views/_core/authentication/code-login.vue index acfd1fd7..3b50c626 100644 --- a/playground/src/views/_core/authentication/code-login.vue +++ b/playground/src/views/_core/authentication/code-login.vue @@ -2,16 +2,36 @@ import type { VbenFormSchema } from '@vben/common-ui'; import type { Recordable } from '@vben/types'; -import { computed, ref } from 'vue'; +import { computed, ref, useTemplateRef } from 'vue'; import { AuthenticationCodeLogin, z } from '@vben/common-ui'; import { $t } from '@vben/locales'; +import { message } from 'ant-design-vue'; + defineOptions({ name: 'CodeLogin' }); const loading = ref(false); const CODE_LENGTH = 6; - +const loginRef = + useTemplateRef>('loginRef'); +function sendCodeApi(phoneNumber: string) { + message.loading({ + content: $t('page.auth.sendingCode'), + duration: 0, + key: 'sending-code', + }); + return new Promise((resolve) => { + setTimeout(() => { + message.success({ + content: $t('page.auth.codeSentTo', [phoneNumber]), + duration: 3, + key: 'sending-code', + }); + resolve({ code: '123456', phoneNumber }); + }, 3000); + }); +} const formSchema = computed((): VbenFormSchema[] => { return [ { @@ -39,6 +59,25 @@ const formSchema = computed((): VbenFormSchema[] => { : $t('authentication.sendCode'); return text; }, + handleSendCode: async () => { + // 模拟发送验证码 + // Simulate sending verification code + loading.value = true; + const formApi = loginRef.value?.getFormApi(); + if (!formApi) { + loading.value = false; + throw new Error('formApi is not ready'); + } + await formApi.validateField('phoneNumber'); + const isPhoneReady = await formApi.isFieldValid('phoneNumber'); + if (!isPhoneReady) { + loading.value = false; + throw new Error('Phone number is not Ready'); + } + const { phoneNumber } = await formApi.getValues(); + await sendCodeApi(phoneNumber); + loading.value = false; + }, placeholder: $t('authentication.code'), }, fieldName: 'code', @@ -62,6 +101,7 @@ async function handleLogin(values: Recordable) {