123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- <template>
- <div class="app-container" v-loading="loading">
- <evaluation-title :title="title" :sub-title="subTitle" nav-back-button></evaluation-title>
- <el-card>
- <mx-condition :query-params="queryParams" :require-fields="requireFields" :local-data="localData"
- @query="handleQuery"></mx-condition>
- </el-card>
- <mx-table :rows="detailTable.rows" :prop-defines="detailTable.columns" border class="mt20 elective-flow-table">
- <template #pagedIndex="{$index}">
- {{ (queryParams.pageNum - 1) * queryParams.pageSize + $index + 1 }}
- </template>
- <template #group-header="{label, key}">
- <div class="fx-row jc-cen">
- <span>{{ label }}</span>
- <el-popover trigger="hover" :disabled="!!!detailTable.groupHeaders[key].text" class="mx-generation-rank">
- <div v-for="(desc,idx) in detailTable.groupHeaders[key].descriptions" :key="idx">
- <span>{{ desc.description }}</span> <span class="bold">{{ desc.value }}</span></div>
- <el-tag slot="reference" size="mini" class="round-y ml3">{{ detailTable.groupHeaders[key].text }}</el-tag>
- </el-popover>
- </div>
- </template>
- <template #studentName="{value, row}">
- <span :style="getStudentNameCellStyle(row)">{{ value }}</span>
- </template>
- <template #group-flow="{value}">
- <elective-flow-major v-if="value.matchedMajors.length" :icon-classes="['f-primary']"
- :matched-majors="value.matchedMajors"></elective-flow-major>
- <div class="fx-row fx-cen-cen">
- <span v-if="value.text">{{ value.text }}</span>
- <span v-else v-html="' '"></span>
- </div>
- <elective-flow-rank-descriptor v-if="value.rankDescriptors"
- :rank-descriptors="value.rankDescriptors"></elective-flow-rank-descriptor>
- </template>
- <template #flow-action="{row}">
- <el-link v-if="enableStudentTableView(row)" @click="handleStudentTable(row)" :underline="false">查看</el-link>
- <el-link v-if="enableLogView(row)" @click="handleFlowLog(row)" :underline="false">查看</el-link>
- <el-popover v-if="row['enableForce']" :ref="'force_'+row['studentId']" width="80" trigger="click"
- popper-class="zero-padding-popover">
- <div class="fx-column">
- <el-divider>调剂</el-divider>
- <el-button v-for="(g,i) in prevData.groups" :key="i" class="ml0" plain type="text"
- :disabled="g.groupId==row['disableForceGroupId']"
- @click="handleForceAdjust(g, row)&&$refs['force_'+row['studentId']].doClose()">{{ g.groupName }}
- </el-button>
- </div>
- <el-link slot="reference" :underline="false" type="warning" class="ml10">调剂</el-link>
- </el-popover>
- </template>
- </mx-table>
- <pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
- @pagination="loadGenerationDetails"></pagination>
- <el-dialog title="选科流程明细" v-if="logVisible" :visible.sync="logVisible" :width="logDialogWidth">
- <elective-generation-flow-log :groups="prevData.groups" :histories="logRow.histories"
- :matched-majors="this.majorsMap[logRow['studentId']]"/>
- </el-dialog>
- <el-dialog :title="'查看分析 '+activeOpt.title" :visible.sync="studentTableVisible" :width="logDialogWidth">
- <elective-ai-table :generation="studentGeneration" :optional-majors="studentMajors" readonly></elective-ai-table>
- </el-dialog>
- </div>
- </template>
- <script>
- import config from '@/common/mx-config'
- import consts from '@/common/mx-const'
- import transferMixin from '@/components/mx-transfer-mixin'
- import ElectiveToolsMixin from '@/views/elective/select/components/elective-tools-mixins'
- import groupTranslateMixin from '@/components/Cache/modules/mx-select-translate-mixin'
- import ElectiveColorMap from './components/elective-color-map-mixins'
- import { mapGetters } from 'vuex'
- import MxCondition from '@/components/MxCondition/mx-condition'
- import {
- enrollByForce,
- getElectiveGenerationDetails,
- getGenerationOptionalMajorsBatch
- } from '@/api/webApi/elective/generation'
- import ElectiveGenerationFlowLog from '@/views/elective/generation/components/elective-generation-flow-log'
- import ElectiveFlowMajor from '@/views/elective/generation/components/elective-flow-major'
- import ElectiveFlowRankDescriptor from '@/views/elective/generation/components/elective-flow-rank-descriptor'
- import { getStudentElectiveModels } from '@/api/webApi/elective/selected-subject'
- import ElectiveAiTable from '@/views/elective/select/components/elective-ai-table'
- import EventBus from '@/components/EventBus'
- export default {
- components: {
- ElectiveAiTable,
- ElectiveFlowRankDescriptor,
- ElectiveFlowMajor,
- ElectiveGenerationFlowLog,
- MxCondition
- },
- mixins: [transferMixin, groupTranslateMixin, ElectiveColorMap, ElectiveToolsMixin],
- name: 'generation-detail',
- computed: {
- ...mapGetters(['school']),
- title() {
- const y = (this.prevData.year + '').tailingFix('学年')
- const s = this.school.schoolName
- const n = this.prevData.roundName
- return y + s + n
- },
- options() {
- return config.electiveGenerationOptions
- },
- activeOpt() {
- return Object.values(this.options).find(opt => opt.value == this.prevData.activeGeneration)
- },
- subTitle() {
- const hideGenerations = [this.options.init, this.options.terminate]
- let generationDesc = hideGenerations.includes(this.activeOpt) ? '' : this.activeOpt?.title || ''
- if (this.prevData.isAccumulate && generationDesc) generationDesc = this.options.primary.title + ' 至 ' + generationDesc
- return generationDesc
- },
- localData() {
- this.queryParams.generation = this.prevData.queryGeneration
- this.queryParams.generationQueryCode = this.prevData.queryCode
- this.queryParams.generationGroupId = this.prevData.queryGroupId
- return {
- ignoreGroupCategories: this.prevData.ignoreGroupCategories,
- categories: this.prevData.queryableCategories,
- groups: this.prevData.groups
- }
- },
- detailTable() {
- if (!this.majorsMap) return {}
- if (!this.prevData.queryableCategories.length) return {}
- if (!this.detailWrapper?.groupDescriptors?.length) return {}
- // fixed columns
- const queryCategory = this.prevData.queryableCategories.find(i => i.id == this.queryParams.generationQueryCode)
- const ignoreGroups = this.prevData.ignoreGroupCategories.includes(this.queryParams.generationQueryCode)
- const columns = {
- index: { label: '序号', slot: 'pagedIndex' },
- className: { label: '班级' },
- studentName: { label: '姓名', slot: 'studentName' },
- userName: { label: '账号' }
- }
- if (!ignoreGroups) {
- columns.groupName = { label: queryCategory.detailName || '组合' }
- columns.datetime = { label: '时间', minWidth: '110px' }
- }
- // extension generation columns & custom group header
- const rows = this.detailWrapper.details // todo: need clone?
- const groupHeaders = {}
- const groupKeyPrefix = 'group_'
- // if (!ignoreGroups) {
- this.prevData.groups.forEach(group => {
- const groupKey = groupKeyPrefix + group.groupId
- columns[groupKey] = {
- label: group.groupName,
- slotHeader: 'group-header',
- slot: 'group-flow',
- minWidth: '180px'
- }
- groupHeaders[groupKey] = this.getGroupHeaderDescription(group.groupId)
- rows.forEach(row => {
- row[groupKey] = this.getGroupDescription(row, group.groupId, this.majorsMap)
- })
- })
- // }
- // 初选报名不需要展示操作
- const actionHide = this.activeOpt?.value == this.options.primary.value
- columns.actions = { label: '操作', slot: 'flow-action', minWidth: '100px', hidden: actionHide }
- return {
- rows,
- columns,
- groupHeaders
- }
- },
- logDialogWidth() {
- const expectedWidth = (this.prevData.groups.length + 3) * 160 // 假定elective-generation-flow-log 单格宽160
- const finalWidth = Math.min(expectedWidth, window.innerWidth * 0.8)
- return finalWidth + 'px'
- }
- },
- data() {
- return {
- loading: false,
- requireFields: ['generationQueryCode'],
- queryParams: {
- pageNum: 1,
- pageSize: 20,
- generation: '',
- generationQueryCode: '',
- generationGroupId: ''
- },
- // query data
- total: 0,
- detailWrapper: null,
- majorsMap: null,
- // log
- logVisible: false,
- logRow: {},
- studentTableVisible: false,
- studentGeneration: {},
- studentMajors: []
- }
- },
- methods: {
- handleQuery() {
- this.queryParams.pageNum = 1
- this.loadGenerationDetails()
- },
- loadGenerationDetails() {
- const params = {
- pageNum: this.queryParams.pageNum,
- pageSize: this.queryParams.pageSize,
- roundId: this.prevData.roundId,
- generation: this.queryParams.generation,
- groupId: this.queryParams.generationGroupId,
- queryCode: this.queryParams.generationQueryCode,
- active: this.prevData.activeGeneration
- }
- this.loading = true
- this.majorsMap = null
- getElectiveGenerationDetails(params).then(res => {
- this.total = res['total'] || 0
- this.detailWrapper = res.data
- const studentIds = res.data.details?.map(d => d['studentId']).toString()
- if (!studentIds) return Promise.resolve({ data: {} })
- return getGenerationOptionalMajorsBatch({ studentIds })
- }).then(res => {
- this.majorsMap = res.data
- }).finally(() => this.loading = false)
- },
- getGroupHeaderDescription(groupId) {
- const statistic = this.detailWrapper.groupDescriptors.find(d => d.groupId == groupId)
- if (!statistic?.descriptors?.length) return {}
- const descriptors = statistic.descriptors //.reverse()
- return {
- text: descriptors.map(d => d.value).join('/'),
- descriptions: descriptors
- }
- },
- getGroupDescription(row, groupId) {
- const matchedMajors = (this.majorsMap[row['studentId']]
- ?.marjors?.filter(m => m['matchedGroupIds'].some(id => id == groupId)) || [])
- .groupBy(m => m.collegeName)
- const current = this.prevData.activeGeneration
- const options = Object.values(this.options)
- const histories = row['histories'] || []
- const filterHistories = []
- for (let g = current; g > 0; g--) {
- const opt = options.find(opt => opt.value == g)
- const groupHistories = histories.filter(h => h.generation == g && h.groupId == groupId)
- if (groupHistories.length) filterHistories.push(groupHistories)
- // if (g < current && opt.decisionMaking) break // TODO: 仅迭代至最近的决策代(可能需要调整)
- }
- if (!filterHistories.length) return { matchedMajors }
- filterHistories.reverse() // 还原顺序
- const mergedHistories = filterHistories.reduce((prev, cur) => {
- prev.push(...cur)
- return prev
- }, [])
- return {
- matchedMajors,
- text: mergedHistories.map(h => h.description).join('/'),
- histories: mergedHistories,
- rankDescriptors: mergedHistories.last(i => !!i.rankDescriptors?.length)?.rankDescriptors
- }
- },
- getStudentNameCellStyle(row) {
- const style = {}
- if (row.color) style.color = this.matchElectiveColor(row.color)
- return style
- },
- enableLogView(student) {
- return this.activeOpt?.value > this.options.primary.value
- && !this.enableStudentTableView(student)
- },
- enableStudentTableView(student) {
- return !this.isGroupEnrolled(student)
- && this.activeOpt?.value > this.options.primary.value
- && this.activeOpt?.value < this.options.forceAdjust.value
- },
- handleFlowLog(row) {
- this.logRow = row
- this.logVisible = true
- },
- handleStudentTable(row) {
- this.loading = true
- const query = {
- generation: this.activeOpt.value,
- studentId: row.studentId
- }
- const majors = this.majorsMap[query.studentId]?.marjors || []
- getStudentElectiveModels(query).then(res => {
- this.studentMajors = majors
- const generationModels = res.data?.filter(g => g.generation <= query.generation) || []
- this.initGenerationModels(generationModels, this.options)
- this.studentGeneration = {
- options: this.options,
- current: query.generation,
- currentOpt: this.activeOpt,
- active: query.generation,
- activeOpt: this.activeOpt,
- roundGroups: this.prevData.groups,
- models: generationModels,
- activeModels: generationModels,
- activeModel: generationModels.find(g => g.generation == query.generation)
- }
- this.studentTableVisible = true
- }).finally(() => this.loading = false)
- },
- handleForceAdjust(group, row) {
- let message = `确认将'${row.studentName}'调剂至'${group.groupName}'?!`
- if (row['disableForceGroupId'] > 0) {
- message += `\n 当前录取组合'${this.translateGroup(row['disableForceGroupId'])}'`
- }
- this.$confirm(message, '强制调剂提醒', { type: 'warning' }).then(() => {
- enrollByForce(group.groupId, row['studentId']).then(res => {
- this.loadGenerationDetails() // refresh
- EventBus.instance.$emit(consts.keys.electiveGlobalChangeEvent) // global notify for refresh data
- })
- })
- return true
- }
- }
- }
- </script>
- <style>
- @import url('./components/elective-flow-table-style.css');
- </style>
|