survey-questionnaire.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. <template>
  2. <view class="container">
  3. <view class="header-card">
  4. <text class="header-title">{{ templateTitle }}</text>
  5. <text class="header-dimension" v-if="templateDimension">适用维度: {{ templateDimension }}</text>
  6. <text class="header-period" v-if="periodStart || periodEnd">周期: {{ periodStart && formatDate(periodStart) }} - {{ periodEnd && formatDate(periodEnd) }}</text>
  7. </view>
  8. <view class="question-section" v-for="(q, qIdx) in questions" :key="getQuestionKey(q)">
  9. <view class="question-header">
  10. <text class="question-num">Q{{ qIdx + 1 }}</text>
  11. <text class="question-text">{{ q.text }}</text>
  12. </view>
  13. <view class="options-list">
  14. <view class="option-item" :class="answers[qIdx] === opt ? 'option-selected' : ''" v-for="(opt, oIdx) in q.options" :key="getOptionKey(q, opt)" @click="selectOption(qIdx, opt)">
  15. <view class="option-radio" :class="answers[qIdx] === opt ? 'radio-checked' : ''"></view>
  16. <text class="option-text">{{ opt }}</text>
  17. </view>
  18. </view>
  19. </view>
  20. <view class="footer-area">
  21. <button class="btn-submit" :disabled="isSubmitting" @click="handleSubmit">{{ isSubmitting ? '提交中...' : '提交答案' }}</button>
  22. </view>
  23. </view>
  24. </template>
  25. <script>
  26. import { getActiveTemplates, submitSurvey } from '../../utils/api.js'
  27. export default {
  28. data() { return { templateId: null, templateTitle: '定期调研', templateDimension: '', questions: [], answers: {}, periodStart: '', periodEnd: '', isSubmitting: false } },
  29. onLoad(options) {
  30. if (options.templateId) this.templateId = parseInt(options.templateId)
  31. if (options.templateTitle) this.templateTitle = decodeURIComponent(options.templateTitle)
  32. if (options.templateDimension) this.templateDimension = options.templateDimension
  33. if (options.periodStart) this.periodStart = options.periodStart
  34. if (options.periodEnd) this.periodEnd = options.periodEnd
  35. if (options.questions) { try { this.questions = JSON.parse(decodeURIComponent(options.questions)) } catch(e) { this.questions = [] } }
  36. // 有 templateId 或带 dim 参数(如首页"再次了解"回跳 dim=all)且无内联题目时,加载模板
  37. if (this.questions.length) return
  38. if (this.templateId || options.dim) this.loadTemplate()
  39. },
  40. methods: {
  41. getQuestionKey(q) { return q && q.text ? q.text : 'q-' + Math.random() },
  42. getOptionKey(q, opt) { return q && q.text ? q.text + '-' + opt : 'opt-' + Math.random() },
  43. formatDate(s) { return s ? s.substring(0, 10) : '' },
  44. selectOption(qIdx, opt) { this.$set(this.answers, qIdx, opt) },
  45. async loadTemplate() {
  46. try {
  47. var res = await getActiveTemplates()
  48. if (res.code === 200 && res.data) {
  49. var found = null
  50. if (this.templateId) {
  51. for (var i = 0; i < res.data.length; i++) {
  52. var t = res.data[i]
  53. if (t && t.id === this.templateId) { found = t; break }
  54. }
  55. }
  56. // dim=all 或无 templateId 匹配(如"再次了解"回跳):取第一个可用的活跃模板
  57. if (!found && res.data.length) found = res.data[0]
  58. if (found) {
  59. this.templateId = found.id
  60. this.templateTitle = found.title || '定期调研'
  61. this.templateDimension = found.dimension || ''
  62. if (found.questionsJson) { try { this.questions = JSON.parse(found.questionsJson) } catch(e) {} }
  63. }
  64. }
  65. } catch(e) { console.error('加载模板失败', e) }
  66. },
  67. async handleSubmit() {
  68. var self = this
  69. var memberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || uni.getStorageSync('userId') || ''
  70. var answersArr = []
  71. for (var i = 0; i < this.questions.length; i++) answersArr.push(this.answers[i] || '')
  72. var answersJson = JSON.stringify(answersArr)
  73. if (!this.templateId || !this.questions.length) { uni.showToast({ title: '问卷未加载,请从调研列表进入', icon: 'none' }); return }
  74. this.isSubmitting = true
  75. try {
  76. var d = { memberId: memberId, templateId: this.templateId, answersJson: answersJson }
  77. if (this.periodStart) d.periodStart = this.periodStart
  78. if (this.periodEnd) d.periodEnd = this.periodEnd
  79. var res = await submitSurvey(d)
  80. if (res.code === 200) { uni.showToast({ title: '提交成功', icon: 'success' }); setTimeout(function(){ uni.navigateTo({ url: '/pages/health/survey-history?memberId=' + (memberId || '') }) }, 1200) }
  81. } catch(e) { uni.showToast({ title: e.message || '提交失败', icon: 'none' }) }
  82. finally { this.isSubmitting = false }
  83. }
  84. }
  85. }
  86. </script>
  87. <style scoped>
  88. .container{background:#FFF7ED;min-height:100vh;padding-bottom:120rpx}
  89. .header-card{background:#fff;margin:20rpx 24rpx;border-radius:20rpx;padding:28rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  90. .header-title{font-size:32rpx;font-weight:bold;color:#333;display:block;margin-bottom:8rpx}
  91. .header-dimension{font-size:24rpx;color:#999;display:block;margin-bottom:4rpx}
  92. .header-period{font-size:24rpx;color:#F97316;display:block}
  93. .question-section{background:#fff;margin:16rpx 24rpx;border-radius:20rpx;padding:24rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  94. .question-header{display:flex;align-items:flex-start;margin-bottom:16rpx}
  95. .question-num{font-size:24rpx;font-weight:bold;color:#F97316;margin-right:12rpx;flex-shrink:0}
  96. .question-text{font-size:26rpx;color:#333;flex:1}
  97. .option-item{display:flex;align-items:center;padding:16rpx 0;border-bottom:1rpx solid #f0f0f0}
  98. .option-item:last-child{border-bottom:none}
  99. .option-radio{width:28rpx;height:28rpx;border-radius:50%;border:2rpx solid #ddd;margin-right:16rpx;flex-shrink:0}
  100. .radio-checked{border-color:#F97316;background:#F97316}
  101. .option-text{font-size:26rpx;color:#333}
  102. .option-selected .option-text{color:#F97316;font-weight:bold}
  103. .footer-area{padding:20rpx 24rpx}
  104. .btn-submit{background:#F97316;color:#fff;border:none;border-radius:16rpx;font-size:30rpx;font-weight:bold;height:80rpx;line-height:80rpx}
  105. .btn-submit[disabled]{background:#ffb47a}
  106. </style>