黄何裕
2024-07-12 ca9a92cfc2f2bd8c724020ee5e46094633eb80a5
处理冲突
7个文件已修改
2个文件已添加
624 ■■■■ 已修改文件
auto-imports.d.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
components.d.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/modules/personalCenter.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/NormalHeader/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/exam/index.vue 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/home/components/user-panel/index.vue 228 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/personal-center/index.vue 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
auto-imports.d.ts
@@ -5,5 +5,5 @@
// Generated by unplugin-auto-import
export {}
declare global {
  const ElMessage: typeof import('element-plus/es')['ElMessage']
}
components.d.ts
@@ -7,12 +7,14 @@
/* prettier-ignore */
declare module 'vue' {
  export interface GlobalComponents {
    ElAvatar: typeof import('element-plus/es')['ElAvatar']
    ElButton: typeof import('element-plus/es')['ElButton']
    ElCard: typeof import('element-plus/es')['ElCard']
    ElCol: typeof import('element-plus/es')['ElCol']
    ElCollapse: typeof import('element-plus/es')['ElCollapse']
    ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
    ElCountdown: typeof import('element-plus/es')['ElCountdown']
    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
    ElDialog: typeof import('element-plus/es')['ElDialog']
    ElDropdown: typeof import('element-plus/es')['ElDropdown']
    ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
@@ -32,6 +34,8 @@
    ElSlider: typeof import('element-plus/es')['ElSlider']
    ElTable: typeof import('element-plus/es')['ElTable']
    ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
    ElTabPane: typeof import('element-plus/es')['ElTabPane']
    ElTabs: typeof import('element-plus/es')['ElTabs']
    ElTag: typeof import('element-plus/es')['ElTag']
    ExamAudio: typeof import('./src/components/ExamAudio/index.vue')['default']
    ExamInfo: typeof import('./src/components/ExamInfo/index.vue')['default']
src/api/modules/personalCenter.js
New file
@@ -0,0 +1,9 @@
import service from "@/api";
export const uploadImg = (fromData) => {
  return service.post("/api/student/upload/image", fromData, {
    headers: {
      "Content-Type": "multipart/form-data",
    },
  });
};
src/components/NormalHeader/index.vue
@@ -9,8 +9,8 @@
    <div class="user-container flex items-center">
      <div class="avatar-container w-12 h-12 rounded-full overflow-hidden mr-3">
        <div class="avatar-content" v-if="userInfo.imagePath"">
          <img :src="userInfo.imagePath" class="avatar-img" alt="">
        <div class="avatar-content" v-if="userInfo.imagePath">
          <img :src="'/api/files/'+userInfo.imagePath" class="avatar-img" alt="">
        </div>
        <div class="avatar-content" :style="{ backgroundColor: getColor }">
          <div class="name text-xl font-bold text-white">{{ getFirstName }}</div>
src/router/index.js
@@ -66,6 +66,11 @@
    path: '/folder',
    component: () => import('@/views/folder/index.vue'),
  },
  //个人中心
  {
    path: '/personal-center',
    component: () => import('@/views/personal-center/index.vue'),
  }
];
const router = createRouter({
src/views/exam/index.vue
@@ -1,5 +1,7 @@
<template>
  <div class="exam-container w-screen h-screen bg-slate-50 relative overflow-hidden">
  <div
    class="exam-container w-screen h-screen bg-slate-50 relative overflow-hidden"
  >
    <div class="top-bg bg-blue-500"></div>
    <div class="exam-content">
      <div class="exam-wrapper container mx-auto h-full flex flex-col">
@@ -24,8 +26,10 @@
          <!-- 答题卡区 -->
          <div class="answer-wrapper answer-left mr-8 shadow-xl p-4 box-border">
            <div class="wrapper h-full flex flex-col items-center">
              <div class="title-wrapper w-full flex justify-between items-center mb-5">
                <div class="title text-xl font-semibold ">答题卡</div>
              <div
                class="title-wrapper w-full flex justify-between items-center mb-5"
              >
                <div class="title text-xl font-semibold">答题卡</div>
                <AnswerTag></AnswerTag>
              </div>
@@ -40,9 +44,13 @@
              </div>
              <div class="submit-wrapper">
                <el-button type="primary" class="submit-button" @click="submitExamHandle">提交试卷</el-button>
                <el-button
                  type="primary"
                  class="submit-button"
                  @click="submitExamHandle"
                  >提交试卷</el-button
                >
              </div>
            </div>
          </div>
@@ -50,15 +58,20 @@
          <div class="answer-wrapper answer-right grow shadow-xl p-4">
            <div class="wrapper h-full flex flex-col">
              <div class="title-wrapper w-full flex mb-5">
                <div class="title text-xl font-semibold ">{{ examType[currentType] }} ({{
                  examStore.getActiveQuestion.questionScore }}分)
                <div class="title text-xl font-semibold">
                  {{ examType[currentType] }} ({{
                    examStore.getActiveQuestion.questionScore
                  }}分)
                </div>
              </div>
              <div class="main-wrapper w-full grow relative my-5">
                <div class="main-content absolute top-0 bottom-0 w-full">
                  <Transition appear name="fade-transform" mode="out-in">
                    <component :is="typeComponent[currentType]" :key="currentIndex"></component>
                    <component
                      :is="typeComponent[currentType]"
                      :key="currentIndex"
                    ></component>
                  </Transition>
                </div>
              </div>
@@ -66,10 +79,17 @@
              <div class="tool-wrapper flex justify-end">
                <div class="button-container flex items-center">
                  <div class="button-item">
                    <el-button class="tool-button" @click="prevQuestion">上一题</el-button>
                    <el-button class="tool-button" @click="prevQuestion"
                      >上一题</el-button
                    >
                  </div>
                  <div class="button-item">
                    <el-button class="tool-button" type="primary" @click="nextQuestion">下一题</el-button>
                    <el-button
                      class="tool-button"
                      type="primary"
                      @click="nextQuestion"
                      >下一题</el-button
                    >
                  </div>
                </div>
              </div>
@@ -78,7 +98,6 @@
        </div>
      </div>
    </div>
    <!-- 退出考试提示弹窗 -->
    <el-dialog v-model="quitDialog" title="注意" width="500">
@@ -89,9 +108,7 @@
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="quitDialog = false">继续作答</el-button>
          <el-button type="danger" @click="confirmQuit">
            确定退出
          </el-button>
          <el-button type="danger" @click="confirmQuit"> 确定退出 </el-button>
        </div>
      </template>
    </el-dialog>
@@ -113,45 +130,50 @@
    </el-dialog>
    <!-- 考试时间弹窗 -->
    <el-dialog v-model="timeDialog" align-center width="500" :close-on-click-modal="false"
      :close-on-press-escape="false" :show-close="false">
    <el-dialog
      v-model="timeDialog"
      align-center
      width="500"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :show-close="false"
    >
      <div class="dialog-container flex flex-col items-center">
        <div class="icon-container">
          <el-icon :size="50" color="#3680fa">
            <Timer />
          </el-icon>
        </div>
        <div class="dialog-info">
          考试结束,系统自动收卷中.......
        </div>
        <div class="dialog-info">考试结束,系统自动收卷中.......</div>
      </div>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref, watchEffect, watch, onMounted } from 'vue';
import { storeToRefs } from 'pinia';
import { Close, Timer } from '@element-plus/icons-vue';
import AnswerTag from './components/answer-tag/index.vue';
import AnswerProgress from './components/answer-progress/index.vue';
import AnswerSheet from './components/answer-sheet/index.vue';
import AnswerTime from './components/answer-time/index.vue';
import { ref, watchEffect, watch, onMounted ,onUnmounted} from "vue";
import { storeToRefs } from "pinia";
import { Close, Timer } from "@element-plus/icons-vue";
import AnswerTag from "./components/answer-tag/index.vue";
import AnswerProgress from "./components/answer-progress/index.vue";
import AnswerSheet from "./components/answer-sheet/index.vue";
import AnswerTime from "./components/answer-time/index.vue";
import AnswerSingle from './components/answer-main/answer-single/index.vue';
import AnswerMultiple from './components/answer-main/answer-multiple/index.vue';
import AnswerAudio from './components/answer-main/answer-audio/index.vue';
import AnswerFill from './components/answer-main/answer-fill/index.vue';
import AnswerDetermine from './components/answer-main/answer-determine/index.vue';
import AnswerShort from './components/answer-main/answer-short/index.vue';
import AnswerCount from './components/answer-main/answer-count/index.vue';
import AnswerSingle from "./components/answer-main/answer-single/index.vue";
import AnswerMultiple from "./components/answer-main/answer-multiple/index.vue";
import AnswerAudio from "./components/answer-main/answer-audio/index.vue";
import AnswerFill from "./components/answer-main/answer-fill/index.vue";
import AnswerDetermine from "./components/answer-main/answer-determine/index.vue";
import AnswerShort from "./components/answer-main/answer-short/index.vue";
import AnswerCount from "./components/answer-main/answer-count/index.vue";
import { useExamStore, useUserStore } from '@/store/index.js';
import { useRouter } from 'vue-router';
import { useExamStore, useUserStore } from "@/store/index.js";
import { useRouter } from "vue-router";
import { submitExam } from '@/api/modules/exam.js';
import useWebScoket from '@/hooks/useWebScoket.js';
import { submitExam } from "@/api/modules/exam.js";
import useWebScoket from "@/hooks/useWebScoket.js";
import { ElMessage, ElMessageBox } from "element-plus";
const router = useRouter();
@@ -159,7 +181,8 @@
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const { currentType, currentIndex, examDetail, examType, examInfo } = storeToRefs(examStore);
const { currentType, currentIndex, examDetail, examType, examInfo } =
  storeToRefs(examStore);
const typeComponent = {
  1: AnswerSingle,
@@ -181,11 +204,11 @@
//   heartBeatData: 'ping'
// });
const { status, message, error, connect, disconnect, sendMessage } = useWebScoket({
  url: '//192.168.3.64:8000/websocket/' + userInfo.value.id,
  heartBeatData: 'ping'
});
const { status, message, error, connect, disconnect, sendMessage } =
  useWebScoket({
    url: "//192.168.3.64:8000/websocket/" + userInfo.value.id,
    heartBeatData: "ping",
  });
// 上一题
const prevQuestion = () => {
@@ -220,19 +243,18 @@
        currentType.value = typeQuestion.questionType;
        currentIndex.value = typeQuestion.questionList.length - 1;
      }
    } else if (currentIndex.value < 0) {
      tempIndex--;
      if (examDetail.value[tempIndex]) {
        currentType.value = examDetail.value[tempIndex].questionType;
        currentIndex.value = examDetail.value[tempIndex].questionList.length - 1;
        currentIndex.value =
          examDetail.value[tempIndex].questionList.length - 1;
      } else {
        currentType.value = typeQuestion.questionType;
        currentIndex.value = 0;
      }
    }
  }
};
// 退出考试
@@ -262,18 +284,20 @@
const timeOut = () => {
  const temp = {
    ...examInfo.value,
    titleList: examDetail.value
    titleList: examDetail.value,
  };
  timeDialog.value = true;
  resetAllDialog();
  disconnect();
  submitExam(temp).then(res => {
    returnBack();
  }).catch(() => {
    returnBack();
  });
  submitExam(temp)
    .then((res) => {
      returnBack();
    })
    .catch(() => {
      returnBack();
    });
};
const returnBack = () => {
@@ -284,30 +308,55 @@
watchEffect(() => {
  let progress = 0;
  examDetail.value.forEach(item => {
    item.questionList.forEach(question => {
      if (question.answer || (Array.isArray(question.answerList) && question.answerList.length)) {
  examDetail.value.forEach((item) => {
    item.questionList.forEach((question) => {
      if (
        question.answer ||
        (Array.isArray(question.answerList) && question.answerList.length)
      ) {
        progress += 1;
      }
    });
  });
  examStore.setProgress(progress);
});
const answerTime = ref()
watch(() => message.value, (msg) => {
  if(msg.commend=="delayed"){
    answerTime.value.addTime(msg.data.addTimeM)
  }else if(msg.commend=="forceSubmit"){
    confirmSubmit()
const answerTime = ref();
watch(
  () => message.value,
  (msg) => {
    if (msg.commend == "delayed") {
      answerTime.value.addTime(msg.data.addTimeM);
    } else if (msg.commend == "forceSubmit") {
      confirmSubmit();
    }
  }
});
);
// document.addEventListener('blur', function() {
//     console.log('页面失去焦点');
// });
const warningNum = ref(0)
const handleBlur = () => {
  if (document.visibilityState === "visible") {
  } else if (document.visibilityState === "hidden") {
    ElMessageBox.alert("考试过程中请勿离开考试页面!警告三次将强制收卷", "警告", {
      confirmButtonText: "确定",
    });
    warningNum.value++
    if (warningNum.value==3) {
      timeOut();
    }
  }
};
// -----------------------------------生命周期
onMounted(() => {
  // 连接webscoket
  connect();
  document.addEventListener("visibilitychange", handleBlur);
});
onUnmounted(()=>{
  document.removeEventListener("visibilitychange", handleBlur);
})
</script>
<style lang="scss" scoped>
src/views/home/components/user-panel/index.vue
@@ -1,73 +1,62 @@
<template>
    <div class="user-panel max-w-sm min-w-96 h-fit">
        <el-card class="card">
            <div class="panel-content flex flex-col items-center">
                <!-- 学生信息 -->
                <el-dropdown>
                    <div
                        class="avatar-container w-40 h-40 rounded-full overflow-hidden el-dropdown-link"
                        @click="stateClick()"
                    >
                        <div
                            class="avatar-content"
                            v-if="userInfo.imagePath"
                        >
                            <img
                                :src="userInfo.imagePath"
                                class="avatar-img"
                                alt=""
                            />
                        </div>
                        <div
                            class="avatar-content"
                            :style="{ backgroundColor: getColor }"
                            v-else
                        >
                            <div class="name text-5xl font-bold text-white">
                                {{ getFirstName }}
                            </div>
                        </div>
                    </div>
                    <template #dropdown>
                        <el-dropdown-menu>
                            <el-dropdown-item>修改密码</el-dropdown-item>
                            <el-dropdown-item>个人中心</el-dropdown-item>
                            <el-dropdown-item @click="quit()"
                                >退出登录</el-dropdown-item
                            >
                        </el-dropdown-menu>
                    </template>
                </el-dropdown>
                <div class="name-container text-lg font-bold mt-5 mb-2">
                    {{ userInfo.realName }}
                </div>
                <div class="department-container text-base mb-10">
                    {{ userInfo.userName }}
                </div>
                <!-- 学生选项 -->
                <div class="tool-container grid grid-cols-3 gap-10">
                    <div
                        class="tool-item text-center cursor-pointer"
                        v-for="item in toolList"
                        @click="toolClick(item)"
                    >
                        <div class="tool-icon mb-1">
                            <img
                                :src="item.iconPath"
                                class="width-img"
                                alt=""
                            />
                        </div>
                        <div class="tool-title">
                            {{ item.title }}
                        </div>
                    </div>
                </div>
  <div class="user-panel max-w-sm min-w-96 h-fit">
    <el-card class="card">
      <div class="panel-content flex flex-col items-center">
        <!-- 学生信息 -->
        <el-dropdown>
          <div
            class="avatar-container w-40 h-40 rounded-full overflow-hidden el-dropdown-link"
            @click="stateClick()"
          >
            <div class="avatar-content" v-if="userInfo.imagePath">
              <img :src="'/api/files/'+userInfo.imagePath" class="avatar-img" alt="" />
            </div>
        </el-card>
    </div>
            <div
              class="avatar-content"
              :style="{ backgroundColor: getColor }"
              v-else
            >
              <div class="name text-5xl font-bold text-white">
                {{ getFirstName }}
              </div>
            </div>
          </div>
          <template #dropdown>
            <el-dropdown-menu>
              <el-dropdown-item>修改密码</el-dropdown-item>
              <el-dropdown-item @click="goPersonalCenter()"
                >个人中心</el-dropdown-item
              >
              <el-dropdown-item @click="quit()">退出登录</el-dropdown-item>
            </el-dropdown-menu>
          </template>
        </el-dropdown>
        <div class="name-container text-lg font-bold mt-5 mb-2">
          {{ userInfo.realName }}
        </div>
        <div class="department-container text-base mb-10">
          {{ userInfo.userName }}
        </div>
        <!-- 学生选项 -->
        <div class="tool-container grid grid-cols-3 gap-10">
          <div
            class="tool-item text-center cursor-pointer"
            v-for="item in toolList"
            @click="toolClick(item)"
          >
            <div class="tool-icon mb-1">
              <img :src="item.iconPath" class="width-img" alt="" />
            </div>
            <div class="tool-title">
              {{ item.title }}
            </div>
          </div>
        </div>
      </div>
    </el-card>
  </div>
</template>
<script setup>
@@ -84,86 +73,89 @@
const router = useRouter();
// 获取列表
const toolList = ref([
    {
        id: 1,
        title: "在线培训",
        iconPath: new URL("@/assets/icons/icon1.png", import.meta.url).href,
        path: "/train",
    },
    {
        id: 2,
        title: "我的考试",
        iconPath: new URL("@/assets/icons/icon2.png", import.meta.url).href,
        path: "/exam-list",
    },
    {
        id: 3,
        title: "我的成绩",
        iconPath: new URL("@/assets/icons/icon2.png", import.meta.url).href,
        path: "/grade-list",
    },
  {
    id: 1,
    title: "在线培训",
    iconPath: new URL("@/assets/icons/icon1.png", import.meta.url).href,
    path: "/train",
  },
  {
    id: 2,
    title: "我的考试",
    iconPath: new URL("@/assets/icons/icon2.png", import.meta.url).href,
    path: "/exam-list",
  },
  {
    id: 3,
    title: "我的成绩",
    iconPath: new URL("@/assets/icons/icon2.png", import.meta.url).href,
    path: "/grade-list",
  },
]);
// 获取颜色
const getColor = computed(() => {
    return randomColor();
  return randomColor();
});
// 获取用户信息
const getFirstName = computed(() => {
    return userInfo.value.realName && userInfo.value.realName[0];
  return userInfo.value.realName && userInfo.value.realName[0];
});
// 点击事件
const toolClick = (item) => {
    if (item.path) {
        router.push(item.path);
    }
  if (item.path) {
    router.push(item.path);
  }
};
// 用户选项
const dropdownRef = ref();
// 退出登录
const quit = () => {
    logout()
        .then(() => {
            router.push("/login").then(() => {
                userStore.setUserInfo(null);
                localStorage.clear("user");
            });
        })
        .catch((err) => {
            console.log("退出登录失败,失败原因;", err);
        });
  logout()
    .then(() => {
      router.push("/login").then(() => {
        userStore.setUserInfo(null);
        localStorage.clear("user");
      });
    })
    .catch((err) => {
      console.log("退出登录失败,失败原因;", err);
    });
};
const goPersonalCenter = () => {
  router.push("/personal-center");
};
</script>
<style lang="scss" scoped>
.card {
    border-radius: 30px;
  border-radius: 30px;
}
.avatar-content {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}
.avatar-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.avatar-container {
    cursor: pointer;
    color: var(--el-color-primary);
    display: flex;
    align-items: center;
  cursor: pointer;
  color: var(--el-color-primary);
  display: flex;
  align-items: center;
}
.example-showcase .el-dropdown-link {
    cursor: pointer;
    color: var(--el-color-primary);
    display: flex;
    align-items: center;
  cursor: pointer;
  color: var(--el-color-primary);
  display: flex;
  align-items: center;
}
</style>
src/views/personal-center/index.vue
New file
@@ -0,0 +1,195 @@
<template>
  <div
    class="exam-list-container w-screen h-screen bg-slate-50 flex flex-col items-center"
  >
    <NormalHeader class="shrink-0"></NormalHeader>
    <div class="list-container container grow relative">
      <div
        class="personal-center-box list-content absolute top-0 bottom-0 left-0 right-0 py-4"
      >
        <div class="information">
          <el-card class="h-full" :body-style="{ height: '50%' }">
            <div
              class="card-wrapper w-full h-full flex flex-col px-8 box-border"
            >
              <div>个人信息</div>
              <div class="img-box">
                <input
                  type="file"
                  @change="changeHeadPortrait"
                  accept=".jpg, .png"
                  style="display: none"
                  ref="fileHeadPortrait"
                  id="fileHeadPortrait"
                />
                  <img
                    class="img"
                    id="headPortrait"
                    @click="uploadImage"
                    :src="'api/files/' + userData.imagePath"
                  />
                <span>{{ userData.userName }}</span>
              </div>
              <div>
                <el-form label-width="auto" style="max-width: 600px">
                  <el-form-item label="姓名">
                    {{ userData.realName }}
                  </el-form-item>
                  <el-form-item label="班级">
                    {{ userData.className.join(",") }}
                  </el-form-item>
                  <el-form-item label="注册时间">
                    {{ timestampToDate(userData.createTime) }}
                  </el-form-item>
                </el-form>
              </div>
            </div>
          </el-card>
        </div>
        <div class="list-wrapper w-full h-full">
          <el-card class="h-full" :body-style="{ height: '100%' }">
            <el-tabs v-model="activeName" class="demo-tabs">
              <el-tab-pane label="资料修改" name="information">
                <el-form
                  :model="informationForm"
                  label-width="auto"
                  style="max-width: 600px"
                >
                  <el-form-item label="真实姓名">
                    <el-input v-model="informationForm.name" />
                  </el-form-item>
                  <el-form-item label="年龄">
                    <el-input v-model="informationForm.age" />
                  </el-form-item>
                  <el-form-item label="性别">
                    <el-select
                      v-model="informationForm.sex"
                      style="width: 100px"
                    >
                      <el-option label="男" value="1" />
                      <el-option label="女" value="2" />
                    </el-select>
                  </el-form-item>
                  <el-form-item label="出生年月">
                    <el-date-picker
                      v-model="informationForm.birthDay"
                      type="date"
                      placeholder="Pick a day"
                      :size="size"
                    />
                  </el-form-item>
                  <el-form-item label="手机">
                    <el-input
                      v-model="informationForm.phone"
                    /> </el-form-item></el-form
              ></el-tab-pane>
              <el-tab-pane label="密码修改" name="password">
                <el-form
                  :model="passwordForm"
                  label-width="auto"
                  style="max-width: 600px"
                >
                  <el-form-item label="旧密码">
                    <el-input v-model="passwordForm.name" />
                  </el-form-item>
                  <el-form-item label="新密码">
                    <el-input v-model="passwordForm.name" />
                  </el-form-item>
                  <el-form-item label="确认密码">
                    <el-input v-model="passwordForm.name" />
                  </el-form-item>
                </el-form>
              </el-tab-pane>
            </el-tabs>
          </el-card>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup>
import { ref } from "vue";
import { uploadImg } from "@/api/modules/personalCenter.js";
import { ElMessage } from "element-plus";
const activeName = ref("information");
const userData = ref(JSON.parse(localStorage.getItem("user")).userInfo);
const informationForm = ref({
  name: userData.value.realName,
  sex: userData.value.sex,
  age: userData.value.age,
  phone: userData.value.phone,
  birthDay: userData.value.birthDay,
});
const passwordForm = ref({ name: "" });
//头像上传
let formData = new FormData();
const uploadImage = () => {
  let logoFile = document.getElementById("fileHeadPortrait");
  if (logoFile) {
    logoFile.click();
  }
};
const changeHeadPortrait = (e) => {
  // let platformLogo = document.getElementById("headPortrait");
  if (e.target.files[0]) {
    // let reader = new FileReader();
    // reader.onload = function (e) {
    //   if (platformLogo) {
    //     platformLogo.setAttribute("src", e.target.result);
    //   }
    // };
    // reader.readAsDataURL(e.target.files[0]);
    formData.set("file", e.target.files[0]);
    uploadImg(formData).then(
      () => {
        ElMessage({
          showClose: true,
          message: "上传成功",
          type: "success",
        });
        // window.location.reload();
      },
      (err) => {
        ElMessage.error(err.data.errorMsg);
      }
    );
  }
};
function timestampToDate(timestamp) {
  const date = new Date(timestamp); // 将时间戳转换为Date对象
  const options = { year: "numeric", month: "long", day: "numeric" }; // 定义日期格式
  return new Intl.DateTimeFormat("zh-CN", options).format(date); // 使用Intl.DateTimeFormat进行格式化
}
</script>
<style lang="scss" scoped>
:deep(.el-tabs__nav-wrap:after) {
  display: none;
}
.personal-center-box {
  display: flex;
  justify-content: space-between;
}
.information {
  width: 500px;
  margin-right: 20px;
}
.img-box {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  margin-top: 30px;
  margin-bottom: 30px;
}
.img {
  height: 150px;
  width: 150px;
  border-radius: 100px;
  overflow: hidden;
  object-fit: cover;
}
</style>
vite.config.js
@@ -30,7 +30,7 @@
        proxy: {
            '/api': {
                // target: 'http://192.168.3.88:8000',
                target: 'http://42.193.1.25:8000',
                target: 'http://192.168.3.64:8000',
                changeOrigin: true,
            }
        }