shmily1213 hace 4 semanas
padre
commit
706e11fb6a
Se han modificado 30 ficheros con 597 adiciones y 206 borrados
  1. 41 3
      src/api/modules/study.ts
  2. 40 0
      src/common/enum.ts
  3. 13 4
      src/components/ie-button/ie-button.vue
  4. 56 0
      src/components/ie-page/components/vip-popup.vue
  5. 52 3
      src/components/ie-page/ie-page.vue
  6. 20 12
      src/composables/useExamType.ts
  7. 9 4
      src/pages.json
  8. 4 0
      src/pagesMain/pages/index/index.vue
  9. 9 0
      src/pagesMain/pages/me/components/me-info.vue
  10. 1 1
      src/pagesMain/pages/me/components/me-menu.vue
  11. 9 37
      src/pagesStudy/components/knowledge-table.vue
  12. 47 46
      src/pagesStudy/components/practice-table.vue
  13. 10 27
      src/pagesStudy/components/video-table.vue
  14. 9 3
      src/pagesStudy/pages/index/compoentns/index-banner.vue
  15. 43 12
      src/pagesStudy/pages/index/index.vue
  16. 16 10
      src/pagesStudy/pages/study-history/components/exam-history-student.vue
  17. 11 9
      src/pagesStudy/pages/study-history/components/exam-history.vue
  18. 12 3
      src/pagesStudy/pages/study-history/components/knowledge-history-student.vue
  19. 4 4
      src/pagesStudy/pages/study-history/components/knowledge-history.vue
  20. 1 1
      src/pagesStudy/pages/study-history/components/practice-history-student.vue
  21. 4 3
      src/pagesStudy/pages/study-history/components/practice-history.vue
  22. 10 1
      src/pagesStudy/pages/study-history/components/video-history-student.vue
  23. 4 4
      src/pagesStudy/pages/study-history/components/video-history.vue
  24. 1 1
      src/pagesStudy/pages/study-history/study-history.vue
  25. 11 9
      src/pagesSystem/pages/edit-student-profile/edit-student-profile.vue
  26. 78 0
      src/pagesSystem/pages/edit-teacher-profile/edit-teacher-profile.vue
  27. 41 0
      src/store/userStore.ts
  28. 12 1
      src/types/injectionSymbols.ts
  29. 27 7
      src/types/study.ts
  30. 2 1
      src/types/user.ts

+ 41 - 3
src/api/modules/study.ts

@@ -1,6 +1,6 @@
-import { ApiResponse } from "@/types";
+import { ApiResponse, ApiResponseList } from "@/types";
 import flyio from "../flyio";
-import { ApiQuestion, DirectedSchool, Examinee, ExamPaper, ExamPaperSubmit, GetExamPaperRequestDTO, Knowledge, KnowledgeListRequestDTO, OpenExamineeRequestDTO, SimulationTestInfo, StudyPlan, Subject, SubjectListRequestDTO } from "@/types/study";
+import { ApiQuestion, DirectedSchool, Examinee, ExamPaper, ExamPaperSubmit, GetExamPaperRequestDTO, Knowledge, KnowledgeListRequestDTO, KnowledgeRecord, OpenExamineeRequestDTO, SimulatedRecord, SimulationTestInfo, StudyPlan, Subject, SubjectListRequestDTO, VideoStudyRecord } from "@/types/study";
 
 /**
  * 获取学习计划
@@ -129,4 +129,42 @@ export function collectQuestion(questionId: number) {
  */
 export function cancelCollectQuestion(questionId: number) {
   return flyio.post('/front/questions/cancelCollect', null, { params: { questionId } }) as Promise<ApiResponse<any>>;
-}
+}
+
+/**
+ * 获取知识点学习记录
+ * @param params 
+ * @returns 
+ */
+export function getKnowledgeRecord() {
+  return flyio.get('/front/student/record/knowledge') as Promise<ApiResponseList<KnowledgeRecord>>;
+}
+
+
+/**
+ * 获取模拟考试记录
+ * @param params 
+ * @returns 
+ */
+export function getSimulatedRecord() {
+  return flyio.get('/front/student/record/simulated') as Promise<ApiResponse<SimulatedRecord[]>>;
+}
+
+
+/**
+ * 获取计划学习记录
+ * @param params 
+ * @returns 
+ */
+export function getPlanStudyRecord(params: {year: number, monthSeq: number, weekSeq: number}) {
+  return flyio.get('/front/student/record/planStudy', params) as Promise<ApiResponse<any>>;
+}
+
+/**
+ * 获取视频学习记录
+ * @param params 
+ * @returns 
+ */
+export function getVideoStudyRecord() {
+  return flyio.get('/front/student/record/video') as Promise<ApiResponse<VideoStudyRecord>>;
+}

+ 40 - 0
src/common/enum.ts

@@ -126,4 +126,44 @@ export enum EnumQuestionType {
    * 阅读题
    */
   OTHER = 99
+}
+
+/**
+ * 用户类型
+ */
+export enum EnumUserType {
+  /**
+   * 系统用户
+   */
+  SYSTEM = '00',
+  /**
+   * 学生
+   */
+  STUDENT = '01',
+  /**
+   * 教师
+   */
+  TEACHER = '11',
+  /**
+   * 代理商
+   */
+  AGENT = '10',
+  /**
+   * 机构
+   */
+  AGENCY = '12'
+}
+
+/**
+ * 考试类型
+ */
+export enum EnumExamType {
+  /**
+   * 模拟考试
+   */
+  SIMULATED = 'simulated',
+  /**
+   * 组卷作业
+   */
+  HOMEWORK = 'homework'
 }

+ 13 - 4
src/components/ie-button/ie-button.vue

