فهرست منبع

fix/feat: 完善功能,修复bug

1. 新增修改用户密码的功能
2. 修复注销操作的bug: 未清除pinia状态中的数据
zhupei 1 سال پیش
والد
کامیت
81ec5e4140

+ 14 - 23
public/i18n/lang-en.json

@@ -24,7 +24,6 @@
     "角色管理": "Role Management",
     "机构管理": "Organization Management",
     "资源管理": "Resource Management",
-
     "search": "Search",
     "language": "Language",
     "theme": "Theme",
@@ -67,7 +66,6 @@
     "delete": "Delete",
     "online": "Online",
     "offline": "Offline",
-
     "delete_success": "Deletion Successful",
     "delete_fail": "Deletion Failed",
     "create_success": "Creation Successful",
@@ -83,7 +81,6 @@
       "lim_rule": "Model cannot be empty",
       "kb_rule": "Knowledge base cannot be empty"
     },
-
     "createUser": "Create User",
     "addedModel": "Added Models",
     "unaddModel": "Models to be Added",
@@ -268,7 +265,6 @@
     "no_permission_sorry": "Sorry, you do not have permission to access this page",
     "no_permission_route": "You do not have permission to access this page. Please contact the administrator",
     "btn_back": "Back",
-
     "query": "Query",
     "reset": "Reset",
     "delete": "Delete",
@@ -300,7 +296,6 @@
       "permissionEdit": "Permission Configuration",
       "deptEdit": "Department Configuration",
       "confirmDelete": "Are you sure you want to delete this record?",
-
       "roleName": "Role Name",
       "remark": "Remark",
       "roleNameRequired": "Role Name is required",
@@ -313,7 +308,6 @@
       "serial_number": "Serial Number",
       "status": "Status",
       "action": "Action",
-
       "search_placeholder": "Enter text"
     },
     "account": {
@@ -394,13 +388,10 @@
       "description": "Description",
       "status": "Status",
       "action": "Action",
-
       "member_title": "Permission Configuration",
       "search_placeholder": "Please enter username to search",
-
       "optional_members": "Available Members",
       "selected_members": "Selected Members",
-
       "permission_title": "Permission Configuration",
       "knowledge_feature": "Knowledge Base Features:",
       "agent_feature": "Agent Features:",
@@ -416,7 +407,6 @@
     "chat_new": "New Session",
     "chat_history": "History",
     "chat_smart": "SmartAI+",
-
     "search_history": "Search history",
     "history_today": "Today",
     "history_month": "This month",
@@ -424,19 +414,15 @@
     "history_loadmore": "Load more",
     "history_nomore": "No more",
     "history_empty": "Empty",
-
     "hello": "Welcome",
     "nice_day": "Let's spend a wonderful day together!",
-
     "attention": "The content is generated by an AI big model, please carefully identify it",
-
     "file_upload_message": "Upload",
     "input_message": "Enter your question",
     "input_placholder_v0": "Enter text content",
     "input_placholder_v1": "Enter “/” to use Smart intelligent dialogue to solve your problem",
     "input_placholder": "",
     "input_confirm": "Confirm",
-
     "back": "Back",
     "source": "Source",
     "view_links": "View all associations",
@@ -445,29 +431,22 @@
     "related_docs": "Documents",
     "doc_total": "There are a total of {total}",
     "fragments": "Retrieve fragments",
-
     "btn_ok": "Confirm",
     "btn_cancel": "Cancel",
     "btn_stop": "Stop",
-
     "are_you_sure": "Are you sure to delete?",
     "btn_yes": "Yes",
     "btn_no": "No",
     "del_success": "Success",
-
     "online_search": "Online search",
-
     "tag_all": "All",
-
     "copy": "Copy",
-
     "please_input_text": "Please enter text",
     "please_upload_file": "Please upload the file"
   },
   "wendangchujuan": {
     "agent_title": "Document paper",
     "dynamic_title": "Parameter configuration",
-
     "dificulty": "Difficulty",
     "should_generate_paper": "Whether to generate a test paper",
     "count_q_single_choice": "Number of Single choice question",
@@ -477,7 +456,6 @@
     "count_q_ask": "Number of short answer questions",
     "count_q_case": "Number of Case Questions",
     "keywords": "Topic direction or keywords",
-
     "optional": "(Optional)",
     "type_easy": "Easy",
     "type_difficulty": "Difficult",
@@ -488,5 +466,18 @@
     "confirm_delete": "Are you sure delete?",
     "doc_error": "Document loading error",
     "click_2_download": "Click to download the file"
+  },
+  "settings": {
+    "global": "General configuration",
+    "password": "Change Password",
+    "current_password": "Current Password",
+    "placeholder_cur_password": "Please enter the original password",
+    "new_password": "New Password",
+    "placeholder_new1_password": "Please enter a new password",
+    "confirm_password": "Confirm password",
+    "placeholder_new2_password": "Please confirm the new password",
+    "btn_cancel": "Cancel",
+    "btn_submit": "Confirm",
+    "warning_not_match": "The new password entered twice does not match. Please confirm"
   }
