survey.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <template>
  2. <view class="sv-wrap">
  3. <view class="sv-header">
  4. <text class="sv-title">专项调研</text>
  5. <text class="sv-sub">回答以下问题,我们为你推荐最适合的方案</text>
  6. </view>
  7. <view class="sv-loading" v-if="loading">加载中...</view>
  8. <view class="sv-empty" v-else-if="!questions || questions.length === 0">暂无问卷题目</view>
  9. <view class="sv-form" v-else>
  10. <view class="sv-q" v-for="(q, qi) in questions" :key="getQKey(qi)">
  11. <text class="sv-q-label">{{ qi + 1 }}. {{ q.text }}</text>
  12. <view class="sv-options">
  13. <view
  14. class="sv-opt"
  15. v-for="(o, oi) in q.options"
  16. :key="getOptKey(qi, oi)"
  17. :class="{ 'sv-opt-active': isSelected(qi, oi) }"
  18. @click="toggleOpt(qi, oi, q)">
  19. <text class="sv-opt-text">{{ o.text }}</text>
  20. <text class="sv-opt-check" v-if="isSelected(qi, oi)">✓</text>
  21. </view>
  22. </view>
  23. </view>
  24. <view class="sv-btn" @click="submitSurvey">
  25. <text class="sv-btn-text">提交调研</text>
  26. </view>
  27. </view>
  28. </view>
  29. </template>
  30. <script>
  31. import { submitProblemSurvey } from '../../utils/api.js'
  32. export default {
  33. data() {
  34. return {
  35. code: '',
  36. templateId: '',
  37. questions: [],
  38. answers: [],
  39. loading: false,
  40. submitting: false
  41. }
  42. },
  43. onLoad(options) {
  44. this.code = (options && options.code) || ''
  45. this.templateId = (options && options.templateId) || ''
  46. this.loadQuestions()
  47. },
  48. methods: {
  49. getQKey: function(qi) { return 'q' + qi },
  50. getOptKey: function(qi, oi) { return 'o' + qi + '_' + oi },
  51. isSelected(qi, oi) {
  52. var ans = this.answers[qi]
  53. return ans && ans.indexOf(oi) !== -1
  54. },
  55. toggleOpt(qi, oi, q) {
  56. var isMulti = q.multi !== false
  57. if (!this.answers[qi]) {
  58. this.answers[qi] = []
  59. }
  60. var arr = this.answers[qi]
  61. var idx = arr.indexOf(oi)
  62. if (idx !== -1) {
  63. arr.splice(idx, 1)
  64. } else {
  65. if (isMulti) {
  66. arr.push(oi)
  67. } else {
  68. this.answers[qi] = [oi]
  69. }
  70. }
  71. this.$forceUpdate()
  72. },
  73. loadQuestions() {
  74. var self = this
  75. self.loading = true
  76. uni.showLoading({ title: '加载中...', mask: true })
  77. // 从模板ID加载问卷,或通过问题域获取模板
  78. var api = require('../../utils/api.js')
  79. // 尝试加载模板列表找到匹配的
  80. var getSurveyTemplates = api.getSurveyTemplateList || api.getActiveTemplates
  81. if (getSurveyTemplates && self.templateId) {
  82. getSurveyTemplates().then(function(res) {
  83. uni.hideLoading()
  84. self.loading = false
  85. var list = res && res.data
  86. if (Array.isArray(list)) {
  87. var found = null
  88. for (var i = 0; i < list.length; i++) {
  89. var item = list[i]
  90. if (item.id == self.templateId || item.id === self.templateId) {
  91. found = item
  92. break
  93. }
  94. }
  95. if (found && found.questionsJson) {
  96. var parsed = typeof found.questionsJson === 'string' ? JSON.parse(found.questionsJson) : found.questionsJson
  97. self.questions = Array.isArray(parsed) ? parsed : []
  98. } else {
  99. self.questions = self.getFallbackQuestions()
  100. }
  101. } else {
  102. self.questions = self.getFallbackQuestions()
  103. }
  104. }).catch(function() {
  105. uni.hideLoading()
  106. self.loading = false
  107. self.questions = self.getFallbackQuestions()
  108. })
  109. } else {
  110. uni.hideLoading()
  111. self.loading = false
  112. self.questions = self.getFallbackQuestions()
  113. }
  114. },
  115. getFallbackQuestions() {
  116. return [
  117. { text: '请简单描述你的情况', options: [{ text: '了解中' }, { text: '已有初步了解' }], multi: false },
  118. { text: '你希望获得什么帮助?', options: [{ text: '了解方案' }, { text: '购买产品' }, { text: '咨询专家' }], multi: true }
  119. ]
  120. },
  121. submitSurvey() {
  122. var self = this
  123. // 检查是否至少答了一题
  124. var hasAnswer = false
  125. for (var i = 0; i < this.answers.length; i++) {
  126. if (this.answers[i] && this.answers[i].length > 0) {
  127. hasAnswer = true
  128. break
  129. }
  130. }
  131. if (!hasAnswer) {
  132. uni.showToast({ title: '请至少回答一题', icon: 'none' })
  133. return
  134. }
  135. if (self.submitting) return
  136. self.submitting = true
  137. uni.showLoading({ title: '提交中...', mask: true })
  138. var answerList = []
  139. for (var qi = 0; qi < this.questions.length; qi++) {
  140. var q = this.questions[qi]
  141. var selected = this.answers[qi] || []
  142. if (selected.length > 0) {
  143. var texts = []
  144. for (var si = 0; si < selected.length; si++) {
  145. var opt = q.options[selected[si]]
  146. if (opt) {
  147. texts.push(opt.text)
  148. }
  149. }
  150. answerList.push({
  151. questionText: q.text,
  152. optionTexts: texts
  153. })
  154. }
  155. }
  156. submitProblemSurvey({
  157. problemDomainCode: self.code,
  158. templateId: self.templateId || undefined,
  159. answers: answerList
  160. }).then(function(res) {
  161. uni.hideLoading()
  162. self.submitting = false
  163. uni.showToast({ title: '提交成功', icon: 'success' })
  164. setTimeout(function() {
  165. uni.navigateTo({
  166. url: '/pages/problem/package-match?code=' + self.code
  167. })
  168. }, 500)
  169. }).catch(function() {
  170. uni.hideLoading()
  171. self.submitting = false
  172. uni.showToast({ title: '提交失败,请重试', icon: 'none' })
  173. })
  174. }
  175. }
  176. }
  177. </script>
  178. <style scoped>
  179. .sv-wrap {
  180. min-height: 100vh;
  181. background: #FFF7ED;
  182. padding: 40rpx 32rpx 120rpx;
  183. box-sizing: border-box;
  184. }
  185. .sv-header {
  186. margin-bottom: 32rpx;
  187. }
  188. .sv-title {
  189. display: block;
  190. font-size: 38rpx;
  191. font-weight: bold;
  192. color: #333;
  193. text-align: center;
  194. }
  195. .sv-sub {
  196. display: block;
  197. font-size: 26rpx;
  198. color: #999;
  199. text-align: center;
  200. margin-top: 10rpx;
  201. }
  202. .sv-loading, .sv-empty {
  203. text-align: center;
  204. font-size: 28rpx;
  205. color: #999;
  206. padding: 80rpx 0;
  207. }
  208. .sv-q {
  209. margin-bottom: 36rpx;
  210. }
  211. .sv-q-label {
  212. display: block;
  213. font-size: 30rpx;
  214. font-weight: 600;
  215. color: #333;
  216. margin-bottom: 16rpx;
  217. }
  218. .sv-options {
  219. display: flex;
  220. flex-wrap: wrap;
  221. gap: 16rpx;
  222. }
  223. .sv-opt {
  224. display: flex;
  225. align-items: center;
  226. background: #fff;
  227. border: 2rpx solid #E5E7EB;
  228. border-radius: 16rpx;
  229. padding: 18rpx 24rpx;
  230. margin-right: 16rpx;
  231. margin-bottom: 12rpx;
  232. }
  233. .sv-opt-active {
  234. border-color: #F97316;
  235. background: #FFF7ED;
  236. }
  237. .sv-opt-text {
  238. font-size: 28rpx;
  239. color: #333;
  240. }
  241. .sv-opt-check {
  242. color: #F97316;
  243. font-size: 28rpx;
  244. font-weight: bold;
  245. margin-left: 8rpx;
  246. }
  247. .sv-btn {
  248. background: linear-gradient(135deg, #F97316, #FB923C);
  249. border-radius: 48rpx;
  250. padding: 26rpx 0;
  251. text-align: center;
  252. margin-top: 32rpx;
  253. }
  254. .sv-btn:active {
  255. opacity: 0.9;
  256. }
  257. .sv-btn-text {
  258. color: #fff;
  259. font-size: 32rpx;
  260. font-weight: 600;
  261. }
  262. </style>