Browse Source

Fix: Optimize UI

iaun 3 months ago
parent
commit
f85ab69190

+ 25 - 3
backend/app/services/work_record_service.py

@@ -269,11 +269,13 @@ class WorkRecordService:
             Dictionary with yearly summary data including:
             - year: The year
             - persons: List of person summaries with monthly_earnings, yearly_total,
-                       settled_total, and unsettled_total
+                       settled_total, unsettled_total, monthly_settled, monthly_unsettled
             - monthly_totals: Array of 12 monthly totals across all persons
             - grand_total: Overall yearly total
             - settled_grand_total: Total settled earnings for the year
             - unsettled_grand_total: Total unsettled earnings for the year
+            - monthly_settled_totals: Array of 12 monthly settled totals
+            - monthly_unsettled_totals: Array of 12 monthly unsettled totals
         """
         # Calculate date range for the year
         start_date = date(year, 1, 1)
@@ -286,7 +288,7 @@ class WorkRecordService:
         ).join(Person).order_by(Person.name).all()
         
         # Calculate totals by person and month (same logic as ExportService)
-        # Also track settlement status per person
+        # Also track settlement status per person and per month
         person_monthly_totals = {}
         for record in work_records:
             person_id = record.person_id
@@ -298,33 +300,47 @@ class WorkRecordService:
                     'person_id': person_id,
                     'person_name': person_name,
                     'monthly_data': {m: 0.0 for m in range(1, 13)},
+                    'monthly_settled': {m: 0.0 for m in range(1, 13)},
+                    'monthly_unsettled': {m: 0.0 for m in range(1, 13)},
                     'settled_total': 0.0,
                     'unsettled_total': 0.0
                 }
             
             person_monthly_totals[person_id]['monthly_data'][month] += record.total_price
             
-            # Track settlement status
+            # Track settlement status per month
             if record.is_settled:
                 person_monthly_totals[person_id]['settled_total'] += record.total_price
+                person_monthly_totals[person_id]['monthly_settled'][month] += record.total_price
             else:
                 person_monthly_totals[person_id]['unsettled_total'] += record.total_price
+                person_monthly_totals[person_id]['monthly_unsettled'][month] += record.total_price
         
         # Build persons list sorted alphabetically by name
         persons = []
         monthly_grand_totals = [0.0] * 12
+        monthly_settled_totals = [0.0] * 12
+        monthly_unsettled_totals = [0.0] * 12
         settled_grand_total = 0.0
         unsettled_grand_total = 0.0
         
         for person_data in sorted(person_monthly_totals.values(), key=lambda x: x['person_name']):
             monthly_earnings = []
+            monthly_settled = []
+            monthly_unsettled = []
             yearly_total = 0.0
             
             for month in range(1, 13):
                 value = round(person_data['monthly_data'][month], 2)
+                settled_value = round(person_data['monthly_settled'][month], 2)
+                unsettled_value = round(person_data['monthly_unsettled'][month], 2)
                 monthly_earnings.append(value)
+                monthly_settled.append(settled_value)
+                monthly_unsettled.append(unsettled_value)
                 yearly_total += value
                 monthly_grand_totals[month - 1] += value
+                monthly_settled_totals[month - 1] += settled_value
+                monthly_unsettled_totals[month - 1] += unsettled_value
             
             settled_total = round(person_data['settled_total'], 2)
             unsettled_total = round(person_data['unsettled_total'], 2)
@@ -333,6 +349,8 @@ class WorkRecordService:
                 'person_id': person_data['person_id'],
                 'person_name': person_data['person_name'],
                 'monthly_earnings': monthly_earnings,
+                'monthly_settled': monthly_settled,
+                'monthly_unsettled': monthly_unsettled,
                 'yearly_total': round(yearly_total, 2),
                 'settled_total': settled_total,
                 'unsettled_total': unsettled_total
@@ -344,12 +362,16 @@ class WorkRecordService:
         
         # Round monthly totals
         monthly_totals = [round(total, 2) for total in monthly_grand_totals]
+        monthly_settled = [round(total, 2) for total in monthly_settled_totals]
+        monthly_unsettled = [round(total, 2) for total in monthly_unsettled_totals]
         grand_total = round(sum(monthly_totals), 2)
         
         return {
             'year': year,
             'persons': persons,
             'monthly_totals': monthly_totals,
+            'monthly_settled_totals': monthly_settled,
+            'monthly_unsettled_totals': monthly_unsettled,
             'grand_total': grand_total,
             'settled_grand_total': round(settled_grand_total, 2),
             'unsettled_grand_total': round(unsettled_grand_total, 2)

+ 74 - 22
frontend/src/components/Dashboard.jsx

@@ -1,4 +1,4 @@
-import { useState, useEffect } from 'react'
+import React, { useState, useEffect, useMemo } from 'react'
 import { useNavigate } from 'react-router-dom'
 import { Card, Row, Col, Statistic, Button, DatePicker, Table, Empty, Spin, message, Alert } from 'antd'
 import {
@@ -264,41 +264,79 @@ function Dashboard() {
 
   // Table columns for yearly summary
   const monthNames = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
+  
+  // Transform yearly summary data to split settled/unsettled into separate rows
+  const transformedYearlyData = useMemo(() => {
+    if (!yearlySummary?.persons) return []
+    const rows = []
+    yearlySummary.persons.forEach(person => {
+      // Settled row
+      rows.push({
+        key: `${person.person_id}-settled`,
+        person_id: person.person_id,
+        person_name: person.person_name,
+        settlement_type: '已结算',
+        is_unsettled: false,
+        monthly_data: person.monthly_settled || Array(12).fill(0),
+        row_total: person.settled_total || 0
+      })
+      // Unsettled row
+      rows.push({
+        key: `${person.person_id}-unsettled`,
+        person_id: person.person_id,
+        person_name: person.person_name,
+        settlement_type: '未结算',
+        is_unsettled: true,
+        monthly_data: person.monthly_unsettled || Array(12).fill(0),
+        row_total: person.unsettled_total || 0
+      })
+    })
+    return rows
+  }, [yearlySummary])
+
   const yearlySummaryColumns = [
     {
       title: '人员',
       dataIndex: 'person_name',
       key: 'person_name',
       fixed: 'left',
-      width: 100
+      width: 100,
+      onCell: (record) => ({
+        style: record.is_unsettled ? { backgroundColor: '#fff7e6' } : {}
+      })
     },
     {
       title: '结算状态',
-      key: 'settlement_status',
-      width: 200,
-      render: (_, record) => (
-        <div>
-          <div style={{ color: '#52c41a' }}>已结算: ¥{(record.settled_total || 0).toFixed(2)}</div>
-          <div style={{ color: '#fa8c16' }}>未结算: ¥{(record.unsettled_total || 0).toFixed(2)}</div>
-        </div>
+      dataIndex: 'settlement_type',
+      key: 'settlement_type',
+      width: 80,
+      render: (value, record) => (
+        <span style={{ color: record.is_unsettled ? '#fa8c16' : '#52c41a' }}>
+          {value}
+        </span>
       )
     },
     ...monthNames.map((name, index) => ({
       title: name,
-      dataIndex: ['monthly_earnings', index],
       key: `month_${index}`,
       align: 'right',
       width: 80,
-      render: (value) => value ? `¥${value.toFixed(2)}` : '¥0.00'
+      render: (_, record) => {
+        const value = record.monthly_data?.[index] || 0
+        return value ? `¥${value.toFixed(2)}` : '¥0.00'
+      }
     })),
     {
       title: '年度合计',
-      dataIndex: 'yearly_total',
-      key: 'yearly_total',
+      dataIndex: 'row_total',
+      key: 'row_total',
       align: 'right',
       fixed: 'right',
       width: 100,
-      render: (value) => <strong>¥{(value || 0).toFixed(2)}</strong>
+      render: (value) => <strong>¥{(value || 0).toFixed(2)}</strong>,
+      onCell: (record) => ({
+        style: record.is_unsettled ? { backgroundColor: '#fff7e6' } : {}
+      })
     }
   ]
 
@@ -589,11 +627,12 @@ function Dashboard() {
           {yearlySummary?.persons?.length > 0 ? (
             <Table
               columns={yearlySummaryColumns}
-              dataSource={yearlySummary.persons}
-              rowKey="person_id"
+              dataSource={transformedYearlyData}
+              rowKey="key"
               pagination={false}
               size="small"
               scroll={{ x: 1400 }}
+              rowClassName={(record) => record.is_unsettled ? 'unsettled-row' : ''}
               summary={() => (
                 <Table.Summary fixed>
                   <Table.Summary.Row>
@@ -601,18 +640,31 @@ function Dashboard() {
                       <strong>合计</strong>
                     </Table.Summary.Cell>
                     <Table.Summary.Cell index={1}>
-                      <div>
-                        <div style={{ color: '#52c41a' }}>已结算: ¥{(yearlySummary.settled_grand_total || 0).toFixed(2)}</div>
-                        <div style={{ color: '#fa8c16' }}>未结算: ¥{(yearlySummary.unsettled_grand_total || 0).toFixed(2)}</div>
-                      </div>
+                      <span style={{ color: '#52c41a' }}>已结算</span>
                     </Table.Summary.Cell>
-                    {(yearlySummary.monthly_totals || []).map((total, index) => (
+                    {(yearlySummary.monthly_settled_totals || []).map((total, index) => (
                       <Table.Summary.Cell key={index} index={index + 2} align="right">
                         <strong>¥{(total || 0).toFixed(2)}</strong>
                       </Table.Summary.Cell>
                     ))}
                     <Table.Summary.Cell index={14} align="right" fixed="right">
-                      <strong>¥{(yearlySummary.grand_total || 0).toFixed(2)}</strong>
+                      <strong>¥{(yearlySummary.settled_grand_total || 0).toFixed(2)}</strong>
+                    </Table.Summary.Cell>
+                  </Table.Summary.Row>
+                  <Table.Summary.Row className="unsettled-row">
+                    <Table.Summary.Cell index={0} fixed="left" style={{ backgroundColor: '#fff7e6' }}>
+                      <strong>合计</strong>
+                    </Table.Summary.Cell>
+                    <Table.Summary.Cell index={1}>
+                      <span style={{ color: '#fa8c16' }}>未结算</span>
+                    </Table.Summary.Cell>
+                    {(yearlySummary.monthly_unsettled_totals || []).map((total, index) => (
+                      <Table.Summary.Cell key={index} index={index + 2} align="right">
+                        <strong>¥{(total || 0).toFixed(2)}</strong>
+                      </Table.Summary.Cell>
+                    ))}
+                    <Table.Summary.Cell index={14} align="right" fixed="right" style={{ backgroundColor: '#fff7e6' }}>
+                      <strong>¥{(yearlySummary.unsettled_grand_total || 0).toFixed(2)}</strong>
                     </Table.Summary.Cell>
                   </Table.Summary.Row>
                 </Table.Summary>

+ 46 - 0
frontend/src/index.css

@@ -170,3 +170,49 @@ body {
 .unsettled-row:hover > td {
   background-color: #fff1d6 !important;
 }
+
+/* Fixed columns in unsettled rows need explicit background */
+.unsettled-row > td.ant-table-cell-fix-left,
+.unsettled-row > td.ant-table-cell-fix-right {
+  background-color: #fff7e6 !important;
+}
+
+.unsettled-row:hover > td.ant-table-cell-fix-left,
+.unsettled-row:hover > td.ant-table-cell-fix-right {
+  background-color: #fff1d6 !important;
+}
+
+/* Table summary row fixed columns */
+.ant-table-summary .unsettled-row > td.ant-table-cell-fix-left,
+.ant-table-summary .unsettled-row > td.ant-table-cell-fix-right,
+.ant-table-summary .unsettled-row > td {
+  background-color: #fff7e6 !important;
+}
+
+.ant-table-summary .unsettled-row:hover > td.ant-table-cell-fix-left,
+.ant-table-summary .unsettled-row:hover > td.ant-table-cell-fix-right,
+.ant-table-summary .unsettled-row:hover > td {
+  background-color: #fff1d6 !important;
+}
+
+/* Table summary row hover for all rows (including settled) */
+.ant-table-summary tr:hover > td {
+  background-color: #fafafa !important;
+}
+
+.ant-table-summary tr:hover > td.ant-table-cell-fix-left,
+.ant-table-summary tr:hover > td.ant-table-cell-fix-right {
+  background-color: #fafafa !important;
+}
+
+/* Override for unsettled summary row hover */
+.ant-table-summary tr.unsettled-row:hover > td,
+.ant-table-summary tr.unsettled-row:hover > td.ant-table-cell-fix-left,
+.ant-table-summary tr.unsettled-row:hover > td.ant-table-cell-fix-right {
+  background-color: #fff1d6 !important;
+}
+
+/* Table summary row transition */
+.ant-table-summary tr > td {
+  transition: background-color 0.2s ease;
+}