-}
+}

+ 14 - 21
public/i18n/lang-zh.json

@@ -24,7 +24,6 @@
     "角色管理": "角色管理",
     "机构管理": "机构管理",
     "资源管理": "资源管理",
-
     "search": "搜索",
     "language": "语言",
     "theme": "主题",
@@ -82,7 +81,6 @@
       "lim_rule": "模型不能为空",
       "kb_rule": "知识库不能为空"
     },
-
     "createUser": "创建用户",
     "addedModel": "添加了的模型",
     "unaddModel": "待添加的模型",
@@ -261,7 +259,6 @@
     "no_permission_sorry": "抱歉,您无权限访问此页面",
     "no_permission_route": "您没有权限访问此页面,请联系管理员",
     "btn_back": "返回",
-
     "query": "查询",
     "reset": "重置",
     "delete": "删除",
@@ -309,7 +306,6 @@
       "action": "操作",
       "search_placeholder": "请输入关键字查询"
     },
-
     "account": {
       "new_user": "新建用户",
       "reset_password": "重置密码",
@@ -348,7 +344,6 @@
       "address": "机构地址",
       "save": "保存",
       "reset": "重置",
-
       "org_name_required": "机构名称必填",
       "leader_required": "联系人必填",
       "phone_required": "联系电话必填"
@@ -356,7 +351,6 @@
     "resource": {
       "tree_title": "资源",
       "details": "详情",
-
       "parent_org": "上级资源",
       "status": "资源状态",
       "resource_name": "资源名称",
@@ -370,7 +364,6 @@
       "description": "提示词",
       "perms": "资源控制权限字符",
       "component": "组件地址",
-
       "resource_name_required": "资源名称必填",
       "address_required": "资源地址必填",
       "menu_type": "资源类型",
@@ -401,7 +394,6 @@
       "search_placeholder": "请输入用户名搜索",
       "optional_members": "可选成员",
       "selected_members": "已选成员",
-
       "permission_title": "权限配置",
       "knowledge_feature": "知识库功能:",
       "agent_feature": "智能体功能:",
@@ -414,7 +406,6 @@
     "chat_new": "开启新会话",
     "chat_history": "历史会话",
     "chat_smart": "SmartAI+",
-
     "search_history": "搜索历史会话",
     "history_today": "今天",
     "history_month": "本月",
@@ -424,16 +415,13 @@
     "history_empty": "暂无数据",
     "hello": "你好",
     "nice_day": "让我们一起度过美好的一天!",
-
     "attention": "内容由 AI 大模型生成,请仔细甄别",
-
     "file_upload_message": "文件上传",
     "input_message": "输入您的问题",
     "input_placholder_v0": "输入文本内容",
     "input_placholder_v1": "输入“/”使用Smart智能对话,解决你的问题",
     "input_placholder": "",
     "input_confirm": "发送",
-
     "back": "返回",
     "source": "来源",
     "view_links": "查看全部关联",
@@ -442,29 +430,22 @@
     "related_docs": "以下是根据您的提问搜索到的相关文档",
     "doc_total": "共{total}条",
     "fragments": "检索片段",
-
     "btn_ok": "确定",
     "btn_cancel": "取消",
     "btn_stop": "停止生成",
-
     "are_you_sure": "确定删除么?",
     "btn_yes": "是",
     "btn_no": "否",
     "del_success": "删除成功",
-
     "online_search": "联网搜索",
-
     "tag_all": "全部",
-
     "copy": "复制",
-
     "please_input_text": "请输入文字",
     "please_upload_file": "请上传文件"
   },
   "wendangchujuan": {
     "agent_title": "文档出卷",
     "dynamic_title": "参数配置",
-
     "dificulty": "难度",
     "should_generate_paper": "是否生成试卷",
     "count_q_single_choice": "单选题数量",
@@ -474,7 +455,6 @@
     "count_q_ask": "简答题数量",
     "count_q_case": "案例题数量",
     "keywords": "出题方向或关键字",
-
     "optional": "(可选)",
     "type_easy": "简单",
     "type_difficulty": "困难",
@@ -485,5 +465,18 @@
     "confirm_delete": "确定删除吗?",
     "doc_error": "文档加载出错",
     "click_2_download": "点击下载文件"
+  },
+  "settings": {
+    "global": "通用配置",
+    "password": "修改密码",
+    "current_password": "当前密码",
+    "placeholder_cur_password": "请输入原密码",
+    "new_password": "新密码",
+    "placeholder_new1_password": "请输入新密码",
+    "confirm_password": "确认密码",
+    "placeholder_new2_password": "请确认新密码",
+    "btn_cancel": "取消",
+    "btn_submit": "保存",
+    "warning_not_match": "新密码两次输入不一致,请确认"
   }
-}
+}

