Browse Source

wip: coding

kailong321200875 2 years ago
parent
commit
f4c940d95e

+ 4 - 3
.eslintrc.js

@@ -38,8 +38,8 @@ module.exports = defineConfig({
     '@typescript-eslint/ban-types': 'off',
     '@typescript-eslint/no-non-null-assertion': 'off',
     '@typescript-eslint/explicit-module-boundary-types': 'off',
-    '@typescript-eslint/no-unused-vars': 'error',
-    'no-unused-vars': 'error',
+    '@typescript-eslint/no-unused-vars': 'off',
+    'no-unused-vars': 'off',
     'space-before-function-paren': 'off',
 
     'vue/attributes-order': 'off',
@@ -63,6 +63,7 @@ module.exports = defineConfig({
         math: 'always'
       }
     ],
-    'vue/multi-word-component-names': 'off'
+    'vue/multi-word-component-names': 'off',
+    'vue/no-v-html': 'off'
   }
 })

+ 1 - 1
.vscode/settings.json

@@ -10,7 +10,7 @@
   "i18n-ally.localesPaths": ["src/locales"],
   "i18n-ally.keystyle": "nested",
   "i18n-ally.sortKeys": true,
-  "i18n-ally.namespace": true,
+  "i18n-ally.namespace": false,
   "i18n-ally.enabledParsers": ["ts"],
   "i18n-ally.sourceLanguage": "en",
   "i18n-ally.displayLanguage": "zh-CN",

+ 7 - 0
src/api-types/user.ts

@@ -0,0 +1,7 @@
+export interface IUserModel {
+  user_name: string
+  password: string
+  check_password: string
+  is_admin: number
+  code: string | number
+}

+ 12 - 0
src/api/register/index.ts

@@ -0,0 +1,12 @@
+import { useAxios } from '@/hooks/web/useAxios'
+import { IUserModel } from '@/api-types/user'
+
+const request = useAxios()
+
+export const getCodeApi = () => {
+  return request.get<IResponse<string>>({ url: 'user/captcha' })
+}
+
+export const registerApi = (data: Omit<IUserModel, 'is_admin'>) => {
+  return request.post<IResponse<IUserModel>>({ url: 'user/register', data })
+}

+ 2 - 2
src/components/Form/src/Form.vue

