Quellcode durchsuchen

Merge branch 'master' of http://49.234.186.218:9000/root/ieplus-app

jinxia.mo vor 1 Monat
Ursprung
Commit
eee405d99f
80 geänderte Dateien mit 1076 neuen und 250 gelöschten Zeilen
  1. 1 1
      src/api/flyio.ts
  2. 2 1
      src/api/modules/login.ts
  3. 1 1
      src/api/modules/news.ts
  4. 2 1
      src/api/modules/system.ts
  5. 1 1
      src/components/ie-dict/ie-dict.vue
  6. 3 13
      src/components/ie-image/ie-image.vue
  7. 2 2
      src/components/ie-navbar/ie-navbar.vue
  8. 3 7
      src/components/ie-page/ie-page.vue
  9. 143 0
      src/components/ie-picker/ie-picker.vue
  10. 3 3
      src/components/ie-safe-toolbar/ie-safe-toolbar.vue
  11. 3 3
      src/components/ie-sms/ie-sms.vue
  12. 93 0
      src/components/ie-table/ie-table.vue
  13. 3 5
      src/hooks/useAppConfig.ts
  14. 21 0
      src/hooks/useImage.ts
  15. 2 4
      src/hooks/useNavbar.ts
  16. 15 6
      src/hooks/useScroll.ts
  17. 3 6
      src/hooks/useSms.ts
  18. 2 4
      src/hooks/useTransferPage.ts
  19. 2 4
      src/hooks/useUniStore.ts
  20. 2 4
      src/hooks/useValidation.ts
  21. 2 1
      src/manifest.json
  22. 12 0
      src/pages.json
  23. 1 1
      src/pagesMain/pages/index/components/index-banner.vue
  24. 2 2
      src/pagesMain/pages/index/components/index-guide.vue
  25. 24 6
      src/pagesMain/pages/index/index.vue
  26. 2 2
      src/pagesMain/pages/me/components/me-info.vue
  27. 3 3
      src/pagesMain/pages/me/components/me-menu.vue
  28. 3 1
      src/pagesMain/pages/me/me.vue
  29. 4 4
      src/pagesMain/pages/splash/splash.vue
  30. 7 4
      src/pagesMain/pages/volunteer/volunteer.vue
  31. 1 1
      src/pagesOther/pages/college-library/picker/picker.vue
  32. 1 1
      src/pagesOther/pages/news/detail/detail.vue
  33. 1 1
      src/pagesStudy/components/exam-record-item.vue
  34. 91 0
      src/pagesStudy/components/knowledge-table.vue
  35. 1 1
      src/pagesStudy/components/knowledge-tree-node.vue
  36. 1 1
      src/pagesStudy/components/knowledge-tree.vue
  37. 42 0
      src/pagesStudy/components/teacher-class-view.vue
  38. 5 3
      src/pagesStudy/pages/homework/homework.vue
  39. 1 1
      src/pagesStudy/pages/index/compoentns/index-banner.vue
  40. 9 3
      src/pagesStudy/pages/index/compoentns/index-exam-record.vue
  41. 2 2
      src/pagesStudy/pages/index/compoentns/index-menu.vue
  42. 1 1
      src/pagesStudy/pages/index/compoentns/index-test.vue
  43. 1 1
      src/pagesStudy/pages/index/index.vue
  44. 1 1
      src/pagesStudy/pages/simulation-entry/simulation-entry.vue
  45. 1 1
      src/pagesStudy/pages/simulation-start/simulation-start.vue
  46. 7 0
      src/pagesStudy/pages/study-history/components/exam-history-student.vue
  47. 7 0
      src/pagesStudy/pages/study-history/components/exam-history-teacher.vue
  48. 49 0
      src/pagesStudy/pages/study-history/components/exam-history.vue
  49. 9 0
      src/pagesStudy/pages/study-history/components/knowledge-history-student.vue
  50. 54 0
      src/pagesStudy/pages/study-history/components/knowledge-history-teacher.vue
  51. 39 0
      src/pagesStudy/pages/study-history/components/knowledge-history.vue
  52. 7 0
      src/pagesStudy/pages/study-history/components/practice-history-student.vue
  53. 7 0
      src/pagesStudy/pages/study-history/components/practice-history-teacher.vue
  54. 7 0
      src/pagesStudy/pages/study-history/components/practice-history.vue
  55. 66 0
      src/pagesStudy/pages/study-history/components/student-stat-table.vue
  56. 7 0
      src/pagesStudy/pages/study-history/components/video-history-student.vue
  57. 7 0
      src/pagesStudy/pages/study-history/components/video-history-teacher.vue
  58. 7 0
      src/pagesStudy/pages/study-history/components/video-history.vue
  59. 42 0
      src/pagesStudy/pages/study-history/study-history.vue
  60. 26 0
      src/pagesStudy/pages/study-knowledge-detail/study-knowledge-detail.vue
  61. 1 1
      src/pagesStudy/pages/study-plan-edit/components/course-setting.vue
  62. 1 1
      src/pagesStudy/pages/study-plan-edit/components/question-setting.vue
  63. 1 1
      src/pagesStudy/pages/study-plan/study-plan.vue
  64. 2 2
      src/pagesStudy/pages/targeted-setting/targeted-setting.vue
  65. BIN
      src/pagesStudy/static/image/icon-exam-homework.png
  66. BIN
      src/pagesStudy/static/image/icon-exam-test.png
  67. 5 5
      src/pagesSystem/pages/login/login.vue
  68. 24 20
      src/pagesSystem/pages/user-profile/user-profile.vue
  69. 5 0
      src/static/theme/var.scss
  70. 2 4
      src/store/appStore.ts
  71. 2 4
      src/store/dictStore.ts
  72. 3 5
      src/store/userStore.ts
  73. 33 84
      src/types/index.ts
  74. 6 0
      src/types/injectionSymbols.ts
  75. 20 6
      src/types/study.ts
  76. 83 0
      src/types/user.ts
  77. 11 6
      src/uni_modules/uv-picker/components/uv-picker/uv-picker.vue
  78. 4 2
      src/uni_modules/uv-tabs/components/uv-tabs/uv-tabs.vue
  79. 1 0
      src/uni_modules/uv-toolbar/components/uv-toolbar/uv-toolbar.vue
  80. 5 1
      tailwind.config.js

+ 1 - 1
src/api/flyio.ts

@@ -1,7 +1,7 @@
 // @ts-ignore
 import Fly from "flyio/dist/npm/wx"
 import config from "@/config";
-import useUserStore from '@/store/userStore';
+import { useUserStore } from '@/store/userStore';
 import { storeToRefs } from 'pinia';
 import { ApiResponse, LoginInfo } from "@/types";
 

+ 2 - 1
src/api/modules/login.ts

@@ -1,4 +1,5 @@
-import { ApiResponse, LoginInfo, MobileLoginRequestDTO, MobileLoginResponseDTO, RegisterInfo, UserInfo } from "@/types";
+import { LoginInfo, MobileLoginRequestDTO, MobileLoginResponseDTO, RegisterInfo, UserInfo } from "@/types/user";
+import { ApiResponse } from "@/types";
 import flyio from "../flyio";
 
 

+ 1 - 1
src/api/modules/news.ts

@@ -1,6 +1,6 @@
 import { Guide, News, NewsQueryDTO } from "@/types/news";
 import flyio from "../flyio";