+ 4 - 4
src/3rd-libs/arco-vue-install-icon.js

@@ -324,8 +324,8 @@ export default function installArcoVue(app) {
   // app.use(IconExpand);
   // app.use(IconLeftCircle);
   app.use(IconLeft);
-  // app.use(IconMenuFold);
-  // app.use(IconMenuUnfold);
+  app.use(IconMenuFold);
+  app.use(IconMenuUnfold);
   // app.use(IconRightCircle);
   // app.use(IconRight);
   // app.use(IconRotateLeft);
@@ -508,8 +508,8 @@ export default function installArcoVue(app) {
   // app.use(IconBook);
   app.use(IconBookmark);
   // app.use(IconBranch);
-  // app.use(IconBug);
-  // app.use(IconBulb);
+  app.use(IconBug);
+  app.use(IconBulb);
   // app.use(IconCalendarClock);
   app.use(IconCalendar);
   // app.use(IconCamera);

+ 18 - 0
src/apis/login.js

@@ -45,3 +45,21 @@ export function tokenExchange() {
     method: 'get'
   });
 }
+
+/**
+ * 修改用户密码
+ * @see https://app.apifox.com/link/project/5404857/apis/api-244151743
+ * @param {String} pre - 旧密码(需要加密)
+ * @param {String} cur - 新密码(需要加密)
+ * @returns
+ */
+export function changeUserPassword(pre, cur) {
+  return request({
+    url: '/api/user/change_password',
+    method: 'put',
+    data: JSON.stringify({
+      oldPassword: pre,
+      newPassword: cur
+    })
+  });
+}

+ 12 - 1
src/base/store/use-current-user.js

