ScoringPanel.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. <template>
  2. <scoring-panel-container
  3. v-model="modalVisible"
  4. title="键盘给分"
  5. modal-class="no-mask"
  6. :width="'388px'"
  7. :modal="false"
  8. :can-resize="true"
  9. class="keybord-dialog"
  10. @close="onToggleClick"
  11. >
  12. <div class="scoring-panel-box" :class="getClass('modal-box')">
  13. <template v-for="(question, index) in questionList" :key="question.mainNumber + question.subNumber">
  14. <div v-if="dialogMode" class="flex dialog-name items-center">
  15. <p>{{ question.mainNumber }} - {{ question.subNumber }}</p>
  16. <p class="main-title">{{ question.mainTitle }}</p>
  17. </div>
  18. <scoring-panel-item
  19. v-model:score="scoreValues[index]"
  20. v-model:scoreValidFail="scoreValidFail[index]"
  21. :active="activeIndex === index"
  22. :modal="dialogMode"
  23. :toggle-modal="props.toggleModal && index === 0"
  24. :question="question"
  25. @blur="() => onBlur(index)"
  26. @enter="() => onEnter(index)"
  27. @focused="() => onFocused(index)"
  28. @toggle-click="onToggleClick"
  29. ></scoring-panel-item>
  30. </template>
  31. </div>
  32. <template #footer>
  33. <el-button type="primary" class="full-w" :disabled="!allowSubmit" @click="onEnter(0)">确定</el-button>
  34. </template>
  35. </scoring-panel-container>
  36. </template>
  37. <script setup lang="ts" name="ScoringPanel">
  38. import { watch, withDefaults, ref, defineComponent, useSlots, computed, nextTick, onMounted } from 'vue'
  39. import { ElButton } from 'element-plus'
  40. import BaseDialog from '@/components/element/BaseDialog.vue'
  41. import ScoringPanelItem from './ScoringPanelItem.vue'
  42. import useVModel from '@/hooks/useVModel'
  43. import useVW from '@/hooks/useVW'
  44. import useFetch from '@/hooks/useFetch'
  45. import { sessionStorage } from '@/plugins/storage'
  46. const props = withDefaults(
  47. defineProps<{
  48. /** 弹窗模式? */
  49. modal?: boolean
  50. /** 是否可以切换显示模式 */
  51. toggleModal?: boolean
  52. /** 显示隐藏 */
  53. visible?: boolean
  54. /** 分值 */
  55. score: (number | string)[]
  56. mainNumber?: number | null
  57. id?: number | null
  58. autoVisible?: boolean | undefined
  59. }>(),
  60. { modal: false, toggleModal: true, score: () => [], mainNumber: null, id: null, autoVisible: true }
  61. )
  62. const emits = defineEmits(['submit', 'update:score', 'update:visible', 'update:modal'])
  63. const dialogModeBeforeSubmit = ref<boolean>(false)
  64. const dialogMode = ref<boolean>(props.modal)
  65. const LessRenderComponent = defineComponent({
  66. name: 'LessRender',
  67. inheritAttrs: false,
  68. props: {
  69. modelValue: {
  70. type: Boolean,
  71. default: false,
  72. },
  73. },
  74. render() {
  75. return this.modelValue ? useSlots()?.default?.() : null
  76. },
  77. })
  78. const ScoringPanelContainer = computed(() => {
  79. return dialogMode.value ? BaseDialog : LessRenderComponent
  80. })
  81. const modalVisible = useVModel(props, 'visible')
  82. const scoreValues = useVModel(props, 'score')
  83. const activeIndex = ref<number | null>(0)
  84. const scoreValidFail = ref<boolean[]>([])
  85. watch(modalVisible, (val) => {
  86. activeIndex.value = 0
  87. let sessionKeyboardShowType = sessionStorage.get('dialogModeBeforeSubmit')
  88. dialogMode.value =
  89. typeof sessionKeyboardShowType === 'boolean' ? sessionKeyboardShowType : dialogModeBeforeSubmit.value
  90. })
  91. onMounted(() => {
  92. let sessionKeyboardShowType = sessionStorage.get('dialogModeBeforeSubmit')
  93. dialogMode.value =
  94. typeof sessionKeyboardShowType === 'boolean' ? sessionKeyboardShowType : dialogModeBeforeSubmit.value
  95. })
  96. const { fetch: getQuestionStruct, reset: resetQuestionStruct, result: questionStruct } = useFetch('getQuestionStruct')
  97. watch([() => props.id, () => props.autoVisible], () => {
  98. if (props.autoVisible) {
  99. modalVisible.value = !!props.id
  100. }
  101. scoreValues.value = []
  102. })
  103. watch(
  104. () => props.mainNumber,
  105. () => {
  106. /** reset scores */
  107. scoreValues.value = []
  108. if (props.mainNumber) {
  109. resetQuestionStruct()
  110. getQuestionStruct({ mainNumber: props.mainNumber })
  111. }
  112. },
  113. { immediate: true }
  114. )
  115. const questionList = computed(() => {
  116. if (!questionStruct.value) {
  117. return []
  118. }
  119. const { mainNumber, mainTitle, questionList = [] } = questionStruct.value
  120. return questionList.map((q) => ({ ...q, mainNumber, mainTitle }))
  121. })
  122. const allowSubmit = computed(() => {
  123. let filterArr = scoreValues.value.filter((score) => score != undefined)
  124. // return scoreValues.value?.length === questionList.value?.length
  125. return filterArr.length === questionList.value?.length
  126. })
  127. const getClass = (val: string, callback?: string) => {
  128. return dialogMode.value ? val : callback || ''
  129. }
  130. const onSubmit = () => {
  131. if (!scoreValidFail.value.some((valid) => valid)) {
  132. emits('submit', questionStruct.value)
  133. }
  134. }
  135. const onEnter = (index: number) => {
  136. dialogModeBeforeSubmit.value = dialogMode.value
  137. sessionStorage.set('dialogModeBeforeSubmit', dialogModeBeforeSubmit.value)
  138. nextTick(() => {
  139. // console.log('index:', index)
  140. // console.log('scoreValues.value.length:', scoreValues.value.length)
  141. // console.log('questionList.value?.length:', questionList.value?.length)
  142. let filterArr = scoreValues.value.filter((score) => score != undefined)
  143. // if (scoreValues.value.length >= questionList.value?.length) {
  144. if (filterArr.length >= questionList.value?.length) {
  145. const nullScoreIndex = scoreValues.value?.findIndex((v) => !`${v}`)
  146. const validFailIndexIndex = scoreValidFail.value?.findIndex((v) => !!v)
  147. if (nullScoreIndex >= 0) {
  148. activeIndex.value = nullScoreIndex
  149. } else if (validFailIndexIndex >= 0) {
  150. activeIndex.value = validFailIndexIndex
  151. } else {
  152. onSubmit()
  153. }
  154. } else {
  155. activeIndex.value = index + 1
  156. }
  157. })
  158. }
  159. const onFocused = (index: number) => {
  160. activeIndex.value = index
  161. }
  162. const onBlur = (index: number) => {
  163. if (activeIndex.value === index) {
  164. activeIndex.value = null
  165. }
  166. }
  167. const onToggleClick = () => {
  168. if (modalVisible.value) {
  169. dialogModeBeforeSubmit.value = dialogMode.value ? false : true
  170. sessionStorage.set('dialogModeBeforeSubmit', dialogModeBeforeSubmit.value)
  171. }
  172. dialogMode.value = props.toggleModal ? !dialogMode.value : dialogMode.value
  173. if (!props.toggleModal) {
  174. modalVisible.value = false
  175. }
  176. }
  177. </script>
  178. <style scoped lang="scss">
  179. .modal-box {
  180. max-height: 50vh;
  181. min-height: 8vw;
  182. .dialog-name {
  183. color: $color--primary;
  184. font-weight: bold;
  185. padding-left: 10px;
  186. }
  187. }
  188. </style>