浏览代码

Merge branch 'master' of http://121.4.203.192:9000/mingxue/front

shilipojs 2 年之前
父节点
当前提交
5d6e84c48a

+ 22 - 0
doc/Mind/ElectiveGeneration.cs

@@ -411,6 +411,12 @@ namespace mxdemo.Mind
             // 5.27 hht +迭代至参数中acitve阶段的录取状态(非最终态,用来展示某阶段未录取学生AI分析表)
             public bool approved;
             public bool forceAdjusted;
+            // 6.11 hht +录取组合&报名组合(迭代至参数中acitve阶段的录取状态, getClassGenerationDetails专用)
+            public int enrollGeneration; // 真实录取时的generation
+            public string enrollGroupName; // 录取组合
+            public string rankInEnrolledGroup; // 录取排名:已录取情况下,组合所有录取同学中的排名, 录取排名/组合全校全部录取人数
+            public string[] applyGroupNames; // active只会传入决策代,取active前一代的报名组合
+            public string rankInAppliedGroup; // 报名排名:报名时的排名(多志愿取一志愿) 报名排名/applyGroupNames中1志愿报名全校总人数
 
             public long id { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
             public string category { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
@@ -555,6 +561,22 @@ namespace mxdemo.Mind
         /// <param name="keywords">姓名/学号/账号</param>
         /// </summary>
         ElectiveGenerationDetail[] searchElectiveGenerationDetails(int pageNum, int pageSize, int roundId, string keywords);
+
+        public class ClassGenerationDetailWrapper {
+          ElectiveGenerationDetail[] details;
+          int studentCount;  // 本班总人数
+          int enrolledCount; // 迭代至activeGeneration的本班录取人数
+        }
+
+        /// <summary>
+        /// 重用getElectiveGenerationDetails, query=active=generation
+        /// 班主任查询接口,查询当前班级的选科数据
+        /// +isNew true表示查询当前老师待生效的新班级学生,false表示查当前班主任所在班级的学生
+        /// 先通过oldClassId或者newClassId锁定查询范围,再查迭代至某个generation的录取与未录取状态
+        /// 6.13 isNew参数暂不用
+        /// </summary>
+        ClassGenerationDetailWrapper getClassGenerationDetails(int roundId, int generation, string queryCode, bool isNew);
+
         #endregion
     }
 }

+ 10 - 0
src/api/webApi/elective/generation.js

@@ -123,3 +123,13 @@ export function searchElectiveGenerationDetails(params) {
     params
   })
 }
+
+// 班级放榜查询
+// 重用getElectiveGenerationDetails, query=active=传入generation
+export function getClassGenerationDetails(params) {
+  return request({
+    url: '/front/elective/generation/getClassGenerationDetails',
+    method: 'get',
+    params
+  })
+}

+ 0 - 2
src/api/webApi/elective/selected-subject.js

@@ -77,5 +77,3 @@ export function rejectRecommend(data) {
 
 
 
-
-

+ 13 - 2
src/common/mx-config.js

@@ -95,6 +95,7 @@ export default {
       decisionMaking: false,
       stepsVisible: true,
       title: '初录报名',
+      enrollName: '初录录取',
       description: '',
       icon: ''
     },
@@ -105,6 +106,7 @@ export default {
       decisionMaking: true,
       stepsVisible: true,
       title: '初录结果',
+      enrollName: '初录录取',
       description: '',
       icon: ''
     },
@@ -115,6 +117,7 @@ export default {
       decisionMaking: false,
       stepsVisible: true,
       title: '补录报名',
+      enrollName: '补录录取',
       description: '',
       icon: ''
     },
@@ -125,6 +128,7 @@ export default {
       decisionMaking: true,
       stepsVisible: true,
       title: '补录结果',
+      enrollName: '补录录取',
       description: '',
       icon: ''
     },
@@ -135,6 +139,7 @@ export default {
       decisionMaking: false,
       stepsVisible: true,
       title: '二次补录报名',
+      enrollName: '二次补录录取',
       description: '',
       icon: ''
     },
@@ -145,6 +150,7 @@ export default {
       decisionMaking: true,
       stepsVisible: true,
       title: '二次补录结果',
+      enrollName: '二次补录录取',
       description: '',
       icon: ''
     },
@@ -154,7 +160,8 @@ export default {
       code: 'ForceAdjust',
       decisionMaking: true,
       stepsVisible: true,
-      title: '补录调剂',
+      title: '调剂结果',
+      enrollName: '调剂录取',
       description: '',
       icon: ''
     },
@@ -190,5 +197,9 @@ export default {
       value: 'MajorFirst',
       title: '按专业'
     }
-  }
+  },
+  studentReleaseQueryCodes: [
+    { id: 'acceptCount', name: '已录取', isEnroll: true },
+    { id: 'unfinishedCount', name: '未录取', isEnroll: false }
+  ]
 }

