Bladeren bron

feat: 修改密码完成

xyh 2 jaren geleden
bovenliggende
commit
2618470516

+ 10 - 2
src/App.vue

@@ -10,8 +10,9 @@ import {
   type GlobalThemeOverrides,
   NMessageProvider,
   NDialogProvider,
+  useMessage,
 } from 'naive-ui';
-import {computed} from 'vue';
+import {computed, defineComponent, h} from 'vue';
 import {lightVariable} from '@utils';
 
 defineOptions({
@@ -63,13 +64,20 @@ const themeConfig: GlobalThemeOverrides = {
   },
 };
 
+const Wrapper = defineComponent({
+  setup() {
+    (window as any).$message = useMessage();
+    return () => h(RouterView);
+  },
+});
+
 </script>
 
 <template>
   <NConfigProvider v-bind="uiLocale" :themeOverrides="themeConfig">
     <NMessageProvider>
       <NDialogProvider>
-        <RouterView />
+        <Wrapper />
       </NDialogProvider>
     </NMessageProvider>
   </NConfigProvider>

+ 2 - 3
src/apis/network.ts

@@ -47,7 +47,6 @@ export async function request<T, R extends BaseResultContent<any>>(options: {
 
   let res: BaseResultContent<any>;
   const useData = useBody ? useBody : method !== 'GET' && method !== 'DELETE';
-  const message = useMessage();
 
   try {
     const result = await http.request<R>({
@@ -61,12 +60,12 @@ export async function request<T, R extends BaseResultContent<any>>(options: {
     res = result.data;
 
     if (res.msg !== '200' && !skipError && !exportReg.test(url))
-      message.error(res.errMsg);
+      (window as any).$message.error(res.errMsg);
   } catch (error: any) {
     res = {msg: '510', errMsg: 'NETWORK_ERROR'};
 
     if (!skipError && error.code !== 'ERR_CANCELED' && !exportReg.test(url))
-      message.error(res.errMsg);
+      (window as any).$message.error(res.errMsg);
   }
 
   return res;

+ 15 - 1
src/apis/user.ts

@@ -1,4 +1,9 @@
-import {BaseResult, UserLoginData, UserLoginParams} from '@models';
+import {
+  BaseResult,
+  EditUserPasswordParams,
+  UserLoginData,
+  UserLoginParams,
+} from '@models';
 import {request} from './network';
 
 const BASE_URL = '/user';
@@ -24,3 +29,12 @@ export function getUserCompanyName(
     signal,
   });
 }
+
+/** 修改密码 */
+export function editUserPassword(data: EditUserPasswordParams): BaseResult {
+  return request({
+    method: 'POST',
+    url: `${BASE_URL}/updatePassword`,
+    data,
+  });
+}

+ 8 - 2
src/components/modal-field/input/index.tsx

@@ -1,7 +1,8 @@
 import '../index.css';
 import {defineComponent} from 'vue';
-import {NInput} from 'naive-ui';
+import {NInput, inputProps} from 'naive-ui';
 import {useField} from 'vee-validate';
+import {useI18n} from 'vue-i18n';
 
 export default defineComponent({
   name: 'LDModalInput',
@@ -15,9 +16,12 @@ export default defineComponent({
       required: true,
     },
     optional: Boolean,
+    type: inputProps.type,
+    showPasswordOn: inputProps.showPasswordOn,
   },
   setup(props) {
     const {value, setValue, errorMessage} = useField<string>(props.name);
+    const {t} = useI18n();
 
     return () => (
       <div class="ld-modal-field-wrapper">
@@ -27,9 +31,11 @@ export default defineComponent({
             bordered={false}
             value={value.value}
             onUpdateValue={setValue}
+            type={props.type}
+            showPasswordOn={props.showPasswordOn}
           />
         </div>
-        <p class="ld-modal-filed-error">{errorMessage.value}</p>
+        <p class="ld-modal-filed-error" >{errorMessage.value && t(errorMessage.value)}</p>
       </div>
     );
   },

+ 18 - 3
src/components/modal/normal/index.tsx

@@ -1,5 +1,11 @@
 import './index.css';
-import {PropType, defineComponent} from 'vue';
+import {
+  CSSProperties,
+  PropType,
+  StyleValue,
+  computed,
+  defineComponent,
+} from 'vue';
 import {VueFinalModal} from 'vue-final-modal';
 import {CloseOne} from '@icon-park/vue-next';
 import addIcon from '@assets/images/modal/add.svg';
@@ -23,6 +29,8 @@ export default defineComponent(
       icon: String,
       onSubmit: Function as PropType<(e: Event) => void>,
       isLoading: Boolean,
+      width: Number,
+      height: Number,
     },
     emits: {
       'update:modelValue': Boolean,
@@ -37,6 +45,13 @@ export default defineComponent(
         emit('update:modelValue', false);
       }
 
+      const style = computed<StyleValue>(function() {
+        return {
+          height: (props.height ?? '600px') + 'px',
+          width: (props.width ?? '680px') + 'px',
+        };
+      });
+
       return function() {
         return (
           <VueFinalModal
@@ -44,8 +59,8 @@ export default defineComponent(
             displayDirective="show"
             modelValue={props.modelValue}
           >
-            <div class="ld-normal-wrapper ">
-              <div class="ld-normal-modal">
+            <div class="ld-normal-wrapper">
+              <div class="ld-normal-modal" style={style.value}>
                 <div onClick={onClose} class="ld-normal-modal-close">
                   <CloseOne />
                 </div>

+ 17 - 0
src/locales/home.ts

@@ -13,6 +13,17 @@ export default {
       loginBtnOK: '确定',
       loginBtnCancel: '取消',
     },
+    editpasswordError: {
+      oldPassword: '请输入旧密码',
+      newPassword: '请输入新密码',
+      repeatPassword: '请输入再次输入新密码',
+      validatePassword: '两次输入密码不一致',
+    },
+    editPassword: {
+      title: '修改密码',
+      labels: ['旧密码', '新密码', '再次输入'],
+      success: '密码修改成功,请重新登录',
+    },
   },
   ko: {
     menuBtn: {supplier: '供应商管理系统'},
@@ -28,5 +39,11 @@ export default {
       loginBtnOK: '确定',
       loginBtnCancel: '取消',
     },
+    editpasswordError: {
+      oldPassword: '请输入旧密码',
+      newPassword: '请输入新密码',
+      repeatPassword: '请输入再次输入新密码',
+      validatePassword: '两次输入密码不一致',
+    },
   },
 };

+ 10 - 0
src/models/request/user.ts

@@ -5,3 +5,13 @@ export type UserLoginParams = {
   /** 用户密码 */
   password: string;
 };
+
+/** 修改用户密码 */
+export type EditUserPasswordParams = {
+  /** 用户id */
+  id: string;
+  /** 旧密码 */
+  password: string;
+  /** 新密码 */
+  newPassword: string;
+};

+ 10 - 2
src/pages/home/user/hooks.ts

@@ -6,6 +6,7 @@ import {useI18n} from 'vue-i18n';
 import {lightVariable} from '@utils';
 import {useRouter} from 'vue-router';
 import {LOGIN_PATH} from '@routes';
+import {useToggle} from '@vueuse/core';
 
 export const dropdownOptions: DropdownOption[] = [
   {label: '修改密码', key: 'editPassword', icon: () => h(EditOne)},
@@ -34,11 +35,18 @@ export function useDownSelect() {
     });
   }
 
-  return function onSelect(key: string) {
+  const [visible, setVisible] = useToggle();
+
+  function onSelect(key: string) {
     switch (key) {
       case 'logout':
         logout();
         break;
+      case 'editPassword':
+        setVisible(true);
+        break;
     }
-  };
+  }
+
+  return [visible, {setVisible, onSelect}] as const;
 }

+ 4 - 1
src/pages/home/user/index.vue

@@ -6,12 +6,13 @@ import {dropdownOptions, useDownSelect} from './hooks';
 import {useUserStore} from '@stores';
 import {toRefs} from 'vue';
 import face from '@assets/images/face.svg';
+import Modal from './modal/index.vue';
 
 defineOptions({name: 'HomeUser'});
 const [isOpen, setOpen] = useToggle();
 const userStore = useUserStore();
 const {realName} = toRefs(userStore.user);
-const onSelect = useDownSelect();
+const [visible, {onSelect}] = useDownSelect();
 </script>
 
 <template>
@@ -35,6 +36,8 @@ const onSelect = useDownSelect();
       />
     </section>
   </NDropdown>
+
+  <Modal v-model:visible="visible" />
 </template>
 
 <style scoped>

+ 70 - 0
src/pages/home/user/modal/hooks.ts

@@ -0,0 +1,70 @@
+import {editUserPassword} from '@apis';
+import {LOGIN_PATH} from '@routes';
+import {storeToRefs, useUserStore} from '@stores';
+import {useMutation} from '@tanstack/vue-query';
+import {formatValidateError} from '@utils';
+import {toTypedSchema} from '@vee-validate/zod';
+import {useMessage} from 'naive-ui';
+import {useForm} from 'vee-validate';
+import {Ref, watchEffect} from 'vue';
+import {useI18n} from 'vue-i18n';
+import {useRouter} from 'vue-router';
+import {object, string} from 'zod';
+
+type FromState = {
+  oldPassword: string;
+  newPassword: string;
+  repeatPassword: string;
+};
+
+export function useFormState(visible: Ref<boolean>) {
+  const {handleSubmit, handleReset, setFieldError} = useForm<FromState>({
+    initialValues: {
+      oldPassword: '',
+      newPassword: '',
+      repeatPassword: '',
+    },
+    validationSchema: toTypedSchema(object({
+      oldPassword: string(formatValidateError('home.editpasswordError.oldPassword'))
+        .min(1, 'home.editpasswordError.oldPassword'),
+      newPassword: string(formatValidateError('home.editpasswordError.newPassword'))
+        .min(1, 'home.editpasswordError.newPassword'),
+      repeatPassword: string(formatValidateError('home.editpasswordError.repeatPassword'))
+        .min(1, 'home.editpasswordError.repeatPassword'),
+    })),
+  });
+
+  const userStore = useUserStore();
+  const {user} = storeToRefs(userStore);
+  const message = useMessage();
+  const {t} = useI18n();
+  const {push} = useRouter();
+
+  const {mutate, isLoading} = useMutation({
+    mutationFn: editUserPassword,
+    onSuccess({msg}) {
+      if (msg === '200') {
+        message.success(t('home.editPassword.success'));
+        userStore.logout();
+        push(LOGIN_PATH);
+      }
+    },
+  });
+
+  const onSubmit = handleSubmit(function({
+    oldPassword,
+    newPassword,
+    repeatPassword,
+  }) {
+    if (newPassword !== repeatPassword)
+      return setFieldError('repeatPassword', 'home.editpasswordError.validatePassword');
+
+    mutate({id: user.value.id, password: oldPassword, newPassword});
+  });
+
+  watchEffect(function() {
+    visible.value && handleReset();
+  });
+
+  return [isLoading, onSubmit] as const;
+}

+ 48 - 0
src/pages/home/user/modal/index.vue

@@ -0,0 +1,48 @@
+<script setup lang='ts'>
+import {LDNormalModal, LDModalInput} from '@components';
+import {useVModel} from '@vueuse/core';
+import {useFormState} from './hooks';
+import {toRefs} from 'vue';
+import {useI18n} from 'vue-i18n';
+
+defineOptions({name: 'HomeEditPasswordModal'});
+
+const props = defineProps<{visible: boolean}>();
+const {visible: visibleProps} = toRefs(props);
+const emits = defineEmits<{(event: 'update:visible', value: boolean): void}>();
+const visible = useVModel(props, 'visible', emits);
+const [isLoading, onSubmit] = useFormState(visibleProps);
+
+const {t} = useI18n();
+</script>
+
+<template>
+  <LDNormalModal
+    v-model="visible"
+    @submit="onSubmit"
+    :title="t('home.editPassword.title')"
+    :width="540"
+    :height="480"
+    :isLoading="isLoading"
+  >
+    <LDModalInput
+      :label="t('home.editPassword.labels[0]')"
+      name="oldPassword"
+      type="password"
+      showPasswordOn="click"
+    />
+    <LDModalInput
+      :label="t('home.editPassword.labels[1]')"
+      name="newPassword"
+      type="password"
+      showPasswordOn="click"
+    />
+    <LDModalInput
+      :label="t('home.editPassword.labels[2]')"
+      name="repeatPassword"
+      type="password"
+      showPasswordOn="click"
+    />
+  </LDNormalModal>
+</template>
+