PuzzleSelector.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <template>
  2. <nut-popup v-model:visible="show" position="bottom">
  3. <view class="selector-container">
  4. <view class="selector-header">
  5. <view class="selector-title">选择游戏题目</view>
  6. <nut-button size="small" @click="onClose">取消</nut-button>
  7. </view>
  8. <scroll-view
  9. scroll-y
  10. class="puzzle-scroll"
  11. :style="{ maxHeight: scrollHeight + 'px' }"
  12. >
  13. <view class="puzzle-list">
  14. <view
  15. v-for="puzzle in puzzleOptions"
  16. :key="puzzle.value"
  17. class="puzzle-option"
  18. :class="{ 'disabled': puzzle.disabled, 'selected': selectedPuzzleId === puzzle.value }"
  19. @click="!puzzle.disabled && onSelect(puzzle)"
  20. >
  21. <view class="option-content">
  22. <view class="option-title">{{ puzzle.text }}</view>
  23. <view v-if="puzzle.description" class="option-desc">{{ puzzle.description }}</view>
  24. </view>
  25. <IconFont v-if="selectedPuzzleId === puzzle.value" name="check" color="#3C92FB" size="16"></IconFont>
  26. </view>
  27. </view>
  28. </scroll-view>
  29. </view>
  30. </nut-popup>
  31. </template>
  32. <script lang="ts">
  33. import { defineComponent, computed, PropType } from 'vue'
  34. import { IconFont } from '@nutui/icons-vue-taro'
  35. import { CascaderOption } from '@/types/cascader'
  36. export default defineComponent({
  37. name: 'PuzzleSelector',
  38. components: {
  39. IconFont
  40. },
  41. props: {
  42. visible: {
  43. type: Boolean,
  44. default: false
  45. },
  46. puzzleOptions: {
  47. type: Array as PropType<CascaderOption[]>,
  48. default: () => []
  49. },
  50. selectedPuzzleId: {
  51. type: String,
  52. default: ''
  53. },
  54. scrollHeight: {
  55. type: Number,
  56. default: 300
  57. }
  58. },
  59. emits: ['update:visible', 'select-puzzle'],
  60. setup(props, { emit }) {
  61. const show = computed({
  62. get: () => props.visible,
  63. set: (val) => emit('update:visible', val)
  64. })
  65. const onClose = () => {
  66. show.value = false
  67. }
  68. const onSelect = (puzzle) => {
  69. emit('select-puzzle', puzzle)
  70. }
  71. return {
  72. show,
  73. onClose,
  74. onSelect
  75. }
  76. }
  77. })
  78. </script>
  79. <style lang="scss">
  80. .selector-container {
  81. padding: $spacing-base;
  82. .selector-header {
  83. display: flex;
  84. justify-content: space-between;
  85. align-items: center;
  86. padding: $spacing-base 0;
  87. margin-bottom: $spacing-base;
  88. border-bottom: 1px solid $border-color-light;
  89. .selector-title {
  90. font-size: $font-size-medium;
  91. font-weight: $font-weight-medium;
  92. color: $text-color-primary;
  93. }
  94. }
  95. .puzzle-scroll {
  96. border: 1px solid $border-color-light;
  97. border-radius: $border-radius-small;
  98. margin-top: $spacing-base;
  99. background-color: $background-color-light;
  100. }
  101. .puzzle-list {
  102. padding: $spacing-small;
  103. .puzzle-option {
  104. background-color: $background-color-base;
  105. padding: $spacing-base;
  106. border-radius: $border-radius-mini;
  107. margin-bottom: $spacing-small;
  108. box-shadow: $shadow-light;
  109. display: flex;
  110. justify-content: space-between;
  111. align-items: center;
  112. &:last-child {
  113. margin-bottom: 0;
  114. }
  115. &.disabled {
  116. opacity: 0.5;
  117. }
  118. &.selected {
  119. background-color: rgba(60, 146, 251, 0.1);
  120. }
  121. .option-content {
  122. flex: 1;
  123. .option-title {
  124. font-size: $font-size-base;
  125. color: $text-color-primary;
  126. margin-bottom: $spacing-mini;
  127. }
  128. .option-desc {
  129. font-size: $font-size-small;
  130. color: $text-color-secondary;
  131. }
  132. }
  133. }
  134. }
  135. }
  136. </style>