+ 16 - 0
src/components/MxCondition/condition-object/condition-release-generation.js

@@ -0,0 +1,16 @@
+import conditionObjectBase from '../condition-object-base.js'
+import config from '@/common/mx-config'
+
+export default {
+  ...conditionObjectBase,
+  key: 'releaseGeneration',
+  title: '选科状态',
+  getList: function() {
+    return new Promise((resolve, reject) => {
+      const dmGenerations = Object.values(config.electiveGenerationOptions)
+        .filter(opt => opt.decisionMaking && opt.stepsVisible)
+        .map(opt => ({ name: opt.title, id: opt.value }))
+      resolve(dmGenerations)
+    })
+  }
+}

+ 14 - 0
src/components/MxCondition/condition-object/condition-release-query-code.js

@@ -0,0 +1,14 @@
+import conditionObjectBase from '../condition-object-base.js'
+import config from '@/common/mx-config'
+
+export default {
+  ...conditionObjectBase,
+  key: 'releaseQueryCode',
+  title: '录取情况',
+  getList: function() {
+    return new Promise((resolve, reject) => {
+      const queryCodes = config.studentReleaseQueryCodes
+      resolve(queryCodes)
+    })
+  }
+}

+ 24 - 0
src/components/MxCondition/condition-object/condition-student-round.js

@@ -0,0 +1,24 @@
+import conditionObjectBase from '../condition-object-base.js'
+import {
+  getSchoolRounds
+} from '@/api/webApi/selection.js'
+import store from '@/store/index'
+
+export default {
+  ...conditionObjectBase,
+  key: 'studentRoundId',
+  title: '选科次数',
+  getList: function() {
+    return new Promise((resolve, reject) => {
+      getSchoolRounds({ year: store.getters.currentUser.year })
+        .then(res => resolve(res.data))
+        .catch(e => reject(e))
+    })
+  },
+  getCode: function(item) {
+    return item.roundId
+  },
+  getLabel: function(item) {
+    return item.name
+  }
+}

+ 2 - 1
src/views/elective/generation/components/elective-generation-command-search.vue

@@ -19,7 +19,8 @@
         </el-input>
       </el-popover>
     </transition>
-    <el-dialog title="选科流程明细" v-if="logVisible" :visible.sync="logVisible" :width="logDialogWidth">
+    <el-dialog :title="'选科流程明细 - '+logDetail.studentName+'('+logDetail.userName+')'" v-if="logVisible"
+               :visible.sync="logVisible" :width="logDialogWidth">
       <elective-generation-flow-log :groups="generation.roundGroups" :histories="logDetail.histories"
                                     :matched-majors="logMajors"/>
     </el-dialog>

+ 6 - 1
src/views/elective/generation/components/elective-generation-flow-log.vue

