Просмотр исходного кода

feat(speaking): add SortMenu.vue

jimmylee 2 месяцев назад
Родитель
Сommit
423c2e6502
1 измененных файлов с 99 добавлено и 0 удалено
  1. 99 0
      src/views/Student/components/SpeakingClassPanel/SortMenu.vue

+ 99 - 0
src/views/Student/components/SpeakingClassPanel/SortMenu.vue

@@ -0,0 +1,99 @@
+<template>
+  <div class="sort-menu">
+    <button
+      class="sort-btn"
+      :class="{ 'active': modelValue !== 'time-asc' }"
+      @click.stop="open = !open"
+      :title="lang.ssSpkSortTimeAsc"
+    >
+      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
+           stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+        <path d="M3 6h18M3 12h12M3 18h6" />
+      </svg>
+    </button>
+    <template v-if="open">
+      <div class="sort-overlay" @click="open = false" />
+      <div class="sort-dropdown">
+        <button v-for="opt in options" :key="opt.key"
+                class="sort-option"
+                :class="{ 'active': modelValue === opt.key }"
+                @click="onSelect(opt.key)">
+          {{ opt.label }}
+        </button>
+      </div>
+    </template>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, computed } from 'vue'
+import { lang } from '@/main'
+
+export type SortKey = 'time-asc' | 'time-desc' | 'name-asc'
+
+const props = defineProps<{ modelValue: SortKey }>()
+const emit = defineEmits<{ 'update:modelValue': [val: SortKey] }>()
+
+const open = ref(false)
+
+const options = computed(() => [
+  { key: 'time-asc'  as SortKey, label: (lang as any).ssSpkSortTimeAsc },
+  { key: 'time-desc' as SortKey, label: (lang as any).ssSpkSortTimeDesc },
+  { key: 'name-asc'  as SortKey, label: (lang as any).ssSpkSortNameAsc },
+])
+
+function onSelect(key: SortKey) {
+  emit('update:modelValue', key)
+  open.value = false
+}
+</script>
+
+<style lang="scss" scoped>
+.sort-menu { position: relative; }
+
+.sort-btn {
+  width: 28px; height: 28px;
+  display: flex; align-items: center; justify-content: center;
+  border-radius: 6px;
+  background: transparent;
+  border: none;
+  color: #9ca3af;
+  cursor: pointer;
+  transition: background .15s ease;
+  &:hover { background: #f3f4f6; }
+  &.active {
+    color: #ca8a04;
+    background: #fef3c7;
+  }
+}
+
+.sort-overlay {
+  position: fixed; inset: 0;
+  z-index: 30;
+}
+
+.sort-dropdown {
+  position: absolute;
+  right: 0; top: 32px;
+  z-index: 40;
+  background: #fff;
+  border: 1px solid #e5e7eb;
+  border-radius: 8px;
+  box-shadow: 0 4px 12px rgba(0,0,0,.08);
+  padding: 4px 0;
+  min-width: 128px;
+}
+
+.sort-option {
+  width: 100%;
+  text-align: left;
+  padding: 6px 12px;
+  font-size: 12px;
+  background: transparent;
+  border: none;
+  color: #4b5563;
+  cursor: pointer;
+  &:hover { background: #f9fafb; }
+  &.active { color: #ca8a04; background: #fef3c7; font-weight: 500; }
+}
+</style>