index.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. <template>
  2. <view class="vote-page">
  3. <view class="round-toggle">
  4. <view :class="['round-tab', round === 1 ? 'active' : '']" @click="switchRound(1)">第1轮 · 我学会了</view>
  5. <view :class="['round-tab', round === 2 ? 'active' : '']" @click="switchRound(2)">第2轮 · 最想抄走</view>
  6. </view>
  7. <view v-if="!voted" class="vote-section">
  8. <text class="vote-hint">{{ round === 1 ? '每人2票,不可投本组' : '每人1票,可投本组' }}</text>
  9. <view class="group-list">
  10. <view class="group-item" v-for="(g, idx) in groups" :key="idx"
  11. :class="{ selected: selectedGroups.includes(g.id), disabled: round === 1 && g.id === myGroupId }"
  12. @click="toggleGroup(g.id)">
  13. <text class="group-name">{{ g.name }}</text>
  14. <text class="group-check" v-if="selectedGroups.includes(g.id)">✅</text>
  15. <text class="group-disabled" v-if="round === 1 && g.id === myGroupId">(本组)</text>
  16. </view>
  17. </view>
  18. <button class="submit-btn" @click="handleVote" :loading="voting"
  19. :disabled="voting || selectedGroups.length === 0">提交投票</button>
  20. </view>
  21. <view v-else class="result-section">
  22. <text class="result-title">投票结果</text>
  23. <view class="bar-list">
  24. <view class="bar-item" v-for="(b, idx) in bars" :key="idx">
  25. <view class="bar-label">{{ b.groupName }}</view>
  26. <view class="bar-track">
  27. <view class="bar-fill" :style="{ width: barWidth(b.count) }"></view>
  28. </view>
  29. <view class="bar-count">{{ b.count }}票</view>
  30. </view>
  31. </view>
  32. <view class="voted-badge">您已在第{{ round }}轮投票</view>
  33. </view>
  34. </view>
  35. </template>
  36. <script>
  37. import { castVote, getVoteResult, getVotedStatus, getMyGroup, getClassGroups } from '@/utils/api.js'
  38. import { guardPage } from '@/utils/guard.js'
  39. export default {
  40. data() {
  41. return {
  42. round: 1,
  43. classId: '',
  44. myGroupId: '',
  45. groups: [],
  46. selectedGroups: [],
  47. voted: false,
  48. bars: [],
  49. voting: false
  50. }
  51. },
  52. onShow() {
  53. if (!guardPage()) return
  54. this.loadGroup()
  55. },
  56. methods: {
  57. loadGroup() {
  58. var self = this
  59. getMyGroup().then(function(resp) {
  60. if (resp.data) {
  61. self.myGroupId = resp.data.id
  62. self.classId = resp.data.classId
  63. return getClassGroups()
  64. }
  65. }).then(function(resp) {
  66. if (resp && resp.data) {
  67. self.groups = resp.data
  68. }
  69. self.checkVoted()
  70. self.loadResult()
  71. }).catch(function() {})
  72. },
  73. switchRound(r) {
  74. this.round = r
  75. this.selectedGroups = []
  76. this.checkVoted()
  77. this.loadResult()
  78. },
  79. checkVoted() {
  80. var self = this
  81. if (!self.classId) return
  82. getVotedStatus({ round: self.round, classId: self.classId }).then(function(resp) {
  83. self.voted = resp.data === true
  84. }).catch(function() {
  85. self.voted = false
  86. })
  87. },
  88. loadResult() {
  89. var self = this
  90. if (!self.classId) return
  91. getVoteResult({ round: self.round, classId: self.classId }).then(function(resp) {
  92. self.bars = (resp.data && resp.data.bars) || []
  93. }).catch(function() {})
  94. },
  95. toggleGroup(id) {
  96. if (this.round === 1 && id === this.myGroupId) return
  97. var idx = this.selectedGroups.indexOf(id)
  98. if (idx >= 0) {
  99. this.selectedGroups.splice(idx, 1)
  100. } else {
  101. var max = this.round === 1 ? 2 : 1
  102. if (this.selectedGroups.length >= max) {
  103. uni.showToast({ title: '第' + this.round + '轮最多' + max + '票', icon: 'none' })
  104. return
  105. }
  106. this.selectedGroups.push(id)
  107. }
  108. },
  109. handleVote() {
  110. if (this.selectedGroups.length === 0 || this.voting) return
  111. this.voting = true
  112. var self = this
  113. castVote({
  114. round: self.round,
  115. classId: self.classId,
  116. targetGroupIds: self.selectedGroups
  117. }).then(function() {
  118. uni.showToast({ title: '投票成功', icon: 'success' })
  119. self.voted = true
  120. self.loadResult()
  121. }).catch(function(err) {
  122. uni.showToast({ title: (err && err.message) || '投票失败', icon: 'none' })
  123. }).finally(function() {
  124. self.voting = false
  125. })
  126. },
  127. barWidth(count) {
  128. var max = 1
  129. this.bars.forEach(function(b) { if (b.count > max) max = b.count })
  130. return Math.round((count / max) * 100) + '%'
  131. }
  132. }
  133. }
  134. </script>
  135. <style scoped>
  136. .vote-page { min-height: 100vh; background: #F5F5F5; padding: 24rpx 32rpx; }
  137. .round-toggle { display: flex; background: #FFF; border-radius: 12rpx; overflow: hidden; margin-bottom: 24rpx; }
  138. .round-tab { flex: 1; height: 80rpx; line-height: 80rpx; text-align: center; font-size: 28rpx; color: #5A4F4B; background: #FFF; }
  139. .round-tab.active { color: #FFF; background: #FC7A57; font-weight: 600; }
  140. .vote-hint { display: block; font-size: 26rpx; color: #FC7A57; margin-bottom: 20rpx; text-align: center; }
  141. .group-list { background: #FFF; border-radius: 12rpx; overflow: hidden; margin-bottom: 24rpx; }
  142. .group-item { display: flex; align-items: center; justify-content: space-between; padding: 24rpx 28rpx; border-bottom: 1rpx solid #F1F5F9; }
  143. .group-item.selected { background: #FFF0E8; }
  144. .group-item.disabled { opacity: 0.4; }
  145. .group-name { font-size: 28rpx; color: #2A2326; }
  146. .group-check { font-size: 28rpx; }
  147. .group-disabled { font-size: 24rpx; color: #94877F; margin-left: 8rpx; }
  148. .submit-btn { width: 100%; height: 88rpx; line-height: 88rpx; background: #FC7A57; color: #FFF; font-size: 30rpx; font-weight: 600; border-radius: 44rpx; border: none; }
  149. .submit-btn:active { opacity: 0.85; }
  150. .result-section { background: #FFF; border-radius: 16rpx; padding: 32rpx; }
  151. .result-title { display: block; font-size: 32rpx; font-weight: 700; color: #2A2326; margin-bottom: 24rpx; }
  152. .bar-list { margin-bottom: 24rpx; }
  153. .bar-item { display: flex; align-items: center; margin-bottom: 16rpx; }
  154. .bar-label { width: 160rpx; font-size: 26rpx; color: #2A2326; text-align: right; margin-right: 16rpx; }
  155. .bar-track { flex: 1; height: 32rpx; background: #F1F5F9; border-radius: 16rpx; overflow: hidden; }
  156. .bar-fill { height: 100%; background: linear-gradient(90deg, #FC7A57, #FF9E5E); border-radius: 16rpx; transition: width 0.5s; }
  157. .bar-count { width: 80rpx; font-size: 24rpx; color: #5A4F4B; text-align: right; margin-left: 12rpx; }
  158. .voted-badge { text-align: center; font-size: 26rpx; color: #22C55E; background: #F0FDF4; padding: 16rpx; border-radius: 8rpx; }
  159. </style>