@@ -39,7 +39,12 @@ export default {
       this.groups.forEach(group => {
         const keyPrefix = 'group_'
         const key = keyPrefix + group.groupId
-        columns[key] = { label: group.groupName, minWidth: '160px', slot: 'group-flow', slotHeader: 'group-header' }
+        columns[key] = {
+          label: group.groupName || group.name,
+          minWidth: '160px',
+          slot: 'group-flow',
+          slotHeader: 'group-header'
+        }
         // match major
         const groupMajors = (this.matchedMajors?.marjors
           ?.filter(m => m['matchedGroupIds'].some(id => id == group.groupId)) || [])

+ 3 - 1
src/views/elective/select/components/elective-preference-info.vue

@@ -22,7 +22,9 @@ export default {
   props: ['generation'],
   computed: {
     appliedModel() {
-      return this.generation.activeModel.prevModel
+      let prev = this.generation.activeModel.prevModel
+      while (prev.option.decisionMaking) prev = prev.prevModel
+      return prev
     },
     allSelectedGroupNames() {
       return this.appliedModel.selectedList.map(g => g.groupName).join('、 ')

+ 166 - 0
src/views/elective/select/release-list.vue

@@ -0,0 +1,166 @@
+<template>
+  <div class="app-container" v-loading="loading">
+    <el-card>
+      <mx-condition :query-params="queryParams" :require-fields="requireFields" @query="handleQuery"
+                    @invalid="handleInvalid" label-width="80px"></mx-condition>
+    </el-card>
+    <evaluation-empty v-if="emptyTitle" :title="emptyTitle" class="mt20"></evaluation-empty>
+    <el-card v-else class="mt20">
+      <template #header>
+        <div class="fx-row fx-sta-cen">
+          <el-button circle class="el-icon-refresh" @click="handleQuery"></el-button>
+          <div>
+            <span class="ml30">班级人数:{{ studentCount }}人</span>
+            <span class="ml20">已录取:{{ enrolledCount }}人</span>
+            <span class="ml20">未录取:{{ disenrollCount }}人</span>
+          </div>
+        </div>
+      </template>
+      <dynamic-table :columns="tableData.columns" :rows="tableData.rows">
+        <template #history="{row}">
+          <el-button type="text" @click="handleLogView(row)">查看</el-button>
+        </template>
+      </dynamic-table>
+    </el-card>
+    <el-dialog :title="'选科流程明细 - '+logDetail.studentName" v-if="logVisible"
+               :visible.sync="logVisible" :width="logDialogWidth">
+      <elective-generation-flow-log :groups="roundGroups" :histories="logDetail.histories"/>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import MxCondition from '@/components/MxCondition/mx-condition'
+import { getStudentSelected } from '@/api/webApi/elective/selected-subject'
+import config from '@/common/mx-config'
+import { mapGetters } from 'vuex'
+import DynamicTable from '@/components/dynamic-table/index'
+import { getClassGenerationDetails } from '@/api/webApi/elective/generation'
+import ElectiveGenerationFlowLog from '@/views/elective/generation/components/elective-generation-flow-log'
+
+export default {
+  name: 'ElectiveReleaseList',
+  components: { ElectiveGenerationFlowLog, DynamicTable, MxCondition },
+  data() {
+    return {
+      loading: false,
+      // query params
+      queryParams: {
+        year: '',
+        roundId: '',
+        releaseGeneration: '',
+        releaseQueryCode: ''
+      },
+      requireFields: ['year', 'roundId', 'releaseGeneration', 'releaseQueryCode'],
+      // status & results
+      electiveStatus: null,
+      releaseSummary: null,
+      // log
+      logVisible: false,
+      logDetail: null
+    }
+  },
+  computed: {
+    ...mapGetters(['currentUser']),
+    mappedParams() {
+      return {
+        year: this.queryParams.year,
+        roundId: this.queryParams.roundId,
+        generation: this.queryParams.releaseGeneration,
+        queryCode: this.queryParams.releaseQueryCode
+      }
+    },
+    currentOpt() {
+      return Object.values(config.electiveGenerationOptions).find(opt => opt.value == this.electiveStatus?.currentGeneration)
+    },
+    releaseOpt() {
+      return Object.values(config.electiveGenerationOptions).find(opt => opt.value == this.queryParams.releaseGeneration)
+    },
+    queryCodeOpt() {
+      return config.studentReleaseQueryCodes.find(opt => opt.id == this.queryParams.releaseQueryCode)
+    },
+    emptyTitle() {
+      if (!this.electiveStatus) return '暂无数据'
+      if (this.electiveStatus.currentGeneration <= config.electiveGenerationOptions.init.value) return '选科未开启'
+      if (this.queryParams.releaseGeneration > this.electiveStatus.currentGeneration) return '选科还未进行至' + this.releaseOpt.title
+      return ''
+    },
+    roundName() {
+      return this.electiveStatus?.selectResult?.name || ''
+    },
+    studentCount() {
+      return this.releaseSummary?.studentCount || 0
+    },
+    enrolledCount() {
+      return this.releaseSummary?.enrolledCount || 0
+    },
+    disenrollCount() {
+      return this.studentCount - this.enrolledCount
+    },
+    tableData() {
+      const rows = this.releaseSummary?.details || []
+      const columns = [
+        { prop: 'localRoundName', label: '选科次数' },
+        { prop: 'className', label: '班级' },
+        { prop: 'studentName', label: '姓名' },
+        { prop: 'localApplyGroupNames', label: '报名组合', hidden: this.queryCodeOpt.isEnroll },
+        { prop: 'rankInAppliedGroup', label: '报名排名', hidden: this.queryCodeOpt.isEnroll },
+        { prop: 'enrollGroupName', label: '录取组合', hidden: !this.queryCodeOpt.isEnroll },
+        { prop: 'rankInEnrolledGroup', label: '录取排名', hidden: !this.queryCodeOpt.isEnroll },
+        { prop: 'localEnrollGeneration', label: '录取情况' },
+        { prop: 'action', label: '操作', slotBody: 'history' }
+      ]
+      rows.forEach(row => {
+        row.localRoundName = this.roundName
+        if (this.queryCodeOpt.isEnroll) {
+          const enrollGenOpt = Object.values(config.electiveGenerationOptions).find(opt => opt.value == row['enrollGeneration'])
+          row.localEnrollGeneration = enrollGenOpt.enrollName
+        } else {
+          row.localApplyGroupNames = Array.isArray(row['applyGroupNames'])
+            ? row['applyGroupNames'].join('/')
+            : row['applyGroupNames']
+        }
+      })
+      return {
+        rows,
+        columns
+      }
+    },
+    roundGroups() {
+      return this.electiveStatus?.selectResult?.groupList || []
+    },
+    logDialogWidth() {
+      const expectedWidth = (this.roundGroups.length + 3) * 160 // 假定elective-generation-flow-log 单格宽160
+      const finalWidth = Math.min(expectedWidth, window.innerWidth * 0.8)
+      return finalWidth + 'px'
+    }
+  },
+  methods: {
+    async handleQuery() {
+      this.loading = true
+      try {
+        if (this.queryParams.roundId != this.electiveStatus?.selectResult?.roundId) {
+          const resStatus = await getStudentSelected(this.mappedParams)
+          this.electiveStatus = resStatus.data
+        }
+        const resSummary = await getClassGenerationDetails(this.mappedParams)
+        this.releaseSummary = resSummary.data
+      } finally {
+        this.loading = false
+      }
+    },
+    handleInvalid() {
+      this.electiveStatus = null
+      this.releaseSummary = null
+    },
+    handleLogView(row) {
+      this.logDetail = row
+      this.logVisible = true
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 2 - 2
src/views/system/user/profile/round-select.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="app-container" v-loading="loading">
-    <el-card shadow="hover">
+    <el-card>
       <template #header>选科信息</template>
       <el-row :gutter="20">
         <el-col :span="12">
@@ -31,7 +31,7 @@
                     :optional-majors="optionalMajors"></report-table>
       <evaluation-empty v-else :shadow="false" :title="emptyTitle"></evaluation-empty>
     </el-card>
-    <el-card shadow="hover" class="mt20">
+    <el-card class="mt20">
       <template #header>选科报告</template>
       <el-button v-if="false" @click="goReportDemo">选科报告1</el-button>
       <el-button @click="goReportDemoOnline">选科报告</el-button>