Files
hr-performance-system/績效管理系統_UI預覽_v1.1.html
donald c24634f4b7 Initial commit: HR Performance System
- Database schema with 31 tables for 4-card system
- LLM API integration (Gemini, DeepSeek, OpenAI)
- Error handling system with modal component
- Connection test UI for LLM services
- Environment configuration files
- Complete database documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 23:34:13 +08:00

1772 lines
57 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>績效管理系統 - 四卡循環 v1.1</title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@300;400;500;600;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
:root {
--primary: #1e40af;
--primary-light: #3b82f6;
--primary-50: #eff6ff;
--success: #059669;
--success-light: #10b981;
--success-50: #ecfdf5;
--warning: #f59e0b;
--warning-50: #fffbeb;
--danger: #dc2626;
--danger-50: #fef2f2;
--purple: #7c3aed;
--purple-50: #f5f3ff;
--slate-50: #f8fafc;
--slate-100: #f1f5f9;
--slate-200: #e2e8f0;
--slate-300: #cbd5e1;
--slate-400: #94a3b8;
--slate-500: #64748b;
--slate-600: #475569;
--slate-700: #334155;
--slate-800: #1e293b;
--slate-900: #0f172a;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Noto Sans TC', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--slate-50);
color: var(--slate-800);
line-height: 1.6;
}
/* Header */
.header {
background: linear-gradient(135deg, var(--slate-900), var(--slate-800));
color: white;
padding: 0.875rem 1.5rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
max-width: 1440px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.logo-section {
display: flex;
align-items: center;
gap: 1rem;
}
.logo-icon {
width: 42px;
height: 42px;
background: linear-gradient(135deg, #60a5fa, #3b82f6);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}
.logo-text h1 {
font-size: 1.125rem;
font-weight: 700;
letter-spacing: -0.5px;
}
.logo-text p {
font-size: 0.65rem;
color: var(--slate-400);
}
.header-actions {
display: flex;
align-items: center;
gap: 0.75rem;
}
.lang-switch {
display: flex;
background: var(--slate-700);
border-radius: 8px;
padding: 2px;
font-size: 0.75rem;
}
.lang-btn {
padding: 0.375rem 0.75rem;
border: none;
background: transparent;
color: var(--slate-400);
cursor: pointer;
border-radius: 6px;
transition: all 0.2s;
font-weight: 500;
}
.lang-btn.active {
background: var(--primary);
color: white;
}
.notification-btn {
position: relative;
padding: 0.5rem;
background: transparent;
border: none;
color: white;
cursor: pointer;
border-radius: 8px;
transition: background 0.2s;
}
.notification-btn:hover {
background: var(--slate-700);
}
.notification-badge {
position: absolute;
top: 4px;
right: 4px;
width: 8px;
height: 8px;
background: #ef4444;
border-radius: 50%;
}
.user-profile {
display: flex;
align-items: center;
gap: 0.625rem;
padding-left: 0.75rem;
border-left: 1px solid var(--slate-700);
}
.user-avatar {
width: 34px;
height: 34px;
background: linear-gradient(135deg, #fbbf24, #f97316);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 0.8rem;
}
.user-info .name {
font-weight: 600;
font-size: 0.8rem;
}
.user-info .role {
font-size: 0.65rem;
color: var(--slate-400);
}
/* Main Container */
.main-container {
max-width: 1440px;
margin: 0 auto;
padding: 1.5rem;
}
/* Period Selector */
.period-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.25rem;
}
.period-selector {
display: flex;
gap: 0.5rem;
}
.period-btn {
padding: 0.5rem 1rem;
border: 1px solid var(--slate-300);
background: white;
border-radius: 8px;
font-size: 0.8rem;
font-weight: 500;
color: var(--slate-600);
cursor: pointer;
transition: all 0.2s;
}
.period-btn:hover {
border-color: var(--primary-light);
color: var(--primary);
}
.period-btn.active {
background: var(--primary);
border-color: var(--primary);
color: white;
}
.period-info {
font-size: 0.8rem;
color: var(--slate-500);
}
.period-info strong {
color: var(--slate-700);
}
/* Progress Stepper */
.stepper-container {
background: white;
border-radius: 16px;
padding: 1.25rem 1.5rem;
margin-bottom: 1.5rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
border: 1px solid var(--slate-200);
}
.stepper-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.25rem;
}
.stepper-header h2 {
font-size: 0.8rem;
font-weight: 600;
color: var(--slate-600);
}
.stepper-header span {
font-size: 0.75rem;
color: var(--slate-500);
}
.stepper {
display: flex;
justify-content: space-between;
position: relative;
padding: 0 3rem;
}
.stepper::before {
content: '';
position: absolute;
top: 22px;
left: 100px;
right: 100px;
height: 3px;
background: var(--slate-200);
border-radius: 2px;
}
.stepper::after {
content: '';
position: absolute;
top: 22px;
left: 100px;
width: calc(50% - 50px);
height: 3px;
background: linear-gradient(90deg, var(--success), var(--success-light));
border-radius: 2px;
}
.step {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
z-index: 1;
cursor: pointer;
transition: transform 0.2s;
}
.step:hover {
transform: translateY(-2px);
}
.step-icon {
width: 46px;
height: 46px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
margin-bottom: 0.625rem;
transition: all 0.3s;
}
.step.completed .step-icon {
background: linear-gradient(135deg, var(--success), var(--success-light));
box-shadow: 0 4px 12px rgba(5, 150, 105, 0.35);
color: white;
}
.step.active .step-icon {
background: linear-gradient(135deg, var(--primary), var(--primary-light));
box-shadow: 0 4px 12px rgba(30, 64, 175, 0.35), 0 0 0 3px rgba(59, 130, 246, 0.2);
color: white;
}
.step.pending .step-icon {
background: var(--slate-200);
color: var(--slate-400);
}
.step-title {
font-size: 0.825rem;
font-weight: 600;
color: var(--slate-700);
margin-bottom: 0.125rem;
}
.step.active .step-title {
color: var(--primary);
}
.step-subtitle {
font-size: 0.65rem;
color: var(--slate-400);
margin-bottom: 0.375rem;
}
.step-status {
font-size: 0.65rem;
padding: 0.2rem 0.625rem;
border-radius: 20px;
font-weight: 500;
}
.step.completed .step-status {
background: #d1fae5;
color: #065f46;
}
.step.active .step-status {
background: #dbeafe;
color: #1e40af;
}
.step.pending .step-status {
background: var(--slate-100);
color: var(--slate-500);
}
/* Content Grid */
.content-grid {
display: grid;
grid-template-columns: 240px 1fr 300px;
gap: 1.5rem;
}
/* Sidebar */
.sidebar {
position: sticky;
top: 80px;
height: fit-content;
}
.sidebar-card {
background: white;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
border: 1px solid var(--slate-200);
}
.sidebar-header {
background: linear-gradient(135deg, var(--slate-800), var(--slate-700));
color: white;
padding: 0.875rem 1rem;
font-weight: 600;
font-size: 0.85rem;
}
.sidebar-nav {
padding: 0.375rem;
}
.nav-item {
display: flex;
align-items: center;
gap: 0.625rem;
padding: 0.75rem 0.875rem;
border-radius: 10px;
cursor: pointer;
transition: all 0.2s;
margin-bottom: 0.125rem;
border: none;
background: transparent;
width: 100%;
text-align: left;
font-size: 0.8rem;
font-weight: 500;
color: var(--slate-600);
}
.nav-item:hover {
background: var(--slate-50);
}
.nav-item.active {
background: var(--primary-50);
color: var(--primary);
}
.nav-icon {
width: 32px;
height: 32px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
background: var(--slate-100);
font-size: 0.9rem;
}
.nav-item.active .nav-icon {
background: #dbeafe;
}
.nav-check {
margin-left: auto;
color: var(--success);
font-size: 0.9rem;
}
.sidebar-divider {
height: 1px;
background: var(--slate-200);
margin: 0.5rem;
}
/* Main Panel */
.main-panel {
background: white;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
border: 1px solid var(--slate-200);
}
.panel-header {
padding: 1.25rem 1.5rem;
background: linear-gradient(135deg, var(--slate-50), white);
border-bottom: 1px solid var(--slate-200);
}
.panel-header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.panel-title-section {
display: flex;
align-items: center;
gap: 0.875rem;
}
.panel-icon {
width: 46px;
height: 46px;
background: var(--primary-50);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.375rem;
}
.panel-title h2 {
font-size: 1.2rem;
font-weight: 700;
color: var(--slate-800);
}
.panel-title p {
font-size: 0.75rem;
color: var(--slate-500);
}
.panel-meta {
display: flex;
align-items: center;
gap: 1.5rem;
}
.meta-item {
text-align: center;
}
.meta-item .label {
font-size: 0.7rem;
color: var(--slate-500);
}
.meta-item .value {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary);
}
.progress-ring {
width: 50px;
height: 50px;
}
/* Tabs */
.tabs {
display: flex;
padding: 0 1.25rem;
border-bottom: 1px solid var(--slate-200);
background: white;
overflow-x: auto;
}
.tab {
padding: 0.875rem 1.25rem;
font-size: 0.8rem;
font-weight: 500;
color: var(--slate-500);
cursor: pointer;
position: relative;
transition: color 0.2s;
border: none;
background: transparent;
white-space: nowrap;
}
.tab:hover {
color: var(--slate-700);
}
.tab.active {
color: var(--primary);
}
.tab.active::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 2px;
background: var(--primary);
border-radius: 1px;
}
/* Content Area */
.content-area {
padding: 1.25rem;
max-height: 600px;
overflow-y: auto;
}
/* Section */
.section {
margin-bottom: 1.5rem;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.875rem;
}
.section-title {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 1rem;
font-weight: 600;
color: var(--slate-800);
}
.section-badge {
font-size: 0.7rem;
background: var(--slate-100);
color: var(--slate-500);
padding: 0.3rem 0.75rem;
border-radius: 20px;
}
/* KRA Card */
.kra-card {
background: linear-gradient(135deg, var(--slate-50), white);
border: 1px solid var(--slate-200);
border-radius: 12px;
padding: 1.25rem;
margin-bottom: 0.875rem;
transition: box-shadow 0.3s;
}
.kra-card:hover {
box-shadow: 0 6px 20px rgba(0,0,0,0.06);
}
.kra-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 0.875rem;
}
.kra-badge {
font-size: 0.65rem;
font-weight: 600;
background: var(--primary-50);
color: var(--primary);
padding: 0.3rem 0.625rem;
border-radius: 5px;
}
.kra-title {
font-size: 0.925rem;
font-weight: 600;
margin-top: 0.375rem;
color: var(--slate-800);
}
.kra-weight {
font-size: 0.7rem;
color: var(--slate-500);
margin-top: 0.25rem;
}
.kra-status {
font-size: 0.75rem;
font-weight: 600;
color: var(--success);
}
.kra-output {
background: white;
border: 1px solid var(--slate-200);
border-radius: 8px;
padding: 0.875rem;
margin-bottom: 0.875rem;
}
.kra-output .label {
font-size: 0.7rem;
color: var(--slate-500);
margin-bottom: 0.25rem;
}
.kra-output .value {
font-size: 0.825rem;
color: var(--slate-700);
}
/* Dual Rating */
.rating-section {
background: var(--slate-50);
border-radius: 8px;
padding: 0.875rem;
}
.rating-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.rating-item {
background: white;
border-radius: 8px;
padding: 0.75rem;
border: 1px solid var(--slate-200);
}
.rating-item .header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.rating-item .type {
font-size: 0.7rem;
font-weight: 600;
padding: 0.2rem 0.5rem;
border-radius: 4px;
}
.rating-item .type.self {
background: var(--warning-50);
color: #b45309;
}
.rating-item .type.manager {
background: var(--success-50);
color: #047857;
}
.rating-item .percentage {
font-size: 1rem;
font-weight: 700;
}
.rating-item .percentage.self {
color: var(--warning);
}
.rating-item .percentage.manager {
color: var(--success);
}
.progress-track {
height: 8px;
background: var(--slate-200);
border-radius: 4px;
overflow: hidden;
}
.progress-track .fill {
height: 100%;
border-radius: 4px;
}
.progress-track .fill.amber {
background: linear-gradient(90deg, #fbbf24, #f59e0b);
}
.progress-track .fill.green {
background: linear-gradient(90deg, #34d399, #10b981);
}
/* Add Button */
.add-btn {
width: 100%;
padding: 0.875rem;
border: 2px dashed var(--slate-300);
border-radius: 10px;
background: transparent;
color: var(--slate-500);
font-size: 0.825rem;
cursor: pointer;
transition: all 0.2s;
}
.add-btn:hover {
border-color: var(--primary-light);
color: var(--primary);
background: var(--primary-50);
}
/* Competency Section */
.competency-card {
background: linear-gradient(135deg, var(--purple-50), white);
border: 1px solid #ddd6fe;
border-radius: 12px;
padding: 1.25rem;
}
.competency-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 0.75rem;
}
.competency-source {
font-size: 0.65rem;
background: #ede9fe;
color: #6d28d9;
padding: 0.2rem 0.5rem;
border-radius: 4px;
}
.competency-title {
font-size: 0.925rem;
font-weight: 600;
color: var(--slate-800);
margin-bottom: 0.375rem;
}
.competency-desc {
font-size: 0.8rem;
color: var(--slate-500);
margin-bottom: 1rem;
}
.level-section {
margin-bottom: 1rem;
}
.level-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.625rem;
}
.level-label {
font-size: 0.75rem;
font-weight: 500;
color: var(--slate-600);
}
.level-buttons {
display: flex;
gap: 0.375rem;
}
.level-btn {
flex: 1;
padding: 0.5rem 0.25rem;
border-radius: 8px;
font-size: 0.65rem;
font-weight: 500;
border: none;
cursor: pointer;
transition: all 0.2s;
background: var(--slate-100);
color: var(--slate-600);
}
.level-btn:hover {
background: var(--slate-200);
}
.level-btn.active {
background: linear-gradient(135deg, var(--purple), #8b5cf6);
color: white;
box-shadow: 0 3px 10px rgba(124, 58, 237, 0.3);
}
.level-btn.manager-active {
background: linear-gradient(135deg, var(--success), var(--success-light));
color: white;
box-shadow: 0 3px 10px rgba(5, 150, 105, 0.3);
}
.sbi-card {
background: white;
border: 1px solid var(--slate-200);
border-radius: 10px;
padding: 1rem;
}
.sbi-card .label {
font-size: 0.75rem;
color: var(--slate-500);
margin-bottom: 0.625rem;
}
.sbi-item {
font-size: 0.8rem;
margin-bottom: 0.375rem;
line-height: 1.5;
}
.sbi-item .tag {
font-weight: 600;
}
.sbi-item .tag.situation { color: var(--primary); }
.sbi-item .tag.behavior { color: var(--success); }
.sbi-item .tag.impact { color: var(--purple); }
/* Footer */
.panel-footer {
padding: 0.875rem 1.25rem;
background: var(--slate-50);
border-top: 1px solid var(--slate-200);
display: flex;
justify-content: space-between;
align-items: center;
}
.btn {
padding: 0.625rem 1.25rem;
border-radius: 8px;
font-size: 0.8rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
border: none;
display: inline-flex;
align-items: center;
gap: 0.375rem;
}
.btn-ghost {
background: transparent;
color: var(--slate-600);
}
.btn-ghost:hover {
background: var(--slate-200);
}
.btn-secondary {
background: var(--slate-200);
color: var(--slate-700);
}
.btn-secondary:hover {
background: var(--slate-300);
}
.btn-primary {
background: linear-gradient(135deg, var(--primary), var(--primary-light));
color: white;
box-shadow: 0 3px 10px rgba(30, 64, 175, 0.3);
}
.btn-primary:hover {
transform: translateY(-1px);
box-shadow: 0 5px 14px rgba(30, 64, 175, 0.35);
}
.btn-group {
display: flex;
gap: 0.625rem;
}
/* Right Panel - Dashboard */
.right-panel {
position: sticky;
top: 80px;
height: fit-content;
}
.dashboard-card {
background: white;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
border: 1px solid var(--slate-200);
margin-bottom: 1rem;
}
.dashboard-header {
padding: 0.875rem 1rem;
border-bottom: 1px solid var(--slate-200);
font-weight: 600;
font-size: 0.85rem;
color: var(--slate-700);
display: flex;
align-items: center;
gap: 0.5rem;
}
.dashboard-content {
padding: 1rem;
}
/* Distribution Chart */
.distribution-chart {
margin-bottom: 1rem;
}
.dist-row {
display: flex;
align-items: center;
margin-bottom: 0.625rem;
}
.dist-label {
width: 36px;
font-size: 0.75rem;
font-weight: 600;
}
.dist-label.a-plus { color: #059669; }
.dist-label.a { color: #10b981; }
.dist-label.b-plus { color: #3b82f6; }
.dist-label.b { color: #f59e0b; }
.dist-label.c { color: #ef4444; }
.dist-bar-container {
flex: 1;
height: 20px;
background: var(--slate-100);
border-radius: 4px;
overflow: hidden;
margin: 0 0.5rem;
position: relative;
}
.dist-bar {
height: 100%;
border-radius: 4px;
transition: width 0.5s ease;
}
.dist-bar.a-plus { background: linear-gradient(90deg, #059669, #10b981); }
.dist-bar.a { background: linear-gradient(90deg, #10b981, #34d399); }
.dist-bar.b-plus { background: linear-gradient(90deg, #3b82f6, #60a5fa); }
.dist-bar.b { background: linear-gradient(90deg, #f59e0b, #fbbf24); }
.dist-bar.c { background: linear-gradient(90deg, #ef4444, #f87171); }
.dist-suggest {
position: absolute;
right: 0;
top: 0;
bottom: 0;
border-left: 2px dashed var(--slate-400);
}
.dist-value {
width: 40px;
font-size: 0.75rem;
text-align: right;
color: var(--slate-600);
}
.suggest-note {
font-size: 0.7rem;
color: var(--slate-500);
text-align: center;
margin-top: 0.5rem;
}
/* Quick Stats */
.quick-stats {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.625rem;
}
.stat-item {
background: var(--slate-50);
border-radius: 10px;
padding: 0.875rem;
text-align: center;
}
.stat-value {
font-size: 1.375rem;
font-weight: 700;
color: var(--slate-800);
}
.stat-label {
font-size: 0.7rem;
color: var(--slate-500);
margin-top: 0.25rem;
}
/* Approval Queue */
.approval-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem;
background: var(--slate-50);
border-radius: 8px;
margin-bottom: 0.5rem;
}
.approval-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: var(--primary-50);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 600;
color: var(--primary);
}
.approval-info {
flex: 1;
}
.approval-name {
font-size: 0.8rem;
font-weight: 600;
color: var(--slate-700);
}
.approval-type {
font-size: 0.7rem;
color: var(--slate-500);
}
.approval-action {
font-size: 0.7rem;
color: var(--primary);
cursor: pointer;
}
/* Data Flow */
.data-flow {
background: white;
border-radius: 16px;
padding: 1.5rem;
margin-top: 1.5rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
border: 1px solid var(--slate-200);
}
.flow-title {
font-size: 0.9rem;
font-weight: 600;
color: var(--slate-700);
margin-bottom: 1rem;
text-align: center;
}
.flow-cards {
display: flex;
justify-content: center;
align-items: center;
gap: 0.375rem;
}
.flow-card {
background: linear-gradient(135deg, var(--slate-50), white);
border: 2px solid var(--slate-200);
border-radius: 10px;
padding: 0.75rem 1rem;
text-align: center;
transition: all 0.3s;
flex: 1;
max-width: 140px;
}
.flow-card:hover {
border-color: var(--primary-light);
transform: translateY(-2px);
}
.flow-card .icon {
font-size: 1.25rem;
margin-bottom: 0.375rem;
}
.flow-card .name {
font-weight: 600;
font-size: 0.8rem;
color: var(--slate-700);
}
.flow-card .desc {
font-size: 0.65rem;
color: var(--slate-500);
}
.flow-arrow {
font-size: 1.125rem;
color: var(--slate-400);
}
.flow-note {
text-align: center;
margin-top: 0.75rem;
font-size: 0.7rem;
color: var(--slate-500);
}
/* Responsive */
@media (max-width: 1280px) {
.content-grid {
grid-template-columns: 220px 1fr;
}
.right-panel {
display: none;
}
}
@media (max-width: 1024px) {
.content-grid {
grid-template-columns: 1fr;
}
.sidebar {
display: none;
}
.stepper {
padding: 0;
}
.stepper::before,
.stepper::after {
display: none;
}
.step-subtitle {
display: none;
}
}
@media (max-width: 640px) {
.panel-header-content {
flex-direction: column;
gap: 1rem;
}
.rating-row {
grid-template-columns: 1fr;
}
.level-buttons {
flex-wrap: wrap;
}
.level-btn {
flex: 1 1 45%;
}
.period-bar {
flex-direction: column;
gap: 0.75rem;
align-items: flex-start;
}
.flow-cards {
flex-direction: column;
}
.flow-arrow {
transform: rotate(90deg);
}
}
</style>
</head>
<body>
<!-- Header -->
<header class="header">
<div class="header-content">
<div class="logo-section">
<div class="logo-icon">🏆</div>
<div class="logo-text">
<h1>績效管理系統</h1>
<p>Performance Management System</p>
</div>
</div>
<div class="header-actions">
<div class="lang-switch">
<button class="lang-btn active" onclick="switchLang('zh')">繁中</button>
<button class="lang-btn" onclick="switchLang('en')">EN</button>
</div>
<button class="notification-btn">
<div class="notification-badge"></div>
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>
</svg>
</button>
<div class="user-profile">
<div class="user-avatar"></div>
<div class="user-info">
<div class="name">王小明</div>
<div class="role">產品部 PM</div>
</div>
</div>
</div>
</div>
</header>
<!-- Main Container -->
<div class="main-container">
<!-- Period Selector -->
<div class="period-bar">
<div class="period-selector">
<button class="period-btn" onclick="setPeriod('q1')">Q1 季度</button>
<button class="period-btn" onclick="setPeriod('q2')">Q2 季度</button>
<button class="period-btn" onclick="setPeriod('q3')">Q3 季度</button>
<button class="period-btn active" onclick="setPeriod('annual')">年度完整</button>
</div>
<div class="period-info">
評核期間:<strong>2024/01/01 - 2024/12/31</strong>
</div>
</div>
<!-- Progress Stepper -->
<div class="stepper-container">
<div class="stepper-header">
<h2>📋 2024 年度績效評核流程 - 單向連動</h2>
<span>整體進度65%</span>
</div>
<div class="stepper">
<div class="step completed">
<div class="step-icon">👤</div>
<div class="step-title">角色卡</div>
<div class="step-subtitle">Role Card</div>
<div class="step-status">已完成</div>
</div>
<div class="step completed">
<div class="step-icon">🎯</div>
<div class="step-title">能力卡</div>
<div class="step-subtitle">Competency</div>
<div class="step-status">已完成</div>
</div>
<div class="step active">
<div class="step-icon">📊</div>
<div class="step-title">績效卡</div>
<div class="step-subtitle">Performance</div>
<div class="step-status">進行中</div>
</div>
<div class="step pending">
<div class="step-icon">🌱</div>
<div class="step-title">成長卡</div>
<div class="step-subtitle">Growth</div>
<div class="step-status">待開始</div>
</div>
</div>
</div>
<!-- Content Grid -->
<div class="content-grid">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-card">
<div class="sidebar-header">📁 快速導航</div>
<nav class="sidebar-nav">
<button class="nav-item">
<div class="nav-icon">👤</div>
<span>角色卡</span>
<span class="nav-check"></span>
</button>
<button class="nav-item">
<div class="nav-icon">🎯</div>
<span>能力卡</span>
<span class="nav-check"></span>
</button>
<button class="nav-item active">
<div class="nav-icon">📊</div>
<span>績效卡</span>
</button>
<button class="nav-item">
<div class="nav-icon">🌱</div>
<span>成長卡</span>
</button>
<div class="sidebar-divider"></div>
<button class="nav-item">
<div class="nav-icon">📈</div>
<span>儀表板</span>
</button>
<button class="nav-item">
<div class="nav-icon">📅</div>
<span>歷史快照</span>
</button>
</nav>
</div>
</aside>
<!-- Main Panel -->
<main class="main-panel">
<div class="panel-header">
<div class="panel-header-content">
<div class="panel-title-section">
<div class="panel-icon">📊</div>
<div class="panel-title">
<h2>績效卡 - 2024年度完整評核</h2>
<p>Performance Review Form - Annual</p>
</div>
</div>
<div class="panel-meta">
<div class="meta-item">
<div class="label">目標佔比</div>
<div class="value">70%</div>
</div>
<div class="meta-item">
<div class="label">行為佔比</div>
<div class="value">30%</div>
</div>
<div class="meta-item">
<div class="label">完成度</div>
<div class="value">65%</div>
</div>
</div>
</div>
</div>
<!-- Tabs -->
<div class="tabs">
<button class="tab">基本資訊</button>
<button class="tab active">目標成果</button>
<button class="tab">行為評估</button>
<button class="tab">綜合評價</button>
<button class="tab">主管回饋</button>
</div>
<!-- Content -->
<div class="content-area">
<!-- Section 1: Goals & Outcomes -->
<div class="section">
<div class="section-header">
<h3 class="section-title">
🎯 一、目標與成果 (Goals & Outcomes)
</h3>
<span class="section-badge">佔比: 70%</span>
</div>
<!-- KRA Card 1 -->
<div class="kra-card">
<div class="kra-header">
<div>
<span class="kra-badge">KRA 1</span>
<h4 class="kra-title">產品上市時程</h4>
<p class="kra-weight">權重40%</p>
</div>
<span class="kra-status">✓ 已達成</span>
</div>
<div class="kra-output">
<div class="label">具體產出</div>
<div class="value">Q1 完成 MVP 開發Q2 Beta 測試上線,獲得 500+ 測試用戶回饋</div>
</div>
<div class="rating-section">
<div class="rating-row">
<div class="rating-item">
<div class="header">
<span class="type self">👤 自評</span>
<span class="percentage self">80%</span>
</div>
<div class="progress-track">
<div class="fill amber" style="width: 80%"></div>
</div>
</div>
<div class="rating-item">
<div class="header">
<span class="type manager">👔 主管評</span>
<span class="percentage manager">85%</span>
</div>
<div class="progress-track">
<div class="fill green" style="width: 85%"></div>
</div>
</div>
</div>
</div>
</div>
<!-- KRA Card 2 -->
<div class="kra-card">
<div class="kra-header">
<div>
<span class="kra-badge">KRA 2</span>
<h4 class="kra-title">客戶滿意度提升</h4>
<p class="kra-weight">權重30%</p>
</div>
<span class="kra-status">✓ 超額達成</span>
</div>
<div class="kra-output">
<div class="label">具體產出</div>
<div class="value">NPS 從 32 提升至 48目標 45客戶續約率達 92%</div>
</div>
<div class="rating-section">
<div class="rating-row">
<div class="rating-item">
<div class="header">
<span class="type self">👤 自評</span>
<span class="percentage self">100%</span>
</div>
<div class="progress-track">
<div class="fill green" style="width: 100%"></div>
</div>
</div>
<div class="rating-item">
<div class="header">
<span class="type manager">👔 主管評</span>
<span class="percentage manager">100%</span>
</div>
<div class="progress-track">
<div class="fill green" style="width: 100%"></div>
</div>
</div>
</div>
</div>
</div>
<button class="add-btn">+ 新增 KRA從角色卡連動</button>
</div>
<!-- Section 2: Behavior Assessment -->
<div class="section">
<div class="section-header">
<h3 class="section-title">
👥 二、行為與能力評估 (Behavior Assessment)
</h3>
<span class="section-badge">佔比: 30%</span>
</div>
<div class="competency-card">
<div class="competency-header">
<div>
<span class="competency-source">📚 職能字典</span>
<h4 class="competency-title">管理職能:橫向整合力</h4>
</div>
</div>
<p class="competency-desc">整合跨部門資源,建立有效協作關係,達成共同目標</p>
<!-- Self Assessment -->
<div class="level-section">
<div class="level-header">
<span class="level-label">👤 自評等級</span>
</div>
<div class="level-buttons">
<button class="level-btn">L1<br>初學新手</button>
<button class="level-btn">L2<br>基礎應用</button>
<button class="level-btn active">L3<br>獨立勝任</button>
<button class="level-btn">L4<br>精通深化</button>
<button class="level-btn">L5<br>專家引領</button>
</div>
</div>
<!-- Manager Assessment -->
<div class="level-section">
<div class="level-header">
<span class="level-label">👔 主管評等級</span>
</div>
<div class="level-buttons">
<button class="level-btn">L1<br>初學新手</button>
<button class="level-btn">L2<br>基礎應用</button>
<button class="level-btn">L3<br>獨立勝任</button>
<button class="level-btn manager-active">L4<br>精通深化</button>
<button class="level-btn">L5<br>專家引領</button>
</div>
</div>
<div class="sbi-card">
<div class="label">📝 評估舉例 (SBI 格式)</div>
<p class="sbi-item"><span class="tag situation">【情境 Situation】</span> 跨部門產品發布專案中</p>
<p class="sbi-item"><span class="tag behavior">【行為 Behavior】</span> 主動召開週會,建立共享進度看板,協調資源分配</p>
<p class="sbi-item"><span class="tag impact">【影響 Impact】</span> 專案準時上線,團隊協作效率提升 30%</p>
</div>
</div>
</div>
</div>
<!-- Footer -->
<div class="panel-footer">
<button class="btn btn-ghost">💾 暫存草稿</button>
<div class="btn-group">
<button class="btn btn-secondary">← 上一步</button>
<button class="btn btn-primary">送出審批 (主管) →</button>
</div>
</div>
</main>
<!-- Right Panel - Dashboard -->
<aside class="right-panel">
<!-- Distribution Card -->
<div class="dashboard-card">
<div class="dashboard-header">
📊 績效等級分佈(建議)
</div>
<div class="dashboard-content">
<div class="distribution-chart">
<div class="dist-row">
<span class="dist-label a-plus">A+</span>
<div class="dist-bar-container">
<div class="dist-bar a-plus" style="width: 8%"></div>
<div class="dist-suggest" style="right: 90%"></div>
</div>
<span class="dist-value">8%</span>
</div>
<div class="dist-row">
<span class="dist-label a">A</span>
<div class="dist-bar-container">
<div class="dist-bar a" style="width: 22%"></div>
<div class="dist-suggest" style="right: 75%"></div>
</div>
<span class="dist-value">22%</span>
</div>
<div class="dist-row">
<span class="dist-label b-plus">B+</span>
<div class="dist-bar-container">
<div class="dist-bar b-plus" style="width: 38%"></div>
<div class="dist-suggest" style="right: 60%"></div>
</div>
<span class="dist-value">38%</span>
</div>
<div class="dist-row">
<span class="dist-label b">B</span>
<div class="dist-bar-container">
<div class="dist-bar b" style="width: 24%"></div>
<div class="dist-suggest" style="right: 75%"></div>
</div>
<span class="dist-value">24%</span>
</div>
<div class="dist-row">
<span class="dist-label c">C</span>
<div class="dist-bar-container">
<div class="dist-bar c" style="width: 8%"></div>
<div class="dist-suggest" style="right: 90%"></div>
</div>
<span class="dist-value">8%</span>
</div>
</div>
<p class="suggest-note">虛線為建議分佈,不強制執行</p>
</div>
</div>
<!-- Quick Stats -->
<div class="dashboard-card">
<div class="dashboard-header">
📈 快速統計
</div>
<div class="dashboard-content">
<div class="quick-stats">
<div class="stat-item">
<div class="stat-value">87%</div>
<div class="stat-label">目標達成率</div>
</div>
<div class="stat-item">
<div class="stat-value">L3.5</div>
<div class="stat-label">能力平均</div>
</div>
<div class="stat-item">
<div class="stat-value">3</div>
<div class="stat-label">KRA 項目</div>
</div>
<div class="stat-item">
<div class="stat-value">5</div>
<div class="stat-label">職能項目</div>
</div>
</div>
</div>
</div>
<!-- Approval Queue -->
<div class="dashboard-card">
<div class="dashboard-header">
✅ 待審批清單
</div>
<div class="dashboard-content">
<div class="approval-item">
<div class="approval-avatar"></div>
<div class="approval-info">
<div class="approval-name">李小華</div>
<div class="approval-type">績效卡 - 年度</div>
</div>
<span class="approval-action">審批 →</span>
</div>
<div class="approval-item">
<div class="approval-avatar"></div>
<div class="approval-info">
<div class="approval-name">張大明</div>
<div class="approval-type">成長卡 - IDP</div>
</div>
<span class="approval-action">審批 →</span>
</div>
<div class="approval-item">
<div class="approval-avatar"></div>
<div class="approval-info">
<div class="approval-name">陳小美</div>
<div class="approval-type">角色卡 - 變更</div>
</div>
<span class="approval-action">審批 →</span>
</div>
</div>
</div>
</aside>
</div>
<!-- Data Flow Diagram -->
<div class="data-flow">
<h3 class="flow-title">🔄 四卡單向連動資料流</h3>
<div class="flow-cards">
<div class="flow-card">
<div class="icon">👤</div>
<div class="name">角色卡</div>
<div class="desc">職責・KRA/KPI</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-card">
<div class="icon">🎯</div>
<div class="name">能力卡</div>
<div class="desc">技能・熟練度</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-card">
<div class="icon">📊</div>
<div class="name">績效卡</div>
<div class="desc">達成・評分</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-card">
<div class="icon">🌱</div>
<div class="name">成長卡</div>
<div class="desc">IDP・追蹤</div>
</div>
</div>
<p class="flow-note">↺ 基本資訊自動帶入連動 週期保留快照歸檔</p>
</div>
</div>
<script>
// Language Switch
function switchLang(lang) {
document.querySelectorAll('.lang-btn').forEach(btn => {
btn.classList.remove('active');
});
event.target.classList.add('active');
if (lang === 'en') {
alert('English version coming in Phase 2!');
}
}
// Period Switch
function setPeriod(period) {
document.querySelectorAll('.period-btn').forEach(btn => {
btn.classList.remove('active');
});
event.target.classList.add('active');
const periodInfo = document.querySelector('.period-info strong');
if (period === 'annual') {
periodInfo.textContent = '2024/01/01 - 2024/12/31';
} else if (period === 'q1') {
periodInfo.textContent = '2024/01/01 - 2024/03/31';
} else if (period === 'q2') {
periodInfo.textContent = '2024/04/01 - 2024/06/30';
} else if (period === 'q3') {
periodInfo.textContent = '2024/07/01 - 2024/09/30';
}
}
// Tab switching
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', function() {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
this.classList.add('active');
});
});
// Level button selection
document.querySelectorAll('.level-section').forEach(section => {
section.querySelectorAll('.level-btn').forEach(btn => {
btn.addEventListener('click', function() {
const buttons = section.querySelectorAll('.level-btn');
const isManager = section.querySelector('.level-label').textContent.includes('主管');
buttons.forEach(b => {
b.classList.remove('active', 'manager-active');
});
if (isManager) {
this.classList.add('manager-active');
} else {
this.classList.add('active');
}
});
});
});
// Nav item selection
document.querySelectorAll('.nav-item').forEach(item => {
item.addEventListener('click', function() {
document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active'));
this.classList.add('active');
});
});
</script>
</body>
</html>