Ver Fonte

添加视频学习记录

shmily1213 há 1 mês atrás
pai
commit
5d79d00e46
27 ficheiros alterados com 506 adições e 59 exclusões
  1. 2 2
      src/api/flyio.ts
  2. 11 0
      src/api/modules/plan.ts
  3. 1 0
      src/components/ie-picker/ie-picker.vue
  4. 1 1
      src/components/ie-tab/ie-tab.vue
  5. 6 6
      src/components/ie-table/ie-table.vue
  6. 23 8
      src/composables/useCalendar.ts
  7. 12 0
      src/pages.json
  8. 9 4
      src/pagesMain/pages/index/components/index-banner.vue
  9. 6 6
      src/pagesStudy/components/practice-table.vue
  10. 88 0
      src/pagesStudy/components/video-table.vue
  11. 8 1
      src/pagesStudy/pages/index/compoentns/index-banner.vue
  12. 1 1
      src/pagesStudy/pages/study-history/components/exam-history-teacher.vue
  13. 1 1
      src/pagesStudy/pages/study-history/components/exam-history.vue
  14. 6 1
      src/pagesStudy/pages/study-history/components/knowledge-history-teacher.vue
  15. 7 2
      src/pagesStudy/pages/study-history/components/practice-history-student.vue
  16. 52 3
      src/pagesStudy/pages/study-history/components/practice-history-teacher.vue
  17. 74 3
      src/pagesStudy/pages/study-history/components/practice-history.vue
  18. 8 3
      src/pagesStudy/pages/study-history/components/student-stat-table.vue
  19. 4 2
      src/pagesStudy/pages/study-history/components/video-history-student.vue
  20. 49 2
      src/pagesStudy/pages/study-history/components/video-history-teacher.vue
  21. 39 2
      src/pagesStudy/pages/study-history/components/video-history.vue
  22. 5 7
      src/pagesStudy/pages/study-history/study-history.vue
  23. 3 3
      src/pagesStudy/pages/study-knowledge-detail/study-knowledge-detail.vue
  24. 26 0
      src/pagesStudy/pages/study-practice-detail/study-practice-detail.vue
  25. 26 0
      src/pagesStudy/pages/study-video-detail/study-video-detail.vue
  26. 11 1
      src/types/injectionSymbols.ts
  27. 27 0
      src/types/study.ts

+ 2 - 2
src/api/flyio.ts

@@ -3,7 +3,7 @@ import Fly from "flyio/dist/npm/wx"
 import config from "@/config";
 import { useUserStore } from '@/store/userStore';
 import { storeToRefs } from 'pinia';
-import { ApiResponse, LoginInfo } from "@/types";
+import { ApiResponse } from "@/types";
 
 const { serverBaseUrl } = config;
 
@@ -56,7 +56,7 @@ function logout() {
   const userStore = useUserStore();
   userStore.logout();
   uni.reLaunch({
-    url: '/pages/index/guide'
+    url: '/pagesSystem/pages/login/login'
   });
 }
 export default fly;

+ 11 - 0
src/api/modules/plan.ts

@@ -0,0 +1,11 @@
+import { ApiResponse } from "@/types";
+import flyio from "../flyio";
+
+/**
+ * 获取学习计划
+ * @param params 
+ * @returns 
+ */
+export function getStudyPlan() {
+  return flyio.get('/front/student/plan') as Promise<ApiResponse<any>>;
+}

+ 1 - 0
src/components/ie-picker/ie-picker.vue

@@ -155,6 +155,7 @@ const handleClick = () => {
   if (props.disabled) {
     return;
   }
+  init();
   isOpen.value = true;
   pickerRef.value.open();
 }

+ 1 - 1
src/components/ie-tab/ie-tab.vue

@@ -1,6 +1,6 @@
 <template>
   <view class="w-fit bg-[#EBF9FF] p-4 rounded-full flex items-center">
-    <view class="w-fit px-20 py-10 rounded-full text-28" :class="{ 'is-active': modelValue === item.value }"
+    <view class="w-fit px-20 py-10 rounded-full text-28 leading-28" :class="{ 'is-active': modelValue === item.value }"
       v-for="item in options" @click="changeTab(item.value)">
       {{ item.label }}
     </view>

