فهرست منبع

style: redesign okr alignment workspace

wangkangyjy 1 ماه پیش
والد
کامیت
4580a2453f
3فایلهای تغییر یافته به همراه25 افزوده شده و 20 حذف شده
  1. 6 1
      frontend/scripts/verify-medtech-theme.mjs
  2. 17 17
      frontend/src/views/okr/MyOkrView.vue
  3. 2 2
      frontend/src/views/okr/TeamOkrView.vue

+ 6 - 1
frontend/scripts/verify-medtech-theme.mjs

@@ -22,7 +22,7 @@ const requiredTokens = [
   '--radius-md: 8px'
 ]
 
-const bannedEmoji = /[📋📌⚡⏳✅❌🎯🚀💡🔥]/
+const bannedEmoji = /[📋📌⚡⏳✅❌🎯🚀💡🔥]/u
 const bannedDecor = /(radial-gradient|gradient orb|bokeh|blur\(60px\)|border-radius:\s*(1[2-9]|[2-9]\d)px)/
 const migratedMarkers = [
   'clinical-shell',
@@ -79,6 +79,8 @@ const layout = readFileSync(join(root, 'src/components/MainLayout.vue'), 'utf8')
 const login = readFileSync(join(root, 'src/views/auth/LoginView.vue'), 'utf8')
 const periodList = readFileSync(join(root, 'src/views/period/PeriodListView.vue'), 'utf8')
 const periodBanner = readFileSync(join(root, 'src/components/PeriodBanner.vue'), 'utf8')
+const myOkr = readFileSync(join(root, 'src/views/okr/MyOkrView.vue'), 'utf8')
+const teamOkr = readFileSync(join(root, 'src/views/okr/TeamOkrView.vue'), 'utf8')
 
 if (!layout.includes('clinical-shell')) failures.push('MainLayout must use clinical-shell')
 if (!layout.includes('clinical-sidebar')) failures.push('MainLayout must use clinical-sidebar')
@@ -87,6 +89,9 @@ if (login.includes('radial-gradient')) failures.push('LoginView must not use rad
 if (!periodList.includes('period-workbench')) failures.push('PeriodListView must use period-workbench')
 if (!periodList.includes('period-metrics')) failures.push('PeriodListView must show period-metrics')
 if (!periodBanner.includes('period-status-strip')) failures.push('PeriodBanner must use period-status-strip')
+if (!myOkr.includes('okr-workbench')) failures.push('MyOkrView must use okr-workbench')
+if (!myOkr.includes('okr-standard-panel')) failures.push('MyOkrView must use okr-standard-panel')
+if (!teamOkr.includes('team-okr-workbench')) failures.push('TeamOkrView must use team-okr-workbench')
 
 const vueAndCssFiles = walk(src).filter((file) => /\.(vue|css)$/.test(file))
 for (const file of vueAndCssFiles) {

+ 17 - 17
frontend/src/views/okr/MyOkrView.vue

@@ -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; }

+ 2 - 2
frontend/src/views/okr/TeamOkrView.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="page">
+  <div class="app-page team-okr-workbench">
     <div class="page-hero">
       <div class="hero-info">
         <h2>团队 OKR</h2>
@@ -64,7 +64,7 @@
                 @{{ kr.assignedToName }}
               </el-tag>
               <el-tag v-if="kr.sourceKrId" size="small" effect="plain" type="info" round>
-                承接上级
+                承接上级
               </el-tag>
             </div>
           </div>