// performance.js — 绩效模块 window.renderPerformance = () => { const data = state.data; if (!data) return; // 确保预算和已发生数据已加载 if (!data._perfDataLoaded) { loadPerfData().then(() => { data._perfDataLoaded = true; renderPerformance(); }); return; } // ── 状态初始化 ── if (state.planYear == null) state.planYear = new Date().getFullYear(); const planYear = state.planYear; if (!['annual','quarterly','monthly'].includes(state.perfView)) state.perfView = 'quarterly'; if (state.perfQuarter === undefined) state.perfQuarter = Math.floor(new Date().getMonth() / 3); const nowD = new Date(); const activeQ = state.perfQuarter; const qRanges = [[1,2,3],[4,5,6],[7,8,9],[10,11,12]]; const activeMonths = qRanges[activeQ]; const qLabels = ['Q1','Q2','Q3','Q4']; // 当前聚合范围 let aggMonths = []; if (state.perfView === 'annual') { for (var mi = 1; mi <= 12; mi++) aggMonths.push(mi); } else if (state.perfView === 'quarterly') { aggMonths = qRanges[activeQ]; } else if (state.perfView === 'monthly') { const m = state.perfMonth; aggMonths = m ? [parseInt(m.substring(5))] : []; } // 月度选择器默认值 if (state.perfView === 'monthly' && !state.perfMonth) { state.perfMonth = planYear + '-' + String(nowD.getMonth() + 1).padStart(2, '0'); } // ── 数据源 ── const pfs = (data.projectFinances || []); // 已发生 → 完成情况 const planPfs = (data.planFinances || []); // 预算 → 目标 // ── 通用聚合(支持年份过滤 + 月度范围)── function sumByMonths(items, field, useSign, months) { let total = 0; items.forEach(p => { if (useSign) { const sm = p.sign_month || ''; const smY = parseInt(sm.substring(0, 4)) || 0; const smM = parseInt(sm.substring(5)) || 0; if (!months.includes(smM)) return; if (planYear !== 0 && smY && smY !== planYear) return; total += parseFloat(p.sign_amount || 0); } else { let bd = []; try { bd = JSON.parse(p.budget_data || '[]'); } catch (e) {} bd.forEach(b => { const m = parseInt((b.month || '').substring(5)) || 0; const y = parseInt((b.month || '').substring(0, 4)) || 0; if (!months.includes(m)) return; if (planYear !== 0 && y && y !== planYear) return; total += parseFloat(b[field] || 0); }); } }); return Math.round(total); } // ── 计算 ── const signActual = sumByMonths(pfs, '', true, aggMonths); const revActual = sumByMonths(pfs, 'rev', false, aggMonths); const grossActual = sumByMonths(pfs, 'gross', false, aggMonths); const paymentActual = sumByMonths(pfs, 'payment', false, aggMonths); const signPlan = sumByMonths(planPfs, '', true, aggMonths); const revPlan = sumByMonths(planPfs, 'rev', false, aggMonths); const grossPlan = sumByMonths(planPfs, 'gross', false, aggMonths); const paymentPlan = sumByMonths(planPfs, 'payment', false, aggMonths); const rows = [ { key: 'sign', label: '签约', complete: signActual, plan: signPlan, icon: 'file-text', defWeight: 10 }, { key: 'rev', label: '收入', complete: revActual, plan: revPlan, icon: 'dollar-sign', defWeight: 20 }, { key: 'gross', label: '毛利', complete: grossActual, plan: grossPlan, icon: 'trending-up', defWeight: 50 }, { key: 'payment', label: '回款', complete: paymentActual, plan: paymentPlan, icon: 'coins', defWeight: 20 }, ]; // 存储 key 基于当前视图模式 let storageKey = 'opc-perf-t'; // default annual if (state.perfView === 'quarterly') storageKey = 'opc-perf-Q' + (activeQ + 1); else if (state.perfView === 'monthly') storageKey = 'opc-perf-M' + (state.perfMonth || ''); const saved = JSON.parse(localStorage.getItem(storageKey) || '{}'); function val(key, field, def) { const r = saved[key] || {}; return r[field] !== undefined && r[field] !== '' ? Number(r[field]) : def; } function moneyWan(v) { if (Math.abs(v) >= 10000) return (v / 10000).toFixed(1) + '万'; return v.toLocaleString(); } let totalScore = 0; const tableRows = rows.map(r => { const target = r.plan; const weight = val(r.key, 'weight', r.defWeight); const score = target > 0 ? Math.round((r.complete / target) * weight * 10) / 10 : 0; const rate = target > 0 ? Math.round((r.complete / target) * 100) : 0; totalScore += score; return `
| 指标 | 目标(万) | 完成情况 | 完成率 | 权重 | 评分 |
|---|---|---|---|---|---|
| 合计 | — | — | — | 100 | ${totalScore.toFixed(1)} |