SimulateScore.vue 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <template>
  2. <div v-if="voluntaryData" class="form">
  3. <el-form
  4. ref="form"
  5. :model="form"
  6. :rules="rules"
  7. :disabled="disabled||scoreLocked"
  8. label-width="120px"
  9. label-position="right"
  10. >
  11. <el-form-item
  12. v-if="gkType == 2 || gkType == 3"
  13. :label="voluntaryData.isNewGaokao?`您的首选科目`:`您的科目`"
  14. prop="firstSubject"
  15. >
  16. <el-radio-group v-model="form.firstSubject">
  17. <el-radio v-for="item in voluntaryData.course0" :key="item" :label="item" />
  18. </el-radio-group>
  19. </el-form-item>
  20. <el-form-item v-if="voluntaryData.isNewGaokao" :label="gkType == 2 ? '您的次选科目' : '您的选科科目'" prop="lastSubject">
  21. <el-checkbox-group v-model="form.lastSubject" :max="gkType == 2 ? 2 : 3">
  22. <el-checkbox v-for="item in voluntaryData.course1" :key="item" :label="item" />
  23. </el-checkbox-group>
  24. </el-form-item>
  25. <el-form-item label="您的总分" prop="score">
  26. <el-input v-model="form.score" type="number" placeholder="请输入分数" @mousewheel.native.prevent />
  27. </el-form-item>
  28. <el-form-item v-if="false" label="匹配位次">
  29. <el-input disabled placeholder="输入分数后查看位次" :value="form.rank.lowestRank" />
  30. <span v-if="!scoreLocked" class="f12 f-666 ml10">当前为估分模拟,不需填写排名</span>
  31. </el-form-item>
  32. <el-form v-if="!hiddenRanking" ref="inner" :model="form" :rules="rules" label-width="120px">
  33. <el-form-item label="填写位次" prop="seatInput">
  34. <el-input v-model="form.seatInput" placeholder="输入位次(可选)" />
  35. <el-link type="success" target="_blank" href="/zhiyuan/scoreSegment" class="ml10" :underline="false">查看位次</el-link>
  36. <div class="f12 f-666 mt5" style="line-height: 16px">
  37. <i class="el-icon-info" />
  38. 已根据最新位次表获取分数的最低位次,您也可以修改为成绩单上的位次。<div v-if="form.rank&&scoreLocked">位次区间[{{ form.rank.highestRank }}~{{ form.rank.lowestRank }}]</div>
  39. </div>
  40. </el-form-item>
  41. </el-form>
  42. <slot />
  43. <el-form-item v-if="scoreLocked" label="" label-width="0" style="margin-bottom: 0">
  44. <div class="f-red" style="line-height: 18px">{{ scoreLockedTips }}</div>
  45. </el-form-item>
  46. <el-form-item v-if="!isLogin||!scoreLocked&&!scoreUnlock" label="" label-width="0" style="margin-bottom: 0">
  47. <div class="f-red" style="line-height: 18px">{{ scoreRuleTips }}</div>
  48. </el-form-item>
  49. </el-form>
  50. </div>
  51. </template>
  52. <script>
  53. import { getRankByScore } from '@/api/webApi/career-course'
  54. import { getVoluntaryData } from '@/api/webApi/professlib'
  55. import { mapGetters } from 'vuex'
  56. import MxConfig from '@/common/MxConfig'
  57. import { debounce } from '@/utils'
  58. export default {
  59. name: 'SimulateScore',
  60. props: ['form', 'disabled', 'hiddenRanking', 'provinceName', 'disableRequired', 'keepValue', 'disableWatch'],
  61. inject: { 'fetchVoluntaryData': { default: () => () => null }},
  62. data() {
  63. return {
  64. scoreLockedTips: MxConfig.scoreLockedTips,
  65. scoreRuleTips: MxConfig.scoreRuleTips,
  66. // TODO: 应该将voluntaryData提取出此控件,因为时间不够没有动这里的逻辑,怕影响其它地方
  67. voluntaryData: {
  68. isNewGaokao: false,
  69. minScore: 0,
  70. maxScore: 750,
  71. course0: [],
  72. course1: []
  73. },
  74. gkType: 0, // 1 6选三 2 4选2 3 旧高考文理
  75. rules: {},
  76. lazyRules: {
  77. firstSubject: [
  78. { required: !this.disableRequired, message: '请选择科目', trigger: 'change' },
  79. {
  80. validator: (r, v, cb) => {
  81. const myForm = this.form || {}
  82. const emptyLast = Array.isArray(myForm.lastSubject) ? myForm.lastSubject.length : myForm.lastSubject
  83. if (emptyLast && !v) cb('请选择科目')
  84. else cb()
  85. }, trigger: 'change'
  86. }
  87. ],
  88. lastSubject: [
  89. { required: !this.disableRequired, message: '请选择次选科目', trigger: 'change' },
  90. {
  91. validator: (r, v, cb) => {
  92. const maxCount = this.gkType == 2 ? 2 : 3
  93. const canEmpty = !this.form.firstSubject && this.disableRequired
  94. if (v.length < maxCount && !canEmpty) {
  95. cb(`请选择${maxCount}项科目`)
  96. } else {
  97. cb()
  98. }
  99. }, trigger: 'change'
  100. }
  101. ],
  102. score: [
  103. { required: !this.disableRequired, message: '请输入你的总分', trigger: 'blur' },
  104. { pattern: /^[1-9]\d*$/, message: '分数必须为整数', trigger: 'blur' },
  105. {
  106. validator: (r, v, cb) => {
  107. if (v > this.voluntaryData.maxScore) {
  108. cb(`成绩超出当前省份满分${this.voluntaryData.maxScore}`)
  109. } else if (v < 0) {
  110. cb(`成绩不能为负数`)
  111. } else {
  112. cb()
  113. }
  114. }, trigger: ['blur', 'change']
  115. }
  116. ],
  117. seatInput: [
  118. { required: true, message: '请输入你的位次', trigger: 'blur' },
  119. { pattern: /^[1-9]\d*$/, message: '请输入合法的位次', trigger: 'blur' },
  120. {
  121. validator: (r, v, cb) => {
  122. if (!this.form.rank && !v) {
  123. cb() // ignore
  124. } else if (v >= this.form.rank?.highestRank && v <= this.form.rank?.lowestRank) {
  125. cb() // valid
  126. } else if (this.scoreUnlock) {
  127. cb() // 锁分期间启动位次校验,否则用户随意输入
  128. } else {
  129. if (!this.form.rank?.highestRank || !this.form.rank?.lowestRank) cb('无效位次') //
  130. else cb(`输入位次必须在${this.form.rank?.highestRank}~${this.form.rank?.lowestRank}之间`)
  131. }
  132. }, trigger: ['blur', 'change']
  133. }
  134. ]
  135. }
  136. }
  137. },
  138. computed: {
  139. ...mapGetters(['currentUser', 'isLogin', 'scoreLocked', 'scoreUnlock']),
  140. injectedVoluntaryData() {
  141. // noinspection JSUnresolvedFunction
  142. return this.fetchVoluntaryData()
  143. }
  144. },
  145. watch: {
  146. 'form.score': { handler: 'handleScoreUpdate' },
  147. 'form.firstSubject': { handler: 'handleScoreUpdate' },
  148. 'provinceName': function() {
  149. this.loadVoluntaryEnv()
  150. }
  151. },
  152. mounted() {
  153. if (this.injectedVoluntaryData?.maxScore) {
  154. // injected voluntary data is valid
  155. this.voluntaryData = this.injectedVoluntaryData
  156. this.processVoluntaryData(this.voluntaryData)
  157. } else {
  158. this.loadVoluntaryEnv()
  159. }
  160. },
  161. methods: {
  162. handleScoreUpdate: debounce(function() {
  163. if (this.disableWatch) return // 如果是从编辑界面同步的情况,就不去重新加载位次
  164. if (!this.form.init) return // 只监控用户修改的属性变化
  165. if (this.form.score < 0) return
  166. if (this.form.score > this.voluntaryData.maxScore) return
  167. // validate score, reduce invalid requests for ranking
  168. this.loadRankByScoreAndSubject()
  169. }, 800),
  170. loadVoluntaryEnv() {
  171. const hasBind = this.isLogin && this.currentUser.isBind
  172. if (!hasBind && !this.provinceName) return
  173. const commit = {}
  174. if (this.provinceName) commit['provinceName'] = this.provinceName
  175. getVoluntaryData(commit).then(res => {
  176. this.voluntaryData = res.data
  177. this.$emit('voluntary-ready', res.data) // 传递给父组件
  178. this.processVoluntaryData(this.voluntaryData)
  179. })
  180. },
  181. processVoluntaryData(data) {
  182. if (!data.course0.length && data.course1?.length) {
  183. this.gkType = 1
  184. } else if (data.course0.length && data.course1) {
  185. this.gkType = 2
  186. } else if (!data.isNewGaokao) {
  187. this.gkType = 3
  188. }
  189. if (this.gkType && !this.keepValue) {
  190. // reset form data
  191. this.form.score = ''
  192. this.form.firstSubject = ''
  193. this.form.lastSubject = ''
  194. if (data.isNewGaokao) this.form.lastSubject = []
  195. }
  196. this.rules = this.lazyRules
  197. this.$nextTick(() => {
  198. this.$refs.form?.clearValidate([])
  199. this.$refs.inner?.clearValidate([])
  200. })
  201. },
  202. loadRankByScoreAndSubject() {
  203. if (this.hiddenRanking) return // 此时不需要查位次
  204. if (!this.form.score) {
  205. this.$set(this.form, 'seatInput', '')
  206. this.$set(this.form, 'rank', {})
  207. return
  208. }
  209. getRankByScore({ mode: this.form.firstSubject, scoreRank: this.form.score }).then(res => {
  210. const { lowestRank, highestRank } = res.data
  211. const current = this.form.seatInput // valid current seat of user input
  212. const rangeInvalid = current < highestRank || current > lowestRank
  213. // const forceChange = !current || (rangeInvalid && !this.scoreUnlock) // 锁分期间启动位次校验,否则用户随意输入
  214. if (rangeInvalid) this.$set(this.form, 'seatInput', res.data.lowestRank)
  215. this.$set(this.form, 'rank', res.data)
  216. this.$nextTick(() => this.$refs.form?.clearValidate([]))
  217. })
  218. },
  219. async validate() {
  220. try {
  221. if (this.$refs.inner) await this.$refs.inner.validate()
  222. await this.$refs.form.validate()
  223. } catch (e) {
  224. const demo = Object.values(e).first()?.first()
  225. if (demo) this.$message.error(demo.message)
  226. return Promise.reject(e)
  227. }
  228. }
  229. }
  230. }
  231. </script>
  232. <style scoped lang="scss">
  233. .form {
  234. ::v-deep {
  235. input::-webkit-outer-spin-button,
  236. input::-webkit-inner-spin-button {
  237. -webkit-appearance: none;
  238. }
  239. }
  240. }
  241. .form {
  242. width: 640px;
  243. padding: 30px 0;
  244. margin: 0 auto;
  245. }
  246. .form {
  247. ::v-deep {
  248. .el-form-item__label {
  249. padding-right: 25px;
  250. }
  251. .el-input {
  252. width: 300px;
  253. }
  254. }
  255. }
  256. .btn {
  257. width: 120px;
  258. margin-left: 83px;
  259. margin-top: 30px;
  260. }
  261. </style>