| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- <template>
- <div class="period-banner" :class="'banner-' + active.status">
- <div class="pb-left">
- <span class="pb-icon">{{ statusIcon }}</span>
- <div class="pb-info">
- <div class="pb-name">{{ active.name }}</div>
- <div class="pb-date">{{ active.startDate }} ~ {{ active.endDate }}</div>
- </div>
- <el-tag :type="statusTagType" effect="dark" size="small" round>{{ statusLabel }}</el-tag>
- </div>
- <div class="pb-right" v-if="switchablePeriods.length > 1">
- <el-dropdown trigger="click" @command="handleSwitch">
- <span class="pb-switch-btn">
- 切换周期 <el-icon><ArrowDown /></el-icon>
- </span>
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item
- v-for="p in switchablePeriods"
- :key="p.id"
- :command="p.id"
- :class="{ 'is-active': p.id === active.id }"
- >
- <span class="pbd-name">{{ p.name }}</span>
- <el-tag :type="statusTag(p.status)" size="small" effect="plain" round>{{ statusText(p.status) }}</el-tag>
- </el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- </div>
- </div>
- </template>
- <script setup>
- import { computed } from 'vue'
- const props = defineProps({
- periods: { type: Array, required: true },
- modelValue: { type: Number, default: null }
- })
- const emit = defineEmits(['update:modelValue'])
- // Only show non-archived, non-future-draft periods (active = DRAFT/OKR_ALIGN/EXECUTING/ASSESSING)
- const activePeriods = computed(() => {
- return (props.periods || []).filter(p => p.status !== 'ARCHIVED')
- })
- const active = computed(() => {
- const found = activePeriods.value.find(p => p.id === props.modelValue)
- return found || activePeriods.value[0] || {}
- })
- const switchablePeriods = computed(() => activePeriods.value)
- const statusIcon = computed(() => {
- return { DRAFT: '', OKR_ALIGN: '', EXECUTING: '', ASSESSING: '' }[active.value.status] || ''
- })
- const statusLabel = computed(() => {
- return { DRAFT: '待发布', OKR_ALIGN: '对齐中', EXECUTING: '执行中', ASSESSING: '考评中' }[active.value.status] || ''
- })
- const statusTagType = computed(() => {
- return { ASSESSING: 'danger', EXECUTING: 'success', OKR_ALIGN: 'warning', DRAFT: 'info' }[active.value.status] || 'info'
- })
- function statusText(s) {
- return { DRAFT: '待发布', OKR_ALIGN: '对齐中', EXECUTING: '执行中', ASSESSING: '考评中' }[s] || s
- }
- function statusTag(s) {
- return { ASSESSING: 'danger', EXECUTING: 'success', OKR_ALIGN: 'warning', DRAFT: 'info' }[s] || 'info'
- }
- function handleSwitch(id) {
- emit('update:modelValue', id)
- }
- </script>
- <style scoped>
- .period-banner {
- display: flex; align-items: center; justify-content: space-between;
- padding: 14px 20px; border-radius: 10px; margin-bottom: 20px;
- box-shadow: 0 1px 4px rgba(0,0,0,0.04);
- }
- .banner-DRAFT { background: #f5f5f5; border: 1px solid #e0e0e0; }
- .banner-OKR_ALIGN { background: #e0f7fa; border: 1px solid #b2ebf2; }
- .banner-EXECUTING { background: #e8f5e9; border: 1px solid #c8e6c9; }
- .banner-ASSESSING { background: #fce4ec; border: 1px solid #f8bbd0; }
- .pb-left { display: flex; align-items: center; gap: 12px; }
- .pb-icon { font-size: 24px; }
- .pb-name { font-size: 16px; font-weight: 700; color: #1a1a2e; }
- .pb-date { font-size: 12px; color: #888; margin-top: 2px; }
- .pb-switch-btn {
- display: inline-flex; align-items: center; gap: 4px;
- padding: 8px 14px; background: rgba(255,255,255,0.8); border-radius: 8px;
- cursor: pointer; font-size: 13px; color: #555; font-weight: 500;
- border: 1px solid #ddd; transition: all 0.15s;
- }
- .pb-switch-btn:hover { border-color: #2b1f99; color: #2b1f99; }
- .pbd-name { margin-right: 8px; font-size: 13px; }
- .is-active .pbd-name { color: #2b1f99; font-weight: 600; }
- </style>
|