Sfoglia il codice sorgente

feat: 用户中心以及请求调整

sunxiao 9 mesi fa
parent
commit
3802d0733a

+ 0 - 27
src/App.vue

@@ -2,10 +2,7 @@
 import { RouterView } from 'vue-router';
 import { NConfigProvider, NMessageProvider, zhCN, dateZhCN } from 'naive-ui';
 import type { GlobalThemeOverrides } from 'naive-ui';
-import { SelectProps, InputProps, TableProps } from 'naive-ui'
 
-// type SelectThemeOverrides = NonNullable<SelectProps['themeOverrides']>
-// type SelectThemeOverrides = NonNullable<InputProps['themeOverrides']>
 const primaryColor = '#1A2029';
 
 /**
@@ -67,30 +64,6 @@ const themeOverrides: GlobalThemeOverrides = {
     colorPressedPrimary: '#1D43CC',
     borderPressedPrimary: '#1D43CC',
   },
-  DataTable: {
-    // thTextColor: '#1A2029',
-    // tdTextColor: primaryColor,
-    // borderRadius: '4px',
-    // borderColor: '#D7D7D7',
-    // thPaddingMedium: '8px 10px',
-    // tdPaddingMedium: '7px 10px',
-    // fontSizeMedium: '11px'
-  }
-  // Menu: {
-  //   itemTextColorHorizontal: '#161616',
-  //   itemTextColorHoverHorizontal: '#2A68FF',
-  //   itemTextColor: '#161616',
-  //   itemColorHover: 'rgba(42, 104, 255, 0.1)',
-  //   itemTextColorHover: '#2A68FF',
-  // },
-  // Pagination: {
-  //   itemColorActiveHover: '#0092FF',
-  //   itemColorActive: '0092FF',
-  //   itemColor: "#f7f7f7",
-  //   itemTextColorActive: '#fff',
-  //   itemTextColorHover: '#fff',
-  //   itemColorHover: '#0092FF',
-  // }
 }
 
 </script>

+ 25 - 11
src/api/chat.js

@@ -1,29 +1,43 @@
-import http from "@/utils/request";
-
+import http, { streamHttp } from "@/utils/request";
 
 export const chatApi = {
-
+  /**
+   * 获取记录
+   */
   getRecordFetch: (path, params) => http.get(path, { params }),
-
+  
   /**
-   * 问答历史记录
-   * module
-   * 0 专家问答
-   * 1 智能工单 
-   * 2 智能体助手
-   * 3 告警
+   * 问答列表
    */
   getAnswerHistoryList: params => http.get('/front/bigModel/qa/pageList', { params }),
 
+  /**
+   * 通过sessionId获取某个用户的问答列表
+   */
   getAnswerHistoryDetail: params => http.get('/front/bigModel/qa/qaListBySessionId', { params }),
 
-  getChatStream: ({ data, onDownloadProgress, signal }) => http.post('/grpc/inferStreamRag', data, { onDownloadProgress, signal }),
+  /**
+   * 问答流数据
+   */
+  getChatStream: ({ data, onDownloadProgress, signal }) => streamHttp.post('/grpc/inferStreamRag', data, { onDownloadProgress, signal }),
 
+  /**
+   * 获取sessionId
+   */
   getChatSessionTag: params =>http.get('/front/bigModel/chat/generateSessionId', { params }),
 
+  /**
+   * 推荐问答列表
+   */
   getWelcomeRecommend: params => http.get('/front/bigModel/home/recommendQAList/' + params),
 
+  /**
+   * 删除历史记录
+   */
   deleteHistory: params => http.delete('/front/bigModel/chat/deleteOneChtById/' + params),
 
+  /**
+   * 点赞 or 取消点赞
+   */
   putIsSatisfiedAnswer: data => http.put(`/front/bigModel/chat/isSatisfiedAnswer`, data),
 }

+ 0 - 1
src/api/helper.js

