Browse Source

feat: 新增个人中心

lujialiang 2 weeks ago
parent
commit
573f1969cc

+ 26 - 0
src/service/api/admin.ts

@@ -123,3 +123,29 @@ export function fetchLoginlogDel(data: object) {
     data
   });
 }
+
+// 查询用户个人信息
+export function fetchUserProfile() {
+  return baseRequest({
+    url: '/api/v1/user/profile',
+    method: 'get'
+  });
+}
+
+// 修改用户
+export function fetchUserEdit(data: object) {
+  return baseRequest({
+    url: '/api/v1/sys-user',
+    method: 'put',
+    data
+  });
+}
+
+// 用户密码重置
+export function fetchUserPwdEdit(data: object) {
+  return baseRequest({
+    url: '/api/v1/user/pwd/set',
+    method: 'put',
+    data
+  });
+}

+ 10 - 0
src/store/modules/route/constantRoutes.json

@@ -13,6 +13,16 @@
         "hideInMenu": true
       }
     },
+    {
+      "name": "user-center",
+      "path": "/user-center",
+      "component": "layout.base$view.user-center",
+      "meta": {
+        "title": "user-center",
+        "i18nKey": "route.user-center",
+        "hideInMenu": true
+      }
+    },
     {
       "name": "403",
       "path": "/403",

+ 13 - 0
src/typings/api.d.ts

@@ -305,5 +305,18 @@ declare namespace Api {
       status: string;
       ipaddr: string;
     }
+
+    interface UserProfile {
+      nickName: string;
+      phone: string;
+      email: string;
+      sex: string;
+    }
+
+    interface UserPwd {
+      oldPassword: string;
+      newPassword: string;
+      confirmPassword: string;
+    }
   }
 }

+ 2 - 0
src/typings/components.d.ts