@@ -14,7 +14,7 @@ const props = defineProps({
     default: false
   },
   type: {
-    type: String as PropType<'primary' | 'secondary'>,
+    type: String as PropType<'primary' | 'secondary' | 'info'>,
     default: 'primary'
   },
   size: {
@@ -35,11 +35,19 @@ const handleClick = () => {
 <style lang="scss" scoped>
 .ie-button {
   height: fit-content !important;
-  background: linear-gradient(to right, #31A0FC, #0088FE);
   line-height: 1;
   font-weight: 800;
+  @apply relative rounded-full text-center;
+}
+
+.ie-button-primary {
+  background: linear-gradient(to right, #31A0FC, #0088FE);
   box-shadow: 0 5px 8px 0px rgba(49, 160, 252, 0.6);
-  @apply relative rounded-full bg-primary text-white text-center;
+  @apply text-white
+}
+
+.ie-button-info {
+  @apply bg-back text-fore-title;
 }
 
 .ie-button-large {
@@ -69,7 +77,8 @@ const handleClick = () => {
 }
 
 .button-hover {
-  background: linear-gradient(to right, #2a8dde, #007ae5);
+  // background: linear-gradient(to right, #2a8dde, #007ae5);
+  @apply opacity-80;
 }
 
 .is-disabled:hover {

+ 56 - 0
src/components/ie-page/components/vip-popup.vue

@@ -0,0 +1,56 @@
+<template>
+  <!-- #ifdef H5 -->
+  <teleport to="body">
+    <!-- #endif -->
+    <!-- #ifdef MP-WEIXIN -->
+    <root-portal externalClass="theme-ie">
+      <!-- #endif -->
+      <uv-popup ref="popupRef" mode="center" :close-on-click-overlay="true" :closeable="false" :round="16">
+        <view class="theme-ie w-[88vw] box-border px-54 pt-60 pb-80 bg-white text-center relative overflow-hidden">
+          <view class="relative z-1">
+            <view class="text-40 text-fore-title font-bold">
+              <text>当前无</text>
+              <text class="text-primary">VIP</text>
+              <text>权限</text>
+            </view>
+            <view class="mt-10 text-32 text-fore-light">开通会员,立即畅享专属权益与服务</view>
+            <ie-button custom-class="mt-60" @click="handleBuy">升级VIP权限</ie-button>
+            <ie-button type="info" custom-class="mt-40" @click="handleActivate">已线下购买,去激活</ie-button>
+          </view>
+          <ie-image :is-oss="true" src="/study-bg14.png" custom-class="absolute bottom-0 left-0 w-full h-full z-0"
+            mode="aspectFill" />
+        </view>
+      </uv-popup>
+      <!-- #ifdef MP-WEIXIN -->
+    </root-portal>
+    <!-- #endif -->
+    <!-- #ifdef H5 -->
+  </teleport>
+  <!-- #endif -->
+</template>
+<script lang="ts" setup>
+import { useTransferPage } from '@/hooks/useTransferPage';
+const { transferTo } = useTransferPage();
+const popupRef = ref();
+const open = () => {
+  console.log('open');
+  popupRef.value.open();
+}
+const close = () => {
+  popupRef.value.close();
+}
+const handleBuy = () => {
+  close();
+}
+const handleActivate = () => {
+  close();
+  transferTo('/pagesSystem/pages/card-verify/card-verify', {
+    data: {}
+  });
+}
+defineExpose({
+  open,
+  close
+});
+</script>
+<style lang="scss" scoped></style>

+ 52 - 3
src/components/ie-page/ie-page.vue

@@ -5,6 +5,9 @@
     <view class="ie-page-content">
       <slot></slot>
     </view>
+    <view class="ie-page-popup">
+      <vip-popup ref="vipPopupRef" />
+    </view>
     <view v-if="$slots.tabbar" class="ie-page-tabbar">
       <slot name="tabbar"></slot>
     </view>
@@ -12,11 +15,14 @@
 </template>
 
 <script lang="ts" setup>
+import { CLOSE_VIP_POPUP, OPEN_VIP_POPUP } from '@/types/injectionSymbols';
+import VipPopup from './components/vip-popup.vue';
 defineOptions({
   name: 'ie-page',
-  options: {
-    virtualHost: true
-  }
+  // 注意:virtualHost: true 会导致 provide/inject 失效
+  // options: {
+  //   virtualHost: true
+  // }
 });
 const props = defineProps({
   safeAreaInsetBottom: {
@@ -32,6 +38,49 @@ const props = defineProps({
     default: false
   }
 });
+// 为 ref 添加类型定义
+const vipPopupRef = ref<InstanceType<typeof VipPopup>>();
+
+// 添加安全检查
+const showVipPopup = () => {
+  console.log('showVipPopup called, vipPopupRef.value:', vipPopupRef.value);
+  if (vipPopupRef.value) {
+    vipPopupRef.value.open();
+  } else {
+    console.error('vipPopupRef is not initialized');
+  }
+}
+
+const closeVipPopup = () => {
+  console.log('closeVipPopup called');
+  if (vipPopupRef.value) {
+    vipPopupRef.value.close();
+  } else {
+    console.error('vipPopupRef is not initialized');
+  }
+}
+
+// provide 给 ie-page 内部的子组件(slot 内容)使用
+console.log('ie-page setup: providing OPEN_VIP_POPUP and CLOSE_VIP_POPUP');
+provide(OPEN_VIP_POPUP, showVipPopup);
+provide(CLOSE_VIP_POPUP, closeVipPopup);
+console.log('ie-page setup: provided successfully');
+
+// 暴露方法给父组件通过 ref 调用
+defineExpose({
+  showVipPopup,
+  closeVipPopup
+});
+
+onMounted(() => {
+  console.log('ie-page mounted, vipPopupRef.value:', vipPopupRef.value);
+  // uni.$on('showVipPopup', showVipPopup);
+  // uni.$on('closeVipPopup', closeVipPopup);
+});
+onBeforeUnmount(() => {
+  // uni.$off('showVipPopup', showVipPopup);
+  // uni.$off('closeVipPopup', closeVipPopup);
+});
 </script>
 
 <style lang="scss" scoped>

+ 20 - 12
src/composables/useExamType.ts

@@ -3,15 +3,12 @@ import { useDictStore } from "@/store/dictStore";
 import { useAppStore } from "@/store/appStore";
 import { DictItem } from "@/types";
 import { getExamMajors, getExamTypes, getGraduateYears, getProvinces } from "@/api/modules/system";
+import { StudentExamInfo, UserInfo } from "@/types/user";
 const dictStore = useDictStore();
 const appStore = useAppStore();
+
 export const useExamType = () => {
-  const form = ref({
-    location: '',
-    examType: '',
-    endYear: undefined,
-    majorType: '',
-  })
+  const form = ref<Partial<Pick<StudentExamInfo, 'location' | 'examType' | 'endYear' | 'majorType'>>>({})
   const provinceList = ref<DictItem[]>([]);
   const examTypeList = ref<DictItem[]>([]);
   const examMajorList = ref<DictItem[]>([]);
@@ -21,16 +18,27 @@ export const useExamType = () => {
     provinceList.value = data;
   }
   const loadExamTypeData = async () => {
-    const { data } = await getExamTypes(form.value.location);
-    examTypeList.value = data;
+    if (form.value.location) {
+      const { data } = await getExamTypes(form.value.location);
+      examTypeList.value = data;
+    }
+
   }
   const loadExamMajorData = async () => {
-    const { data } = await getExamMajors(form.value.location, form.value.examType);
-    examMajorList.value = data;
+    if (form.value.location && form.value.examType) {
+      const { data } = await getExamMajors(form.value.location, form.value.examType);
+      examMajorList.value = data;
+    } else {
+      examMajorList.value = [];
+    }
   }
   const loadGraduateYearData = async () => {
-    const { data } = await getGraduateYears(form.value.location, form.value.examType);
-    endYearList.value = data;
+    if (form.value.location && form.value.examType) {
+      const { data } = await getGraduateYears(form.value.location, form.value.examType);
+      endYearList.value = data;
+    } else {
+      endYearList.value = [];
+    }
   }
   watch(() => form.value.location, (val) => {
     form.value.examType = '';

+ 9 - 4
src/pages.json

@@ -543,10 +543,15 @@
           }
         },
         {
-          "path" : "/pages/edit-profile/edit-profile",
-          "style" : 
-          {
-            "navigationBarTitleText" : ""
+          "path": "pages/edit-student-profile/edit-student-profile",
+          "style": {
+            "navigationBarTitleText": ""
+          }
+        },
+        {
+          "path": "pages/edit-teacher-profile/edit-teacher-profile",
+          "style": {
+            "navigationBarTitleText": ""
           }
         }
       ]

+ 4 - 0
src/pagesMain/pages/index/index.vue

@@ -69,6 +69,9 @@ const checkProvinceInfo = () => {
     popupRef.value.open();
   }
 }
+const checkTeacherInfo = async () => {
+  const isTeacherInfoComplete = await userStore.checkInfoComplete();
+}
 const handleChangeLocation = () => {
   popupRef.value.open();
 }
@@ -83,6 +86,7 @@ onPageScroll((e) => {
 onShow(() => {
   setTimeout(() => {
     checkProvinceInfo();
+    // checkTeacherInfo();
   }, 500);
   isHide.value = false;
   setTimeout(() => {

+ 9 - 0
src/pagesMain/pages/me/components/me-info.vue

@@ -49,6 +49,15 @@ const vipInfo = computed(() => userStore.vipInfo);
 const handleHeaderClick = async () => {
   // 不询问直接跳转登录
   const isLogin = await userStore.checkLogin({ askToLogin: false });
+  if (isLogin) {
+    const isTeacherInfoComplete = await userStore.checkInfoComplete();
+    // if (userStore.isTeacher) {
+      
+    //   if (!isTeacherInfoComplete) {
+    //     transferTo('/pagesSystem/pages/edit-teacher-profile/edit-teacher-profile');
+    //   }
+    // }
+  }
 }
 const handleSettingClick = async () => {
   transferTo('/pagesOther/pages/personal-center/setting/setting');

+ 1 - 1
src/pagesMain/pages/me/components/me-menu.vue

@@ -13,7 +13,7 @@
     <view class="-mt-10 rounded-8 py-20">
       <uv-cell-group :border="false">
         <uv-cell isLink :cellStyle="cellStyle"
-          @click="handleNavigate('/pagesSystem/pages/edit-profile/edit-profile', '基本资料')">
+          @click="handleNavigate('/pagesSystem/pages/edit-student-profile/edit-student-profile', '基本资料')">
           <template #title>
             <view class="flex items-center gap-x-10">
               <ie-image src="/static/personal/icon_jibenziliao@2x.png" custom-class="w-34 h-34" />

+ 9 - 37
src/pagesStudy/components/knowledge-table.vue

@@ -1,10 +1,10 @@
 <template>
   <view class="p-30">
     <ie-table :table-columns="tableColumns" :table-config="tableConfig" :data="data">
-      <template #knowledgeName="{ item }">
+      <template #name="{ item }">
         <view class="">
-          <text class="leading-38">{{ item.knowledgeName }}</text>
-          <text
+          <text class="leading-38">{{ item.name }}</text>
+          <text v-if="item.directed"
             class="ml-10 bg-[#F0FDF4] text-[#22C55E] border border-solid border-[#22C55E] text-20 rounded-4 px-10 py-2">定向</text>
         </view>
       </template>
@@ -18,9 +18,9 @@
 import { Study, TableColumnConfig, TableConfig } from '@/types';
 
 const props = defineProps({
-  studentId: {
-    type: Number,
-    required: true
+  data: {
+    type: Array as PropType<Study.KnowledgeRecord[]>,
+    default: () => []
   }
 });
 const tableConfig: TableConfig = {
@@ -32,10 +32,10 @@ const tableConfig: TableConfig = {
 }
 const tableColumns: TableColumnConfig[] = [
   {
-    prop: 'knowledgeName',
+    prop: 'name',
     label: '知识点',
     flex: 2,
-    slot: 'knowledgeName',
+    slot: 'name',
     headerAlign: 'center',
     align: 'left'
   },
@@ -46,39 +46,11 @@ const tableColumns: TableColumnConfig[] = [
     slot: 'rate'
   },
   {
-    prop: 'questionNum',
+    prop: 'total',
     label: '题量',
     flex: 1
   }
 ]
-const data = ref<Study.StudyRecord[]>([]);
-const loadData = async () => {
-  // const res = await knowRecords({
-  //   studentId: props.studentId
-  // });
-  // data.value = res.data;
-  // 模拟数据
-  data.value = [
-    { id: 1, knowledgeName: 'CA6140型普通车床电气控制线路的安装接线与故障检修', rate: 100, questionNum: 10 },
-    { id: 2, knowledgeName: 'Z3040型摇臂钻床电气控制线路的安装接线与故障检修', rate: 100, questionNum: 10 },
-    { id: 3, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-    { id: 4, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-    { id: 5, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-    { id: 6, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-    { id: 7, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-    { id: 8, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-    { id: 9, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-    { id: 10, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-    { id: 11, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-    { id: 12, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-    { id: 13, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-    { id: 14, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-    { id: 15, knowledgeName: '零件生产过程基础知识', rate: 100, questionNum: 10 },
-  ];
-}
-onMounted(() => {
-  loadData();
-});
 </script>
 <style lang="scss" scoped>
 .table-header {

+ 47 - 46
src/pagesStudy/components/practice-table.vue

@@ -84,7 +84,7 @@
 
             <uni-calendar ref="calendarRef" :insert="true" :lunar="false" :readonly="true" :showMonth="false"
               :sundayFirst="false" :highlightToday="false" :showToolbar="false" :displayMode="displayMode"
-              :weekNumber="calendarWeekNumber" :selected="selected" :date="currentDate"
+              :weekNumber="calendarWeekNumber" :selected="[]" :date="currentDate"
               @change-week="handleCalendarWeekChange" @monthSwitch="handleCalendarMonthSwitch">
               <template #calendar-item="{ weeks }">
                 <view class="calendar-item" :class="{
@@ -153,18 +153,12 @@ const form = ref({
 
 // 年份列表(当前年份前后各2年,但不能超过当前年份)
 const yearList = computed(() => {
-  const currentYearValue = currentYear.value;
-  const today = new Date();
-  const currentYearToday = today.getFullYear();
-  const years = [];
-
-  for (let i = currentYearValue - 2; i <= currentYearToday; i++) {
-    years.push({
-      label: i.toString(),
-      value: i.toString()
-    });
-  }
-  return years;
+  return [
+    {
+      label: '2025',
+      value: '2025'
+    }
+  ];
 });
 
 // 月份列表(不能超过当前月份)
@@ -188,30 +182,30 @@ const monthList = computed(() => {
 
 // 周列表 - 根据当前月份动态生成,不显示未来周数
 const weekList = computed(() => {
-  const weeks = [];
-  const totalWeeks = currentWeekRange.value?.totalWeeks || 1;
-
-  // 计算当前周数
-  const today = new Date();
-  const current = new Date(currentYear.value, currentMonth.value - 1, 1);
-  const isCurrentMonth = current.getFullYear() === today.getFullYear() &&
-    current.getMonth() === today.getMonth();
-
-  let maxWeek = totalWeeks;
-  if (isCurrentMonth) {
-    // 当前月份,只显示到当前周
-    const firstDayOfMonth = new Date(current.getFullYear(), current.getMonth(), 1);
-    const firstWeekday = firstDayOfMonth.getDay() === 0 ? 7 : firstDayOfMonth.getDay();
-    const currentWeekInMonth = Math.ceil((today.getDate() + firstWeekday - 1) / 7);
-    maxWeek = Math.min(totalWeeks, currentWeekInMonth);
-  }
-
-  for (let i = 1; i <= maxWeek; i++) {
-    weeks.push({
-      label: `第${i}周`,
-      value: i.toString()
-    });
-  }
+  const weeks: { label: string, value: string }[] = [];
+  // const totalWeeks = currentWeekRange.value?.totalWeeks || 1;
+
+  // // 计算当前周数
+  // const today = new Date();
+  // const current = new Date(currentYear.value, currentMonth.value - 1, 1);
+  // const isCurrentMonth = current.getFullYear() === today.getFullYear() &&
+  //   current.getMonth() === today.getMonth();
+
+  // let maxWeek = totalWeeks;
+  // if (isCurrentMonth) {
+  //   // 当前月份,只显示到当前周
+  //   const firstDayOfMonth = new Date(current.getFullYear(), current.getMonth(), 1);
+  //   const firstWeekday = firstDayOfMonth.getDay() === 0 ? 7 : firstDayOfMonth.getDay();
+  //   const currentWeekInMonth = Math.ceil((today.getDate() + firstWeekday - 1) / 7);
+  //   maxWeek = Math.min(totalWeeks, currentWeekInMonth);
+  // }
+
+  // for (let i = 1; i <= maxWeek; i++) {
+  //   weeks.push({
+  //     label: `第${i}周`,
+  //     value: i.toString()
+  //   });
+  // }
   return weeks;
 });
 
@@ -267,7 +261,8 @@ const calendarButtonClass = computed(() => {
 const options1 = computed(() => {
   return {
     title: {
-      text: statistics.value.totalQuestions.toString(),
+      // text: statistics.value.totalQuestions.toString(),
+      text: '0',
       subtext: '{a|刷题总量}',
       left: 'center',
       top: '34%',
@@ -322,7 +317,8 @@ const options1 = computed(() => {
 });
 
 const options2 = computed(() => {
-  const accuracy = Math.round(statistics.value.averageAccuracy * 100);
+  // const accuracy = Math.round(statistics.value.averageAccuracy * 100);
+  const accuracy = 0;
 
   return {
     title: {
@@ -404,19 +400,24 @@ const tableColumns = ref<TableColumnConfig[]>([
 
 // 表格数据(从 selected 数据转换)
 const tableDate = computed(() => {
-  return selected.value.map(item => ({
-    date: item.date.replace(/-/g, '.'),
-    questionNum: item.questionNum, // 使用 composable 中的数据
-    correctNum: Math.round(item.info * 100) + '%' // 四舍五入到整数
-  }));
+  // return selected.value.map(item => ({
+  //   date: item.date.replace(/-/g, '.'),
+  //   questionNum: item.questionNum, // 使用 composable 中的数据
+  //   correctNum: Math.round(item.info * 100) + '%' // 四舍五入到整数
+  // }));
+  return [];
 });
 
 // 练习天数统计
-const practiceDate = computed(() => statistics.value.practiceDays);
+const practiceDate = computed(() => {
+  // return statistics.value.practiceDays;
+  return 0;
+});
 
 // 累计刷题天数统计
 const totalPracticeDays = computed(() => {
-  return statistics.value.totalPracticeDays;
+  // return statistics.value.totalPracticeDays;
+  return 0;
 });
 
 // 日历标题

+ 10 - 27
src/pagesStudy/components/video-table.vue

@@ -1,7 +1,7 @@
 <template>
   <view>
     <view class="mx-30 mt-20 mb-40 grid grid-cols-2 gap-x-26 gap-y-26">
-      <view v-for="item in stat" :key="item.name" class="relative p-30 rounded-10 bg-back">
+      <view v-for="item in stat" :key="item.value" class="relative p-30 rounded-10 bg-back">
         <ie-image :src="item.icon" custom-class="w-52 h-70 absolute top-20 right-25" />
         <view class="text-30 text-fore-light font-bold">{{ item.name }}</view>
         <view class="mt-4">
@@ -24,20 +24,22 @@
 </template>
 <script lang="ts" setup>
 import { TableColumnConfig } from '@/types';
-import { StudentVideoRecord } from '@/types/study';
-
+import * as Study from '@/types/study';
+const props = defineProps<{
+  data: Study.VideoStudyRecord;
+}>();
 
 const stat = ref([
   {
     name: '视频总数',
     unit: '课时',
-    value: '98',
+    value: '0',
     icon: '/pagesStudy/static/image/icon-video.png'
   },
   {
     name: '观看时长',
     unit: '分钟',
-    value: '759',
+    value: '0',
     icon: '/pagesStudy/static/image/icon-time.png'
   },
 ])
@@ -58,30 +60,11 @@ const tableColumns = ref<TableColumnConfig[]>([
     flex: 0.5,
   }
 ])
-const data = ref<StudentVideoRecord[]>([
-  {
-    id: 1,
-    name: '充分条件与必要条件的应用充分条件与必要条件',
-    date: '2025-01-01',
-    duration: 100
-  },
-  {
-    id: 2,
-    name: '视频2',
-    date: '2025-01-02',
-    duration: 200
-  },
-  {
-    id: 3,
-    name: '视频3',
-    date: '2025-01-03',
-    duration: 300
-  }
-])
+const data = ref<Study.StudentVideoRecord[]>([])
 const emit = defineEmits<{
-  rowClick: [row: StudentVideoRecord]
+  rowClick: [row: Study.StudentVideoRecord]
 }>();
-const handleRowClick = (row: StudentVideoRecord) => {
+const handleRowClick = (row: Study.StudentVideoRecord) => {
   emit('rowClick', row);
 }
 </script>

+ 9 - 3
src/pagesStudy/pages/index/compoentns/index-banner.vue

@@ -13,7 +13,7 @@
         </view>
         <ie-image :is-oss="true" src="/study-bg3.png" customClass="w-92 h-92" />
       </view>
-      <view class="flex-1 rounded-12 bg-[#FFF6F0] py-40 pl-22 pr-8 flex items-center">
+      <view class="flex-1 rounded-12 bg-[#FFF6F0] py-40 pl-22 pr-8 flex items-center" @click="handleTest">
         <view class="flex-1">
           <view class="text-30 text-fore-title font-bold flex items-center">
             <text class="mr-2">学情报告</text>
@@ -29,8 +29,9 @@
 <script lang="ts" setup>
 import { useTransferPage } from '@/hooks/useTransferPage';
 import { getStudyPlan, getDirectedSchool } from '@/api/modules/study';
+import { OPEN_VIP_POPUP } from '@/types/injectionSymbols';
 const { transferTo } = useTransferPage();
-
+const openVipPopup = inject(OPEN_VIP_POPUP);
 const handleOpenPlan = async () => {
   const { data } = await getStudyPlan();
   if (data) {
@@ -42,6 +43,11 @@ const handleOpenPlan = async () => {
   } else {
     transferTo('/pagesStudy/pages/study-plan-edit/study-plan-edit');
   }
-}
+};
+const handleTest = () => {
+  if (openVipPopup) {
+    openVipPopup();
+  }
+};
 </script>
 <style lang="scss" scoped></style>

+ 43 - 12
src/pagesStudy/pages/index/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <ie-page bg-color="white">
+  <ie-page ref="iePageRef" bg-color="white">
     <ie-navbar>
       <template #headerLeft>
         <view class="flex items-center">
@@ -63,8 +63,8 @@
     <index-menu />
     <index-banner />
     <view class="h-16 bg-back my-32"></view>
-    <index-test />
-    <index-exam-record />
+    <!-- <index-test />
+    <index-exam-record /> -->
   </ie-page>
 </template>
 
@@ -78,30 +78,61 @@ import { useUserStore } from '@/store/userStore';
 import { useTransferPage } from '@/hooks/useTransferPage';
 import { getDirectedSchool } from '@/api/modules/study';
 import { DirectedSchool } from '@/types/study';
+import IePage from '@/components/ie-page/ie-page.vue';
+
 const { transferTo } = useTransferPage();
 const userStore = useUserStore();
 
+// 通过 ref 获取 ie-page 组件实例
+const iePageRef = ref<InstanceType<typeof IePage>>();
+
 const directedSchoolData = ref<DirectedSchool[]>([]);
 const firstDirectedSchool = computed(() => directedSchoolData.value[0] || {});
 const hasDirectedSchool = computed(() => directedSchoolData.value.length > 0);
 
 const handlePracticeAll = () => {
-  transferTo('/pagesStudy/pages/knowledge-practice/knowledge-practice', {
-    data: {
-      directed: false
+  console.log('handlePracticeAll called, isVip:', userStore.isVip);
+  if (userStore.isVip) {
+    transferTo('/pagesStudy/pages/knowledge-practice/knowledge-practice', {
+      data: {
+        directed: false
+      }
+    });
+  } else {
+    // 通过 ref 调用 ie-page 的方法
+    if (iePageRef.value) {
+      console.log('calling iePageRef.value.showVipPopup()');
+      iePageRef.value.showVipPopup();
+    } else {
+      console.error('iePageRef is not available');
+      uni.$ie.showToast('VIP弹窗功能暂不可用');
     }
-  });
+  }
 }
-const handlePracticeDirected = () => {
+const handlePracticeDirected = async () => {
   if (!hasDirectedSchool.value) {
     uni.$ie.showToast('请先选择定向院校');
     return;
   }
-  transferTo('/pagesStudy/pages/knowledge-practice/knowledge-practice', {
-    data: {
-      directed: true
+  const isVip = await userStore.checkVip();
+  console.log('handlePracticeDirected called, isVip:', isVip);
+  if (isVip) {
+    transferTo('/pagesStudy/pages/knowledge-practice/knowledge-practice', {
+      data: {
+        directed: true
+      }
+    });
+  } else {
+    // 通过 ref 调用 ie-page 的方法
+    if (iePageRef.value) {
+      console.log('calling iePageRef.value.showVipPopup()');
+      iePageRef.value.showVipPopup();
+    } else {
+      console.error('iePageRef is not available');
+      uni.$ie.showToast('VIP弹窗功能暂不可用');
     }
-  });
+  }
+
 }
 const handleSetting = async () => {
   if (hasDirectedSchool.value) {

+ 16 - 10
src/pagesStudy/pages/study-history/components/exam-history-student.vue

@@ -1,30 +1,36 @@
 <template>
   <view class="px-30 pb-30">
-    <view class="sibling-border-top px-20 py-30" v-for="(item, index) in list" :key="index">
+    <view class="sibling-border-top px-20 py-30" v-for="(item, index) in dataList" :key="index">
       <exam-record-item />
     </view>
   </view>
 </template>
 <script lang="ts" setup>
+import { getSimulatedRecord } from '@/api/modules/study';
+import { EnumExamType } from '@/common/enum';
 import examRecordItem from '@/pagesStudy/components/exam-record-item.vue';
+import { Study } from '@/types';
 const props = defineProps({
   examType: {
     type: String,
     default: 'test'
   }
 });
-const list = ref([1,1,1,1,1]);
-const loadData = async () => {
-  // const res = await getExamHistory(userType.value, examType.value);
-  // console.log(res);
-  uni.$ie.showLoading();
-  setTimeout(() => {
-    uni.$ie.hideLoading();
-  }, 1000);
+const dataList = ref<Study.SimulatedRecord[]>([]);
+const loadData = async (type: string) => {
+  dataList.value = [];
+  if (type === EnumExamType.SIMULATED) {
+    const { data } = await getSimulatedRecord();
+    // dataList.value = data;
+  } else {
+
+
+  }
+
 }
 
 watch(() => props.examType, (newVal) => {
-  loadData();
+  loadData(newVal);
 }, {
   immediate: true
 });

+ 11 - 9
src/pagesStudy/pages/study-history/components/exam-history.vue

@@ -1,28 +1,30 @@
 <template>
   <view class="flex-1 min-h-1 bg-white">
     <view class="px-30 py-20 flex gap-x-20">
-      <view class="exam-type-item" :class="{ 'is-active': examType === 'test' }" @click="handleChangeExamType('test')">
+      <view class="exam-type-item" :class="{ 'is-active': examType === EnumExamType.SIMULATED }"
+        @click="handleChangeExamType(EnumExamType.SIMULATED)">
         <ie-image src="/pagesStudy/static/image/icon-exam-test.png" custom-class="w-64 h-60" />
         <view class="exam-type-text">模拟仿真</view>
       </view>
-      <view class="exam-type-item" :class="{ 'is-active': examType === 'homework' }"
-        @click="handleChangeExamType('homework')">
+      <view class="exam-type-item" :class="{ 'is-active': examType === EnumExamType.HOMEWORK }"
+        @click="handleChangeExamType(EnumExamType.HOMEWORK)">
         <ie-image src="/pagesStudy/static/image/icon-exam-homework.png" custom-class="w-64 h-60" />
         <view class="exam-type-text">组卷作业</view>
       </view>
     </view>
-    <exam-history-student v-if="userType === 'student'" :exam-type="examType" />
-    <exam-history-teacher v-else-if="userType === 'teacher'" :exam-type="examType" />
+    <exam-history-student v-if="isStudent" :exam-type="examType" />
+    <exam-history-teacher v-else :exam-type="examType" />
   </view>
 </template>
 <script lang="ts" setup>
-import teacherClassView from '@/pagesStudy/components/teacher-class-view.vue';
+import { EnumExamType } from '@/common/enum';
 import examHistoryStudent from './exam-history-student.vue';
 import examHistoryTeacher from './exam-history-teacher.vue';
-const userType = ref('teacher');
-const examType = ref('test')
+import { useUserStore } from '@/store/userStore';
+const { isStudent } = storeToRefs(useUserStore());
+const examType = ref(EnumExamType.SIMULATED);
 
-const handleChangeExamType = (type: string) => {
+const handleChangeExamType = (type: EnumExamType) => {
   examType.value = type;
 }
 </script>

+ 12 - 3
src/pagesStudy/pages/study-history/components/knowledge-history-student.vue

@@ -1,9 +1,18 @@
 <template>
-  <knowledge-table :student-id="studentId" />
+  <knowledge-table :data="dataList" />
 </template>
 <script lang="ts" setup>
+import { getKnowledgeRecord } from '@/api/modules/study';
 import knowledgeTable from '@/pagesStudy/components/knowledge-table.vue';
-// 从用户信息中取值
-const studentId = ref(1);
+import { Study } from '@/types';
+
+const dataList = ref<Study.KnowledgeRecord[]>([]);
+const loadData = async () => {
+  const { rows } = await getKnowledgeRecord();
+  // dataList.value = rows;
+}
+onLoad(() => {
+  loadData();
+});
 </script>
 <style lang="scss" scoped></style>

+ 4 - 4
src/pagesStudy/pages/study-history/components/knowledge-history.vue

@@ -1,7 +1,7 @@
 <template>
   <view class="bg-white">
-    <knowledge-history-student v-if="type === 'student'" />
-    <teacher-class-view v-else-if="type === 'teacher'">
+    <knowledge-history-student v-if="isStudent" />
+    <teacher-class-view v-else>
       <template #default="{ teachClass }">
         <knowledge-history-teacher :teach-class="teachClass" />
       </template>
@@ -14,9 +14,9 @@ import knowledgeHistoryStudent from './knowledge-history-student.vue';
 import knowledgeHistoryTeacher from './knowledge-history-teacher.vue';
 import { OPEN_KNOWLEDGE_DETAIL } from '@/types/injectionSymbols';
 import { useTransferPage } from '@/hooks/useTransferPage';
+import { useUserStore } from '@/store/userStore';
 const { transferTo } = useTransferPage();
-// const type = ref('student');
-const type = ref('teacher');
+const { isStudent } = storeToRefs(useUserStore());
 
 const openKnowledgeDetail = (id: number, name: string) => {
   transferTo('/pagesStudy/pages/study-knowledge-detail/study-knowledge-detail', {

+ 1 - 1
src/pagesStudy/pages/study-history/components/practice-history-student.vue

@@ -7,6 +7,6 @@
 import practiceTable from '@/pagesStudy/components/practice-table.vue';
 import { useUserStore } from '@/store/userStore';
 const userStore = useUserStore();
-const studentId = ref(userStore.userInfo.id);
+const studentId = ref(userStore.userInfo.userId);
 </script>
 <style lang="scss" scoped></style>

+ 4 - 3
src/pagesStudy/pages/study-history/components/practice-history.vue

@@ -1,7 +1,7 @@
 <template>
   <view class="flex-1 min-h-1 bg-white">
-    <practice-history-student v-if="userType === 'student'" />
-    <teacher-class-view v-else-if="userType === 'teacher'">
+    <practice-history-student v-if="isStudent" />
+    <teacher-class-view v-else>
       <template #default="{ teachClass }">
         <view class="type-wrap">
           <view v-for="item in typeList" :key="item.value" class="type-item"
@@ -24,6 +24,7 @@ import practiceHistoryTeacher from './practice-history-teacher.vue';
 import teacherClassView from '@/pagesStudy/components/teacher-class-view.vue';
 import { OPEN_PRACTICE_DETAIL } from '@/types/injectionSymbols';
 import { useTransferPage } from '@/hooks/useTransferPage';
+import { useUserStore } from '@/store/userStore';
 const { transferTo } = useTransferPage();
 const currentType = ref('rate');
 const typeList = ref([
@@ -51,7 +52,7 @@ const tabList = ref([
     value: 'desc'
   }
 ])
-const userType = ref('teacher');
+const { isStudent } = storeToRefs(useUserStore());
 const handleTypeChange = (value: string) => {
   currentType.value = value;
 }

+ 10 - 1
src/pagesStudy/pages/study-history/components/video-history-student.vue

@@ -1,9 +1,18 @@
 <template>
   <view>
-    <video-table />
+    <video-table :data="dataList" />
   </view>
 </template>
 <script lang="ts" setup>
 import videoTable from '@/pagesStudy/components/video-table.vue';
+import { getVideoStudyRecord } from '@/api/modules/study';
+import * as Study from '@/types/study';
+const dataList = ref<Study.VideoStudyRecord>({} as Study.VideoStudyRecord);
+const loadData = async () => {
+  const res = await getVideoStudyRecord();
+  console.log(res)
+  dataList.value = res.data;
+}
+loadData();
 </script>
 <style lang="scss" scoped></style>

+ 4 - 4
src/pagesStudy/pages/study-history/components/video-history.vue

@@ -1,7 +1,7 @@
 <template>
   <view class="bg-white flex-1">
-    <video-history-student v-if="type === 'student'" />
-    <teacher-class-view v-else-if="type === 'teacher'">
+    <video-history-student v-if="isStudent" />
+    <teacher-class-view v-else>
       <template #default="{ teachClass }">
         <view class="mt-30 w-fit mx-auto">
           <ie-tab :options="tabList" v-model="currentTab" />
@@ -17,9 +17,9 @@ import videoHistoryTeacher from './video-history-teacher.vue';
 import teacherClassView from '@/pagesStudy/components/teacher-class-view.vue';
 import { OPEN_VIDEO_DETAIL } from '@/types/injectionSymbols';
 import { useTransferPage } from '@/hooks/useTransferPage';
+import { useUserStore } from '@/store/userStore';
 const { transferTo } = useTransferPage();
-// const type = ref('student');
-const type = ref('teacher');
+const { isStudent } = storeToRefs(useUserStore());
 const currentTab = ref('asc');
 const tabList = ref([
   {

+ 1 - 1
src/pagesStudy/pages/study-history/study-history.vue

@@ -20,7 +20,7 @@ import examHistory from './components/exam-history.vue'
 import practiceHistory from './components/practice-history.vue'
 import videoHistory from './components/video-history.vue'
 const { baseStickyTop } = useNavbar();
-const current = ref(3);
+const current = ref(0);
 const list = [
   { name: '知识点记录', },
   { name: '试卷记录', },

+ 11 - 9
src/pagesSystem/pages/edit-profile/edit-profile.vue → src/pagesSystem/pages/edit-student-profile/edit-student-profile.vue

@@ -3,13 +3,13 @@
     <ie-navbar title="基本信息" />
     <view class="">
       <uv-form labelPosition="left" :model="form" labelWidth="70px" ref="formRef">
-        <content-card title="考生信息">
-          <uv-form-item label="学生姓名" prop="name" borderBottom>
-            <uv-input v-model="form.nickName" border="none" placeholder="请输入姓名" placeholderClass="text-30"
-              font-size="30rpx" :custom-style="customStyle">
+        <content-card :title="userStore.isStudent ? '考生信息' : '个人信息'">
+          <uv-form-item :label="nameLabel" prop="name" borderBottom>
+            <uv-input v-model="form.nickName" border="none" :readonly="!userStore.isStudent" placeholder="请输入姓名"
+              placeholderClass="text-30" font-size="30rpx" :custom-style="customStyle">
             </uv-input>
           </uv-form-item>
-          <uv-form-item label="手机号码" prop="name" borderBottom>
+          <uv-form-item v-if="userStore.isStudent" label="手机号码" prop="name" borderBottom>
             <uv-input v-model="form.phonenumber" border="none" placeholder="请输入手机号码" maxlength="11" type="number"
               placeholderClass="text-30" font-size="30rpx" :custom-style="customStyle" readonly>
             </uv-input>
@@ -38,7 +38,7 @@
           </uv-form-item>
         </content-card>
 
-        <content-card title="文化素质">
+        <content-card v-if="userStore.isStudent" title="文化素质">
           <uv-form-item label="语文" prop="name" borderBottom>
             <uv-input v-model="scores.chinese" border="none" placeholder="请输入" placeholderClass="text-30"
               font-size="30rpx" :custom-style="customStyle" :readonly="form.examType === 'OHS'">
@@ -78,7 +78,7 @@
           </block>
         </content-card>
 
-        <content-card v-if="form.examType === 'OHS' || form.examType === 'SVS'" title="职业技能成绩">
+        <content-card v-if="userStore.isStudent && (form.examType === 'OHS' || form.examType === 'SVS')" title="职业技能成绩">
           <uv-form-item label="职业技能" prop="name">
             <uv-input v-model.number="scores.skill" border="none" placeholder="请输入" placeholderClass="text-30"
               font-size="30rpx" :custom-style="customStyle">
@@ -86,7 +86,7 @@
           </uv-form-item>
         </content-card>
 
-        <content-card v-if="userStore.isLogin" title="学校信息">
+        <content-card v-if="userStore.isLogin && userStore.isStudent" title="学校信息">
           <uv-form-item label="学校名称" prop="form.name" borderBottom>
             <uv-input v-model="form.schoolName" border="none" placeholder="" placeholderClass="text-30"
               font-size="30rpx" :custom-style="customStyle" readonly>
@@ -101,7 +101,7 @@
         </content-card>
       </uv-form>
     </view>
-    <ie-safe-toolbar :height="84" :shadow="false">
+    <ie-safe-toolbar v-if="userStore.isStudent" :height="84" :shadow="false">
       <view class="px-30 py-16">
         <ie-button @click="handleSubmit">确认保存</ie-button>
       </view>
@@ -152,6 +152,7 @@ const form = ref<UserProfile>({
 const scores = ref({
   ...userInfo.value.scores
 })
+const nameLabel = computed(() => userStore.isStudent ? '学生姓名' : '姓名');
 const customStyle = {
   paddingLeft: '26px'
 };
@@ -164,6 +165,7 @@ const handleSubmit = async () => {
     scores: scores.value,
   } as UserInfo;
   await updateUserInfo(params);
+  await userStore.getUserInfo();
   uni.$ie.hideLoading();
   uni.$ie.showToast('保存成功');
 }

+ 78 - 0
src/pagesSystem/pages/edit-teacher-profile/edit-teacher-profile.vue

@@ -0,0 +1,78 @@
+<template>
+  <ie-page bg-color="#F6F8FA" :safeAreaInsetBottom="false">
+    <ie-navbar title="完善信息" custom-back @left-click="handleBack" />
+    <uv-form labelPosition="left" :model="examTypeForm" labelWidth="70px" ref="formRef">
+      <content-card title="基本信息">
+        <uv-form-item label="所在省份" prop="location" borderBottom required>
+          <ie-picker ref="pickerRef" v-model="examTypeForm.location" :list="provinceList" placeholder="选择省份"
+            :custom-style="customStyle" key-label="dictLabel" key-value="dictValue"></ie-picker>
+        </uv-form-item>
+        <uv-form-item label="考生类别" prop="examType" borderBottom required>
+          <ie-picker ref="pickerRef" v-model="examTypeForm.examType" :list="examTypeList"
+            :disabled="!examTypeForm.location" placeholder="选择考生类别" :custom-style="customStyle" key-label="dictLabel"
+            key-value="dictValue"></ie-picker>
+        </uv-form-item>
+        <uv-form-item label="毕业年份" prop="year" required>
+          <ie-picker ref="pickerRef" v-model="examTypeForm.endYear" :list="endYearList"
+            :disabled="!examTypeForm.examType" placeholder="选择毕业年份" :custom-style="customStyle" key-label="dictLabel"
+            key-value="dictValue"></ie-picker>
+        </uv-form-item>
+      </content-card>
+    </uv-form>
+    <ie-safe-toolbar :height="84" :shadow="false">
+      <view class="px-30 py-16">
+        <ie-button @click="handleSubmit">确认提交</ie-button>
+      </view>
+    </ie-safe-toolbar>
+  </ie-page>
+</template>
+
+<script lang="ts" setup>
+import ContentCard from '@/pagesSystem/components/content-card.vue';
+import { useExamType } from '@/composables/useExamType';
+import { updateUserInfo } from '@/api/modules/login';
+import { useUserStore } from '@/store/userStore';
+import { UserInfo } from '@/types/user';
+import { useTransferPage } from '@/hooks/useTransferPage';
+const { prevData, transferTo, transferBack } = useTransferPage();
+const { form: examTypeForm, examTypeList, examMajorList, provinceList, endYearList } = useExamType();
+const userStore = useUserStore();
+const customStyle = {
+  paddingLeft: '26px'
+};
+const handleBack = () => {
+  uni.$ie.showToast('请先完善信息');
+};
+const handleSubmit = async () => {
+  console.log('handleSubmit', examTypeForm.value)
+  const { location, examType, endYear } = examTypeForm.value;
+  if (!location) {
+    uni.$ie.showToast('请先选择所在省份');
+    return;
+  }
+  if (!examType) {
+    uni.$ie.showToast('请先选择考试类别');
+    return;
+  }
+  if (!endYear) {
+    uni.$ie.showToast('请先选择毕业年份');
+    return;
+  }
+  const params = {
+    ...userStore.userInfo,
+    location,
+    examType,
+    endYear,
+  } as UserInfo;
+  uni.$ie.showLoading();
+  await updateUserInfo(params);
+  await userStore.getUserInfo();
+  uni.$ie.hideLoading();
+  uni.$ie.showToast('保存成功');
+  setTimeout(() => {
+    transferBack(true);
+  }, 800);
+};
+</script>
+
+<style></style>

+ 41 - 0
src/store/userStore.ts

@@ -10,6 +10,8 @@ import defaultAvatar from '@/static/personal/avatar_default.png'
 
 // @ts-ignore
 import { useUserStore as useOldUserStore } from '@/hooks/useUserStore';
+import { EnumUserType } from '@/common/enum';
+import { OPEN_VIP_POPUP } from '@/types/injectionSymbols';
 const oldUserStore = useOldUserStore()
 const themeColor = '#31A0FC';
 type CheckLoginOptions = {
@@ -57,6 +59,22 @@ export const useUserStore = defineStore('ie-user', {
       }
       return {} as VipCardInfo;
     },
+    isStudent(): boolean {
+      return this.userInfo.userType === EnumUserType.STUDENT;
+    },
+    isTeacher(): boolean {
+      return this.userInfo.userType === EnumUserType.TEACHER;
+    },
+    isAgent(): boolean {
+      return this.userInfo.userType === EnumUserType.AGENT;
+    },
+    /**
+     * 需要完成信息完善的用户类型
+     * @returns 
+     */
+    needCompleteInfo(): boolean {
+      return this.isTeacher || this.isAgent;
+    },
     // 兼容游客时显示
     getLocation(): string {
       if (this.isLogin) {
@@ -111,6 +129,29 @@ export const useUserStore = defineStore('ie-user', {
         }
       });
     },
+    checkVip() {
+      return new Promise((resolve, reject) => {
+        if (!this.isVip) {
+          resolve(false);
+        }
+        resolve(true);
+      });
+    },
+    checkInfoComplete() {
+      console.log('this.needCompleteInfo', this.needCompleteInfo)
+      return new Promise((resolve, reject) => {
+        if (this.needCompleteInfo && (!this.userInfo.location || !this.userInfo.examType || !this.userInfo.endYear)) {
+          const { transferTo } = useTransferPage();
+          transferTo('/pagesSystem/pages/edit-teacher-profile/edit-teacher-profile').then(res => {
+            resolve(res as boolean);
+          }).catch(() => {
+            resolve(false);
+          });
+        } else {
+          resolve(true);
+        }
+      });
+    },
     async getUserInfo() {
       const res = await getUserInfo();
       const { data, isDefaultModifyPwd, isPasswordExpired, card } = res;

+ 12 - 1
src/types/injectionSymbols.ts

@@ -31,4 +31,15 @@ export const STUDY_PLAN = Symbol('STUDY_PLAN') as InjectionKey<Ref<StudyPlan>>;
 /**
  * 学习计划统计数据
  */
-export const STUDY_PLAN_STATS = Symbol('STUDY_PLAN_STATS') as InjectionKey<Ref<StudyPlanStats>>;
+export const STUDY_PLAN_STATS = Symbol('STUDY_PLAN_STATS') as InjectionKey<Ref<StudyPlanStats>>;
+
+
+/**
+ * 打开VIP弹窗
+ */
+export const OPEN_VIP_POPUP = Symbol('OPEN_VIP_POPUP') as InjectionKey<() => void>;
+
+/**
+ * 关闭VIP弹窗
+ */
+export const CLOSE_VIP_POPUP = Symbol('CLOSE_VIP_POPUP') as InjectionKey<() => void>;

+ 27 - 7
src/types/study.ts

@@ -1,10 +1,3 @@
-export interface StudyRecord {
-  id: number;
-  knowledgeName: string;
-  rate: number;
-  questionNum: number;
-}
-
 export interface TeachClass {
   classId: number;
   schoolId: number;
@@ -247,4 +240,31 @@ export interface SimulationTestOptions {
 export interface SimulationTestInfo {
   evalCount: number;
   subjects: string[]
+}
+
+
+// 学习记录
+export interface KnowledgeRecord {
+  name: string;
+  total: number;
+  rate: number;
+  directed: boolean; // 是否是定向
+}
+
+export interface SimulatedRecord {
+  rate: number;
+  name: string;
+  score: number;
+  total: number;
+  date: string;
+}
+
+export interface VideoStudyRecord {
+  study: number;
+  total: number;
+  list: {
+    name: string;
+    date: string;
+    study: string;
+  }[]
 }

+ 2 - 1
src/types/user.ts

@@ -1,4 +1,4 @@
-import { EnumSmsType } from "@/common/enum";
+import { EnumSmsType, EnumUserType } from "@/common/enum";
 
 export interface LoginInfo {
   accessToken: string;
@@ -146,6 +146,7 @@ export interface UserInfo {
   userId: number;
   userName: string;
   scores: Scores;
+  userType: EnumUserType
 }
 
 export interface VipCardInfo {