PeriodBanner.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. <template>
  2. <div class="period-banner" :class="'banner-' + active.status">
  3. <div class="pb-left">
  4. <span class="pb-icon">{{ statusIcon }}</span>
  5. <div class="pb-info">
  6. <div class="pb-name">{{ active.name }}</div>
  7. <div class="pb-date">{{ active.startDate }} ~ {{ active.endDate }}</div>
  8. </div>
  9. <el-tag :type="statusTagType" effect="dark" size="small" round>{{ statusLabel }}</el-tag>
  10. </div>
  11. <div class="pb-right" v-if="switchablePeriods.length > 1">
  12. <el-dropdown trigger="click" @command="handleSwitch">
  13. <span class="pb-switch-btn">
  14. 切换周期 <el-icon><ArrowDown /></el-icon>
  15. </span>
  16. <template #dropdown>
  17. <el-dropdown-menu>
  18. <el-dropdown-item
  19. v-for="p in switchablePeriods"
  20. :key="p.id"
  21. :command="p.id"
  22. :class="{ 'is-active': p.id === active.id }"
  23. >
  24. <span class="pbd-name">{{ p.name }}</span>
  25. <el-tag :type="statusTag(p.status)" size="small" effect="plain" round>{{ statusText(p.status) }}</el-tag>
  26. </el-dropdown-item>
  27. </el-dropdown-menu>
  28. </template>
  29. </el-dropdown>
  30. </div>
  31. </div>
  32. </template>
  33. <script setup>
  34. import { computed } from 'vue'
  35. const props = defineProps({
  36. periods: { type: Array, required: true },
  37. modelValue: { type: Number, default: null }
  38. })
  39. const emit = defineEmits(['update:modelValue'])
  40. // Only show non-archived, non-future-draft periods (active = DRAFT/OKR_ALIGN/EXECUTING/ASSESSING)
  41. const activePeriods = computed(() => {
  42. return (props.periods || []).filter(p => p.status !== 'ARCHIVED')
  43. })
  44. const active = computed(() => {
  45. const found = activePeriods.value.find(p => p.id === props.modelValue)
  46. return found || activePeriods.value[0] || {}
  47. })
  48. const switchablePeriods = computed(() => activePeriods.value)
  49. const statusIcon = computed(() => {
  50. return { DRAFT: '', OKR_ALIGN: '', EXECUTING: '', ASSESSING: '' }[active.value.status] || ''
  51. })
  52. const statusLabel = computed(() => {
  53. return { DRAFT: '待发布', OKR_ALIGN: '对齐中', EXECUTING: '执行中', ASSESSING: '考评中' }[active.value.status] || ''
  54. })
  55. const statusTagType = computed(() => {
  56. return { ASSESSING: 'danger', EXECUTING: 'success', OKR_ALIGN: 'warning', DRAFT: 'info' }[active.value.status] || 'info'
  57. })
  58. function statusText(s) {
  59. return { DRAFT: '待发布', OKR_ALIGN: '对齐中', EXECUTING: '执行中', ASSESSING: '考评中' }[s] || s
  60. }
  61. function statusTag(s) {
  62. return { ASSESSING: 'danger', EXECUTING: 'success', OKR_ALIGN: 'warning', DRAFT: 'info' }[s] || 'info'
  63. }
  64. function handleSwitch(id) {
  65. emit('update:modelValue', id)
  66. }
  67. </script>
  68. <style scoped>
  69. .period-banner {
  70. display: flex; align-items: center; justify-content: space-between;
  71. padding: 14px 20px; border-radius: 10px; margin-bottom: 20px;
  72. box-shadow: 0 1px 4px rgba(0,0,0,0.04);
  73. }
  74. .banner-DRAFT { background: #f5f5f5; border: 1px solid #e0e0e0; }
  75. .banner-OKR_ALIGN { background: #e0f7fa; border: 1px solid #b2ebf2; }
  76. .banner-EXECUTING { background: #e8f5e9; border: 1px solid #c8e6c9; }
  77. .banner-ASSESSING { background: #fce4ec; border: 1px solid #f8bbd0; }
  78. .pb-left { display: flex; align-items: center; gap: 12px; }
  79. .pb-icon { font-size: 24px; }
  80. .pb-name { font-size: 16px; font-weight: 700; color: #1a1a2e; }
  81. .pb-date { font-size: 12px; color: #888; margin-top: 2px; }
  82. .pb-switch-btn {
  83. display: inline-flex; align-items: center; gap: 4px;
  84. padding: 8px 14px; background: rgba(255,255,255,0.8); border-radius: 8px;
  85. cursor: pointer; font-size: 13px; color: #555; font-weight: 500;
  86. border: 1px solid #ddd; transition: all 0.15s;
  87. }
  88. .pb-switch-btn:hover { border-color: #2b1f99; color: #2b1f99; }
  89. .pbd-name { margin-right: 8px; font-size: 13px; }
  90. .is-active .pbd-name { color: #2b1f99; font-weight: 600; }
  91. </style>