@@ -45,6 +45,8 @@ declare module 'vue' {
     AStatistic: typeof import('ant-design-vue/es')['Statistic']
     ASwitch: typeof import('ant-design-vue/es')['Switch']
     ATable: typeof import('ant-design-vue/es')['Table']
+    ATabPane: typeof import('ant-design-vue/es')['TabPane']
+    ATabs: typeof import('ant-design-vue/es')['Tabs']
     ATag: typeof import('ant-design-vue/es')['Tag']
     ATextarea: typeof import('ant-design-vue/es')['Textarea']
     ATooltip: typeof import('ant-design-vue/es')['Tooltip']

+ 24 - 2
src/views/user-center/index.vue

@@ -1,7 +1,29 @@
-<script setup lang="ts"></script>
+<script setup lang="ts">
+import { ref } from 'vue';
+import UserProfile from './modules/user-profile.vue';
+import UserPwd from './modules/user-pwd.vue';
+
+const activeKey = ref('1');
+</script>
 
 <template>
-  <LookForward />
+  <div class="min-h-6.25rem flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
+    <ACard
+      title="个人中心"
+      :bordered="false"
+      :body-style="{ flex: 1, overflow: 'hidden' }"
+      class="flex-col-stretch sm:flex-1-hidden card-wrapper"
+    >
+      <ATabs v-model:activeKey="activeKey" type="card">
+        <ATabPane key="1" tab="基本资料">
+          <UserProfile></UserProfile>
+        </ATabPane>
+        <ATabPane key="2" tab="修改密码">
+          <UserPwd></UserPwd>
+        </ATabPane>
+      </ATabs>
+    </ACard>
+  </div>
 </template>
 
 <style scoped></style>

+ 105 - 0
src/views/user-center/modules/user-profile.vue

@@ -0,0 +1,105 @@
+<script setup lang="ts">
+import { reactive, ref } from 'vue';
+import { useAntdForm, useFormRules } from '@/hooks/common/form';
+import { fetchUserEdit, fetchUserProfile } from '@/service/api';
+
+interface Emits {
+  (e: 'submitted'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const { formRef, validate, resetFields } = useAntdForm();
+const { defaultRequiredRule, formRules } = useFormRules();
+
+type Model = Pick<Api.Admin.UserProfile, 'nickName' | 'phone' | 'email' | 'sex'>;
+
+const model: Model = reactive(createDefaultModel());
+
+function createDefaultModel(): Model {
+  return {
+    nickName: '',
+    phone: '',
+    email: '',
+    sex: '0'
+  };
+}
+
+type RuleKey = Extract<keyof Model, 'nickName' | 'phone' | 'email' | 'sex'>;
+
+const rules: Record<RuleKey, App.Global.FormRule> = {
+  nickName: defaultRequiredRule,
+  phone: formRules.phone,
+  email: formRules.email,
+  sex: defaultRequiredRule
+};
+
+async function gethUserProfile() {
+  const { data } = await fetchUserProfile();
+  Object.assign(model, data.user);
+}
+
+async function handleInitModel() {
+  Object.assign(model, createDefaultModel());
+  gethUserProfile();
+}
+
+const sexStatus = ref([
+  { label: '男', value: '0' },
+  { label: '女', value: '1' }
+]);
+
+async function handleSubmit() {
+  await validate();
+
+  const formData = { ...model };
+  fetchUserEdit(formData).then(({ code, msg }) => {
+    if (code === 200) {
+      window.$message?.success(msg);
+      emit('submitted');
+    }
+  });
+}
+
+handleInitModel();
+resetFields();
+</script>
+
+<template>
+  <AForm ref="formRef" :model="model" :rules="rules" :label-col="{ lg: 8, xs: 4 }" label-wrap class="pr-20px">
+    <ARow wrap>
+      <ACol :span="12" :md="12" :xs="24">
+        <AFormItem label="用户昵称" name="nickName">
+          <AInput v-model:value="model.nickName" placeholder="请输入用户昵称" allow-clear />
+        </AFormItem>
+      </ACol>
+      <ACol :span="12" :md="12" :xs="24">
+        <AFormItem label="手机号码" name="phone">
+          <AInput v-model:value="model.phone" placeholder="请输入手机号码" allow-clear />
+        </AFormItem>
+      </ACol>
+      <ACol :span="12" :md="12" :xs="24">
+        <AFormItem label="邮箱" name="email">
+          <AInput v-model:value="model.email" placeholder="请输入邮箱" allow-clear />
+        </AFormItem>
+      </ACol>
+
+      <ACol :span="12" :md="12" :xs="24">
+        <AFormItem label="性别" name="sex">
+          <ARadioGroup v-model:value="model.sex">
+            <ARadio v-for="item in sexStatus" :key="item.value" :value="item.value">
+              {{ item.label }}
+            </ARadio>
+          </ARadioGroup>
+        </AFormItem>
+      </ACol>
+    </ARow>
+  </AForm>
+  <ARow wrap>
+    <ACol :offset="4" :span="12" :md="12" :xs="24">
+      <AButton type="primary" @click="handleSubmit">更新信息</AButton>
+    </ACol>
+  </ARow>
+</template>
+
+<style scoped></style>

+ 82 - 0
src/views/user-center/modules/user-pwd.vue

@@ -0,0 +1,82 @@
+<script setup lang="ts">
+import { reactive } from 'vue';
+import { useAntdForm, useFormRules } from '@/hooks/common/form';
+import { fetchUserPwdEdit } from '@/service/api';
+
+interface Emits {
+  (e: 'submitted'): void;
+}
+
+const emit = defineEmits<Emits>();
+
+const { formRef, validate, resetFields } = useAntdForm();
+const { defaultRequiredRule } = useFormRules();
+
+type Model = Pick<Api.Admin.UserPwd, 'oldPassword' | 'newPassword' | 'confirmPassword'>;
+
+const model: Model = reactive(createDefaultModel());
+
+function createDefaultModel(): Model {
+  return {
+    oldPassword: '',
+    newPassword: '',
+    confirmPassword: ''
+  };
+}
+
+type RuleKey = Extract<keyof Model, 'oldPassword' | 'newPassword' | 'confirmPassword'>;
+
+const rules: Record<RuleKey, App.Global.FormRule> = {
+  oldPassword: defaultRequiredRule,
+  newPassword: defaultRequiredRule,
+  confirmPassword: defaultRequiredRule
+};
+
+async function handleInitModel() {
+  Object.assign(model, createDefaultModel());
+}
+
+async function handleSubmit() {
+  await validate();
+
+  const formData = { ...model };
+  fetchUserPwdEdit(formData).then(({ code, msg }) => {
+    if (code === 200) {
+      window.$message?.success(msg);
+      emit('submitted');
+    }
+  });
+}
+
+handleInitModel();
+resetFields();
+</script>
+
+<template>
+  <AForm ref="formRef" :model="model" :rules="rules" :label-col="{ lg: 8, xs: 4 }" label-wrap class="pr-20px">
+    <ARow wrap>
+      <ACol :span="12" :md="12" :xs="24">
+        <AFormItem label="旧密码" name="oldPassword">
+          <AInput v-model:value="model.oldPassword" placeholder="请输入旧密码" allow-clear />
+        </AFormItem>
+      </ACol>
+      <ACol :span="12" :md="12" :xs="24">
+        <AFormItem label="新密码" name="newPassword">
+          <AInput v-model:value="model.newPassword" placeholder="请输入新密码" allow-clear />
+        </AFormItem>
+      </ACol>
+      <ACol :span="12" :md="12" :xs="24">
+        <AFormItem label="确认密码" name="confirmPassword">
+          <AInput v-model:value="model.confirmPassword" placeholder="请输入确认密码" allow-clear />
+        </AFormItem>
+      </ACol>
+    </ARow>
+  </AForm>
+  <ARow wrap>
+    <ACol :offset="4" :span="12" :md="12" :xs="24">
+      <AButton type="primary" @click="handleSubmit">更新信息</AButton>
+    </ACol>
+  </ARow>
+</template>
+
+<style scoped></style>