@@ -219,7 +219,7 @@ export default defineComponent({
               const { autoSetPlaceholder } = unref(getProps)
 
               return slots[item.field] ? (
-                getSlot(slots, item.field, { item })
+                getSlot(slots, item.field, formModel.value)
               ) : (
                 <Com
                   vModel={formModel.value[item.field]}
@@ -293,7 +293,7 @@ export default defineComponent({
 
 <style lang="less" scoped>
 .@{elNamespace}-form.@{namespace}-form .@{elNamespace}-row {
-  margin-left: 0 !important;
   margin-right: 0 !important;
+  margin-left: 0 !important;
 }
 </style>

+ 1 - 1
src/config/axios/config.ts

@@ -14,7 +14,7 @@ const config: {
    */
   base_url: {
     // 开发环境接口前缀
-    base: '',
+    base: '/api',
 
     // 打包开发环境接口前缀
     dev: '',

+ 1 - 1
src/config/axios/index.ts

@@ -59,7 +59,7 @@ service.interceptors.request.use(
 service.interceptors.response.use(
   (response: AxiosResponse<Recordable>) => {
     if (response.data.code === result_code) {
-      return response.data
+      return response
     } else {
       ElMessage.error(response.data.message)
     }

+ 60 - 0
src/hooks/web/useValidator.ts

@@ -0,0 +1,60 @@
+type Callback = (error?: string | Error | undefined) => void
+
+interface LengthRange {
+  min: number
+  max: number
+  message: string
+}
+
+export const useValidator = () => {
+  const required = (message: string) => {
+    return {
+      required: true,
+      message
+    }
+  }
+
+  const lengthRange = (val: any, callback: Callback, options: LengthRange) => {
+    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()
+    }
+  }
+
+  const notSpecialCharacters = (val: any, callback: Callback, message: string) => {
+    // 密码不能是特殊字符
+    if (/[`~!@#$%^&*()_+<>?:"{},.\/;'[\]]/gi.test(val)) {
+      callback(new Error(message))
+    } else {
+      callback()
+    }
+  }
+
+  // 两个字符串是否想等
+  const isEqual = (val1: string, val2: string, callback: Callback, message: string) => {
+    if (val1 === val2) {
+      callback()
+    } else {
+      callback(new Error(message))
+    }
+  }
+
+  return {
+    required,
+    lengthRange,
+    notSpace,
+    notSpecialCharacters,
+    isEqual
+  }
+}

+ 3 - 1
src/locales/en.ts

@@ -94,7 +94,9 @@ export default {
     hasUser: 'Existing account? Go to login',
     forgetPassword: 'Forget password',
     usernamePlaceholder: 'Please input username',
-    passwordPlaceholder: 'Please input password'
+    passwordPlaceholder: 'Please input password',
+    code: 'Verification code',
+    codePlaceholder: 'Please input verification code'
   },
   router: {
     login: 'Login',

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

@@ -94,7 +94,9 @@ export default {
     hasUser: '已有账号?去登录',
     forgetPassword: '忘记密码',
     usernamePlaceholder: '请输入用户名',
-    passwordPlaceholder: '请输入密码'
+    passwordPlaceholder: '请输入密码',
+    code: '验证码',
+    codePlaceholder: '请输入验证码'
   },
   router: {
     login: '登录',

+ 1 - 1
src/styles/index.less

@@ -1,2 +1,2 @@
 @import './var.css';
-@import 'element-plus/theme-chalk/dark/css-vars.css';
+@import 'element-plus/theme-chalk/dark/css-vars.css';

+ 1 - 1
src/views/Login/Login.vue

@@ -30,7 +30,7 @@ const toLogin = () => {
 <template>
   <div
     :class="prefixCls"
-    class="h-[100%] relative overflow-hidden <xl:bg-v-dark <sm:px-10px <xl:px-10px <md:px-10px"
+    class="h-[100%] relative <xl:bg-v-dark <sm:px-10px <xl:px-10px <md:px-10px"
   >
     <div class="relative h-full flex mx-auto">
       <div

+ 96 - 10
src/views/Login/components/RegisterForm.vue

@@ -1,17 +1,23 @@
 <script setup lang="ts">
 import { Form } from '@/components/Form'
-import { reactive } from 'vue'
+import { reactive, ref, unref } from 'vue'
 import { useI18n } from '@/hooks/web/useI18n'
-import { required } from '@/utils/formRules'
 import { useForm } from '@/hooks/web/useForm'
-import { ElButton } from 'element-plus'
+import { ElButton, ElInput, FormRules, ElMessage } from 'element-plus'
+import { getCodeApi, registerApi } from '@/api/register'
+import { useValidator } from '@/hooks/web/useValidator'
+import { IUserModel } from '@/api-types/user'
 
 const emit = defineEmits(['to-login'])
 
-const { register } = useForm()
+const { register, methods, elFormRef } = useForm()
+
+const { getFormData } = methods
 
 const { t } = useI18n()
 
+const { required, lengthRange, notSpace, notSpecialCharacters, isEqual } = useValidator()
+
 const schema = reactive<FormSchema[]>([
   {
     field: 'title',
@@ -34,7 +40,7 @@ const schema = reactive<FormSchema[]>([
   {
     field: 'password',
     label: t('login.password'),
-    value: 'admin',
+    value: '',
     component: 'InputPassword',
     colProps: {
       span: 24
@@ -43,13 +49,14 @@ const schema = reactive<FormSchema[]>([
       style: {
         width: '100%'
       },
+      strength: true,
       placeholder: t('login.passwordPlaceholder')
     }
   },
   {
     field: 'check_password',
     label: t('login.checkPassword'),
-    value: 'admin',
+    value: '',
     component: 'InputPassword',
     colProps: {
       span: 24
@@ -58,9 +65,17 @@ const schema = reactive<FormSchema[]>([
       style: {
         width: '100%'
       },
+      strength: true,
       placeholder: t('login.passwordPlaceholder')
     }
   },
+  {
+    field: 'code',
+    label: t('login.code'),
+    colProps: {
+      span: 24
+    }
+  },
   {
     field: 'register',
     colProps: {
@@ -69,14 +84,78 @@ const schema = reactive<FormSchema[]>([
   }
 ])
 
-const rules = {
-  username: [required],
-  password: [required]
+const rules: FormRules = {
+  user_name: [
+    required('用户名不能为空'),
+    {
+      validator: (_, value, callback) =>
+        lengthRange(value, callback, { min: 2, max: 10, message: '用户名长度必须在2-10之间' })
+    },
+    {
+      validator: (_, value, callback) => notSpace(value, callback, '用户名不能有空格')
+    }
+  ],
+  password: [
+    required('密码不能为空'),
+    {
+      validator: (_, value, callback) =>
+        lengthRange(value, callback, { min: 6, max: 20, message: '密码长度必须在6-20之间' })
+    },
+    {
+      validator: (_, value, callback) => notSpecialCharacters(value, callback, '密码不能是特殊字符')
+    }
+  ],
+  check_password: [
+    required('确认密码不能为空'),
+    {
+      validator: (_, value, callback) =>
+        lengthRange(value, callback, { min: 6, max: 20, message: '确认密码长度必须在6-20之间' })
+    },
+    {
+      validator: (_, value, callback) =>
+        notSpecialCharacters(value, callback, '确认密码不能是特殊字符')
+    },
+    {
+      validator: async (_, value, callback) => {
+        const formData = await getFormData<Omit<IUserModel, 'is_admin'>>()
+        return isEqual(value, formData.password, callback, '两次密码不一致')
+      }
+    }
+  ],
+  code: [required('验证码不能为空')]
 }
 
 const toLogin = () => {
   emit('to-login')
 }
+
+const codeUrl = ref('')
+const getCode = async () => {
+  const { data } = await getCodeApi()
+  codeUrl.value = data.result
+}
+getCode()
+
+const loading = ref(false)
+
+const loginRegister = async () => {
+  const formRef = unref(elFormRef)
+  formRef?.validate(async (valid) => {
+    if (valid) {
+      try {
+        loading.value = true
+        const formData = await getFormData<Omit<IUserModel, 'is_admin'>>()
+        const { data } = await registerApi(formData)
+        if (data) {
+          ElMessage.success('注册成功')
+          toLogin()
+        }
+      } finally {
+        loading.value = false
+      }
+    }
+  })
+}
 </script>
 
 <template>
@@ -93,9 +172,16 @@ const toLogin = () => {
       <h2 class="text-2xl font-bold text-center w-[100%]">{{ t('login.register') }}</h2>
     </template>
 
+    <template #code="form">
+      <div class="w-[100%] flex">
+        <ElInput v-model="form['code']" :placeholder="t('login.codePlaceholder')" class="flex-2" />
+        <div v-html="codeUrl" class="h-38px flex-1 cursor-pointer" @click="getCode"></div>
+      </div>
+    </template>
+
     <template #register>
       <div class="w-[100%]">
-        <ElButton type="primary" class="w-[100%]">
+        <ElButton type="primary" class="w-[100%]" :loading="loading" @click="loginRegister">
           {{ t('login.register') }}
         </ElButton>
       </div>

+ 5 - 0
types/global.d.ts

@@ -33,3 +33,8 @@ declare type AxiosConfig = {
   headersType?: string
   responseType?: AxiosResponseType
 }
+
+declare interface IResponse<T = any> {
+  code: string
+  result: T extends any ? T : T & any
+}

+ 5 - 5
vite.config.ts

@@ -119,11 +119,11 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
       port: 4000,
       proxy: {
         // 选项写法
-        // '/api': {
-        //   target: 'http://localhost:3000',
-        //   changeOrigin: true,
-        //   rewrite: path => path.replace(/^\/api/, '')
-        // }
+        '/api': {
+          target: 'http://127.0.0.1:8000',
+          changeOrigin: true,
+          rewrite: path => path.replace(/^\/api/, '')
+        }
       },
       hmr: {
         overlay: false