survey-questionnaire.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  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="qIdx">
  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="oIdx" @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. formatDate(s) { return s ? s.substring(0, 10) : '' },
  42. selectOption(qIdx, opt) { this.$set(this.answers, qIdx, opt) },
  43. async loadTemplate() {
  44. try {
  45. var res = await getActiveTemplates()
  46. if (res.code === 200 && res.data) {
  47. var found = null
  48. if (this.templateId) {
  49. for (var i = 0; i < res.data.length; i++) {
  50. var t = res.data[i]
  51. if (t && t.id === this.templateId) { found = t; break }
  52. }
  53. }
  54. // dim=all 或无 templateId 匹配(如"再次了解"回跳):取第一个可用的活跃模板
  55. if (!found && res.data.length) found = res.data[0]
  56. if (found) {
  57. this.templateId = found.id
  58. this.templateTitle = found.title || '定期调研'
  59. this.templateDimension = found.dimension || ''
  60. if (found.questionsJson) { try { this.questions = JSON.parse(found.questionsJson) } catch(e) {} }
  61. }
  62. }
  63. } catch(e) { console.error('加载模板失败', e) }
  64. },
  65. async handleSubmit() {
  66. var self = this
  67. var memberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || uni.getStorageSync('userId') || ''
  68. var answersArr = []
  69. for (var i = 0; i < this.questions.length; i++) answersArr.push(this.answers[i] || '')
  70. var answersJson = JSON.stringify(answersArr)
  71. if (!this.templateId || !this.questions.length) { uni.showToast({ title: '问卷未加载,请从调研列表进入', icon: 'none' }); return }
  72. this.isSubmitting = true
  73. try {
  74. var d = { memberId: memberId, templateId: this.templateId, answersJson: answersJson }
  75. if (this.periodStart) d.periodStart = this.periodStart
  76. if (this.periodEnd) d.periodEnd = this.periodEnd
  77. var res = await submitSurvey(d)
  78. if (res.code === 200) { uni.showToast({ title: '提交成功', icon: 'success' }); setTimeout(function(){ uni.navigateTo({ url: '/pages/health/survey-history?memberId=' + (memberId || '') }) }, 1200) }
  79. } catch(e) { uni.showToast({ title: e.message || '提交失败', icon: 'none' }) }
  80. finally { this.isSubmitting = false }
  81. }
  82. }
  83. }
  84. </script>
  85. <style scoped>
  86. .container{background:#FFF7ED;min-height:100vh;padding-bottom:120rpx}
  87. .header-card{background:#fff;margin:20rpx 24rpx;border-radius:20rpx;padding:28rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  88. .header-title{font-size:32rpx;font-weight:bold;color:#333;display:block;margin-bottom:8rpx}
  89. .header-dimension{font-size:24rpx;color:#999;display:block;margin-bottom:4rpx}
  90. .header-period{font-size:24rpx;color:#F97316;display:block}
  91. .question-section{background:#fff;margin:16rpx 24rpx;border-radius:20rpx;padding:24rpx;box-shadow:0 2rpx 12rpx rgba(0,0,0,0.06)}
  92. .question-header{display:flex;align-items:flex-start;margin-bottom:16rpx}
  93. .question-num{font-size:24rpx;font-weight:bold;color:#F97316;margin-right:12rpx;flex-shrink:0}
  94. .question-text{font-size:26rpx;color:#333;flex:1}
  95. .option-item{display:flex;align-items:center;padding:16rpx 0;border-bottom:1rpx solid #f0f0f0}
  96. .option-item:last-child{border-bottom:none}
  97. .option-radio{width:28rpx;height:28rpx;border-radius:50%;border:2rpx solid #ddd;margin-right:16rpx;flex-shrink:0}
  98. .radio-checked{border-color:#F97316;background:#F97316}
  99. .option-text{font-size:26rpx;color:#333}
  100. .option-selected .option-text{color:#F97316;font-weight:bold}
  101. .footer-area{padding:20rpx 24rpx}
  102. .btn-submit{background:#F97316;color:#fff;border:none;border-radius:16rpx;font-size:30rpx;font-weight:bold;height:80rpx;line-height:80rpx}
  103. .btn-submit[disabled]{background:#ffb47a}
  104. </style>