|
|
@@ -1,6 +1,57 @@
|
|
|
<template>
|
|
|
<div class="okr-execution-view">
|
|
|
- <h2>执行进度</h2>
|
|
|
- <p>OKR 执行进度页面(将在任务12完善)</p>
|
|
|
+ <div class="data-toolbar">
|
|
|
+ <span class="data-toolbar__title">执行进度</span>
|
|
|
+ </div>
|
|
|
+ <div class="data-table" v-if="myOkr">
|
|
|
+ <div class="data-table__header" style="grid-template-columns:1fr 80px 80px 100px 80px">
|
|
|
+ <span>KR</span><span>进度</span><span>当前/目标</span><span>状态</span><span>操作</span>
|
|
|
+ </div>
|
|
|
+ <div v-for="kr in myOkr.keyResults" :key="kr.id" class="data-table__row"
|
|
|
+ style="grid-template-columns:1fr 80px 80px 100px 80px">
|
|
|
+ <span>{{ kr.title }}</span>
|
|
|
+ <span style="font-weight:600">{{ krPercent(kr) }}%</span>
|
|
|
+ <span style="color:var(--color-text-secondary)">{{ kr.currentValue }}/{{ kr.targetValue }}</span>
|
|
|
+ <StatusLabel :tone="krStatusTone(kr.status)" :label="krStatusLabel(kr.status)" />
|
|
|
+ <el-button size="small" @click="kr._showUpdate = !kr._showUpdate">更新</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="empty-state">加载中...</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, onMounted } from 'vue'
|
|
|
+import { useRoute } from 'vue-router'
|
|
|
+import * as okrApi from '@/api/okr'
|
|
|
+import StatusLabel from '@/components/common/StatusLabel.vue'
|
|
|
+
|
|
|
+const route = useRoute()
|
|
|
+const myOkr = ref(null)
|
|
|
+
|
|
|
+function krPercent(kr) {
|
|
|
+ if (kr.targetValue <= 0) return 0
|
|
|
+ return Math.round((kr.currentValue / kr.targetValue) * 100)
|
|
|
+}
|
|
|
+function krStatusLabel(s) {
|
|
|
+ const map = { NOT_STARTED: '未开始', IN_PROGRESS: '进行中', COMPLETED: '完成', BLOCKED: '阻塞' }
|
|
|
+ return map[s] || s
|
|
|
+}
|
|
|
+function krStatusTone(s) {
|
|
|
+ const map = { NOT_STARTED: 'info', IN_PROGRESS: 'active', COMPLETED: 'success', BLOCKED: 'danger' }
|
|
|
+ return map[s] || 'info'
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ const periodId = route.query.periodId
|
|
|
+ if (periodId) {
|
|
|
+ const res = await okrApi.getMyOkr(periodId)
|
|
|
+ myOkr.value = res.data
|
|
|
+ }
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.okr-execution-view { max-width: 960px; margin: 0 auto; }
|
|
|
+.empty-state { text-align: center; padding: 48px; color: var(--color-text-muted); }
|
|
|
+</style>
|