quick-survey.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. <template>
  2. <view class="qs-wrap">
  3. <view class="qs-header">
  4. <text class="qs-title">快速了解你</text>
  5. <text class="qs-sub">答几道小题,为你推荐最合适的内容和方案</text>
  6. </view>
  7. <view class="qs-loading" v-if="loading">加载中...</view>
  8. <view class="qs-empty" v-else-if="!questions || questions.length === 0">暂无可用问卷</view>
  9. <view class="qs-body" v-else>
  10. <!-- 模板选择(多模板时可切换) -->
  11. <view class="qs-template-row" v-if="templates.length > 1">
  12. <text class="qs-template-label">选择话题:</text>
  13. <view class="qs-template-chip" v-for="(t, i) in templates" :key="getTemplateKey(t, i)"
  14. :class="{ 'qs-template-chip-active': selectedTemplateId === t.id }"
  15. @click="switchTemplate(t)">
  16. <text>{{ t.title }}</text>
  17. </view>
  18. </view>
  19. <view class="qs-q" v-for="(q, qi) in questions" :key="getQuestionKey(q, qi)">
  20. <text class="qs-q-label">{{ qi + 1 }}. {{ q.text }}</text>
  21. <view class="qs-options">
  22. <view class="qs-opt" v-for="(o, oi) in q.options" :key="getOptionKey(o, qi, oi)"
  23. :class="{ 'qs-opt-active': selectedIdx(qi) === oi }"
  24. @click="select(qi, oi)">
  25. <text class="qs-opt-text">{{ o.text }}</text>
  26. <text class="qs-opt-check" v-if="selectedIdx(qi) === oi">✓</text>
  27. </view>
  28. </view>
  29. </view>
  30. <view class="qs-btn" @click="submit" v-if="!submitting">
  31. <text class="qs-btn-text">提交,查看推荐</text>
  32. </view>
  33. <view class="qs-btn qs-btn-disabled" v-else>
  34. <text class="qs-btn-text">提交中...</text>
  35. </view>
  36. </view>
  37. </view>
  38. </template>
  39. <script>
  40. import { getActiveTemplates } from '../../utils/api.js'
  41. import { submitProblemSurvey } from '../../utils/api.js'
  42. export default {
  43. data() {
  44. return {
  45. templates: [],
  46. selectedTemplateId: null,
  47. templateTitle: '',
  48. questions: [],
  49. answers: [],
  50. loading: false,
  51. submitting: false
  52. }
  53. },
  54. onLoad() {
  55. this.loadTemplates()
  56. },
  57. methods: {
  58. getTemplateKey(t, i) {
  59. return t && t.id ? 't' + t.id : 't' + i
  60. },
  61. getQuestionKey(q, qi) {
  62. return q && q.id ? 'q' + q.id : 'q' + qi
  63. },
  64. getOptionKey(o, qi, oi) {
  65. return o && o.id ? 'o' + qi + '_' + o.id : 'o' + qi + '_' + oi
  66. },
  67. selectedIdx(qi) {
  68. var arr = this.answers[qi]
  69. return (arr && arr.length > 0) ? arr[0] : -1
  70. },
  71. select(qi, oi) {
  72. this.answers[qi] = [oi]
  73. this.$forceUpdate()
  74. },
  75. loadTemplates() {
  76. var self = this
  77. self.loading = true
  78. uni.showLoading({ title: '加载中...', mask: true })
  79. getActiveTemplates().then(function(res) {
  80. uni.hideLoading()
  81. self.loading = false
  82. var list = res && res.data
  83. var arr = Array.isArray(list) ? list : []
  84. self.templates = arr
  85. if (arr.length > 0) {
  86. self.applyTemplate(arr[0])
  87. } else {
  88. self.questions = []
  89. }
  90. }).catch(function() {
  91. uni.hideLoading()
  92. self.loading = false
  93. self.templates = []
  94. self.questions = []
  95. })
  96. },
  97. applyTemplate(t) {
  98. this.selectedTemplateId = t.id
  99. this.templateTitle = t.title || ''
  100. var parsed = []
  101. if (t.questionsJson) {
  102. try {
  103. var p = typeof t.questionsJson === 'string' ? JSON.parse(t.questionsJson) : t.questionsJson
  104. parsed = Array.isArray(p) ? p : []
  105. } catch (e) {
  106. parsed = []
  107. }
  108. }
  109. this.questions = parsed
  110. this.answers = []
  111. },
  112. switchTemplate(t) {
  113. this.applyTemplate(t)
  114. },
  115. submit() {
  116. var self = this
  117. // 至少答一题
  118. var hasAnswer = false
  119. for (var i = 0; i < this.answers.length; i++) {
  120. if (this.answers[i] && this.answers[i].length > 0) {
  121. hasAnswer = true
  122. break
  123. }
  124. }
  125. if (!hasAnswer) {
  126. uni.showToast({ title: '请至少回答一题', icon: 'none' })
  127. return
  128. }
  129. self.submitting = true
  130. uni.showLoading({ title: '提交中...', mask: true })
  131. var answers = {}
  132. for (var qi = 0; qi < this.questions.length; qi++) {
  133. var selected = this.answers[qi] || []
  134. if (selected.length === 0) continue
  135. var q = this.questions[qi]
  136. var opt = q.options && q.options[selected[0]]
  137. if (opt && opt.id) {
  138. answers[String(opt.id)] = opt.id
  139. } else if (opt && opt.value) {
  140. answers[String(opt.value)] = opt.value
  141. } else {
  142. answers['q' + qi] = opt ? opt.text : ''
  143. }
  144. }
  145. submitProblemSurvey({
  146. problemDomainCode: '',
  147. surveyTemplateId: this.selectedTemplateId,
  148. answers: answers
  149. }).then(function(res) {
  150. uni.hideLoading()
  151. self.submitting = false
  152. if (res && (res.code === 0 || res.code === 200)) {
  153. uni.showToast({ title: '提交成功', icon: 'success' })
  154. setTimeout(function() {
  155. // 跳回首页展示定向内容
  156. uni.switchTab({ url: '/pages/index-home/index' })
  157. }, 800)
  158. } else {
  159. uni.showToast({ title: res.message || '提交失败', icon: 'none' })
  160. }
  161. }).catch(function() {
  162. uni.hideLoading()
  163. self.submitting = false
  164. uni.showToast({ title: '提交失败,请重试', icon: 'none' })
  165. })
  166. }
  167. }
  168. }
  169. </script>
  170. <style scoped>
  171. .qs-wrap {
  172. min-height: 100vh;
  173. background: #FFF7ED;
  174. padding: 40rpx 32rpx 120rpx;
  175. box-sizing: border-box;
  176. }
  177. .qs-header {
  178. margin-bottom: 32rpx;
  179. }
  180. .qs-title {
  181. display: block;
  182. font-size: 40rpx;
  183. font-weight: bold;
  184. color: #333;
  185. text-align: center;
  186. }
  187. .qs-sub {
  188. display: block;
  189. font-size: 26rpx;
  190. color: #999;
  191. text-align: center;
  192. margin-top: 10rpx;
  193. }
  194. .qs-loading, .qs-empty {
  195. text-align: center;
  196. font-size: 28rpx;
  197. color: #999;
  198. padding: 80rpx 0;
  199. }
  200. .qs-template-row {
  201. display: flex;
  202. align-items: center;
  203. flex-wrap: wrap;
  204. background: #fff;
  205. border-radius: 16rpx;
  206. padding: 16rpx 20rpx;
  207. margin-bottom: 24rpx;
  208. }
  209. .qs-template-label {
  210. font-size: 24rpx;
  211. color: #999;
  212. margin-right: 8rpx;
  213. }
  214. .qs-template-chip {
  215. font-size: 24rpx;
  216. color: #666;
  217. background: #F3F4F6;
  218. border-radius: 24rpx;
  219. padding: 6rpx 20rpx;
  220. margin: 4rpx;
  221. }
  222. .qs-template-chip-active {
  223. background: #F97316;
  224. color: #fff;
  225. }
  226. .qs-q {
  227. margin-bottom: 32rpx;
  228. }
  229. .qs-q-label {
  230. display: block;
  231. font-size: 30rpx;
  232. font-weight: 600;
  233. color: #333;
  234. margin-bottom: 16rpx;
  235. }
  236. .qs-options {
  237. display: flex;
  238. flex-wrap: wrap;
  239. gap: 16rpx;
  240. }
  241. .qs-opt {
  242. display: flex;
  243. align-items: center;
  244. background: #fff;
  245. border: 2rpx solid #E5E7EB;
  246. border-radius: 16rpx;
  247. padding: 18rpx 24rpx;
  248. margin-right: 16rpx;
  249. margin-bottom: 12rpx;
  250. }
  251. .qs-opt-active {
  252. border-color: #F97316;
  253. background: #FFF7ED;
  254. }
  255. .qs-opt-text {
  256. font-size: 28rpx;
  257. color: #333;
  258. }
  259. .qs-opt-check {
  260. color: #F97316;
  261. font-size: 28rpx;
  262. font-weight: bold;
  263. margin-left: 8rpx;
  264. }
  265. .qs-btn {
  266. background: linear-gradient(135deg, #F97316, #FB923C);
  267. border-radius: 48rpx;
  268. padding: 26rpx 0;
  269. text-align: center;
  270. margin-top: 32rpx;
  271. }
  272. .qs-btn:active {
  273. opacity: 0.9;
  274. }
  275. .qs-btn-disabled {
  276. opacity: 0.7;
  277. }
  278. .qs-btn-text {
  279. color: #fff;
  280. font-size: 32rpx;
  281. font-weight: 600;
  282. }
  283. </style>