manual-score-entry.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. <template>
  2. <view class="mse-page">
  3. <view class="mse-loading" v-if="loading">
  4. <text class="mse-loading-text">加载成员...</text>
  5. </view>
  6. <template v-else>
  7. <view class="mse-hero">
  8. <text class="mse-hero-title">五维直接填分</text>
  9. <text class="mse-hero-desc">跳过问卷,直接填写你已有的 0-100 分</text>
  10. </view>
  11. <view class="mse-section">
  12. <text class="mse-section-title">选择成员</text>
  13. <view class="mse-member-list">
  14. <view class="mse-member-item" :class="{ active: selectedMemberId === m.id }" v-for="m in members" :key="m.id" @click="selectMember(m)">
  15. <image class="mse-member-avatar" :src="m.avatar || '/static/avatar-default.png'" mode="aspectFill"></image>
  16. <view class="mse-member-info">
  17. <text class="mse-member-name">{{ m.nickname }}</text>
  18. <text class="mse-member-role" :class="'role-' + m.effectiveRole">{{ m.roleLabel }}</text>
  19. </view>
  20. <view class="mse-member-check" v-if="selectedMemberId === m.id">✓</view>
  21. </view>
  22. </view>
  23. </view>
  24. <view class="mse-section">
  25. <text class="mse-section-title">五维分值(0-100)</text>
  26. <view class="mse-slider-list">
  27. <view class="mse-slider-item" v-for="dim in dimensions" :key="dim.code">
  28. <view class="mse-slider-head" :style="{ color: dim.color }">
  29. <view class="mse-slider-dot" :style="{ background: dim.color }"></view>
  30. <text class="mse-slider-label">{{ dim.label }}</text>
  31. <text class="mse-slider-value">{{ scores[dim.code] }}</text>
  32. </view>
  33. <slider class="mse-slider" :min="0" :max="100" :step="1" :value="scores[dim.code]" @change="onSliderChange(dim.code, $event)" :activeColor="dim.color"></slider>
  34. </view>
  35. </view>
  36. </view>
  37. <view class="mse-footer">
  38. <button class="mse-btn mse-btn-primary" :disabled="submitLoading" @click="submit">{{ submitLoading ? '提交中...' : '提交并查看结果' }}</button>
  39. </view>
  40. </template>
  41. </view>
  42. </template>
  43. <script>
  44. import { getFamilyMemberList, submitManualScore } from '@/utils/api'
  45. export default {
  46. data() {
  47. return {
  48. loading: true,
  49. members: [],
  50. selectedMemberId: null,
  51. scores: { body: 50, mind: 50, wisdom: 50, action: 50, wealth: 50 },
  52. dimensions: [
  53. { code: 'body', label: '身', color: '#FF8C42' },
  54. { code: 'mind', label: '心', color: '#FF6B9D' },
  55. { code: 'wisdom', label: '智', color: '#6366F1' },
  56. { code: 'action', label: '行', color: '#10B981' },
  57. { code: 'wealth', label: '富', color: '#F59E0B' }
  58. ],
  59. submitLoading: false
  60. }
  61. },
  62. onLoad() {
  63. this.loadMembers()
  64. },
  65. methods: {
  66. loadMembers() {
  67. var self = this
  68. getFamilyMemberList({ visibleOnly: true }).then(function(res) {
  69. if (res.code === 200 && res.data && res.data.length > 0) {
  70. self.members = res.data
  71. var own = null
  72. for (var i = 0; i < self.members.length; i++) {
  73. if (self.members[i].isSelf) { own = self.members[i]; break }
  74. }
  75. self.selectedMemberId = own ? own.id : (self.members.length > 0 ? self.members[0].id : null)
  76. }
  77. self.loading = false
  78. }).catch(function() { self.loading = false })
  79. },
  80. selectMember(m) { this.selectedMemberId = m.id },
  81. onSliderChange(code, e) { this.scores[code] = e.detail.value },
  82. submit() {
  83. if (!this.selectedMemberId) return
  84. this.submitLoading = true
  85. var self = this
  86. submitManualScore({ memberId: this.selectedMemberId, scores: this.scores }).then(function(res) {
  87. self.submitLoading = false
  88. if (res.code === 200) {
  89. uni.navigateTo({ url: '/pages/family/self-check-result?source=manual' })
  90. } else {
  91. uni.showToast({ title: res.message || '提交失败', icon: 'none' })
  92. }
  93. }).catch(function() {
  94. self.submitLoading = false
  95. uni.showToast({ title: '网络异常', icon: 'none' })
  96. })
  97. }
  98. }
  99. }
  100. </script>
  101. <style scoped>
  102. .mse-page { min-height: 100vh; background: #F5FAFE; padding: 24rpx 30rpx 120rpx; box-sizing: border-box }
  103. .mse-loading { display: flex; justify-content: center; align-items: center; height: 50vh }
  104. .mse-loading-text { font-size: 28rpx; color: #999 }
  105. .mse-hero { text-align: center; padding: 40rpx 0 20rpx }
  106. .mse-hero-title { font-size: 36rpx; font-weight: 600; color: #333; display: block; margin-bottom: 8rpx }
  107. .mse-hero-desc { font-size: 24rpx; color: #999; display: block }
  108. .mse-section { background: #fff; border-radius: 24rpx; padding: 24rpx; margin-bottom: 24rpx; box-shadow: 0 2rpx 12rpx rgba(0,0,0,0.05) }
  109. .mse-section-title { font-size: 28rpx; font-weight: 600; color: #333; margin-bottom: 20rpx; display: block }
  110. .mse-member-list { display: flex; overflow-x: auto; gap: 20rpx; padding-bottom: 10rpx }
  111. .mse-member-item { flex: 0 0 140rpx; display: flex; flex-direction: column; align-items: center; border: 4rpx solid transparent; border-radius: 20rpx; padding: 16rpx 10rpx; transition: border-color .2s }
  112. .mse-member-item.active { border-color: #F97316 }
  113. .mse-member-avatar { width: 80rpx; height: 80rpx; border-radius: 50%; margin-bottom: 12rpx }
  114. .mse-member-name { font-size: 26rpx; font-weight: 500; color: #333; text-align: center; width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
  115. .mse-member-role { font-size: 20rpx; padding: 2rpx 10rpx; border-radius: 10rpx; display: inline-block }
  116. .role-parent { background: #FFF7ED; color: #E65100 }
  117. .role-child { background: #E8F5E9; color: #2E7D32 }
  118. .role-teacher { background: #E0E7FF; color: #3730A3 }
  119. .mse-member-check { margin-top: 8rpx; font-size: 24rpx; color: #F97316 }
  120. .mse-slider-item { margin-bottom: 28rpx }
  121. .mse-slider-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8rpx }
  122. .mse-slider-dot { width: 16rpx; height: 16rpx; border-radius: 50%; margin-right: 10rpx }
  123. .mse-slider-label { font-size: 28rpx; font-weight: 500; flex: 1 }
  124. .mse-slider-value { font-size: 28rpx; font-weight: 700; color: #333 }
  125. .mse-slider { width: 100% }
  126. .mse-footer { position: fixed; bottom: 0; left: 0; right: 0; padding: 20rpx 30rpx; background: #fff; border-top: 1rpx solid #F0F0F0; box-sizing: border-box }
  127. .mse-btn { width: 100%; height: 88rpx; line-height: 88rpx; font-size: 30rpx; border-radius: 44rpx; border: none; text-align: center }
  128. .mse-btn-primary { background: linear-gradient(135deg, #F97316, #FF8C42); color: #fff; font-weight: 600 }
  129. </style>