report-table.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. <template>
  2. <div>
  3. <div class="mb10 fx-row fx-end-base jc-between">
  4. <div class="fx-row fx-end-cen">
  5. <slot name="header-prefix"></slot>
  6. <p v-if="generation.active > 1" v-html="currentSupplyInfo"></p>
  7. </div>
  8. <div>
  9. <el-button v-if="aiButtonShow" type="primary" @click="toAiAnalysis">AI分析</el-button>
  10. <!-- <el-button>选科历史记录</el-button>-->
  11. </div>
  12. </div>
  13. <mx-table :propDefines="formatCols" :rows="formatRows">
  14. <template #underOver="{value}">
  15. <over-under-badge :value="value"></over-under-badge>
  16. </template>
  17. <template #group="{row}">
  18. <over-under-badge :value="row.isRecommend?1:0" over-text="荐" over-type="success">
  19. <el-tag size="medium" :type="isGroupOverSetting(row) ? 'danger' : 'warning'">{{ row.groupName }}</el-tag>
  20. </over-under-badge>
  21. </template>
  22. <template #temp="{row}">
  23. <span class="btn-blue mr5" @click="toSelectSub(row)">选择</span>
  24. <span class="btn-green" @click="toReport">查看记录</span>
  25. <!-- <el-button>查看</el-button>-->
  26. </template>
  27. <template #signUp="{row}">
  28. <div v-if="!row.allowSelect">
  29. <div v-if="row.selected">
  30. <span class="btn-red" v-if="!row.rejected" @click="singleCommit('','取消报名')">取消报名</span>
  31. <span v-else>已拒绝</span>
  32. </div>
  33. <span v-else>{{ row.disabledReason || '无法报名' }}</span>
  34. </div>
  35. <div v-else>
  36. <!-- 初录后报名代 -->
  37. <span v-if="generation.current > 1" @click="singleCommit(row)" class="btn-green">报名</span>
  38. <!-- 初录报名代 -->
  39. <div v-else>
  40. <span class="f-red btn-red" v-if="row.selected" @click="toUnSelect(row)">取消报名</span>
  41. <span class="btn-green" v-else @click="toSelect(row)">报名</span>
  42. </div>
  43. </div>
  44. </template>
  45. <template #subjects="{row}">
  46. <el-row>
  47. <el-col :span="8" v-for="subject in row.subjects">
  48. <el-popover
  49. placement="top"
  50. popper-class="zero-padding-popover"
  51. trigger="hover"
  52. >
  53. <div class="fx-column">
  54. <el-button plain type="text">{{ subject }}</el-button>
  55. </div>
  56. <el-tag type="success" slot="reference" class="mr10 mb10">{{ subject[0] }}</el-tag>
  57. </el-popover>
  58. </el-col>
  59. </el-row>
  60. </template>
  61. <template #colleges="{row}">
  62. <el-row>
  63. <el-col :span="12" v-for="college in row.colleges">
  64. <el-popover
  65. placement="top"
  66. popper-class="zero-padding-popover"
  67. trigger="hover"
  68. >
  69. <div class="fx-column">
  70. <el-button plain type="text">{{ college.major }}</el-button>
  71. </div>
  72. <el-tag type="success" slot="reference" class="mr10 mb10">{{ college.major[0] }}</el-tag>
  73. </el-popover>
  74. :
  75. <span>{{ college.college }}</span>
  76. </el-col>
  77. </el-row>
  78. </template>
  79. </mx-table>
  80. <!-- 初录 多志愿拖拽 -->
  81. <div
  82. v-if="this.generation.active == generation.options.primary.value || this.generation.active == generation.options.primaryDM.value">
  83. <p>您的选科志愿: <span v-for="(item,index) in activeModels.generation.selectedList">
  84. {{ item.groupName }}
  85. </span>
  86. </p>
  87. <div>
  88. <test-drage ref="drage" :btnDisabled="this.generation.current != generation.options.primary.value"
  89. :selectedList="activeModels.generation.selectedList"
  90. ></test-drage>
  91. <el-button @click="commit" type="primary" v-if="this.generation.current == generation.options.primary.value">
  92. 提交
  93. </el-button>
  94. </div>
  95. </div>
  96. <div v-if="generation.current > generation.active && generation.active > generation.options.primaryDM.value">
  97. <!-- 补录报名和二次补录报名和调剂报名历史报名信息 -->
  98. {{ historySupply }}
  99. </div>
  100. <!-- 补录报名和二次补录报名和调剂报名 -->
  101. <div v-if="flagShow">
  102. <div class="mb5 mt10 text-right">
  103. <el-popover
  104. v-if="activeModels.generation.models.filter(item => {return item.selected}).length == 0"
  105. placement="right"
  106. width="300"
  107. v-model="popoShow"
  108. trigger="click"
  109. >
  110. <div>
  111. <!-- 确定拒绝<span class="f-primary">{{recommendGroup.groupName}}</span>组合么?-->
  112. <!-- 确定拒绝系统给您推荐的<span class="f-primary">{{ recommendGroup.groupName }}</span>组合么?-->
  113. <el-input
  114. type="textarea"
  115. :rows="4"
  116. placeholder="请输入原因"
  117. v-model="regInfo"
  118. >
  119. </el-input>
  120. <p class="fx-row jc-between mt10">
  121. <el-button type="primary" size="mini" @click="popoShow = false">取消</el-button>
  122. <el-button type="danger" size="mini" @click="handleRejectRecommend">提交</el-button>
  123. </p>
  124. </div>
  125. <el-button slot="reference" type="danger">不同意</el-button>
  126. </el-popover>
  127. <el-button v-if="activeModels.generation.models.filter(item => {return item.rejected}).length > 0"
  128. type="primary" @click="singleCommit('','撤销拒绝报名')">撤销拒绝报名
  129. </el-button>
  130. </div>
  131. </div>
  132. <esign-dialog ref="esignDialog"></esign-dialog>
  133. <choose-subject-dialog ref="chooseDialog"></choose-subject-dialog>
  134. <select-subject-report-dialog ref="reportDialog"></select-subject-report-dialog>
  135. <Ai-dialog ref="aiDialog" :prevPreferencesInfo="currentSupplyInfo" :generation="generation"></Ai-dialog>
  136. </div>
  137. </template>
  138. <script>
  139. import AiDialog from './ai-analysis-dialog'
  140. import MxSelectTranslate from '@/components/Cache/modules/mx-select-translate-mixin.js'
  141. import TestDrage from './test-drage'
  142. import ChooseSubjectDialog from './choose-subject-dialog'
  143. import SelectSubjectReportDialog from '@/views/system/user/profile/components/select-subject-report-dialog'
  144. import EsignDialog from '@/views/system/user/profile/components/esign-dialog'
  145. import ReportStep from './report-step'
  146. import OverUnderBadge from '@/views/elective/publish/components/steps/fauclty/over-under-badge'
  147. import { rejectRecommend, submitElectiveModels } from '@/api/webApi/elective/selected-subject'
  148. const resolverModules = require.context('./round-select-resolvers', false, /\.js$/)
  149. const resolvers = resolverModules.keys().map(key => resolverModules(key).default)
  150. export default {
  151. props: {
  152. generation: Object,
  153. readonly: Boolean, // 校长端不允许操作
  154. optionalMajors: { type: Array, default: () => [] }
  155. },
  156. components: {
  157. OverUnderBadge,
  158. SelectSubjectReportDialog,
  159. ReportStep,
  160. EsignDialog,
  161. TestDrage,
  162. ChooseSubjectDialog,
  163. AiDialog
  164. },
  165. inject: {
  166. 'refreshData': {
  167. default: function() {
  168. // do nothing
  169. }
  170. }
  171. },
  172. mixins: [MxSelectTranslate, ...resolvers],
  173. data() {
  174. return {
  175. popoShow: false,
  176. activeStep: '',
  177. regInfo: '', // 拒绝原因
  178. dialogVisible: false,
  179. singleList: [], // 单志愿列表
  180. rows: []
  181. }
  182. },
  183. computed: {
  184. historySupply() {
  185. // 当前代历史报名信息
  186. if (!this.generation.models.length) return ''
  187. // 填报 || 拒绝 || 未选择
  188. // 当前代
  189. const activeModels = this.generation.models.find(item => item.generation == this.generation.active)
  190. const isRefuse = activeModels.models.filter(item => item.rejected)
  191. if (isRefuse.length > 0) {
  192. // 拒绝了
  193. return '您拒绝了填报志愿'
  194. } else {
  195. // 填报 or 未选择
  196. const supply = activeModels.models.filter(item => item.selected)
  197. if (supply.length > 0) {
  198. return `填报志愿为${supply[0].groupName}`
  199. } else {
  200. return '您未填报志愿'
  201. }
  202. }
  203. },
  204. // 不符
  205. currentSupplyInfo() {
  206. if (!this.generation.models.length) return ''
  207. // console.log(this.generation)
  208. if (this.generation.active < this.generation.options.primaryDM.value) return ''
  209. let info = ''
  210. const activeModels = this.generation.models.find(item => item.generation == this.generation.active)
  211. console.log(activeModels)
  212. // 当前代是否被录取?
  213. const approved = activeModels.models.filter(item => {
  214. return item.approved
  215. })
  216. if (approved.length) {
  217. // 查找在哪一阶段被录取 ? 根据selected 和 approved 都为true
  218. const model = this.generation.activeModels.filter(model => {
  219. const flag = model.models.some(item => {
  220. return item.selected && item.approved
  221. })
  222. if (flag) return model
  223. })
  224. console.log(model)
  225. const admissionAgent = Object.values(this.generation.options).find(item => item.value == model[0].generation - 1)
  226. console.log(Object.values(this.generation.options))
  227. // 已被录取
  228. info = `你在${admissionAgent.title}所选择的${approved[0].groupName}已被录取`
  229. } else {
  230. // 已报名未被录取的group 取当前models的前一个
  231. const prevModels = this.generation.models.find(item => item.generation == activeModels.generation - 1)
  232. // 是否拒绝 ?
  233. const isRefuse = prevModels.models.filter(item => item.rejected)
  234. if (isRefuse.length > 0) {
  235. // 拒绝报名
  236. let tips = ''
  237. const prevGen = Object.values(this.generation.options).find(item => item.value == prevModels.generation)
  238. if (prevGen.decisionMaking) {
  239. tips = Object.values(this.generation.options).find(item => item.value == prevModels.generation - 1).title
  240. } else {
  241. tips = prevGen.title
  242. }
  243. const isSupply = prevGen.decisionMaking ? ',现有以下标黄组合可以重新报名' : ''
  244. info = `<p>你在${tips}时拒绝填报志愿 ${isSupply} </p>`
  245. } else {
  246. // 没被录取的组合
  247. const refuseGroup = prevModels.models.filter(item => {
  248. return item.selected
  249. })
  250. // 获取报名的阶段
  251. let tips = ''
  252. const prevGen = Object.values(this.generation.options).find(item => item.value == prevModels.generation)
  253. if (prevGen.decisionMaking) {
  254. tips = Object.values(this.generation.options).find(item => item.value == prevModels.generation - 1).title
  255. } else {
  256. tips = prevGen.title
  257. }
  258. const isSupply = prevGen.decisionMaking ? ',现有以下标黄组合可以重新报名' : ''
  259. if (refuseGroup.length > 0) {
  260. info = `<p>你在${tips}所选择的 <span class="f-red">${refuseGroup.map(g => g.groupName).join(',').toString()}</span> 不符合条件${isSupply} </p>`
  261. } else {
  262. info = `<p>你在${tips}时未填报志愿 ${isSupply} </p>`
  263. }
  264. }
  265. }
  266. return info
  267. },
  268. flagShow() {
  269. const stepMatched = this.generation.active == this.generation.current
  270. const selectStep = !this.generation.activeOpt.decisionMaking
  271. return stepMatched && selectStep && !this.readonly && !this.activeModels.isAdmission
  272. },
  273. aiButtonShow() {
  274. if (!this.generation.activeOpt) return false
  275. const options = this.generation.options
  276. return !this.generation.activeOpt.decisionMaking &&
  277. this.generation.activeOpt != options.primary &&
  278. !this.readonly
  279. },
  280. resolveTablePrefix() {
  281. return {
  282. index: {
  283. type: 'index',
  284. label: '编号'
  285. },
  286. groupName: {
  287. label: '选科组合',
  288. slot: 'group',
  289. width: '85px'
  290. },
  291. scoreSumGroup: {
  292. label: '组合成绩'
  293. },
  294. classCount: {
  295. label: '开设班级数'
  296. },
  297. personCount: {
  298. label: '人数设置'
  299. }
  300. }
  301. },
  302. resolveTableSuffix() {
  303. const stepMatched = this.generation.active == this.generation.current
  304. const enableApply = !this.generation.currentOpt.decisionMaking
  305. const enableSignUp = stepMatched && enableApply && !this.readonly
  306. return {
  307. rankInGroup: {
  308. label: '当前组合实时排名'
  309. },
  310. rankInGrade: {
  311. label: '选科全校排名'
  312. },
  313. allowSelectTips: {
  314. label: '报名状态'
  315. },
  316. temp: {
  317. label: '选择专业',
  318. width: '140',
  319. slot: 'temp',
  320. hidden: this.readonly
  321. },
  322. subjects: {
  323. label: '自选专业',
  324. slot: 'subjects',
  325. minWidth: '150'
  326. },
  327. colleges: {
  328. label: '院校',
  329. slot: 'colleges',
  330. minWidth: '250'
  331. },
  332. signUp: {
  333. label: '操作',
  334. slot: 'signUp',
  335. width: '100',
  336. fixed: 'right',
  337. hidden: !enableSignUp
  338. }
  339. }
  340. },
  341. resolveDynamicTable() {
  342. if (!Object.keys(this.formatRows).length) return {}
  343. const options = this.generation.options
  344. if (!options || !this.generation.active) return {}
  345. const optValues = Object.values(options)
  346. const dynamicColumns = {}
  347. for (let gen = options.primary.value; gen <= this.generation.active; gen++) {
  348. const opt = optValues.find(opt => opt.value == gen)
  349. const resolverKey = opt.key + 'Resolver'
  350. const resolver = this[resolverKey]
  351. if (typeof resolver === 'function') {
  352. const genColumns = resolver(gen, this.generation.active, dynamicColumns)
  353. Object.assign(dynamicColumns, genColumns)
  354. }
  355. }
  356. return dynamicColumns
  357. },
  358. activeModels() {
  359. if (!this.generation) return {}
  360. const generation = this.generation.models.find(item => item.generation == this.generation.active)
  361. // 是否被录取
  362. const isAdmission = generation.models.some(item => item.approved || item.forceAdjusted)
  363. console.log(isAdmission)
  364. // generation.selectedList = generation.models.filter(item => {
  365. // return item.selected
  366. // })
  367. return {
  368. isAdmission,
  369. generation
  370. }
  371. },
  372. recommendGroup() {
  373. return this.activeModels?.generation.models?.find(m => m.isRecommend) || {}
  374. },
  375. // 初始化 rows 填充固定数据
  376. formatRows() {
  377. if (!this.optionalMajors) return []
  378. if (!this.generation.roundGroups?.length) return []
  379. if (!this.generation.activeModels?.length) return []
  380. const generationModels = this.generation.activeModels.last()?.models || []
  381. return this.generation.roundGroups.map(rg => {
  382. const row = generationModels.find(item => item.groupId == rg.groupId) || {}
  383. row.allowSelectTips = row.rejected ? '已拒绝' : row.allowSelect ? '报名中' : row.selected ? '已报名' : row.disabledReason || '无法报名'
  384. const matchedMajors = this.optionalMajors.filter(college => college.matchedGroupIds.includes(row.groupId))
  385. row.colleges = matchedMajors.map(m => ({ college: m.collegeName, major: m.majorCategoryName }))
  386. row.subjects = matchedMajors.map(m => m['majorCategoryName'])
  387. return row
  388. })
  389. },
  390. formatCols() {
  391. return {
  392. ...this.resolveTablePrefix,
  393. ...this.resolveDynamicTable,
  394. ...this.resolveTableSuffix
  395. }
  396. }
  397. },
  398. methods: {
  399. getModelsByStep() {
  400. return this.models.findIndex()
  401. },
  402. isGroupOverSetting(row) {
  403. if (this.generation.activeOpt.decisionMaking) {
  404. console.log('isGroupOverSetting decisionMaking', row.groupApprovedCount >= row.personCount, row)
  405. return row.groupApprovedCount >= row.personCount
  406. } else {
  407. console.log('isGroupOverSetting', row.actualCount >= row.groupIndicator, row)
  408. return row.actualCount >= row.groupIndicator
  409. }
  410. },
  411. toReport() {
  412. // 是否更改了报名数据 ?
  413. const flag = this.activeModels.generation.models.filter(item => item.selected).length == this.activeModels.generation.selectedList.length
  414. if (!flag) {
  415. this.$message.warning('请先提交更改过的志愿')
  416. return
  417. }
  418. this.$refs.reportDialog.open()
  419. },
  420. toAiAnalysis() {
  421. // AI 分析 跳转
  422. this.$refs.aiDialog.open(this.formatRows)
  423. },
  424. singleCommit(row, tips) {
  425. const type = row == '' ? tips : '提交报名'
  426. // 补录及之后
  427. this.$confirm(`是否要${type}`, {
  428. confirmButtonText: '确定',
  429. cancelButtonText: '取消',
  430. type: 'warning'
  431. }).then(() => {
  432. const arrRow = row == '' ? [] : [row]
  433. submitElectiveModels({
  434. models: arrRow
  435. // esign:this.base64Img
  436. }).then(res => {
  437. if (res.code == 200) {
  438. this.$message.success('操作成功')
  439. this.refreshData()
  440. }
  441. })
  442. }).catch(() => {
  443. this.$message({
  444. type: 'info',
  445. message: `已取消${type}`
  446. })
  447. })
  448. },
  449. commit(row) {
  450. // 初录
  451. const real = this.activeModels.generation.selectedList.filter(item => {
  452. return item.selected == true
  453. })
  454. if (real.length < this.generation.status.preferenceCount) {
  455. this.$message.warning(`需要选择${this.generation.status.preferenceCount}个志愿`)
  456. return
  457. }
  458. this.$confirm(`是否要提交报名`, {
  459. confirmButtonText: '确定',
  460. cancelButtonText: '取消',
  461. type: 'warning'
  462. }).then(() => {
  463. submitElectiveModels({
  464. models: this.activeModels.generation.selectedList
  465. // esign:this.base64Img
  466. }).then(res => {
  467. if (res.code == 200) {
  468. this.$message.success('报名成功')
  469. this.refreshData()
  470. }
  471. })
  472. }).catch(() => {
  473. this.$message({
  474. type: 'info',
  475. message: '已取消提交'
  476. })
  477. })
  478. //
  479. // this.$refs.esignDialog.open(this.activeModels.selectedList)
  480. },
  481. toSelect(row) {
  482. const preferenceCount = this.generation.status.preferenceCount
  483. const count = this.formatRows.reduce((prev, cur) => {
  484. return prev += cur.selected ? 1 : 0
  485. }, 0)
  486. if (count >= preferenceCount) {
  487. this.$message.warning(`最多选择${preferenceCount}个志愿`)
  488. return
  489. }
  490. this.activeModels.generation.models.find(item => item.groupId == row.groupId).selected = true
  491. row.selected = true
  492. if (!this.activeModels.selectedList.includes(row)) this.activeModels.generation.selectedList.push(row)
  493. },
  494. toUnSelect(row) {
  495. this.$confirm(`是否解除选科组合【${row.groupName}】`, '警告', {
  496. confirmButtonText: '确定',
  497. cancelButtonText: '取消',
  498. type: 'warning'
  499. }).then(() => {
  500. this.activeModels.generation.models.find(item => item.groupId == row.groupId).selected = false
  501. row.selected = false
  502. const start = this.activeModels.generation.selectedList.findIndex(item => item.groupId == row.groupId)
  503. this.activeModels.generation.selectedList.splice(start, 1)
  504. }).catch(() => {
  505. this.$message({
  506. type: 'info',
  507. message: '已取消解除报名'
  508. })
  509. })
  510. },
  511. toSelectSub(row) {
  512. // 是否更改了报名数据 ?
  513. const flag = this.activeModels.generation.models.filter(item => item.selected).length == this.activeModels.generation.selectedList.length
  514. if (!flag) {
  515. this.$message.warning('请先提交更改过的志愿')
  516. return
  517. }
  518. // 打开选科弹窗
  519. const course0 = this.translateCourse0(row.groupId)
  520. const course1 = this.translateCourse1(row.groupId)
  521. this.$refs.chooseDialog.open(course0, course1)
  522. },
  523. async handleRejectRecommend() {
  524. if (!this.regInfo.length) {
  525. this.$message.error('拒绝原因不能为空')
  526. return
  527. }
  528. const rejectRow = this.activeModels.generation.models.filter(item => item.allowSelect)
  529. await this.$confirm(`是否拒绝报名${rejectRow.map(item => item.groupName).join(',')}?`)
  530. // 可以报名的组合都拒绝
  531. rejectRecommend({
  532. models: rejectRow
  533. }).then(res => {
  534. if (res.code == 200) {
  535. this.refreshData()
  536. }
  537. }).finally(_ => {
  538. this.regInfo = ''
  539. })
  540. },
  541. initOption(optionalMajors) {
  542. this.optionalMajors = optionalMajors
  543. }
  544. }
  545. }
  546. </script>
  547. <style scoped>
  548. /*.cell .el-tag {*/
  549. /* margin-right: 2px;*/
  550. /*}*/
  551. /*.group /deep/ .el-badge__content.is-fixed {*/
  552. /* top: 8px;*/
  553. /* right: 14px;*/
  554. /*}*/
  555. </style>