Quellcode durchsuchen

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

abpcoder vor 18 Stunden
Ursprung
Commit
7405b9ca6c
27 geänderte Dateien mit 403 neuen und 170 gelöschten Zeilen
  1. 9 0
      src/api/modules/major.ts
  2. 11 1
      src/api/modules/university.ts
  3. 25 0
      src/common/routes.ts
  4. 7 1
      src/components/ie-dropdown/ie-dropdown-item.vue
  5. 3 1
      src/components/ie-dropdown/ie-dropdown.vue
  6. 18 6
      src/components/ie-tabs-swiper/ie-tabs-swiper.vue
  7. 6 0
      src/pages.json
  8. 1 1
      src/pagesMain/pages/index/index.vue
  9. 5 19
      src/pagesMain/pages/me/components/me-menu.vue
  10. 0 0
      src/pagesOther/components/university-item.vue
  11. 2 2
      src/pagesOther/pages/career/detail/components/related-jobs.vue
  12. 6 8
      src/pagesOther/pages/career/detail/detail.vue
  13. 35 0
      src/pagesOther/pages/collect/collect.vue
  14. 74 0
      src/pagesOther/pages/collect/components/collect-major.vue
  15. 20 0
      src/pagesOther/pages/collect/components/collect-university.vue
  16. 18 18
      src/pagesOther/pages/major/detail/components/related-university.vue
  17. 6 8
      src/pagesOther/pages/major/detail/detail.vue
  18. 14 9
      src/pagesOther/pages/news/index/index.vue
  19. 75 66
      src/pagesOther/pages/university/detail/detail.vue
  20. 2 1
      src/pagesOther/pages/university/index/components/college-list.vue
  21. 2 1
      src/pagesOther/pages/university/index/components/plus/college-conditions-picker.vue
  22. 19 19
      src/pagesOther/pages/university/index/index.vue
  23. 8 8
      src/pagesStudy/pages/index/compoentns/index-menu.vue
  24. 3 1
      src/pagesSystem/pages/setting/setting.vue
  25. BIN
      src/static/personal/icon-protocol.png
  26. 19 0
      src/store/userStore.ts
  27. 15 0
      src/types/major.ts

+ 9 - 0
src/api/modules/major.ts