-import { ApiCaptchaResponse, ApiResponse, ApiResponseList, CaptchaImage, ConfigItem, DictItem, SmsRequestDTO } from "@/types";
+import { ApiResponseList } from "@/types";
 
 /**
  * 获取新闻列表

+ 2 - 1
src/api/modules/system.ts

@@ -1,5 +1,6 @@
 import flyio from "../flyio";
-import { ApiCaptchaResponse, ApiResponse, CaptchaImage, ConfigItem, DictItem, SmsRequestDTO } from "@/types";
+import { ApiCaptchaResponse, ApiResponse, DictItem, ConfigItem } from "@/types";
+import { SmsRequestDTO } from "@/types/user";
 
 /**
  * 获取配置属性

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

@@ -3,7 +3,7 @@
 </template>
 
 <script lang="ts" setup>
-import useDictStore from '@/store/dictStore';
+import { useDictStore } from '@/store/dictStore';
 const dictStore = useDictStore();
 const props = defineProps({
   dictName: {

+ 3 - 13
src/components/ie-image/ie-image.vue

@@ -8,6 +8,8 @@
 <script lang="ts" setup>
 // @ts-ignore
 import config from '@/config';
+import { useImage} from '@/hooks/useImage';
+const { resolvePath } = useImage();
 const props = defineProps({
   src: {
     type: String,
@@ -38,20 +40,8 @@ const props = defineProps({
     default: '#F3F5F6'
   }
 });
-const handleSrc = (src: string) => {
-  // h5的开发环境,补上/h5/src/前缀
-  // #ifdef H5
-  if (import.meta.env.DEV && !src.startsWith('/h5/src/')) {
-    return `/h5/src/${src.replace(/^\//, '')}`;
-  }
-  return src;
-  // #endif
-  // #ifdef MP-WEIXIN
-  return src;
-  // #endif
-}
 const imageSrc = computed(() => {
-  return props.src ? (props.isOss ? config.ossUrl + props.src : handleSrc(props.src)) : '';
+  return props.src ? (props.isOss ? config.ossUrl + props.src : resolvePath(props.src)) : '';
 });
 const emit = defineEmits(['click']);
 const handleClick = () => {

+ 2 - 2
src/components/ie-navbar/ie-navbar.vue

@@ -15,8 +15,8 @@
 </template>
 
 <script lang="ts" setup>
-import useTransferPage from "@/hooks/useTransferPage";
-import useScroll from '@/hooks/useScroll';
+import { useTransferPage } from "@/hooks/useTransferPage";
+import { useScroll } from '@/hooks/useScroll';
 const { transferBack } = useTransferPage();
 const { scrollTop } = useScroll();
 const isTransparent = ref(false);

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

@@ -2,10 +2,10 @@
   <view class="ie-page theme-ie"
     :class="[safeAreaInsetBottom ? 'safe-area-inset-bottom' : '', { 'is-fixed': fixHeight }]"
     :style="{ backgroundColor: bgColor }">
-    <view class="ie-page-content" :class="{ 'tabbar-layout': tabbar }">
+    <view class="ie-page-content" :class="{ 'tabbar-layout': $slots.tabbar }">
       <slot></slot>
     </view>
-    <view v-if="tabbar" class="ie-page-tabbar">
+    <view v-if="$slots.tabbar" class="ie-page-tabbar">
       <slot name="tabbar"></slot>
     </view>
   </view>
@@ -19,10 +19,6 @@ defineOptions({
   }
 });
 const props = defineProps({
-  tabbar: {
-    type: Boolean,
-    default: false
-  },
   safeAreaInsetBottom: {
     type: Boolean,
     default: true
@@ -45,7 +41,7 @@ const props = defineProps({
 }
 
 .ie-page-content {
-  @apply min-h-full relative flex flex-col;
+  @apply min-h-full relative flex flex-col z-99;
 
   &.tabbar-layout {
     // padding-bottom: 60px;

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

@@ -0,0 +1,143 @@
+<template>
+  <view class="w-full" @click="handleClick">
+    <view class="flex items-center text-30 gap-x-10" :style="customStyle">
+      <view v-if="matchValue" class="flex-1 text-fore-title" :class="{ 'text-[#c0c4cc]': disabled || readonly }">
+        {{ label }}
+      </view>
+      <view v-else class="flex-1 text-[#c0c4cc]">{{ placeholder }}</view>
+      <view v-if="!readonly" class="transition-all duration-300">
+        <uv-icon :name="icon" size="15" color="#B3B3B3" />
+      </view>
+    </view>
+  </view>
+  <uv-picker ref="pickerRef" :showToolbar="false" :columns="columns" :defaultIndex="defaultIndex" :round="16"
+    activeColor="#31A0FC" :keyName="keyLabel" :title="title" @change="handleChange" @close="onClose">
+    <template #toolbar>
+      <view class="flex items-center justify-between pt-20">
+        <view class="px-46 py-20 text-28 text-fore-light" @click="handleCancel">取消</view>
+        <text class="text-30 text-fore-title font-bold">{{ title }}</text>
+        <view class="px-46 py-20 text-28 text-fore-title" @click="handleConfirm">确认</view>
+      </view>
+    </template>
+  </uv-picker>
+</template>
+<script lang="ts" setup>
+
+const modelValue = defineModel<string | number>('modelValue', {
+  default: ''
+});
+const props = defineProps({
+  placeholder: {
+    type: String,
+    default: '请选择',
+  },
+  title: {
+    type: String,
+    default: '',
+  },
+  icon: {
+    type: String,
+    default: 'arrow-right',
+  },
+  list: {
+    type: Array as PropType<any[]>,
+    default: () => [],
+  },
+  keyLabel: {
+    type: String,
+    default: 'label',
+  },
+  keyValue: {
+    type: String,
+    default: 'value',
+  },
+  disabled: {
+    type: Boolean,
+    default: false,
+  },
+  readonly: {
+    type: Boolean,
+    default: false,
+  },
+  customStyle: {
+    type: Object,
+    default: () => ({}),
+  }
+});
+const defaultIndex = ref([0]);
+const label = ref('');
+const isOpen = ref(false);
+const matchValue = ref(false);
+const columns = computed(() => {
+  return [props.list];
+});
+
+const init = () => {
+  if (modelValue.value !== null && modelValue.value !== undefined && modelValue.value !== '') {
+    const index = props.list.findIndex(item => item[props.keyValue] == modelValue.value);
+    if (index !== -1) {
+      defaultIndex.value = [index];
+      label.value = props.list[index][props.keyLabel];
+      matchValue.value = true;
+    } else {
+      // 如果找不到匹配项,重置状态
+      defaultIndex.value = [0];
+      label.value = props.placeholder;
+      matchValue.value = false;
+    }
+  } else {
+    defaultIndex.value = [0];
+    label.value = props.placeholder;
+    matchValue.value = false;
+  }
+}
+
+// 使用 watch 替代 watchEffect,避免循环更新
+watch([() => modelValue.value, () => props.list], init, { immediate: true });
+
+const pickerRef = ref();
+
+const emit = defineEmits<{
+  (e: 'change', value: number, selectedItem: any): void;
+  (e: 'click'): void;
+}>();
+
+const handleConfirm = () => {
+  const { value } = pickerRef.value.manualConfirm();
+  const oldValue = modelValue.value;
+  const newValue = value[0][props.keyValue];
+
+  // 更新 modelValue
+  modelValue.value = newValue;
+
+  // 手动更新显示状态,确保界面同步
+  const selectedItem = value[0];
+  label.value = selectedItem[props.keyLabel];
+  matchValue.value = true;
+
+  // 发出 change 事件
+  if (oldValue !== newValue) {
+    emit('change', newValue, selectedItem);
+  }
+
+  pickerRef.value.close();
+}
+const handleCancel = () => {
+  pickerRef.value.close();
+}
+const handleChange = () => { }
+
+const handleClick = () => {
+  emit('click');
+  if (props.disabled) {
+    return;
+  }
+  isOpen.value = true;
+  pickerRef.value.open();
+}
+
+const onClose = () => {
+  isOpen.value = false;
+}
+</script>
+<style lang="scss" scoped></style>

+ 3 - 3
src/components/ie-safe-toolbar/ie-safe-toolbar.vue

@@ -1,13 +1,13 @@
 <template>
   <view class="min-h-62" :style="{ minHeight: minHeight + 'px' }">
-    <view class="safe-area-inset-bottom fixed left-0 bottom-0 right-0 z-10 box-border"
-      :class="{ 'shadow-box': shadow }" :style="{ height: minHeight + 'px' }">
+    <view class="safe-area-inset-bottom fixed left-0 bottom-0 right-0 z-10 box-border" :class="{ 'shadow-box': shadow }"
+      :style="{ height: minHeight + 'px' }">
       <slot></slot>
     </view>
   </view>
 </template>
 <script lang="ts" setup>
-import useAppStore from '@/store/appStore';
+import { useAppStore } from '@/store/appStore';
 const appStore = useAppStore();
 const props = defineProps({
   height: {

+ 3 - 3
src/components/ie-sms/ie-sms.vue

@@ -3,11 +3,11 @@
   <ie-captcha ref="captchaRef" v-model:code="code" v-model:uuid="uuid" @valid="handleValid" />
 </template>
 <script lang="ts" setup>
-import useAppConfig from '@/hooks/useAppConfig';
-import useSms from '@/hooks/useSms';
+import { useAppConfig } from '@/hooks/useAppConfig';
+import { useSms } from '@/hooks/useSms';
 import IeCaptcha from './ie-captcha.vue';
 import { EnumSmsApiType, EnumSmsType } from '@/common/enum';
-import { SmsRequestDTO } from '@/types';
+import { SmsRequestDTO } from '@/types/user';
 const sms = useSms();
 const { smsTip, isCountingDown, smsIsSending } = sms;
 const appConfig = useAppConfig();

+ 93 - 0
src/components/ie-table/ie-table.vue

@@ -0,0 +1,93 @@
+<template>
+  <view class="">
+    <view class="table-header">
+      <view class="table-header-cell" v-for="item in tableColumns" :key="item.prop" :style="getHeaderStyle(item)">
+        {{ item.label }}
+      </view>
+    </view>
+    <view class="table-body">
+      <block v-if="data.length">
+        <view class="table-row" :class="{ 'has-border': tableConfig.border }" v-for="(row, index) in data" :key="row.id"
+          @click="handleRowClick(row)">
+          <view class="table-row-cell" v-for="item in tableColumns" :key="item.prop" :style="getColumnStyle(item)">
+            <view v-if="item.type === 'index'">
+              {{ index + 1 }}
+            </view>
+            <view v-else>
+              <slot :name="item.slot" :item="row">
+                <text>{{ row[item.prop as keyof StudyRecord] }}</text>
+              </slot>
+            </view>
+          </view>
+        </view>
+      </block>
+      <view v-else class="no-data">暂无数据</view>
+    </view>
+  </view>
+</template>
+<script lang="ts" setup>
+import { TableColumnConfig, TableConfig } from '@/types';
+import { StudyRecord } from '@/types/study';
+
+const props = defineProps({
+  tableConfig: {
+    type: Object as PropType<TableConfig>,
+    default: () => ({})
+  },
+  tableColumns: {
+    type: Array as PropType<TableColumnConfig[]>,
+    default: () => []
+  },
+  data: {
+    type: Array as PropType<StudyRecord[]>,
+    default: () => []
+  }
+});
+const getHeaderStyle = (item: TableColumnConfig) => {
+  return {
+    flex: item.flex ? item.flex : 1,
+    minWidth: '1px',
+    textAlign: item.headerAlign ? item.headerAlign : 'center'
+  };
+};
+const getColumnStyle = (item: TableColumnConfig) => {
+  return {
+    flex: item.flex ? item.flex : 1,
+    minWidth: '1px',
+    textAlign: item.align ? item.align : 'center'
+  };
+};
+const emit = defineEmits<{
+  rowClick: [row: StudyRecord]
+}>();
+const handleRowClick = (row: StudyRecord) => {
+  emit('rowClick', row);
+};
+</script>
+<style lang="scss" scoped>
+.table-header {
+  @apply flex items-center bg-[#EBF9FF] rounded-5;
+}
+
+.table-header-cell {
+  @apply flex-1 py-20 text-30 text-fore-light;
+}
+
+.table-row {
+  @apply flex items-center;
+}
+
+.has-border {
+  &+.has-border {
+    @apply border-0 border-solid border-t border-border;
+  }
+}
+
+.table-row-cell {
+  @apply px-20 py-20 text-28 text-fore-title;
+}
+
+.no-data {
+  @apply mt-16 bg-[#F6F8FA] text-center py-50 text-26 text-fore-tip-light rounded-5;
+}
+</style>

+ 3 - 5
src/hooks/useAppConfig.ts

@@ -1,7 +1,7 @@
 import { EnumAppConfigKey } from "@/common/enum";
-import useAppStore from "@/store/appStore";
+import { useAppStore } from "@/store/appStore";
 
-const useAppConfig = () => {
+export const useAppConfig = () => {
   const appStore = useAppStore();
   /**
    * 短信验证码是否开启图形验证
@@ -12,6 +12,4 @@ const useAppConfig = () => {
   return {
     isSmsCaptchaEnable
   }
-}
-
-export default useAppConfig;
+}

+ 21 - 0
src/hooks/useImage.ts

@@ -0,0 +1,21 @@
+export const useImage = () => {
+  const resolvePath = (src: string) => {
+    // 如果是base64的,直接返回
+    if (src.startsWith('data:image/')) {
+      return src;
+    }
+    // h5的开发环境,补上/h5/src/前缀
+    // #ifdef H5
+    if (import.meta.env.DEV && !src.startsWith('/h5/src/')) {
+      return `/h5/src/${src.replace(/^\//, '')}`;
+    }
+    return src;
+    // #endif
+    // #ifdef MP-WEIXIN
+    return src;
+    // #endif
+  }
+  return {
+    resolvePath
+  }
+}

+ 2 - 4
src/hooks/useNavbar.ts

@@ -1,4 +1,4 @@
-const useNavbar = () => {
+export const useNavbar = () => {
   const height = ref(44);
   const statusBarHeight = uni.getSystemInfoSync().statusBarHeight;
   const baseStickyTop = computed(() => {
@@ -9,6 +9,4 @@ const useNavbar = () => {
     statusBarHeight,
     baseStickyTop
   }
-};
-
-export default useNavbar;
+};

+ 15 - 6
src/hooks/useScroll.ts

@@ -1,14 +1,15 @@
 import { onPageScroll } from '@dcloudio/uni-app';
 
-const useScrollList = (height?: number) => {
+export const useScroll = (height?: number) => {
   const scrollTop = ref(0);
+  const isHide = ref(false);
   const opacity = computed(() => {
     if (height) {
       return Math.max(1 - (scrollTop.value / height), 0)
     }
     return 0;
   });
-  
+
   /**
    * 滚动到指定位置
    * @param selector 
@@ -36,8 +37,18 @@ const useScrollList = (height?: number) => {
     });
   }
 
+  onHide(() => {
+    isHide.value = true;
+  })
+
+  onShow(() => {
+    isHide.value = false;
+  })
+
   onPageScroll((e: any) => {
-    scrollTop.value = e.scrollTop
+    if (!isHide.value) {
+      scrollTop.value = e.scrollTop
+    }
   });
   return {
     scrollTop,
@@ -45,6 +56,4 @@ const useScrollList = (height?: number) => {
     scrollTo,
     getRect
   };
-}
-
-export default useScrollList;
+}

+ 3 - 6
src/hooks/useSms.ts

@@ -1,5 +1,4 @@
-import { ref, computed } from 'vue';
-import useUniStore from './useUniStore';
+import { useUniStore } from './useUniStore';
 import { sendSmsNoValidationNoToken, sendSmsNoToken, sendSms as sendSmsApi } from '@/api/modules/system';
 import { EnumSmsApiType, EnumSmsType } from '@/common/enum';
 
@@ -11,7 +10,7 @@ type SmsParams = {
   code?: string;
 };
 
-const useSms = () => {
+export const useSms = () => {
   const { setItem, getItem, removeItem } = useUniStore();
 
   // 状态常量定义
@@ -188,6 +187,4 @@ const useSms = () => {
     smsIsSending,
     isCountingDown
   };
-};
-
-export default useSms;
+};

+ 2 - 4
src/hooks/useTransferPage.ts

@@ -1,5 +1,5 @@
 import { onLoad } from "@dcloudio/uni-app";
-const useTransferPage = () => {
+export const useTransferPage = () => {
   const funcMap = {
     redirect: uni.redirectTo,
     reLaunch: uni.reLaunch,
@@ -165,6 +165,4 @@ const useTransferPage = () => {
     transferTo,
     transferBack,
   }
-};
-
-export default useTransferPage;
+};

+ 2 - 4
src/hooks/useUniStore.ts

@@ -32,13 +32,11 @@ function removeAll() {
   }
 }
 
-const useUniStore = () => {
+export const useUniStore = () => {
   return {
     getItem,
     setItem,
     removeItem,
     removeAll
   }
-}
-
-export default useUniStore;
+}

+ 2 - 4
src/hooks/useValidation.ts

@@ -1,4 +1,4 @@
-const useValidation = () => {
+export const useValidation = () => {
   const validatePhone = (phone: string): boolean => {
     // 简单的手机号验证规则,可根据实际需求调整
     const phoneRegex = /^1[3-9]\d{9}$/;
@@ -14,6 +14,4 @@ const useValidation = () => {
     validatePhone,
     validateTelephone
   }
-}
-
-export default useValidation;
+}

+ 2 - 1
src/manifest.json

@@ -54,7 +54,8 @@
         "singlePage" : {
             "navigationBarFit" : "float"
         },
-        "optimization":{"subPackages":true}
+        "optimization":{"subPackages":true},
+        "mergeVirtualHostAttributes": true
     },
     "mp-alipay" : {
         "usingComponents" : true

+ 12 - 0
src/pages.json

@@ -576,6 +576,18 @@
           "style": {
             "navigationBarTitleText": ""
           }
+        },
+        {
+          "path": "pages/study-history/study-history",
+          "style": {
+            "navigationBarTitleText": ""
+          }
+        },
+        {
+          "path": "/pages/study-knowledge-detail/study-knowledge-detail",
+          "style": {
+            "navigationBarTitleText": ""
+          }
         }
       ]
     }

+ 1 - 1
src/pagesMain/pages/index/components/index-banner.vue

@@ -14,7 +14,7 @@
   </view>
 </template>
 <script lang="ts" setup>
-import useTransferPage from '@/hooks/useTransferPage';
+import { useTransferPage } from '@/hooks/useTransferPage';
 const { transferTo } = useTransferPage();
 const menus = [
   {

+ 2 - 2
src/pagesMain/pages/index/components/index-guide.vue

@@ -21,8 +21,8 @@
 import { getNewsMainList } from '@/api/modules/news';
 import { Guide } from '@/types/news';
 import config from '@/config';
-import useUserStore from '@/store/userStore';
-import useTransferPage from '@/hooks/useTransferPage';
+import { useUserStore } from '@/store/userStore';
+import { useTransferPage } from '@/hooks/useTransferPage';
 const userStore = useUserStore();
 const { transferTo } = useTransferPage();
 

+ 24 - 6
src/pagesMain/pages/index/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <ie-page tabbar bg-color="white">
+  <ie-page bg-color="white">
     <ie-navbar transparent bg-color="#FFFFFF" :placeholder="false">
       <template #headerLeft>
         <view class="flex items-center">
@@ -27,13 +27,16 @@ import IndexBanner from './components/index-banner.vue';
 import IndexGuide from './components/index-guide.vue';
 import IndexNews from './components/index-news.vue';
 
-import useUserStore from '@/store/userStore';
+import { useUserStore } from '@/store/userStore';
 // @ts-ignore
 import mxConst from '@/common/mxConst';
-import useTransferPage from '@/hooks/useTransferPage';
-import useNavbar from '@/hooks/useNavbar';
+import { useTransferPage } from '@/hooks/useTransferPage';
+import { useNavbar } from '@/hooks/useNavbar';
+import { onPageShow } from '@dcloudio/uni-app';
 const { transferTo } = useTransferPage();
 const { baseStickyTop } = useNavbar();
+const scrollTop = ref(0);
+const isHide = ref(false);
 const userStore = useUserStore();
 
 const handleDetail = async (id: number | string, title?: string) => {
@@ -56,8 +59,23 @@ const handleDetail = async (id: number | string, title?: string) => {
     }
   }
 }
-
-onPageScroll(() => { });
+onHide(() => {
+  isHide.value = true;
+})
+onPageScroll((e) => {
+  if (!isHide.value) {
+    scrollTop.value = e.scrollTop;
+  }
+});
+onShow(() => {
+  isHide.value = false;
+  setTimeout(() => {
+    uni.pageScrollTo({
+      scrollTop: scrollTop.value,
+      duration: 0
+    });
+  }, 0);
+});
 </script>
 
 <style lang="scss" scoped></style>

+ 2 - 2
src/pagesMain/pages/me/components/me-info.vue

@@ -37,8 +37,8 @@
   </view>
 </template>
 <script lang="ts" setup>
-import useUserStore from '@/store/userStore';
-import useTransferPage from '@/hooks/useTransferPage';
+import { useUserStore } from '@/store/userStore';
+import { useTransferPage } from '@/hooks/useTransferPage';
 const userStore = useUserStore();
 const { transferTo } = useTransferPage();
 const avatar = computed(() => userStore.avatar);

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

@@ -41,14 +41,14 @@
         </uv-cell>
       </uv-cell-group>
     </view>
-    <view v-if="userStore.isLogin" class="mt-80 w-400 mx-auto">
+    <view v-if="userStore.isLogin" class="mt-80 mb-40 w-400 mx-auto">
       <uv-button type="error" size="medium" shape="circle" text="退出登陆" plain @click="handleLogout"></uv-button>
     </view>
   </view>
 </template>
 <script lang="ts" setup>
-import useTransferPage from '@/hooks/useTransferPage';
-import useUserStore from '@/store/userStore';
+import { useTransferPage } from '@/hooks/useTransferPage';
+import { useUserStore } from '@/store/userStore';
 const { transferTo } = useTransferPage();
 const userStore = useUserStore();
 

+ 3 - 1
src/pagesMain/pages/me/me.vue

@@ -3,7 +3,9 @@
     <ie-image :is-oss="true" src="/volunteer/page-bg.png" custom-class="w-full h-auto absolute top-0 left-0 -z-1" />
     <me-info />
     <me-menu />
-    <ie-tabbar #tabbar :active="2" />
+    <template #tabbar>
+      <ie-tabbar :active="2" />
+    </template>
   </ie-page>
 </template>
 

+ 4 - 4
src/pagesMain/pages/splash/splash.vue

@@ -8,10 +8,10 @@
 
 <script lang="ts" setup>
 import config from '@/config';
-import useAppStore from '@/store/appStore';
-import useAppConfig from '@/hooks/useAppConfig';
-import useUserStore from '@/store/userStore';
-import useTransferPage from '@/hooks/useTransferPage';
+import { useAppStore } from '@/store/appStore';
+import { useAppConfig } from '@/hooks/useAppConfig';
+import { useUserStore } from '@/store/userStore';
+import { useTransferPage } from '@/hooks/useTransferPage';
 const appStore = useAppStore();
 const userStore = useUserStore();
 const { transferTo } = useTransferPage();

+ 7 - 4
src/pagesMain/pages/volunteer/volunteer.vue

@@ -12,7 +12,8 @@
         class="px-46 h-1/2 absolute left-0 right-0 top-1/2 -translate-y-1/2 flex items-center justify-between box-border pt-50 text-24 text-primary">
         <text>姓名:{{ userStore.nickName }}</text>
         <text>省份:{{ userStore.userInfo.location || '--' }}</text>
-        <text>类别:<ie-dict :dict-name="EnumDictName.EXAM_TYPE" :dict-value="userStore.userInfo.examType || '--'" /></text>
+        <text>类别:<ie-dict :dict-name="EnumDictName.EXAM_TYPE"
+            :dict-value="userStore.userInfo.examType || '--'" /></text>
       </view>
     </view>
     <view class="mx-26">
@@ -26,13 +27,15 @@
         <uv-icon name="arrow-right" size="16" color="#B2B2B2" />
       </view>
     </view>
-    <ie-tabbar #tabbar :active="1" />
+    <template #tabbar>
+      <ie-tabbar :active="1" />
+    </template>
   </ie-page>
 </template>
 
 <script lang="ts" setup>
-import useUserStore from '@/store/userStore';
-import useTransferPage from '@/hooks/useTransferPage';
+import { useUserStore } from '@/store/userStore';
+import { useTransferPage } from '@/hooks/useTransferPage';
 import { EnumDictName } from '@/common/enum';
 const { transferTo } = useTransferPage();
 const userStore = useUserStore();

+ 1 - 1
src/pagesOther/pages/college-library/picker/picker.vue

@@ -17,7 +17,7 @@ import {ref, computed, onMounted} from 'vue';
 import CollegeList from "@/pagesOther/pages/college-library/components/college-list.vue";
 import {useCacheStore} from "@/hooks/useCacheStore";
 import {useTransfer} from "@/hooks/useTransfer";
-import useTransferPage from '@/hooks/useTransferPage';
+import {useTransferPage} from '@/hooks/useTransferPage';
 import {cacheActions} from "@/hooks/defineCacheActions";
 import {findTreeNode} from "@/utils/tree-helper";
 import {toast} from "@/uni_modules/uv-ui-tools/libs/function";

+ 1 - 1
src/pagesOther/pages/news/detail/detail.vue

@@ -44,7 +44,7 @@ onMounted(async () => {
     ::v-deep table {
         width: 100%;
         max-width: 100%;
-        height: auto;
+        height: auto !important;
     }
 
     ::v-deep p {

+ 1 - 1
src/pagesStudy/components/exam-record-item.vue

@@ -1,5 +1,5 @@
 <template>
-  <view class="wrap mb-20 rounded-15 bg-white px-20 py-38">
+  <view class="bg-white">
     <view class="text-30 text-fore-title">长沙民政职业技术学院-大数据与会计</view>
     <view class="mt-16 text-26 text-fore-light">卷面得分 210分</view>
     <view class="mt-8 flex items-center justify-between">

+ 91 - 0
src/pagesStudy/components/knowledge-table.vue

@@ -0,0 +1,91 @@
+<template>
+  <view class="p-30">
+    <ie-table :table-columns="tableColumns" :table-config="tableConfig" :data="data">
+      <template #knowledgeName="{ item }">
+        <view class="">
+          <text class="leading-38">{{ item.knowledgeName }}</text>
+          <text
+            class="ml-10 bg-[#F0FDF4] text-[#22C55E] border border-solid border-[#22C55E] text-20 rounded-4 px-10 py-2">定向</text>
+        </view>
+      </template>
+      <template #rate="{ item }">
+        {{ item.rate }}%
+      </template>
+    </ie-table>
+  </view>
+</template>
+<script lang="ts" setup>
+import { Study, TableColumnConfig, TableConfig } from '@/types';
+
+const props = defineProps({
+  studentId: {
+    type: Number,
+    required: true
+  }
+});
+const tableConfig: TableConfig = {
+  border: true,
+  stripe: false,
+  emptyText: '暂无数据',
+  loading: false,
+  rowKey: 'id'
+}
+const tableColumns: TableColumnConfig[] = [
+  {
+    prop: 'knowledgeName',
+    label: '知识点',
+    flex: 2,
+    slot: 'knowledgeName',
+    headerAlign: 'center',
+    align: 'left'
+  },
+  {
+    prop: 'rate',
+    label: '正确率',
+    flex: 1,
+    slot: 'rate'
+  },
+  {
+    prop: 'questionNum',
+    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 {
+  @apply flex items-center bg-[#EBF9FF] rounded-5;
+}
+
+.table-header-cell {
+  @apply flex-1 text-center py-20 text-30 text-fore-light;
+}
+</style>

+ 1 - 1
src/pagesStudy/components/knowledge-tree-node.vue

@@ -32,7 +32,7 @@
 </template>
 
 <script lang="ts" setup>
-import { TreeData } from '@/types/study';
+import { TreeData } from '@/types';
 
 // 定义 props
 const props = defineProps({

+ 1 - 1
src/pagesStudy/components/knowledge-tree.vue

@@ -9,7 +9,7 @@
   </view>
 </template>
 <script lang="ts" setup>
-import { TreeData } from '@/types/study';
+import { TreeData } from '@/types';
 import KnowledgeTreeNode from './knowledge-tree-node.vue';
 
 // 定义 emits

+ 42 - 0
src/pagesStudy/components/teacher-class-view.vue

@@ -0,0 +1,42 @@
+<template>
+  <view class="p-30">
+    <view class="flex items-center justify-between">
+      <text class="text-30 text-fore-title font-bold">我的班级</text>
+      <view>
+        <ie-picker ref="pickerRef" v-model="classId" :list="classList" placeholder="选择班级" title="选择班级" icon="arrow-down"
+          key-label="name" key-value="classId" @change="handleClassChange"></ie-picker>
+      </view>
+    </view>
+    <view>
+      <slot :teachClass="selectedClass"></slot>
+    </view>
+  </view>
+
+</template>
+<script lang="ts" setup>
+import { TeachClass } from '@/types/study';
+const classId = ref<number>(0);
+const classList = ref<TeachClass[]>([]);
+const selectedClass = ref<TeachClass>();
+const pickerRef = ref();
+const loadData = async () => {
+  // const res = await getClassHistory();
+  // 模拟数据
+  classList.value = [
+    { classId: 1, schoolId: 1, year: 2025, name: '班级1' },
+    { classId: 2, schoolId: 1, year: 2025, name: '班级2' },
+    { classId: 3, schoolId: 1, year: 2025, name: '班级3' },
+  ];
+  // 默认选中第一个
+  classId.value = classList.value[0].classId;
+  selectedClass.value = classList.value[0];
+}
+onMounted(() => {
+  loadData();
+});
+const handleClassChange = (value: number, selectedItem: TeachClass) => {
+  classId.value = value;
+  selectedClass.value = selectedItem;
+}
+</script>
+<style lang="scss" scoped></style>

+ 5 - 3
src/pagesStudy/pages/homework/homework.vue

@@ -2,13 +2,14 @@
   <ie-page bg-color="#F6F8FA" :fix-height="true">
     <ie-navbar title="组卷作业" />
     <view class="bg-white">
-      <uv-tabs :list="list" :activeStyle="activeStyle" :inactiveStyle="inactiveStyle" :scrollable="false" @change="handleTabChange"></uv-tabs>
+      <uv-tabs :list="list" :activeStyle="activeStyle" :inactiveStyle="inactiveStyle" :scrollable="false"
+        @change="handleTabChange"></uv-tabs>
     </view>
     <view class="flex-1 min-h-1 relative">
       <view class="absolute inset-0">
         <z-paging ref="paging" :fixed="false" v-model="dataList" :safe-area-inset-bottom="true"
           :use-safe-area-placeholder="true" :auto="false" empty-view-text="暂无发布组卷作业~"
-          empty-view-img="/pagesStudy/static/image/icon-empty.png" :empty-view-style="emptyViewStyle"
+          :empty-view-img="resolvePath('/pagesStudy/static/image/icon-empty.png')" :empty-view-style="emptyViewStyle"
           :empty-view-img-style="emptyViewImgStyle" :empty-view-title-style="emptyViewTextStyle" @query="handleQuery">
           <view v-if="dataList.length > 0" class="pt-10">
             <view class="card-item" v-for="item in dataList" :key="item.id">
@@ -22,6 +23,8 @@
 </template>
 
 <script lang="ts" setup>
+import { useImage } from '@/hooks/useImage';
+const { resolvePath } = useImage();
 const activeStyle = {
   color: '#1A1A1A'
 }
@@ -61,7 +64,6 @@ const handleTabChange = (item: any) => {
   paging.value.reload();
 }
 const handleQuery = (page: number, pageSize: number) => {
-  console.log(page, pageSize);
   uni.$ie.showLoading();
   setTimeout(() => {
     uni.$ie.hideLoading();

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

@@ -29,7 +29,7 @@
   </view>
 </template>
 <script lang="ts" setup>
-import useTransferPage from '@/hooks/useTransferPage';
+import { useTransferPage } from '@/hooks/useTransferPage';
 const { transferTo } = useTransferPage();
 const navigateTo = (pageUrl: string) => {
   transferTo(pageUrl);

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

@@ -7,9 +7,15 @@
       <ie-image src="/pagesStudy/static/image/icon-ear-right.png" customClass="w-20 h-26 ml-26 mr-30" />
       <view class="h-0 w-160 border-0 border-b border-dashed border-border"></view>
     </view>
-    <exam-record-item />
-    <exam-record-item />
-    <exam-record-item />
+    <view class="rounded-15 mb-20 bg-white px-20 py-38">
+      <exam-record-item />
+    </view>
+    <view class="rounded-15 mb-20 bg-white px-20 py-38">
+      <exam-record-item />
+    </view>
+    <view class="rounded-15 mb-20 bg-white px-20 py-38">
+      <exam-record-item />
+    </view>
   </view>
 </template>
 <script lang="ts" setup>

+ 2 - 2
src/pagesStudy/pages/index/compoentns/index-menu.vue

@@ -7,7 +7,7 @@
   </view>
 </template>
 <script lang="ts" setup>
-import useTransferPage from '@/hooks/useTransferPage';
+import { useTransferPage } from '@/hooks/useTransferPage';
 const { transferTo } = useTransferPage();
 
 const menus = [
@@ -34,7 +34,7 @@ const menus = [
   {
     label: '学习记录',
     icon: '/menu/menu-record.png',
-    pageUrl: '/pagesStudy/record/index'
+    pageUrl: '/pagesStudy/pages/study-history/study-history'
   }
 ]
 const navigateTo = (pageUrl: string) => {

+ 1 - 1
src/pagesStudy/pages/index/compoentns/index-test.vue

@@ -43,7 +43,7 @@
   </view>
 </template>
 <script lang="ts" setup>
-import useTransferPage from '@/hooks/useTransferPage';
+import { useTransferPage } from '@/hooks/useTransferPage';
 const { transferTo } = useTransferPage();
 const tab = ref('0');
 const selectedCollege = ref('长沙民政职业技术学院');

+ 1 - 1
src/pagesStudy/pages/index/index.vue

@@ -60,7 +60,7 @@ import IndexMenu from './compoentns/index-menu.vue';
 import IndexBanner from './compoentns/index-banner.vue';
 import IndexTest from './compoentns/index-test.vue';
 import IndexExamRecord from './compoentns/index-exam-record.vue';
-import useTransferPage from '@/hooks/useTransferPage';
+import { useTransferPage } from '@/hooks/useTransferPage';
 const { transferTo } = useTransferPage();
 
 const navigateTo = (pageUrl: string) => {

+ 1 - 1
src/pagesStudy/pages/simulation-entry/simulation-entry.vue

@@ -35,7 +35,7 @@
 </template>
 
 <script lang="ts" setup>
-import useTransferPage from '@/hooks/useTransferPage';
+import { useTransferPage } from '@/hooks/useTransferPage';
 const { transferTo } = useTransferPage();
 const form = ref({
   college: '长沙民政职业技术学院',

+ 1 - 1
src/pagesStudy/pages/simulation-start/simulation-start.vue

@@ -52,7 +52,7 @@
 </template>
 
 <script lang="ts" setup>
-import useTransferPage from '@/hooks/useTransferPage';
+import { useTransferPage } from '@/hooks/useTransferPage';
 const { transferTo } = useTransferPage();
 
 const handleStartTest = () => {

+ 7 - 0
src/pagesStudy/pages/study-history/components/exam-history-student.vue

@@ -0,0 +1,7 @@
+<template>
+  <view></view>
+</template>
+<script lang="ts" setup>
+
+</script>
+<style lang="scss" scoped></style>

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

@@ -0,0 +1,7 @@
+<template>
+  <view></view>
+</template>
+<script lang="ts" setup>
+
+</script>
+<style lang="scss" scoped></style>

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

@@ -0,0 +1,49 @@
+<template>
+  <view class="bg-white">
+    <view class="p-30 pb-40 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>
+      </view>
+      <view class="exam-type-item" :class="{ 'is-active': examType === 'homework' }"
+        @click="handleChangeExamType('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="type === 'student'" />
+    <exam-history-teacher /> -->
+  </view>
+</template>
+<script lang="ts" setup>
+import teacherClassView from '@/pagesStudy/components/teacher-class-view.vue';
+import examHistoryStudent from './exam-history-student.vue';
+import examHistoryTeacher from './exam-history-teacher.vue';
+const userType = ref('teacher');
+const examType = ref('test')
+const loadData = async () => {
+  // const res = await getExamHistory(userType.value, examType.value);
+  // console.log(res);
+}
+const handleChangeExamType = (type: string) => {
+  examType.value = type;
+  loadData();
+}
+</script>
+<style lang="scss" scoped>
+.exam-type-item {
+  @apply flex-1 h-175 rounded-15 bg-[#F5F5F5] flex flex-col items-center justify-center gap-y-10;
+}
+
+.exam-type-text {
+  @apply text-28 text-fore-title font-bold;
+}
+
+.is-active {
+  @apply bg-[#E6F7FF];
+
+  .exam-type-text {
+    @apply text-primary;
+  }
+}
+</style>

+ 9 - 0
src/pagesStudy/pages/study-history/components/knowledge-history-student.vue

@@ -0,0 +1,9 @@
+<template>
+  <knowledge-table :student-id="studentId" />
+</template>
+<script lang="ts" setup>
+import knowledgeTable from '@/pagesStudy/components/knowledge-table.vue';
+// 从用户信息中取值
+const studentId = ref(1);
+</script>
+<style lang="scss" scoped></style>

+ 54 - 0
src/pagesStudy/pages/study-history/components/knowledge-history-teacher.vue

@@ -0,0 +1,54 @@
+<template>
+  <view class="mt-40 flex items-center justify-end gap-x-10">
+    <text class="text-24 text-fore-light">平均正确率</text>
+    <text class="text-30 text-fore-title font-bold">80%</text>
+  </view>
+  <view class="mt-10">
+    <progress :percent="80" stroke-width="12" activeColor="#68D119" backgroundColor="#F2F2F2" :border-radius="8"
+      class="custom-progress" />
+  </view>
+  <view class="mt-76">
+    <student-stat-table :teach-class="teachClass" :data="studentStatData" />
+  </view>
+</template>
+<script lang="ts" setup>
+import studentStatTable from './student-stat-table.vue';
+import { StudentStat, TeachClass } from '@/types/study';
+const props = defineProps({
+  teachClass: {
+    type: Object as PropType<TeachClass>,
+    default: () => ({})
+  }
+});
+const studentStatData = ref<StudentStat[]>([
+  {
+    id: 1,
+    name: '张三',
+    questionNum: 10,
+    dateNum: 10,
+    rate: 80
+  },
+  {
+    id: 2,
+    name: '李四',
+    questionNum: 10,
+    dateNum: 10,
+    rate: 80
+  }
+]);
+watch(() => props.teachClass, (newVal) => {
+  if (newVal.classId) {
+    console.log('新的班级', newVal);
+    loadData();
+  }
+}, {
+  deep: true,
+  immediate: true
+});
+
+const loadData = async () => {
+  // const res = await getStudentStat(props.teachClass.classId);
+  // console.log(res);
+}
+</script>
+<style lang="scss" scoped></style>

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

@@ -0,0 +1,39 @@
+<template>
+  <view class="bg-white">
+    <knowledge-history-student v-if="type === 'student'" />
+    <teacher-class-view v-else-if="type === 'teacher'">
+      <template #default="{ teachClass }">
+        <knowledge-history-teacher :teach-class="teachClass" />
+      </template>
+    </teacher-class-view>
+  </view>
+</template>
+<script lang="ts" setup>
+import teacherClassView from '@/pagesStudy/components/teacher-class-view.vue';
+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';
+const { transferTo } = useTransferPage();
+// const type = ref('student');
+const type = ref('teacher');
+
+const openKnowledgeDetail = (id: number, name: string) => {
+  transferTo('/pagesStudy/pages/study-knowledge-detail/study-knowledge-detail', {
+    data: {
+      studentId: id,
+      name: name
+    }
+  });
+}
+provide(OPEN_KNOWLEDGE_DETAIL, openKnowledgeDetail);
+</script>
+<style lang="scss" scoped>
+:deep(.custom-progress .uni-progress-bar) {
+  @apply rounded-8;
+}
+
+:deep(.custom-progress .uni-progress-inner-bar) {
+  @apply rounded-8;
+}
+</style>

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

@@ -0,0 +1,7 @@
+<template>
+  <view></view>
+</template>
+<script lang="ts" setup>
+
+</script>
+<style lang="scss" scoped></style>

+ 7 - 0
src/pagesStudy/pages/study-history/components/practice-history-teacher.vue

@@ -0,0 +1,7 @@
+<template>
+  <view></view>
+</template>
+<script lang="ts" setup>
+
+</script>
+<style lang="scss" scoped></style>

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

@@ -0,0 +1,7 @@
+<template>
+  <view></view>
+</template>
+<script lang="ts" setup>
+
+</script>
+<style lang="scss" scoped></style>

+ 66 - 0
src/pagesStudy/pages/study-history/components/student-stat-table.vue

@@ -0,0 +1,66 @@
+<template>
+  <view>
+    <ie-table :table-columns="tableColumns" :table-config="tableConfig" :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, TableConfig } from '@/types';
+import { OPEN_KNOWLEDGE_DETAIL } from '@/types/injectionSymbols';
+import { TeachClass, StudentStat } from '@/types/study';
+const props = defineProps({
+  teachClass: {
+    type: Object as PropType<TeachClass>,
+    default: () => ({})
+  },
+  data: {
+    type: Array as PropType<StudentStat[]>,
+    default: () => []
+  }
+});
+const tableConfig: TableConfig = {
+  border: true,
+  stripe: false,
+  emptyText: '暂无数据',
+  loading: false,
+  rowKey: 'id'
+}
+const tableColumns: TableColumnConfig[] = [
+  {
+    prop: '',
+    label: '序号',
+    type: 'index',
+    flex: 0.5
+  },
+  {
+    prop: 'name',
+    label: '姓名',
+    slot: 'name'
+  },
+  {
+    prop: 'questionNum',
+    label: '题量',
+    flex: 0.6
+  },
+  {
+    prop: 'dateNum',
+    label: '天数',
+    flex: 0.6
+  },
+  {
+    prop: 'rate',
+    label: '正确率'
+  }
+]
+const openKnowledgeDetail = inject(OPEN_KNOWLEDGE_DETAIL);
+const handleRowClick = (row: StudentStat) => {
+  openKnowledgeDetail?.(row.id, row.name);
+}
+</script>
+<style lang="scss" scoped></style>

+ 7 - 0
src/pagesStudy/pages/study-history/components/video-history-student.vue

@@ -0,0 +1,7 @@
+<template>
+  <view></view>
+</template>
+<script lang="ts" setup>
+
+</script>
+<style lang="scss" scoped></style>

+ 7 - 0
src/pagesStudy/pages/study-history/components/video-history-teacher.vue

@@ -0,0 +1,7 @@
+<template>
+  <view></view>
+</template>
+<script lang="ts" setup>
+
+</script>
+<style lang="scss" scoped></style>

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

@@ -0,0 +1,7 @@
+<template>
+  <view></view>
+</template>
+<script lang="ts" setup>
+
+</script>
+<style lang="scss" scoped></style>

+ 42 - 0
src/pagesStudy/pages/study-history/study-history.vue

@@ -0,0 +1,42 @@
+<template>
+  <ie-page bg-color="#F6F8FA">
+    <ie-navbar title="学习记录" />
+    <view class="bg-white sticky" :style="{ top: baseStickyTop + 'px' }">
+      <uv-tabs :list="list" :current="current" :activeStyle="activeStyle" :inactiveStyle="inactiveStyle"
+        :scrollable="false" @change="handleTabChange"></uv-tabs>
+    </view>
+    <view class="h-20"></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>
+
+<script lang="ts" setup>
+import { useNavbar } from '@/hooks/useNavbar';
+import knowledgeHistory from './components/knowledge-history.vue'
+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(1);
+const list = [
+  { name: '知识点记录', },
+  { name: '试卷记录', },
+  { name: '刷题总量', },
+  { name: '视频总数', }
+]
+const activeStyle = {
+  fontWeight: 'bold',
+  color: '#31A0FC'
+}
+const inactiveStyle = {
+  color: '#999999'
+}
+const handleTabChange = (e: any) => {
+  current.value = e.index;
+}
+</script>
+
+<style lang="scss" scoped></style>

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

@@ -0,0 +1,26 @@
+<template>
+  <ie-page bg-color="#F6F8FA">
+    <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>
+      <knowledge-table :student-id="studentId" />
+    </view>
+  </ie-page>
+</template>
+<script lang="ts" setup>
+import knowledgeTable from '@/pagesStudy/components/knowledge-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>

+ 1 - 1
src/pagesStudy/pages/study-plan-edit/components/course-setting.vue

@@ -1,7 +1,7 @@
 <template>
   <uv-popup ref="popup" mode="bottom" round="16" :close-on-click-overlay="false">
     <view class="py-23 bg-white rounded-16 overflow-hidden">
-      <view class="flex items-center justify-between">
+      <view class="flex items-center justify-between pt-20">
         <view class="px-46 py-20 text-28 text-fore-light" @click="handleCancel">取消</view>
         <text class="text-30 text-fore-title font-bold">课程学习</text>
         <view class="px-46 py-20 text-28 text-fore-title" @click="handleConfirm">确认</view>

+ 1 - 1
src/pagesStudy/pages/study-plan-edit/components/question-setting.vue

@@ -1,7 +1,7 @@
 <template>
   <uv-popup ref="popup" mode="bottom" round="16" :close-on-click-overlay="false">
     <view class="py-23 bg-white rounded-16 overflow-hidden">
-      <view class="flex items-center justify-between">
+      <view class="flex items-center justify-between pt-20">
         <view class="px-46 py-20 text-28 text-fore-light" @click="handleCancel">取消</view>
         <text class="text-30 text-fore-title font-bold">题库练习</text>
         <view class="px-46 py-20 text-28 text-fore-title" @click="handleConfirm">确认</view>

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

@@ -14,7 +14,7 @@
 <script lang="ts" setup>
 import PageHeader from './components/page-header.vue';
 import PageCalendar from './components/page-calendar.vue';
-import useTransferPage from '@/hooks/useTransferPage';
+import { useTransferPage } from '@/hooks/useTransferPage';
 const { transferTo } = useTransferPage();
 onPageScroll(() => { });
 

+ 2 - 2
src/pagesStudy/pages/targeted-setting/targeted-setting.vue

@@ -18,7 +18,7 @@
     <view v-if="hasSetting" class="px-30">
       <view class="bg-white rounded-10 py-28 pl-32 pr-20 relative flex items-center gap-x-20 mb-20"
         v-for="(item, index) in collegeList" :key="item.code">
-        <view class="text-64 text-primary font-bold absolute top-6 left-14 italic leading-[1] opacity-30">
+        <view class="w-44 text-64 text-primary font-bold absolute top-6 left-14 italic leading-[1] opacity-30 z-1">
           {{ index + 1 }}
         </view>
         <view class="w-40 h-40 bg-back"></view>
@@ -53,7 +53,7 @@
 </template>
 
 <script lang="ts" setup>
-import useTransferPage from '@/hooks/useTransferPage';
+import { useTransferPage } from '@/hooks/useTransferPage';
 const { transferTo } = useTransferPage();
 const hasSetting = ref(false);
 const collegeList = ref<any[]>([]);

BIN
src/pagesStudy/static/image/icon-exam-homework.png


BIN
src/pagesStudy/static/image/icon-exam-test.png


+ 5 - 5
src/pagesSystem/pages/login/login.vue

@@ -59,14 +59,14 @@
 </template>
 
 <script lang="ts" setup>
-import useUserStore from '@/store/userStore';
+import { useUserStore } from '@/store/userStore';
 import config from '@/config';
-import useTransferPage from '@/hooks/useTransferPage';
-import useValidation from '@/hooks/useValidation';
-// import useDictStore from '@/hooks/useDictStore';
+import { useTransferPage } from '@/hooks/useTransferPage';
+import { useValidation } from '@/hooks/useValidation';
+// import {useDictStore} from '@/hooks/useDictStore';
 import { EnumSmsApiType } from '@/common/enum';
 import { mobileLogin } from '@/api/modules/login';
-import { MobileLoginRequestDTO, MobileLoginResponseDTO } from '@/types';
+import { MobileLoginRequestDTO, MobileLoginResponseDTO } from '@/types/user';
 const { transferBack, transferTo } = useTransferPage();
 const userStore = useUserStore();
 const { validatePhone, validateTelephone } = useValidation();

+ 24 - 20
src/pagesSystem/pages/user-profile/user-profile.vue

@@ -4,62 +4,65 @@
     <uv-form labelPosition="left" :model="form" labelWidth="70px" ref="formRef">
       <content-card title="考生信息">
         <uv-form-item label="学生姓名" prop="name" borderBottom required>
-          <uv-input v-model="form.nickName" border="none" placeholder="请输入姓名" :customStyle="customStyle">
+          <uv-input v-model="form.nickName" border="none" placeholder="请输入姓名" :custom-style="customStyle">
           </uv-input>
         </uv-form-item>
         <uv-form-item label="所在省份" prop="location" borderBottom required>
           <ie-picker ref="pickerRef" v-model="form.location" :list="appStore.provinceList" placeholder="选择省份"
-            :customStyle="customStyle" @change="handleProvinceChange"></ie-picker>
+            :custom-style="customStyle" key-label="dictLabel" key-value="dictValue"
+            @change="handleProvinceChange"></ie-picker>
         </uv-form-item>
         <uv-form-item label="考生类别" prop="examType" borderBottom required>
           <ie-picker ref="pickerRef" v-model="form.examType" :list="examTypeList" :disabled="!form.location"
-            placeholder="选择考生类别" :customStyle="customStyle" @change="handleExamTypeChange"></ie-picker>
+            placeholder="选择考生类别" :custom-style="customStyle" key-label="dictLabel" key-value="dictValue"
+            @change="handleExamTypeChange"></ie-picker>
         </uv-form-item>
         <uv-form-item v-if="form.examType === 'VHS'" label="专业类别" prop="majorType" borderBottom required>
           <ie-picker ref="pickerRef" v-model="form.majorType" :list="majorTypes" :disabled="!form.examType"
-            placeholder="选择专业类别" :customStyle="customStyle" @change="handleMajorChange"></ie-picker>
+            placeholder="选择专业类别" :custom-style="customStyle" key-label="dictLabel" key-value="dictValue"
+            @change="handleMajorChange"></ie-picker>
         </uv-form-item>
         <uv-form-item label="毕业年份" prop="year" required>
           <ie-picker ref="pickerRef" v-model="form.endYear" :list="endYearList" placeholder="选择毕业年份"
-            :customStyle="customStyle"></ie-picker>
+            :custom-style="customStyle" key-label="dictLabel" key-value="dictValue"></ie-picker>
         </uv-form-item>
 
       </content-card>
       <content-card title="邀请信息">
         <uv-form-item label="邀请码" prop="form.inviteCode">
-          <uv-input v-model="form.inviteCode" border="none" placeholder="请输入邀请码(非必填)" :customStyle="customStyle">
+          <uv-input v-model="form.inviteCode" border="none" placeholder="请输入邀请码(非必填)" :custom-style="customStyle">
           </uv-input>
         </uv-form-item>
       </content-card>
       <content-card title="文化素质">
         <uv-form-item label="语文" prop="form.scores.chinese" borderBottom>
-          <uv-input v-model="scores.chinese" border="none" placeholder="满分100分" :customStyle="customStyle">
+          <uv-input v-model="scores.chinese" border="none" placeholder="满分100分" :custom-style="customStyle">
           </uv-input>
         </uv-form-item>
         <uv-form-item label="数学" prop="form.score.mathematics" borderBottom>
-          <uv-input v-model="scores.mathematics" border="none" placeholder="满分100分" :customStyle="customStyle">
+          <uv-input v-model="scores.mathematics" border="none" placeholder="满分100分" :custom-style="customStyle">
           </uv-input>
         </uv-form-item>
         <uv-form-item label="外语" prop="form.scores.foreign" borderBottom>
-          <uv-input v-model="scores.foreign" border="none" placeholder="满分100分" :customStyle="customStyle">
+          <uv-input v-model="scores.foreign" border="none" placeholder="满分100分" :custom-style="customStyle">
           </uv-input>
         </uv-form-item>
         <uv-form-item label="物理" prop="form.scores.physics" borderBottom>
-          <uv-input v-model="scores.physics" border="none" placeholder="满分100分" :customStyle="customStyle">
+          <uv-input v-model="scores.physics" border="none" placeholder="满分100分" :custom-style="customStyle">
           </uv-input>
         </uv-form-item>
         <uv-form-item label="政治" prop="form.scores.political">
-          <uv-input v-model="scores.political" border="none" placeholder="满分100分" :customStyle="customStyle">
+          <uv-input v-model="scores.political" border="none" placeholder="满分100分" :custom-style="customStyle">
           </uv-input>
         </uv-form-item>
       </content-card>
       <!-- <content-card title="学校信息">
         <uv-form-item label="学校名称" prop="form.name">
-          <uv-input v-model="form.name" border="none" placeholder="请输入学校名称" :customStyle="customStyle">
+          <uv-input v-model="form.name" border="none" placeholder="请输入学校名称" :custom-style="customStyle">
           </uv-input>
         </uv-form-item>
         <uv-form-item label="所在班级" prop="form.name">
-          <uv-input v-model="form.name" border="none" placeholder="请输入所在班级" :customStyle="customStyle">
+          <uv-input v-model="form.name" border="none" placeholder="请输入所在班级" :custom-style="customStyle">
           </uv-input>
         </uv-form-item>
       </content-card> -->
@@ -74,16 +77,17 @@
 
 <script lang="ts" setup>
 import ContentCard from './components/content-card.vue';
-import useUserStore from '@/store/userStore';
-import {  } from '@/api/modules/login';
+import { useUserStore } from '@/store/userStore';
+import { } from '@/api/modules/login';
 import { getExamTypes, getExamMajors, getGraduateYears } from '@/api/modules/system';
 import { registry } from '@/api/modules/login';
-import useTransferPage from '@/hooks/useTransferPage';
+import { useTransferPage } from '@/hooks/useTransferPage';
 
-import useAppStore from '@/store/appStore';
-import useDictStore from '@/store/dictStore';
+import { useAppStore } from '@/store/appStore';
+import { useDictStore } from '@/store/dictStore';
 import config from "@/config";
-import { DictItem, PickerItem, RegisterInfo, Scores } from '@/types';
+import { RegisterInfo, Scores } from '@/types/user';
+import { DictItem } from '@/types';
 const userStore = useUserStore();
 const appStore = useAppStore();
 const dictStore = useDictStore();
@@ -218,7 +222,7 @@ const handleSubmit = async () => {
     registry(params as RegisterInfo).then(res => {
       const token = res.token;
       if (token) {
-        
+
       }
     });
   }

+ 5 - 0
src/static/theme/var.scss

@@ -7,10 +7,13 @@ $fore-subtitle: #333;
 $fore-content: #555;
 $fore-subcontent: #666;
 $fore-tip: #777;
+$fore-tip-light: #808080;
 $fore-light: #999;
 $fore-disabled: #b9bcc5;
 $fore-placeholder: #B3B3B3;
 
+$border: #E6E6E6;
+
 $back: #f6f6f6;
 $back-light: #f7f8fa;
 
@@ -48,6 +51,8 @@ $base: map-merge((), ('ie-light': $ie-light,
       'fore-disabled': $fore-disabled,
       'fore-placeholder': $fore-placeholder,
 
+      'border': $border,
+
       'back': $back,
       'back-light': $back-light,
 

+ 2 - 4
src/store/appStore.ts

@@ -1,13 +1,13 @@
 import { defineStore } from 'pinia';
 import { AppStoreState, ConfigItem, DictItem, PickerItem } from '@/types';
 import { getConfig, getProvinces } from '@/api/modules/system';
-import useDictStore from '@/store/dictStore';
+import { useDictStore } from '@/store/dictStore';
 import { EnumDictName } from '@/common/enum';
 const preloadDicts: string[] = [
   EnumDictName.EXAM_TYPE
 ];
 
-const useAppStore = defineStore('ie-app', {
+export const useAppStore = defineStore('ie-app', {
   state: (): AppStoreState => {
     return {
       isInitialized: false,
@@ -97,5 +97,3 @@ const useAppStore = defineStore('ie-app', {
     omit: [],
   }
 });
-
-export default useAppStore;

+ 2 - 4
src/store/dictStore.ts

@@ -2,7 +2,7 @@ import { defineStore } from 'pinia';
 import { getDictData } from '@/api/modules/system';
 import { DictItem, DictStoreState } from '@/types';
 
-const useDictStore = defineStore('ie-dict', {
+export const useDictStore = defineStore('ie-dict', {
   state: (): DictStoreState => {
     return {
       dicts: {}
@@ -63,6 +63,4 @@ const useDictStore = defineStore('ie-dict', {
       this.dicts = {};
     }
   }
-});
-
-export default useDictStore;
+});

+ 3 - 5
src/store/userStore.ts

@@ -1,5 +1,5 @@
 import { defineStore } from 'pinia';
-import useTransferPage from '@/hooks/useTransferPage';
+import { useTransferPage } from '@/hooks/useTransferPage';
 import { ref, computed } from 'vue';
 import { getUserInfo } from '@/api/modules/login';
 import avatarDefault from '@/static/image/avatar.png';
@@ -15,7 +15,7 @@ type CheckLoginOptions = {
   askToLogin?: boolean;
 }
 
-const useUserStore = defineStore('ie-user', {
+export const useUserStore = defineStore('ie-user', {
   state: (): UserStoreState => ({
     accessToken: null,
     user: null,
@@ -124,6 +124,4 @@ const useUserStore = defineStore('ie-user', {
       setItem: uni.setStorageSync,
     }
   }
-})
-
-export default useUserStore;
+});

+ 33 - 84
src/types/index.ts

@@ -1,4 +1,6 @@
-import { EnumSmsType } from "@/common/enum";
+import * as Study from "./study";
+import * as User from "./user";
+import * as News from "./news";
 
 /// 接口响应
 export interface ApiResponse<T> {
@@ -7,7 +9,7 @@ export interface ApiResponse<T> {
   data: T;
   // 以下是注册登录才有的
   token?: string;
-  user?: UserInfo;
+  user?: User.UserInfo;
   isDefaultModifyPwd?: boolean;
   isPasswordExpired?: boolean;
 }
@@ -26,53 +28,31 @@ export interface ApiResponseList<T> {
   total: number;
 }
 
-export interface LoginInfo {
-  accessToken: string;
-  refreshToken: string;
-  expiresTime: string;
-  openid: string;
+export interface TreeData {
+  name: string;
+  value: number;
+  children?: TreeData[];
+  isExpanded?: boolean;
+  isLeaf?: boolean;
+  actualHeight?: number;
 }
 
-export interface RegisterInfo {
-  // classId: number;
-
-  endYear: number;
-  examType: string;
-  inviteCode?: string;
-  location: string;
-  majorType?: string;
-  nickName: string;
-  // schoolId: number;
-  scores: Scores;
-  // username: string;
-  mobile: string;
-  password: string;
-  code: string;
-  uuid: string;
-}
-
-export interface Scores {
-  biology?: number;
-  chemistry?: number;
-  physics?: number;
-  chinese?: number;
-  english?: number;
-  foreign?: number;
-  geography?: number;
-  history?: number;
-  mathematics?: number;
-  political?: number;
+export interface TableConfig {
+  border?: boolean;
+  stripe?: boolean;
+  emptyText?: string;
+  loading?: boolean;
+  rowKey?: string;
 }
 
-/**
- * 配置项
- */
-export interface ConfigItem {
-  configKey: string;
-  configValue: string;
-  configType: string;
-  configName: string;
-  configId: number;
+export interface TableColumnConfig {
+  prop: string;
+  label: string;
+  flex?: number;
+  slot?: string;
+  type?: string;
+  headerAlign?: 'left' | 'center' | 'right';
+  align?: 'left' | 'center' | 'right';
 }
 
 /**
@@ -92,18 +72,11 @@ export interface DictStoreState {
 
 export interface UserStoreState {
   accessToken: string | null;
-  user: UserInfo | null;
+  user: User.UserInfo | null;
   isDefaultModifyPwd?: boolean;
   isPasswordExpired?: boolean;
 }
 
-export interface SmsRequestDTO {
-  mobile: string;
-  smsType?: EnumSmsType;
-  uuid?: string;
-  code?: string;
-}
-
 /**
  * 字典项
  */
@@ -117,38 +90,14 @@ export interface PickerItem {
   value: string | number;
 }
 
-export interface CaptchaImage {
-  img: string;
-  uuid: string;
-}
-
-export interface MobileLoginRequestDTO {
-  code?: string;
-  mobile: string;
-  password?: string;
-  username?: string;
-  uuid?: string;
+export interface ConfigItem {
+  configKey: string;
+  configValue: string;
+  configType: string;
+  configName: string;
+  configId: number;
 }
 
-export interface MobileLoginResponseDTO {
-  code?: number;
-  message?: string;
-}
 
 
-export interface UserInfo {
-  admin: boolean;
-  avatar?: string;
-  cardId?: number;
-  code: string;
-  endYear: number;
-  examType: string;
-  inviteCode?: string;
-  location: string;
-  nickName: string;
-  phonenumber: string;
-  regStatus: string;
-  userId: number;
-  userName: string;
-  scores: Scores;
-}
+export { Study, User, News };

+ 6 - 0
src/types/injectionSymbols.ts

@@ -0,0 +1,6 @@
+import type { InjectionKey } from 'vue'
+
+/**
+ * 打开知识点记录详情
+ */
+export const OPEN_KNOWLEDGE_DETAIL = Symbol('OPEN_KNOWLEDGE_DETAIL') as InjectionKey<(id: number, name: string) => void>;

+ 20 - 6
src/types/study.ts

@@ -1,8 +1,22 @@
-export interface TreeData {
+export interface StudyRecord {
+  id: number;
+  knowledgeName: string;
+  rate: number;
+  questionNum: number;
+}
+
+export interface TeachClass {
+  classId: number;
+  schoolId: number;
+  year: number;
   name: string;
-  value: number;
-  children?: TreeData[];
-  isExpanded?: boolean;
-  isLeaf?: boolean;
-  actualHeight?: number;
+}
+
+export interface StudentStat {
+  id: number;
+  avatar?: string;
+  name: string;
+  questionNum: number;
+  dateNum: number;
+  rate: number;
 }

+ 83 - 0
src/types/user.ts

@@ -0,0 +1,83 @@
+import { EnumSmsType } from "@/common/enum";
+
+export interface LoginInfo {
+  accessToken: string;
+  refreshToken: string;
+  expiresTime: string;
+  openid: string;
+}
+
+
+export interface RegisterInfo {
+  // classId: number;
+
+  endYear: number;
+  examType: string;
+  inviteCode?: string;
+  location: string;
+  majorType?: string;
+  nickName: string;
+  // schoolId: number;
+  scores: Scores;
+  // username: string;
+  mobile: string;
+  password: string;
+  code: string;
+  uuid: string;
+}
+
+export interface Scores {
+  biology?: number;
+  chemistry?: number;
+  physics?: number;
+  chinese?: number;
+  english?: number;
+  foreign?: number;
+  geography?: number;
+  history?: number;
+  mathematics?: number;
+  political?: number;
+}
+
+export interface SmsRequestDTO {
+  mobile: string;
+  smsType?: EnumSmsType;
+  uuid?: string;
+  code?: string;
+}
+
+export interface CaptchaImage {
+  img: string;
+  uuid: string;
+}
+
+export interface MobileLoginRequestDTO {
+  code?: string;
+  mobile: string;
+  password?: string;
+  username?: string;
+  uuid?: string;
+}
+
+export interface MobileLoginResponseDTO {
+  code?: number;
+  message?: string;
+}
+
+
+export interface UserInfo {
+  admin: boolean;
+  avatar?: string;
+  cardId?: number;
+  code: string;
+  endYear: number;
+  examType: string;
+  inviteCode?: string;
+  location: string;
+  nickName: string;
+  phonenumber: string;
+  regStatus: string;
+  userId: number;
+  userName: string;
+  scores: Scores;
+}

+ 11 - 6
src/uni_modules/uv-picker/components/uv-picker/uv-picker.vue

@@ -7,7 +7,8 @@
 		@change="popupChange"
 	>
 		<view class="uv-picker">
-			<uv-toolbar
+			<slot name="toolbar">
+        <uv-toolbar
 				v-if="showToolbar"
 				:cancelColor="cancelColor"
 				:confirmColor="confirmColor"
@@ -17,6 +18,7 @@
 				@cancel="cancel"
 				@confirm="confirm"
 			></uv-toolbar>
+      </slot>
 			<!-- #ifdef MP-TOUTIAO -->
 			<picker-view
 				class="uv-picker__view"
@@ -175,14 +177,17 @@ export default {
 			this.$emit('cancel');
 			this.close();
 		},
+    manualConfirm() {
+      return this.$uv.deepClone({
+        indexs: this.innerIndex,
+        value: this.innerColumns.map((item, index) => item[this.innerIndex[index]]),
+        values: this.innerColumns
+      });
+    },
 		// 点击工具栏的确定按钮
 		confirm() {
 			// 在这里使用deepClone拷贝后,vue3会自动转换成原始对象,这样处理是因为cli项目可能出现不返回值的情况
-			this.$emit('confirm', this.$uv.deepClone({
-				indexs: this.innerIndex,
-				value: this.innerColumns.map((item, index) => item[this.innerIndex[index]]),
-				values: this.innerColumns
-			}));
+			this.$emit('confirm', this.manualConfirm());
 			if(this.closeOnClickConfirm) {
 				this.close();
 			}

+ 4 - 2
src/uni_modules/uv-tabs/components/uv-tabs/uv-tabs.vue

@@ -201,8 +201,10 @@
 				// 这里需要一个定时器,因为在非nvue下,是直接通过style绑定过渡时间,需要等其过渡完成后,再设置为false(非第一次移动滑块)
 				if (this.firstTime) {
 					setTimeout(() => {
-						this.firstTime = false
-					}, 20);
+						this.$nextTick(() => {
+							this.firstTime = false
+						})
+					}, 60);
 				}
 			},
 			// nvue下设置滑块的位置

+ 1 - 0
src/uni_modules/uv-toolbar/components/uv-toolbar/uv-toolbar.vue

@@ -95,6 +95,7 @@
 			color: $uv-main-color;
 			padding: 0 60rpx;
 			font-size: 16px;
+      font-weight: bold;
 			flex: 1;
 			text-align: center;
 		}

+ 5 - 1
tailwind.config.js

@@ -85,7 +85,7 @@ module.exports = {
         warning: genSimilarColorsName('warning'),
         error: genSimilarColorsName('error'),
         info: genSimilarColorsName('info'),
-        ...genSimpleColorsName(['main', 'content', 'tips', 'light', 'border', 'bg', 'disabled']),
+        ...genSimpleColorsName(['main', 'content', 'tips', 'light', 'bg', 'disabled']),
         // 只添加部分颜色,防止覆盖掉老颜色
         fore: {
           title: "var(--fore-title)",
@@ -93,6 +93,7 @@ module.exports = {
           content: "var(--fore-content)",
           subcontent: "var(--fore-subcontent)",
           tip: "var(--fore-tip)",
+          'tip-light': "var(--fore-tip-light)",
           light: "var(--fore-light)",
           disabled: "var(--fore-disabled)",
           placeholder: "var(--fore-placeholder)",
@@ -100,6 +101,9 @@ module.exports = {
         back: {
           DEFAULT: "var(--back)",
           light: "var(--back-light)",
+        },
+        border: {
+          DEFAULT: "var(--border)",
         }
       },
       // borderWidth: {