CurriculumSelector.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. <template>
  2. <div class="curriculum-selector">
  3. <!-- 教材 -->
  4. <div class="select-wrapper">
  5. <select
  6. :value="selectedTextbook"
  7. @change="$emit('update:selectedTextbook', ($event.target as HTMLSelectElement).value)"
  8. >
  9. <option v-for="tb in textbooks" :key="tb.id" :value="tb.id">{{ tb.name }}</option>
  10. </select>
  11. <svg class="select-arrow" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
  12. </div>
  13. <!-- 年级 -->
  14. <div class="select-wrapper">
  15. <select
  16. :value="selectedGrade"
  17. @change="$emit('update:selectedGrade', ($event.target as HTMLSelectElement).value)"
  18. >
  19. <option v-for="g in grades" :key="g.id" :value="g.id">{{ g.name }}{{ g.semester }}</option>
  20. </select>
  21. <svg class="select-arrow" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
  22. </div>
  23. <!-- 单元 -->
  24. <div class="select-wrapper">
  25. <select
  26. :value="selectedUnit"
  27. @change="$emit('update:selectedUnit', ($event.target as HTMLSelectElement).value)"
  28. >
  29. <option v-for="u in units" :key="u.id" :value="u.id">{{ u.number }}</option>
  30. </select>
  31. <svg class="select-arrow" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
  32. </div>
  33. </div>
  34. </template>
  35. <script lang="ts" setup>
  36. import { computed } from 'vue'
  37. import curriculumData from '../data/curriculum.json'
  38. import type { CurriculumData } from '@/types/englishSpeaking'
  39. const props = defineProps<{
  40. selectedTextbook: string
  41. selectedGrade: string
  42. selectedUnit: string
  43. }>()
  44. defineEmits<{
  45. (e: 'update:selectedTextbook', value: string): void
  46. (e: 'update:selectedGrade', value: string): void
  47. (e: 'update:selectedUnit', value: string): void
  48. }>()
  49. const data = curriculumData as CurriculumData
  50. const textbooks = computed(() => data.textbooks)
  51. const grades = computed(() => {
  52. const tb = data.textbooks.find(t => t.id === props.selectedTextbook)
  53. return tb?.grades || []
  54. })
  55. const units = computed(() => {
  56. const tb = data.textbooks.find(t => t.id === props.selectedTextbook)
  57. const grade = tb?.grades.find(g => g.id === props.selectedGrade)
  58. return grade?.units || []
  59. })
  60. </script>
  61. <style lang="scss" scoped>
  62. .curriculum-selector {
  63. display: flex;
  64. align-items: center;
  65. gap: 10px;
  66. }
  67. .select-wrapper {
  68. position: relative;
  69. flex: 1;
  70. select {
  71. width: 100%;
  72. appearance: none;
  73. background: rgba(249, 250, 251, 0.8);
  74. border: 1px solid rgba(229, 231, 235, 0.8);
  75. border-radius: 12px;
  76. padding: 8px 28px 8px 12px;
  77. font-size: 13px;
  78. color: #1f2937;
  79. cursor: pointer;
  80. transition: all 0.2s;
  81. &:hover {
  82. background: #fff;
  83. border-color: #d1d5db;
  84. }
  85. &:focus {
  86. outline: none;
  87. border-color: #fb923c;
  88. box-shadow: 0 0 0 2px rgba(251, 146, 60, 0.1);
  89. background: #fff;
  90. }
  91. }
  92. }
  93. .select-arrow {
  94. position: absolute;
  95. right: 10px;
  96. top: 50%;
  97. transform: translateY(-50%);
  98. color: #9ca3af;
  99. pointer-events: none;
  100. }
  101. </style>