@@ -54,4 +54,13 @@ export function collectMajor(code: string) {
  */
 export function cancelCollectMajor(code: string) {
   return flyio.get('/front/customer/marjors/remove', { code }) as Promise<ApiResponse<any>>;
+}
+
+/**
+ * 获取收藏的专业
+ * @param params 
+ * @returns 
+ */
+export function getCollectedMajors(params: any) {
+  return flyio.get('/front/customer/marjors/list', params) as Promise<ApiResponseList<Major.CollectedMajor>>;
 }

+ 11 - 1
src/api/modules/university.ts

@@ -1,6 +1,7 @@
 import { ApiResponse, ApiResponseList } from "@/types";
 import flyio from "../flyio";
 import { University, UniversityDetail, UniversityFilter, UniversityTier } from "@/types/university";
+import { Major } from "@/types";
 // import {sleep} from "@/uni_modules/uv-ui-tools/libs/function";
 
 // 院校库 01 院校列表
@@ -478,4 +479,13 @@ export function removeConcernedUniversity(params: any) {
 
 export function getUniversitiesEnrollBrochureDetail(params: any) {
   return flyio.get('/front/university/getUniversitiesEnrollBrochureDetail', params)
-}
+}
+
+/**
+ * 获取关注院校列表
+ * @param params 
+ * @returns 
+ */
+export function getCollectedUniversities(params: any) {
+  return flyio.get('/front/customer/university/list', params) as Promise<ApiResponseList<Major.University>>;
+}

+ 25 - 0
src/common/routes.ts

@@ -88,6 +88,31 @@ export const routes = {
    * 隐私政策
    */
   pagePrivacyPolicy: '/pagesSystem/pages/privacy-policy/privacy-policy',
+  /**
+   * 我的收藏
+   */
+  pageCollect: '/pagesOther/pages/collect/collect',
+  /**
+   * 课程学习
+   */
+  pageCourseStudy: '/pagesOther/pages/video-center/index/index',
+  /**
+   * 组卷作业
+   */
+  pageHomework: '/pagesStudy/pages/homework/homework',
+  /**
+   * 错题本
+   */
+  pageWrongBook: '/pagesOther/pages/topic-center/wrong-book/wrong-book',
+  /**
+   * 学习记录
+   */
+  pageStudyHistory: '/pagesStudy/pages/study-history/study-history',
+  /**
+   * 会员卡校验
+   */
+  pageCardVerify: '/pagesSystem/pages/card-verify/card-verify',
+
 } as const;
 
 export type Routes = keyof typeof routes;

+ 7 - 1
src/components/ie-dropdown/ie-dropdown-item.vue

@@ -53,6 +53,8 @@ const instance = getCurrentInstance();
 const props = defineProps<{
   config: Dropdown.DropdownItem;
   index: number;
+  // 是否使用绝对定位
+  absolute?: boolean;
 }>();
 const dropdown = inject(DROPDOWN_SYMBOL);
 const triggerSelector = `#dropdown-trigger-${props.config.prop}`;
@@ -109,7 +111,11 @@ const open = async () => {
   checkboxValue.value = dropdown?.form.value[props.config.prop] || [];
   isOpen.value = true;
   const triggerRect = await getRect(triggerSelector);
-  top.value = triggerRect.top + triggerRect.height;
+  if (props.absolute) {
+    top.value = triggerRect.height;
+  } else {
+    top.value = triggerRect.top + triggerRect.height;
+  }
   setTimeout(() => {
     nextTick(() => {
       show.value = true;

+ 3 - 1
src/components/ie-dropdown/ie-dropdown.vue

@@ -1,7 +1,7 @@
 <template>
   <view class="flex items-center gap-20 px-20 h-[44px] border-0 border-b border-solid border-border-light">
     <view v-for="(item, index) in configs" :key="item.prop" class="flex-1 min-w-1 h-full">
-      <ie-dropdown-item :config="item" :index="index" />
+      <ie-dropdown-item :config="item" :index="index" :absolute="absolute" />
     </view>
   </view>
 </template>
@@ -16,6 +16,8 @@ const emit = defineEmits<{
 const modelValue = defineModel<Record<string, any>>();
 const props = defineProps<{
   configs: Dropdown.DropdownItem[];
+  // 是否使用绝对定位
+  absolute?: boolean;
 }>();
 const dropdown = useDropdown(props.configs);
 

+ 18 - 6
src/components/ie-tabs-swiper/ie-tabs-swiper.vue

@@ -5,7 +5,15 @@
         :key-name="keyName"></uv-tabs>
     </view>
     <ie-auto-resizer>
-      <slot></slot>
+      <swiper class="swiper h-full" :current="modelValue" @change="handleChangeSwiper">
+        <swiper-item v-for="(item, index) in list" :key="index" class="h-full">
+          <view class="h-full" v-if="Math.abs(index - modelValue) <= cacheCount">
+            <scroll-view class=" h-full" scroll-y>
+              <slot :name="item.slot"></slot>
+            </scroll-view>
+          </view>
+        </swiper-item>
+      </swiper>
     </ie-auto-resizer>
   </view>
 </template>
@@ -20,17 +28,21 @@ defineOptions({
 });
 type Props = {
   list: SwiperTabItem[];
-  scrollable: boolean;
-  keyName: string;
-  bgColor: string;
-  border: boolean;
+  scrollable?: boolean;
+  keyName?: string;
+  bgColor?: string;
+  border?: boolean;
+  cacheCount?: number;
+  height?: string;
 }
 const props = withDefaults(defineProps<Props>(), {
   list: () => [],
   modelValue: 0,
   keyName: 'name',
   bgColor: '#FFFFFF',
-  border: true
+  border: true,
+  cacheCount: 2,
+  height: '100%'
 });
 const modelValue = defineModel<number>('modelValue', {
   type: Number,

+ 6 - 0
src/pages.json

@@ -140,6 +140,12 @@
           "style": {
             "navigationBarTitleText": ""
           }
+        },
+        {
+          "path": "pages/collect/collect",
+          "style": {
+            "navigationBarTitleText": ""
+          }
         }
       ]
     },

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

@@ -11,7 +11,7 @@
       <template #headerRight="{ isTransparent }">
         <view v-if="userStore.getLocation" class="ml-10 flex items-center gap-x-4" @click="handleChangeLocation">
           <uv-icon name="map" size="16" :color="isTransparent ? '#333' : 'var(--primary-color)'" />
-          <text class="text-30 transition-colors duration-300 font-medium"
+          <text class="text-30 font-medium"
             :class="[isTransparent ? 'text-fore-title' : 'text-primary']">
             {{ userStore.getLocation }}
           </text>

+ 5 - 19
src/pagesMain/pages/me/components/me-menu.vue

@@ -50,8 +50,8 @@
 <script lang="ts" setup>
 import { useTransferPage } from '@/hooks/useTransferPage';
 import { useUserStore } from '@/store/userStore';
-import {routes} from "@/common/routes";
-const { transferTo } = useTransferPage();
+
+const { transferTo, routes } = useTransferPage();
 const userStore = useUserStore();
 
 const contactPhone = computed(() => userStore.orgInfo.contactPhone);
@@ -71,7 +71,7 @@ const menus = [
   {
     name: '我的收藏',
     icon: '/static/personal/my_collected.png',
-    pagePath: '/pagesOther/pages/personal-center/my-concerned/my-concerned',
+    pagePath: routes.pageCollect,
   },
   {
     name: '我的志愿表',
@@ -81,8 +81,7 @@ const menus = [
   {
     name: '绑定会员卡',
     icon: '/static/personal/bind_card.png',
-    // pagePath: '/pagesOther/pages/personal-center/bind-card/bind-card',
-    pagePath: '/pagesSystem/pages/card-verify/card-verify',
+    pagePath: routes.pageCardVerify,
   }
 ];
 const cellStyle = {
@@ -123,20 +122,7 @@ const handleLogout = async () => {
   }
 }
 const handlePhone = async () => {
-  if (!contactPhone.value) {
-    uni.$ie.showToast('暂无客服电话');
-    return;
-  } else {
-    uni.showActionSheet({
-      title: '客服电话',
-      itemList: [`拨打电话:${contactPhone.value}`],
-      success: (res) => {
-        uni.makePhoneCall({
-          phoneNumber: contactPhone.value
-        });
-      }
-    });
-  }
+  userStore.callContactPhone();
 }
 </script>
 <style lang="scss" scoped></style>

+ 0 - 0
src/pagesOther/pages/major/detail/components/university-item.vue → src/pagesOther/components/university-item.vue


+ 2 - 2
src/pagesOther/pages/career/detail/components/related-jobs.vue

@@ -1,5 +1,5 @@
 <template>
-  <scroll-view class=" h-full overflow-auto" scroll-y>
+  <view>
     <view class="py-20">
       <uv-tabs :list="list" :current="current" :itemStyle="itemStyle" :customStyle="customStyle" lineColor="transparent"
         @change="handleChange">
@@ -60,7 +60,7 @@
       <view class="text-24 text-fore-light">{{ detail?.vocationalSource }}</view>
     </view>
     <ie-safe-area-bottom bg-color="#FFFFFF" />
-  </scroll-view>
+  </view>
 </template>
 <script lang="ts" setup>
 import SalaryChart from './salary-chart.vue';

+ 6 - 8
src/pagesOther/pages/career/detail/detail.vue

@@ -3,14 +3,12 @@
     <ie-navbar :title="pageTitle" />
     <ie-auto-resizer>
       <ie-tabs-swiper v-model="current" :list="tabs" :scrollable="false">
-        <swiper class="swiper h-full" :current="current" @change="handleChangeSwiper">
-          <swiper-item v-for="(item, index) in tabs" :key="index" class="h-full">
-            <view class="h-full" v-if="Math.abs(index - current) <= 2">
-              <career-overview v-if="item.slot === 'career-overview' && code" :code="code" @change-job="handleChangeJob" />
-              <related-jobs v-if="item.slot === 'related-jobs' && code" :code="code" :name="jobName" />
-            </view>
-          </swiper-item>
-        </swiper>
+        <template #career-overview>
+          <career-overview v-if="code" :code="code" @change-job="handleChangeJob" />
+        </template>
+        <template #related-jobs>
+          <related-jobs v-if="code" :code="code" :name="jobName" />
+        </template>
       </ie-tabs-swiper>
     </ie-auto-resizer>
   </ie-page>

+ 35 - 0
src/pagesOther/pages/collect/collect.vue

@@ -0,0 +1,35 @@
+<template>
+  <ie-page :fix-height="true" :safe-area-inset-bottom="false">
+    <ie-navbar title="我的收藏" />
+    <ie-auto-resizer>
+      <ie-tabs-swiper v-model="current" :list="tabs" :scrollable="false">
+        <template #university>
+          <collect-university />
+        </template>
+        <template #major>
+          <collect-major />
+        </template>
+      </ie-tabs-swiper>
+    </ie-auto-resizer>
+  </ie-page>
+</template>
+
+<script lang="ts" setup>
+import type { SwiperTabItem } from '@/types';
+import CollectUniversity from './components/collect-university.vue';
+import CollectMajor from './components/collect-major.vue';
+
+const current = ref(0);
+const tabs = ref<SwiperTabItem[]>([
+  {
+    name: '关注的院校',
+    slot: 'university'
+  },
+  {
+    name: '关注的专业',
+    slot: 'major'
+  }
+]);
+</script>
+
+<style lang="scss"></style>

+ 74 - 0
src/pagesOther/pages/collect/components/collect-major.vue

@@ -0,0 +1,74 @@
+<template>
+  <z-paging ref="paging" :auto="false" v-model="list" :safe-area-inset-bottom="true" :hide-no-more-by-limit="10" @query="handleQuery">
+    <uv-cell-group>
+      <uv-cell v-for="item in list" :key="item.id" :title="item.name" :label="item.code" @click="handleClick(item)">
+        <template #title>
+          <text class="text-30 text-fore-title">{{ item.name }}</text>
+        </template>
+        <template #label>
+          <text class="text-26 text-fore-light mt-10">{{ getParentName(item.ancestors) }}</text>
+        </template>
+        <template #value>
+          <uv-icon name="arrow-right" size="18" color="var(--fore-light)" />
+        </template>
+      </uv-cell>
+    </uv-cell-group>
+  </z-paging>
+</template>
+<script lang="ts" setup>
+import type { Major } from '@/types';
+import { getMajorTree, getCollectedMajors } from '@/api/modules/major';
+import { useTransferPage } from '@/hooks/useTransferPage';
+
+const { transferTo, routes } = useTransferPage();
+
+const list = ref<Major.CollectedMajor[]>([]);
+const paging = ref<ZPagingInstance>();
+const tree = ref<Major.MajorItem[]>([]);
+
+/**
+ * 从树中获取指定code的对象,深度优先
+ * @param ancestors 
+ */
+const getDataByCode = (code: string, tree: Major.MajorItem[]): Major.MajorItem | null => {
+  for (const item of tree) {
+    if (item.code === code) {
+      return item;
+    }
+    if (item.children) {
+      const data = getDataByCode(code, item.children);
+      if (data) {
+        return data;
+      }
+    }
+  }
+  return null;
+}
+
+const getParentName = (ancestors: string) => {
+  const codes = ancestors.split(',');
+  return codes.filter(code => code !== '0').map(code => {
+    const item = getDataByCode(code, tree.value);
+    return item?.name;
+  }).join(' > ');
+}
+
+const handleClick = (item: Major.CollectedMajor) => {
+  transferTo(routes.majorDetail, {
+    data: { code: item.code }
+  });
+}
+
+const handleQuery = (page: number, size: number) => {
+  getCollectedMajors({ pageNum: page, pageSize: size }).then(res => {
+    paging.value?.completeByTotal(res.rows, res.total);
+  }).catch(() => paging.value?.complete(false));
+}
+onLoad(() => {
+  getMajorTree({}).then(res => {
+    tree.value = res.data;
+    paging.value?.reload();
+  });
+});
+</script>
+<style lang="scss" scoped></style>

+ 20 - 0
src/pagesOther/pages/collect/components/collect-university.vue

@@ -0,0 +1,20 @@
+<template>
+  <z-paging ref="paging" v-model="list" :safe-area-inset-bottom="true" :hide-no-more-by-limit="10" @query="handleQuery">
+    <university-item v-for="item in list" :key="item.id" :data="item" />
+  </z-paging>
+</template>
+<script lang="ts" setup>
+import type { Major } from '@/types';
+import { getCollectedUniversities } from '@/api/modules/university';
+import UniversityItem from '@/pagesOther/components/university-item.vue';
+
+const list = ref<Major.University[]>([]);
+const paging = ref<ZPagingInstance>();
+
+const handleQuery = (page: number, size: number) => {
+  getCollectedUniversities({ pageNum: page, pageSize: size }).then(res => {
+    paging.value?.completeByTotal(res.rows, res.total);
+  }).catch(() => paging.value?.complete(false));
+}
+</script>
+<style lang="scss" scoped></style>

+ 18 - 18
src/pagesOther/pages/major/detail/components/related-university.vue

@@ -1,35 +1,35 @@
 <template>
-    <z-paging ref="paging" v-model="list" :safe-area-inset-bottom="true" @query="handleQuery">
-        <view v-for="item in list" :key="item.id">
-            <university-item :data="item" @click="handleClick(item)"/>
-        </view>
-    </z-paging>
+  <z-paging ref="paging" v-model="list" :safe-area-inset-bottom="true" @query="handleQuery">
+    <view v-for="item in list" :key="item.id">
+      <university-item :data="item" @click="handleClick(item)" />
+    </view>
+  </z-paging>
 </template>
 <script lang="ts" setup>
-import {getUniversityByMajorCode} from '@/api/modules/major';
-import UniversityItem from './university-item.vue';
-import {Major} from '@/types';
-import {useTransferPage} from '@/hooks/useTransferPage';
+import { getUniversityByMajorCode } from '@/api/modules/major';
+import UniversityItem from '@/pagesOther/components/university-item.vue';
+import { Major } from '@/types';
+import { useTransferPage } from '@/hooks/useTransferPage';
 
-const {transferTo, routes} = useTransferPage();
+const { transferTo, routes } = useTransferPage();
 
 const props = defineProps<{
-    data: Major.MajorOverview;
+  data: Major.MajorOverview;
 }>();
 const list = ref<Major.University[]>([]);
 const paging = ref<ZPagingInstance>();
 
 const handleQuery = (page: number, size: number) => {
-    getUniversityByMajorCode({code: props.data.code, pageNum: page, pageSize: size}).then(res => {
-        paging.value?.completeByTotal(res.rows, res.total);
-    }).catch(() => paging.value?.complete(false));
+  getUniversityByMajorCode({ code: props.data.code, pageNum: page, pageSize: size }).then(res => {
+    paging.value?.completeByTotal(res.rows, res.total);
+  }).catch(() => paging.value?.complete(false));
 }
 
 const handleClick = (item: Major.University) => {
-    const {id, code, name} = item
-    transferTo(routes.universityDetail, {
-        data: {id, code, name}
-    });
+  const { id, code, name } = item
+  transferTo(routes.universityDetail, {
+    data: { id, code, name }
+  });
 }
 </script>
 <style lang="scss" scoped></style>

+ 6 - 8
src/pagesOther/pages/major/detail/detail.vue

@@ -3,14 +3,12 @@
     <ie-navbar title="专业详情" />
     <ie-auto-resizer>
       <ie-tabs-swiper v-model="current" :list="tabs" :scrollable="false">
-        <swiper class="swiper h-full" :current="current" @change="handleChangeSwiper">
-          <swiper-item v-for="(item, index) in tabs" :key="index" class="h-full">
-            <view class="h-full" v-if="Math.abs(index - current) <= 2">
-              <major-overview v-if="item.slot === 'major-overview' && overviewData" :data="overviewData" />
-              <related-university v-if="item.slot === 'open-college' && overviewData" :data="overviewData" />
-            </view>
-          </swiper-item>
-        </swiper>
+        <template #major-overview>
+          <major-overview v-if="overviewData" :data="overviewData" />
+        </template>
+        <template #open-college>
+          <related-university v-if="overviewData" :data="overviewData" />
+        </template>
       </ie-tabs-swiper>
     </ie-auto-resizer>
   </ie-page>

+ 14 - 9
src/pagesOther/pages/news/index/index.vue

@@ -3,13 +3,18 @@
     <ie-navbar title="资讯" />
     <ie-auto-resizer>
       <ie-tabs-swiper v-model="current" :list="tabs" :scrollable="true">
-        <swiper class="swiper h-full" :current="current" @change="handleChangeSwiper">
-          <swiper-item v-for="(item, index) in tabs" :key="index" class="h-full">
-            <view class="h-full" v-if="Math.abs(index - current) <= 2">
-              <news-paging-list :params="item.params" />
-            </view>
-          </swiper-item>
-        </swiper>
+        <!-- #ifdef WEB -->
+        <template v-for="item in tabs" #[item.slot]>
+          <news-paging-list :params="item.params" />
+        </template>
+        <!-- #endif -->
+        <!-- #ifdef MP-WEIXIN -->
+        <block v-for="item in tabs">
+          <template #[item.slot]>
+            <news-paging-list :params="item.params" />
+          </template>
+        </block>
+        <!-- #endif -->
       </ie-tabs-swiper>
     </ie-auto-resizer>
   </ie-page>
@@ -33,13 +38,13 @@ const handleChangeSwiper = (e: any) => {
 }
 const loadData = () => {
   getNewsTypes().then(res => {
-    const list = res.rows.map(item => ({ name: item.label, value: item.value, slot: item.label, params: {type: item.value} }));
+    const list = res.rows.map(item => ({ name: item.label, value: item.value, slot: item.label, params: { type: item.value } }));
     tabs.value = [
       {
         name: '热门资讯',
         value: 'hot',
         slot: 'hot',
-        params: {tag: 'hot'}
+        params: { tag: 'hot' }
       },
       ...list
     ]

+ 75 - 66
src/pagesOther/pages/university/detail/detail.vue

@@ -1,49 +1,58 @@
 <template>
-    <ie-page>
-        <ie-navbar :title="prevData.name" transparent bg-color="#FFFFFF" title-color="black" keep-title-color/>
-        <ie-image :src="baseInfo.bannerUrl||baseInfo.logo" custom-class="w-full h-360"/>
-        <view class="-mt-60 z-1 rounded-t-3xl p-30 bg-white">
-            <college-info :info="baseInfo" :loading="loading"/>
-        </view>
-        <uv-gap height="10" bg-color="#F6F8FA"/>
-        <uv-sticky :offset-top="appStore.isH5?0:baseStickyTop">
-            <view :style="{height: tabHeight + 'px'}">
-                <ie-tabs-swiper v-model="current" :list="tabs" :scrollable="false">
-                    <swiper class="swiper h-full" :current="current" @change="handleChangeSwiper">
-                        <swiper-item v-for="(item, index) in tabs" :key="index" class="h-full">
-                            <scroll-view scroll-y :style="{height: (tabHeight-44) + 'px'}">
-                                <college-profile v-if="item.slot==='profile'&&item.visited" :loading="loading"/>
-                                <college-brochure v-if="item.slot==='brochure'&&item.visited"/>
-                                <college-exam v-if="item.slot==='exam'&&item.visited"/>
-                            </scroll-view>
-                            <plan-enroll-list v-if="item.slot==='plan'&&item.visited" mode="plan" :list="planList"/>
-                            <plan-enroll-list v-if="item.slot==='enroll'&&item.visited" mode="enroll" :list="enrollList"/>
-                        </swiper-item>
-                    </swiper>
-                </ie-tabs-swiper>
-            </view>
-        </uv-sticky>
-    </ie-page>
+  <ie-page>
+    <ie-navbar :title="prevData.name" transparent bg-color="#FFFFFF" title-color="black" keep-title-color />
+    <ie-image :src="baseInfo.bannerUrl || baseInfo.logo" custom-class="w-full h-360" />
+    <view class="-mt-60 z-1 rounded-t-3xl p-30 bg-white">
+      <college-info :info="baseInfo" :loading="loading" />
+    </view>
+    <uv-gap height="10" bg-color="#F6F8FA" />
+    <uv-sticky :offset-top="appStore.isH5 ? 0 : baseStickyTop">
+      <view :style="{ height: tabHeight + 'px' }">
+        <ie-tabs-swiper v-model="current" :list="tabs" :scrollable="false">
+          <!-- #ifdef WEB -->
+          <template v-for="item in tabs" #[item.slot]>
+            <college-profile v-if="item.slot === 'profile' && item.visited" :loading="loading" />
+            <college-brochure v-if="item.slot === 'brochure' && item.visited" />
+            <college-plan v-if="item.slot === 'plan' && item.visited" />
+            <college-enroll v-if="item.slot === 'enroll' && item.visited" />
+            <college-exam v-if="item.slot === 'exam' && item.visited" />
+          </template>
+          <!-- #endif -->
+          <!-- #ifdef MP-WEIXIN -->
+          <block v-for="item in tabs">
+            <template #[item.slot]>
+              <college-profile v-if="item.slot === 'profile' && item.visited" :loading="loading" />
+              <college-brochure v-if="item.slot === 'brochure' && item.visited" />
+              <college-plan v-if="item.slot === 'plan' && item.visited" />
+              <college-enroll v-if="item.slot === 'enroll' && item.visited" />
+              <college-exam v-if="item.slot === 'exam' && item.visited" />
+            </template>
+          </block>
+          <!-- #endif -->
+        </ie-tabs-swiper>
+      </view>
+    </uv-sticky>
+  </ie-page>
 </template>
 <script lang="ts" setup>
 
-import {useTransferPage} from "@/hooks/useTransferPage";
-import {UniversityDetail, University} from "@/types/university";
-import {MajorItem} from "@/types/major";
-import {universityDetail} from "@/api/modules/university";
-import {getMajorTree} from "@/api/modules/major";
+import { useTransferPage } from "@/hooks/useTransferPage";
+import { UniversityDetail, University } from "@/types/university";
+import { MajorItem } from "@/types/major";
+import { universityDetail } from "@/api/modules/university";
+import { getMajorTree } from "@/api/modules/major";
 import CollegeInfo from "@/pagesOther/pages/university/detail/components/college-info.vue";
-import {SwiperTabItem} from "@/types";
-import {useNavbar} from "@/hooks/useNavbar";
-import {useAppStore} from "@/store/appStore";
-import {MAJOR_TREE, UNIVERSITY_DETAIL} from "@/types/injectionSymbols";
+import { SwiperTabItem } from "@/types";
+import { useNavbar } from "@/hooks/useNavbar";
+import { useAppStore } from "@/store/appStore";
+import { MAJOR_TREE, UNIVERSITY_DETAIL } from "@/types/injectionSymbols";
 import CollegeProfile from "@/pagesOther/pages/university/detail/components/college-profile.vue";
 import CollegeBrochure from "@/pagesOther/pages/university/detail/components/college-brochure.vue";
 import CollegeExam from "@/pagesOther/pages/university/detail/components/college-exam.vue";
 import PlanEnrollList from "@/pagesOther/pages/university/detail/components/plan-enroll-list.vue";
 
-const {prevData} = useTransferPage()
-const {baseStickyTop} = useNavbar()
+const { prevData } = useTransferPage()
+const { baseStickyTop } = useNavbar()
 const detail = ref<UniversityDetail>({} as UniversityDetail)
 const baseInfo = computed<University>(() => detail.value.baseInfo || {} as University)
 const planList = computed(() => detail.value.planHistories || [])
@@ -55,47 +64,47 @@ const appStore = useAppStore()
 const current = ref(0)
 const tabHeight = computed(() => appStore.sysInfo.screenHeight - baseStickyTop.value)
 const tabs = ref<SwiperTabItem[]>([{
-    name: '概况',
-    slot: 'profile',
-    visited: true
+  name: '概况',
+  slot: 'profile',
+  visited: true
 }, {
-    name: '简章',
-    slot: 'brochure',
-    visited: false
+  name: '简章',
+  slot: 'brochure',
+  visited: false
 }, {
-    name: '计划',
-    slot: 'plan',
-    visited: false
+  name: '计划',
+  slot: 'plan',
+  visited: false
 }, {
-    name: '录取',
-    slot: 'enroll',
-    visited: false
+  name: '录取',
+  slot: 'enroll',
+  visited: false
 }, {
-    name: '考试大纲',
-    slot: 'exam',
-    visited: false
+  name: '考试大纲',
+  slot: 'exam',
+  visited: false
 }])
 
 const handleChangeSwiper = function (e: any) {
-    current.value = e.detail.current
-    tabs.value[current.value].visited = true
+  current.value = e.detail.current
+  tabs.value[current.value].visited = true
 }
 
 provide(UNIVERSITY_DETAIL, detail)
 provide(MAJOR_TREE, majorTree)
 onMounted(() => {
-    uni.$ie.showLoading()
-    loading.value = true
-    universityDetail({code: prevData.value.code})
-        .then(res => {
-            detail.value = res.data
-            return getMajorTree({})
-        })
-        .then(res => majorTree.value = res.data)
-        .finally(() => {
-            uni.$ie.hideLoading()
-            loading.value = false
-        })
+  uni.$ie.showLoading()
+  loading.value = true
+  universityDetail({ code: prevData.value.code })
+    .then(res => {
+      detail.value = res.data
+      return getMajorTree({})
+    })
+    .then(res => majorTree.value = res.data)
+    .finally(() => {
+      uni.$ie.hideLoading()
+      loading.value = false
+    })
 })
 // 必须手动触发才能保证 navbar.transparent 正常工作
 onPageScroll(() => {
@@ -104,6 +113,6 @@ onPageScroll(() => {
 
 <style lang="scss" scoped>
 ::v-deep .uv-tabs__wrapper__nav__item__text {
-    white-space: nowrap;
+  white-space: nowrap;
 }
 </style>

+ 2 - 1
src/pagesOther/pages/university/index/components/college-list.vue

@@ -3,7 +3,7 @@
     <z-paging ref="paging" v-model="list" @query="handleQuery">
       <template #top>
         <slot name="top" />
-        <college-conditions-picker v-if="filterOptions" :options="filterOptions" @change="handleSearch" />
+        <college-conditions-picker v-if="filterOptions" :options="filterOptions" :absolute="absolute" @change="handleSearch" />
         <ie-search v-model="queryParams.name" placeholder="输入院校名称" @search="handleSearch" @clear="handleSearch" />
       </template>
       <view class="p-20 flex flex-col gap-20">
@@ -25,6 +25,7 @@ import { routes } from "@/common/routes";
 const props = withDefaults(defineProps<{
   customItemClick?: boolean;
   extraFilter?: Record<string, any>;
+  absolute?: boolean;
 }>(), {
   customItemClick: false,
   extraFilter: () => ({})

+ 2 - 1
src/pagesOther/pages/university/index/components/plus/college-conditions-picker.vue

@@ -1,6 +1,6 @@
 <template>
   <view>
-    <ie-dropdown :configs="configs" v-model="form" @change="handleChange" />
+    <ie-dropdown :configs="configs" v-model="form" :absolute="absolute" @change="handleChange" />
   </view>
 </template>
 
@@ -12,6 +12,7 @@ import type { UniversityQueryDto, UniversityFilter } from "@/types/university";
 
 const props = defineProps<{
   options: UniversityFilter;
+  absolute?: boolean;
 }>()
 const filter = inject(UNIVERSITY_FILTER) || ref({} as UniversityQueryDto)
 

+ 19 - 19
src/pagesOther/pages/university/index/index.vue

@@ -1,34 +1,34 @@
 <template>
-    <ie-page fix-height bg-color="#F6F8FA" :safe-area-inset-bottom="false">
-        <ie-navbar title="院校库"/>
-        <ie-auto-resizer>
-            <ie-tabs-swiper v-model="current" :list="tabs" :scrollable="false">
-                <swiper class="swiper h-full" :current="current" @change="handleChangeSwiper">
-                    <swiper-item v-for="(item, index) in tabs" :key="index" class="h-full">
-                        <college-list v-if="item.slot==='list'"/>
-                        <college-rank v-if="item.slot==='rank'"/>
-                    </swiper-item>
-                </swiper>
-            </ie-tabs-swiper>
-        </ie-auto-resizer>
-    </ie-page>
+  <ie-page fix-height bg-color="#F6F8FA" :safe-area-inset-bottom="false">
+    <ie-navbar title="院校库" />
+    <ie-auto-resizer>
+      <ie-tabs-swiper v-model="current" :list="tabs" :scrollable="false">
+        <template #list>
+          <college-list :absolute="true" />
+        </template>
+        <template #rank>
+          <college-rank />
+        </template>
+      </ie-tabs-swiper>
+    </ie-auto-resizer>
+  </ie-page>
 </template>
 <script lang="ts" setup>
-import {SwiperTabItem} from "@/types";
+import { SwiperTabItem } from "@/types";
 import CollegeList from "@/pagesOther/pages/university/index/components/college-list.vue";
 import CollegeRank from "@/pagesOther/pages/university/index/components/college-rank.vue";
 
 const current = ref(0);
 const tabs = ref<SwiperTabItem[]>([{
-    name: '院校库',
-    slot: 'list'
+  name: '院校库',
+  slot: 'list'
 }, {
-    name: '院校排名',
-    slot: 'rank'
+  name: '院校排名',
+  slot: 'rank'
 }]);
 
 const handleChangeSwiper = function (e: any) {
-    current.value = e.detail.current;
+  current.value = e.detail.current;
 }
 </script>
 <style lang="scss" scoped></style>

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

@@ -11,7 +11,8 @@
 <script lang="ts" setup>
 import { useUserStore } from '@/store/userStore';
 import { useTransferPage } from '@/hooks/useTransferPage';
-const { transferTo } = useTransferPage();
+
+const { transferTo, routes } = useTransferPage();
 const userStore = useUserStore();
 type MenuItem = {
   label: string;
@@ -22,38 +23,37 @@ const menus = computed(() => [
   {
     label: '课程学习',
     icon: '/menu/menu-course.png',
-    pageUrl: '/pagesOther/pages/video-center/index/index',
+    pageUrl: routes.pageCourseStudy,
     visible: true
   },
   {
     label: '组卷作业',
     icon: '/menu/menu-exam.png',
-    pageUrl: '/pagesStudy/pages/homework/homework',
+    pageUrl: routes.pageHomework,
     visible: userStore.isStudent
   },
   {
     label: '收藏夹',
     icon: '/menu/menu-favorite.png',
-    // pageUrl: '/pagesOther/pages/personal-center/my-concerned/my-concerned'
-    pageUrl: '/pagesOther/pages/topic-center/topic-collection/topic-collection',
+    pageUrl: routes.pageCollect,
     visible: true
   },
   {
     label: '错题本',
     icon: '/menu/menu-mistake.png',
-    pageUrl: '/pagesOther/pages/topic-center/wrong-book/wrong-book',
+    pageUrl: routes.pageWrongBook,
     visible: true
   },
   {
     label: '学习记录',
     icon: '/menu/menu-record.png',
-    pageUrl: '/pagesStudy/pages/study-history/study-history',
+    pageUrl: routes.pageStudyHistory,
     visible: true
   }
 ])
 const navigateTo = (menu: MenuItem) => {
   if (menu.label === '错题本') {
-    transferTo('/pagesOther/pages/topic-center/wrong-book/wrong-book', {
+    transferTo(routes.pageWrongBook, {
       data: {}
     });
   } else {

+ 3 - 1
src/pagesSystem/pages/setting/setting.vue

@@ -82,7 +82,9 @@ const settings = computed(() => [
     title: '客服电话',
     icon: 'server-man',
     value: contactPhone.value,
-    handler: () => { }
+    handler: () => {
+      userStore.callContactPhone();
+    }
   }
 ]);
 </script>

BIN
src/static/personal/icon-protocol.png


+ 19 - 0
src/store/userStore.ts

@@ -280,6 +280,25 @@ export const useUserStore = defineStore('ie-user', {
       }).finally(() => {
         uni.$ie.hideLoading();
       });
+    },
+    /**
+     * 拨打客服电话
+     * @returns 
+     */
+    callContactPhone() {
+      if (!this.orgInfo.contactPhone) {
+        uni.$ie.showToast('暂无客服电话');
+        return;
+      }
+      uni.showActionSheet({
+        title: '客服电话',
+        itemList: [`拨打电话:${this.orgInfo.contactPhone}`],
+        success: (res) => {
+          uni.makePhoneCall({
+            phoneNumber: this.orgInfo.contactPhone
+          });
+        }
+      });
     }
   },
   persist: {

+ 15 - 0
src/types/major.ts

@@ -150,4 +150,19 @@ export interface University {
     type: string;
     webSite: string;
     introduction: string;
+}
+
+export interface CollectedMajor {
+    ancestors: string;
+    name: string;
+    type: string;
+    level: boolean;
+    code: string;
+    child_count: number;
+    customerCode: string;
+    id: number;
+    learn_year_arab: string;
+    majorId: string;
+    parent_code: string;
+    status: number;
 }