+ 6 - 6
src/components/ie-table/ie-table.vue

@@ -13,11 +13,9 @@
             <view v-if="item.type === 'index'">
               {{ index + 1 }}
             </view>
-            <view v-else>
-              <slot :name="item.slot" :item="row" :index="index">
-                <text>{{ getCellValue(row, item.prop) }}</text>
-              </slot>
-            </view>
+            <slot v-else :name="item.slot" :item="row" :index="index">
+              <text>{{ getCellValue(row, item.prop) }}</text>
+            </slot>
           </view>
         </view>
       </block>
@@ -70,6 +68,8 @@ const getCellStyle = (item: TableColumnConfig) => {
   return {
     flex: item.flex ? item.flex : 1,
     minWidth: '1px',
+    flexShrink: 0,
+    width: '100%',
     textAlign: item.align ? item.align : 'center',
     ...props.cellStyle
   };
@@ -97,7 +97,7 @@ const getCellValue = (row: any, prop: string) => {
 }
 
 .table-header-cell {
-  @apply flex-1 py-20 text-30 text-fore-light;
+  @apply flex-1 px-20 py-20 text-30 text-fore-light;
 }
 
 .table-row {

+ 23 - 8
src/composables/useCalendar.ts

@@ -17,6 +17,7 @@ export interface PracticeStatistics {
 
 export function useCalendar() {
   const calendarUtil = new CalendarUtil();
+  const globalStudentId = ref(0);
   const selected = ref<PracticeData[]>([]);
   const statistics = ref<PracticeStatistics>({
     totalQuestions: 0,
@@ -29,6 +30,9 @@ export function useCalendar() {
   const currentDate = ref(new Date());
   const displayMode = ref<'year' | 'month' | 'week'>('year');
   const loading = ref(false);
+  
+  // 配置变量:最早可访问的年份
+  const EARLIEST_YEAR = 2020;
 
   // 计算当前周范围
   const currentWeekRange = computed(() => {
@@ -46,12 +50,13 @@ export function useCalendar() {
     const current = currentDate.value;
 
     if (displayMode.value === 'year') {
-      return current.getFullYear() > 2020; // 假设最早年份是2020年
+      return current.getFullYear() > EARLIEST_YEAR;
     } else if (displayMode.value === 'week') {
-      return currentWeekNumber.value > 1 || current.getMonth() > 0;
+      // 周模式:支持跨年
+      return currentWeekNumber.value > 1 || !(current.getFullYear() === EARLIEST_YEAR && current.getMonth() === 0);
     } else {
-      // 月份模式:支持跨年,只要不是2020年1月就可以往前切换
-      return !(current.getFullYear() === 2020 && current.getMonth() === 0);
+      // 月份模式:支持跨年,只要不是最早年份1月就可以往前切换
+      return !(current.getFullYear() === EARLIEST_YEAR && current.getMonth() === 0);
     }
   });
 
@@ -96,7 +101,7 @@ export function useCalendar() {
   // 4. 统计数据会从返回的练习数据中自动计算,无需额外处理
   // ========================================================
   const fetchPracticeData = async (startDate: string, endDate: string): Promise<PracticeData[]> => {
-    console.log('请求数据', startDate, endDate)
+    console.log('请求数据', startDate, endDate, globalStudentId.value)
     // ========== 模拟网络延迟 - 真实接口时删除此部分 ==========
     await new Promise(resolve => setTimeout(resolve, 300));
 
@@ -235,8 +240,14 @@ export function useCalendar() {
       await updateCalendarData(undefined, 'week', currentWeekNumber.value - 1);
     } else {
       // 切换到上个月的最后一周
-      const preDate = new Date(currentDate.value);
-      preDate.setMonth(preDate.getMonth() - 1);
+      const currentMonth = currentDate.value.getMonth();
+      const currentYear = currentDate.value.getFullYear();
+      
+      // 计算上个月的年月
+      const prevYear = currentMonth === 0 ? currentYear - 1 : currentYear;
+      const prevMonth = currentMonth === 0 ? 11 : currentMonth - 1;
+      
+      const preDate = new Date(prevYear, prevMonth, 1);
       calendarUtil.setDate(preDate);
       const range = calendarUtil.getCurrentWeekRange(preDate);
       await updateCalendarData(preDate, 'week', range.totalWeeks);
@@ -326,8 +337,9 @@ export function useCalendar() {
   };
 
   // 初始化数据 - 默认按年份模式初始化
-  const init = async () => {
+  const init = async (studentId: number) => {
     const today = new Date();
+    globalStudentId.value = studentId;
     await updateCalendarData(today, 'year', today.getFullYear());
   };
 
@@ -352,6 +364,9 @@ export function useCalendar() {
     currentYear,
     currentMonth,
     
+    // 配置
+    EARLIEST_YEAR,
+    
     // 方法
     updateCalendarData,
     goToPrevWeek,

+ 12 - 0
src/pages.json

@@ -588,6 +588,18 @@
           "style": {
             "navigationBarTitleText": ""
           }
+        },
+        {
+          "path": "/pages/study-practice-detail/study-practice-detail",
+          "style": {
+            "navigationBarTitleText": ""
+          }
+        },
+        {
+          "path": "/pages/study-video-detail/study-video-detail",
+          "style": {
+            "navigationBarTitleText": ""
+          }
         }
       ]
     }

+ 9 - 4
src/pagesMain/pages/index/components/index-banner.vue

@@ -16,6 +16,8 @@
 <script lang="ts" setup>
 import { useTransferPage } from '@/hooks/useTransferPage';
 const { transferTo } = useTransferPage();
+import { useUserStore } from '@/store/userStore';
+const userStore = useUserStore();
 const menus = [
   {
     name: '学习备考',
@@ -59,10 +61,13 @@ const menus = [
     pageUrl: '/pages/index/index',
   }
 ]
-const navigateTo = (pageUrl: string, navigateType?: string) => {
-  transferTo(pageUrl, {
-    type: navigateType || 'navigate',
-  });
+const navigateTo = async (pageUrl: string, navigateType?: string) => {
+  const isLogin = await userStore.checkLogin();
+  if (isLogin) {
+    transferTo(pageUrl, {
+      type: navigateType || 'navigate',
+    });
+  };
 }
 </script>
 <style lang="scss" scoped></style>

+ 6 - 6
src/pagesStudy/components/practice-table.vue

@@ -11,7 +11,7 @@
           </ie-picker>
         </view>
         <view class="picker-wrap">
-          <ie-picker ref="pickerRef" v-model="form.month" :list="monthList" placeholder="请选择" title="选择月份"
+          <ie-picker ref="pickerRef" :disabled="!form.year" v-model="form.month" :list="monthList" placeholder="请选择" title="选择月份"
             :fontSize="26" icon="arrow-down" key-label="label" key-value="value" @change="handleMonthChange">
             <template #default="{ label }">
               <text>第{{ label }}</text>
@@ -19,7 +19,7 @@
           </ie-picker>
         </view>
         <view class="picker-wrap">
-          <ie-picker ref="pickerRef" v-model="form.week" :list="weekList" placeholder="请选择" title="选择周" :fontSize="26"
+          <ie-picker ref="pickerRef" :disabled="!form.month" v-model="form.week" :list="weekList" placeholder="请选择" title="选择周" :fontSize="26"
             icon="arrow-down" key-label="label" key-value="value" @change="handleWeekChange">
             <template #default="{ label }">
               <text>{{ label }}</text>
@@ -115,7 +115,9 @@ import { nextTick } from 'vue';
 import { TableColumnConfig } from '@/types';
 import ieEchart from './ie-echart/ie-echart.vue';
 import { useCalendar } from '@/composables/useCalendar';
-
+const props = defineProps<{ 
+  studentId: number;
+}>();
 // 使用 useCalendar composable
 const {
   selected,
@@ -138,8 +140,6 @@ const {
   goToYear,
   goToYearMonth,
   goToWeek,
-  setDisplayMode,
-  updateCalendarData,
   initializeFormData,
   init: initCalendar
 } = useCalendar();
@@ -558,7 +558,7 @@ onMounted(async () => {
   uni.$ie.showLoading();
   try {
     // 初始化日历
-    await initCalendar();
+    await initCalendar(props.studentId);
   } finally {
     // 隐藏loading
     uni.$ie.hideLoading();

+ 88 - 0
src/pagesStudy/components/video-table.vue

@@ -0,0 +1,88 @@
+<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">
+        <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">
+          <text class="text-36 text-fore-title font-bold">{{ item.value }}</text>
+          <text class="ml-20 text-24 text-fore-light">{{ item.unit }}</text>
+        </view>
+      </view>
+    </view>
+    <view class="mx-30">
+      <ie-table :table-columns="tableColumns" :data="data" @rowClick="handleRowClick">
+        <template #name="{ item }">
+          <view class="flex items-center justify-center">
+            <ie-image :src="item.avatar" class="w-60 h-60 bg-back" :round="999" />
+            <text class="ml-10 flex-1 min-w-1 ellipsis-1">{{ item.name }}</text>
+          </view>
+        </template>
+      </ie-table>
+    </view>
+  </view>
+</template>
+<script lang="ts" setup>
+import { TableColumnConfig } from '@/types';
+import { StudentVideoRecord } from '@/types/study';
+
+
+const stat = ref([
+  {
+    name: '视频总数',
+    unit: '课时',
+    value: '98',
+    icon: '/pagesStudy/static/image/icon-video.png'
+  },
+  {
+    name: '观看时长',
+    unit: '分钟',
+    value: '759',
+    icon: '/pagesStudy/static/image/icon-time.png'
+  },
+])
+const tableColumns = ref<TableColumnConfig[]>([
+  {
+    prop: 'name',
+    label: '视频名称',
+    flex: 1.6,
+  },
+  {
+    prop: 'date',
+    label: '日期',
+    flex: 1,
+  },
+  {
+    prop: 'duration',
+    label: '时长',
+    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 emit = defineEmits<{
+  rowClick: [row: StudentVideoRecord]
+}>();
+const handleRowClick = (row: StudentVideoRecord) => {
+  emit('rowClick', row);
+}
+</script>
+<style lang="scss" scoped></style>

+ 8 - 1
src/pagesStudy/pages/index/compoentns/index-banner.vue

@@ -4,7 +4,7 @@
       mode="widthFix" />
     <view class="mt-32 flex gap-x-30">
       <view class="flex-1 rounded-12 bg-[#F0FFF2] py-40 pl-22 pr-8 flex items-center"
-        @click="navigateTo('/pagesStudy/pages/study-plan/study-plan')">
+        @click="handleOpenPlan">
         <!-- /pagesStudy/pages/study-plan-edit/study-plan-edit -->
         <view class="flex-1">
           <view class="text-30 text-fore-title font-bold flex items-center">
@@ -30,9 +30,16 @@
 </template>
 <script lang="ts" setup>
 import { useTransferPage } from '@/hooks/useTransferPage';
+import { getStudyPlan } from '@/api/modules/plan';
 const { transferTo } = useTransferPage();
 const navigateTo = (pageUrl: string) => {
   transferTo(pageUrl);
 }
+
+const handleOpenPlan = async () => {
+  const data = await getStudyPlan()
+  console.log(data)
+  // transferTo('/pagesStudy/pages/study-plan-edit/study-plan-edit');
+}
 </script>
 <style lang="scss" scoped></style>

+ 1 - 1
src/pagesStudy/pages/study-history/components/exam-history-teacher.vue

@@ -1,6 +1,6 @@
 <template>
   <view class="p-30 pt-50">
-    <view class="w-fit flex gap-x-80">
+    <view class="w-fit flex gap-x-50">
       <ie-picker ref="pickerRef" v-model="classId" :list="classList" placeholder="选择批次" title="选择批次" icon="arrow-down"
         key-label="name" key-value="classId" @change="handleClassChange"
         :placeholder-style="placeholderStyle"></ie-picker>

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

@@ -1,6 +1,6 @@
 <template>
   <view class="flex-1 min-h-1 bg-white">
-    <view class="p-30 pb-20 py-20 flex gap-x-20">
+    <view class="px-30 py-20 flex gap-x-20">
       <view class="exam-type-item" :class="{ 'is-active': examType === 'test' }" @click="handleChangeExamType('test')">
         <ie-image src="/pagesStudy/static/image/icon-exam-test.png" custom-class="w-64 h-60" />
         <view class="exam-type-text">模拟仿真</view>

+ 6 - 1
src/pagesStudy/pages/study-history/components/knowledge-history-teacher.vue

@@ -8,12 +8,14 @@
       class="custom-progress" />
   </view>
   <view class="mt-76">
-    <student-stat-table :teach-class="teachClass" :data="studentStatData" />
+    <student-stat-table :teach-class="teachClass" :data="studentStatData" @rowClick="handleRowClick" />
   </view>
 </template>
 <script lang="ts" setup>
+import { OPEN_KNOWLEDGE_DETAIL } from '@/types/injectionSymbols';
 import studentStatTable from './student-stat-table.vue';
 import { StudentStat, TeachClass } from '@/types/study';
+const openKnowledgeDetail = inject(OPEN_KNOWLEDGE_DETAIL);
 const props = defineProps({
   teachClass: {
     type: Object as PropType<TeachClass>,
@@ -50,5 +52,8 @@ const loadData = async () => {
   // const res = await getStudentStat(props.teachClass.classId);
   // console.log(res);
 }
+const handleRowClick = (row: StudentStat) => {
+  openKnowledgeDetail?.(row.id, row.name);
+}
 </script>
 <style lang="scss" scoped></style>

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

@@ -1,7 +1,12 @@
 <template>
-  <view></view>
+  <view>
+    <practice-table :student-id="studentId" />
+  </view>
 </template>
 <script lang="ts" setup>
-
+import practiceTable from '@/pagesStudy/components/practice-table.vue';
+import { useUserStore } from '@/store/userStore';
+const userStore = useUserStore();
+const studentId = ref(userStore.userInfo.id);
 </script>
 <style lang="scss" scoped></style>

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

@@ -1,7 +1,56 @@
 <template>
-  <view></view>
+  <view>
+     <student-stat-table :teach-class="teachClass" :data="studentStatData" @row-click="handleRowClick" />
+  </view>
 </template>
 <script lang="ts" setup>
-
+import studentStatTable from './student-stat-table.vue';
+import { StudentStat, TeachClass } from '@/types/study';
+import { OPEN_PRACTICE_DETAIL } from '@/types/injectionSymbols';
+const openPracticeDetail = inject(OPEN_PRACTICE_DETAIL);
+const props = defineProps({
+  teachClass: {
+    type: Object as PropType<TeachClass>,
+    default: () => ({})
+  },
+  currentType: {
+    type: String,
+    default: 'rate'
+  },
+  currentTab: {
+    type: String,
+    default: 'asc'
+  }
+});
+const studentId = ref(0);
+const studentStatData = ref<StudentStat[]>([
+  {
+    id: 1,
+    name: '张三',
+    questionNum: 10,
+    dateNum: 10,
+    rate: 80
+  },
+  {
+    id: 2,
+    name: '李四',
+    questionNum: 10,
+    dateNum: 10,
+    rate: 80
+  }
+]);
+const loadData = async () => {
+  console.log('loadData', props.teachClass, props.currentTab, props.currentType);
+  // const res = await getPracticeData(props.teachClass.classId);
+  // data.value = res.data;
+}
+watchEffect(() => {
+  loadData();
+});
+const handleRowClick = (row: StudentStat) => {
+  console.log('点击了行:', row);
+  openPracticeDetail?.(row.id, row.name);
+}
 </script>
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+</style>

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

@@ -1,9 +1,80 @@
 <template>
   <view class="flex-1 min-h-1 bg-white">
-    <practice-table />
+    <practice-history-student v-if="userType === 'student'" />
+    <teacher-class-view v-else-if="userType === 'teacher'">
+      <template #default="{ teachClass }">
+        <view class="type-wrap">
+          <view v-for="item in typeList" :key="item.value" class="type-item"
+            :class="{ 'is-active': currentType === item.value }" @click="handleTypeChange(item.value)">{{ item.label }}
+          </view>
+        </view>
+        <view class="mt-30 w-fit mx-auto">
+          <ie-tab :options="tabList" v-model="currentTab" />
+        </view>
+        <view class="mt-30">
+          <practice-history-teacher :teach-class="teachClass" :current-type="currentType" :current-tab="currentTab" />
+        </view>
+      </template>
+    </teacher-class-view>
   </view>
 </template>
 <script lang="ts" setup>
-import practiceTable from '@/pagesStudy/components/practice-table.vue';
+import practiceHistoryStudent from './practice-history-student.vue';
+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';
+const { transferTo } = useTransferPage();
+const currentType = ref('rate');
+const typeList = ref([
+  {
+    label: '按正确率',
+    value: 'rate'
+  },
+  {
+    label: '按刷题天数',
+    value: 'practiceDays'
+  },
+  {
+    label: '按刷题题量',
+    value: 'questionNum'
+  }
+])
+const currentTab = ref('asc');
+const tabList = ref([
+  {
+    label: '升序',
+    value: 'asc'
+  },
+  {
+    label: '降序',
+    value: 'desc'
+  }
+])
+const userType = ref('teacher');
+const handleTypeChange = (value: string) => {
+  currentType.value = value;
+}
+const openPracticeDetail = (id: number, name: string) => {
+  transferTo('/pagesStudy/pages/study-practice-detail/study-practice-detail', {
+    data: {
+      studentId: id,
+      name: name
+    }
+  });
+}
+provide(OPEN_PRACTICE_DETAIL, openPracticeDetail);
 </script>
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.type-wrap {
+  @apply mt-40 mx-auto p-10 bg-white border border-solid border-border rounded-8 w-fit flex gap-x-10;
+}
+
+.type-item {
+  @apply text-28 text-fore-light px-30 py-8 rounded-5;
+}
+
+.is-active {
+  @apply bg-back text-fore-title font-bold;
+}
+</style>

+ 8 - 3
src/pagesStudy/pages/study-history/components/student-stat-table.vue

@@ -12,7 +12,6 @@
 </template>
 <script lang="ts" setup>
 import { TableColumnConfig, TableConfig } from '@/types';
-import { OPEN_KNOWLEDGE_DETAIL } from '@/types/injectionSymbols';
 import { TeachClass, StudentStat } from '@/types/study';
 const props = defineProps({
   teachClass: {
@@ -58,9 +57,15 @@ const tableColumns: TableColumnConfig[] = [
     label: '正确率'
   }
 ]
-const openKnowledgeDetail = inject(OPEN_KNOWLEDGE_DETAIL);
+const emit = defineEmits<{
+  rowClick: [row: StudentStat]
+}>();
 const handleRowClick = (row: StudentStat) => {
-  openKnowledgeDetail?.(row.id, row.name);
+  emit('rowClick', row);
 }
+// const openKnowledgeDetail = inject(OPEN_KNOWLEDGE_DETAIL);
+// const handleRowClick = (row: StudentStat) => {
+//   openKnowledgeDetail?.(row.id, row.name);
+// }
 </script>
 <style lang="scss" scoped></style>

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

@@ -1,7 +1,9 @@
 <template>
-  <view></view>
+  <view>
+    <video-table />
+  </view>
 </template>
 <script lang="ts" setup>
-
+import videoTable from '@/pagesStudy/components/video-table.vue';
 </script>
 <style lang="scss" scoped></style>

+ 49 - 2
src/pagesStudy/pages/study-history/components/video-history-teacher.vue

@@ -1,7 +1,54 @@
 <template>
-  <view></view>
+  <view class="mt-30">
+    <ie-table :table-columns="tableColumns" :data="data" @rowClick="handleRowClick">
+      <template #name="{ item }">
+        <view class="flex items-center justify-center">
+          <ie-image :src="item.avatar" class="w-60 h-60 bg-back" :round="999" />
+          <text class="ml-10 flex-1 min-w-1 ellipsis-1">{{ item.name }}</text>
+        </view>
+      </template>
+    </ie-table>
+  </view>
 </template>
 <script lang="ts" setup>
-
+import { TableColumnConfig } from '@/types';
+import { StudentVideoStat } from '@/types/study';
+import { OPEN_VIDEO_DETAIL } from '@/types/injectionSymbols';
+const openVideoDetail = inject(OPEN_VIDEO_DETAIL);
+const tableColumns = ref<TableColumnConfig[]>([
+  {
+    type: 'index',
+    prop: 'index',
+    label: '序号',
+    flex: 0.5
+  },
+  {
+    prop: 'name',
+    label: '姓名',
+    flex: 1
+  },
+  {
+    prop: 'videoCount',
+    label: '课时',
+    flex: 1
+  },
+  {
+    prop: 'duration',
+    label: '时长(分钟)',
+    flex: 1
+  }
+])
+const data = ref<StudentVideoStat[]>([
+  {
+    id: 1,
+    name: '张三',
+    videoCount: 10,
+    duration: 100
+  }
+])
+const handleRowClick = (row: StudentVideoStat) => {
+  console.log('点击了行:', row);
+  openVideoDetail?.(row.id, row.name);
+}
 </script>
 <style lang="scss" scoped></style>

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

@@ -1,7 +1,44 @@
 <template>
-  <view></view>
+  <view class="bg-white flex-1">
+    <video-history-student v-if="type === 'student'" />
+    <teacher-class-view v-else-if="type === 'teacher'">
+      <template #default="{ teachClass }">
+        <view class="mt-30 w-fit mx-auto">
+          <ie-tab :options="tabList" v-model="currentTab" />
+        </view>
+        <video-history-teacher :teach-class="teachClass" />
+      </template>
+    </teacher-class-view>
+  </view>
 </template>
 <script lang="ts" setup>
-
+import videoHistoryStudent from './video-history-student.vue';
+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';
+const { transferTo } = useTransferPage();
+// const type = ref('student');
+const type = ref('teacher');
+const currentTab = ref('asc');
+const tabList = ref([
+  {
+    label: '升序',
+    value: 'asc'
+  },
+  {
+    label: '降序',
+    value: 'desc'
+  }
+])
+const openVideoDetail = (id: number, name: string) => {
+  transferTo('/pagesStudy/pages/study-video-detail/study-video-detail', {
+    data: {
+      studentId: id,
+      name: name
+    }
+  });
+}
+provide(OPEN_VIDEO_DETAIL, openVideoDetail);
 </script>
 <style lang="scss" scoped></style>

+ 5 - 7
src/pagesStudy/pages/study-history/study-history.vue

@@ -6,12 +6,10 @@
         :scrollable="false" @change="handleTabChange"></uv-tabs>
     </view>
     <view class="h-20"></view>
-    <!-- <view class="z-0"> -->
-      <knowledge-history v-if="current === 0" />
-      <exam-history v-if="current === 1" />
-      <practice-history v-if="current === 2" />
-      <video-history v-if="current === 3" />
-    <!-- </view> -->
+    <knowledge-history v-if="current === 0" />
+    <exam-history v-if="current === 1" />
+    <practice-history v-if="current === 2" />
+    <video-history v-if="current === 3" />
   </ie-page>
 </template>
 
@@ -22,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(2);
+const current = ref(3);
 const list = [
   { name: '知识点记录', },
   { name: '试卷记录', },

+ 3 - 3
src/pagesStudy/pages/study-knowledge-detail/study-knowledge-detail.vue

@@ -1,9 +1,9 @@
 <template>
-  <ie-page bg-color="#F6F8FA">
+  <ie-page bg-color="#F6F8FA" :fix-height="true">
     <ie-navbar :title="pageTitle" />
     <view class="h-20"></view>
-    <view class="bg-white">
-      <view class="pt-35 px-30 text-30 text-fore-title font-bold">{{ prevData.name }}</view>
+    <view class="bg-white flex-1 min-h-1">
+      <view class="pt-32 px-30 text-30 text-fore-title font-bold">{{ prevData.name }}的知识点记录</view>
       <knowledge-table :student-id="studentId" />
     </view>
   </ie-page>

+ 26 - 0
src/pagesStudy/pages/study-practice-detail/study-practice-detail.vue

@@ -0,0 +1,26 @@
+<template>
+  <ie-page bg-color="#F6F8FA" :fix-height="true">
+    <ie-navbar :title="pageTitle" />
+    <view class="h-20"></view>
+    <view class="bg-white flex-1 min-h-1">
+      <view class="pt-32 px-30 text-30 text-fore-title font-bold">{{ prevData.name }}的刷题详情</view>
+      <practice-table :student-id="studentId" />
+    </view>
+  </ie-page>
+</template>
+<script lang="ts" setup>
+import practiceTable from '@/pagesStudy/components/practice-table.vue';
+import { useTransferPage } from '@/hooks/useTransferPage';
+import { useScroll } from '@/hooks/useScroll';
+const { prevData } = useTransferPage();
+const studentId = ref(prevData.value.studentId);
+const { scrollTop } = useScroll();
+
+const pageTitle = computed(() => {
+  if (scrollTop.value > 45) {
+    return `学生刷题详情-${prevData.value.name}`;
+  }
+  return '学生刷题详情';
+});
+</script>
+<style lang="scss" scoped></style>

+ 26 - 0
src/pagesStudy/pages/study-video-detail/study-video-detail.vue

@@ -0,0 +1,26 @@
+<template>
+  <ie-page bg-color="#F6F8FA" :fix-height="true">
+    <ie-navbar :title="pageTitle" />
+    <view class="h-20"></view>
+    <view class="bg-white flex-1 min-h-1 relative">
+      <view class="pt-30 px-32 text-30 text-fore-title font-bold">{{ prevData.name }}的视频观看统计</view>
+      <video-table :student-id="studentId" />
+    </view>
+  </ie-page>
+</template>
+<script lang="ts" setup>
+import videoTable from '@/pagesStudy/components/video-table.vue';
+import { useTransferPage } from '@/hooks/useTransferPage';
+import { useScroll } from '@/hooks/useScroll';
+const { prevData } = useTransferPage();
+const studentId = ref(prevData.value.studentId);
+const { scrollTop } = useScroll();
+
+const pageTitle = computed(() => {
+  if (scrollTop.value > 45) {
+    return `学生刷题详情-${prevData.value.name}`;
+  }
+  return '学生刷题详情';
+});
+</script>
+<style lang="scss" scoped></style>

+ 11 - 1
src/types/injectionSymbols.ts

@@ -3,4 +3,14 @@ import type { InjectionKey } from 'vue'
 /**
  * 打开知识点记录详情
  */
-export const OPEN_KNOWLEDGE_DETAIL = Symbol('OPEN_KNOWLEDGE_DETAIL') as InjectionKey<(id: number, name: string) => void>;
+export const OPEN_KNOWLEDGE_DETAIL = Symbol('OPEN_KNOWLEDGE_DETAIL') as InjectionKey<(id: number, name: string) => void>;
+
+/**
+ * 打开刷题记录详情
+ */
+export const OPEN_PRACTICE_DETAIL = Symbol('OPEN_PRACTICE_DETAIL') as InjectionKey<(id: number, name: string) => void>;
+ 
+/**
+ * 打开视频记录详情
+ */
+export const OPEN_VIDEO_DETAIL = Symbol('OPEN_VIDEO_DETAIL') as InjectionKey<(id: number, name: string) => void>;

+ 27 - 0
src/types/study.ts

@@ -26,4 +26,31 @@ export interface StudentExamRecord {
   name: string;
   score: number;
   status: number;
+}
+
+export interface StudentVideoRecord {
+  id: number;
+  name: string;
+  date: string;
+  duration: number;
+}
+
+export interface StudentVideoStat {
+  id: number;
+  avatar?: string;
+  name: string;
+  videoCount: number;
+  duration: number;
+}
+
+/**
+ * 学习计划
+ */
+export interface StudyPlan {
+  id: number;
+  beginTime: string;
+  questionCnt: number;
+  studentId: number;
+  videoTime: number;
+  status: number;
 }