From 4c43c00e73be325995cc3aae87ca28a7fa4eb1ac Mon Sep 17 00:00:00 2001 From: kkfluous Date: Thu, 2 Apr 2026 17:02:50 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=BD=A6=E8=BE=86=E8=80=83=E6=A0=B8?= =?UTF-8?q?=E8=BF=BD=E8=B8=AAsheet=E7=BE=8E=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 达标月份绿底✓,未达标红底✗ - 累计达标/未达标加粗+颜色 - 有发放的行金底高亮 - 奖金池已发期数蓝底加粗,显示为"N/12"格式 - 车辆基本信息列浅灰底色区分 - 冻结首行+首列,支持滚动查看 - 开启自动筛选 - 优化列宽 Co-Authored-By: Claude Opus 4.6 (1M context) --- excel_writer.py | 108 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 87 insertions(+), 21 deletions(-) diff --git a/excel_writer.py b/excel_writer.py index 105086b..84ab24f 100644 --- a/excel_writer.py +++ b/excel_writer.py @@ -341,18 +341,33 @@ def write_salesperson_sheet(wb, person, dept, settle_month, D, G, month_data, ve def write_vehicle_tracking_sheet(wb, settle_month, G, master_vehicles, vehicle_payments, vehicle_info): ws = wb.create_sheet('车辆考核追踪') - wrap_align = Alignment(wrap_text=True, vertical='top') + from calc_engine import VEHICLE_TARGET_MAP - # 表头:每月用一列"业务员/里程/目标/达标"(多人换行) - headers = ['车牌号','车架号','归属公司','车型','考核目标','月度奖励金额'] + wrap_top = Alignment(wrap_text=True, vertical='top') + center_top = Alignment(horizontal='center', vertical='top') + # 颜色 + green_font = Font(color='006100') + green_fill = PatternFill(start_color='C6EFCE', end_color='C6EFCE', fill_type='solid') + red_font = Font(color='9C0006') + red_fill = PatternFill(start_color='FFC7CE', end_color='FFC7CE', fill_type='solid') + grey_fill = PatternFill(start_color='F2F2F2', end_color='F2F2F2', fill_type='solid') + blue_fill = PatternFill(start_color='DAEEF3', end_color='DAEEF3', fill_type='solid') + gold_fill = PatternFill(start_color='FFF2CC', end_color='FFF2CC', fill_type='solid') + + # 表头 + headers = ['车牌号','车架号','归属公司','车型','考核目标','月度奖励'] + month_start_col = len(headers) # 月度考核开始列(0-indexed) for m in range(1, settle_month+1): headers.append(f'{m}月考核明细') + cum_start_col = len(headers) if settle_month >= 2: headers += ['累计里程/目标','累计达标'] - headers += ['本月发放明细','累计已发期数','累计已发金额','剩余可发期数'] + pay_start_col = len(headers) + headers += ['本月发放明细','已发期数','已发金额','剩余期数'] WH(ws, headers) - from calc_engine import VEHICLE_TARGET_MAP, RULES + # 冻结首行+首列 + ws.freeze_panes = 'B2' rn=2 for mv in master_vehicles: @@ -360,7 +375,6 @@ def write_vehicle_tracking_sheet(wb, settle_month, G, master_vehicles, vehicle_p info=vehicle_info.get(plate, {}) pays=vehicle_payments.get(plate, []) - # 补全缺失的考核目标 target_name = info.get('考核目标','') monthly_bonus = info.get('月度奖励',0) if not target_name: @@ -374,46 +388,98 @@ def write_vehicle_tracking_sheet(wb, settle_month, G, master_vehicles, vehicle_p target_name or '',monthly_bonus or ''] cum_t=0; cum_a=0 + month_qualified = {} # {月份: bool} for m in range(1, settle_month+1): mgs=[(k,g) for k,g in G.get(m,{}).items() if k[0]==plate] if mgs: - # 每个(车牌,销售)组一行,换行显示 lines = [] + any_q = False for _,g in sorted(mgs, key=lambda x: x[0][1]): t=g['应考核']; a=g['实际'] cum_t+=t; cum_a+=a - q='达标' if g['有达标'] else '未达标' + q = g['有达标'] + if q: any_q = True sd = g['部门'].replace('业务','') if '业务' in g.get('部门','') else g.get('部门','') - lines.append(f"{sd}-{g['销售']}: {R(a,0)}/{R(t,0)} {q}") + lines.append(f"{sd}-{g['销售']}: {R(a,0)}/{R(t,0)} {'✓' if q else '✗'}") row.append('\n'.join(lines)) + month_qualified[m] = any_q else: row.append('') + month_qualified[m] = None # 无数据 + cum_q = False if settle_month>=2: - cum_q='达标' if (cum_a>=cum_t and cum_t>0) else '未达标' - row+=[f'{R(cum_a,0)}/{R(cum_t,0)}', cum_q] + cum_q = cum_a>=cum_t and cum_t>0 + row+=[f'{R(cum_a,0)}/{R(cum_t,0)}', '✓ 达标' if cum_q else '✗ 未达标'] - # 本月发放明细(多人多类型换行) + # 本月发放 tp=[p for p in pays if p['结算月']==settle_month] if tp: pay_lines = [] for p in sorted(tp, key=lambda x: x['业务员']): pd = p.get('部门','').replace('业务','') if '业务' in p.get('部门','') else p.get('部门','') - pay_lines.append(f"{pd}-{p['业务员']}: {R(p['金额'])}({p['类型']})") + pay_lines.append(f"{pd}-{p['业务员']}: {R(p['金额'])}元 ({p['类型']})") row.append('\n'.join(pay_lines)) else: row.append('') - # 奖金池(截至settle_month) pays_to_date=[p for p in pays if p['结算月']<=settle_month] tp_count=len(pays_to_date); tp_amt=sum(p['金额'] for p in pays_to_date) - row+=[tp_count,R(tp_amt) if tp_amt>0 else 0,12-tp_count] + row+=[f'{tp_count}/12', R(tp_amt) if tp_amt>0 else 0, 12-tp_count] WR(ws,rn,row) - # 对含换行的单元格设置自动换行 - for ci in range(len(row)): - cell = ws.cell(row=rn, column=ci+1) - if isinstance(cell.value, str) and '\n' in cell.value: - cell.alignment = wrap_align + + # --- 美化 --- + # 车辆基本信息列:浅灰底色 + for ci in range(1, month_start_col+1): + ws.cell(row=rn, column=ci).fill = grey_fill + + # 月度考核列:达标绿底/未达标红底 + for mi, m in enumerate(range(1, settle_month+1)): + col = month_start_col + mi + 1 + cell = ws.cell(row=rn, column=col) + cell.alignment = wrap_top + if month_qualified.get(m) == True: + cell.fill = green_fill; cell.font = green_font + elif month_qualified.get(m) == False: + cell.fill = red_fill; cell.font = red_font + + # 累计列 + if settle_month >= 2: + cum_cell = ws.cell(row=rn, column=cum_start_col+2) # 累计达标列 + if cum_q: + cum_cell.fill = green_fill; cum_cell.font = Font(bold=True, color='006100') + elif cum_t > 0: + cum_cell.fill = red_fill; cum_cell.font = Font(bold=True, color='9C0006') + + # 发放列:有发放金底 + pay_cell = ws.cell(row=rn, column=pay_start_col+1) + pay_cell.alignment = wrap_top + if tp: + pay_cell.fill = gold_fill + + # 奖金池:已发期数列 + period_cell = ws.cell(row=rn, column=pay_start_col+2) + period_cell.alignment = center_top + if tp_count > 0: + period_cell.fill = blue_fill; period_cell.font = Font(bold=True) + rn+=1 - AW(ws) + + # 列宽 + col_widths = {'A':12,'B':20,'C':20,'D':12,'E':20,'F':8} + for col_letter, w in col_widths.items(): + ws.column_dimensions[col_letter].width = w + # 月度考核列宽 + for mi in range(settle_month): + col_letter = chr(ord('G') + mi) + ws.column_dimensions[col_letter].width = 30 + # 剩余列自动 + remaining_start = ord('G') + settle_month + for i in range(8): + cl = chr(remaining_start + i) + if cl <= 'Z': + ws.column_dimensions[cl].width = 16 + + # 自动筛选 + ws.auto_filter.ref = f"A1:{chr(ord('A')+len(headers)-1)}1"