entry-ai-form.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. <template>
  2. <view>
  3. <z-paging ref="paging" v-model="list" @query="handleQuery">
  4. <template #top>
  5. <mx-nav-bar v-bind="navBinding"/>
  6. </template>
  7. <ai-form-filter @force="init=false,$refs.paging.reload()"/>
  8. <ai-form-filter-shortcut/>
  9. <view class="p-20 fx-col gap-20">
  10. <voluntary-major v-for="item in list" :data="item"/>
  11. </view>
  12. <template #bottom>
  13. <ai-form-footer :total="total" @preview="handlePreview"/>
  14. </template>
  15. <uv-loading-page :loading="loading"/>
  16. </z-paging>
  17. <ai-form-cart ref="cartRef"/>
  18. </view>
  19. </template>
  20. <script>
  21. import _ from 'lodash';
  22. import LargeHeader from "@/pages/ie/components/large-header.vue";
  23. import AiFormFooter from "@/pages/ie/entry-ai-form/components/ai-form-footer.vue";
  24. import AiFormFilter from "@/pages/ie/entry-ai-form/components/ai-form-filter.vue";
  25. import AiFormFilterShortcut from "@/pages/ie/entry-ai-form/components/ai-form-filter-shortcut.vue";
  26. import VoluntaryMajor from "../components/card/voluntary-major.vue";
  27. import AiFormCart from "./components/ai-form-cart.vue";
  28. import MxConst from "@/common/MxConst";
  29. import {postAIResult, submitVoluntary} from "@/api/webApi/ie-voluntary";
  30. import {sleep, toast} from "@/uni_modules/uv-ui-tools/libs/function";
  31. import {confirmAsync} from "@/utils/uni-helper";
  32. import {useTransfer} from "@/hooks/useTransfer";
  33. import {useProvidePageScroll} from "@/hooks/usePageScrollInjection";
  34. import {useProvideMajorTreeService} from "@/pages/ie/hooks/useMajorTreeInjection";
  35. export default {
  36. components: {AiFormFilterShortcut, AiFormFilter, AiFormFooter, LargeHeader, VoluntaryMajor, AiFormCart},
  37. data() {
  38. return {
  39. init: false,
  40. loading: false,
  41. navBinding: {title: 'AI志愿', mode: 'light', fixed: false, leftClickBlock: this.checkFormBeforeQuit},
  42. filter: {
  43. majorTypes: [],
  44. enumPickType: MxConst.enum.ai.pickType.all.value,
  45. enumPickEmpty: null,
  46. hasClearing: null,
  47. keyword: '',
  48. level: [],
  49. type: [],
  50. natureTypeCN: [],
  51. location: [],
  52. star: [],
  53. bxType: []
  54. },
  55. filterShortcut: [],
  56. config: {
  57. collegeMin: 2,
  58. collegeLimit: 5,
  59. majorLimit: 4,
  60. // 提交时仅保留的university属性,减少传输量
  61. commitUPropsKeep: ['code'],
  62. // 变更条件时,需要清空志愿表的filter属性,这些条件会影响专业概率
  63. confirmChangeFilterKeys: ['majorTypes', 'enumPickType', 'enumPickEmpty', 'hasClearing']
  64. },
  65. list: [],
  66. total: 0,
  67. // 填报项 // 包含:填报的院校引用,院校的专业用selected属性标记已填报
  68. selected: [],
  69. commit: {
  70. year: '', // 缺省的话后台会补充
  71. name: '', // 志愿表名,缺省的话后台会补充
  72. voluntaryType: MxConst.enum.ai.voluntaryType.ai,
  73. },
  74. showCart: false
  75. }
  76. },
  77. setup() {
  78. const {prevData, transferTo} = useTransfer()
  79. const scrollTop = useProvidePageScroll()
  80. return {
  81. prevData,
  82. transferTo,
  83. scrollTop
  84. }
  85. },
  86. computed: {
  87. request() {
  88. return {
  89. ...this.prevData,
  90. filter: this.filter
  91. }
  92. }
  93. },
  94. watch: {
  95. filter: {
  96. deep: true,
  97. handler: function () {
  98. this.init = false
  99. this.$refs.paging.reload()
  100. }
  101. }
  102. },
  103. provide() {
  104. return {
  105. getPrevData: () => this.prevData,
  106. getFilter: () => this.filter,
  107. getShortcut: () => this.filterShortcut,
  108. getConfig: () => this.config,
  109. getCart: () => this.selected,
  110. getCommit: () => this.commit,
  111. handleCartAdd: this.handleCartAdd,
  112. handleCartRemove: this.handleCartRemove,
  113. handleCartClear: this.handleCartClear,
  114. handleCommit: this.handleCommit
  115. }
  116. },
  117. methods: {
  118. checkFormBeforeQuit() {
  119. if (!this.selected.length) return true
  120. return confirmAsync('您有未保存的志愿表,确认退出?')
  121. },
  122. syncRowsToSelected(rows) {
  123. if (!Array.isArray(rows)) return
  124. rows.forEach(u => {
  125. // created `selected` property here, can make it reactive.
  126. u.majorDetails.forEach(m => m.selected = false)
  127. const match = this.selected.find(i => i.university.code == u.university.code)
  128. if (match && match.majorDetails.length == u.majorDetails.length &&
  129. match.majorDetails.every(m1 => u.majorDetails.some(m2 => m1.majorCode == m2.majorCode))) {
  130. // fully match, then copy major's selected property, and replace ele in cart.
  131. u.majorDetails.forEach(m => {
  132. const mMatch = match.majorDetails.find(item => item.majorCode == m.majorCode)
  133. m.selected = mMatch.selected
  134. })
  135. const replaceIdx = this.selected.indexOf(match)
  136. this.selected.splice(replaceIdx, 1, u)
  137. } else if (match) {
  138. throw new Error('query list not match with selected elements')
  139. }
  140. })
  141. },
  142. async handleQuery(pageNum, pageSize) {
  143. const params = {
  144. pageNum, pageSize,
  145. ...this.request
  146. }
  147. console.log('handle query', params)
  148. try {
  149. if (pageNum == 1 && !this.init) this.loading = true
  150. const res = await postAIResult(params)
  151. this.syncRowsToSelected(res.rows)
  152. this.$refs.paging.completeByTotal(res.rows, res.total)
  153. this.total = res.total
  154. this.loading = false
  155. } catch (e) {
  156. console.log('ai request failed', e)
  157. this.loading = false
  158. } finally {
  159. this.init = true
  160. }
  161. },
  162. handleCartAdd(uElement, mElement) {
  163. const uAddDisable = !this.selected.includes(uElement) && this.selected.length >= this.config.collegeLimit
  164. if (uAddDisable) {
  165. toast(`最多填报${this.config.collegeLimit}个院校`)
  166. return
  167. }
  168. const mAllSelected = uElement.majorDetails.filter(i => i.selected)
  169. const mAddDisable = !mAllSelected.includes(mElement) && mAllSelected.length >= this.config.majorLimit
  170. if (mAddDisable) {
  171. toast(`每个院校最多填报${this.config.majorLimit}个专业`)
  172. return
  173. }
  174. if (!this.selected.includes(uElement)) this.selected.push(uElement)
  175. mElement.selected = true
  176. },
  177. handleCartRemove(uElement, mElement) {
  178. if (!mElement) {
  179. // remove all
  180. uElement.majorDetails.forEach(m => m.selected = false)
  181. _.remove(this.selected, uElement)
  182. return
  183. }
  184. // remove indicate
  185. mElement.selected = false
  186. const anySelected = uElement.majorDetails.some(i => i.selected)
  187. if (!anySelected) _.pull(this.selected, uElement)
  188. },
  189. handleCartClear() {
  190. this.selected.forEach(u => u.majorDetails.forEach(m => m.selected = false))
  191. _.remove(this.selected)
  192. },
  193. handlePreview() {
  194. if (!this.selected.length) {
  195. toast('请先至少选择1个专业到志愿表')
  196. return
  197. }
  198. this.$refs.cartRef.open()
  199. },
  200. async _validateCommit() {
  201. if (this.config.collegeMin && this.selected.length < this.config.collegeMin) {
  202. const err = `请至少填报${this.config.collegeMin}个院校`
  203. toast(err)
  204. return Promise.reject(err)
  205. }
  206. },
  207. async _createCommitData() {
  208. const uPropsKeep = this.config.commitUPropsKeep || []
  209. const uPropsClear = (u) => {
  210. const result = {}
  211. uPropsKeep.forEach(prop => result[prop] = u[prop])
  212. return result
  213. }
  214. const pureSelected = this.selected.map(u => ({
  215. ...u,
  216. university: uPropsClear(u.university),
  217. majorDetails: u.majorDetails.filter(m => m.selected)
  218. }))
  219. return {
  220. ...this.commit,
  221. request: this.request,
  222. details: pureSelected
  223. }
  224. },
  225. async handleCommit() {
  226. await this._validateCommit()
  227. const data = await this._createCommitData()
  228. const res = await submitVoluntary(data)
  229. toast('保存成功')
  230. await sleep(2000)
  231. const url = '/pages/ie/entry-ai-detail/entry-ai-detail'
  232. this.transferTo({url, params: {id: res.data, type: data.voluntaryType}, type: 'redirect'})
  233. }
  234. }
  235. }
  236. </script>
  237. <style lang="scss" scoped>
  238. ::v-deep .zp-page-bottom-container {
  239. z-index: 10;
  240. }
  241. </style>