@@ -5,5 +5,4 @@ export const helperApi = {
    * 获取助手列表
    */
   getHelperList: params => http.get('/front/bigModel/agentAssistant/list', { params }),
-
 }

+ 6 - 0
src/api/user.js

@@ -15,8 +15,14 @@ export const userApi = {
    * 退出登录
    */
   postLogout: () => http.post('/logout'),
+
   /**
    * 修改个人信息接口
    */
   UpUser: (data) => http.put('/system/user/profile', data),
+  
+  /**
+   * 修改密码
+   */
+  putPassword: ({ oldPassword, newPassword }) => http.put(`/system/user/profile/updatePwd?oldPassword=${oldPassword}&newPassword=${newPassword}`),
 }

+ 3 - 16
src/api/water.js

@@ -1,8 +1,9 @@
 import http from "@/utils/request";
 
 export const waterApi = {
-
-  // 取报警统计信息
+   /**
+   * 取报警统计信息
+  */
   getWarningCount: params => http.get('/front/bigModel/warningCount', { params }),
 
   /**
@@ -24,18 +25,4 @@ export const waterApi = {
    * 获取预测
   */
   getWaringForecast: params => http.get('/front/bigModel/warning/forecastList/' + params),
-
-  // getAnswerHistoryList: params => http.get('/front/bigModel/qa/pageList', { params }),
-
-  // getAnswerHistoryDetail: params => http.get('/front/bigModel/qa/qaListBySessionId', { params }),
-
-  // getChatStream: ({ data, onDownloadProgress, signal }) => http.post('/grpc/inferStreamRag', data, { onDownloadProgress, signal }),
-
-  // getChatSessionTag: params =>http.get('/front/bigModel/chat/generateSessionId', { params }),
-
-  // getWelcomeRecommend: params => http.get('/front/bigModel/home/recommendQAList/' + params),
-
-  // deleteHistory: params => http.delete('/front/bigModel/chat/deleteOneChtById/' + params),
-
-  // putIsSatisfiedAnswer: data => http.put(`/front/bigModel/chat/isSatisfiedAnswer`, data),
 }

+ 96 - 16
src/components/Dialog/editPassword.vue

@@ -1,14 +1,86 @@
 <script setup>
-import { ref } from 'vue';
-const userStore = useUserStore();
+import { ref, unref } from 'vue';
 import { useUserStore } from '@/stores/modules/userStore';
-import { NModal, NForm, NFormItem, NInput, NButton } from "naive-ui";
+import { NModal, NForm, NFormItem, NInput, NButton, useMessage } from "naive-ui";
+import { userApi } from '@/api/user'
+
+const message = useMessage();
+const userStore = useUserStore();
+
+const formRef = ref(null);
+
 const model = ref({
   oldPassword: '',
   newPassword: '',
-  confirmPassword: ''
+  reenteredPassword: ''
 })
 
+const rules = {
+  oldPassword: [
+    {
+      required: true,
+      message: '请输入旧密码'
+    }
+  ],
+  newPassword: [
+    {
+      required: true,
+      message: '请输入新密码'
+    },
+    {
+      validator: (_, value) => (/^(?=.*\d)(?=.*[a-zA-Z])[a-zA-Z\d]{8,16}$/.test(value)),
+      message: '新密码需要8-16位, 且包含字母和数字'
+    }
+  ],
+  reenteredPassword: [
+    {
+      required: true,
+      message: '请再次输入密码',
+      trigger: ['input', 'blur']
+    },
+    {
+      validator: (_, value) => {
+        return !!model.value.newPassword && model.value.newPassword.startsWith(value) && model.value.newPassword.length >= value.length;
+      },
+      message: "两次密码输入不一致",
+      trigger: "input"
+    }
+  ]
+}
+
+/**
+ * 重置表单
+*/
+const resetForm = () => {
+  formRef.value?.restoreValidation();
+  model.value = {
+    oldPassword: '',
+    newPassword: '',
+    reenteredPassword: ''
+  }
+  userStore.dialogStatus = false;
+}
+
+/**
+ * 取消表单
+*/
+const onCancel = () => resetForm();
+
+/**
+ * 提交表单
+*/
+const onSubmit = event => {
+  event.preventDefault();
+  formRef.value?.validate((errors) => {
+    if (!errors) {
+      const { oldPassword, newPassword } = unref(model);
+      userApi.putPassword({ oldPassword, newPassword });
+      resetForm();
+      message.success('密码修改成功');
+    }
+  });
+}
+
 </script>
 
 <template>