@@ -19,7 +19,7 @@ export const useCurrentUserStore = defineStore('current_user', {
   },
   state: () => {
     return {
-      name: 'Jack',
+      name: '',
       token_type: '',
       token: kuky_Authorization.get(),
       username: '',
@@ -31,6 +31,17 @@ export const useCurrentUserStore = defineStore('current_user', {
     };
   },
   actions: {
+    logout() {
+      this.name = '';
+      this.token_type = '';
+      this.token = '';
+      this.username = '';
+      this.nickname = '';
+      this.dept = [];
+      this.roles = [];
+      this.permissions = [];
+      this.user = {};
+    },
     updateName(name) {
       this.name = name;
     },

+ 55 - 8
src/router/index.js

@@ -9,9 +9,29 @@ const router = createRouter({
   scrollBehavior: () => ({ left: 0, top: 0 })
 });
 
+/**
+ * 路由验证有三个级别:
+ * 级别1:任何情况都可进入
+ * 级别2:在已登录的情况下可以进入
+ * 级别3:在已登录,并且有权限的情况下可以进入
+ */
+
 /** 免验证白名单 */
 const whiteList = ['/login', '/reg', '/denied'];
 
+/**
+ * 需要登录的链接
+ * 这些路由无需权限判断,但是需要用户登录
+ */
+const require_login_paths = [
+  '/settings',
+  // '/settings/profile',
+  '/settings/global',
+  '/settings/password'
+  // '/settings/phone',
+  // '/settings/email'
+];
+
 /** 是否已经生成过路由表 */
 let hasRouteFlag = false;
 export const resetHasRouteFlag = () => {
@@ -24,33 +44,60 @@ router.beforeEach(async (to, from, next) => {
   const isAuthorized = kuky_Authorization.get(); // 获取用户授权状态
   const isLoginPage = to.path === '/login'; // 判断目标路径是否为登录页
   const isInWhiteList = whiteList.includes(to.path); // 检查目标路径是否在白名单中
+  const isRequireLoginPath = require_login_paths.includes(to.path); // 检查目标路径是否在白名单中
 
   // 用户已授权的情况
   if (isAuthorized) {
+    // 用户已登录
+    // 判断动态路由表是否已经生成,若未生成,则生成动态路由表
     if (!hasRouteFlag) {
       const { accessRoutes, accessFlatRoutes, firstAccessibleRoute } = await initializeRoutes();
+
       accessRoutes.forEach((route) => {
         if (route.path && route.component) {
           router.addRoute(route); // 动态添加可访问路由表
         }
       });
+
       flatRoutes = accessFlatRoutes;
       hasRouteFlag = true;
-      next({ path: firstAccessibleRoute.path, replace: true });
-    } else if (isLoginPage) {
+      // 路由表已经生成
+      // next({ path: firstAccessibleRoute.path, replace: true });
+    }
+
+    /**
+     * 判断当前目标路由位置
+     * 如果是登录页面,则重定向到首页
+     * 如果是需要登录的页面,则直接进入
+     * 其他情况1: 按断是否有权限进入,如果没有权限,则重定向到无权限页面
+     */
+
+    const has_permission = flatRoutes.some((route) => route.path == to.path);
+
+    if (isLoginPage) {
+      // 登录页面:重定向到首页
       next('/');
-    } else if (!flatRoutes.some((route) => route.path == to.path) && !isInWhiteList) {
+    } else if (isRequireLoginPath) {
+      // 需要登录的页面:直接进入
+      next();
+    } else if (!has_permission && !isInWhiteList) {
       next('/denied'); // 如果用户无访问权限且不在白名单中,重定向到无权限页面
     } else {
       next(); // 其他情况,正常访问
     }
   } else {
-    // 用户未授权的情况
-    if (isInWhiteList) {
-      // 如果目标路径在免登录白名单中
-      next(); // 允许进入
+    if (isRequireLoginPath) {
+      // 需要用户登录
+      next('/login');
     } else {
-      next('/login'); // 否则重定向到登录页面
+      // 无需登录的页面
+      // 用户未授权的情况
+      if (isInWhiteList) {
+        // 如果目标路径在免登录白名单中
+        next(); // 允许进入
+      } else {
+        next('/login'); // 否则重定向到登录页面
+      }
     }
   }
 });

+ 33 - 0
src/router/route.js

@@ -11,6 +11,39 @@ export const systemRoutes = [
   { path: '/login', component: () => import('../views/Login.vue') },
   { path: '/reg', component: () => import('../views/Register.vue') },
 
+  /**
+   * 设置
+   */
+  {
+    path: '/settings',
+    component: () => import('../views/Settings.vue'),
+    children: [
+      {
+        path: 'profile',
+        component: () => import('../views/SettingsProfile.vue')
+      },
+      // 修改密码
+      {
+        // path: '/settings/password',
+        path: 'password',
+        component: () => import('../views/SettingsPassword.vue')
+      },
+      {
+        path: 'phone',
+        component: () => import('../views/SettingsPhone.vue')
+      },
+      {
+        path: 'email',
+        component: () => import('../views/SettingsEmail.vue')
+      },
+      // 通用配置
+      {
+        path: 'global',
+        component: () => import('../views/SettingsGlobal.vue')
+      }
+    ]
+  },
+
   // 错误页面
   { path: '/denied', component: () => import('../views/Denied.vue') }
 

+ 91 - 0
src/views/Settings.vue

@@ -0,0 +1,91 @@
+<template>
+  <LayoutExperience>
+    <ContentContainer :containerStyle="content_style">
+      <div class="settings-container">
+        <div class="settings-aside-menu-container">
+          <a-menu :style="{ width: '200px', borderRadius: '4px' }" @menu-item-click="handleMenuClick">
+            <!-- <a-menu-item key="/settings/profile">修改资料</a-menu-item> -->
+            <a-menu-item key="/settings/global">
+              <template #icon>
+                <icon-file />
+              </template>
+
+              {{ $t('settings.global') }}
+            </a-menu-item>
+
+            <a-menu-item key="/settings/password">
+              <template #icon>
+                <icon-lock />
+              </template>
+
+              {{ $t('settings.password') }}
+            </a-menu-item>
+
+            <!-- <a-menu-item key="/settings/phone">绑定电话号码</a-menu-item> -->
+            <!-- <a-menu-item key="/settings/email">绑定邮箱</a-menu-item> -->
+            <!-- <a-menu-item key="1_0">logo设置</a-menu-item> -->
+            <!-- <a-menu-item key="1_1">主题设置</a-menu-item> -->
+            <!-- <a-menu-item key="1_2">多语言设置</a-menu-item> -->
+          </a-menu>
+        </div>
+
+        <div class="settings-main-content-container">
+          <router-view></router-view>
+        </div>
+      </div>
+    </ContentContainer>
+  </LayoutExperience>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useRouter } from 'vue-router';
+
+import LayoutExperience from './layout/LayoutExperience.vue';
+import ContentContainer from './layout/ContentContainer.vue';
+
+import bg_image from '../assets/chat/board-bg.png';
+
+const router = useRouter();
+
+const content_style = {
+  // backgroundImage: `url(${bg_image})`,
+  backgroundRepeat: 'no-repeat',
+  backgroundSize: 'contain'
+};
+
+const collapsed = ref(false);
+
+const handleMenuClick = (key) => {
+  router.push({
+    path: key
+  });
+};
+</script>
+
+<style scoped>
+.settings-container {
+  display: flex;
+  justify-content: flex-start;
+}
+
+.settings-aside-menu-container {
+  box-sizing: border-box;
+
+  background-color: var(--color-menu-light-bg);
+
+  padding: 10px;
+  height: calc(100vh - 90px);
+}
+
+.settings-main-content-container {
+  box-sizing: border-box;
+
+  background-color: var(--color-fill-1);
+
+  flex-grow: 1;
+
+  padding: 15px;
+  height: calc(100vh - 90px);
+}
+</style>

+ 10 - 0
src/views/SettingsEmail.vue

@@ -0,0 +1,10 @@
+<template>
+  <div>绑定邮箱</div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useRouter } from 'vue-router';
+</script>
+
+<style scoped></style>

+ 109 - 0
src/views/SettingsGlobal.vue

@@ -0,0 +1,109 @@
+<template>
+  <div>
+    <a-form ref="ref_form" :model="form" :style="{ width: '700px' }">
+      <a-form-item field="平台名称" label="平台名称" :required="true">
+        <a-input v-model="form.platform_name" placeholder="请输入平台名称" />
+      </a-form-item>
+
+      <a-form-item field="logo" label="logo" :required="true">
+        <a-upload
+          action="/"
+          :fileList="file ? [file] : []"
+          :show-file-list="false"
+          @change="onChange"
+          @progress="onProgress"
+        >
+          <template #upload-button>
+            <div
+              :class="`arco-upload-list-item${file && file.status === 'error' ? ' arco-upload-list-item-error' : ''}`"
+            >
+              <div class="arco-upload-list-picture custom-upload-avatar" v-if="file && file.url">
+                <img :src="file.url" />
+                <div class="arco-upload-list-picture-mask">
+                  <IconEdit />
+                </div>
+                <a-progress
+                  v-if="file.status === 'uploading' && file.percent < 100"
+                  :percent="file.percent"
+                  type="circle"
+                  size="mini"
+                  :style="{
+                    position: 'absolute',
+                    left: '50%',
+                    top: '50%',
+                    transform: 'translateX(-50%) translateY(-50%)'
+                  }"
+                />
+              </div>
+              <div class="arco-upload-picture-card" v-else>
+                <div class="arco-upload-picture-card-text">
+                  <IconPlus />
+                  <div style="margin-top: 10px; font-weight: 600">Upload</div>
+                </div>
+              </div>
+            </div>
+          </template>
+        </a-upload>
+      </a-form-item>
+
+      <a-form-item field="平台介绍" label="平台介绍" :required="true">
+        <a-textarea v-model="form.platform_introduce" placeholder="Please enter something" allow-clear />
+      </a-form-item>
+
+      <a-form-item>
+        <div class="setting-password-btn-container">
+          <a-space>
+            <a-button class="submit-btn">取消 </a-button>
+
+            <a-button
+              class="submit-btn"
+              type="primary"
+              html-type="submit"
+              :loading="loading"
+              @click="handleUpdatePassword"
+              >保存
+            </a-button>
+          </a-space>
+        </div>
+      </a-form-item>
+    </a-form>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, getCurrentInstance, computed } from 'vue';
+import { useRouter } from 'vue-router';
+
+import { Message } from '../3rd-libs/arco-vue-libs';
+
+const router = useRouter();
+
+const form = reactive({
+  platform_name: '',
+  platform_logo: '',
+  platform_introduce: ''
+});
+
+const loading = ref(false);
+
+const file = ref();
+
+const onChange = (_, currentFile) => {
+  file.value = {
+    ...currentFile
+    // url: URL.createObjectURL(currentFile.file),
+  };
+};
+
+const onProgress = (currentFile) => {
+  file.value = currentFile;
+};
+</script>
+
+<style scoped>
+.setting-password-btn-container {
+  display: flex;
+  justify-content: flex-end;
+  flex-grow: 1;
+}
+</style>

+ 139 - 0
src/views/SettingsPassword.vue

@@ -0,0 +1,139 @@
+<template>
+  <div>
+    <a-form ref="ref_form" :model="form" :style="{ width: '700px' }">
+      <a-form-item :field="$t('settings.current_password')" :label="$t('settings.current_password')" :required="true">
+        <a-input-password
+          v-model="form.password_current"
+          :placeholder="$t('settings.placeholder_cur_password')"
+          :defaultVisibility="true"
+          allow-clear
+        >
+          <template #prefix>
+            <icon-lock />
+          </template>
+        </a-input-password>
+      </a-form-item>
+
+      <a-form-item :field="$t('settings.new_password')" :label="$t('settings.new_password')" :required="true">
+        <a-input-password
+          v-model="form.password_new_1"
+          :placeholder="$t('settings.placeholder_new1_password')"
+          :defaultVisibility="true"
+          allow-clear
+        >
+          <template #prefix>
+            <icon-lock />
+          </template>
+        </a-input-password>
+      </a-form-item>
+
+      <a-form-item :field="$t('settings.confirm_password')" :label="$t('settings.confirm_password')" :required="true">
+        <a-input-password
+          v-model="form.password_new_2"
+          :placeholder="$t('settings.placeholder_new2_password')"
+          :defaultVisibility="true"
+          allow-clear
+        >
+          <template #prefix>
+            <icon-lock />
+          </template>
+        </a-input-password>
+      </a-form-item>
+
+      <a-form-item>
+        <div class="setting-password-btn-container">
+          <a-space>
+            <a-button class="submit-btn">{{ $t('settings.btn_cancel') }} </a-button>
+
+            <a-button
+              class="submit-btn"
+              type="primary"
+              html-type="submit"
+              :loading="loading"
+              @click="handleUpdatePassword"
+              >{{ $t('settings.btn_submit') }}
+            </a-button>
+          </a-space>
+        </div>
+      </a-form-item>
+    </a-form>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, getCurrentInstance, computed } from 'vue';
+import { useRouter } from 'vue-router';
+import { useI18n } from 'vue-i18n';
+
+import { Message } from '../3rd-libs/arco-vue-libs';
+
+import { changeUserPassword } from '../apis/login';
+import { encrypt } from '../utils/jsencrypt';
+
+const router = useRouter();
+const i18n = useI18n();
+
+const form = reactive({
+  password_current: '',
+  password_new_1: '',
+  password_new_2: ''
+});
+
+const loading = ref(false);
+
+const handleUpdatePassword = () => {
+  const pre = form.password_current;
+  const new_1 = form.password_new_1;
+  const new_2 = form.password_new_2;
+
+  if (!pre || !new_1 || !new_2) {
+    return;
+  }
+
+  if (new_1 !== new_2) {
+    // console.log('新密码两次输入不一致,请确认后再修改');
+    const msg = i18n.t('settings.warning_not_match');
+
+    Message.error(msg);
+    return;
+  }
+
+  const pre_pswd = encrypt(pre); // 加密后的旧密码
+  const new_pswd = encrypt(new_1); // 加密后的新密码
+
+  loading.value = true;
+  changeUserPassword(pre_pswd, new_pswd)
+    .then((res) => {
+      // {"code":200,"msg":"user pwd change success","data":{}}
+
+      const code = res.code / 1;
+
+      if (code === 200) {
+        // 成功: 清空原先的值
+        form.password_current = '';
+        form.password_new_1 = '';
+        form.password_new_2 = '';
+
+        Message.success(res.msg);
+      } else {
+        // 失败
+        Message.warning(res.msg);
+      }
+
+      loading.value = false;
+    })
+    .catch((err) => {
+      console.log(err, 'flag-njsdfjsau345jkdsfljfjsklaj->>');
+      Message.error(err.msg);
+      loading.value = false;
+    });
+};
+</script>
+
+<style scoped>
+.setting-password-btn-container {
+  display: flex;
+  justify-content: flex-end;
+  flex-grow: 1;
+}
+</style>

+ 20 - 0
src/views/SettingsPhone.vue

@@ -0,0 +1,20 @@
+<template>
+  <div>绑定电话号码</div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useRouter } from 'vue-router';
+
+import bg_image from '../assets/chat/board-bg.png';
+
+const router = useRouter();
+
+const content_style = {
+  // backgroundImage: `url(${bg_image})`,
+  backgroundRepeat: 'no-repeat',
+  backgroundSize: 'contain'
+};
+</script>
+
+<style scoped></style>

+ 20 - 0
src/views/SettingsProfile.vue

@@ -0,0 +1,20 @@
+<template>
+  <div>修改个人资料</div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useRouter } from 'vue-router';
+
+// import bg_image from '../assets/chat/board-bg.png';
+
+const router = useRouter();
+
+const content_style = {
+  // backgroundImage: `url(${bg_image})`,
+  backgroundRepeat: 'no-repeat',
+  backgroundSize: 'contain'
+};
+</script>
+
+<style scoped></style>

