Browse Source

refactor: 重构useValidator

kailong321200875 1 year ago
parent
commit
f9cfa7cc73

+ 68 - 0
mock/role/index.ts

@@ -84,6 +84,73 @@ const adminList = [
           activeMenu: '/manage/news-page'
         }
       },
+      {
+        path: 'useValidator',
+        component: 'views/hooks/useValidator',
+        name: 'UseValidator',
+        meta: {
+          title: 'useValidator'
+        }
+      }
+      // {
+      //   path: 'useCrudSchemas',
+      //   component: 'views/hooks/useCrudSchemas',
+      //   name: 'UseCrudSchemas',
+      //   meta: {
+      //     title: 'useCrudSchemas'
+      //   }
+      // }
+    ]
+  },
+  {
+    path: '/level',
+    component: '#',
+    redirect: '/level/menu1/menu1-1/menu1-1-1',
+    name: 'Level',
+    meta: {
+      title: 'router.level',
+      icon: 'carbon:skill-level-advanced'
+    },
+    children: [
+      {
+        path: 'menu1',
+        name: 'Menu1',
+        component: '##',
+        redirect: '/level/menu1/menu1-1/menu1-1-1',
+        meta: {
+          title: 'router.menu1'
+        },
+        children: [
+          {
+            path: 'menu1-1',
+            name: 'Menu11',
+            component: '##',
+            redirect: '/level/menu1/menu1-1/menu1-1-1',
+            meta: {
+              title: 'router.menu11',
+              alwaysShow: true
+            },
+            children: [
+              {
+                path: 'menu1-1-1',
+                name: 'Menu111',
+                component: 'views/Level/Menu111',
+                meta: {
+                  title: 'router.menu111'
+                }
+              }
+            ]
+          },
+          {
+            path: 'menu1-2',
+            name: 'Menu12',
+            component: 'views/Level/Menu12',
+            meta: {
+              title: 'router.menu12'
+            }
+          }
+        ]
+      },
       {
         path: 'news-detail',
         component: 'views/Manage/News/NewsDetail',
@@ -301,6 +368,7 @@ const testList: string[] = [
   '/hooks',
   '/hooks/useWatermark',
   '/hooks/useTagsView',
+  '/hooks/useValidator',
   // '/hooks/useCrudSchemas',
   '/level',
   '/level/menu1',

+ 49 - 0
src/hooks/web/useGuide.ts

@@ -0,0 +1,49 @@
+import { Config, driver } from 'driver.js'
+import 'driver.js/dist/driver.css'
+import { useDesign } from '@/hooks/web/useDesign'
+import { useI18n } from '@/hooks/web/useI18n'
+
+const { t } = useI18n()
+
+const { variables } = useDesign()
+
+export const useGuide = (options?: Config) => {
+  const driverObj = driver(
+    options || {
+      showProgress: true,
+      nextBtnText: t('common.nextLabel'),
+      prevBtnText: t('common.prevLabel'),
+      doneBtnText: t('common.doneLabel'),
+      steps: [
+        {
+          element: `#${variables.namespace}-menu`,
+          popover: {
+            title: t('common.menu'),
+            description: t('common.menuDes'),
+            side: 'right'
+          }
+        },
+        {
+          element: `#${variables.namespace}-tool-header`,
+          popover: {
+            title: t('common.tool'),
+            description: t('common.toolDes'),
+            side: 'left'
+          }
+        },
+        {
+          element: `#${variables.namespace}-tags-view`,
+          popover: {
+            title: t('common.tagsView'),
+            description: t('common.tagsViewDes'),
+            side: 'bottom'
+          }
+        }
+      ]
+    }
+  )
+
+  return {
+    ...driverObj
+  }
+}

+ 48 - 44
src/hooks/web/useValidator.ts

@@ -1,76 +1,81 @@
 import { useI18n } from '@/hooks/web/useI18n'
+import { FormItemRule } from 'element-plus'
 
 const { t } = useI18n()
 
-type Callback = (error?: string | Error | undefined) => void
-
 interface LengthRange {
   min: number
   max: number
-  message: string
+  message?: string
 }
 
 export const useValidator = () => {
-  const required = (message?: string) => {
+  const required = (message?: string): FormItemRule => {
     return {
       required: true,
       message: message || t('common.required')
     }
   }
 
-  const lengthRange = (val: any, callback: Callback, options: LengthRange) => {
+  const lengthRange = (options: LengthRange): FormItemRule => {
     const { min, max, message } = options
-    if (val.length < min || val.length > max) {
-      callback(new Error(message))
-    } else {
-      callback()
-    }
-  }
 
-  const notSpace = (val: any, callback: Callback, message: string) => {
-    // 用户名不能有空格
-    if (val.indexOf(' ') !== -1) {
-      callback(new Error(message))
-    } else {
-      callback()
+    return {
+      min,
+      max,
+      message: message || t('common.lengthRange', { min, max })
     }
   }
 
-  const notSpecialCharacters = (val: any, callback: Callback, message: string) => {
-    // 密码不能是特殊字符
-    if (/[`~!@#$%^&*()_+<>?:"{},.\/;'[\]]/gi.test(val)) {
-      callback(new Error(message))
-    } else {
-      callback()
+  const notSpace = (message?: string): FormItemRule => {
+    return {
+      validator: (_, val, callback) => {
+        if (val.indexOf(' ') !== -1) {
+          callback(new Error(message || t('common.notSpace')))
+        } else {
+          callback()
+        }
+      }
     }
   }
 
-  // 两个字符串是否想等
-  const isEqual = (val1: string, val2: string, callback: Callback, message: string) => {
-    if (val1 === val2) {
-      callback()
-    } else {
-      callback(new Error(message))
+  const notSpecialCharacters = (message?: string): FormItemRule => {
+    return {
+      validator: (_, val, callback) => {
+        if (/[`~!@#$%^&*()_+<>?:"{},.\/;'[\]]/gi.test(val)) {
+          callback(new Error(message || t('common.notSpecialCharacters')))
+        } else {
+          callback()
+        }
+      }
     }
   }
 
-  const isEmail = (val: any, callback: Callback, message: string) => {
-    // 判断是否是邮箱
-    if (!/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/g.test(val)) {
-      callback(new Error(message))
-    } else {
-      callback()
+  const isEmail = (message: string): FormItemRule => {
+    return {
+      validator: (_, val, callback) => {
+        // 判断是否是邮箱
+        if (!/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/g.test(val)) {
+          callback(new Error(message))
+        } else {
+          callback()
+        }
+      }
     }
   }
 
-  const isMobilePhone = (val: any, callback: Callback, message: string) => {
-    // 判断是否是手机号
-    if (
-      !/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/g.test(val)
-    ) {
-      callback(new Error(message))
-    } else {
-      callback()
+  const isMobilePhone = (message: string): FormItemRule => {
+    return {
+      validator: (_, val, callback) => {
+        // 判断是否是手机号
+        if (
+          !/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/g.test(val)
+        ) {
+          callback(new Error(message))
+        } else {
+          callback()
+        }
+      }
     }
   }
 
@@ -79,7 +84,6 @@ export const useValidator = () => {
     lengthRange,
     notSpace,
     notSpecialCharacters,
-    isEqual,
     isEmail,
     isMobilePhone
   }

+ 5 - 1
src/locales/en.ts

@@ -44,7 +44,11 @@ export default {
     refresh: 'Refresh',
     fullscreen: 'Fullscreen',
     size: 'Size',
-    columnSetting: 'Column setting'
+    columnSetting: 'Column setting',
+    lengthRange: 'The length should be between {min} and {max}',
+    notSpace: 'Spaces are not allowed',
+    notSpecialCharacters: 'Special characters are not allowed',
+    isEqual: 'The two are not equal'
   },
   lock: {
     lockScreen: 'Lock screen',

+ 5 - 1
src/locales/zh-CN.ts

@@ -44,7 +44,11 @@ export default {
     refresh: '刷新',
     fullscreen: '全屏',
     size: '尺寸',
-    columnSetting: '列设置'
+    columnSetting: '列设置',
+    lengthRange: '长度在 {min} 到 {max} 个字符',
+    notSpace: '不能包含空格',
+    notSpecialCharacters: '不能包含特殊字符',
+    isEqual: '两次输入不一致'
   },
   lock: {
     lockScreen: '锁定屏幕',

+ 246 - 1
src/router/index.ts

@@ -103,7 +103,252 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
         component: () => import('@/views/Manage/News/NewsAdd.vue'),
         name: 'NewsAdd',
         meta: {
-          title: '新增文案',
+          title: t('router.descriptions')
+        }
+      },
+      {
+        path: 'image-viewer',
+        component: () => import('@/views/Components/ImageViewer.vue'),
+        name: 'ImageViewer',
+        meta: {
+          title: t('router.imageViewer')
+        }
+      },
+      {
+        path: 'dialog',
+        component: () => import('@/views/Components/Dialog.vue'),
+        name: 'Dialog',
+        meta: {
+          title: t('router.dialog')
+        }
+      },
+      {
+        path: 'icon',
+        component: () => import('@/views/Components/Icon.vue'),
+        name: 'Icon',
+        meta: {
+          title: t('router.icon')
+        }
+      },
+      {
+        path: 'echart',
+        component: () => import('@/views/Components/Echart.vue'),
+        name: 'Echart',
+        meta: {
+          title: t('router.echart')
+        }
+      },
+      {
+        path: 'count-to',
+        component: () => import('@/views/Components/CountTo.vue'),
+        name: 'CountTo',
+        meta: {
+          title: t('router.countTo')
+        }
+      },
+      {
+        path: 'qrcode',
+        component: () => import('@/views/Components/Qrcode.vue'),
+        name: 'Qrcode',
+        meta: {
+          title: t('router.qrcode')
+        }
+      },
+      {
+        path: 'highlight',
+        component: () => import('@/views/Components/Highlight.vue'),
+        name: 'Highlight',
+        meta: {
+          title: t('router.highlight')
+        }
+      },
+      {
+        path: 'infotip',
+        component: () => import('@/views/Components/Infotip.vue'),
+        name: 'Infotip',
+        meta: {
+          title: t('router.infotip')
+        }
+      },
+      {
+        path: 'input-password',
+        component: () => import('@/views/Components/InputPassword.vue'),
+        name: 'InputPassword',
+        meta: {
+          title: t('router.inputPassword')
+        }
+      }
+    ]
+  },
+  {
+    path: '/function',
+    component: Layout,
+    redirect: '/function/multipleTabs',
+    name: 'Function',
+    meta: {
+      title: t('router.function'),
+      icon: 'ri:function-fill',
+      alwaysShow: true
+    },
+    children: [
+      {
+        path: 'multiple-tabs',
+        component: () => import('@/views/Function/MultipleTabs.vue'),
+        name: 'MultipleTabs',
+        meta: {
+          title: t('router.multipleTabs')
+        }
+      },
+      {
+        path: 'multiple-tabs-demo/:id',
+        component: () => import('@/views/Function/MultipleTabsDemo.vue'),
+        name: 'MultipleTabsDemo',
+        meta: {
+          hidden: true,
+          title: t('router.details'),
+          canTo: true,
+          activeMenu: '/function/multiple-tabs'
+        }
+      }
+    ]
+  },
+  {
+    path: '/hooks',
+    component: Layout,
+    redirect: '/hooks/useWatermark',
+    name: 'Hooks',
+    meta: {
+      title: 'hooks',
+      icon: 'ic:outline-webhook',
+      alwaysShow: true
+    },
+    children: [
+      {
+        path: 'useWatermark',
+        component: () => import('@/views/hooks/useWatermark.vue'),
+        name: 'UseWatermark',
+        meta: {
+          title: 'useWatermark'
+        }
+      },
+      {
+        path: 'useTagsView',
+        component: () => import('@/views/hooks/useTagsView.vue'),
+        name: 'UseTagsView',
+        meta: {
+          title: 'useTagsView'
+        }
+      },
+      {
+        path: 'useValidator',
+        component: () => import('@/views/hooks/useValidator.vue'),
+        name: 'UseValidator',
+        meta: {
+          title: 'useValidator'
+        }
+      }
+      // {
+      //   path: 'useCrudSchemas',
+      //   component: () => import('@/views/hooks/useCrudSchemas.vue'),
+      //   name: 'UseCrudSchemas',
+      //   meta: {
+      //     title: 'useCrudSchemas'
+      //   }
+      // }
+    ]
+  },
+  {
+    path: '/level',
+    component: Layout,
+    redirect: '/level/menu1/menu1-1/menu1-1-1',
+    name: 'Level',
+    meta: {
+      title: t('router.level'),
+      icon: 'carbon:skill-level-advanced'
+    },
+    children: [
+      {
+        path: 'menu1',
+        name: 'Menu1',
+        component: getParentLayout(),
+        redirect: '/level/menu1/menu1-1/menu1-1-1',
+        meta: {
+          title: t('router.menu1')
+        },
+        children: [
+          {
+            path: 'menu1-1',
+            name: 'Menu11',
+            component: getParentLayout(),
+            redirect: '/level/menu1/menu1-1/menu1-1-1',
+            meta: {
+              title: t('router.menu11'),
+              alwaysShow: true
+            },
+            children: [
+              {
+                path: 'menu1-1-1',
+                name: 'Menu111',
+                component: () => import('@/views/Level/Menu111.vue'),
+                meta: {
+                  title: t('router.menu111')
+                }
+              }
+            ]
+          },
+          {
+            path: 'menu1-2',
+            name: 'Menu12',
+            component: () => import('@/views/Level/Menu12.vue'),
+            meta: {
+              title: t('router.menu12')
+            }
+          }
+        ]
+      },
+      {
+        path: 'menu2',
+        name: 'Menu2',
+        component: () => import('@/views/Level/Menu2.vue'),
+        meta: {
+          title: t('router.menu2')
+        }
+      }
+    ]
+  },
+  {
+    path: '/example',
+    component: Layout,
+    redirect: '/example/example-dialog',
+    name: 'Example',
+    meta: {
+      title: t('router.example'),
+      icon: 'ep:management',
+      alwaysShow: true
+    },
+    children: [
+      {
+        path: 'example-dialog',
+        component: () => import('@/views/Example/Dialog/ExampleDialog.vue'),
+        name: 'ExampleDialog',
+        meta: {
+          title: t('router.exampleDialog')
+        }
+      },
+      {
+        path: 'example-page',
+        component: () => import('@/views/Example/Page/ExamplePage.vue'),
+        name: 'ExamplePage',
+        meta: {
+          title: t('router.examplePage')
+        }
+      },
+      {
+        path: 'example-add',
+        component: () => import('@/views/Example/Page/ExampleAdd.vue'),
+        name: 'ExampleAdd',
+        meta: {
+          title: t('router.exampleAdd'),
           noTagsView: true,
           noCache: true,
           hidden: true,

+ 3 - 38
src/views/Guide/Guide.vue

@@ -2,49 +2,14 @@
 import { ContentWrap } from '@/components/ContentWrap'
 import { useI18n } from '@/hooks/web/useI18n'
 import { ElButton } from 'element-plus'
-import { driver } from 'driver.js'
-import 'driver.js/dist/driver.css'
-import { useDesign } from '@/hooks/web/useDesign'
+import { useGuide } from '@/hooks/web/useGuide'
 
 const { t } = useI18n()
 
-const { variables } = useDesign()
-
-const driverObj = driver({
-  showProgress: true,
-  nextBtnText: t('common.nextLabel'),
-  prevBtnText: t('common.prevLabel'),
-  doneBtnText: t('common.doneLabel'),
-  steps: [
-    {
-      element: `#${variables.namespace}-menu`,
-      popover: {
-        title: t('common.menu'),
-        description: t('common.menuDes'),
-        side: 'right'
-      }
-    },
-    {
-      element: `#${variables.namespace}-tool-header`,
-      popover: {
-        title: t('common.tool'),
-        description: t('common.toolDes'),
-        side: 'left'
-      }
-    },
-    {
-      element: `#${variables.namespace}-tags-view`,
-      popover: {
-        title: t('common.tagsView'),
-        description: t('common.tagsViewDes'),
-        side: 'bottom'
-      }
-    }
-  ]
-})
+const { drive } = useGuide()
 
 const guideStart = () => {
-  driverObj.drive()
+  drive()
 }
 </script>
 

+ 80 - 0
src/views/hooks/useValidator.vue

@@ -0,0 +1,80 @@
+<script setup lang="ts">
+import { ContentWrap } from '@/components/ContentWrap'
+import { Form, FormSchema } from '@/components/Form'
+import { useValidator } from '@/hooks/web/useValidator'
+import { useForm } from '@/hooks/web/useForm'
+import { reactive } from 'vue'
+import { FormItemRule } from 'element-plus'
+
+const { formRegister, formMethods } = useForm()
+
+const { getFormData } = formMethods
+
+const { required, lengthRange, notSpace, notSpecialCharacters } = useValidator()
+
+const formSchema = reactive<FormSchema[]>([
+  {
+    field: 'field1',
+    label: '必填',
+    component: 'Input'
+  },
+  {
+    field: 'field2',
+    label: '长度范围',
+    component: 'Input'
+  },
+  {
+    field: 'field3',
+    label: '不能有空格',
+    component: 'Input'
+  },
+  {
+    field: 'field4',
+    label: '不能有特殊字符',
+    component: 'Input'
+  },
+  {
+    field: 'field5',
+    label: '是否相等-值1',
+    component: 'Input'
+  },
+  {
+    field: 'field6',
+    label: '是否相等-值2',
+    component: 'Input'
+  }
+])
+
+const rules = reactive<{
+  [key: string]: FormItemRule[]
+}>({
+  field1: [required()],
+  field2: [
+    lengthRange({
+      min: 2,
+      max: 5
+    })
+  ],
+  field3: [notSpace()],
+  field4: [notSpecialCharacters()],
+  field5: [
+    {
+      asyncValidator: async (_, val, callback) => {
+        const formData = await getFormData()
+        const { field6 } = formData
+        if (val !== field6) {
+          callback(new Error('两个值不相等'))
+        } else {
+          callback()
+        }
+      }
+    }
+  ]
+})
+</script>
+
+<template>
+  <ContentWrap title="useValidator">
+    <Form :schema="formSchema" :rules="rules" @register="formRegister" />
+  </ContentWrap>
+</template>