|
|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
|
- <div class="page">
|
|
|
+ <div class="app-page okr-workbench">
|
|
|
<div class="page-hero">
|
|
|
<div class="hero-info">
|
|
|
<h2>我的 OKR</h2>
|
|
|
@@ -17,8 +17,8 @@
|
|
|
</div>
|
|
|
|
|
|
<!-- ByteDance rules + Position template -->
|
|
|
- <div class="rules-card">
|
|
|
- <div class="rules-title">📋 OKR 规则 · {{ auth.user?.position || '通用岗位' }}</div>
|
|
|
+ <div class="rules-card okr-standard-panel med-panel">
|
|
|
+ <div class="rules-title">OKR 规则 · {{ auth.user?.position || '通用岗位' }}</div>
|
|
|
<div class="rules-grid">
|
|
|
<div class="rule-item"><b>O</b>:定性方向,15-30字,不含数字指标</div>
|
|
|
<div class="rule-item"><b>KR</b>:2~5条,可量化结果,权重之和=100</div>
|
|
|
@@ -26,7 +26,7 @@
|
|
|
<div class="rule-item"><b>打分</b>:逐条KR 0-1分,看结果不看过程</div>
|
|
|
</div>
|
|
|
<div v-if="positionTemplate" class="position-ref">
|
|
|
- <div class="pr-title">📌 你的岗位指标参考({{ positionTemplate.name }})</div>
|
|
|
+ <div class="pr-title">你的岗位指标参考({{ positionTemplate.name }})</div>
|
|
|
<div class="pr-items">
|
|
|
<el-tag v-for="(item, i) in parseKpiItems(positionTemplate.itemsJson)" :key="i"
|
|
|
size="small" effect="plain" type="info" round>
|
|
|
@@ -60,7 +60,7 @@
|
|
|
<div class="kr-display-text">{{ kr.title }}</div>
|
|
|
<div class="kr-display-meta">
|
|
|
<span class="kr-target">目标 {{ kr.targetValue }}</span>
|
|
|
- <el-tag v-if="kr.sourceKrId" size="small" effect="plain" type="info" round>↗ 承接上级</el-tag>
|
|
|
+ <el-tag v-if="kr.sourceKrId" size="small" effect="plain" type="info" round>承接上级</el-tag>
|
|
|
<el-tag v-if="kr.assignedToName" size="small" effect="dark" type="warning" round>@{{ kr.assignedToName }}</el-tag>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -73,7 +73,7 @@
|
|
|
|
|
|
<template v-else>
|
|
|
<div v-if="!canSubmit" class="hint-card waiting">
|
|
|
- <div class="hint-icon">⏳</div>
|
|
|
+ <div class="hint-icon"><el-icon :size="28"><Clock /></el-icon></div>
|
|
|
<div class="hint-content">
|
|
|
<div class="hint-title">等待上级先制定 OKR</div>
|
|
|
<div class="hint-desc">{{ superiorName }} 需要先完成 OKR 制定并通过审核,然后你才能对齐并提交自己的 OKR。</div>
|
|
|
@@ -193,13 +193,13 @@
|
|
|
<el-dialog v-model="showForm" :title="editingOkr ? '编辑 OKR' : '制定个人 OKR'" width="720px" top="4vh" :close-on-click-modal="false">
|
|
|
<div class="dialog-rules">
|
|
|
<div class="dr-item"><b>1个O(目标)</b>:定性方向,15-30字,不含数字指标,必须对齐上级方向</div>
|
|
|
- <div class="dr-item">📏 <b>2~5条KR</b>(推荐3条):可量化结果,10-25字,必须有验收标准</div>
|
|
|
- <div class="dr-item">⚖️ <b>权重之和 = 100</b>:每条KR分配权重,总和必须为100</div>
|
|
|
+ <div class="dr-item"><b>2~5条KR</b>(推荐3条):可量化结果,10-25字,必须有验收标准</div>
|
|
|
+ <div class="dr-item"><b>权重之和 = 100</b>:每条KR分配权重,总和必须为100</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- Position template reference in dialog -->
|
|
|
<div v-if="positionTemplate" class="dialog-kpi-ref">
|
|
|
- <div class="dkr-title">📌 你的岗位指标参考({{ positionTemplate.name }})</div>
|
|
|
+ <div class="dkr-title">你的岗位指标参考({{ positionTemplate.name }})</div>
|
|
|
<div class="dkr-tags">
|
|
|
<el-tag v-for="(item, i) in parseKpiItems(positionTemplate.itemsJson)" :key="i"
|
|
|
size="small" type="info" effect="plain" round>
|
|
|
@@ -213,7 +213,7 @@
|
|
|
<div v-if="superiorOkrs.length" class="align-section">
|
|
|
<div class="align-head">
|
|
|
<div class="align-head-left">
|
|
|
- <span class="align-head-icon">↗</span>
|
|
|
+
|
|
|
<span class="align-head-title">对齐上级 OKR</span>
|
|
|
<span class="align-head-hint">· 勾选你要承接的关键结果,将自动创建引用条目</span>
|
|
|
</div>
|
|
|
@@ -279,14 +279,14 @@
|
|
|
<!-- Dynamic KRs with source reference -->
|
|
|
<div class="kr-section">
|
|
|
<div class="kr-section-title">
|
|
|
- 📏 关键结果 KR({{ newOkr.krs.length }} 条)
|
|
|
+ 关键结果 KR({{ newOkr.krs.length }} 条)
|
|
|
<span class="weight-sum" :class="{ 'weight-ok': weightSum === 100, 'weight-bad': weightSum !== 100 }">
|
|
|
权重合计:{{ weightSum }} / 100
|
|
|
</span>
|
|
|
</div>
|
|
|
<div v-for="(kr, i) in newOkr.krs" :key="i" class="kr-form-card" :class="{ 'has-source': kr._sourceKrId }">
|
|
|
<div v-if="kr._sourceInfo" class="kr-source-bar">
|
|
|
- <span class="kr-source-icon">↗</span>
|
|
|
+
|
|
|
<span class="kr-source-text">承接上级 {{ kr._sourceInfo.userName }} 的 {{ kr._sourceInfo.krTitle }}</span>
|
|
|
</div>
|
|
|
<div class="kr-form-head">
|
|
|
@@ -687,14 +687,14 @@ function truncate(s, n) { if (!s) return ''; return s.length > n ? s.slice(0, n)
|
|
|
|
|
|
.section { margin-bottom: 36px; }
|
|
|
.section-head { display: flex; align-items: center; gap: 14px; margin-bottom: 16px; padding-bottom: 12px; border-bottom: 2px solid #f0f0f0; }
|
|
|
-.section-badge { padding: 6px 18px; border-radius: 20px; font-size: 14px; font-weight: 700; color: #fff; letter-spacing: 2px; }
|
|
|
+.section-badge { padding: 6px 18px; border-radius: 8px; font-size: 14px; font-weight: 700; color: #fff; letter-spacing: 2px; }
|
|
|
.section-badge.personal { background: linear-gradient(135deg, #3ad4d8, #1eada8); box-shadow: 0 2px 8px rgba(58,212,216,0.3); }
|
|
|
.section-badge.superior { background: linear-gradient(135deg, #2b1f99, #1a1560); box-shadow: 0 2px 8px rgba(43,31,153,0.3); }
|
|
|
.section-badge.review { background: linear-gradient(135deg, #f39c12, #e67e22); box-shadow: 0 2px 8px rgba(230,126,34,0.3); }
|
|
|
.section-title { font-size: 18px; font-weight: 700; color: #1a1a2e; }
|
|
|
.section-hint { font-size: 13px; color: #aaa; }
|
|
|
|
|
|
-.hint-card { display: flex; align-items: flex-start; gap: 20px; background: #fff; border-radius: 12px; padding: 28px 32px; box-shadow: 0 1px 4px rgba(0,0,0,0.04); margin-bottom: 24px; }
|
|
|
+.hint-card { display: flex; align-items: flex-start; gap: 20px; background: #fff; border-radius: 8px; padding: 28px 32px; box-shadow: 0 1px 4px rgba(0,0,0,0.04); margin-bottom: 24px; }
|
|
|
.hint-card.waiting { border: 2px solid #fdebd0; background: #fef9e7; }
|
|
|
.hint-card.top-level { border: 2px solid #d5f5e3; background: #eafaf1; }
|
|
|
.hint-icon { font-size: 36px; }
|
|
|
@@ -705,7 +705,7 @@ function truncate(s, n) { if (!s) return ''; return s.length > n ? s.slice(0, n)
|
|
|
.rule-hint { font-size: 12px; color: #e67e22; background: #fef9e7; padding: 8px 14px; border-radius: 6px; margin-bottom: 8px; }
|
|
|
|
|
|
.okr-grid { display: grid; grid-template-columns: 1fr; gap: 12px; }
|
|
|
-.okr-card { background: #fff; border-radius: 12px; padding: 20px 24px; box-shadow: 0 1px 4px rgba(0,0,0,0.04); border: 1px solid #f0f0f0; }
|
|
|
+.okr-card { background: #fff; border-radius: 8px; padding: 20px 24px; box-shadow: 0 1px 4px rgba(0,0,0,0.04); border: 1px solid #f0f0f0; }
|
|
|
.okr-card.superior-card { border-left: 4px solid #2b1f99; }
|
|
|
.okr-card.card-APPROVED { border-left: 4px solid #27ae60; }
|
|
|
.okr-card.card-SUBMITTED { border-left: 4px solid #e67e22; }
|
|
|
@@ -756,7 +756,7 @@ function truncate(s, n) { if (!s) return ''; return s.length > n ? s.slice(0, n)
|
|
|
.dr-item { font-size: 13px; color: #333; line-height: 1.8; }
|
|
|
|
|
|
/* Alignment section */
|
|
|
-.align-section { background: #fff; border: 1px solid #e5e7eb; border-radius: 14px; margin-bottom: 20px; overflow: hidden; }
|
|
|
+.align-section { background: #fff; border: 1px solid #e5e7eb; border-radius: 8px; margin-bottom: 20px; overflow: hidden; }
|
|
|
.align-head { display: flex; align-items: center; justify-content: space-between; padding: 18px 22px; border-bottom: 1px solid #f3f4f6; }
|
|
|
.align-head-left { display: flex; align-items: center; gap: 6px; }
|
|
|
.align-head-icon { font-size: 18px; font-weight: 700; color: #2b1f99; }
|
|
|
@@ -795,7 +795,7 @@ function truncate(s, n) { if (!s) return ''; return s.length > n ? s.slice(0, n)
|
|
|
|
|
|
.kr-section { margin-bottom: 16px; }
|
|
|
.kr-section-title { font-weight: 600; font-size: 15px; color: #1a1a2e; margin-bottom: 12px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
|
|
|
-.weight-sum { font-size: 13px; padding: 2px 10px; border-radius: 12px; }
|
|
|
+.weight-sum { font-size: 13px; padding: 2px 10px; border-radius: 8px; }
|
|
|
.weight-ok { background: #eafaf1; color: #1e8449; font-weight: 700; }
|
|
|
.weight-bad { background: #fdedec; color: #c0392b; font-weight: 700; }
|
|
|
.kr-form-card { background: #fafbfc; border-radius: 10px; padding: 14px 16px; margin-bottom: 10px; border: 1px solid #f0f0f0; }
|