Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

form.vue 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <template lang="pug">
  2. .form.f-col.start.mr-1.w-full(v-if='form.length')
  3. header
  4. p answers to save: {{ answers }}
  5. ul.w-full(v-for='(step, i) in form')
  6. li.f-row.start.b-solid.p-0(
  7. v-for='prompt in step'
  8. v-if='step && step.length'
  9. v-show='(i + 1) == state.step'
  10. )
  11. h3 {{ prompt.question }}?
  12. .response-wrapper(v-if='prompt.type === "input-string"')
  13. label {{ prompt.type }}
  14. input(v-model='answers[makeKebob(prompt.question)]')
  15. .response-wrapper(v-else-if='prompt.type === "tag-cloud"')
  16. label {{ prompt.type }}
  17. button.p-0(
  18. :disabled='response == answers[makeKebob(prompt.question)]'
  19. :prompt-question='makeKebob(prompt.question)'
  20. @click='respondFromTag'
  21. v-for='response in prompt.responses'
  22. ) {{ response }}
  23. //- TODO: Checklist
  24. .response-wrapper(v-if='prompt.type === "checklist"')
  25. .checklist(v-for='response in prompt.responses')
  26. input(
  27. type='checkbox'
  28. :id='response'
  29. :name='response'
  30. v-model='answers[makeKebob(prompt.question)]')
  31. label {{ response}}
  32. //- TODO: Slider from -3 to 0 to +3 (increments of 1)
  33. .response-wrapper(v-if='prompt.type === "slider"')
  34. label {{ prompt.type }}
  35. input(
  36. type='range'
  37. min='-3'
  38. max='3'
  39. v-model='answers[makeKebob(prompt.question)]')
  40. footer.f-row.w-full
  41. button.p-1(:disabled='state.step == 1' @click='back') back
  42. button.p-1(:disabled='state.step == form.length' @click='next') next
  43. button.p-1(:disabled='state.step != form.length' @click='next') save
  44. .f-grow
  45. p step: {{ state.step }} of {{ form.length }}
  46. </template>
  47. <script setup>
  48. import Joi from 'joi'
  49. import { validatorMapping, makeKebob } from '@/utils'
  50. import { defineProps, reactive } from 'vue'
  51. const props = defineProps({
  52. form: {
  53. type: Array,
  54. required: true,
  55. },
  56. })
  57. /**
  58. * Our form is comprised of steps, and each step has a series of questions
  59. */
  60. const state = reactive({ step: 1 })
  61. /**
  62. * Create state object to store answers based on the questions in formSteps
  63. */
  64. const answers = reactive({})
  65. const resetAnswers = () => {
  66. Object.keys(answers).forEach(key => (answers[key] = null))
  67. }
  68. resetAnswers()
  69. /**
  70. * Callback for clicking a tag to respond
  71. */
  72. const respondFromTag = e => {
  73. const answer = e.target.textContent
  74. const questionKey =
  75. e.target.attributes.getNamedItem('prompt-question').nodeValue
  76. answers[questionKey] = answer
  77. }
  78. /**
  79. * Check answered fields in current step, build a validator, then validate the answers by prompt.type
  80. * @param {number} step // The current step
  81. * @returns {object} // Joi object
  82. */
  83. const isValid = step => {
  84. const schema = {}
  85. const answeredThisStep = {}
  86. props.form[step].forEach(prompt => {
  87. const key = makeKebob(prompt.question)
  88. schema[key] = validatorMapping[prompt.type]
  89. answeredThisStep[key] = answers[key]
  90. })
  91. return Joi.object(schema).validate(answeredThisStep)
  92. }
  93. /**
  94. * Save or take the-nNext step in the form
  95. */
  96. const next = e => {
  97. const validity = isValid(state.step - 1)
  98. if (validity.error) return console.error(validity.error)
  99. // Save or next
  100. if (state.step === props.form.length) {
  101. alert('saved...')
  102. resetAnswers()
  103. state.step = 1
  104. } else if (state.step < props.form.length) {
  105. state.step++
  106. } else {
  107. console.error(
  108. `Cannot perform action: next() | on form step: ${state.step} of ${props.formSteps.length}`,
  109. )
  110. }
  111. }
  112. /**
  113. * Back a step in the form
  114. */
  115. const back = e => {
  116. if (state.step > 1) {
  117. state.step--
  118. } else {
  119. console.error(
  120. `Cannot perform action: back() | on form step: ${state.step} of ${props.formSteps.length}`,
  121. )
  122. }
  123. }
  124. </script>
  125. <style lang="postcss">
  126. // prettier-ignore
  127. @import '@/sss/theme.sss'
  128. .form
  129. color: $light
  130. ul
  131. list-style: none
  132. </style>