+ 3 - 0
src/views/account/use-logout.js

@@ -1,5 +1,6 @@
 import { useRouter } from 'vue-router';
 import { kuky_Authorization, localST_RoutesList } from '../../base/storage';
+import { useCurrentUserStore } from '../../base/store/use-current-user';
 import { resetHasRouteFlag } from '../../router';
 
 /**
@@ -8,8 +9,10 @@ import { resetHasRouteFlag } from '../../router';
  */
 export default function useLogout() {
   const router = useRouter();
+  const curUser = useCurrentUserStore();
 
   return () => {
+    curUser.logout();
     localST_RoutesList.removeItem();
     kuky_Authorization.remove();
 

+ 15 - 1
src/views/common/header-btns/BtnCurrentUser.vue

@@ -1,7 +1,7 @@
 <template>
   <a-dropdown trigger="hover" @select="handleSelect">
     <div class="header-right-profile">
-      <span>{{ currentUser.username }}</span>
+      <span class="header-right-user-name" @click="handleGoSetting">{{ currentUser.username }}</span>
     </div>
 
     <template #content>
@@ -13,9 +13,12 @@
 
 <script setup>
 import { ref } from 'vue';
+import { useRouter } from 'vue-router';
 import ModalLogout from './ModalLogout.vue';
 import { useCurrentUserStore } from '../../../base/store/use-current-user';
 
+const router = useRouter();
+
 const currentUser = useCurrentUserStore();
 
 const visible = ref(false);
@@ -23,9 +26,20 @@ const visible = ref(false);
 const handleSelect = (value) => {
   visible.value = true;
 };
+
+// 进入设置页面
+const handleGoSetting = () => {
+  router.push({
+    path: '/settings'
+  });
+};
 </script>
 
 <style lang="css" scoped>
 .header-right-profile {
 }
+
+.header-right-user-name {
+  cursor: pointer;
+}
 </style>