questionnaireFill.vue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. <template>
  2. <view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
  3. <cus-header title='问卷填写' :backAlert="true"></cus-header>
  4. <div class="top adffcacjc">
  5. <p>{{title}}</p>
  6. <p class="tip">共 <span>{{list.length}}</span> 题,已作答 <span style="font-weight: bold;">{{answerNum}}</span> 题,请耐心选择!</p>
  7. </div>
  8. <div class="list" :style="{'height':(h-180-mt)+'px'}">
  9. <div v-if="isLoading" class="loading-container adfacjc">
  10. <div class="adfac">
  11. <u-loading-icon size="42"></u-loading-icon>
  12. <text style="margin-left: 10rpx;font-size: 34rpx;color: #666666;">问卷加载中...</text>
  13. </div>
  14. </div>
  15. <template v-else>
  16. <div class="l_item" v-for="(item,index) in list" :key="item.id">
  17. <QuestionItem :item="item" :index="index" @change="handleAnswerChange"></QuestionItem>
  18. </div>
  19. </template>
  20. </div>
  21. <div class="bottom">
  22. <view class="zt_btn" @tap="submitWj">{{isSubmitting ? '提交中...' : '提交问卷'}}</view>
  23. </div>
  24. </view>
  25. </template>
  26. <script>
  27. import QuestionItem from '@/components/QuestionItem/index.vue'
  28. export default {
  29. components:{QuestionItem},
  30. data(){
  31. return {
  32. title:'',
  33. teamQuestionnaireId:'',
  34. list:[],
  35. questionnaire:null,
  36. isLoading: true,
  37. isSubmitting: false
  38. }
  39. },
  40. onLoad(option) {
  41. this.title = option.title;
  42. this.teamQuestionnaireId = option.teamQuestionnaireId;
  43. this.getList();
  44. },
  45. computed: {
  46. answerNum() {
  47. return this.list.filter(l => !!l.answer).length || 0;
  48. }
  49. },
  50. methods:{
  51. handleAnswerChange(e) {
  52. const item = this.list[e.index];
  53. if (item) {
  54. this.$set(item, 'answer', e.value);
  55. if (item.warn) {
  56. this.$set(item, 'warn', false);
  57. }
  58. }
  59. this.setQuestionnaireCache();
  60. },
  61. getList(){
  62. let questionnaire = null;
  63. try {
  64. const cacheStr = uni.getStorageSync('questionnaire');
  65. if (cacheStr) {
  66. questionnaire = JSON.parse(cacheStr);
  67. }
  68. } catch (e) {
  69. console.error("解析问卷缓存失败:", e);
  70. uni.removeStorageSync('questionnaire'); // 缓存有问题,直接清除
  71. }
  72. this.isLoading = true;
  73. this.$api.get('/core/team/member/answer/listByUser/' + this.teamQuestionnaireId).then(res => {
  74. if (res.data.code !== 0) return this.$showToast(res.data.msg);
  75. const answerMap = new Map();
  76. if (questionnaire && this.teamQuestionnaireId == questionnaire.key) {
  77. questionnaire.list.forEach(q => answerMap.set(q.id, q.answer));
  78. }
  79. this.list = res.data.data.map(item => ({
  80. ...item,
  81. warn: false,
  82. answer: answerMap.get(item.id) || '' // 直接从 Map 中获取答案
  83. }));
  84. }).catch(()=>{
  85. this.isLoading = false;
  86. this.$showToast('网络异常,请稍后重试');
  87. }).finally(() => {
  88. this.isLoading = false;
  89. });
  90. },
  91. submitWj() {
  92. if (this.isSubmitting) return;
  93. let firstUnansweredIndex = -1;
  94. this.list.forEach((l, i) => {
  95. const isAnswered = !!l.answer;
  96. this.$set(l, 'warn', !isAnswered);
  97. if (!isAnswered && firstUnansweredIndex === -1) {
  98. firstUnansweredIndex = i;
  99. }
  100. });
  101. if (firstUnansweredIndex > -1) {
  102. return uni.showModal({
  103. title: '提示',
  104. content: `第 ${firstUnansweredIndex + 1} 项未选择答案,请选择。`,
  105. showCancel: false
  106. });
  107. }
  108. const submitList = this.list.map(question => {
  109. const formattedUserAnswer = question.userAnswer.map(option => ({
  110. ...option,
  111. isSelected: option.questionOption === question.answer
  112. }));
  113. const { answer, warn, ...restOfQuestion } = question;
  114. return {
  115. ...restOfQuestion,
  116. isAnswer: '1',
  117. userAnswer: formattedUserAnswer
  118. };
  119. });
  120. this.isSubmitting = true;
  121. this.$api.post('/core/team/member/answer/submit', submitList).then(res => {
  122. if (res.data.code !== 0) {
  123. this.isSubmitting = false;
  124. return this.$showToast(res.data.msg);
  125. }
  126. uni.removeStorageSync('questionnaire');
  127. uni.redirectTo({
  128. url: '/pages/questionnaireResult'
  129. });
  130. }).catch(err => {
  131. this.$showToast('网络异常,请稍后重试');
  132. })
  133. .finally(() => {
  134. this.isSubmitting = false;
  135. });
  136. },
  137. setQuestionnaireCache(){
  138. const answeredList = this.list
  139. .filter(l => l.answer)
  140. .map(l => ({ id: l.id, answer: l.answer }));
  141. if (answeredList.length === 0) {
  142. uni.removeStorageSync('questionnaire');
  143. return;
  144. }
  145. const qinfo = {
  146. key: this.teamQuestionnaireId,
  147. list: answeredList
  148. };
  149. uni.setStorageSync('questionnaire', JSON.stringify(qinfo));
  150. }
  151. },
  152. // onUnload() {
  153. // this.setQuestionnaireCache();
  154. // },
  155. // onBackPress() {
  156. // this.setQuestionnaireCache();
  157. // return false;
  158. // }
  159. }
  160. </script>
  161. <style scoped lang="less">
  162. .loading-container {
  163. width: 100%;
  164. height: 100%;
  165. }
  166. .page{
  167. background: #F7F2F6;
  168. box-sizing: border-box;
  169. .top{
  170. p{
  171. font-family: PingFang-SC, PingFang-SC;
  172. font-weight: bold;
  173. font-size: 42rpx;
  174. color: #252525;
  175. line-height: 51rpx;
  176. text-align: center;
  177. margin-top: 48rpx;
  178. }
  179. .tip{
  180. font-family: PingFangSC, PingFang SC;
  181. font-weight: 400;
  182. font-size: 26rpx;
  183. color: #646464;
  184. line-height: 26rpx;
  185. text-align: center;
  186. margin-top: 36rpx;
  187. span{
  188. margin: 0 10rpx;
  189. }
  190. }
  191. }
  192. .list{
  193. width: 100%;
  194. overflow-y: auto;
  195. margin-top: 28rpx;
  196. .l_item{
  197. margin-top: 20rpx;
  198. width: 100%;
  199. background: #FFFFFF;
  200. padding: 6rpx;
  201. box-sizing: border-box;
  202. }
  203. }
  204. .bottom{
  205. width: calc(100% - 80rpx);
  206. height: 88rpx;
  207. position: fixed;
  208. left: 40rpx;
  209. bottom: 40rpx;
  210. }
  211. }
  212. </style>