Kaynağa Gözat

style: remove AI clichés — solid sidebar, no emojis, clean gradients, company colors via CSS vars

wangkangyjy 2 ay önce
ebeveyn
işleme
3476c4b8b1

+ 10 - 0
frontend/src/App.vue

@@ -5,4 +5,14 @@
 <style>
 * { margin: 0; padding: 0; box-sizing: border-box; }
 body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif; }
+
+:root {
+  --el-color-primary: #2b1f99;
+  --el-color-primary-light-3: #5544b8;
+  --el-color-primary-light-5: #7a6bc9;
+  --el-color-primary-light-7: #a096dc;
+  --el-color-primary-light-8: #c5beea;
+  --el-color-primary-light-9: #e2def6;
+  --el-color-primary-dark-2: #22197a;
+}
 </style>

+ 2 - 2
frontend/src/components/MainLayout.vue

@@ -202,7 +202,7 @@ function handleLogout() {
 <style scoped>
 .layout { height: 100vh; }
 .sidebar {
-  background: linear-gradient(180deg, #1a1560 0%, #2b1f99 60%, #1e1780 100%) !important;
+  background: #1a1560 !important;
   overflow-y: auto;
   transition: width 0.3s;
 }
@@ -246,7 +246,7 @@ function handleLogout() {
 .topbar-left { display: flex; align-items: center; gap: 8px; }
 .topbar-right { display: flex; align-items: center; gap: 12px; }
 .user-info { font-size: 13px; color: #666; }
-.main-content { background: #f5f7fa; padding: 20px 24px; min-height: calc(100vh - 48px); }
+.main-content { background: #f8f9fb; padding: 20px 24px; min-height: calc(100vh - 48px); }
 
 .notify-panel { max-height: 400px; overflow-y: auto; }
 .notify-header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 8px; border-bottom: 1px solid #eee; margin-bottom: 4px; font-weight: 600; }

+ 1 - 1
frontend/src/components/PeriodBanner.vue

@@ -54,7 +54,7 @@ const active = computed(() => {
 const switchablePeriods = computed(() => activePeriods.value)
 
 const statusIcon = computed(() => {
-  return { DRAFT: '📝', OKR_ALIGN: '🎯', EXECUTING: '▶️', ASSESSING: '✍️' }[active.value.status] || '📋'
+  return { DRAFT: '', OKR_ALIGN: '', EXECUTING: '', ASSESSING: '' }[active.value.status] || ''
 })
 
 const statusLabel = computed(() => {

+ 5 - 18
frontend/src/views/auth/LoginView.vue

@@ -110,27 +110,14 @@ async function handleSubmit() {
 .bg-grid {
   position: absolute;
   inset: 0;
-  background-image:
-    radial-gradient(circle at 20% 30%, rgba(58,212,216,0.06) 0%, transparent 50%),
-    radial-gradient(circle at 80% 70%, rgba(43,31,153,0.15) 0%, transparent 50%),
-    radial-gradient(circle at 50% 50%, rgba(43,31,153,0.08) 0%, transparent 70%);
-}
-.bg-grid::before {
-  content: '';
-  position: absolute;
-  inset: 0;
-  background-image:
-    linear-gradient(rgba(255,255,255,0.015) 1px, transparent 1px),
-    linear-gradient(90deg, rgba(255,255,255,0.015) 1px, transparent 1px);
-  background-size: 60px 60px;
+  background: radial-gradient(ellipse at 50% 0%, rgba(58,212,216,0.08) 0%, transparent 60%);
 }
 .login-card {
   position: relative;
   width: 420px;
-  background: rgba(12,10,35,0.85);
-  backdrop-filter: blur(24px);
-  border: 1px solid rgba(255,255,255,0.08);
-  border-radius: 16px;
+  background: #110e2b;
+  border: 1px solid rgba(255,255,255,0.06);
+  border-radius: 12px;
   padding: 0;
   overflow: hidden;
 }
@@ -159,7 +146,7 @@ async function handleSubmit() {
 }
 .card-divider {
   height: 1px;
-  background: linear-gradient(90deg, transparent, rgba(58,212,216,0.3), transparent);
+  background: rgba(255,255,255,0.06);
   margin: 0 32px;
 }
 .login-form {

+ 12 - 10
frontend/src/views/okr/MyOkrView.vue

@@ -104,7 +104,9 @@
               </div>
             </div>
             <div v-else class="hint-card top-level">
-              <div class="hint-icon">🚀</div>
+              <div class="hint-icon">
+                <el-icon :size="28"><Aim /></el-icon>
+              </div>
               <div class="hint-content">
                 <div class="hint-title">你是最高层级,制定公司级 OKR</div>
                 <div class="hint-desc">你的目标将决定下属的 OKR 方向。1个O + 3条KR,团队成员将基于此对齐。</div>
@@ -123,9 +125,9 @@
           <div class="section-head">
             <span class="section-badge personal">我的 OKR</span>
             <span class="section-title">{{ myOkr.title }}</span>
-            <el-tag type="success" size="large" effect="dark" round>🔒 已锁定</el-tag>
+            <el-tag type="success" size="large" effect="dark" round>已锁定</el-tag>
           </div>
-          <div class="rule-hint">⚠️ OKR 已锁定,仅可更新进度和标记阻塞,不可修改目标与关键结果。</div>
+          <div class="rule-hint">OKR 已锁定,仅可更新进度和标记阻塞,不可修改目标与关键结果。</div>
           <div class="okr-card card-APPROVED" style="margin-top:12px">
             <div v-for="(kr, i) in myOkr.keyResults" :key="kr.id" class="kr-progress-row">
               <div class="kr-progress-head">
@@ -144,7 +146,7 @@
                 <el-button size="small" type="primary" @click="updateProgress(kr)" round :disabled="kr.status === 'COMPLETED'">更新</el-button>
               </div>
               <div v-if="kr.status === 'BLOCKED'" class="blocked-hint">
-                ⚠️ 阻塞项,请说明原因与所需支持。上级已收到提醒。
+                阻塞项,请说明原因与所需支持。上级已收到提醒。
               </div>
             </div>
           </div>
@@ -179,8 +181,8 @@
               <el-tag v-if="kr.assignedToName" size="small" effect="dark" type="warning" round>@{{ kr.assignedToName }}</el-tag>
             </div>
             <div class="review-actions">
-              <el-button size="small" type="success" @click="doReview(o.id, true)" round>通过</el-button>
-              <el-button size="small" type="danger" @click="doReview(o.id, false)" round>驳回</el-button>
+              <el-button size="small" type="success" @click="doReview(o.id, true)" round>通过</el-button>
+              <el-button size="small" type="danger" @click="doReview(o.id, false)" round>驳回</el-button>
             </div>
           </div>
         </div>
@@ -190,7 +192,7 @@
     <!-- Create OKR Dialog -->
     <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>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>
@@ -256,7 +258,7 @@
 
       <!-- O Input -->
       <div class="o-section">
-        <div class="o-label">🎯 目标 O <span class="o-hint">(定性方向,15-30字,不含数字)</span></div>
+        <div class="o-label">目标 O <span class="o-hint">(定性方向,15-30字,不含数字)</span></div>
         <el-input v-model="newOkr.title" :placeholder="currentHints.oHint" size="large" maxlength="35" show-word-limit />
       </div>
 
@@ -311,7 +313,7 @@
       </div>
 
       <div class="dialog-footer-hint">
-        💡 提交后由直属上级审核。审核通过后锁定,周期内不可修改。
+        提交后由直属上级审核。审核通过后锁定,周期内不可修改。
       </div>
 
       <template #footer>
@@ -513,7 +515,7 @@ function initAlignment() {
     }
   }
 }
-const stateIcon = computed(() => ({ DRAFT: '📝', OKR_ALIGN: '🎯', EXECUTING: '▶️', ASSESSING: '✍️', ARCHIVED: '📦' }[period.value?.status] || ''))
+const stateIcon = computed(() => ({ DRAFT: '', OKR_ALIGN: '', EXECUTING: '', ASSESSING: '', ARCHIVED: '' }[period.value?.status] || ''))
 const stateBanner = computed(() => ({
   DRAFT: '本周期尚未发布。',
   OKR_ALIGN: 'OKR 对齐阶段 — 自上而下:上级先制定,下级对齐后制定。1O+3KR,不可偏离上级方向。',

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

@@ -49,7 +49,7 @@
         </div>
 
         <div v-if="sub.okr" class="sub-okr-body">
-          <div class="sub-o-title">🎯 {{ sub.okr.title }}</div>
+          <div class="sub-o-title">{{ sub.okr.title }}</div>
           <div class="sub-krs">
             <div v-for="(kr, i) in sub.okr.keyResults" :key="kr.id" class="sub-kr-row">
               <span class="sub-kr-num">KR{{ i+1 }}</span>

+ 10 - 10
frontend/src/views/period/PeriodListView.vue

@@ -11,9 +11,11 @@
     </div>
 
     <div v-if="periods.length === 0" class="empty-state">
-      <div class="empty-icon">📅</div>
-      <div class="empty-text">暂无考核周期</div>
-      <div class="empty-hint">管理员创建周期后,团队即可开始 OKR 对齐</div>
+      <el-empty description="暂无考核周期">
+        <template #extra>
+          <span class="empty-hint">管理员创建周期后,团队即可开始 OKR 对齐</span>
+        </template>
+      </el-empty>
     </div>
 
     <template v-else>
@@ -34,7 +36,7 @@
 
         <!-- Sub-group: 执行中 + 考评 -->
         <div class="sub-group" v-if="executing.length">
-          <div class="sub-label">▶️ 执行 & 考评</div>
+          <div class="sub-label">执行 & 考评</div>
           <div v-for="p in executing" :key="p.id" class="period-card" :class="'status-'+p.status">
             <div class="card-body">
               <div class="card-main">
@@ -61,7 +63,7 @@
 
         <!-- Sub-group: 已发布(OKR对齐中) -->
         <div class="sub-group" v-if="okrAlign.length">
-          <div class="sub-label">🎯 OKR 对齐中</div>
+          <div class="sub-label">OKR 对齐中</div>
           <div v-for="p in okrAlign" :key="p.id" class="period-card status-OKR_ALIGN">
             <div class="card-body">
               <div class="card-main">
@@ -85,7 +87,7 @@
 
         <!-- Sub-group: 未发布 -->
         <div class="sub-group" v-if="drafts.length">
-          <div class="sub-label">📝 未发布</div>
+          <div class="sub-label">未发布</div>
           <div v-for="p in drafts" :key="p.id" class="period-card status-DRAFT">
             <div class="card-body">
               <div class="card-main">
@@ -323,10 +325,8 @@ function statusGuide(p) {
 .page-hero { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 24px; }
 .hero-info h2 { font-size: 28px; font-weight: 700; color: #1a1a2e; margin: 0; }
 .hero-info p { font-size: 14px; color: #888; margin: 4px 0 0; }
-.empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 300px; color: #bbb; background: #fff; border-radius: 12px; }
-.empty-icon { font-size: 48px; margin-bottom: 12px; }
-.empty-text { font-size: 16px; color: #999; }
-.empty-hint { font-size: 13px; color: #ccc; margin-top: 4px; }
+.empty-state { display: flex; align-items: center; justify-content: center; min-height: 300px; background: #fff; border-radius: 12px; }
+.empty-hint { font-size: 13px; color: #bbb; }
 
 /* Tabs */
 .tab-bar { display: flex; gap: 4px; margin-bottom: 24px; background: #f5f7fa; border-radius: 10px; padding: 4px; }

+ 2 - 2
frontend/src/views/scoring/HistoryView.vue

@@ -66,7 +66,7 @@
 
           <div class="two-col">
             <div class="col okr-col">
-              <div class="col-head">🎯 当期 OKR 记录</div>
+              <div class="col-head">当期 OKR 记录</div>
               <div v-for="o in data.okrRecords || []" :key="o.id" class="mini-okr">
                 <div class="mini-o">{{ o.title }}</div>
                 <div v-for="kr in o.keyResults" :key="kr.id" class="mini-kr">
@@ -145,7 +145,7 @@ function gradeDesc(g) {
 
 .score-hero {
   display: flex; align-items: center; gap: 32px;
-  background: linear-gradient(135deg, #fafaff, #f0fcff); border-radius: 16px;
+  background: #f6f6fb; border-radius: 16px;
   padding: 32px 40px; margin-bottom: 24px; border: 1px solid #e8e8f0;
 }
 .grade-ring { width: 100px; height: 100px; border-radius: 50%; display: flex; align-items: center; justify-content: center; border: 4px solid #ddd; flex-shrink: 0; }

+ 8 - 8
frontend/src/views/scoring/MyResultView.vue

@@ -5,7 +5,7 @@
         <h2>我的绩效</h2>
         <p>{{ data.period ? (data.period.name + ' · ' + data.period.startDate + ' ~ ' + data.period.endDate) : '数据驱动,结果可追溯' }}</p>
       </div>
-      <el-button @click="$router.push('/scores/history')" round>📦 历史记录</el-button>
+      <el-button @click="$router.push('/scores/history')" round>历史记录</el-button>
     </div>
 
     <PeriodBanner v-if="periods.length" :periods="periods" v-model="periodId" @update:model-value="load" />
@@ -17,7 +17,7 @@
       <div class="form-banner">请完成绩效自评后提交,上级将在此基础进行终审</div>
 
       <div class="form-card">
-        <div class="form-section-title">🎯 OKR 目标达成自评 <span class="fst-sub">逐条 KR 打分(0.0-1.0),满分 60</span></div>
+        <div class="form-section-title">OKR 目标达成自评 <span class="fst-sub">逐条 KR 打分(0.0-1.0),满分 60</span></div>
         <div v-if="okrRecords.length" class="kr-score-list">
           <div v-for="(kr, i) in okrRecords[0].keyResults" :key="kr.id" class="kr-score-row">
             <div class="ksr-label">
@@ -44,7 +44,7 @@
       </div>
 
       <div class="form-card">
-        <div class="form-section-title">📊 工作成果自评 <span class="fst-sub">按维度自述 + 打分,满分 40</span></div>
+        <div class="form-section-title">工作成果自评 <span class="fst-sub">按维度自述 + 打分,满分 40</span></div>
         <div v-for="(dim, i) in dimScores" :key="i" class="dim-row">
           <div class="dim-head">
             <span class="dim-num">{{ i+1 }}</span>
@@ -131,13 +131,13 @@
 
       <div v-if="data.finalScore && !data.finalScore.confirmedAt" class="confirm-bar">
         <div class="confirm-info">
-          <span class="confirm-icon"></span>
+          <span class="confirm-icon"><el-icon><Check /></el-icon></span>
           <span>你的绩效结果如上,请审阅无误后确认。确认后本轮考核流程结束。</span>
         </div>
         <el-button type="success" size="large" @click="handleConfirm" :loading="confirming" round>确认绩效结果</el-button>
       </div>
       <div v-else-if="data.finalScore?.confirmedAt" class="confirmed-bar">
-        <span>已确认 · {{ data.finalScore.confirmedAt?.slice(0, 16) }}</span>
+        <span>已确认 · {{ data.finalScore.confirmedAt?.slice(0, 16) }}</span>
       </div>
 
       <div class="compare-row" v-if="data.selfScore && data.finalScore">
@@ -154,7 +154,7 @@
 
       <div class="two-col">
         <div class="col okr-col">
-          <div class="col-head">🎯 当期 OKR 记录</div>
+          <div class="col-head">当期 OKR 记录</div>
           <div v-for="o in data.okrRecords || []" :key="o.id" class="mini-okr">
             <div class="mini-o">{{ o.title }}</div>
             <div v-for="kr in o.keyResults" :key="kr.id" class="mini-kr">
@@ -350,7 +350,7 @@ function gradeDesc(g) {
 .bonus-label { font-size: 13px; color: #666; }
 
 .result-preview {
-  background: linear-gradient(135deg, #f5f3ff, #e8f5f5); border-radius: 12px;
+  background: #f4f2f9; border-radius: 12px;
   padding: 20px; text-align: center; border: 1px solid #dcd4f0; margin-bottom: 16px;
 }
 .rp-formula { font-size: 12px; color: #888; }
@@ -366,7 +366,7 @@ function gradeDesc(g) {
 
 .score-hero {
   display: flex; align-items: center; gap: 32px;
-  background: linear-gradient(135deg, #fafaff, #f0fcff); border-radius: 16px;
+  background: #f6f6fb; border-radius: 16px;
   padding: 32px 40px; margin-bottom: 24px; border: 1px solid #e8e8f0;
 }
 .score-hero.self-submitted { background: linear-gradient(135deg, #fffcf5, #fef9e7); border-color: #f9e79f; }

+ 8 - 8
frontend/src/views/scoring/ScoringEditView.vue

@@ -72,12 +72,12 @@
 
         <!-- Employee Self Assessment (mirrors right-side scoring structure) -->
         <div class="panel-divider"></div>
-        <div class="panel-head">📝 员工自评</div>
+        <div class="panel-head">员工自评</div>
         <div v-if="!data.selfScore" class="panel-empty">员工尚未提交自评</div>
         <template v-else>
           <!-- 1. OKR self scores -->
           <div class="self-block">
-            <div class="self-block-title">🎯 OKR 目标达成自评 <span class="sb-sub">(满分 {{ computedOkrMaxScore }})</span></div>
+            <div class="self-block-title">OKR 目标达成自评 <span class="sb-sub">(满分 {{ computedOkrMaxScore }})</span></div>
             <div v-if="selfKrScores.length" class="self-kr-list">
               <div v-for="(sk, i) in selfKrScores" :key="i" class="self-kr-item">
                 <span class="ski-num">KR{{ i+1 }}</span>
@@ -90,7 +90,7 @@
 
           <!-- 2. Work result self scores -->
           <div class="self-block">
-            <div class="self-block-title">📊 工作成果自评 <span class="sb-sub">(满分 40)</span></div>
+            <div class="self-block-title">工作成果自评 <span class="sb-sub">(满分 40)</span></div>
             <div v-if="selfDimensions.length" class="self-dim-list">
               <div v-for="d in selfDimensions" :key="d.name" class="self-dim-item">
                 <div class="sdi-head">
@@ -127,7 +127,7 @@
 
         <!-- KR Scores -->
         <div class="score-card">
-          <div class="sc-header">🎯 OKR 目标达成 <span class="sc-sub">逐条 KR 打分,满分 60</span></div>
+          <div class="sc-header">OKR 目标达成 <span class="sc-sub">逐条 KR 打分,满分 60</span></div>
           <div class="kr-score-list">
             <div v-for="(ks, i) in krScores" :key="i" class="kr-score-row">
               <div class="ksr-label">
@@ -154,7 +154,7 @@
 
         <!-- Work Result (Dimensions) -->
         <div class="score-card">
-          <div class="sc-header">📊 工作成果评价 <span class="sc-sub">满分 40</span></div>
+          <div class="sc-header">工作成果评价 <span class="sc-sub">满分 40</span></div>
           <div v-for="(dim, i) in dimScores" :key="i" class="dim-row">
             <div class="dim-head">
               <span class="dim-num">{{ i+1 }}</span>
@@ -416,7 +416,7 @@ function fmtTime(t) { if (!t) return ''; return t.slice(0, 16).replace('T', ' ')
 
 .self-total-bar {
   margin-top: 12px; padding: 12px 16px;
-  background: linear-gradient(135deg, #f5f3ff, #e8f5f5); border-radius: 10px;
+  background: #f4f2f9; border-radius: 10px;
   text-align: center; font-size: 14px; color: #555;
   border: 1px solid #dcd4f0;
 }
@@ -453,14 +453,14 @@ function fmtTime(t) { if (!t) return ''; return t.slice(0, 16).replace('T', ' ')
 .dim-score-unit { font-size: 13px; color: #999; margin-left: 6px; }
 
 .result-preview {
-  background: linear-gradient(135deg, #f5f3ff, #e8f5f5); border-radius: 12px;
+  background: #f4f2f9; border-radius: 12px;
   padding: 20px; text-align: center; border: 1px solid #dcd4f0; margin-bottom: 16px;
 }
 .rp-formula { font-size: 12px; color: #888; }
 .rp-score { font-size: 40px; font-weight: 700; color: #2b1f99; margin: 6px 0; line-height: 1; }
 .rp-score small { font-size: 18px; color: #999; font-weight: 400; }
 .rp-grade { font-size: 14px; color: #666; }
-.submit-btn { width: 100%; background: linear-gradient(135deg, #3ad4d8, #2b1f99); border: none; height: 48px; font-size: 15px; }
+.submit-btn { width: 100%; background: #2b1f99; border: none; height: 48px; font-size: 15px; }
 
 @media (max-width: 1100px) {
   .same-screen { flex-direction: column; }

+ 1 - 1
frontend/src/views/scoring/ScoringListView.vue

@@ -5,7 +5,7 @@
         <h2>绩效打分</h2>
         <p>下属绩效考评管理</p>
       </div>
-      <el-button @click="$router.push('/scores/history')" round>📦 历史记录</el-button>
+      <el-button @click="$router.push('/scores/history')" round>历史记录</el-button>
     </div>
 
     <PeriodBanner v-if="periods.length" :periods="periods" v-model="periodId" @update:model-value="load" />