瀏覽代碼

skill + voluntary - empty value fkex

abpcoder 17 小時之前
父節點
當前提交
abc02a2be7

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

@@ -1,6 +1,6 @@
 <template>
     <view :class="width" @click="handleClick">
-        <view class="flex items-center gap-x-6 justify-start" :style="customStyle">
+        <view class="flex items-center gap-x-6 justify-start" :class="customClass" :style="customStyle">
             <view v-if="matchValue || customLabel"
                   class="text-[15px] h-[24px] leading-[24px] flex-1 min-w-1 ellipsis-1"
                   :style="getValueStyle"
@@ -98,6 +98,10 @@ const props = defineProps({
         type: Object,
         default: () => ({}),
     },
+    customClass: {
+        type: [String,Array],
+        default: ''
+    },
     width: {
         type: String,
         default: 'w-full'

+ 1 - 1
src/pagesOther/pages/skill/index/index.vue

@@ -71,7 +71,7 @@ const handleSelect = async () => {
         rules.value = res.data
         // init model
         rules.value.forEach((r) => {
-            r.details.forEach((d) => {
+            r.details?.forEach((d) => {
                 if (d.options?.length) {
                     model.value[d.fieldName] = d.defaultValue ? d.defaultValue : ''
                     model.value[d.fieldName + 'Total'] = d.options ? d.options[0] : null

+ 0 - 13
src/pagesOther/pages/university/detail/components/college-enroll.vue

@@ -1,13 +0,0 @@
-<template>
-
-</template>
-
-<script>
-export default {
-    name: "college-enroll"
-}
-</script>
-
-<style scoped>
-
-</style>

+ 0 - 13
src/pagesOther/pages/university/detail/components/college-plan.vue

@@ -1,13 +0,0 @@
-<template>
-
-</template>
-
-<script>
-export default {
-    name: "college-plan"
-}
-</script>
-
-<style scoped>
-
-</style>

+ 114 - 0
src/pagesOther/pages/university/detail/components/plan-enroll-list.vue

@@ -0,0 +1,114 @@
+<template>
+    <view class="h-full">
+        <z-paging ref="paging" v-model="results" bg-color="white" :auto="false" :refresher-enabled="false"
+                  @query="handleQuery">
+            <template #top>
+                <view class="flex items-center gap-20 px-30 py-20">
+                    <ie-picker v-model="queryParams.year" :list="years" width="w-fit" icon="arrow-down"
+                               custom-class="px-30 py-10 bg-primary-100 rounded" @change="handleYearChange"/>
+                    <ie-picker v-model="queryParams.level" :list="levels" width="w-fit" icon="arrow-down"
+                               custom-class="px-30 py-10 bg-primary-100 rounded" @change="handleLevelChange"/>
+                    <ie-picker v-model="queryParams.group" :list="groups" width="w-fit" icon="arrow-down"
+                               custom-class="px-30 py-10 bg-primary-100 rounded" @change="handleGroupChange"/>
+                </view>
+            </template>
+            <view v-for="([label, values], idx) in groupedResults" class="fx-col px-30">
+                <uv-divider v-if="idx>0"/>
+                <view v-if="label" class="text-primary font-bold mb-20">{{ label }}</view>
+                <view v-for="(item,i) in values" class="px-20 fx-col">
+                    <view class="font-bold text-main">
+                        {{ item.majorName }}
+                        <template v-if="item.groupName">({{ item.groupName }})</template>
+                        <template v-if="item.specialProject">({{ item.specialProject }})</template>
+                    </view>
+                    <view v-if="item.marjorDirection" class="font-[PingFang] text-2xs text-content">
+                        {{ item.marjorDirection }}
+                    </view>
+                    <view class="mt-10 grid gap-20" :class="`grid-cols-`+descriptors.length">
+                        <plan-enroll-descriptor v-for="d in descriptors" :title="d.title" :value="item[d.key]"
+                                                :title-only="d.titleOnly" @click="handleRuleClick(d, item)"/>
+                    </view>
+                    <uv-divider v-if="i<values.length-1" dashed/>
+                </view>
+            </view>
+        </z-paging>
+    </view>
+</template>
+
+<script setup lang="ts">
+import _ from 'lodash';
+import {HistoryMode, IPlanEnrollDescriptor, IPlanEnrollHistory} from "@/types/university";
+import PlanEnrollDescriptor from "@/pagesOther/pages/university/detail/components/plus/plan-enroll-descriptor.vue";
+
+
+const props = withDefaults(defineProps<{
+    mode: HistoryMode;
+    list: IPlanEnrollHistory[];
+}>(), {
+    mode: 'plan',
+    list: () => []
+})
+const emits = defineEmits(['rule'])
+
+const paging = ref<ZPagingInstance>()
+const isCultural = ref(false) // 河南文化类遗留结构
+const tag = computed(() => isCultural.value && '') // examMajorName 河南文化类遗留结构
+const descriptors = computed<IPlanEnrollDescriptor[]>(() => {
+    const enrollShared = [{title: '录取', key: 'realNum'}, {title: '最低分', key: 'minScore'}]
+    const cols: IPlanEnrollDescriptor[] = props.mode == 'plan'
+        ? [{title: '计划', key: 'planNum'}, {title: '学制', key: 'xueZhi'}, {title: '学费', key: 'fee'}]
+        : isCultural.value
+            ? [...enrollShared, {title: '最低位', key: 'minSeat'}]
+            : [...enrollShared]
+    if (!isCultural.value) cols.push({title: '录取规则', key: '', titleOnly: true})
+    return cols
+})
+
+const results = ref<IPlanEnrollHistory[]>([])
+const groupedResults = computed(() => Object.entries(_.groupBy(results.value, i => i.majorGroup)))
+const queryParams = ref({year: '', level: '', group: ''})
+const years = computed(() => _.orderBy(_.uniq(_.map(props.list, i => i.year)), [], ['desc'])
+    .map(i => ({label: i, value: i})))
+const levels = computed(() => _.uniq(_.map(props.list.filter(i => i.year == queryParams.value.year), i => i.level))
+    .map(i => ({label: i, value: i})))
+const groups = computed(() => _.uniq(_.map(props.list.filter(i => i.year == queryParams.value.year && i.level == queryParams.value.level), i => i.majorGroup))
+    .map(g => ({label: g, value: g})))
+
+const listOfYear = computed(() => queryParams.value.year ? props.list.filter(i => i.year == queryParams.value.year) : [])
+const listOfLevel = computed(() => queryParams.value.level ? listOfYear.value.filter(i => i.level == queryParams.value.level) : [])
+const listOfGroup = computed(() => !queryParams.value.group ? listOfLevel.value : listOfLevel.value.filter(i => i.majorGroup == queryParams.value.group))
+
+const handleYearChange = () => {
+    paging.value?.reload()
+}
+
+const handleLevelChange = () => {
+    paging.value?.reload()
+}
+
+const handleGroupChange = () => {
+    paging.value?.reload()
+}
+
+const handleQuery = () => {
+    // 到这里时listOfLevel应该已经动态计算完毕了
+    // onSearch, results结构都是多余的,这里只是为了实验z-paging的虚拟列表
+    paging.value?.completeByNoMore(listOfGroup.value, true)
+}
+
+const handleRuleClick = (descriptor: IPlanEnrollDescriptor, history: IPlanEnrollHistory) => {
+    if (descriptor.titleOnly) emits('rule', history.totalRule)
+}
+
+watch(props.list, (list) => {
+    if (list.length) {
+        queryParams.value.year = years.value[0].value
+        queryParams.value.level = levels.value[0].value
+    }
+    paging.value?.reload()
+})
+</script>
+
+<style scoped>
+
+</style>

+ 19 - 0
src/pagesOther/pages/university/detail/components/plus/plan-enroll-descriptor.vue

@@ -0,0 +1,19 @@
+<template>
+    <view class="fx-col fx-cen-cen rounded-lg bg-slate-100 py-10">
+        <template v-if="!titleOnly">
+            <text class="text-main text-sm">{{ value || '-' }}</text>
+            <text class="text-tips text-3xs">{{ title }}</text>
+        </template>
+        <text v-else class="text-primary text-sm">{{ title }}</text>
+    </view>
+</template>
+
+<script setup lang="ts">
+import {IPlanEnrollDescriptor} from "@/types/university";
+
+defineProps<IPlanEnrollDescriptor>()
+</script>
+
+<style scoped>
+
+</style>

+ 6 - 5
src/pagesOther/pages/university/detail/detail.vue

@@ -14,10 +14,10 @@
                             <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-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"/>
                             </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>
@@ -31,6 +31,7 @@ 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";
@@ -38,15 +39,15 @@ 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 CollegePlan from "@/pagesOther/pages/university/detail/components/college-plan.vue";
-import CollegeEnroll from "@/pagesOther/pages/university/detail/components/college-enroll.vue";
 import CollegeExam from "@/pagesOther/pages/university/detail/components/college-exam.vue";
-import {getMajorTree} from "@/api/modules/major";
+import PlanEnrollList from "@/pagesOther/pages/university/detail/components/plan-enroll-list.vue";
 
 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 || [])
+const enrollList = computed(() => detail.value.enrollHistories || [])
 const majorTree = ref<MajorItem[]>([])
 const loading = ref(true)
 const appStore = useAppStore()

+ 1 - 1
src/pagesOther/pages/voluntary/index/components/voluntary-form-core.vue

@@ -43,7 +43,7 @@ watch([renderRules, model], ([renderRules, model]) => {
     if (!renderRules || !model) return
     const autoRules: Record<string, any> = {}
     renderRules.forEach((item: EnrollRule) => {
-        item.details.forEach((r: EnrollRuleItem) => {
+        item.details?.forEach((r: EnrollRuleItem) => {
             // TODO: 此规则逻辑从旧的uni-vueuse mx-base分支迁移过来,可能不完全适用于新版本。
             const fieldRules = []
             // 录取规则,自动添加非空校验

+ 1 - 1
src/pagesOther/pages/voluntary/index/index.vue

@@ -63,7 +63,7 @@ const handleSelect = async () => {
         rules.value = res.data
         // init model
         rules.value.forEach((r) => {
-            r.details.forEach((d) => {
+            r.details?.forEach((d) => {
                 if (d.options?.length) {
                     model.value[d.fieldName] = d.defaultValue ? d.defaultValue : ''
                     model.value[d.fieldName + 'Total'] = d.options ? d.options[0] : null

+ 36 - 0
src/types/university.ts

@@ -70,6 +70,42 @@ export interface UniversityBrochure {
     "logo"?: string;
 }
 
+export type HistoryMode = 'plan' | 'enroll';
+
+export interface IPlanEnrollHistory {
+    "id": number;
+    "collegeCode": string;
+    "year": string;
+    "level": string;
+    "marjorDirection": string;
+    "specialProject": string;
+    "examMajor": string;
+    "majorGroup": string;
+    "majorCode": string;
+    "majorName": string;
+    "enrollCode": string;
+    "totalRule": string;
+}
+
+export interface UniversityPlanHistory extends IPlanEnrollHistory {
+    "planNum": number|null;
+    "fee": string;
+    "xueZhi": string;
+}
+
+export interface UniversityEnrollHistory extends IPlanEnrollHistory {
+    "minScore": number|null;
+    "minSeat": number|null;
+    "realNum": number|null;
+}
+
+export interface IPlanEnrollDescriptor {
+    key?: string;
+    title?: string;
+    value?: string;
+    titleOnly?: boolean;
+}
+
 export interface UniversityDetail {
     baseInfo: University;
     enrollBrochures: UniversityBrochure[];