@@ -17,21 +89,21 @@ const model = ref({
       <div class="edit-passWord">
         <div class="title">修改密码</div>
         <div class="main">
-          <n-form ref="formRef" :model="model" label-placement="left" label-align="left" :label-width="64">
-            <n-form-item label="旧密码" path="oldPassword">
+          <n-form ref="formRef" :model="model" :rules="rules" label-placement="left" label-align="left" :label-width="64" :show-require-mark="false">
+            <n-form-item label="旧密码" path="oldPassword" first>
               <n-input v-model:value="model.oldPassword" class="input" placeholder="请输入登录密码" type="password" />
             </n-form-item>
-            <n-form-item label="新密码" path="newPassword">
+            <n-form-item label="新密码" path="newPassword" first>
               <n-input v-model:value="model.newPassword" class="input" placeholder="请输入8~16位数字,字母" type="password" />
             </n-form-item>
-            <n-form-item label="确认密码" path="confirmPassword">
-              <n-input v-model:value="model.confirmPassword" class="input" placeholder="请再次输入密码" type="password" />
+            <n-form-item label="确认密码" path="reenteredPassword" first>
+              <n-input v-model:value="model.reenteredPassword" class="input" placeholder="请再次输入密码" type="password" />
             </n-form-item>
           </n-form>
         </div>
         <div class="footer">
-          <n-button class="cencel btn" @click="userStore.dialogStatus = false">取消</n-button>
-          <n-button type="primary" class="ok btn" @click="userStore.dialogStatus = false">确定</n-button>
+          <n-button class="cencel btn" @click="onCancel">取消</n-button>
+          <n-button type="primary" class="ok btn" @click="onSubmit">确定</n-button>
         </div>
       </div>
     </n-modal>
@@ -44,7 +116,9 @@ const model = ref({
   padding: 20px 32px;
   background: linear-gradient(180deg, #D6EEFF 0%, #F1F7FB 100%);
 
-
+  .n-form-item-feedback__line {
+    font-size: 12px;
+  }
 
   .title {
     color: #1A2029;
@@ -53,12 +127,10 @@ const model = ref({
     font-weight: bold;
   }
 
-
-
   .main {
     background: #fff;
-    padding: 20px 43px;
-    height: 192px;
+    padding: 20px 43px 0 43px;
+    height: 200px;
 
     .input {
       border: 0.5px solid #D7D7D7;
@@ -92,4 +164,12 @@ const model = ref({
   }
 
 }
+</style>
+
+<style lang="scss">
+.edit-passWord {
+  .n-form-item-feedback__line {
+    font-size: 12px;
+  }
+}
 </style>

+ 1 - 9
src/components/Layout/TheUserAvatar.vue

@@ -66,7 +66,7 @@ const RenderUserAvatar = ({ store }) => {
     ),
     trigger: () => (
       <div class="flex items-center cursor-pointer">
-        <img src={user.avatar} alt="" class="w-[32px] mr-[10px]" />
+        <img src={user.avatar} alt="" class="w-[32px] mr-[10px] rounded-[50%]" />
         <span class="text-[#272D35] text-[12px]">{user.nickName}</span>
       </div>
     )
@@ -88,14 +88,6 @@ export default defineComponent({
 
     const userStore = useUserStore();
 
-    const clearLoginStatus = async () => {
-      // await userApi.postLogout();
-      // userStore.clearUserInfo();
-      // router.push("/login");
-    }
-
-    // console.log(userStore.userInfo);
-    // userStore.userInfo
     return () => (
       <div >{<RenderUserAvatar store={userStore} />}</div>
     );

+ 18 - 4
src/components/User/contactUs.vue

@@ -3,13 +3,27 @@ import { reactive } from 'vue';
 import { NForm, NFormItem, NInput, NButton } from "naive-ui";
 import { screenApi } from "@/api/screen"
 import { useMessage } from 'naive-ui';
+
 const model = reactive({
-  description: null,
-  phone: null
+  description: '',
+  phone: ''
 });
+
 const message = useMessage();
 
 const feedback = () => {
+  if (!(model.description.trim())) {
+    return message.warning("请输入问题描述");
+  }
+
+  if (!model.phone.trim()) {
+    return message.warning("请输入联系方式");
+  }
+
+  if (!/^(1[3-9])\d{9}$/.test(model.phone)) {
+    return message.warning("请输入正确的联系方式");
+  }
+
   screenApi.feedback(model).then(res => {
     if (res.code == 200) {
       message.success(res.msg)
@@ -28,7 +42,7 @@ const feedback = () => {
       <n-form ref="formRef" :model="model">
         <n-form-item path="description" label="问题描述" class="form-line">
           <n-input v-model:value="model.description" placeholder="请输入您的建议,500字以内" class="textarea" type="textarea"
-            @keydown.enter.prevent />
+            @keydown.enter.prevent autosize maxlength="500"/>
         </n-form-item>
         <n-form-item path="phone" label="联系方式">
           <n-input v-model:value="model.phone" maxlength="11" placeholder="请输入手机号" class="input"
@@ -60,7 +74,7 @@ const feedback = () => {
   }
 
   .textarea {
-    width: 50%;
+    width: 396px;
     height: 200px;
   }
 

+ 26 - 34
src/components/User/userEdit.vue

@@ -1,55 +1,48 @@
 <script setup>
-import { reactive } from 'vue';
+import { reactive, ref } from 'vue';
+import { storeToRefs } from 'pinia';
 import { upLoadImageFun } from "@/utils/tools";
 import { NForm, NFormItem, NButton } from "naive-ui";
 import { userApi } from "@/api/user";
 import { useMessage } from 'naive-ui';
-const props = defineProps({
-  user: {
-    type: Object,
-    default: {
-      dept: { deptName: '' }
-    }
-  },
-})
+import { useUserStore } from '@/stores/modules/userStore';
+
 const message = useMessage();
+
+const userStore = useUserStore();
+const { userInfo:user } = storeToRefs(userStore);
+
+const tempAvatar = ref(user.value.avatar);
+
 const model = reactive({
   des: null,
   selectValue: "信义污水厂",
   submitStatus: false
 });
 
-
 const changeImg = async (e) => {
-  const avatar = await upLoadImageFun(e.target)
-  console.log(avatar, 12);
+  const avatar = await upLoadImageFun(e.target);
   if (avatar) {
-    props.user.avatar = avatar
+    tempAvatar.value = avatar;
+    message.success("头像更新成功");
   }
-
 }
-const submit = () => {
-  console.log(model.submitStatus);
+const submit = async () => {
   if (!model.submitStatus) {
     model.submitStatus = true
     const data = {
-      avatar: props.user.avatar,
-      userName: props.user.userName,
-      phonenumber: props.user.phonenumber,
-      nickName: props.user.nickName,
-      position: props.user.position,
-      deptId: props.user.deptId,
-      emergencyPhone: props.user.emergencyPhone,
+      avatar: tempAvatar.value,
+      // userName: user.value.userName,
+      // phonenumber: user.value.phonenumber,
+      // nickName: user.value.nickName,
+      // position: user.value.position,
+      // deptId: user.value.deptId,
+      // emergencyPhone: user.value.emergencyPhone,
     }
-    userApi.UpUser(data).then((res) => {
-      if (res.code == 200) {
-        message.success(res.msg)
-      } else {
-        message.error(res.msg)
-      }
-
-    }).finally(() => {
-      model.submitStatus = false
+    await userApi.UpUser(data);
+    await userApi.getUserInfo().then(({user: userInfo}) => {
+      userStore.setUserInfo({ ...userInfo });
+      model.submitStatus = false;
     })
   }
 }
@@ -63,7 +56,7 @@ const submit = () => {
           <div class="avatar-wrap">
             <div class="replace">更换<input class="file" type="file" @change="changeImg"></input></div>
             <div class="avatar-wrap-box">
-              <figure class="avatar"><img :src="user.avatar" alt=""></figure>
+              <figure class="avatar"><img :src="tempAvatar" alt=""></figure>
               <span>头像尺寸64*64</span>
             </div>
           </div>
@@ -93,7 +86,6 @@ const submit = () => {
         <n-form-item label="水厂" v-if="user.dept">
           <div class="content">
             信义污水厂
-            <!-- {{ user.dept['deptName'] }} -->
             <span class="des">设置为默认登录</span>
           </div>
         </n-form-item>

+ 3 - 1
src/stores/modules/userStore.js

@@ -1,11 +1,13 @@
-import { ref, unref, computed } from 'vue'
+import { ref, unref } from 'vue'
 import { defineStore } from 'pinia'
 
 export const useUserStore = defineStore('user', () => {
   const userInfo = ref({});
   const dialogStatus = ref(false);
+
   const setUserInfo = params => {
     userInfo.value = { ...unref(userInfo), ...params };
+    console.log("userInfo.value", userInfo.value);
   }
 
   const clearUserInfo = user => {

+ 21 - 21
src/utils/request.ts

@@ -1,9 +1,8 @@
 import axios from 'axios';
+import { useRouter } from 'vue-router';
 import { useUserStore } from '@/stores/modules/userStore';
 import { createDiscreteApi } from 'naive-ui';
 
-import { tansParams } from "@/utils/tools";
-
 import type { Result } from '@/types/data';
 import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig, AxiosError } from 'axios';
 
@@ -41,6 +40,8 @@ const showNotification = (type: keyof NotificationApi = 'error', meta: string) =
   })
 }
 
+const router = useRouter();
+
 export class Request {
 
   private instance: AxiosInstance;
@@ -52,12 +53,6 @@ export class Request {
 
     this.instance.interceptors.request.use((config: InternalAxiosRequestConfig<Result>) => {
       const { token } = useStore.userInfo;
-      // if (config.method === "get" && config.params) {
-      //   let url = config.url + '?' + tansParams(config.params);
-      //   url = url.slice(0, -1);
-      //   config.params = {};
-      //   config.url = url;
-      // }
       
       token && (config.headers.Authorization = 'Bearer ' + token);
 
@@ -67,23 +62,23 @@ export class Request {
     });
 
     this.instance.interceptors.response.use(res => {
-    
+
       if ( res.config.onDownloadProgress ) return res;
    
       const { code } = res.data;
-      // console.log(code)
-      // !success && showNotification("error", message);
-      return code === 200 ? res.data : Promise.reject(res.data);
+
+      if ( code == 200 ) {
+        return res.data;
+      }
+
+      if ( code == 401 ) {
+        showNotification('error', 'Token已失效')
+        router.push("/login");
+      }
+
+      return Promise.reject(res.data);
 
     }, (error: AxiosError) => {
-      // if (error.code === 'ERR_ABORTED') {
-      //   console.log( "取消了请求", error.code );
-      //   return
-      // }
-      // error.code === 'ERR_ABORTED'
-      // console.log("取消了请求");
-      const errorMessage = errorCode[error.response?.status as number] || error.message ||'未知错误';
-      // showNotification("error", errorMessage);
       return Promise.reject(error);
     })
   }
@@ -123,7 +118,12 @@ export class Request {
   }
 }
 
+export const streamHttp  = new Request({
+  baseURL,
+  timeout: 3 * 60 * 1000
+})
+
 export default new Request({
   baseURL,
-  timeout: 120 * 1000
+  timeout: 10 * 1000
 });

+ 4 - 11
src/views/analyse/WaterView.vue

@@ -160,6 +160,8 @@ const handleOpenContent = async ({ id, category }) => {
 
     const answerObjItem = JSON.parse( item );
 
+    // TODO: 后面带需求确定后完善
+
     if ( answerObjItem.biz === "DECISION_REPORT" ) {
       reportList.push(answerObjItem.message);
 
@@ -241,15 +243,6 @@ const handleOpenContent = async ({ id, category }) => {
     })
   }
 
-  // console.log( "reportList", reportList );
-  // console.log( "alertList", alertList );
-  // const [ answerStrItem ] = answer;
-  // const answerObjItem = JSON.parse( answerStrItem );
-
-
-
-
-  // console.log( "answerObjItem", answer, JSON.parse(answerObjItem.message) );
   const textWhiteList = [
     { label: '报警时间', realKey: '报警时间', value: '', isWarning: false },
     { label: '报警值',   realKey: '报警值', value: 'mg/L', isWarning: true },
@@ -395,9 +388,8 @@ const handlerAlertOptions = (item, val, index) => {
       return accumulator;
     }, {});
 
-   const newResult = { ...flowParams.feedback, ...result };
+    const newResult = { ...flowParams.feedback, ...result };
     flowParams.feedback = newResult;
-    console.log(  flowParams.feedback );
     onRegenerate();
   }
 }
@@ -578,6 +570,7 @@ const handleWelcomeRecommend = question => {
         v-show="answerLoading"
         loadingText="内容生成中,大概需要50秒..."
       ></ChatAnswer>
+
     </TheChatView>
   </section>
   <CustomModal

+ 5 - 4
src/views/user/index.vue

@@ -19,7 +19,6 @@ const getInfo = () => {
   screenApi.getInfo().then(res => {
     data.user = res.user
   })
-
 }
 getInfo()
 </script>
@@ -31,10 +30,12 @@ getInfo()
       <div class="user-warp-title">个人中心</div>
       <div class="user-warp-box">
 
-
         <div class="header">
-          <span :class="['tab', data.tab_action == index ? 'action' : '']" v-for="item, index in data.tabs"
-            @click="changeTab(index)">{{ item }}</span>
+          <span
+            :class="['tab', data.tab_action == index ? 'action' : '']"
+            v-for="item, index in data.tabs"
+            @click="changeTab(index)"
+          >{{ item }}</span>
         </div>
 
         <!-- 账号管理 -->

+ 6 - 11
yarn.lock

@@ -354,10 +354,10 @@
   resolved "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz"
   integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
 
-"@esbuild/darwin-x64@0.20.2":
+"@esbuild/win32-x64@0.20.2":
   version "0.20.2"
-  resolved "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz"
-  integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==
+  resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz"
+  integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==
 
 "@isaacs/cliui@^8.0.2":
   version "8.0.2"
@@ -448,10 +448,10 @@
     estree-walker "^2.0.2"
     picomatch "^2.3.1"
 
-"@rollup/rollup-darwin-x64@4.17.2":
+"@rollup/rollup-win32-x64-msvc@4.17.2":
   version "4.17.2"
-  resolved "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz"
-  integrity sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==
+  resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz"
+  integrity sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==
 
 "@traptitech/markdown-it-katex@^3.6.0":
   version "3.6.0"
@@ -1775,11 +1775,6 @@ fs-extra@^11.2.0:
     jsonfile "^6.0.1"
     universalify "^2.0.0"
 
-fsevents@~2.3.2, fsevents@~2.3.3:
-  version "2.3.3"
-  resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz"
-  integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
-
 function-bind@^1.1.2:
   version "1.1.2"
   resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz"