zhouhui.jiang

订单、出库 ,发票

Showing 53 changed files with 8494 additions and 93 deletions
...@@ -1370,6 +1370,934 @@ ...@@ -1370,6 +1370,934 @@
1370 1370
1371 --- 1371 ---
1372 1372
1373 +## 订单管理接口
1374 +
1375 +### 1. 分页查询订单列表
1376 +
1377 +**接口路径:** `GET /api/order/list`
1378 +**请求方法:** GET
1379 +**权限要求:** `order:list`
1380 +
1381 +**请求参数:**
1382 +
1383 +| 参数名 | 类型 | 必填 | 描述 | 示例 |
1384 +|--------|------|------|------|------|
1385 +| orderNo | String | 否 | 订单编号 | ORD-2024-001 |
1386 +| dealerCode | String | 否 | 经销商编码 | APL-DLR-001 |
1387 +| dealerName | String | 否 | 经销商名称 | 北京经销商 |
1388 +| deliveryStatus | Integer | 否 | 出库状态(0-未出库/1-已出库) | 1 |
1389 +| invoiceStatus | Integer | 否 | 开票状态(0-未开票/1-已开票) | 1 |
1390 +| rebateCalcFlag | Integer | 否 | 返利计算状态(0-未计算/1-已计算) | 1 |
1391 +| dataSource | String | 否 | 数据来源 | ERP系统 |
1392 +| verifyStatus | Integer | 否 | 数据验证状态(0-待验证/1-验证通过/2-验证失败) | 1 |
1393 +| orderStartDate | String | 否 | 订单开始日期 | 2024-01-01T00:00:00 |
1394 +| orderEndDate | String | 否 | 订单结束日期 | 2024-12-31T23:59:59 |
1395 +| minAmount | BigDecimal | 否 | 金额最小值 | 1000.00 |
1396 +| maxAmount | BigDecimal | 否 | 金额最大值 | 100000.00 |
1397 +| pageNum | Integer | 是 | 页码 | 1 |
1398 +| pageSize | Integer | 是 | 每页大小 | 10 |
1399 +
1400 +**响应示例:**
1401 +```json
1402 +{
1403 + "code": 200,
1404 + "message": "操作成功",
1405 + "data": {
1406 + "records": [
1407 + {
1408 + "orderId": 1,
1409 + "orderNo": "ORD-2024-001",
1410 + "dealerCode": "APL-DLR-001",
1411 + "dealerName": "北京经销商",
1412 + "orderDate": "2024-01-15T10:30:00",
1413 + "totalAmount": 119980.00,
1414 + "rebateAmount": 5999.00,
1415 + "deliveryStatus": 1,
1416 + "invoiceStatus": 1,
1417 + "rebateCalcFlag": 1,
1418 + "dataSource": "ERP系统",
1419 + "verifyStatus": 1,
1420 + "uploadTime": "2024-01-15T10:35:00",
1421 + "createBy": "admin",
1422 + "createTime": "2024-01-15T10:30:00",
1423 + "updateBy": "",
1424 + "updateTime": "2024-01-15T10:30:00"
1425 + }
1426 + ],
1427 + "total": 1,
1428 + "size": 10,
1429 + "current": 1,
1430 + "pages": 1
1431 + }
1432 +}
1433 +```
1434 +
1435 +### 2. 获取订单详情
1436 +
1437 +**接口路径:** `GET /api/order/{orderId}`
1438 +**请求方法:** GET
1439 +**权限要求:** `order:detail`
1440 +
1441 +**路径参数:**
1442 +
1443 +| 参数名 | 类型 | 必填 | 描述 |
1444 +|--------|------|------|------|
1445 +| orderId | Long | 是 | 订单ID |
1446 +
1447 +**响应示例:**
1448 +```json
1449 +{
1450 + "code": 200,
1451 + "message": "操作成功",
1452 + "data": {
1453 + "orderId": 1,
1454 + "orderNo": "ORD-2024-001",
1455 + "dealerCode": "APL-DLR-001",
1456 + "dealerName": "北京经销商",
1457 + "orderDate": "2024-01-15T10:30:00",
1458 + "totalAmount": 119980.00,
1459 + "rebateAmount": 5999.00,
1460 + "deliveryStatus": 1,
1461 + "invoiceStatus": 1,
1462 + "rebateCalcFlag": 1,
1463 + "dataSource": "ERP系统",
1464 + "verifyStatus": 1,
1465 + "uploadTime": "2024-01-15T10:35:00",
1466 + "createBy": "admin",
1467 + "createTime": "2024-01-15T10:30:00",
1468 + "updateBy": "",
1469 + "updateTime": "2024-01-15T10:30:00",
1470 + "orderItems": [
1471 + {
1472 + "itemId": 1,
1473 + "orderId": 1,
1474 + "orderNo": "ORD-2024-001",
1475 + "productCode": "APL-IP15-128G-BK",
1476 + "productName": "iPhone 15 128GB 黑色",
1477 + "productSpec": "128GB/黑色",
1478 + "productType": "手机",
1479 + "unitPrice": 5999.00,
1480 + "quantity": 20,
1481 + "itemAmount": 119980.00,
1482 + "rebateRate": 0.05,
1483 + "rebateAmount": 5999.00,
1484 + "createBy": "admin",
1485 + "createTime": "2024-01-15T10:30:00",
1486 + "updateBy": "",
1487 + "updateTime": "2024-01-15T10:30:00"
1488 + }
1489 + ]
1490 + }
1491 +}
1492 +```
1493 +
1494 +### 3. 新增订单
1495 +
1496 +**接口路径:** `POST /api/order`
1497 +**请求方法:** POST
1498 +**权限要求:** `order:add`
1499 +
1500 +**请求体参数:**
1501 +
1502 +| 参数名 | 类型 | 必填 | 描述 |
1503 +|--------|------|------|------|
1504 +| orderNo | String | 是 | 订单编号 |
1505 +| dealerCode | String | 是 | 经销商编码 |
1506 +| dealerName | String | 是 | 经销商名称 |
1507 +| orderDate | String | 是 | 订单创建日期 |
1508 +| totalAmount | BigDecimal | 是 | 订单总金额 |
1509 +| rebateAmount | BigDecimal | 否 | 订单返利金额 |
1510 +| deliveryStatus | Integer | 否 | 出库状态(0-未出库/1-已出库) |
1511 +| invoiceStatus | Integer | 否 | 开票状态(0-未开票/1-已开票) |
1512 +| rebateCalcFlag | Integer | 否 | 返利计算状态(0-未计算/1-已计算) |
1513 +| dataSource | String | 否 | 数据来源 |
1514 +| verifyStatus | Integer | 否 | 数据验证状态(0-待验证/1-验证通过/2-验证失败) |
1515 +| uploadTime | String | 否 | 数据上传时间 |
1516 +| orderItems | Array | 是 | 订单明细列表 |
1517 +
1518 +**订单明细参数:**
1519 +
1520 +| 参数名 | 类型 | 必填 | 描述 |
1521 +|--------|------|------|------|
1522 +| productCode | String | 是 | 产品编码 |
1523 +| productName | String | 是 | 产品名称 |
1524 +| productSpec | String | 否 | 产品规格 |
1525 +| productType | String | 否 | 产品类别 |
1526 +| unitPrice | BigDecimal | 是 | 产品单价 |
1527 +| quantity | Integer | 是 | 订购数量 |
1528 +| itemAmount | BigDecimal | 否 | 明细金额 |
1529 +| rebateRate | BigDecimal | 否 | 返利比例 |
1530 +| rebateAmount | BigDecimal | 否 | 返利金额 |
1531 +
1532 +**请求示例:**
1533 +```json
1534 +{
1535 + "orderNo": "ORD-2024-001",
1536 + "dealerCode": "APL-DLR-001",
1537 + "dealerName": "北京经销商",
1538 + "orderDate": "2024-01-15T10:30:00",
1539 + "totalAmount": 119980.00,
1540 + "rebateAmount": 5999.00,
1541 + "deliveryStatus": 0,
1542 + "invoiceStatus": 0,
1543 + "rebateCalcFlag": 0,
1544 + "dataSource": "ERP系统",
1545 + "verifyStatus": 0,
1546 + "orderItems": [
1547 + {
1548 + "productCode": "APL-IP15-128G-BK",
1549 + "productName": "iPhone 15 128GB 黑色",
1550 + "productSpec": "128GB/黑色",
1551 + "productType": "手机",
1552 + "unitPrice": 5999.00,
1553 + "quantity": 20,
1554 + "itemAmount": 119980.00,
1555 + "rebateRate": 0.05,
1556 + "rebateAmount": 5999.00
1557 + }
1558 + ]
1559 +}
1560 +```
1561 +
1562 +**响应示例:**
1563 +```json
1564 +{
1565 + "code": 200,
1566 + "message": "新增订单成功",
1567 + "data": null
1568 +}
1569 +```
1570 +
1571 +### 4. 修改订单
1572 +
1573 +**接口路径:** `POST /api/order/update`
1574 +**请求方法:** POST
1575 +**权限要求:** `order:edit`
1576 +
1577 +**请求体参数:**
1578 +
1579 +| 参数名 | 类型 | 必填 | 描述 |
1580 +|--------|------|------|------|
1581 +| orderId | Long | 是 | 订单ID |
1582 +| orderNo | String | 是 | 订单编号 |
1583 +| dealerCode | String | 是 | 经销商编码 |
1584 +| dealerName | String | 是 | 经销商名称 |
1585 +| orderDate | String | 是 | 订单创建日期 |
1586 +| totalAmount | BigDecimal | 是 | 订单总金额 |
1587 +| rebateAmount | BigDecimal | 否 | 订单返利金额 |
1588 +| deliveryStatus | Integer | 否 | 出库状态(0-未出库/1-已出库) |
1589 +| invoiceStatus | Integer | 否 | 开票状态(0-未开票/1-已开票) |
1590 +| rebateCalcFlag | Integer | 否 | 返利计算状态(0-未计算/1-已计算) |
1591 +| dataSource | String | 否 | 数据来源 |
1592 +| verifyStatus | Integer | 否 | 数据验证状态(0-待验证/1-验证通过/2-验证失败) |
1593 +| uploadTime | String | 否 | 数据上传时间 |
1594 +| orderItems | Array | 是 | 订单明细列表 |
1595 +
1596 +**响应示例:**
1597 +```json
1598 +{
1599 + "code": 200,
1600 + "message": "修改订单成功",
1601 + "data": null
1602 +}
1603 +```
1604 +
1605 +### 5. 删除订单
1606 +
1607 +**接口路径:** `DELETE /api/order/{orderId}`
1608 +**请求方法:** DELETE
1609 +**权限要求:** `order:delete`
1610 +
1611 +**路径参数:**
1612 +
1613 +| 参数名 | 类型 | 必填 | 描述 |
1614 +|--------|------|------|------|
1615 +| orderId | Long | 是 | 订单ID |
1616 +
1617 +**响应示例:**
1618 +```json
1619 +{
1620 + "code": 200,
1621 + "message": "删除订单成功",
1622 + "data": null
1623 +}
1624 +```
1625 +
1626 +### 6. 批量删除订单
1627 +
1628 +**接口路径:** `POST /api/order/batchDelete`
1629 +**请求方法:** POST
1630 +**权限要求:** `order:delete`
1631 +
1632 +**请求体参数:**
1633 +
1634 +| 参数名 | 类型 | 必填 | 描述 |
1635 +|--------|------|------|------|
1636 +| - | Array | 是 | 订单ID列表 |
1637 +
1638 +**请求示例:**
1639 +```json
1640 +[1, 2, 3]
1641 +```
1642 +
1643 +**响应示例:**
1644 +```json
1645 +{
1646 + "code": 200,
1647 + "message": "批量删除订单成功",
1648 + "data": null
1649 +}
1650 +```
1651 +
1652 +### 7. 修改订单出库状态
1653 +
1654 +**接口路径:** `POST /api/order/{orderId}/deliveryStatus`
1655 +**请求方法:** POST
1656 +**权限要求:** `order:edit`
1657 +
1658 +**路径参数:**
1659 +
1660 +| 参数名 | 类型 | 必填 | 描述 |
1661 +|--------|------|------|------|
1662 +| orderId | Long | 是 | 订单ID |
1663 +
1664 +**请求参数:**
1665 +
1666 +| 参数名 | 类型 | 必填 | 描述 |
1667 +|--------|------|------|------|
1668 +| deliveryStatus | Integer | 是 | 出库状态(0-未出库/1-已出库) |
1669 +
1670 +**响应示例:**
1671 +```json
1672 +{
1673 + "code": 200,
1674 + "message": "修改出库状态成功",
1675 + "data": null
1676 +}
1677 +```
1678 +
1679 +### 8. 修改订单开票状态
1680 +
1681 +**接口路径:** `POST /api/order/{orderId}/invoiceStatus`
1682 +**请求方法:** POST
1683 +**权限要求:** `order:edit`
1684 +
1685 +**路径参数:**
1686 +
1687 +| 参数名 | 类型 | 必填 | 描述 |
1688 +|--------|------|------|------|
1689 +| orderId | Long | 是 | 订单ID |
1690 +
1691 +**请求参数:**
1692 +
1693 +| 参数名 | 类型 | 必填 | 描述 |
1694 +|--------|------|------|------|
1695 +| invoiceStatus | Integer | 是 | 开票状态(0-未开票/1-已开票) |
1696 +
1697 +**响应示例:**
1698 +```json
1699 +{
1700 + "code": 200,
1701 + "message": "修改开票状态成功",
1702 + "data": null
1703 +}
1704 +```
1705 +
1706 +### 9. 修改订单返利计算状态
1707 +
1708 +**接口路径:** `POST /api/order/{orderId}/rebateCalcFlag`
1709 +**请求方法:** POST
1710 +**权限要求:** `order:edit`
1711 +
1712 +**路径参数:**
1713 +
1714 +| 参数名 | 类型 | 必填 | 描述 |
1715 +|--------|------|------|------|
1716 +| orderId | Long | 是 | 订单ID |
1717 +
1718 +**请求参数:**
1719 +
1720 +| 参数名 | 类型 | 必填 | 描述 |
1721 +|--------|------|------|------|
1722 +| rebateCalcFlag | Integer | 是 | 返利计算状态(0-未计算/1-已计算) |
1723 +
1724 +**响应示例:**
1725 +```json
1726 +{
1727 + "code": 200,
1728 + "message": "修改返利计算状态成功",
1729 + "data": null
1730 +}
1731 +```
1732 +
1733 +---
1734 +
1735 +## 出库管理接口
1736 +
1737 +### 1. 分页查询出库列表
1738 +
1739 +**接口路径:** `GET /api/delivery/list`
1740 +**请求方法:** GET
1741 +**权限要求:** `delivery:list`
1742 +
1743 +**请求参数:**
1744 +
1745 +| 参数名 | 类型 | 必填 | 描述 |
1746 +|--------|------|------|------|
1747 +| pageNum | Integer | 是 | 页码,从1开始 |
1748 +| pageSize | Integer | 是 | 每页大小 |
1749 +| deliveryNo | String | 否 | 出库单编号 |
1750 +| dealerCode | String | 否 | 经销商编码 |
1751 +| dealerName | String | 否 | 经销商名称 |
1752 +| orderNo | String | 否 | 关联订单编号 |
1753 +| deliveryStatus | Integer | 否 | 出库状态(0-未出库/1-已出库) |
1754 +| warehouseCode | String | 否 | 出库仓库编码 |
1755 +| dataSource | String | 否 | 数据来源 |
1756 +| deliveryStartDate | String | 否 | 出库开始日期(格式:yyyy-MM-dd HH:mm:ss) |
1757 +| deliveryEndDate | String | 否 | 出库结束日期(格式:yyyy-MM-dd HH:mm:ss) |
1758 +
1759 +**响应示例:**
1760 +```json
1761 +{
1762 + "code": 200,
1763 + "message": "查询成功",
1764 + "data": {
1765 + "records": [
1766 + {
1767 + "deliveryId": 1,
1768 + "deliveryNo": "DL202501270001",
1769 + "dealerCode": "DL001",
1770 + "dealerName": "北京经销商",
1771 + "orderNo": "ORD202501270001",
1772 + "deliveryDate": "2025-01-27 10:00:00",
1773 + "deliveryStatus": 1,
1774 + "warehouseCode": "WH001",
1775 + "dataSource": "系统录入",
1776 + "uploadTime": "2025-01-27 09:30:00",
1777 + "createBy": "admin",
1778 + "createTime": "2025-01-27 09:30:00",
1779 + "updateBy": "admin",
1780 + "updateTime": "2025-01-27 10:00:00",
1781 + "deliveryItems": [
1782 + {
1783 + "deliveryItemId": 1,
1784 + "deliveryId": 1,
1785 + "deliveryNo": "DL202501270001",
1786 + "orderNo": "ORD202501270001",
1787 + "productCode": "P001",
1788 + "productName": "iPhone 15",
1789 + "deliveryQty": 10,
1790 + "deliveryPrice": 5999.00,
1791 + "deliveryAmount": 59990.00,
1792 + "createBy": "admin",
1793 + "createTime": "2025-01-27 09:30:00",
1794 + "updateBy": "admin",
1795 + "updateTime": "2025-01-27 10:00:00"
1796 + }
1797 + ]
1798 + }
1799 + ],
1800 + "total": 1,
1801 + "size": 10,
1802 + "current": 1,
1803 + "pages": 1
1804 + }
1805 +}
1806 +```
1807 +
1808 +### 2. 获取出库详情
1809 +
1810 +**接口路径:** `GET /api/delivery/{deliveryId}`
1811 +**请求方法:** GET
1812 +**权限要求:** `delivery:detail`
1813 +
1814 +**路径参数:**
1815 +
1816 +| 参数名 | 类型 | 必填 | 描述 |
1817 +|--------|------|------|------|
1818 +| deliveryId | Long | 是 | 出库单ID |
1819 +
1820 +**响应示例:**
1821 +```json
1822 +{
1823 + "code": 200,
1824 + "message": "查询成功",
1825 + "data": {
1826 + "deliveryId": 1,
1827 + "deliveryNo": "DL202501270001",
1828 + "dealerCode": "DL001",
1829 + "dealerName": "北京经销商",
1830 + "orderNo": "ORD202501270001",
1831 + "deliveryDate": "2025-01-27 10:00:00",
1832 + "deliveryStatus": 1,
1833 + "warehouseCode": "WH001",
1834 + "dataSource": "系统录入",
1835 + "uploadTime": "2025-01-27 09:30:00",
1836 + "createBy": "admin",
1837 + "createTime": "2025-01-27 09:30:00",
1838 + "updateBy": "admin",
1839 + "updateTime": "2025-01-27 10:00:00",
1840 + "deliveryItems": [
1841 + {
1842 + "deliveryItemId": 1,
1843 + "deliveryId": 1,
1844 + "deliveryNo": "DL202501270001",
1845 + "orderNo": "ORD202501270001",
1846 + "productCode": "P001",
1847 + "productName": "iPhone 15",
1848 + "deliveryQty": 10,
1849 + "deliveryPrice": 5999.00,
1850 + "deliveryAmount": 59990.00,
1851 + "createBy": "admin",
1852 + "createTime": "2025-01-27 09:30:00",
1853 + "updateBy": "admin",
1854 + "updateTime": "2025-01-27 10:00:00"
1855 + }
1856 + ]
1857 + }
1858 +}
1859 +```
1860 +
1861 +### 3. 新增出库
1862 +
1863 +**接口路径:** `POST /api/delivery`
1864 +**请求方法:** POST
1865 +**权限要求:** `delivery:add`
1866 +
1867 +**请求体:**
1868 +```json
1869 +{
1870 + "deliveryNo": "DL202501270001",
1871 + "dealerCode": "DL001",
1872 + "dealerName": "北京经销商",
1873 + "orderNo": "ORD202501270001",
1874 + "deliveryDate": "2025-01-27 10:00:00",
1875 + "deliveryStatus": 0,
1876 + "warehouseCode": "WH001",
1877 + "dataSource": "系统录入",
1878 + "uploadTime": "2025-01-27 09:30:00",
1879 + "deliveryItems": [
1880 + {
1881 + "productCode": "P001",
1882 + "productName": "iPhone 15",
1883 + "deliveryQty": 10,
1884 + "deliveryPrice": 5999.00,
1885 + "deliveryAmount": 59990.00
1886 + }
1887 + ]
1888 +}
1889 +```
1890 +
1891 +**响应示例:**
1892 +```json
1893 +{
1894 + "code": 200,
1895 + "message": "新增出库单成功",
1896 + "data": null
1897 +}
1898 +```
1899 +
1900 +### 4. 修改出库
1901 +
1902 +**接口路径:** `POST /api/delivery/update`
1903 +**请求方法:** POST
1904 +**权限要求:** `delivery:edit`
1905 +
1906 +**请求体:**
1907 +```json
1908 +{
1909 + "deliveryId": 1,
1910 + "deliveryNo": "DL202501270001",
1911 + "dealerCode": "DL001",
1912 + "dealerName": "北京经销商",
1913 + "orderNo": "ORD202501270001",
1914 + "deliveryDate": "2025-01-27 10:00:00",
1915 + "deliveryStatus": 1,
1916 + "warehouseCode": "WH001",
1917 + "dataSource": "系统录入",
1918 + "uploadTime": "2025-01-27 09:30:00",
1919 + "deliveryItems": [
1920 + {
1921 + "deliveryItemId": 1,
1922 + "productCode": "P001",
1923 + "productName": "iPhone 15",
1924 + "deliveryQty": 15,
1925 + "deliveryPrice": 5999.00,
1926 + "deliveryAmount": 89985.00
1927 + }
1928 + ]
1929 +}
1930 +```
1931 +
1932 +**响应示例:**
1933 +```json
1934 +{
1935 + "code": 200,
1936 + "message": "修改出库单成功",
1937 + "data": null
1938 +}
1939 +```
1940 +
1941 +### 5. 删除出库
1942 +
1943 +**接口路径:** `DELETE /api/delivery/{deliveryId}`
1944 +**请求方法:** DELETE
1945 +**权限要求:** `delivery:delete`
1946 +
1947 +**路径参数:**
1948 +
1949 +| 参数名 | 类型 | 必填 | 描述 |
1950 +|--------|------|------|------|
1951 +| deliveryId | Long | 是 | 出库单ID |
1952 +
1953 +**响应示例:**
1954 +```json
1955 +{
1956 + "code": 200,
1957 + "message": "删除出库单成功",
1958 + "data": null
1959 +}
1960 +```
1961 +
1962 +### 6. 批量删除出库
1963 +
1964 +**接口路径:** `POST /api/delivery/batchDelete`
1965 +**请求方法:** POST
1966 +**权限要求:** `delivery:delete`
1967 +
1968 +**请求体:**
1969 +```json
1970 +[1, 2, 3]
1971 +```
1972 +
1973 +**响应示例:**
1974 +```json
1975 +{
1976 + "code": 200,
1977 + "message": "批量删除出库单成功",
1978 + "data": null
1979 +}
1980 +```
1981 +
1982 +### 7. 修改出库状态
1983 +
1984 +**接口路径:** `POST /api/delivery/{deliveryId}/deliveryStatus`
1985 +**请求方法:** POST
1986 +**权限要求:** `delivery:edit`
1987 +
1988 +**路径参数:**
1989 +
1990 +| 参数名 | 类型 | 必填 | 描述 |
1991 +|--------|------|------|------|
1992 +| deliveryId | Long | 是 | 出库单ID |
1993 +
1994 +**请求参数:**
1995 +
1996 +| 参数名 | 类型 | 必填 | 描述 |
1997 +|--------|------|------|------|
1998 +| deliveryStatus | Integer | 是 | 出库状态(0-未出库/1-已出库) |
1999 +
2000 +**响应示例:**
2001 +```json
2002 +{
2003 + "code": 200,
2004 + "message": "修改出库状态成功",
2005 + "data": null
2006 +}
2007 +```
2008 +
2009 +---
2010 +
2011 +## 发票管理接口
2012 +
2013 +### 1. 分页查询发票列表
2014 +
2015 +**接口路径:** `GET /api/invoice/list`
2016 +**请求方法:** GET
2017 +**权限要求:** `invoice:list`
2018 +
2019 +**请求参数:**
2020 +
2021 +| 参数名 | 类型 | 必填 | 描述 |
2022 +|--------|------|------|------|
2023 +| pageNum | Integer | 是 | 页码,从1开始 |
2024 +| pageSize | Integer | 是 | 每页大小 |
2025 +| invoiceNo | String | 否 | 发票编号 |
2026 +| orderNo | String | 否 | 关联订单编号 |
2027 +| deliveryNo | String | 否 | 关联出库单编号 |
2028 +| dealerCode | String | 否 | 经销商编码 |
2029 +| dealerName | String | 否 | 经销商名称 |
2030 +| invoiceStatus | Integer | 否 | 发票状态(0-未开票/1-已开票) |
2031 +| dataSource | String | 否 | 数据来源 |
2032 +| invoiceStartDate | String | 否 | 开票开始日期(格式:yyyy-MM-dd) |
2033 +| invoiceEndDate | String | 否 | 开票结束日期(格式:yyyy-MM-dd) |
2034 +| minAmount | BigDecimal | 否 | 金额最小值 |
2035 +| maxAmount | BigDecimal | 否 | 金额最大值 |
2036 +
2037 +**响应示例:**
2038 +```json
2039 +{
2040 + "code": 200,
2041 + "message": "查询成功",
2042 + "data": {
2043 + "records": [
2044 + {
2045 + "invoiceId": 1,
2046 + "invoiceNo": "INV202501270001",
2047 + "orderNo": "ORD202501270001",
2048 + "deliveryNo": "DL202501270001",
2049 + "dealerCode": "DL001",
2050 + "dealerName": "北京经销商",
2051 + "totalAmount": 67890.00,
2052 + "invoiceDate": "2025-01-27",
2053 + "invoiceStatus": 1,
2054 + "taxRate": 13.00,
2055 + "dataSource": "系统录入",
2056 + "uploadTime": "2025-01-27 09:30:00",
2057 + "createBy": "admin",
2058 + "createTime": "2025-01-27 09:30:00",
2059 + "updateBy": "admin",
2060 + "updateTime": "2025-01-27 10:00:00",
2061 + "invoiceItems": [
2062 + {
2063 + "invoiceItemId": 1,
2064 + "invoiceId": 1,
2065 + "invoiceNo": "INV202501270001",
2066 + "orderNo": "ORD202501270001",
2067 + "productCode": "P001",
2068 + "productName": "iPhone 15",
2069 + "invoiceQty": 10,
2070 + "unitPriceNoTax": 5309.73,
2071 + "amountNoTax": 53097.30,
2072 + "taxAmount": 6902.70,
2073 + "createBy": "admin",
2074 + "createTime": "2025-01-27 09:30:00",
2075 + "updateBy": "admin",
2076 + "updateTime": "2025-01-27 10:00:00"
2077 + }
2078 + ]
2079 + }
2080 + ],
2081 + "total": 1,
2082 + "size": 10,
2083 + "current": 1,
2084 + "pages": 1
2085 + }
2086 +}
2087 +```
2088 +
2089 +### 2. 获取发票详情
2090 +
2091 +**接口路径:** `GET /api/invoice/{invoiceId}`
2092 +**请求方法:** GET
2093 +**权限要求:** `invoice:detail`
2094 +
2095 +**路径参数:**
2096 +
2097 +| 参数名 | 类型 | 必填 | 描述 |
2098 +|--------|------|------|------|
2099 +| invoiceId | Long | 是 | 发票ID |
2100 +
2101 +**响应示例:**
2102 +```json
2103 +{
2104 + "code": 200,
2105 + "message": "查询成功",
2106 + "data": {
2107 + "invoiceId": 1,
2108 + "invoiceNo": "INV202501270001",
2109 + "orderNo": "ORD202501270001",
2110 + "deliveryNo": "DL202501270001",
2111 + "dealerCode": "DL001",
2112 + "dealerName": "北京经销商",
2113 + "totalAmount": 67890.00,
2114 + "invoiceDate": "2025-01-27",
2115 + "invoiceStatus": 1,
2116 + "taxRate": 13.00,
2117 + "dataSource": "系统录入",
2118 + "uploadTime": "2025-01-27 09:30:00",
2119 + "createBy": "admin",
2120 + "createTime": "2025-01-27 09:30:00",
2121 + "updateBy": "admin",
2122 + "updateTime": "2025-01-27 10:00:00",
2123 + "invoiceItems": [
2124 + {
2125 + "invoiceItemId": 1,
2126 + "invoiceId": 1,
2127 + "invoiceNo": "INV202501270001",
2128 + "orderNo": "ORD202501270001",
2129 + "productCode": "P001",
2130 + "productName": "iPhone 15",
2131 + "invoiceQty": 10,
2132 + "unitPriceNoTax": 5309.73,
2133 + "amountNoTax": 53097.30,
2134 + "taxAmount": 6902.70,
2135 + "createBy": "admin",
2136 + "createTime": "2025-01-27 09:30:00",
2137 + "updateBy": "admin",
2138 + "updateTime": "2025-01-27 10:00:00"
2139 + }
2140 + ]
2141 + }
2142 +}
2143 +```
2144 +
2145 +### 3. 新增发票
2146 +
2147 +**接口路径:** `POST /api/invoice`
2148 +**请求方法:** POST
2149 +**权限要求:** `invoice:add`
2150 +
2151 +**请求体:**
2152 +```json
2153 +{
2154 + "invoiceNo": "INV202501270001",
2155 + "orderNo": "ORD202501270001",
2156 + "deliveryNo": "DL202501270001",
2157 + "dealerCode": "DL001",
2158 + "dealerName": "北京经销商",
2159 + "totalAmount": 67890.00,
2160 + "invoiceDate": "2025-01-27",
2161 + "invoiceStatus": 0,
2162 + "taxRate": 13.00,
2163 + "dataSource": "系统录入",
2164 + "uploadTime": "2025-01-27 09:30:00",
2165 + "invoiceItems": [
2166 + {
2167 + "productCode": "P001",
2168 + "productName": "iPhone 15",
2169 + "invoiceQty": 10,
2170 + "unitPriceNoTax": 5309.73,
2171 + "amountNoTax": 53097.30,
2172 + "taxAmount": 6902.70
2173 + }
2174 + ]
2175 +}
2176 +```
2177 +
2178 +**响应示例:**
2179 +```json
2180 +{
2181 + "code": 200,
2182 + "message": "新增发票成功",
2183 + "data": null
2184 +}
2185 +```
2186 +
2187 +### 4. 修改发票
2188 +
2189 +**接口路径:** `POST /api/invoice/update`
2190 +**请求方法:** POST
2191 +**权限要求:** `invoice:edit`
2192 +
2193 +**请求体:**
2194 +```json
2195 +{
2196 + "invoiceId": 1,
2197 + "invoiceNo": "INV202501270001",
2198 + "orderNo": "ORD202501270001",
2199 + "deliveryNo": "DL202501270001",
2200 + "dealerCode": "DL001",
2201 + "dealerName": "北京经销商",
2202 + "totalAmount": 67890.00,
2203 + "invoiceDate": "2025-01-27",
2204 + "invoiceStatus": 1,
2205 + "taxRate": 13.00,
2206 + "dataSource": "系统录入",
2207 + "uploadTime": "2025-01-27 09:30:00",
2208 + "invoiceItems": [
2209 + {
2210 + "invoiceItemId": 1,
2211 + "productCode": "P001",
2212 + "productName": "iPhone 15",
2213 + "invoiceQty": 15,
2214 + "unitPriceNoTax": 5309.73,
2215 + "amountNoTax": 79645.95,
2216 + "taxAmount": 10353.05
2217 + }
2218 + ]
2219 +}
2220 +```
2221 +
2222 +**响应示例:**
2223 +```json
2224 +{
2225 + "code": 200,
2226 + "message": "修改发票成功",
2227 + "data": null
2228 +}
2229 +```
2230 +
2231 +### 5. 删除发票
2232 +
2233 +**接口路径:** `DELETE /api/invoice/{invoiceId}`
2234 +**请求方法:** DELETE
2235 +**权限要求:** `invoice:delete`
2236 +
2237 +**路径参数:**
2238 +
2239 +| 参数名 | 类型 | 必填 | 描述 |
2240 +|--------|------|------|------|
2241 +| invoiceId | Long | 是 | 发票ID |
2242 +
2243 +**响应示例:**
2244 +```json
2245 +{
2246 + "code": 200,
2247 + "message": "删除发票成功",
2248 + "data": null
2249 +}
2250 +```
2251 +
2252 +### 6. 批量删除发票
2253 +
2254 +**接口路径:** `POST /api/invoice/batchDelete`
2255 +**请求方法:** POST
2256 +**权限要求:** `invoice:delete`
2257 +
2258 +**请求体:**
2259 +```json
2260 +[1, 2, 3]
2261 +```
2262 +
2263 +**响应示例:**
2264 +```json
2265 +{
2266 + "code": 200,
2267 + "message": "批量删除发票成功",
2268 + "data": null
2269 +}
2270 +```
2271 +
2272 +### 7. 修改发票状态
2273 +
2274 +**接口路径:** `POST /api/invoice/{invoiceId}/invoiceStatus`
2275 +**请求方法:** POST
2276 +**权限要求:** `invoice:edit`
2277 +
2278 +**路径参数:**
2279 +
2280 +| 参数名 | 类型 | 必填 | 描述 |
2281 +|--------|------|------|------|
2282 +| invoiceId | Long | 是 | 发票ID |
2283 +
2284 +**请求参数:**
2285 +
2286 +| 参数名 | 类型 | 必填 | 描述 |
2287 +|--------|------|------|------|
2288 +| invoiceStatus | Integer | 是 | 发票状态(0-未开票/1-已开票) |
2289 +
2290 +**响应示例:**
2291 +```json
2292 +{
2293 + "code": 200,
2294 + "message": "修改发票状态成功",
2295 + "data": null
2296 +}
2297 +```
2298 +
2299 +---
2300 +
1373 **文档版本:** 1.0.0 2301 **文档版本:** 1.0.0
1374 **最后更新:** 2025-01-27 2302 **最后更新:** 2025-01-27
1375 **维护人员:** Apple ERP Team 2303 **维护人员:** Apple ERP Team
......
1 +package com.apple.erp.controller;
2 +
3 +import com.apple.erp.dto.DeliveryAddReq;
4 +import com.apple.erp.dto.DeliveryQueryReq;
5 +import com.apple.erp.dto.DeliveryRes;
6 +import com.apple.erp.dto.DeliveryUpdateReq;
7 +import com.apple.erp.service.DeliveryMainService;
8 +import com.apple.erp.dto.response.ApiRes;
9 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
10 +import io.swagger.v3.oas.annotations.Operation;
11 +import io.swagger.v3.oas.annotations.Parameter;
12 +import io.swagger.v3.oas.annotations.tags.Tag;
13 +import org.springframework.beans.factory.annotation.Autowired;
14 +import org.springframework.security.access.prepost.PreAuthorize;
15 +import org.springframework.validation.annotation.Validated;
16 +import org.springframework.web.bind.annotation.*;
17 +
18 +import javax.validation.Valid;
19 +import java.util.List;
20 +
21 +/**
22 + * 出库管理Controller
23 + *
24 + * @author Apple ERP System
25 + * @since 2025-01-01
26 + */
27 +@Tag(name = "出库管理", description = "出库管理相关接口")
28 +@RestController
29 +@RequestMapping("/api/delivery")
30 +@Validated
31 +public class DeliveryMainController {
32 +
33 + @Autowired
34 + private DeliveryMainService deliveryMainService;
35 +
36 + @Operation(summary = "分页查询出库列表", description = "根据查询条件分页获取出库列表")
37 + @GetMapping("/list")
38 + @PreAuthorize("hasAuthority('delivery:list')")
39 + public ApiRes<Page<DeliveryRes>> getDeliveryList(@Valid DeliveryQueryReq queryReq) {
40 + Page<DeliveryRes> page = deliveryMainService.getDeliveryList(queryReq);
41 + return ApiRes.success(page);
42 + }
43 +
44 + @Operation(summary = "获取出库详情", description = "根据出库单ID获取出库详细信息,包含出库明细")
45 + @GetMapping("/{deliveryId}")
46 + @PreAuthorize("hasAuthority('delivery:detail')")
47 + public ApiRes<DeliveryRes> getDeliveryDetail(
48 + @Parameter(description = "出库单ID", required = true) @PathVariable Long deliveryId) {
49 + DeliveryRes deliveryRes = deliveryMainService.getDeliveryDetail(deliveryId);
50 + if (deliveryRes != null) {
51 + return ApiRes.success(deliveryRes);
52 + }
53 + return ApiRes.error("出库单不存在或已删除");
54 + }
55 +
56 + @Operation(summary = "新增出库", description = "新增一个出库单,包含出库主表和明细")
57 + @PostMapping
58 + @PreAuthorize("hasAuthority('delivery:add')")
59 + public ApiRes<Void> addDelivery(@Valid @RequestBody DeliveryAddReq addReq) {
60 + try {
61 + boolean result = deliveryMainService.addDelivery(addReq);
62 + if (result) {
63 + return ApiRes.success("新增出库单成功", null);
64 + } else {
65 + return ApiRes.error("新增出库单失败");
66 + }
67 + } catch (Exception e) {
68 + return ApiRes.error("新增出库单失败: " + e.getMessage());
69 + }
70 + }
71 +
72 + @Operation(summary = "修改出库", description = "修改现有出库单信息,包含出库主表和明细")
73 + @PostMapping("/update")
74 + @PreAuthorize("hasAuthority('delivery:edit')")
75 + public ApiRes<Void> updateDelivery(@Valid @RequestBody DeliveryUpdateReq updateReq) {
76 + try {
77 + boolean result = deliveryMainService.updateDelivery(updateReq);
78 + if (result) {
79 + return ApiRes.success("修改出库单成功", null);
80 + } else {
81 + return ApiRes.error("修改出库单失败");
82 + }
83 + } catch (Exception e) {
84 + return ApiRes.error("修改出库单失败: " + e.getMessage());
85 + }
86 + }
87 +
88 + @Operation(summary = "删除出库", description = "根据出库单ID逻辑删除出库单")
89 + @DeleteMapping("/{deliveryId}")
90 + @PreAuthorize("hasAuthority('delivery:delete')")
91 + public ApiRes<Void> deleteDelivery(
92 + @Parameter(description = "出库单ID", required = true) @PathVariable Long deliveryId) {
93 + try {
94 + boolean result = deliveryMainService.deleteDelivery(deliveryId);
95 + if (result) {
96 + return ApiRes.success("删除出库单成功", null);
97 + } else {
98 + return ApiRes.error("删除出库单失败");
99 + }
100 + } catch (Exception e) {
101 + return ApiRes.error("删除出库单失败: " + e.getMessage());
102 + }
103 + }
104 +
105 + @Operation(summary = "批量删除出库", description = "根据出库单ID列表批量逻辑删除出库单")
106 + @PostMapping("/batchDelete")
107 + @PreAuthorize("hasAuthority('delivery:delete')")
108 + public ApiRes<Void> batchDeleteDeliveries(
109 + @Parameter(description = "出库单ID列表", required = true) @RequestBody List<Long> deliveryIds) {
110 + try {
111 + if (deliveryIds == null || deliveryIds.isEmpty()) {
112 + return ApiRes.error("出库单ID列表不能为空");
113 + }
114 + boolean result = deliveryMainService.batchDeleteDeliveries(deliveryIds);
115 + if (result) {
116 + return ApiRes.success("批量删除出库单成功", null);
117 + } else {
118 + return ApiRes.error("批量删除出库单失败");
119 + }
120 + } catch (Exception e) {
121 + return ApiRes.error("批量删除出库单失败: " + e.getMessage());
122 + }
123 + }
124 +
125 + @Operation(summary = "修改出库状态", description = "修改指定出库单的出库状态")
126 + @PostMapping("/{deliveryId}/deliveryStatus")
127 + @PreAuthorize("hasAuthority('delivery:edit')")
128 + public ApiRes<Void> updateDeliveryStatus(
129 + @Parameter(description = "出库单ID", required = true) @PathVariable Long deliveryId,
130 + @Parameter(description = "出库状态", required = true) @RequestParam Integer deliveryStatus) {
131 + try {
132 + boolean result = deliveryMainService.updateDeliveryStatus(deliveryId, deliveryStatus);
133 + if (result) {
134 + return ApiRes.success("修改出库状态成功", null);
135 + } else {
136 + return ApiRes.error("修改出库状态失败");
137 + }
138 + } catch (Exception e) {
139 + return ApiRes.error("修改出库状态失败: " + e.getMessage());
140 + }
141 + }
142 +}
143 +
1 +package com.apple.erp.controller;
2 +
3 +import com.apple.erp.dto.InvoiceAddReq;
4 +import com.apple.erp.dto.InvoiceQueryReq;
5 +import com.apple.erp.dto.InvoiceRes;
6 +import com.apple.erp.dto.InvoiceUpdateReq;
7 +import com.apple.erp.service.InvoiceMainService;
8 +import com.apple.erp.dto.response.ApiRes;
9 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
10 +import io.swagger.v3.oas.annotations.Operation;
11 +import io.swagger.v3.oas.annotations.Parameter;
12 +import io.swagger.v3.oas.annotations.tags.Tag;
13 +import org.springframework.beans.factory.annotation.Autowired;
14 +import org.springframework.security.access.prepost.PreAuthorize;
15 +import org.springframework.validation.annotation.Validated;
16 +import org.springframework.web.bind.annotation.*;
17 +
18 +import javax.validation.Valid;
19 +import java.util.List;
20 +
21 +/**
22 + * 发票管理Controller
23 + *
24 + * @author Apple ERP System
25 + * @since 2025-01-01
26 + */
27 +@Tag(name = "发票管理", description = "发票管理相关接口")
28 +@RestController
29 +@RequestMapping("/api/invoice")
30 +@Validated
31 +public class InvoiceMainController {
32 +
33 + @Autowired
34 + private InvoiceMainService invoiceMainService;
35 +
36 + @Operation(summary = "分页查询发票列表", description = "根据查询条件分页获取发票列表")
37 + @GetMapping("/list")
38 + @PreAuthorize("hasAuthority('invoice:list')")
39 + public ApiRes<Page<InvoiceRes>> getInvoiceList(@Valid InvoiceQueryReq queryReq) {
40 + Page<InvoiceRes> page = invoiceMainService.getInvoiceList(queryReq);
41 + return ApiRes.success(page);
42 + }
43 +
44 + @Operation(summary = "获取发票详情", description = "根据发票ID获取发票详细信息,包含发票明细")
45 + @GetMapping("/{invoiceId}")
46 + @PreAuthorize("hasAuthority('invoice:detail')")
47 + public ApiRes<InvoiceRes> getInvoiceDetail(
48 + @Parameter(description = "发票ID", required = true) @PathVariable Long invoiceId) {
49 + InvoiceRes invoiceRes = invoiceMainService.getInvoiceDetail(invoiceId);
50 + if (invoiceRes != null) {
51 + return ApiRes.success(invoiceRes);
52 + }
53 + return ApiRes.error("发票不存在或已删除");
54 + }
55 +
56 + @Operation(summary = "新增发票", description = "新增一个发票,包含发票主表和明细")
57 + @PostMapping
58 + @PreAuthorize("hasAuthority('invoice:add')")
59 + public ApiRes<Void> addInvoice(@Valid @RequestBody InvoiceAddReq addReq) {
60 + try {
61 + boolean result = invoiceMainService.addInvoice(addReq);
62 + if (result) {
63 + return ApiRes.success("新增发票成功", null);
64 + } else {
65 + return ApiRes.error("新增发票失败");
66 + }
67 + } catch (Exception e) {
68 + return ApiRes.error("新增发票失败: " + e.getMessage());
69 + }
70 + }
71 +
72 + @Operation(summary = "修改发票", description = "修改现有发票信息,包含发票主表和明细")
73 + @PostMapping("/update")
74 + @PreAuthorize("hasAuthority('invoice:edit')")
75 + public ApiRes<Void> updateInvoice(@Valid @RequestBody InvoiceUpdateReq updateReq) {
76 + try {
77 + boolean result = invoiceMainService.updateInvoice(updateReq);
78 + if (result) {
79 + return ApiRes.success("修改发票成功", null);
80 + } else {
81 + return ApiRes.error("修改发票失败");
82 + }
83 + } catch (Exception e) {
84 + return ApiRes.error("修改发票失败: " + e.getMessage());
85 + }
86 + }
87 +
88 + @Operation(summary = "删除发票", description = "根据发票ID逻辑删除发票")
89 + @DeleteMapping("/{invoiceId}")
90 + @PreAuthorize("hasAuthority('invoice:delete')")
91 + public ApiRes<Void> deleteInvoice(
92 + @Parameter(description = "发票ID", required = true) @PathVariable Long invoiceId) {
93 + try {
94 + boolean result = invoiceMainService.deleteInvoice(invoiceId);
95 + if (result) {
96 + return ApiRes.success("删除发票成功", null);
97 + } else {
98 + return ApiRes.error("删除发票失败");
99 + }
100 + } catch (Exception e) {
101 + return ApiRes.error("删除发票失败: " + e.getMessage());
102 + }
103 + }
104 +
105 + @Operation(summary = "批量删除发票", description = "根据发票ID列表批量逻辑删除发票")
106 + @PostMapping("/batchDelete")
107 + @PreAuthorize("hasAuthority('invoice:delete')")
108 + public ApiRes<Void> batchDeleteInvoices(
109 + @Parameter(description = "发票ID列表", required = true) @RequestBody List<Long> invoiceIds) {
110 + try {
111 + if (invoiceIds == null || invoiceIds.isEmpty()) {
112 + return ApiRes.error("发票ID列表不能为空");
113 + }
114 + boolean result = invoiceMainService.batchDeleteInvoices(invoiceIds);
115 + if (result) {
116 + return ApiRes.success("批量删除发票成功", null);
117 + } else {
118 + return ApiRes.error("批量删除发票失败");
119 + }
120 + } catch (Exception e) {
121 + return ApiRes.error("批量删除发票失败: " + e.getMessage());
122 + }
123 + }
124 +
125 + @Operation(summary = "修改发票状态", description = "修改指定发票的发票状态")
126 + @PostMapping("/{invoiceId}/invoiceStatus")
127 + @PreAuthorize("hasAuthority('invoice:edit')")
128 + public ApiRes<Void> updateInvoiceStatus(
129 + @Parameter(description = "发票ID", required = true) @PathVariable Long invoiceId,
130 + @Parameter(description = "发票状态", required = true) @RequestParam Integer invoiceStatus) {
131 + try {
132 + boolean result = invoiceMainService.updateInvoiceStatus(invoiceId, invoiceStatus);
133 + if (result) {
134 + return ApiRes.success("修改发票状态成功", null);
135 + } else {
136 + return ApiRes.error("修改发票状态失败");
137 + }
138 + } catch (Exception e) {
139 + return ApiRes.error("修改发票状态失败: " + e.getMessage());
140 + }
141 + }
142 +}
143 +
1 +package com.apple.erp.controller;
2 +
3 +import com.apple.erp.dto.OrderAddReq;
4 +import com.apple.erp.dto.OrderQueryReq;
5 +import com.apple.erp.dto.OrderRes;
6 +import com.apple.erp.dto.OrderUpdateReq;
7 +import com.apple.erp.service.OrderMainService;
8 +import com.apple.erp.dto.response.ApiRes;
9 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
10 +import io.swagger.v3.oas.annotations.Operation;
11 +import io.swagger.v3.oas.annotations.Parameter;
12 +import io.swagger.v3.oas.annotations.tags.Tag;
13 +import org.springframework.beans.factory.annotation.Autowired;
14 +import org.springframework.security.access.prepost.PreAuthorize;
15 +import org.springframework.validation.annotation.Validated;
16 +import org.springframework.web.bind.annotation.*;
17 +
18 +import javax.validation.Valid;
19 +import java.util.List;
20 +
21 +/**
22 + * 订单管理Controller
23 + *
24 + * @author Apple ERP System
25 + * @since 2025-01-01
26 + */
27 +@Tag(name = "订单管理", description = "订单管理相关接口")
28 +@RestController
29 +@RequestMapping("/order")
30 +@Validated
31 +public class OrderMainController {
32 +
33 + @Autowired
34 + private OrderMainService orderMainService;
35 +
36 + @Operation(summary = "分页查询订单列表", description = "根据条件分页查询订单列表")
37 + @GetMapping("/list")
38 + @PreAuthorize("hasAuthority('order:list')")
39 + public ApiRes<Page<OrderRes>> getOrderList(@Valid OrderQueryReq queryReq) {
40 + try {
41 + Page<OrderRes> result = orderMainService.getOrderList(queryReq);
42 + return ApiRes.success(result);
43 + } catch (Exception e) {
44 + return ApiRes.error("查询订单列表失败: " + e.getMessage());
45 + }
46 + }
47 +
48 + @Operation(summary = "获取订单详情", description = "根据订单ID获取订单详情")
49 + @GetMapping("/{orderId}")
50 + @PreAuthorize("hasAuthority('order:detail')")
51 + public ApiRes<OrderRes> getOrderDetail(
52 + @Parameter(description = "订单ID", required = true) @PathVariable Long orderId) {
53 + try {
54 + OrderRes result = orderMainService.getOrderDetail(orderId);
55 + if (result == null) {
56 + return ApiRes.error("订单不存在");
57 + }
58 + return ApiRes.success(result);
59 + } catch (Exception e) {
60 + return ApiRes.error("获取订单详情失败: " + e.getMessage());
61 + }
62 + }
63 +
64 + @Operation(summary = "新增订单", description = "新增订单信息")
65 + @PostMapping
66 + @PreAuthorize("hasAuthority('order:add')")
67 + public ApiRes<Void> addOrder(@Valid @RequestBody OrderAddReq addReq) {
68 + try {
69 + boolean result = orderMainService.addOrder(addReq);
70 + if (result) {
71 + return ApiRes.success("新增订单成功", null);
72 + } else {
73 + return ApiRes.error("新增订单失败");
74 + }
75 + } catch (Exception e) {
76 + return ApiRes.error("新增订单失败: " + e.getMessage());
77 + }
78 + }
79 +
80 + @Operation(summary = "修改订单", description = "修改订单信息")
81 + @PostMapping("/update")
82 + @PreAuthorize("hasAuthority('order:edit')")
83 + public ApiRes<Void> updateOrder(@Valid @RequestBody OrderUpdateReq updateReq) {
84 + try {
85 + boolean result = orderMainService.updateOrder(updateReq);
86 + if (result) {
87 + return ApiRes.success("修改订单成功", null);
88 + } else {
89 + return ApiRes.error("修改订单失败");
90 + }
91 + } catch (Exception e) {
92 + return ApiRes.error("修改订单失败: " + e.getMessage());
93 + }
94 + }
95 +
96 + @Operation(summary = "删除订单", description = "根据订单ID删除订单")
97 + @DeleteMapping("/{orderId}")
98 + @PreAuthorize("hasAuthority('order:delete')")
99 + public ApiRes<Void> deleteOrder(
100 + @Parameter(description = "订单ID", required = true) @PathVariable Long orderId) {
101 + try {
102 + boolean result = orderMainService.deleteOrder(orderId);
103 + if (result) {
104 + return ApiRes.success("删除订单成功", null);
105 + } else {
106 + return ApiRes.error("删除订单失败");
107 + }
108 + } catch (Exception e) {
109 + return ApiRes.error("删除订单失败: " + e.getMessage());
110 + }
111 + }
112 +
113 + @Operation(summary = "批量删除订单", description = "批量删除订单")
114 + @PostMapping("/batchDelete")
115 + @PreAuthorize("hasAuthority('order:delete')")
116 + public ApiRes<Void> batchDeleteOrders(@RequestBody List<Long> orderIds) {
117 + try {
118 + if (orderIds == null || orderIds.isEmpty()) {
119 + return ApiRes.error("订单ID列表不能为空");
120 + }
121 + boolean result = orderMainService.batchDeleteOrders(orderIds);
122 + if (result) {
123 + return ApiRes.success("批量删除订单成功", null);
124 + } else {
125 + return ApiRes.error("批量删除订单失败");
126 + }
127 + } catch (Exception e) {
128 + return ApiRes.error("批量删除订单失败: " + e.getMessage());
129 + }
130 + }
131 +
132 + @Operation(summary = "修改订单出库状态", description = "修改订单出库状态")
133 + @PostMapping("/{orderId}/deliveryStatus")
134 + @PreAuthorize("hasAuthority('order:edit')")
135 + public ApiRes<Void> updateDeliveryStatus(
136 + @Parameter(description = "订单ID", required = true) @PathVariable Long orderId,
137 + @Parameter(description = "出库状态", required = true) @RequestParam Integer deliveryStatus) {
138 + try {
139 + boolean result = orderMainService.updateDeliveryStatus(orderId, deliveryStatus);
140 + if (result) {
141 + return ApiRes.success("修改出库状态成功", null);
142 + } else {
143 + return ApiRes.error("修改出库状态失败");
144 + }
145 + } catch (Exception e) {
146 + return ApiRes.error("修改出库状态失败: " + e.getMessage());
147 + }
148 + }
149 +
150 + @Operation(summary = "修改订单开票状态", description = "修改订单开票状态")
151 + @PostMapping("/{orderId}/invoiceStatus")
152 + @PreAuthorize("hasAuthority('order:edit')")
153 + public ApiRes<Void> updateInvoiceStatus(
154 + @Parameter(description = "订单ID", required = true) @PathVariable Long orderId,
155 + @Parameter(description = "开票状态", required = true) @RequestParam Integer invoiceStatus) {
156 + try {
157 + boolean result = orderMainService.updateInvoiceStatus(orderId, invoiceStatus);
158 + if (result) {
159 + return ApiRes.success("修改开票状态成功", null);
160 + } else {
161 + return ApiRes.error("修改开票状态失败");
162 + }
163 + } catch (Exception e) {
164 + return ApiRes.error("修改开票状态失败: " + e.getMessage());
165 + }
166 + }
167 +
168 + @Operation(summary = "修改订单返利计算状态", description = "修改订单返利计算状态")
169 + @PostMapping("/{orderId}/rebateCalcFlag")
170 + @PreAuthorize("hasAuthority('order:edit')")
171 + public ApiRes<Void> updateRebateCalcFlag(
172 + @Parameter(description = "订单ID", required = true) @PathVariable Long orderId,
173 + @Parameter(description = "返利计算状态", required = true) @RequestParam Integer rebateCalcFlag) {
174 + try {
175 + boolean result = orderMainService.updateRebateCalcFlag(orderId, rebateCalcFlag);
176 + if (result) {
177 + return ApiRes.success("修改返利计算状态成功", null);
178 + } else {
179 + return ApiRes.error("修改返利计算状态失败");
180 + }
181 + } catch (Exception e) {
182 + return ApiRes.error("修改返利计算状态失败: " + e.getMessage());
183 + }
184 + }
185 +}
1 +package com.apple.erp.dto;
2 +
3 +import com.fasterxml.jackson.annotation.JsonFormat;
4 +import io.swagger.v3.oas.annotations.media.Schema;
5 +import lombok.Data;
6 +
7 +import javax.validation.Valid;
8 +import javax.validation.constraints.NotBlank;
9 +import javax.validation.constraints.NotNull;
10 +import javax.validation.constraints.Size;
11 +import java.time.LocalDateTime;
12 +import java.util.List;
13 +
14 +/**
15 + * 出库新增请求DTO
16 + *
17 + * @author Apple ERP System
18 + * @since 2025-01-01
19 + */
20 +@Data
21 +@Schema(description = "出库新增请求DTO")
22 +public class DeliveryAddReq {
23 +
24 + @NotBlank(message = "出库单编号不能为空")
25 + @Schema(description = "出库单编号", required = true)
26 + private String deliveryNo;
27 +
28 + @NotBlank(message = "经销商编码不能为空")
29 + @Schema(description = "经销商编码", required = true)
30 + private String dealerCode;
31 +
32 + @NotBlank(message = "经销商名称不能为空")
33 + @Schema(description = "经销商名称", required = true)
34 + private String dealerName;
35 +
36 + @NotBlank(message = "关联订单编号不能为空")
37 + @Schema(description = "关联订单编号", required = true)
38 + private String orderNo;
39 +
40 + @NotNull(message = "出库日期不能为空")
41 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
42 + @Schema(description = "出库日期", required = true)
43 + private LocalDateTime deliveryDate;
44 +
45 + @Schema(description = "出库状态(0-未出库/1-已出库)")
46 + private Integer deliveryStatus;
47 +
48 + @Schema(description = "出库仓库编码")
49 + private String warehouseCode;
50 +
51 + @Schema(description = "数据来源")
52 + private String dataSource;
53 +
54 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
55 + @Schema(description = "数据上传时间")
56 + private LocalDateTime uploadTime;
57 +
58 + @Valid
59 + @Size(min = 1, message = "出库明细不能为空")
60 + @Schema(description = "出库明细列表", required = true)
61 + private List<DeliveryItemAddReq> deliveryItems;
62 +}
63 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import javax.validation.constraints.NotBlank;
7 +import javax.validation.constraints.NotNull;
8 +import java.math.BigDecimal;
9 +
10 +/**
11 + * 出库明细新增请求DTO
12 + *
13 + * @author Apple ERP System
14 + * @since 2025-01-01
15 + */
16 +@Data
17 +@Schema(description = "出库明细新增请求DTO")
18 +public class DeliveryItemAddReq {
19 +
20 + @NotBlank(message = "产品编码不能为空")
21 + @Schema(description = "产品编码", required = true)
22 + private String productCode;
23 +
24 + @NotBlank(message = "产品名称不能为空")
25 + @Schema(description = "产品名称", required = true)
26 + private String productName;
27 +
28 + @NotNull(message = "出库数量不能为空")
29 + @Schema(description = "出库数量", required = true)
30 + private Integer deliveryQty;
31 +
32 + @NotNull(message = "出库单价不能为空")
33 + @Schema(description = "出库单价", required = true)
34 + private BigDecimal deliveryPrice;
35 +
36 + @Schema(description = "出库金额")
37 + private BigDecimal deliveryAmount;
38 +}
39 +
1 +package com.apple.erp.dto;
2 +
3 +import com.fasterxml.jackson.annotation.JsonFormat;
4 +import io.swagger.v3.oas.annotations.media.Schema;
5 +import lombok.Data;
6 +
7 +import java.math.BigDecimal;
8 +import java.time.LocalDateTime;
9 +
10 +/**
11 + * 出库明细响应DTO
12 + *
13 + * @author Apple ERP System
14 + * @since 2025-01-01
15 + */
16 +@Data
17 +@Schema(description = "出库明细响应DTO")
18 +public class DeliveryItemRes {
19 +
20 + @Schema(description = "出库明细ID")
21 + private Long deliveryItemId;
22 +
23 + @Schema(description = "关联出库单ID")
24 + private Long deliveryId;
25 +
26 + @Schema(description = "出库单编号")
27 + private String deliveryNo;
28 +
29 + @Schema(description = "关联订单编号")
30 + private String orderNo;
31 +
32 + @Schema(description = "产品编码")
33 + private String productCode;
34 +
35 + @Schema(description = "产品名称")
36 + private String productName;
37 +
38 + @Schema(description = "出库数量")
39 + private Integer deliveryQty;
40 +
41 + @Schema(description = "出库单价")
42 + private BigDecimal deliveryPrice;
43 +
44 + @Schema(description = "出库金额")
45 + private BigDecimal deliveryAmount;
46 +
47 + @Schema(description = "创建者")
48 + private String createBy;
49 +
50 + @Schema(description = "创建时间")
51 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
52 + private LocalDateTime createTime;
53 +
54 + @Schema(description = "更新者")
55 + private String updateBy;
56 +
57 + @Schema(description = "更新时间")
58 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
59 + private LocalDateTime updateTime;
60 +}
61 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import javax.validation.constraints.NotBlank;
7 +import javax.validation.constraints.NotNull;
8 +import java.math.BigDecimal;
9 +
10 +/**
11 + * 出库明细修改请求DTO
12 + *
13 + * @author Apple ERP System
14 + * @since 2025-01-01
15 + */
16 +@Data
17 +@Schema(description = "出库明细修改请求DTO")
18 +public class DeliveryItemUpdateReq {
19 +
20 + @Schema(description = "出库明细ID")
21 + private Long deliveryItemId; // 修改时可能需要,新增时不需要
22 +
23 + @NotBlank(message = "产品编码不能为空")
24 + @Schema(description = "产品编码", required = true)
25 + private String productCode;
26 +
27 + @NotBlank(message = "产品名称不能为空")
28 + @Schema(description = "产品名称", required = true)
29 + private String productName;
30 +
31 + @NotNull(message = "出库数量不能为空")
32 + @Schema(description = "出库数量", required = true)
33 + private Integer deliveryQty;
34 +
35 + @NotNull(message = "出库单价不能为空")
36 + @Schema(description = "出库单价", required = true)
37 + private BigDecimal deliveryPrice;
38 +
39 + @Schema(description = "出库金额")
40 + private BigDecimal deliveryAmount;
41 +}
42 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +/**
7 + * 出库查询请求DTO
8 + *
9 + * @author Apple ERP System
10 + * @since 2025-01-01
11 + */
12 +@Data
13 +@Schema(description = "出库查询请求DTO")
14 +public class DeliveryQueryReq {
15 +
16 + @Schema(description = "出库单编号")
17 + private String deliveryNo;
18 +
19 + @Schema(description = "经销商编码")
20 + private String dealerCode;
21 +
22 + @Schema(description = "经销商名称")
23 + private String dealerName;
24 +
25 + @Schema(description = "关联订单编号")
26 + private String orderNo;
27 +
28 + @Schema(description = "出库状态(0-未出库/1-已出库)")
29 + private Integer deliveryStatus;
30 +
31 + @Schema(description = "出库仓库编码")
32 + private String warehouseCode;
33 +
34 + @Schema(description = "数据来源")
35 + private String dataSource;
36 +
37 + @Schema(description = "出库开始日期")
38 + private String deliveryStartDate;
39 +
40 + @Schema(description = "出库结束日期")
41 + private String deliveryEndDate;
42 +
43 + @Schema(description = "页码")
44 + private Integer pageNum = 1;
45 +
46 + @Schema(description = "每页大小")
47 + private Integer pageSize = 10;
48 +}
1 +package com.apple.erp.dto;
2 +
3 +import com.fasterxml.jackson.annotation.JsonFormat;
4 +import io.swagger.v3.oas.annotations.media.Schema;
5 +import lombok.Data;
6 +
7 +import java.time.LocalDateTime;
8 +import java.util.List;
9 +
10 +/**
11 + * 出库响应DTO
12 + *
13 + * @author Apple ERP System
14 + * @since 2025-01-01
15 + */
16 +@Data
17 +@Schema(description = "出库响应DTO")
18 +public class DeliveryRes {
19 +
20 + @Schema(description = "出库单ID")
21 + private Long deliveryId;
22 +
23 + @Schema(description = "出库单编号")
24 + private String deliveryNo;
25 +
26 + @Schema(description = "经销商编码")
27 + private String dealerCode;
28 +
29 + @Schema(description = "经销商名称")
30 + private String dealerName;
31 +
32 + @Schema(description = "关联订单编号")
33 + private String orderNo;
34 +
35 + @Schema(description = "出库日期")
36 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
37 + private LocalDateTime deliveryDate;
38 +
39 + @Schema(description = "出库状态(0-未出库/1-已出库)")
40 + private Integer deliveryStatus;
41 +
42 + @Schema(description = "出库仓库编码")
43 + private String warehouseCode;
44 +
45 + @Schema(description = "数据来源")
46 + private String dataSource;
47 +
48 + @Schema(description = "数据上传时间")
49 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
50 + private LocalDateTime uploadTime;
51 +
52 + @Schema(description = "创建者")
53 + private String createBy;
54 +
55 + @Schema(description = "创建时间")
56 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
57 + private LocalDateTime createTime;
58 +
59 + @Schema(description = "更新者")
60 + private String updateBy;
61 +
62 + @Schema(description = "更新时间")
63 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
64 + private LocalDateTime updateTime;
65 +
66 + @Schema(description = "出库明细列表")
67 + private List<DeliveryItemRes> deliveryItems;
68 +}
69 +
1 +package com.apple.erp.dto;
2 +
3 +import com.fasterxml.jackson.annotation.JsonFormat;
4 +import io.swagger.v3.oas.annotations.media.Schema;
5 +import lombok.Data;
6 +
7 +import javax.validation.Valid;
8 +import javax.validation.constraints.NotBlank;
9 +import javax.validation.constraints.NotNull;
10 +import javax.validation.constraints.Size;
11 +import java.time.LocalDateTime;
12 +import java.util.List;
13 +
14 +/**
15 + * 出库修改请求DTO
16 + *
17 + * @author Apple ERP System
18 + * @since 2025-01-01
19 + */
20 +@Data
21 +@Schema(description = "出库修改请求DTO")
22 +public class DeliveryUpdateReq {
23 +
24 + @NotNull(message = "出库单ID不能为空")
25 + @Schema(description = "出库单ID", required = true)
26 + private Long deliveryId;
27 +
28 + @NotBlank(message = "出库单编号不能为空")
29 + @Schema(description = "出库单编号", required = true)
30 + private String deliveryNo;
31 +
32 + @NotBlank(message = "经销商编码不能为空")
33 + @Schema(description = "经销商编码", required = true)
34 + private String dealerCode;
35 +
36 + @NotBlank(message = "经销商名称不能为空")
37 + @Schema(description = "经销商名称", required = true)
38 + private String dealerName;
39 +
40 + @NotBlank(message = "关联订单编号不能为空")
41 + @Schema(description = "关联订单编号", required = true)
42 + private String orderNo;
43 +
44 + @NotNull(message = "出库日期不能为空")
45 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
46 + @Schema(description = "出库日期", required = true)
47 + private LocalDateTime deliveryDate;
48 +
49 + @Schema(description = "出库状态(0-未出库/1-已出库)")
50 + private Integer deliveryStatus;
51 +
52 + @Schema(description = "出库仓库编码")
53 + private String warehouseCode;
54 +
55 + @Schema(description = "数据来源")
56 + private String dataSource;
57 +
58 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
59 + @Schema(description = "数据上传时间")
60 + private LocalDateTime uploadTime;
61 +
62 + @Valid
63 + @Size(min = 1, message = "出库明细不能为空")
64 + @Schema(description = "出库明细列表", required = true)
65 + private List<DeliveryItemUpdateReq> deliveryItems;
66 +}
67 +
1 +package com.apple.erp.dto;
2 +
3 +import com.fasterxml.jackson.annotation.JsonFormat;
4 +import io.swagger.v3.oas.annotations.media.Schema;
5 +import lombok.Data;
6 +
7 +import javax.validation.Valid;
8 +import javax.validation.constraints.NotBlank;
9 +import javax.validation.constraints.NotNull;
10 +import javax.validation.constraints.Size;
11 +import java.math.BigDecimal;
12 +import java.time.LocalDate;
13 +import java.time.LocalDateTime;
14 +import java.util.List;
15 +
16 +/**
17 + * 发票新增请求DTO
18 + *
19 + * @author Apple ERP System
20 + * @since 2025-01-01
21 + */
22 +@Data
23 +@Schema(description = "发票新增请求DTO")
24 +public class InvoiceAddReq {
25 +
26 + @NotBlank(message = "发票编号不能为空")
27 + @Schema(description = "发票编号", required = true)
28 + private String invoiceNo;
29 +
30 + @NotBlank(message = "关联订单编号不能为空")
31 + @Schema(description = "关联订单编号", required = true)
32 + private String orderNo;
33 +
34 + @Schema(description = "关联出库单编号")
35 + private String deliveryNo;
36 +
37 + @NotBlank(message = "经销商编码不能为空")
38 + @Schema(description = "经销商编码", required = true)
39 + private String dealerCode;
40 +
41 + @NotBlank(message = "经销商名称不能为空")
42 + @Schema(description = "经销商名称", required = true)
43 + private String dealerName;
44 +
45 + @NotNull(message = "发票总金额不能为空")
46 + @Schema(description = "发票总金额(含税)", required = true)
47 + private BigDecimal totalAmount;
48 +
49 + @NotNull(message = "开票日期不能为空")
50 + @JsonFormat(pattern = "yyyy-MM-dd")
51 + @Schema(description = "开票日期", required = true)
52 + private LocalDate invoiceDate;
53 +
54 + @Schema(description = "发票状态(0-未开票/1-已开票)")
55 + private Integer invoiceStatus;
56 +
57 + @Schema(description = "税率")
58 + private BigDecimal taxRate;
59 +
60 + @Schema(description = "数据来源")
61 + private String dataSource;
62 +
63 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
64 + @Schema(description = "数据上传时间")
65 + private LocalDateTime uploadTime;
66 +
67 + @Valid
68 + @Size(min = 1, message = "发票明细不能为空")
69 + @Schema(description = "发票明细列表", required = true)
70 + private List<InvoiceItemAddReq> invoiceItems;
71 +}
72 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import javax.validation.constraints.NotBlank;
7 +import javax.validation.constraints.NotNull;
8 +import java.math.BigDecimal;
9 +
10 +/**
11 + * 发票明细新增请求DTO
12 + *
13 + * @author Apple ERP System
14 + * @since 2025-01-01
15 + */
16 +@Data
17 +@Schema(description = "发票明细新增请求DTO")
18 +public class InvoiceItemAddReq {
19 +
20 + @NotBlank(message = "产品编码不能为空")
21 + @Schema(description = "产品编码", required = true)
22 + private String productCode;
23 +
24 + @NotBlank(message = "产品名称不能为空")
25 + @Schema(description = "产品名称", required = true)
26 + private String productName;
27 +
28 + @NotNull(message = "开票数量不能为空")
29 + @Schema(description = "开票数量", required = true)
30 + private Integer invoiceQty;
31 +
32 + @NotNull(message = "不含税单价不能为空")
33 + @Schema(description = "不含税单价", required = true)
34 + private BigDecimal unitPriceNoTax;
35 +
36 + @NotNull(message = "不含税明细金额不能为空")
37 + @Schema(description = "不含税明细金额", required = true)
38 + private BigDecimal amountNoTax;
39 +
40 + @NotNull(message = "税额不能为空")
41 + @Schema(description = "税额", required = true)
42 + private BigDecimal taxAmount;
43 +}
44 +
1 +package com.apple.erp.dto;
2 +
3 +import com.fasterxml.jackson.annotation.JsonFormat;
4 +import io.swagger.v3.oas.annotations.media.Schema;
5 +import lombok.Data;
6 +
7 +import java.math.BigDecimal;
8 +import java.time.LocalDateTime;
9 +
10 +/**
11 + * 发票明细响应DTO
12 + *
13 + * @author Apple ERP System
14 + * @since 2025-01-01
15 + */
16 +@Data
17 +@Schema(description = "发票明细响应DTO")
18 +public class InvoiceItemRes {
19 +
20 + @Schema(description = "发票明细ID")
21 + private Long invoiceItemId;
22 +
23 + @Schema(description = "关联发票ID")
24 + private Long invoiceId;
25 +
26 + @Schema(description = "发票编号")
27 + private String invoiceNo;
28 +
29 + @Schema(description = "关联订单编号")
30 + private String orderNo;
31 +
32 + @Schema(description = "产品编码")
33 + private String productCode;
34 +
35 + @Schema(description = "产品名称")
36 + private String productName;
37 +
38 + @Schema(description = "开票数量")
39 + private Integer invoiceQty;
40 +
41 + @Schema(description = "不含税单价")
42 + private BigDecimal unitPriceNoTax;
43 +
44 + @Schema(description = "不含税明细金额")
45 + private BigDecimal amountNoTax;
46 +
47 + @Schema(description = "税额")
48 + private BigDecimal taxAmount;
49 +
50 + @Schema(description = "创建者")
51 + private String createBy;
52 +
53 + @Schema(description = "创建时间")
54 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
55 + private LocalDateTime createTime;
56 +
57 + @Schema(description = "更新者")
58 + private String updateBy;
59 +
60 + @Schema(description = "更新时间")
61 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
62 + private LocalDateTime updateTime;
63 +}
64 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import javax.validation.constraints.NotBlank;
7 +import javax.validation.constraints.NotNull;
8 +import java.math.BigDecimal;
9 +
10 +/**
11 + * 发票明细修改请求DTO
12 + *
13 + * @author Apple ERP System
14 + * @since 2025-01-01
15 + */
16 +@Data
17 +@Schema(description = "发票明细修改请求DTO")
18 +public class InvoiceItemUpdateReq {
19 +
20 + @Schema(description = "发票明细ID")
21 + private Long invoiceItemId; // 修改时可能需要,新增时不需要
22 +
23 + @NotBlank(message = "产品编码不能为空")
24 + @Schema(description = "产品编码", required = true)
25 + private String productCode;
26 +
27 + @NotBlank(message = "产品名称不能为空")
28 + @Schema(description = "产品名称", required = true)
29 + private String productName;
30 +
31 + @NotNull(message = "开票数量不能为空")
32 + @Schema(description = "开票数量", required = true)
33 + private Integer invoiceQty;
34 +
35 + @NotNull(message = "不含税单价不能为空")
36 + @Schema(description = "不含税单价", required = true)
37 + private BigDecimal unitPriceNoTax;
38 +
39 + @NotNull(message = "不含税明细金额不能为空")
40 + @Schema(description = "不含税明细金额", required = true)
41 + private BigDecimal amountNoTax;
42 +
43 + @NotNull(message = "税额不能为空")
44 + @Schema(description = "税额", required = true)
45 + private BigDecimal taxAmount;
46 +}
47 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import java.math.BigDecimal;
7 +
8 +/**
9 + * 发票查询请求DTO
10 + *
11 + * @author Apple ERP System
12 + * @since 2025-01-01
13 + */
14 +@Data
15 +@Schema(description = "发票查询请求DTO")
16 +public class InvoiceQueryReq {
17 +
18 + @Schema(description = "发票编号")
19 + private String invoiceNo;
20 +
21 + @Schema(description = "关联订单编号")
22 + private String orderNo;
23 +
24 + @Schema(description = "关联出库单编号")
25 + private String deliveryNo;
26 +
27 + @Schema(description = "经销商编码")
28 + private String dealerCode;
29 +
30 + @Schema(description = "经销商名称")
31 + private String dealerName;
32 +
33 + @Schema(description = "发票状态(0-未开票/1-已开票)")
34 + private Integer invoiceStatus;
35 +
36 + @Schema(description = "数据来源")
37 + private String dataSource;
38 +
39 + @Schema(description = "开票开始日期")
40 + private String invoiceStartDate;
41 +
42 + @Schema(description = "开票结束日期")
43 + private String invoiceEndDate;
44 +
45 + @Schema(description = "金额最小值")
46 + private BigDecimal minAmount;
47 +
48 + @Schema(description = "金额最大值")
49 + private BigDecimal maxAmount;
50 +
51 + @Schema(description = "页码")
52 + private Integer pageNum = 1;
53 +
54 + @Schema(description = "每页大小")
55 + private Integer pageSize = 10;
56 +}
57 +
1 +package com.apple.erp.dto;
2 +
3 +import com.fasterxml.jackson.annotation.JsonFormat;
4 +import io.swagger.v3.oas.annotations.media.Schema;
5 +import lombok.Data;
6 +
7 +import java.math.BigDecimal;
8 +import java.time.LocalDate;
9 +import java.time.LocalDateTime;
10 +import java.util.List;
11 +
12 +/**
13 + * 发票响应DTO
14 + *
15 + * @author Apple ERP System
16 + * @since 2025-01-01
17 + */
18 +@Data
19 +@Schema(description = "发票响应DTO")
20 +public class InvoiceRes {
21 +
22 + @Schema(description = "发票ID")
23 + private Long invoiceId;
24 +
25 + @Schema(description = "发票编号")
26 + private String invoiceNo;
27 +
28 + @Schema(description = "关联订单编号")
29 + private String orderNo;
30 +
31 + @Schema(description = "关联出库单编号")
32 + private String deliveryNo;
33 +
34 + @Schema(description = "经销商编码")
35 + private String dealerCode;
36 +
37 + @Schema(description = "经销商名称")
38 + private String dealerName;
39 +
40 + @Schema(description = "发票总金额(含税)")
41 + private BigDecimal totalAmount;
42 +
43 + @Schema(description = "开票日期")
44 + @JsonFormat(pattern = "yyyy-MM-dd")
45 + private LocalDate invoiceDate;
46 +
47 + @Schema(description = "发票状态(0-未开票/1-已开票)")
48 + private Integer invoiceStatus;
49 +
50 + @Schema(description = "税率")
51 + private BigDecimal taxRate;
52 +
53 + @Schema(description = "数据来源")
54 + private String dataSource;
55 +
56 + @Schema(description = "数据上传时间")
57 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
58 + private LocalDateTime uploadTime;
59 +
60 + @Schema(description = "创建者")
61 + private String createBy;
62 +
63 + @Schema(description = "创建时间")
64 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
65 + private LocalDateTime createTime;
66 +
67 + @Schema(description = "更新者")
68 + private String updateBy;
69 +
70 + @Schema(description = "更新时间")
71 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
72 + private LocalDateTime updateTime;
73 +
74 + @Schema(description = "发票明细列表")
75 + private List<InvoiceItemRes> invoiceItems;
76 +}
77 +
1 +package com.apple.erp.dto;
2 +
3 +import com.fasterxml.jackson.annotation.JsonFormat;
4 +import io.swagger.v3.oas.annotations.media.Schema;
5 +import lombok.Data;
6 +
7 +import javax.validation.Valid;
8 +import javax.validation.constraints.NotBlank;
9 +import javax.validation.constraints.NotNull;
10 +import javax.validation.constraints.Size;
11 +import java.math.BigDecimal;
12 +import java.time.LocalDate;
13 +import java.time.LocalDateTime;
14 +import java.util.List;
15 +
16 +/**
17 + * 发票修改请求DTO
18 + *
19 + * @author Apple ERP System
20 + * @since 2025-01-01
21 + */
22 +@Data
23 +@Schema(description = "发票修改请求DTO")
24 +public class InvoiceUpdateReq {
25 +
26 + @NotNull(message = "发票ID不能为空")
27 + @Schema(description = "发票ID", required = true)
28 + private Long invoiceId;
29 +
30 + @NotBlank(message = "发票编号不能为空")
31 + @Schema(description = "发票编号", required = true)
32 + private String invoiceNo;
33 +
34 + @NotBlank(message = "关联订单编号不能为空")
35 + @Schema(description = "关联订单编号", required = true)
36 + private String orderNo;
37 +
38 + @Schema(description = "关联出库单编号")
39 + private String deliveryNo;
40 +
41 + @NotBlank(message = "经销商编码不能为空")
42 + @Schema(description = "经销商编码", required = true)
43 + private String dealerCode;
44 +
45 + @NotBlank(message = "经销商名称不能为空")
46 + @Schema(description = "经销商名称", required = true)
47 + private String dealerName;
48 +
49 + @NotNull(message = "发票总金额不能为空")
50 + @Schema(description = "发票总金额(含税)", required = true)
51 + private BigDecimal totalAmount;
52 +
53 + @NotNull(message = "开票日期不能为空")
54 + @JsonFormat(pattern = "yyyy-MM-dd")
55 + @Schema(description = "开票日期", required = true)
56 + private LocalDate invoiceDate;
57 +
58 + @Schema(description = "发票状态(0-未开票/1-已开票)")
59 + private Integer invoiceStatus;
60 +
61 + @Schema(description = "税率")
62 + private BigDecimal taxRate;
63 +
64 + @Schema(description = "数据来源")
65 + private String dataSource;
66 +
67 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
68 + @Schema(description = "数据上传时间")
69 + private LocalDateTime uploadTime;
70 +
71 + @Valid
72 + @Size(min = 1, message = "发票明细不能为空")
73 + @Schema(description = "发票明细列表", required = true)
74 + private List<InvoiceItemUpdateReq> invoiceItems;
75 +}
76 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import javax.validation.Valid;
7 +import javax.validation.constraints.NotBlank;
8 +import javax.validation.constraints.NotEmpty;
9 +import javax.validation.constraints.NotNull;
10 +import java.math.BigDecimal;
11 +import java.time.LocalDateTime;
12 +import java.util.List;
13 +
14 +/**
15 + * 订单新增请求DTO
16 + *
17 + * @author Apple ERP System
18 + * @since 2025-01-01
19 + */
20 +@Data
21 +@Schema(description = "订单新增请求DTO")
22 +public class OrderAddReq {
23 +
24 + @NotBlank(message = "订单编号不能为空")
25 + @Schema(description = "订单编号", required = true)
26 + private String orderNo;
27 +
28 + @NotBlank(message = "经销商编码不能为空")
29 + @Schema(description = "经销商编码", required = true)
30 + private String dealerCode;
31 +
32 + @NotBlank(message = "经销商名称不能为空")
33 + @Schema(description = "经销商名称", required = true)
34 + private String dealerName;
35 +
36 + @NotNull(message = "订单日期不能为空")
37 + @Schema(description = "订单创建日期", required = true)
38 + private LocalDateTime orderDate;
39 +
40 + @NotNull(message = "订单总金额不能为空")
41 + @Schema(description = "订单总金额", required = true)
42 + private BigDecimal totalAmount;
43 +
44 + @Schema(description = "订单返利金额")
45 + private BigDecimal rebateAmount;
46 +
47 + @Schema(description = "出库状态(0-未出库/1-已出库)")
48 + private Integer deliveryStatus = 0;
49 +
50 + @Schema(description = "开票状态(0-未开票/1-已开票)")
51 + private Integer invoiceStatus = 0;
52 +
53 + @Schema(description = "返利计算状态(0-未计算/1-已计算)")
54 + private Integer rebateCalcFlag = 0;
55 +
56 + @Schema(description = "数据来源")
57 + private String dataSource;
58 +
59 + @Schema(description = "数据验证状态(0-待验证/1-验证通过/2-验证失败)")
60 + private Integer verifyStatus = 0;
61 +
62 + @Schema(description = "数据上传时间")
63 + private LocalDateTime uploadTime;
64 +
65 + @NotEmpty(message = "订单明细不能为空")
66 + @Valid
67 + @Schema(description = "订单明细列表", required = true)
68 + private List<OrderItemAddReq> orderItems;
69 +}
70 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import javax.validation.constraints.NotBlank;
7 +import javax.validation.constraints.NotNull;
8 +import javax.validation.constraints.Positive;
9 +import java.math.BigDecimal;
10 +
11 +/**
12 + * 订单明细新增请求DTO
13 + *
14 + * @author Apple ERP System
15 + * @since 2025-01-01
16 + */
17 +@Data
18 +@Schema(description = "订单明细新增请求DTO")
19 +public class OrderItemAddReq {
20 +
21 + @NotBlank(message = "产品编码不能为空")
22 + @Schema(description = "产品编码", required = true)
23 + private String productCode;
24 +
25 + @NotBlank(message = "产品名称不能为空")
26 + @Schema(description = "产品名称", required = true)
27 + private String productName;
28 +
29 + @NotNull(message = "产品单价不能为空")
30 + @Schema(description = "产品单价", required = true)
31 + private BigDecimal unitPrice;
32 +
33 + @NotNull(message = "商品数量不能为空")
34 + @Positive(message = "商品数量必须大于0")
35 + @Schema(description = "商品数量", required = true)
36 + private Integer productQty;
37 +
38 + @Schema(description = "商品明细金额")
39 + private BigDecimal itemAmount;
40 +}
41 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import java.math.BigDecimal;
7 +import java.time.LocalDateTime;
8 +
9 +/**
10 + * 订单明细响应DTO
11 + *
12 + * @author Apple ERP System
13 + * @since 2025-01-01
14 + */
15 +@Data
16 +@Schema(description = "订单明细响应DTO")
17 +public class OrderItemRes {
18 +
19 + @Schema(description = "明细ID")
20 + private Long itemId;
21 +
22 + @Schema(description = "订单ID")
23 + private Long orderId;
24 +
25 + @Schema(description = "订单编号")
26 + private String orderNo;
27 +
28 + @Schema(description = "产品编码")
29 + private String productCode;
30 +
31 + @Schema(description = "产品名称")
32 + private String productName;
33 +
34 + @Schema(description = "产品单价")
35 + private BigDecimal unitPrice;
36 +
37 + @Schema(description = "商品数量")
38 + private Integer productQty;
39 +
40 + @Schema(description = "商品明细金额")
41 + private BigDecimal itemAmount;
42 +
43 + @Schema(description = "创建者")
44 + private String createBy;
45 +
46 + @Schema(description = "创建时间")
47 + private LocalDateTime createTime;
48 +
49 + @Schema(description = "更新者")
50 + private String updateBy;
51 +
52 + @Schema(description = "更新时间")
53 + private LocalDateTime updateTime;
54 +}
55 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import javax.validation.constraints.NotBlank;
7 +import javax.validation.constraints.NotNull;
8 +import javax.validation.constraints.Positive;
9 +import java.math.BigDecimal;
10 +
11 +/**
12 + * 订单明细修改请求DTO
13 + *
14 + * @author Apple ERP System
15 + * @since 2025-01-01
16 + */
17 +@Data
18 +@Schema(description = "订单明细修改请求DTO")
19 +public class OrderItemUpdateReq {
20 +
21 + @Schema(description = "明细ID(新增时为空,修改时必填)")
22 + private Long itemId;
23 +
24 + @NotBlank(message = "产品编码不能为空")
25 + @Schema(description = "产品编码", required = true)
26 + private String productCode;
27 +
28 + @NotBlank(message = "产品名称不能为空")
29 + @Schema(description = "产品名称", required = true)
30 + private String productName;
31 +
32 + @NotNull(message = "产品单价不能为空")
33 + @Schema(description = "产品单价", required = true)
34 + private BigDecimal unitPrice;
35 +
36 + @NotNull(message = "商品数量不能为空")
37 + @Positive(message = "商品数量必须大于0")
38 + @Schema(description = "商品数量", required = true)
39 + private Integer productQty;
40 +
41 + @Schema(description = "商品明细金额")
42 + private BigDecimal itemAmount;
43 +}
44 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import java.time.LocalDateTime;
7 +
8 +/**
9 + * 订单查询请求DTO
10 + *
11 + * @author Apple ERP System
12 + * @since 2025-01-01
13 + */
14 +@Data
15 +@Schema(description = "订单查询请求DTO")
16 +public class OrderQueryReq {
17 +
18 + @Schema(description = "订单编号")
19 + private String orderNo;
20 +
21 + @Schema(description = "经销商编码")
22 + private String dealerCode;
23 +
24 + @Schema(description = "经销商名称")
25 + private String dealerName;
26 +
27 + @Schema(description = "出库状态(0-未出库/1-已出库)")
28 + private Integer deliveryStatus;
29 +
30 + @Schema(description = "开票状态(0-未开票/1-已开票)")
31 + private Integer invoiceStatus;
32 +
33 + @Schema(description = "返利计算状态(0-未计算/1-已计算)")
34 + private Integer rebateCalcFlag;
35 +
36 + @Schema(description = "数据来源")
37 + private String dataSource;
38 +
39 + @Schema(description = "数据验证状态(0-待验证/1-验证通过/2-验证失败)")
40 + private Integer verifyStatus;
41 +
42 + @Schema(description = "订单开始日期")
43 + private LocalDateTime orderStartDate;
44 +
45 + @Schema(description = "订单结束日期")
46 + private LocalDateTime orderEndDate;
47 +
48 + @Schema(description = "金额最小值")
49 + private java.math.BigDecimal minAmount;
50 +
51 + @Schema(description = "金额最大值")
52 + private java.math.BigDecimal maxAmount;
53 +
54 + @Schema(description = "页码")
55 + private Integer pageNum = 1;
56 +
57 + @Schema(description = "每页大小")
58 + private Integer pageSize = 10;
59 +}
60 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import java.math.BigDecimal;
7 +import java.time.LocalDateTime;
8 +import java.util.List;
9 +
10 +/**
11 + * 订单响应DTO
12 + *
13 + * @author Apple ERP System
14 + * @since 2025-01-01
15 + */
16 +@Data
17 +@Schema(description = "订单响应DTO")
18 +public class OrderRes {
19 +
20 + @Schema(description = "订单ID")
21 + private Long orderId;
22 +
23 + @Schema(description = "订单编号")
24 + private String orderNo;
25 +
26 + @Schema(description = "经销商编码")
27 + private String dealerCode;
28 +
29 + @Schema(description = "经销商名称")
30 + private String dealerName;
31 +
32 + @Schema(description = "订单创建日期")
33 + private LocalDateTime orderDate;
34 +
35 + @Schema(description = "订单总金额")
36 + private BigDecimal totalAmount;
37 +
38 + @Schema(description = "订单返利金额")
39 + private BigDecimal rebateAmount;
40 +
41 + @Schema(description = "出库状态(0-未出库/1-已出库)")
42 + private Integer deliveryStatus;
43 +
44 + @Schema(description = "开票状态(0-未开票/1-已开票)")
45 + private Integer invoiceStatus;
46 +
47 + @Schema(description = "返利计算状态(0-未计算/1-已计算)")
48 + private Integer rebateCalcFlag;
49 +
50 + @Schema(description = "数据来源")
51 + private String dataSource;
52 +
53 + @Schema(description = "数据验证状态(0-待验证/1-验证通过/2-验证失败)")
54 + private Integer verifyStatus;
55 +
56 + @Schema(description = "数据上传时间")
57 + private LocalDateTime uploadTime;
58 +
59 + @Schema(description = "创建者")
60 + private String createBy;
61 +
62 + @Schema(description = "创建时间")
63 + private LocalDateTime createTime;
64 +
65 + @Schema(description = "更新者")
66 + private String updateBy;
67 +
68 + @Schema(description = "更新时间")
69 + private LocalDateTime updateTime;
70 +
71 + @Schema(description = "订单明细列表")
72 + private List<OrderItemRes> orderItems;
73 +}
74 +
1 +package com.apple.erp.dto;
2 +
3 +import io.swagger.v3.oas.annotations.media.Schema;
4 +import lombok.Data;
5 +
6 +import javax.validation.Valid;
7 +import javax.validation.constraints.NotBlank;
8 +import javax.validation.constraints.NotEmpty;
9 +import javax.validation.constraints.NotNull;
10 +import java.math.BigDecimal;
11 +import java.time.LocalDateTime;
12 +import java.util.List;
13 +
14 +/**
15 + * 订单修改请求DTO
16 + *
17 + * @author Apple ERP System
18 + * @since 2025-01-01
19 + */
20 +@Data
21 +@Schema(description = "订单修改请求DTO")
22 +public class OrderUpdateReq {
23 +
24 + @NotNull(message = "订单ID不能为空")
25 + @Schema(description = "订单ID", required = true)
26 + private Long orderId;
27 +
28 + @NotBlank(message = "订单编号不能为空")
29 + @Schema(description = "订单编号", required = true)
30 + private String orderNo;
31 +
32 + @NotBlank(message = "经销商编码不能为空")
33 + @Schema(description = "经销商编码", required = true)
34 + private String dealerCode;
35 +
36 + @NotBlank(message = "经销商名称不能为空")
37 + @Schema(description = "经销商名称", required = true)
38 + private String dealerName;
39 +
40 + @NotNull(message = "订单日期不能为空")
41 + @Schema(description = "订单创建日期", required = true)
42 + private LocalDateTime orderDate;
43 +
44 + @NotNull(message = "订单总金额不能为空")
45 + @Schema(description = "订单总金额", required = true)
46 + private BigDecimal totalAmount;
47 +
48 + @Schema(description = "订单返利金额")
49 + private BigDecimal rebateAmount;
50 +
51 + @Schema(description = "出库状态(0-未出库/1-已出库)")
52 + private Integer deliveryStatus;
53 +
54 + @Schema(description = "开票状态(0-未开票/1-已开票)")
55 + private Integer invoiceStatus;
56 +
57 + @Schema(description = "返利计算状态(0-未计算/1-已计算)")
58 + private Integer rebateCalcFlag;
59 +
60 + @Schema(description = "数据来源")
61 + private String dataSource;
62 +
63 + @Schema(description = "数据验证状态(0-待验证/1-验证通过/2-验证失败)")
64 + private Integer verifyStatus;
65 +
66 + @Schema(description = "数据上传时间")
67 + private LocalDateTime uploadTime;
68 +
69 + @NotEmpty(message = "订单明细不能为空")
70 + @Valid
71 + @Schema(description = "订单明细列表", required = true)
72 + private List<OrderItemUpdateReq> orderItems;
73 +}
74 +
1 +package com.apple.erp.entity;
2 +
3 +import com.baomidou.mybatisplus.annotation.IdType;
4 +import com.baomidou.mybatisplus.annotation.TableId;
5 +import com.baomidou.mybatisplus.annotation.TableName;
6 +import io.swagger.v3.oas.annotations.media.Schema;
7 +import lombok.Data;
8 +import lombok.EqualsAndHashCode;
9 +
10 +import java.math.BigDecimal;
11 +import java.time.LocalDateTime;
12 +
13 +/**
14 + * 出库商品明细表实体类
15 + *
16 + * @author Apple ERP System
17 + * @since 2025-01-01
18 + */
19 +@Data
20 +@EqualsAndHashCode(callSuper = false)
21 +@TableName("t_delivery_item")
22 +@Schema(description = "出库商品明细表")
23 +public class DeliveryItem {
24 +
25 + @TableId(value = "delivery_item_id", type = IdType.AUTO)
26 + @Schema(description = "出库商品明细ID")
27 + private Long deliveryItemId;
28 +
29 + @Schema(description = "关联出库单ID")
30 + private Long deliveryId;
31 +
32 + @Schema(description = "出库单编号")
33 + private String deliveryNo;
34 +
35 + @Schema(description = "关联订单编号")
36 + private String orderNo;
37 +
38 + @Schema(description = "产品编码")
39 + private String productCode;
40 +
41 + @Schema(description = "产品名称")
42 + private String productName;
43 +
44 + @Schema(description = "出库数量")
45 + private Integer deliveryQty;
46 +
47 + @Schema(description = "出库单价")
48 + private BigDecimal deliveryPrice;
49 +
50 + @Schema(description = "出库金额")
51 + private BigDecimal deliveryAmount;
52 +
53 + @Schema(description = "创建者")
54 + private String createBy;
55 +
56 + @Schema(description = "创建时间")
57 + private LocalDateTime createTime;
58 +
59 + @Schema(description = "更新者")
60 + private String updateBy;
61 +
62 + @Schema(description = "更新时间")
63 + private LocalDateTime updateTime;
64 +
65 + @Schema(description = "删除标志(0代表存在 2代表删除)")
66 + private String delFlag;
67 +}
68 +
1 +package com.apple.erp.entity;
2 +
3 +import com.baomidou.mybatisplus.annotation.IdType;
4 +import com.baomidou.mybatisplus.annotation.TableId;
5 +import com.baomidou.mybatisplus.annotation.TableName;
6 +import io.swagger.v3.oas.annotations.media.Schema;
7 +import lombok.Data;
8 +import lombok.EqualsAndHashCode;
9 +
10 +import java.math.BigDecimal;
11 +import java.time.LocalDateTime;
12 +
13 +/**
14 + * 出库主表实体类
15 + *
16 + * @author Apple ERP System
17 + * @since 2025-01-01
18 + */
19 +@Data
20 +@EqualsAndHashCode(callSuper = false)
21 +@TableName("t_delivery_main")
22 +@Schema(description = "出库主表")
23 +public class DeliveryMain {
24 +
25 + @TableId(value = "delivery_id", type = IdType.AUTO)
26 + @Schema(description = "出库单ID")
27 + private Long deliveryId;
28 +
29 + @Schema(description = "出库单编号")
30 + private String deliveryNo;
31 +
32 + @Schema(description = "经销商编码")
33 + private String dealerCode;
34 +
35 + @Schema(description = "经销商名称")
36 + private String dealerName;
37 +
38 + @Schema(description = "关联订单编号")
39 + private String orderNo;
40 +
41 + @Schema(description = "出库日期")
42 + private LocalDateTime deliveryDate;
43 +
44 + @Schema(description = "出库状态(0-未出库/1-已出库)")
45 + private Integer deliveryStatus;
46 +
47 + @Schema(description = "出库总金额")
48 + private BigDecimal totalAmount;
49 +
50 + @Schema(description = "仓库编码")
51 + private String warehouseCode;
52 +
53 + @Schema(description = "仓库名称")
54 + private String warehouseName;
55 +
56 + @Schema(description = "数据来源")
57 + private String dataSource;
58 +
59 + @Schema(description = "上传时间")
60 + private LocalDateTime uploadTime;
61 +
62 + @Schema(description = "创建者")
63 + private String createBy;
64 +
65 + @Schema(description = "创建时间")
66 + private LocalDateTime createTime;
67 +
68 + @Schema(description = "更新者")
69 + private String updateBy;
70 +
71 + @Schema(description = "更新时间")
72 + private LocalDateTime updateTime;
73 +
74 + @Schema(description = "删除标志(0代表存在 2代表删除)")
75 + private String delFlag;
76 +}
1 +package com.apple.erp.entity;
2 +
3 +import com.baomidou.mybatisplus.annotation.IdType;
4 +import com.baomidou.mybatisplus.annotation.TableId;
5 +import com.baomidou.mybatisplus.annotation.TableName;
6 +import com.fasterxml.jackson.annotation.JsonFormat;
7 +import io.swagger.v3.oas.annotations.media.Schema;
8 +import lombok.Data;
9 +import lombok.EqualsAndHashCode;
10 +
11 +import java.math.BigDecimal;
12 +import java.time.LocalDateTime;
13 +
14 +/**
15 + * 发票商品明细表实体类
16 + *
17 + * @author Apple ERP System
18 + * @since 2025-01-01
19 + */
20 +@Data
21 +@EqualsAndHashCode(callSuper = false)
22 +@TableName("t_invoice_item")
23 +@Schema(description = "发票商品明细表")
24 +public class InvoiceItem {
25 +
26 + @TableId(value = "invoice_item_id", type = IdType.AUTO)
27 + @Schema(description = "发票商品明细ID")
28 + private Long invoiceItemId;
29 +
30 + @Schema(description = "关联发票ID")
31 + private Long invoiceId;
32 +
33 + @Schema(description = "发票编号")
34 + private String invoiceNo;
35 +
36 + @Schema(description = "关联订单编号")
37 + private String orderNo;
38 +
39 + @Schema(description = "产品编码")
40 + private String productCode;
41 +
42 + @Schema(description = "产品名称")
43 + private String productName;
44 +
45 + @Schema(description = "开票数量")
46 + private Integer invoiceQty;
47 +
48 + @Schema(description = "不含税单价")
49 + private BigDecimal unitPriceNoTax;
50 +
51 + @Schema(description = "不含税明细金额")
52 + private BigDecimal amountNoTax;
53 +
54 + @Schema(description = "税额")
55 + private BigDecimal taxAmount;
56 +
57 + @Schema(description = "创建者")
58 + private String createBy;
59 +
60 + @Schema(description = "创建时间")
61 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
62 + private LocalDateTime createTime;
63 +
64 + @Schema(description = "更新者")
65 + private String updateBy;
66 +
67 + @Schema(description = "更新时间")
68 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
69 + private LocalDateTime updateTime;
70 +
71 + @Schema(description = "删除标志(0代表存在 2代表删除)")
72 + private String delFlag;
73 +}
74 +
1 +package com.apple.erp.entity;
2 +
3 +import com.baomidou.mybatisplus.annotation.IdType;
4 +import com.baomidou.mybatisplus.annotation.TableId;
5 +import com.baomidou.mybatisplus.annotation.TableName;
6 +import com.fasterxml.jackson.annotation.JsonFormat;
7 +import io.swagger.v3.oas.annotations.media.Schema;
8 +import lombok.Data;
9 +import lombok.EqualsAndHashCode;
10 +
11 +import java.math.BigDecimal;
12 +import java.time.LocalDate;
13 +import java.time.LocalDateTime;
14 +
15 +/**
16 + * 发票主表实体类
17 + *
18 + * @author Apple ERP System
19 + * @since 2025-01-01
20 + */
21 +@Data
22 +@EqualsAndHashCode(callSuper = false)
23 +@TableName("t_invoice_main")
24 +@Schema(description = "发票主表")
25 +public class InvoiceMain {
26 +
27 + @TableId(value = "invoice_id", type = IdType.AUTO)
28 + @Schema(description = "发票ID")
29 + private Long invoiceId;
30 +
31 + @Schema(description = "发票编号")
32 + private String invoiceNo;
33 +
34 + @Schema(description = "关联订单编号")
35 + private String orderNo;
36 +
37 + @Schema(description = "关联出库单编号")
38 + private String deliveryNo;
39 +
40 + @Schema(description = "经销商编码")
41 + private String dealerCode;
42 +
43 + @Schema(description = "经销商名称")
44 + private String dealerName;
45 +
46 + @Schema(description = "发票总金额(含税)")
47 + private BigDecimal totalAmount;
48 +
49 + @Schema(description = "开票日期")
50 + @JsonFormat(pattern = "yyyy-MM-dd")
51 + private LocalDate invoiceDate;
52 +
53 + @Schema(description = "发票状态(0-未开票/1-已开票)")
54 + private Integer invoiceStatus;
55 +
56 + @Schema(description = "税率")
57 + private BigDecimal taxRate;
58 +
59 + @Schema(description = "数据来源")
60 + private String dataSource;
61 +
62 + @Schema(description = "数据上传时间")
63 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
64 + private LocalDateTime uploadTime;
65 +
66 + @Schema(description = "创建者")
67 + private String createBy;
68 +
69 + @Schema(description = "创建时间")
70 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
71 + private LocalDateTime createTime;
72 +
73 + @Schema(description = "更新者")
74 + private String updateBy;
75 +
76 + @Schema(description = "更新时间")
77 + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
78 + private LocalDateTime updateTime;
79 +
80 + @Schema(description = "删除标志(0代表存在 2代表删除)")
81 + private String delFlag;
82 +}
83 +
1 +package com.apple.erp.entity;
2 +
3 +import com.baomidou.mybatisplus.annotation.IdType;
4 +import com.baomidou.mybatisplus.annotation.TableId;
5 +import com.baomidou.mybatisplus.annotation.TableName;
6 +import io.swagger.v3.oas.annotations.media.Schema;
7 +import lombok.Data;
8 +import lombok.EqualsAndHashCode;
9 +
10 +import java.math.BigDecimal;
11 +import java.time.LocalDateTime;
12 +
13 +/**
14 + * 订单商品明细表实体类
15 + *
16 + * @author Apple ERP System
17 + * @since 2025-01-01
18 + */
19 +@Data
20 +@EqualsAndHashCode(callSuper = false)
21 +@TableName("t_order_item")
22 +@Schema(description = "订单商品明细表")
23 +public class OrderItem {
24 +
25 + @TableId(value = "item_id", type = IdType.AUTO)
26 + @Schema(description = "明细ID")
27 + private Long itemId;
28 +
29 + @Schema(description = "订单ID")
30 + private Long orderId;
31 +
32 + @Schema(description = "订单编号")
33 + private String orderNo;
34 +
35 + @Schema(description = "产品编码")
36 + private String productCode;
37 +
38 + @Schema(description = "产品名称")
39 + private String productName;
40 +
41 + @Schema(description = "产品单价")
42 + private BigDecimal unitPrice;
43 +
44 + @Schema(description = "商品数量")
45 + private Integer productQty;
46 +
47 + @Schema(description = "商品明细金额")
48 + private BigDecimal itemAmount;
49 +
50 + @Schema(description = "创建者")
51 + private String createBy;
52 +
53 + @Schema(description = "创建时间")
54 + private LocalDateTime createTime;
55 +
56 + @Schema(description = "更新者")
57 + private String updateBy;
58 +
59 + @Schema(description = "更新时间")
60 + private LocalDateTime updateTime;
61 +
62 + @Schema(description = "删除标志(0代表存在 2代表删除)")
63 + private String delFlag;
64 +}
65 +
1 +package com.apple.erp.entity;
2 +
3 +import com.baomidou.mybatisplus.annotation.IdType;
4 +import com.baomidou.mybatisplus.annotation.TableId;
5 +import com.baomidou.mybatisplus.annotation.TableName;
6 +import io.swagger.v3.oas.annotations.media.Schema;
7 +import lombok.Data;
8 +import lombok.EqualsAndHashCode;
9 +
10 +import java.math.BigDecimal;
11 +import java.time.LocalDateTime;
12 +
13 +/**
14 + * 订单主表实体类
15 + *
16 + * @author Apple ERP System
17 + * @since 2025-01-01
18 + */
19 +@Data
20 +@EqualsAndHashCode(callSuper = false)
21 +@TableName("t_order_main")
22 +@Schema(description = "订单主表")
23 +public class OrderMain {
24 +
25 + @TableId(value = "order_id", type = IdType.AUTO)
26 + @Schema(description = "订单ID")
27 + private Long orderId;
28 +
29 + @Schema(description = "订单编号")
30 + private String orderNo;
31 +
32 + @Schema(description = "经销商编码")
33 + private String dealerCode;
34 +
35 + @Schema(description = "经销商名称")
36 + private String dealerName;
37 +
38 + @Schema(description = "订单创建日期")
39 + private LocalDateTime orderDate;
40 +
41 + @Schema(description = "订单总金额")
42 + private BigDecimal totalAmount;
43 +
44 + @Schema(description = "订单返利金额")
45 + private BigDecimal rebateAmount;
46 +
47 + @Schema(description = "出库状态(0-未出库/1-已出库)")
48 + private Integer deliveryStatus;
49 +
50 + @Schema(description = "开票状态(0-未开票/1-已开票)")
51 + private Integer invoiceStatus;
52 +
53 + @Schema(description = "返利计算状态(0-未计算/1-已计算)")
54 + private Integer rebateCalcFlag;
55 +
56 + @Schema(description = "数据来源")
57 + private String dataSource;
58 +
59 + @Schema(description = "数据验证状态(0-待验证/1-验证通过/2-验证失败)")
60 + private Integer verifyStatus;
61 +
62 + @Schema(description = "数据上传时间")
63 + private LocalDateTime uploadTime;
64 +
65 + @Schema(description = "创建者")
66 + private String createBy;
67 +
68 + @Schema(description = "创建时间")
69 + private LocalDateTime createTime;
70 +
71 + @Schema(description = "更新者")
72 + private String updateBy;
73 +
74 + @Schema(description = "更新时间")
75 + private LocalDateTime updateTime;
76 +
77 + @Schema(description = "删除标志(0代表存在 2代表删除)")
78 + private String delFlag;
79 +}
80 +
1 +package com.apple.erp.mapper;
2 +
3 +import com.apple.erp.entity.DeliveryItem;
4 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5 +import org.apache.ibatis.annotations.Mapper;
6 +
7 +/**
8 + * 出库明细表Mapper接口
9 + *
10 + * @author Apple ERP System
11 + * @since 2025-01-01
12 + */
13 +@Mapper
14 +public interface DeliveryItemMapper extends BaseMapper<DeliveryItem> {
15 +}
16 +
1 +package com.apple.erp.mapper;
2 +
3 +import com.apple.erp.entity.DeliveryMain;
4 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5 +import org.apache.ibatis.annotations.Mapper;
6 +
7 +/**
8 + * 出库主表Mapper接口
9 + *
10 + * @author Apple ERP System
11 + * @since 2025-01-01
12 + */
13 +@Mapper
14 +public interface DeliveryMainMapper extends BaseMapper<DeliveryMain> {
15 +}
16 +
1 +package com.apple.erp.mapper;
2 +
3 +import com.apple.erp.entity.InvoiceItem;
4 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5 +import org.apache.ibatis.annotations.Mapper;
6 +
7 +/**
8 + * 发票明细表Mapper接口
9 + *
10 + * @author Apple ERP System
11 + * @since 2025-01-01
12 + */
13 +@Mapper
14 +public interface InvoiceItemMapper extends BaseMapper<InvoiceItem> {
15 +}
16 +
1 +package com.apple.erp.mapper;
2 +
3 +import com.apple.erp.entity.InvoiceMain;
4 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5 +import org.apache.ibatis.annotations.Mapper;
6 +
7 +/**
8 + * 发票主表Mapper接口
9 + *
10 + * @author Apple ERP System
11 + * @since 2025-01-01
12 + */
13 +@Mapper
14 +public interface InvoiceMainMapper extends BaseMapper<InvoiceMain> {
15 +}
16 +
1 +package com.apple.erp.mapper;
2 +
3 +import com.apple.erp.entity.OrderItem;
4 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5 +import org.apache.ibatis.annotations.Mapper;
6 +
7 +/**
8 + * 订单明细表Mapper接口
9 + *
10 + * @author Apple ERP System
11 + * @since 2025-01-01
12 + */
13 +@Mapper
14 +public interface OrderItemMapper extends BaseMapper<OrderItem> {
15 +}
16 +
1 +package com.apple.erp.mapper;
2 +
3 +import com.apple.erp.entity.OrderMain;
4 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5 +import org.apache.ibatis.annotations.Mapper;
6 +
7 +/**
8 + * 订单主表Mapper接口
9 + *
10 + * @author Apple ERP System
11 + * @since 2025-01-01
12 + */
13 +@Mapper
14 +public interface OrderMainMapper extends BaseMapper<OrderMain> {
15 +}
16 +
1 +package com.apple.erp.service;
2 +
3 +import com.apple.erp.dto.DeliveryAddReq;
4 +import com.apple.erp.dto.DeliveryQueryReq;
5 +import com.apple.erp.dto.DeliveryRes;
6 +import com.apple.erp.dto.DeliveryUpdateReq;
7 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8 +import com.baomidou.mybatisplus.extension.service.IService;
9 +import com.apple.erp.entity.DeliveryMain;
10 +
11 +import java.util.List;
12 +
13 +/**
14 + * 出库主表Service接口
15 + *
16 + * @author Apple ERP System
17 + * @since 2025-01-01
18 + */
19 +public interface DeliveryMainService extends IService<DeliveryMain> {
20 +
21 + /**
22 + * 分页查询出库列表
23 + * @param queryReq 查询条件
24 + * @return 出库列表(带分页信息)
25 + */
26 + Page<DeliveryRes> getDeliveryList(DeliveryQueryReq queryReq);
27 +
28 + /**
29 + * 获取出库详情
30 + * @param deliveryId 出库单ID
31 + * @return 出库详情(包含明细)
32 + */
33 + DeliveryRes getDeliveryDetail(Long deliveryId);
34 +
35 + /**
36 + * 新增出库
37 + * @param addReq 出库新增请求
38 + * @return 是否成功
39 + */
40 + boolean addDelivery(DeliveryAddReq addReq);
41 +
42 + /**
43 + * 修改出库
44 + * @param updateReq 出库修改请求
45 + * @return 是否成功
46 + */
47 + boolean updateDelivery(DeliveryUpdateReq updateReq);
48 +
49 + /**
50 + * 删除出库(逻辑删除)
51 + * @param deliveryId 出库单ID
52 + * @return 是否成功
53 + */
54 + boolean deleteDelivery(Long deliveryId);
55 +
56 + /**
57 + * 批量删除出库(逻辑删除)
58 + * @param deliveryIds 出库单ID列表
59 + * @return 是否成功
60 + */
61 + boolean batchDeleteDeliveries(List<Long> deliveryIds);
62 +
63 + /**
64 + * 修改出库状态
65 + * @param deliveryId 出库单ID
66 + * @param deliveryStatus 出库状态
67 + * @return 是否成功
68 + */
69 + boolean updateDeliveryStatus(Long deliveryId, Integer deliveryStatus);
70 +}
71 +
1 +package com.apple.erp.service;
2 +
3 +import com.apple.erp.dto.InvoiceAddReq;
4 +import com.apple.erp.dto.InvoiceQueryReq;
5 +import com.apple.erp.dto.InvoiceRes;
6 +import com.apple.erp.dto.InvoiceUpdateReq;
7 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8 +import com.baomidou.mybatisplus.extension.service.IService;
9 +import com.apple.erp.entity.InvoiceMain;
10 +
11 +import java.util.List;
12 +
13 +/**
14 + * 发票主表Service接口
15 + *
16 + * @author Apple ERP System
17 + * @since 2025-01-01
18 + */
19 +public interface InvoiceMainService extends IService<InvoiceMain> {
20 +
21 + /**
22 + * 分页查询发票列表
23 + * @param queryReq 查询条件
24 + * @return 发票列表(带分页信息)
25 + */
26 + Page<InvoiceRes> getInvoiceList(InvoiceQueryReq queryReq);
27 +
28 + /**
29 + * 获取发票详情
30 + * @param invoiceId 发票ID
31 + * @return 发票详情(包含明细)
32 + */
33 + InvoiceRes getInvoiceDetail(Long invoiceId);
34 +
35 + /**
36 + * 新增发票
37 + * @param addReq 发票新增请求
38 + * @return 是否成功
39 + */
40 + boolean addInvoice(InvoiceAddReq addReq);
41 +
42 + /**
43 + * 修改发票
44 + * @param updateReq 发票修改请求
45 + * @return 是否成功
46 + */
47 + boolean updateInvoice(InvoiceUpdateReq updateReq);
48 +
49 + /**
50 + * 删除发票(逻辑删除)
51 + * @param invoiceId 发票ID
52 + * @return 是否成功
53 + */
54 + boolean deleteInvoice(Long invoiceId);
55 +
56 + /**
57 + * 批量删除发票(逻辑删除)
58 + * @param invoiceIds 发票ID列表
59 + * @return 是否成功
60 + */
61 + boolean batchDeleteInvoices(List<Long> invoiceIds);
62 +
63 + /**
64 + * 修改发票状态
65 + * @param invoiceId 发票ID
66 + * @param invoiceStatus 发票状态
67 + * @return 是否成功
68 + */
69 + boolean updateInvoiceStatus(Long invoiceId, Integer invoiceStatus);
70 +}
71 +
1 +package com.apple.erp.service;
2 +
3 +import com.apple.erp.dto.OrderAddReq;
4 +import com.apple.erp.dto.OrderQueryReq;
5 +import com.apple.erp.dto.OrderRes;
6 +import com.apple.erp.dto.OrderUpdateReq;
7 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
8 +import com.baomidou.mybatisplus.extension.service.IService;
9 +import com.apple.erp.entity.OrderMain;
10 +
11 +import java.util.List;
12 +
13 +/**
14 + * 订单主表Service接口
15 + *
16 + * @author Apple ERP System
17 + * @since 2025-01-01
18 + */
19 +public interface OrderMainService extends IService<OrderMain> {
20 +
21 + /**
22 + * 分页查询订单列表
23 + *
24 + * @param queryReq 查询条件
25 + * @return 订单分页数据
26 + */
27 + Page<OrderRes> getOrderList(OrderQueryReq queryReq);
28 +
29 + /**
30 + * 获取订单详情
31 + *
32 + * @param orderId 订单ID
33 + * @return 订单详情
34 + */
35 + OrderRes getOrderDetail(Long orderId);
36 +
37 + /**
38 + * 新增订单
39 + *
40 + * @param addReq 新增请求
41 + * @return 是否成功
42 + */
43 + boolean addOrder(OrderAddReq addReq);
44 +
45 + /**
46 + * 修改订单
47 + *
48 + * @param updateReq 修改请求
49 + * @return 是否成功
50 + */
51 + boolean updateOrder(OrderUpdateReq updateReq);
52 +
53 + /**
54 + * 删除订单
55 + *
56 + * @param orderId 订单ID
57 + * @return 是否成功
58 + */
59 + boolean deleteOrder(Long orderId);
60 +
61 + /**
62 + * 批量删除订单
63 + *
64 + * @param orderIds 订单ID列表
65 + * @return 是否成功
66 + */
67 + boolean batchDeleteOrders(List<Long> orderIds);
68 +
69 + /**
70 + * 修改订单出库状态
71 + *
72 + * @param orderId 订单ID
73 + * @param deliveryStatus 出库状态
74 + * @return 是否成功
75 + */
76 + boolean updateDeliveryStatus(Long orderId, Integer deliveryStatus);
77 +
78 + /**
79 + * 修改订单开票状态
80 + *
81 + * @param orderId 订单ID
82 + * @param invoiceStatus 开票状态
83 + * @return 是否成功
84 + */
85 + boolean updateInvoiceStatus(Long orderId, Integer invoiceStatus);
86 +
87 + /**
88 + * 修改订单返利计算状态
89 + *
90 + * @param orderId 订单ID
91 + * @param rebateCalcFlag 返利计算状态
92 + * @return 是否成功
93 + */
94 + boolean updateRebateCalcFlag(Long orderId, Integer rebateCalcFlag);
95 +}
96 +
1 +package com.apple.erp.service.impl;
2 +
3 +import com.apple.erp.dto.*;
4 +import com.apple.erp.entity.DeliveryItem;
5 +import com.apple.erp.mapper.DeliveryItemMapper;
6 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
7 +import com.baomidou.mybatisplus.core.metadata.IPage;
8 +import com.baomidou.mybatisplus.core.toolkit.Wrappers;
9 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
10 +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
11 +import com.apple.erp.entity.DeliveryMain;
12 +import com.apple.erp.mapper.DeliveryMainMapper;
13 +import com.apple.erp.service.DeliveryMainService;
14 +import lombok.extern.slf4j.Slf4j;
15 +import org.springframework.beans.BeanUtils;
16 +import org.springframework.beans.factory.annotation.Autowired;
17 +import org.springframework.stereotype.Service;
18 +import org.springframework.transaction.annotation.Transactional;
19 +import org.springframework.util.StringUtils;
20 +
21 +import java.time.LocalDateTime;
22 +import java.time.format.DateTimeFormatter;
23 +import java.util.List;
24 +import java.util.stream.Collectors;
25 +
26 +/**
27 + * 出库主表Service实现类
28 + *
29 + * @author Apple ERP System
30 + * @since 2025-01-01
31 + */
32 +@Slf4j
33 +@Service
34 +public class DeliveryMainServiceImpl extends ServiceImpl<DeliveryMainMapper, DeliveryMain> implements DeliveryMainService {
35 +
36 + @Autowired
37 + private DeliveryItemMapper deliveryItemMapper;
38 +
39 + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
40 +
41 + @Override
42 + public Page<DeliveryRes> getDeliveryList(DeliveryQueryReq queryReq) {
43 + LambdaQueryWrapper<DeliveryMain> queryWrapper = Wrappers.lambdaQuery(DeliveryMain.class)
44 + .eq(StringUtils.hasText(queryReq.getDeliveryNo()), DeliveryMain::getDeliveryNo, queryReq.getDeliveryNo())
45 + .eq(StringUtils.hasText(queryReq.getDealerCode()), DeliveryMain::getDealerCode, queryReq.getDealerCode())
46 + .like(StringUtils.hasText(queryReq.getDealerName()), DeliveryMain::getDealerName, queryReq.getDealerName())
47 + .eq(StringUtils.hasText(queryReq.getOrderNo()), DeliveryMain::getOrderNo, queryReq.getOrderNo())
48 + .eq(queryReq.getDeliveryStatus() != null, DeliveryMain::getDeliveryStatus, queryReq.getDeliveryStatus())
49 + .eq(StringUtils.hasText(queryReq.getWarehouseCode()), DeliveryMain::getWarehouseCode, queryReq.getWarehouseCode())
50 + .eq(StringUtils.hasText(queryReq.getDataSource()), DeliveryMain::getDataSource, queryReq.getDataSource())
51 + .ge(StringUtils.hasText(queryReq.getDeliveryStartDate()), DeliveryMain::getDeliveryDate, parseDateTime(queryReq.getDeliveryStartDate()))
52 + .le(StringUtils.hasText(queryReq.getDeliveryEndDate()), DeliveryMain::getDeliveryDate, parseDateTime(queryReq.getDeliveryEndDate()))
53 + .eq(DeliveryMain::getDelFlag, "0")
54 + .orderByDesc(DeliveryMain::getCreateTime);
55 +
56 + Page<DeliveryMain> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize());
57 + IPage<DeliveryMain> deliveryMainPage = baseMapper.selectPage(page, queryWrapper);
58 +
59 + List<DeliveryRes> deliveryResList = deliveryMainPage.getRecords().stream().map(deliveryMain -> {
60 + DeliveryRes deliveryRes = new DeliveryRes();
61 + BeanUtils.copyProperties(deliveryMain, deliveryRes);
62 + // 查找出库明细
63 + LambdaQueryWrapper<DeliveryItem> itemQueryWrapper = Wrappers.lambdaQuery(DeliveryItem.class)
64 + .eq(DeliveryItem::getDeliveryId, deliveryMain.getDeliveryId());
65 + List<DeliveryItem> deliveryItems = deliveryItemMapper.selectList(itemQueryWrapper);
66 + List<DeliveryItemRes> itemResList = deliveryItems.stream().map(deliveryItem -> {
67 + DeliveryItemRes itemRes = new DeliveryItemRes();
68 + BeanUtils.copyProperties(deliveryItem, itemRes);
69 + return itemRes;
70 + }).collect(Collectors.toList());
71 + deliveryRes.setDeliveryItems(itemResList);
72 + return deliveryRes;
73 + }).collect(Collectors.toList());
74 +
75 + Page<DeliveryRes> resultPage = new Page<>(deliveryMainPage.getCurrent(), deliveryMainPage.getSize(), deliveryMainPage.getTotal());
76 + resultPage.setRecords(deliveryResList);
77 + return resultPage;
78 + }
79 +
80 + @Override
81 + public DeliveryRes getDeliveryDetail(Long deliveryId) {
82 + DeliveryMain deliveryMain = baseMapper.selectById(deliveryId);
83 + if (deliveryMain == null || "2".equals(deliveryMain.getDelFlag())) {
84 + return null;
85 + }
86 +
87 + DeliveryRes deliveryRes = new DeliveryRes();
88 + BeanUtils.copyProperties(deliveryMain, deliveryRes);
89 +
90 + LambdaQueryWrapper<DeliveryItem> itemQueryWrapper = Wrappers.lambdaQuery(DeliveryItem.class)
91 + .eq(DeliveryItem::getDeliveryId, deliveryId);
92 + List<DeliveryItem> deliveryItems = deliveryItemMapper.selectList(itemQueryWrapper);
93 + List<DeliveryItemRes> itemResList = deliveryItems.stream().map(deliveryItem -> {
94 + DeliveryItemRes itemRes = new DeliveryItemRes();
95 + BeanUtils.copyProperties(deliveryItem, itemRes);
96 + return itemRes;
97 + }).collect(Collectors.toList());
98 + deliveryRes.setDeliveryItems(itemResList);
99 + return deliveryRes;
100 + }
101 +
102 + @Override
103 + @Transactional
104 + public boolean addDelivery(DeliveryAddReq addReq) {
105 + // 检查出库单编号是否已存在
106 + if (isDeliveryNoExist(addReq.getDeliveryNo(), null)) {
107 + throw new IllegalArgumentException("出库单编号已存在");
108 + }
109 +
110 + DeliveryMain deliveryMain = new DeliveryMain();
111 + BeanUtils.copyProperties(addReq, deliveryMain);
112 + deliveryMain.setCreateTime(LocalDateTime.now());
113 + deliveryMain.setUpdateTime(LocalDateTime.now());
114 + deliveryMain.setDelFlag("0");
115 + int insert = baseMapper.insert(deliveryMain);
116 + if (insert > 0) {
117 + for (DeliveryItemAddReq itemAddReq : addReq.getDeliveryItems()) {
118 + DeliveryItem deliveryItem = new DeliveryItem();
119 + BeanUtils.copyProperties(itemAddReq, deliveryItem);
120 + deliveryItem.setDeliveryId(deliveryMain.getDeliveryId());
121 + deliveryItem.setDeliveryNo(deliveryMain.getDeliveryNo());
122 + deliveryItem.setOrderNo(deliveryMain.getOrderNo());
123 + deliveryItem.setCreateTime(LocalDateTime.now());
124 + deliveryItem.setUpdateTime(LocalDateTime.now());
125 + deliveryItem.setDelFlag("0");
126 + deliveryItemMapper.insert(deliveryItem);
127 + }
128 + return true;
129 + }
130 + return false;
131 + }
132 +
133 + @Override
134 + @Transactional
135 + public boolean updateDelivery(DeliveryUpdateReq updateReq) {
136 + // 检查出库单编号是否已存在且不属于当前出库单
137 + if (isDeliveryNoExist(updateReq.getDeliveryNo(), updateReq.getDeliveryId())) {
138 + throw new IllegalArgumentException("出库单编号已存在");
139 + }
140 +
141 + DeliveryMain deliveryMain = new DeliveryMain();
142 + BeanUtils.copyProperties(updateReq, deliveryMain);
143 + deliveryMain.setUpdateTime(LocalDateTime.now());
144 + int update = baseMapper.updateById(deliveryMain);
145 + if (update > 0) {
146 + // 先删除旧的出库明细
147 + LambdaQueryWrapper<DeliveryItem> deleteWrapper = Wrappers.lambdaQuery(DeliveryItem.class)
148 + .eq(DeliveryItem::getDeliveryId, updateReq.getDeliveryId());
149 + deliveryItemMapper.delete(deleteWrapper);
150 +
151 + // 插入新的出库明细
152 + for (DeliveryItemUpdateReq itemUpdateReq : updateReq.getDeliveryItems()) {
153 + DeliveryItem deliveryItem = new DeliveryItem();
154 + BeanUtils.copyProperties(itemUpdateReq, deliveryItem);
155 + deliveryItem.setDeliveryId(updateReq.getDeliveryId());
156 + deliveryItem.setDeliveryNo(updateReq.getDeliveryNo());
157 + deliveryItem.setOrderNo(updateReq.getOrderNo());
158 + deliveryItem.setCreateTime(LocalDateTime.now()); // 新增明细时设置创建时间
159 + deliveryItem.setUpdateTime(LocalDateTime.now());
160 + deliveryItem.setDelFlag("0");
161 + deliveryItemMapper.insert(deliveryItem);
162 + }
163 + return true;
164 + }
165 + return false;
166 + }
167 +
168 + @Override
169 + @Transactional
170 + public boolean deleteDelivery(Long deliveryId) {
171 + DeliveryMain deliveryMain = new DeliveryMain();
172 + deliveryMain.setDeliveryId(deliveryId);
173 + deliveryMain.setDelFlag("2"); // 逻辑删除
174 + deliveryMain.setUpdateTime(LocalDateTime.now());
175 + return baseMapper.updateById(deliveryMain) > 0;
176 + }
177 +
178 + @Override
179 + @Transactional
180 + public boolean batchDeleteDeliveries(List<Long> deliveryIds) {
181 + List<DeliveryMain> deliveriesToUpdate = deliveryIds.stream().map(deliveryId -> {
182 + DeliveryMain deliveryMain = new DeliveryMain();
183 + deliveryMain.setDeliveryId(deliveryId);
184 + deliveryMain.setDelFlag("2");
185 + deliveryMain.setUpdateTime(LocalDateTime.now());
186 + return deliveryMain;
187 + }).collect(Collectors.toList());
188 + return updateBatchById(deliveriesToUpdate);
189 + }
190 +
191 + @Override
192 + public boolean updateDeliveryStatus(Long deliveryId, Integer deliveryStatus) {
193 + DeliveryMain deliveryMain = new DeliveryMain();
194 + deliveryMain.setDeliveryId(deliveryId);
195 + deliveryMain.setDeliveryStatus(deliveryStatus);
196 + deliveryMain.setUpdateTime(LocalDateTime.now());
197 + return baseMapper.updateById(deliveryMain) > 0;
198 + }
199 +
200 + /**
201 + * 检查出库单编号是否已存在
202 + * @param deliveryNo 出库单编号
203 + * @param excludeDeliveryId 排除的出库单ID(用于修改时)
204 + * @return 是否存在
205 + */
206 + private boolean isDeliveryNoExist(String deliveryNo, Long excludeDeliveryId) {
207 + LambdaQueryWrapper<DeliveryMain> queryWrapper = Wrappers.lambdaQuery(DeliveryMain.class)
208 + .eq(DeliveryMain::getDeliveryNo, deliveryNo)
209 + .eq(DeliveryMain::getDelFlag, "0");
210 + if (excludeDeliveryId != null) {
211 + queryWrapper.ne(DeliveryMain::getDeliveryId, excludeDeliveryId);
212 + }
213 + return baseMapper.selectCount(queryWrapper) > 0;
214 + }
215 +
216 + private LocalDateTime parseDateTime(String dateTimeStr) {
217 + if (!StringUtils.hasText(dateTimeStr)) {
218 + return null;
219 + }
220 + try {
221 + return LocalDateTime.parse(dateTimeStr, DATE_TIME_FORMATTER);
222 + } catch (Exception e) {
223 + log.warn("日期时间解析失败: {}", dateTimeStr, e);
224 + return null;
225 + }
226 + }
227 +}
228 +
1 +package com.apple.erp.service.impl;
2 +
3 +import com.apple.erp.dto.*;
4 +import com.apple.erp.entity.InvoiceItem;
5 +import com.apple.erp.mapper.InvoiceItemMapper;
6 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
7 +import com.baomidou.mybatisplus.core.metadata.IPage;
8 +import com.baomidou.mybatisplus.core.toolkit.Wrappers;
9 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
10 +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
11 +import com.apple.erp.entity.InvoiceMain;
12 +import com.apple.erp.mapper.InvoiceMainMapper;
13 +import com.apple.erp.service.InvoiceMainService;
14 +import lombok.extern.slf4j.Slf4j;
15 +import org.springframework.beans.BeanUtils;
16 +import org.springframework.beans.factory.annotation.Autowired;
17 +import org.springframework.stereotype.Service;
18 +import org.springframework.transaction.annotation.Transactional;
19 +import org.springframework.util.StringUtils;
20 +
21 +import java.time.LocalDate;
22 +import java.time.LocalDateTime;
23 +import java.time.format.DateTimeFormatter;
24 +import java.util.List;
25 +import java.util.stream.Collectors;
26 +
27 +/**
28 + * 发票主表Service实现类
29 + *
30 + * @author Apple ERP System
31 + * @since 2025-01-01
32 + */
33 +@Slf4j
34 +@Service
35 +public class InvoiceMainServiceImpl extends ServiceImpl<InvoiceMainMapper, InvoiceMain> implements InvoiceMainService {
36 +
37 + @Autowired
38 + private InvoiceItemMapper invoiceItemMapper;
39 +
40 + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
41 +
42 + @Override
43 + public Page<InvoiceRes> getInvoiceList(InvoiceQueryReq queryReq) {
44 + LambdaQueryWrapper<InvoiceMain> queryWrapper = Wrappers.lambdaQuery(InvoiceMain.class)
45 + .eq(StringUtils.hasText(queryReq.getInvoiceNo()), InvoiceMain::getInvoiceNo, queryReq.getInvoiceNo())
46 + .eq(StringUtils.hasText(queryReq.getOrderNo()), InvoiceMain::getOrderNo, queryReq.getOrderNo())
47 + .eq(StringUtils.hasText(queryReq.getDeliveryNo()), InvoiceMain::getDeliveryNo, queryReq.getDeliveryNo())
48 + .eq(StringUtils.hasText(queryReq.getDealerCode()), InvoiceMain::getDealerCode, queryReq.getDealerCode())
49 + .like(StringUtils.hasText(queryReq.getDealerName()), InvoiceMain::getDealerName, queryReq.getDealerName())
50 + .eq(queryReq.getInvoiceStatus() != null, InvoiceMain::getInvoiceStatus, queryReq.getInvoiceStatus())
51 + .eq(StringUtils.hasText(queryReq.getDataSource()), InvoiceMain::getDataSource, queryReq.getDataSource())
52 + .ge(StringUtils.hasText(queryReq.getInvoiceStartDate()), InvoiceMain::getInvoiceDate, parseDate(queryReq.getInvoiceStartDate()))
53 + .le(StringUtils.hasText(queryReq.getInvoiceEndDate()), InvoiceMain::getInvoiceDate, parseDate(queryReq.getInvoiceEndDate()))
54 + .ge(queryReq.getMinAmount() != null, InvoiceMain::getTotalAmount, queryReq.getMinAmount())
55 + .le(queryReq.getMaxAmount() != null, InvoiceMain::getTotalAmount, queryReq.getMaxAmount())
56 + .eq(InvoiceMain::getDelFlag, "0")
57 + .orderByDesc(InvoiceMain::getCreateTime);
58 +
59 + Page<InvoiceMain> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize());
60 + IPage<InvoiceMain> invoiceMainPage = baseMapper.selectPage(page, queryWrapper);
61 +
62 + List<InvoiceRes> invoiceResList = invoiceMainPage.getRecords().stream().map(invoiceMain -> {
63 + InvoiceRes invoiceRes = new InvoiceRes();
64 + BeanUtils.copyProperties(invoiceMain, invoiceRes);
65 + // 查找发票明细
66 + LambdaQueryWrapper<InvoiceItem> itemQueryWrapper = Wrappers.lambdaQuery(InvoiceItem.class)
67 + .eq(InvoiceItem::getInvoiceId, invoiceMain.getInvoiceId());
68 + List<InvoiceItem> invoiceItems = invoiceItemMapper.selectList(itemQueryWrapper);
69 + List<InvoiceItemRes> itemResList = invoiceItems.stream().map(invoiceItem -> {
70 + InvoiceItemRes itemRes = new InvoiceItemRes();
71 + BeanUtils.copyProperties(invoiceItem, itemRes);
72 + return itemRes;
73 + }).collect(Collectors.toList());
74 + invoiceRes.setInvoiceItems(itemResList);
75 + return invoiceRes;
76 + }).collect(Collectors.toList());
77 +
78 + Page<InvoiceRes> resultPage = new Page<>(invoiceMainPage.getCurrent(), invoiceMainPage.getSize(), invoiceMainPage.getTotal());
79 + resultPage.setRecords(invoiceResList);
80 + return resultPage;
81 + }
82 +
83 + @Override
84 + public InvoiceRes getInvoiceDetail(Long invoiceId) {
85 + InvoiceMain invoiceMain = baseMapper.selectById(invoiceId);
86 + if (invoiceMain == null || "2".equals(invoiceMain.getDelFlag())) {
87 + return null;
88 + }
89 +
90 + InvoiceRes invoiceRes = new InvoiceRes();
91 + BeanUtils.copyProperties(invoiceMain, invoiceRes);
92 +
93 + LambdaQueryWrapper<InvoiceItem> itemQueryWrapper = Wrappers.lambdaQuery(InvoiceItem.class)
94 + .eq(InvoiceItem::getInvoiceId, invoiceId);
95 + List<InvoiceItem> invoiceItems = invoiceItemMapper.selectList(itemQueryWrapper);
96 + List<InvoiceItemRes> itemResList = invoiceItems.stream().map(invoiceItem -> {
97 + InvoiceItemRes itemRes = new InvoiceItemRes();
98 + BeanUtils.copyProperties(invoiceItem, itemRes);
99 + return itemRes;
100 + }).collect(Collectors.toList());
101 + invoiceRes.setInvoiceItems(itemResList);
102 + return invoiceRes;
103 + }
104 +
105 + @Override
106 + @Transactional
107 + public boolean addInvoice(InvoiceAddReq addReq) {
108 + // 检查发票编号是否已存在
109 + if (isInvoiceNoExist(addReq.getInvoiceNo(), null)) {
110 + throw new IllegalArgumentException("发票编号已存在");
111 + }
112 +
113 + InvoiceMain invoiceMain = new InvoiceMain();
114 + BeanUtils.copyProperties(addReq, invoiceMain);
115 + invoiceMain.setCreateTime(LocalDateTime.now());
116 + invoiceMain.setUpdateTime(LocalDateTime.now());
117 + invoiceMain.setDelFlag("0");
118 + int insert = baseMapper.insert(invoiceMain);
119 + if (insert > 0) {
120 + for (InvoiceItemAddReq itemAddReq : addReq.getInvoiceItems()) {
121 + InvoiceItem invoiceItem = new InvoiceItem();
122 + BeanUtils.copyProperties(itemAddReq, invoiceItem);
123 + invoiceItem.setInvoiceId(invoiceMain.getInvoiceId());
124 + invoiceItem.setInvoiceNo(invoiceMain.getInvoiceNo());
125 + invoiceItem.setOrderNo(invoiceMain.getOrderNo());
126 + invoiceItem.setCreateTime(LocalDateTime.now());
127 + invoiceItem.setUpdateTime(LocalDateTime.now());
128 + invoiceItem.setDelFlag("0");
129 + invoiceItemMapper.insert(invoiceItem);
130 + }
131 + return true;
132 + }
133 + return false;
134 + }
135 +
136 + @Override
137 + @Transactional
138 + public boolean updateInvoice(InvoiceUpdateReq updateReq) {
139 + // 检查发票编号是否已存在且不属于当前发票
140 + if (isInvoiceNoExist(updateReq.getInvoiceNo(), updateReq.getInvoiceId())) {
141 + throw new IllegalArgumentException("发票编号已存在");
142 + }
143 +
144 + InvoiceMain invoiceMain = new InvoiceMain();
145 + BeanUtils.copyProperties(updateReq, invoiceMain);
146 + invoiceMain.setUpdateTime(LocalDateTime.now());
147 + int update = baseMapper.updateById(invoiceMain);
148 + if (update > 0) {
149 + // 先删除旧的发票明细
150 + LambdaQueryWrapper<InvoiceItem> deleteWrapper = Wrappers.lambdaQuery(InvoiceItem.class)
151 + .eq(InvoiceItem::getInvoiceId, updateReq.getInvoiceId());
152 + invoiceItemMapper.delete(deleteWrapper);
153 +
154 + // 插入新的发票明细
155 + for (InvoiceItemUpdateReq itemUpdateReq : updateReq.getInvoiceItems()) {
156 + InvoiceItem invoiceItem = new InvoiceItem();
157 + BeanUtils.copyProperties(itemUpdateReq, invoiceItem);
158 + invoiceItem.setInvoiceId(updateReq.getInvoiceId());
159 + invoiceItem.setInvoiceNo(updateReq.getInvoiceNo());
160 + invoiceItem.setOrderNo(updateReq.getOrderNo());
161 + invoiceItem.setCreateTime(LocalDateTime.now()); // 新增明细时设置创建时间
162 + invoiceItem.setUpdateTime(LocalDateTime.now());
163 + invoiceItem.setDelFlag("0");
164 + invoiceItemMapper.insert(invoiceItem);
165 + }
166 + return true;
167 + }
168 + return false;
169 + }
170 +
171 + @Override
172 + @Transactional
173 + public boolean deleteInvoice(Long invoiceId) {
174 + InvoiceMain invoiceMain = new InvoiceMain();
175 + invoiceMain.setInvoiceId(invoiceId);
176 + invoiceMain.setDelFlag("2"); // 逻辑删除
177 + invoiceMain.setUpdateTime(LocalDateTime.now());
178 + return baseMapper.updateById(invoiceMain) > 0;
179 + }
180 +
181 + @Override
182 + @Transactional
183 + public boolean batchDeleteInvoices(List<Long> invoiceIds) {
184 + List<InvoiceMain> invoicesToUpdate = invoiceIds.stream().map(invoiceId -> {
185 + InvoiceMain invoiceMain = new InvoiceMain();
186 + invoiceMain.setInvoiceId(invoiceId);
187 + invoiceMain.setDelFlag("2");
188 + invoiceMain.setUpdateTime(LocalDateTime.now());
189 + return invoiceMain;
190 + }).collect(Collectors.toList());
191 + return updateBatchById(invoicesToUpdate);
192 + }
193 +
194 + @Override
195 + public boolean updateInvoiceStatus(Long invoiceId, Integer invoiceStatus) {
196 + InvoiceMain invoiceMain = new InvoiceMain();
197 + invoiceMain.setInvoiceId(invoiceId);
198 + invoiceMain.setInvoiceStatus(invoiceStatus);
199 + invoiceMain.setUpdateTime(LocalDateTime.now());
200 + return baseMapper.updateById(invoiceMain) > 0;
201 + }
202 +
203 + /**
204 + * 检查发票编号是否已存在
205 + * @param invoiceNo 发票编号
206 + * @param excludeInvoiceId 排除的发票ID(用于修改时)
207 + * @return 是否存在
208 + */
209 + private boolean isInvoiceNoExist(String invoiceNo, Long excludeInvoiceId) {
210 + LambdaQueryWrapper<InvoiceMain> queryWrapper = Wrappers.lambdaQuery(InvoiceMain.class)
211 + .eq(InvoiceMain::getInvoiceNo, invoiceNo)
212 + .eq(InvoiceMain::getDelFlag, "0");
213 + if (excludeInvoiceId != null) {
214 + queryWrapper.ne(InvoiceMain::getInvoiceId, excludeInvoiceId);
215 + }
216 + return baseMapper.selectCount(queryWrapper) > 0;
217 + }
218 +
219 + private LocalDate parseDate(String dateStr) {
220 + if (!StringUtils.hasText(dateStr)) {
221 + return null;
222 + }
223 + try {
224 + return LocalDate.parse(dateStr, DATE_FORMATTER);
225 + } catch (Exception e) {
226 + log.warn("日期解析失败: {}", dateStr, e);
227 + return null;
228 + }
229 + }
230 +
231 +}
1 +package com.apple.erp.service.impl;
2 +
3 +import com.apple.erp.dto.*;
4 +import com.apple.erp.entity.OrderItem;
5 +import com.apple.erp.entity.OrderMain;
6 +import com.apple.erp.mapper.OrderItemMapper;
7 +import com.apple.erp.mapper.OrderMainMapper;
8 +import com.apple.erp.service.OrderMainService;
9 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
10 +import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
11 +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
12 +import org.springframework.beans.BeanUtils;
13 +import org.springframework.beans.factory.annotation.Autowired;
14 +import org.springframework.stereotype.Service;
15 +import org.springframework.transaction.annotation.Transactional;
16 +import org.springframework.util.StringUtils;
17 +
18 +import java.time.LocalDateTime;
19 +import java.util.List;
20 +import java.util.stream.Collectors;
21 +
22 +/**
23 + * 订单主表Service实现类
24 + *
25 + * @author Apple ERP System
26 + * @since 2025-01-01
27 + */
28 +@Service
29 +public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain> implements OrderMainService {
30 +
31 + @Autowired
32 + private OrderMainMapper orderMainMapper;
33 +
34 + @Autowired
35 + private OrderItemMapper orderItemMapper;
36 +
37 + @Override
38 + public Page<OrderRes> getOrderList(OrderQueryReq queryReq) {
39 + Page<OrderMain> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize());
40 +
41 + LambdaQueryWrapper<OrderMain> wrapper = new LambdaQueryWrapper<>();
42 + wrapper.eq(OrderMain::getDelFlag, "0");
43 +
44 + // 构建查询条件
45 + if (StringUtils.hasText(queryReq.getOrderNo())) {
46 + wrapper.like(OrderMain::getOrderNo, queryReq.getOrderNo());
47 + }
48 + if (StringUtils.hasText(queryReq.getDealerCode())) {
49 + wrapper.like(OrderMain::getDealerCode, queryReq.getDealerCode());
50 + }
51 + if (StringUtils.hasText(queryReq.getDealerName())) {
52 + wrapper.like(OrderMain::getDealerName, queryReq.getDealerName());
53 + }
54 + if (queryReq.getDeliveryStatus() != null) {
55 + wrapper.eq(OrderMain::getDeliveryStatus, queryReq.getDeliveryStatus());
56 + }
57 + if (queryReq.getInvoiceStatus() != null) {
58 + wrapper.eq(OrderMain::getInvoiceStatus, queryReq.getInvoiceStatus());
59 + }
60 + if (queryReq.getRebateCalcFlag() != null) {
61 + wrapper.eq(OrderMain::getRebateCalcFlag, queryReq.getRebateCalcFlag());
62 + }
63 + if (StringUtils.hasText(queryReq.getDataSource())) {
64 + wrapper.like(OrderMain::getDataSource, queryReq.getDataSource());
65 + }
66 + if (queryReq.getVerifyStatus() != null) {
67 + wrapper.eq(OrderMain::getVerifyStatus, queryReq.getVerifyStatus());
68 + }
69 + if (queryReq.getOrderStartDate() != null) {
70 + wrapper.ge(OrderMain::getOrderDate, queryReq.getOrderStartDate());
71 + }
72 + if (queryReq.getOrderEndDate() != null) {
73 + wrapper.le(OrderMain::getOrderDate, queryReq.getOrderEndDate());
74 + }
75 + if (queryReq.getMinAmount() != null) {
76 + wrapper.ge(OrderMain::getTotalAmount, queryReq.getMinAmount());
77 + }
78 + if (queryReq.getMaxAmount() != null) {
79 + wrapper.le(OrderMain::getTotalAmount, queryReq.getMaxAmount());
80 + }
81 +
82 + wrapper.orderByDesc(OrderMain::getCreateTime);
83 +
84 + Page<OrderMain> orderPage = orderMainMapper.selectPage(page, wrapper);
85 +
86 + // 转换为响应DTO
87 + Page<OrderRes> resultPage = new Page<>(orderPage.getCurrent(), orderPage.getSize(), orderPage.getTotal());
88 + List<OrderRes> orderResList = orderPage.getRecords().stream().map(order -> {
89 + OrderRes orderRes = new OrderRes();
90 + BeanUtils.copyProperties(order, orderRes);
91 + return orderRes;
92 + }).collect(Collectors.toList());
93 +
94 + resultPage.setRecords(orderResList);
95 + return resultPage;
96 + }
97 +
98 + @Override
99 + public OrderRes getOrderDetail(Long orderId) {
100 + OrderMain order = orderMainMapper.selectById(orderId);
101 + if (order == null || "2".equals(order.getDelFlag())) {
102 + return null;
103 + }
104 +
105 + OrderRes orderRes = new OrderRes();
106 + BeanUtils.copyProperties(order, orderRes);
107 +
108 + // 查询订单明细
109 + LambdaQueryWrapper<OrderItem> itemWrapper = new LambdaQueryWrapper<>();
110 + itemWrapper.eq(OrderItem::getOrderId, orderId);
111 + itemWrapper.eq(OrderItem::getDelFlag, "0");
112 + itemWrapper.orderByAsc(OrderItem::getCreateTime);
113 +
114 + List<OrderItem> orderItems = orderItemMapper.selectList(itemWrapper);
115 + List<OrderItemRes> itemResList = orderItems.stream().map(item -> {
116 + OrderItemRes itemRes = new OrderItemRes();
117 + BeanUtils.copyProperties(item, itemRes);
118 + return itemRes;
119 + }).collect(Collectors.toList());
120 +
121 + orderRes.setOrderItems(itemResList);
122 + return orderRes;
123 + }
124 +
125 + @Override
126 + @Transactional(rollbackFor = Exception.class)
127 + public boolean addOrder(OrderAddReq addReq) {
128 + // 检查订单编号是否已存在
129 + LambdaQueryWrapper<OrderMain> wrapper = new LambdaQueryWrapper<>();
130 + wrapper.eq(OrderMain::getOrderNo, addReq.getOrderNo());
131 + wrapper.eq(OrderMain::getDelFlag, "0");
132 + if (orderMainMapper.selectCount(wrapper) > 0) {
133 + throw new RuntimeException("订单编号已存在");
134 + }
135 +
136 + // 保存订单主表
137 + OrderMain order = new OrderMain();
138 + BeanUtils.copyProperties(addReq, order);
139 + order.setCreateTime(LocalDateTime.now());
140 + order.setDelFlag("0");
141 +
142 + int result = orderMainMapper.insert(order);
143 + if (result <= 0) {
144 + return false;
145 + }
146 +
147 + // 保存订单明细
148 + for (OrderItemAddReq itemReq : addReq.getOrderItems()) {
149 + OrderItem item = new OrderItem();
150 + BeanUtils.copyProperties(itemReq, item);
151 + item.setOrderId(order.getOrderId());
152 + item.setOrderNo(order.getOrderNo());
153 + item.setCreateTime(LocalDateTime.now());
154 + item.setDelFlag("0");
155 +
156 + // 计算明细金额
157 + if (item.getItemAmount() == null) {
158 + item.setItemAmount(item.getUnitPrice().multiply(java.math.BigDecimal.valueOf(item.getProductQty())));
159 + }
160 +
161 + orderItemMapper.insert(item);
162 + }
163 +
164 + return true;
165 + }
166 +
167 + @Override
168 + @Transactional(rollbackFor = Exception.class)
169 + public boolean updateOrder(OrderUpdateReq updateReq) {
170 + OrderMain existingOrder = orderMainMapper.selectById(updateReq.getOrderId());
171 + if (existingOrder == null || "2".equals(existingOrder.getDelFlag())) {
172 + throw new RuntimeException("订单不存在");
173 + }
174 +
175 + // 检查订单编号是否被其他订单使用
176 + if (!existingOrder.getOrderNo().equals(updateReq.getOrderNo())) {
177 + LambdaQueryWrapper<OrderMain> wrapper = new LambdaQueryWrapper<>();
178 + wrapper.eq(OrderMain::getOrderNo, updateReq.getOrderNo());
179 + wrapper.ne(OrderMain::getOrderId, updateReq.getOrderId());
180 + wrapper.eq(OrderMain::getDelFlag, "0");
181 + if (orderMainMapper.selectCount(wrapper) > 0) {
182 + throw new RuntimeException("订单编号已被其他订单使用");
183 + }
184 + }
185 +
186 + // 更新订单主表
187 + OrderMain order = new OrderMain();
188 + BeanUtils.copyProperties(updateReq, order);
189 + order.setUpdateTime(LocalDateTime.now());
190 +
191 + int result = orderMainMapper.updateById(order);
192 + if (result <= 0) {
193 + return false;
194 + }
195 +
196 + // 删除原有明细
197 + LambdaQueryWrapper<OrderItem> deleteWrapper = new LambdaQueryWrapper<>();
198 + deleteWrapper.eq(OrderItem::getOrderId, updateReq.getOrderId());
199 + orderItemMapper.delete(deleteWrapper);
200 +
201 + // 保存新明细
202 + for (OrderItemUpdateReq itemReq : updateReq.getOrderItems()) {
203 + OrderItem item = new OrderItem();
204 + BeanUtils.copyProperties(itemReq, item);
205 + item.setOrderId(updateReq.getOrderId());
206 + item.setOrderNo(updateReq.getOrderNo());
207 + item.setCreateTime(LocalDateTime.now());
208 + item.setDelFlag("0");
209 +
210 + // 计算明细金额
211 + if (item.getItemAmount() == null) {
212 + item.setItemAmount(item.getUnitPrice().multiply(java.math.BigDecimal.valueOf(item.getProductQty())));
213 + }
214 +
215 + orderItemMapper.insert(item);
216 + }
217 +
218 + return true;
219 + }
220 +
221 + @Override
222 + @Transactional(rollbackFor = Exception.class)
223 + public boolean deleteOrder(Long orderId) {
224 + OrderMain order = orderMainMapper.selectById(orderId);
225 + if (order == null || "2".equals(order.getDelFlag())) {
226 + throw new RuntimeException("订单不存在");
227 + }
228 +
229 + // 软删除订单主表
230 + order.setDelFlag("2");
231 + order.setUpdateTime(LocalDateTime.now());
232 + orderMainMapper.updateById(order);
233 +
234 + // 软删除订单明细
235 + LambdaQueryWrapper<OrderItem> itemWrapper = new LambdaQueryWrapper<>();
236 + itemWrapper.eq(OrderItem::getOrderId, orderId);
237 + List<OrderItem> items = orderItemMapper.selectList(itemWrapper);
238 + for (OrderItem item : items) {
239 + item.setDelFlag("2");
240 + item.setUpdateTime(LocalDateTime.now());
241 + orderItemMapper.updateById(item);
242 + }
243 +
244 + return true;
245 + }
246 +
247 + @Override
248 + @Transactional(rollbackFor = Exception.class)
249 + public boolean batchDeleteOrders(List<Long> orderIds) {
250 + for (Long orderId : orderIds) {
251 + deleteOrder(orderId);
252 + }
253 + return true;
254 + }
255 +
256 + @Override
257 + public boolean updateDeliveryStatus(Long orderId, Integer deliveryStatus) {
258 + OrderMain order = orderMainMapper.selectById(orderId);
259 + if (order == null || "2".equals(order.getDelFlag())) {
260 + throw new RuntimeException("订单不存在");
261 + }
262 +
263 + order.setDeliveryStatus(deliveryStatus);
264 + order.setUpdateTime(LocalDateTime.now());
265 + return orderMainMapper.updateById(order) > 0;
266 + }
267 +
268 + @Override
269 + public boolean updateInvoiceStatus(Long orderId, Integer invoiceStatus) {
270 + OrderMain order = orderMainMapper.selectById(orderId);
271 + if (order == null || "2".equals(order.getDelFlag())) {
272 + throw new RuntimeException("订单不存在");
273 + }
274 +
275 + order.setInvoiceStatus(invoiceStatus);
276 + order.setUpdateTime(LocalDateTime.now());
277 + return orderMainMapper.updateById(order) > 0;
278 + }
279 +
280 + @Override
281 + public boolean updateRebateCalcFlag(Long orderId, Integer rebateCalcFlag) {
282 + OrderMain order = orderMainMapper.selectById(orderId);
283 + if (order == null || "2".equals(order.getDelFlag())) {
284 + throw new RuntimeException("订单不存在");
285 + }
286 +
287 + order.setRebateCalcFlag(rebateCalcFlag);
288 + order.setUpdateTime(LocalDateTime.now());
289 + return orderMainMapper.updateById(order) > 0;
290 + }
291 +}
292 +
1 +-- Apple经销商ERP系统 - 订单管理、出库查询、发票管理按钮权限配置脚本
2 +-- 创建时间: 2025-01-27
3 +-- 版本: v1.0
4 +
5 +-- 设置数据库和字符集
6 +SET NAMES utf8mb4;
7 +SET FOREIGN_KEY_CHECKS = 0;
8 +
9 +-- 使用数据库
10 +USE apple_erp;
11 +
12 +-- =========================================
13 +-- 1. 插入订单管理相关按钮权限
14 +-- =========================================
15 +
16 +-- 订单管理按钮权限 (parent_id = 2)
17 +INSERT INTO t_sys_menu (menu_id, parent_id, menu_name, menu_type, path, icon, sort, status, perms, create_by, create_time, update_by, update_time, del_flag)
18 +VALUES
19 +(201, 2, '订单查询', '2', '', '', 1, '1', 'order:list', 'admin', NOW(), 'admin', NOW(), '0'),
20 +(202, 2, '订单新增', '2', '', '', 2, '1', 'order:add', 'admin', NOW(), 'admin', NOW(), '0'),
21 +(203, 2, '订单修改', '2', '', '', 3, '1', 'order:edit', 'admin', NOW(), 'admin', NOW(), '0'),
22 +(204, 2, '订单删除', '2', '', '', 4, '1', 'order:delete', 'admin', NOW(), 'admin', NOW(), '0'),
23 +(205, 2, '订单详情', '2', '', '', 5, '1', 'order:detail', 'admin', NOW(), 'admin', NOW(), '0'),
24 +(206, 2, '订单导出', '2', '', '', 6, '1', 'order:export', 'admin', NOW(), 'admin', NOW(), '0'),
25 +(207, 2, '订单导入', '2', '', '', 7, '1', 'order:import', 'admin', NOW(), 'admin', NOW(), '0'),
26 +(208, 2, '订单审核', '2', '', '', 8, '1', 'order:audit', 'admin', NOW(), 'admin', NOW(), '0'),
27 +(209, 2, '订单取消', '2', '', '', 9, '1', 'order:cancel', 'admin', NOW(), 'admin', NOW(), '0'),
28 +(210, 2, '订单完成', '2', '', '', 10, '1', 'order:complete', 'admin', NOW(), 'admin', NOW(), '0');
29 +
30 +-- =========================================
31 +-- 2. 插入出库查询相关按钮权限
32 +-- =========================================
33 +
34 +-- 出库查询按钮权限 (parent_id = 3)
35 +INSERT INTO t_sys_menu (menu_id, parent_id, menu_name, menu_type, path, icon, sort, status, perms, create_by, create_time, update_by, update_time, del_flag)
36 +VALUES
37 +(301, 3, '出库查询', '2', '', '', 1, '1', 'delivery:list', 'admin', NOW(), 'admin', NOW(), '0'),
38 +(302, 3, '出库详情', '2', '', '', 2, '1', 'delivery:query', 'admin', NOW(), 'admin', NOW(), '0'),
39 +(303, 3, '出库导出', '2', '', '', 3, '1', 'delivery:export', 'admin', NOW(), 'admin', NOW(), '0'),
40 +(304, 3, '出库统计', '2', '', '', 4, '1', 'delivery:statistics', 'admin', NOW(), 'admin', NOW(), '0'),
41 +(305, 3, '出库打印', '2', '', '', 5, '1', 'delivery:print', 'admin', NOW(), 'admin', NOW(), '0'),
42 +(306, 3, '出库确认', '2', '', '', 6, '1', 'delivery:confirm', 'admin', NOW(), 'admin', NOW(), '0'),
43 +(307, 3, '出库撤销', '2', '', '', 7, '1', 'delivery:cancel', 'admin', NOW(), 'admin', NOW(), '0');
44 +
45 +-- =========================================
46 +-- 3. 插入发票管理相关按钮权限
47 +-- =========================================
48 +
49 +-- 发票管理按钮权限 (parent_id = 4)
50 +INSERT INTO t_sys_menu (menu_id, parent_id, menu_name, menu_type, path, icon, sort, status, perms, create_by, create_time, update_by, update_time, del_flag)
51 +VALUES
52 +(401, 4, '发票查询', '2', '', '', 1, '1', 'invoice:list', 'admin', NOW(), 'admin', NOW(), '0'),
53 +(402, 4, '发票新增', '2', '', '', 2, '1', 'invoice:add', 'admin', NOW(), 'admin', NOW(), '0'),
54 +(403, 4, '发票修改', '2', '', '', 3, '1', 'invoice:edit', 'admin', NOW(), 'admin', NOW(), '0'),
55 +(404, 4, '发票删除', '2', '', '', 4, '1', 'invoice:remove', 'admin', NOW(), 'admin', NOW(), '0'),
56 +(405, 4, '发票详情', '2', '', '', 5, '1', 'invoice:query', 'admin', NOW(), 'admin', NOW(), '0'),
57 +(406, 4, '发票导出', '2', '', '', 6, '1', 'invoice:export', 'admin', NOW(), 'admin', NOW(), '0'),
58 +(407, 4, '发票打印', '2', '', '', 7, '1', 'invoice:print', 'admin', NOW(), 'admin', NOW(), '0'),
59 +(408, 4, '发票审核', '2', '', '', 8, '1', 'invoice:audit', 'admin', NOW(), 'admin', NOW(), '0'),
60 +(409, 4, '发票作废', '2', '', '', 9, '1', 'invoice:void', 'admin', NOW(), 'admin', NOW(), '0'),
61 +(410, 4, '发票红冲', '2', '', '', 10, '1', 'invoice:reverse', 'admin', NOW(), 'admin', NOW(), '0');
62 +
63 +-- =========================================
64 +-- 4. 为ADMIN角色分配新权限
65 +-- =========================================
66 +
67 +-- 获取ADMIN角色ID
68 +SET @admin_role_id = (SELECT role_id FROM t_sys_role WHERE role_code = 'ADMIN' AND del_flag = '0' LIMIT 1);
69 +
70 +-- 为ADMIN角色分配订单管理按钮权限
71 +INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
72 +SELECT @admin_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
73 +FROM t_sys_menu
74 +WHERE menu_id IN (201, 202, 203, 204, 205, 206, 207, 208, 209, 210)
75 +AND del_flag = '0';
76 +
77 +-- 为ADMIN角色分配出库查询按钮权限
78 +INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
79 +SELECT @admin_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
80 +FROM t_sys_menu
81 +WHERE menu_id IN (301, 302, 303, 304, 305, 306, 307)
82 +AND del_flag = '0';
83 +
84 +-- 为ADMIN角色分配发票管理按钮权限
85 +INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
86 +SELECT @admin_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
87 +FROM t_sys_menu
88 +WHERE menu_id IN (401, 402, 403, 404, 405, 406, 407, 408, 409, 410)
89 +AND del_flag = '0';
90 +
91 +-- =========================================
92 +-- 5. 为OPERATOR角色分配权限
93 +-- =========================================
94 +
95 +-- 获取OPERATOR角色ID
96 +SET @operator_role_id = (SELECT role_id FROM t_sys_role WHERE role_code = 'OPERATOR' AND del_flag = '0' LIMIT 1);
97 +
98 +-- 为OPERATOR角色分配订单管理按钮权限(除删除外)
99 +INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
100 +SELECT @operator_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
101 +FROM t_sys_menu
102 +WHERE menu_id IN (201, 202, 203, 205, 206, 207, 208, 209, 210)
103 +AND del_flag = '0';
104 +
105 +-- 为OPERATOR角色分配出库查询按钮权限
106 +INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
107 +SELECT @operator_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
108 +FROM t_sys_menu
109 +WHERE menu_id IN (301, 302, 303, 304, 305, 306, 307)
110 +AND del_flag = '0';
111 +
112 +-- 为OPERATOR角色分配发票管理按钮权限(除删除外)
113 +INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
114 +SELECT @operator_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
115 +FROM t_sys_menu
116 +WHERE menu_id IN (401, 402, 403, 405, 406, 407, 408, 409, 410)
117 +AND del_flag = '0';
118 +
119 +-- =========================================
120 +-- 6. 为DEALER_MANAGER角色分配权限
121 +-- =========================================
122 +
123 +-- 获取DEALER_MANAGER角色ID
124 +SET @dealer_manager_role_id = (SELECT role_id FROM t_sys_role WHERE role_code = 'DEALER_MANAGER' AND del_flag = '0' LIMIT 1);
125 +
126 +-- 为DEALER_MANAGER角色分配订单管理按钮权限(只读权限)
127 +INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
128 +SELECT @dealer_manager_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
129 +FROM t_sys_menu
130 +WHERE menu_id IN (201, 205, 206)
131 +AND del_flag = '0';
132 +
133 +-- 为DEALER_MANAGER角色分配出库查询按钮权限
134 +INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
135 +SELECT @dealer_manager_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
136 +FROM t_sys_menu
137 +WHERE menu_id IN (301, 302, 303, 304, 305)
138 +AND del_flag = '0';
139 +
140 +-- 为DEALER_MANAGER角色分配发票管理按钮权限(只读权限)
141 +INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
142 +SELECT @dealer_manager_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
143 +FROM t_sys_menu
144 +WHERE menu_id IN (401, 405, 406, 407)
145 +AND del_flag = '0';
146 +
147 +-- 重置外键检查
148 +SET FOREIGN_KEY_CHECKS = 1;
149 +
150 +-- 输出完成信息
151 +SELECT '订单管理、出库查询、发票管理按钮权限配置完成!' AS message;
...@@ -5,77 +5,5 @@ ...@@ -5,77 +5,5 @@
5 // Generated by unplugin-auto-import 5 // Generated by unplugin-auto-import
6 export {} 6 export {}
7 declare global { 7 declare global {
8 - const EffectScope: typeof import('vue')['EffectScope'] 8 +
9 - const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
10 - const computed: typeof import('vue')['computed']
11 - const createApp: typeof import('vue')['createApp']
12 - const createPinia: typeof import('pinia')['createPinia']
13 - const customRef: typeof import('vue')['customRef']
14 - const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
15 - const defineComponent: typeof import('vue')['defineComponent']
16 - const defineStore: typeof import('pinia')['defineStore']
17 - const effectScope: typeof import('vue')['effectScope']
18 - const getActivePinia: typeof import('pinia')['getActivePinia']
19 - const getCurrentInstance: typeof import('vue')['getCurrentInstance']
20 - const getCurrentScope: typeof import('vue')['getCurrentScope']
21 - const h: typeof import('vue')['h']
22 - const inject: typeof import('vue')['inject']
23 - const isProxy: typeof import('vue')['isProxy']
24 - const isReactive: typeof import('vue')['isReactive']
25 - const isReadonly: typeof import('vue')['isReadonly']
26 - const isRef: typeof import('vue')['isRef']
27 - const mapActions: typeof import('pinia')['mapActions']
28 - const mapGetters: typeof import('pinia')['mapGetters']
29 - const mapState: typeof import('pinia')['mapState']
30 - const mapStores: typeof import('pinia')['mapStores']
31 - const mapWritableState: typeof import('pinia')['mapWritableState']
32 - const markRaw: typeof import('vue')['markRaw']
33 - const nextTick: typeof import('vue')['nextTick']
34 - const onActivated: typeof import('vue')['onActivated']
35 - const onBeforeMount: typeof import('vue')['onBeforeMount']
36 - const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
37 - const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
38 - const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
39 - const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
40 - const onDeactivated: typeof import('vue')['onDeactivated']
41 - const onErrorCaptured: typeof import('vue')['onErrorCaptured']
42 - const onMounted: typeof import('vue')['onMounted']
43 - const onRenderTracked: typeof import('vue')['onRenderTracked']
44 - const onRenderTriggered: typeof import('vue')['onRenderTriggered']
45 - const onScopeDispose: typeof import('vue')['onScopeDispose']
46 - const onServerPrefetch: typeof import('vue')['onServerPrefetch']
47 - const onUnmounted: typeof import('vue')['onUnmounted']
48 - const onUpdated: typeof import('vue')['onUpdated']
49 - const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
50 - const provide: typeof import('vue')['provide']
51 - const reactive: typeof import('vue')['reactive']
52 - const readonly: typeof import('vue')['readonly']
53 - const ref: typeof import('vue')['ref']
54 - const resolveComponent: typeof import('vue')['resolveComponent']
55 - const setActivePinia: typeof import('pinia')['setActivePinia']
56 - const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
57 - const shallowReactive: typeof import('vue')['shallowReactive']
58 - const shallowReadonly: typeof import('vue')['shallowReadonly']
59 - const shallowRef: typeof import('vue')['shallowRef']
60 - const storeToRefs: typeof import('pinia')['storeToRefs']
61 - const toRaw: typeof import('vue')['toRaw']
62 - const toRef: typeof import('vue')['toRef']
63 - const toRefs: typeof import('vue')['toRefs']
64 - const toValue: typeof import('vue')['toValue']
65 - const triggerRef: typeof import('vue')['triggerRef']
66 - const unref: typeof import('vue')['unref']
67 - const useAttrs: typeof import('vue')['useAttrs']
68 - const useCssModule: typeof import('vue')['useCssModule']
69 - const useCssVars: typeof import('vue')['useCssVars']
70 - const useId: typeof import('vue')['useId']
71 - const useLink: typeof import('vue-router')['useLink']
72 - const useModel: typeof import('vue')['useModel']
73 - const useRoute: typeof import('vue-router')['useRoute']
74 - const useRouter: typeof import('vue-router')['useRouter']
75 - const useSlots: typeof import('vue')['useSlots']
76 - const useTemplateRef: typeof import('vue')['useTemplateRef']
77 - const watch: typeof import('vue')['watch']
78 - const watchEffect: typeof import('vue')['watchEffect']
79 - const watchPostEffect: typeof import('vue')['watchPostEffect']
80 - const watchSyncEffect: typeof import('vue')['watchSyncEffect']
81 } 9 }
......
1 +import { request } from '../utils/request'
2 +
3 +// 订单相关类型定义
4 +export interface OrderInfo {
5 + orderId: number
6 + orderNo: string
7 + dealerCode: string
8 + dealerName: string
9 + orderDate: string
10 + totalAmount: number
11 + rebateAmount?: number
12 + deliveryStatus: number
13 + invoiceStatus: number
14 + rebateCalcFlag: number
15 + dataSource?: string
16 + verifyStatus: number
17 + uploadTime?: string
18 + createBy?: string
19 + createTime?: string
20 + updateBy?: string
21 + updateTime?: string
22 + orderItems?: OrderItemInfo[]
23 +}
24 +
25 +export interface OrderItemInfo {
26 + itemId?: number
27 + orderId?: number
28 + productCode: string
29 + productName: string
30 + productQty: number
31 + unitPrice: number
32 + itemAmount: number
33 +}
34 +
35 +export interface OrderQueryReq {
36 + orderNo?: string
37 + dealerCode?: string
38 + dealerName?: string
39 + deliveryStatus?: number
40 + invoiceStatus?: number
41 + rebateCalcFlag?: number
42 + dataSource?: string
43 + verifyStatus?: number
44 + orderStartDate?: string
45 + orderEndDate?: string
46 + minAmount?: number
47 + maxAmount?: number
48 + pageNum?: number
49 + pageSize?: number
50 +}
51 +
52 +export interface OrderAddReq {
53 + orderNo: string
54 + dealerCode: string
55 + dealerName: string
56 + orderDate: string
57 + totalAmount: number
58 + rebateAmount?: number
59 + deliveryStatus?: number
60 + invoiceStatus?: number
61 + rebateCalcFlag?: number
62 + dataSource?: string
63 + verifyStatus?: number
64 + uploadTime?: string
65 + orderItems: OrderItemAddReq[]
66 +}
67 +
68 +export interface OrderItemAddReq {
69 + productCode: string
70 + productName: string
71 + productQty: number
72 + unitPrice: number
73 + itemAmount: number
74 +}
75 +
76 +export interface OrderUpdateReq {
77 + orderId: number
78 + orderNo: string
79 + dealerCode: string
80 + dealerName: string
81 + orderDate: string
82 + totalAmount: number
83 + rebateAmount?: number
84 + deliveryStatus?: number
85 + invoiceStatus?: number
86 + rebateCalcFlag?: number
87 + dataSource?: string
88 + verifyStatus?: number
89 + uploadTime?: string
90 + orderItems: OrderItemUpdateReq[]
91 +}
92 +
93 +export interface OrderItemUpdateReq {
94 + itemId?: number
95 + productCode: string
96 + productName: string
97 + productQty: number
98 + unitPrice: number
99 + itemAmount: number
100 +}
101 +
102 +// 订单API接口
103 +export const orderApi = {
104 + // 分页查询订单列表
105 + getOrderList: (params: OrderQueryReq) => {
106 + return request.get('/order/list', { params })
107 + },
108 +
109 + // 获取订单详情
110 + getOrderDetail: (orderId: number) => {
111 + return request.get(`/order/${orderId}`)
112 + },
113 +
114 + // 新增订单
115 + addOrder: (data: OrderAddReq) => {
116 + return request.post('/order', data)
117 + },
118 +
119 + // 修改订单
120 + updateOrder: (data: OrderUpdateReq) => {
121 + return request.post('/order/update', data)
122 + },
123 +
124 + // 删除订单
125 + deleteOrder: (orderId: number) => {
126 + return request.delete(`/order/${orderId}`)
127 + },
128 +
129 + // 批量删除订单
130 + batchDeleteOrders: (orderIds: number[]) => {
131 + return request.post('/order/batchDelete', orderIds)
132 + },
133 +
134 + // 修改订单出库状态
135 + updateDeliveryStatus: (orderId: number, deliveryStatus: number) => {
136 + return request.post(`/order/${orderId}/deliveryStatus`, null, {
137 + params: { deliveryStatus }
138 + })
139 + },
140 +
141 + // 修改订单开票状态
142 + updateInvoiceStatus: (orderId: number, invoiceStatus: number) => {
143 + return request.post(`/order/${orderId}/invoiceStatus`, null, {
144 + params: { invoiceStatus }
145 + })
146 + },
147 +
148 + // 修改订单返利计算状态
149 + updateRebateCalcFlag: (orderId: number, rebateCalcFlag: number) => {
150 + return request.post(`/order/${orderId}/rebateCalcFlag`, null, {
151 + params: { rebateCalcFlag }
152 + })
153 + }
154 +}
...@@ -141,6 +141,9 @@ const tabContainer = ref<HTMLElement>() ...@@ -141,6 +141,9 @@ const tabContainer = ref<HTMLElement>()
141 // 菜单项配置 141 // 菜单项配置
142 const menuItems = ref([ 142 const menuItems = ref([
143 { name: '首页', path: '/main/dashboard', icon: '🏠' }, 143 { name: '首页', path: '/main/dashboard', icon: '🏠' },
144 + { name: '订单管理', path: '/main/order', icon: '📋' },
145 + { name: '出库查询', path: '/main/delivery', icon: '📦' },
146 + { name: '发票管理', path: '/main/invoice', icon: '🧾' },
144 { name: '产品管理', path: '/main/product', icon: '📦' }, 147 { name: '产品管理', path: '/main/product', icon: '📦' },
145 { name: '经销商管理', path: '/main/dealer', icon: '🏢' }, 148 { name: '经销商管理', path: '/main/dealer', icon: '🏢' },
146 { 149 {
......
...@@ -135,6 +135,33 @@ const staticRoutes: RouteRecordRaw[] = [ ...@@ -135,6 +135,33 @@ const staticRoutes: RouteRecordRaw[] = [
135 } 135 }
136 }, 136 },
137 { 137 {
138 + path: 'order',
139 + name: 'Order',
140 + component: () => import('@/views/order/index.vue'),
141 + meta: {
142 + title: '订单管理',
143 + requiresAuth: true
144 + }
145 + },
146 + {
147 + path: 'delivery',
148 + name: 'Delivery',
149 + component: () => import('@/views/delivery/index.vue'),
150 + meta: {
151 + title: '出库查询',
152 + requiresAuth: true
153 + }
154 + },
155 + {
156 + path: 'invoice',
157 + name: 'Invoice',
158 + component: () => import('@/views/invoice/index.vue'),
159 + meta: {
160 + title: '发票管理',
161 + requiresAuth: true
162 + }
163 + },
164 + {
138 path: 'settings', 165 path: 'settings',
139 name: 'Settings', 166 name: 'Settings',
140 component: () => import('@/views/settings/index.vue'), 167 component: () => import('@/views/settings/index.vue'),
......
...@@ -3,16 +3,19 @@ import { ElMessage, ElMessageBox } from 'element-plus' ...@@ -3,16 +3,19 @@ import { ElMessage, ElMessageBox } from 'element-plus'
3 import { useUserStore } from '@/stores/modules/user' 3 import { useUserStore } from '@/stores/modules/user'
4 import router from '@/router' 4 import router from '@/router'
5 5
6 +// API基础配置
7 +const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8083'
8 +
6 // 创建axios实例 9 // 创建axios实例
7 const service: AxiosInstance = axios.create({ 10 const service: AxiosInstance = axios.create({
8 - baseURL: import.meta.env.VITE_API_BASE_URL, 11 + baseURL: API_BASE_URL,
9 timeout: 10000, 12 timeout: 10000,
10 headers: { 13 headers: {
11 'Content-Type': 'application/json;charset=UTF-8' 14 'Content-Type': 'application/json;charset=UTF-8'
12 } 15 }
13 }) 16 })
14 17
15 -console.log('Axios实例创建,baseURL:', import.meta.env.VITE_API_BASE_URL) 18 +console.log('Request工具实例创建,baseURL:', API_BASE_URL)
16 19
17 // 请求拦截器 20 // 请求拦截器
18 service.interceptors.request.use( 21 service.interceptors.request.use(
...@@ -25,12 +28,11 @@ service.interceptors.request.use( ...@@ -25,12 +28,11 @@ service.interceptors.request.use(
25 headers: config.headers 28 headers: config.headers
26 }) 29 })
27 30
28 - const userStore = useUserStore()
29 -
30 // 添加token到请求头 31 // 添加token到请求头
31 - if (userStore.token) { 32 + const token = localStorage.getItem('token')
33 + if (token) {
32 config.headers = config.headers || {} 34 config.headers = config.headers || {}
33 - config.headers['Authorization'] = `Bearer ${userStore.token}` 35 + config.headers['Authorization'] = `Bearer ${token}`
34 } 36 }
35 37
36 return config 38 return config
......
1 +<template>
2 + <div class="delivery-container">
3 + <!-- 搜索区域 -->
4 + <div class="search-section">
5 + <div class="search-row">
6 + <div class="search-item">
7 + <label>订单编号:</label>
8 + <input
9 + v-model="searchParams.orderNo"
10 + class="search-input"
11 + placeholder="请输入订单编号"
12 + />
13 + </div>
14 + <div class="search-item">
15 + <label>经销商编码:</label>
16 + <input
17 + v-model="searchParams.dealerCode"
18 + class="search-input"
19 + placeholder="请输入经销商编码"
20 + />
21 + </div>
22 + <div class="search-item">
23 + <label>经销商名称:</label>
24 + <input
25 + v-model="searchParams.dealerName"
26 + class="search-input"
27 + placeholder="请输入经销商名称"
28 + />
29 + </div>
30 + <div class="search-item">
31 + <label>出库状态:</label>
32 + <select v-model="searchParams.deliveryStatus" class="search-select">
33 + <option value="">所有</option>
34 + <option value="0">未出库</option>
35 + <option value="1">已出库</option>
36 + </select>
37 + </div>
38 + </div>
39 + <div class="search-row">
40 + <div class="search-item">
41 + <label>开始日期:</label>
42 + <input
43 + v-model="searchParams.startDate"
44 + class="search-input"
45 + type="date"
46 + />
47 + </div>
48 + <div class="search-item">
49 + <label>结束日期:</label>
50 + <input
51 + v-model="searchParams.endDate"
52 + class="search-input"
53 + type="date"
54 + />
55 + </div>
56 + <div class="search-actions">
57 + <button @click="handleSearch" class="search-btn">🔍 搜索</button>
58 + <button @click="handleReset" class="reset-btn">🔄 重置</button>
59 + </div>
60 + </div>
61 + </div>
62 +
63 + <!-- 操作按钮区域 -->
64 + <div class="action-section">
65 + <div class="action-buttons">
66 + <button @click="handleAdd" class="action-btn primary">➕ 新增出库</button>
67 + <button @click="handleExport" class="action-btn secondary">📋 导出</button>
68 + </div>
69 + </div>
70 +
71 + <!-- 数据表格 -->
72 + <div class="table-section">
73 + <div class="table-header">
74 + <div class="table-controls">
75 + <button @click="handleTableSearch" class="control-btn">🔍</button>
76 + <button @click="handleTableRefresh" class="control-btn">🔄</button>
77 + <button @click="handleTableExport" class="control-btn">📋</button>
78 + <button @click="handleTableViewToggle" class="control-btn">⊞</button>
79 + </div>
80 + </div>
81 +
82 + <div class="table-container">
83 + <div v-if="loading" class="loading-overlay">
84 + <div class="loading-spinner">加载中...</div>
85 + </div>
86 + <table class="data-table">
87 + <thead>
88 + <tr>
89 + <th>订单ID</th>
90 + <th class="sortable">订单编号 ↕️</th>
91 + <th>经销商编码</th>
92 + <th>经销商名称</th>
93 + <th>订单日期</th>
94 + <th>订单金额</th>
95 + <th>出库状态</th>
96 + <th>出库时间</th>
97 + <th>操作</th>
98 + </tr>
99 + </thead>
100 + <tbody>
101 + <tr v-for="order in orderList" :key="order.orderId">
102 + <td>{{ order.orderId }}</td>
103 + <td>{{ order.orderNo }}</td>
104 + <td>{{ order.dealerCode }}</td>
105 + <td>{{ order.dealerName }}</td>
106 + <td>{{ formatDate(order.orderDate) }}</td>
107 + <td>{{ formatCurrency(order.totalAmount) }}</td>
108 + <td>
109 + <span :class="getDeliveryStatusClass(order.deliveryStatus)">
110 + {{ getDeliveryStatusText(order.deliveryStatus) }}
111 + </span>
112 + </td>
113 + <td>{{ formatTime(order.uploadTime || '') }}</td>
114 + <td class="action-col">
115 + <button @click="handleView(order)" class="view-btn">👁️ 查看</button>
116 + </td>
117 + </tr>
118 + </tbody>
119 + </table>
120 + </div>
121 +
122 + <!-- 分页信息 -->
123 + <div class="table-footer">
124 + <div class="pagination-left">
125 + <div class="pagination-info">
126 + 共 {{ total }} 条记录,第 {{ currentPage }} / {{ totalPages }} 页
127 + </div>
128 + <div class="page-size-selector">
129 + <label>每页显示:</label>
130 + <select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
131 + <option value="10">10条</option>
132 + <option value="20">20条</option>
133 + <option value="50">50条</option>
134 + <option value="100">100条</option>
135 + </select>
136 + </div>
137 + </div>
138 + <div class="pagination-container">
139 + <button
140 + @click="handlePageChange(currentPage - 1)"
141 + :disabled="currentPage <= 1"
142 + class="pagination-btn prev-btn"
143 + >
144 + 上一页
145 + </button>
146 + <div class="pagination-pages">
147 + <button
148 + v-for="page in getPageNumbers()"
149 + :key="page"
150 + @click="handlePageChange(page)"
151 + :class="['page-number', { active: page === currentPage }]"
152 + >
153 + {{ page }}
154 + </button>
155 + </div>
156 + <button
157 + @click="handlePageChange(currentPage + 1)"
158 + :disabled="currentPage >= totalPages"
159 + class="pagination-btn next-btn"
160 + >
161 + 下一页
162 + </button>
163 + </div>
164 + </div>
165 + </div>
166 +
167 + <!-- 订单详情对话框 -->
168 + <div v-if="showDetailDialog" class="dialog-overlay" @click="closeDetailDialog">
169 + <div class="dialog-content large-dialog" @click.stop>
170 + <div class="dialog-header">
171 + <h3>出库详情</h3>
172 + <button @click="closeDetailDialog" class="close-btn">×</button>
173 + </div>
174 + <div class="dialog-body">
175 + <div v-if="orderDetail" class="detail-content">
176 + <div class="detail-section">
177 + <h4>订单基本信息</h4>
178 + <div class="detail-grid">
179 + <div class="detail-item">
180 + <label>订单编号:</label>
181 + <span>{{ orderDetail.orderNo }}</span>
182 + </div>
183 + <div class="detail-item">
184 + <label>经销商编码:</label>
185 + <span>{{ orderDetail.dealerCode }}</span>
186 + </div>
187 + <div class="detail-item">
188 + <label>经销商名称:</label>
189 + <span>{{ orderDetail.dealerName }}</span>
190 + </div>
191 + <div class="detail-item">
192 + <label>订单日期:</label>
193 + <span>{{ formatDate(orderDetail.orderDate) }}</span>
194 + </div>
195 + <div class="detail-item">
196 + <label>订单总金额:</label>
197 + <span>{{ formatCurrency(orderDetail.totalAmount) }}</span>
198 + </div>
199 + <div class="detail-item">
200 + <label>出库状态:</label>
201 + <span :class="getDeliveryStatusClass(orderDetail.deliveryStatus)">
202 + {{ getDeliveryStatusText(orderDetail.deliveryStatus) }}
203 + </span>
204 + </div>
205 + <div class="detail-item">
206 + <label>出库时间:</label>
207 + <span>{{ formatTime(orderDetail.uploadTime || '') }}</span>
208 + </div>
209 + </div>
210 + </div>
211 +
212 + <div class="detail-section" v-if="orderDetail.orderItems && orderDetail.orderItems.length > 0">
213 + <h4>出库明细</h4>
214 + <table class="detail-table">
215 + <thead>
216 + <tr>
217 + <th>产品编码</th>
218 + <th>产品名称</th>
219 + <th>产品型号</th>
220 + <th>数量</th>
221 + <th>单价</th>
222 + <th>总价</th>
223 + </tr>
224 + </thead>
225 + <tbody>
226 + <tr v-for="item in orderDetail.orderItems" :key="item.itemId">
227 + <td>{{ item.productCode }}</td>
228 + <td>{{ item.productName }}</td>
229 + <td>{{ item.productModel }}</td>
230 + <td>{{ item.quantity }}</td>
231 + <td>{{ formatCurrency(item.unitPrice) }}</td>
232 + <td>{{ formatCurrency(item.totalPrice) }}</td>
233 + </tr>
234 + </tbody>
235 + </table>
236 + </div>
237 + </div>
238 + </div>
239 + </div>
240 + </div>
241 +
242 + <!-- 新增出库对话框 -->
243 + <div v-if="showAddDialog" class="dialog-overlay" @click="closeAddDialog">
244 + <div class="dialog-content large-dialog" @click.stop>
245 + <div class="dialog-header">
246 + <h3>新增出库</h3>
247 + <button @click="closeAddDialog" class="close-btn">×</button>
248 + </div>
249 + <div class="dialog-body">
250 + <form @submit.prevent="handleSubmitAdd">
251 + <!-- 出库基本信息 -->
252 + <div class="form-section">
253 + <h4>出库基本信息</h4>
254 + <div class="form-row">
255 + <div class="form-item">
256 + <label class="required">出库单编号:</label>
257 + <input
258 + v-model="addFormData.deliveryNo"
259 + class="form-input"
260 + placeholder="请输入出库单编号"
261 + required
262 + />
263 + </div>
264 + <div class="form-item">
265 + <label class="required">经销商编码:</label>
266 + <input
267 + v-model="addFormData.dealerCode"
268 + class="form-input"
269 + placeholder="请输入经销商编码"
270 + required
271 + />
272 + </div>
273 + </div>
274 + <div class="form-row">
275 + <div class="form-item">
276 + <label class="required">经销商名称:</label>
277 + <input
278 + v-model="addFormData.dealerName"
279 + class="form-input"
280 + placeholder="请输入经销商名称"
281 + required
282 + />
283 + </div>
284 + <div class="form-item">
285 + <label class="required">关联订单编号:</label>
286 + <input
287 + v-model="addFormData.orderNo"
288 + class="form-input"
289 + placeholder="请输入关联订单编号"
290 + required
291 + />
292 + </div>
293 + </div>
294 + <div class="form-row">
295 + <div class="form-item">
296 + <label class="required">出库日期:</label>
297 + <input
298 + v-model="addFormData.deliveryDate"
299 + class="form-input"
300 + type="datetime-local"
301 + required
302 + />
303 + </div>
304 + <div class="form-item">
305 + <label>仓库编码:</label>
306 + <input
307 + v-model="addFormData.warehouseCode"
308 + class="form-input"
309 + placeholder="请输入仓库编码"
310 + />
311 + </div>
312 + </div>
313 + <div class="form-row">
314 + <div class="form-item">
315 + <label>仓库名称:</label>
316 + <input
317 + v-model="addFormData.warehouseName"
318 + class="form-input"
319 + placeholder="请输入仓库名称"
320 + />
321 + </div>
322 + <div class="form-item">
323 + <label>数据来源:</label>
324 + <input
325 + v-model="addFormData.dataSource"
326 + class="form-input"
327 + placeholder="请输入数据来源"
328 + />
329 + </div>
330 + </div>
331 + </div>
332 +
333 + <!-- 出库明细 -->
334 + <div class="form-section">
335 + <h4>出库明细</h4>
336 + <div class="order-items-header">
337 + <button type="button" @click="addDeliveryItem" class="add-item-btn">+ 添加明细</button>
338 + </div>
339 + <div class="order-items-container">
340 + <div v-for="(item, index) in addFormData.deliveryItems" :key="index" class="order-item">
341 + <div class="item-row-single">
342 + <div class="form-item">
343 + <label class="required">产品编码:</label>
344 + <input
345 + v-model="item.productCode"
346 + class="form-input"
347 + placeholder="请输入产品编码"
348 + required
349 + />
350 + </div>
351 + <div class="form-item">
352 + <label class="required">产品名称:</label>
353 + <input
354 + v-model="item.productName"
355 + class="form-input"
356 + placeholder="请输入产品名称"
357 + required
358 + />
359 + </div>
360 + <div class="form-item">
361 + <label class="required">出库数量:</label>
362 + <input
363 + v-model.number="item.deliveryQty"
364 + class="form-input"
365 + type="number"
366 + placeholder="请输入出库数量"
367 + @input="calculateDeliveryItemTotal(index)"
368 + required
369 + />
370 + </div>
371 + <div class="form-item">
372 + <label class="required">出库单价:</label>
373 + <input
374 + v-model.number="item.deliveryPrice"
375 + class="form-input"
376 + type="number"
377 + step="0.01"
378 + placeholder="请输入出库单价"
379 + @input="calculateDeliveryItemTotal(index)"
380 + required
381 + />
382 + </div>
383 + <div class="form-item">
384 + <label>出库金额:</label>
385 + <input
386 + v-model.number="item.deliveryAmount"
387 + class="form-input"
388 + type="number"
389 + step="0.01"
390 + readonly
391 + />
392 + </div>
393 + <button type="button" @click="removeDeliveryItem(index)" class="remove-item-btn">删除</button>
394 + </div>
395 + </div>
396 + </div>
397 + </div>
398 +
399 + <div class="form-actions">
400 + <button type="button" @click="closeAddDialog" class="cancel-btn">取消</button>
401 + <button type="submit" class="submit-btn">确定</button>
402 + </div>
403 + </form>
404 + </div>
405 + </div>
406 + </div>
407 + </div>
408 +</template>
409 +
410 +<script setup lang="ts">
411 +import { ref, reactive, computed, onMounted } from 'vue'
412 +import { orderApi, type OrderInfo, type OrderQueryReq } from '../../api/order'
413 +
414 +// 响应式数据
415 +const loading = ref(false)
416 +const orderList = ref<OrderInfo[]>([])
417 +const showDetailDialog = ref(false)
418 +const orderDetail = ref<OrderInfo | null>(null)
419 +
420 +// 新增出库相关数据
421 +const showAddDialog = ref(false)
422 +const addFormData = reactive({
423 + deliveryNo: '',
424 + dealerCode: '',
425 + dealerName: '',
426 + orderNo: '',
427 + deliveryDate: '',
428 + warehouseCode: '',
429 + warehouseName: '',
430 + dataSource: '',
431 + deliveryItems: [] as any[]
432 +})
433 +
434 +// 搜索参数
435 +const searchParams = reactive({
436 + orderNo: '',
437 + dealerCode: '',
438 + dealerName: '',
439 + deliveryStatus: undefined,
440 + startDate: '',
441 + endDate: '',
442 + pageNum: 1,
443 + pageSize: 10
444 +})
445 +
446 +// 分页信息
447 +const pagination = reactive({
448 + total: 0,
449 + pageNum: 1,
450 + pageSize: 10
451 +})
452 +
453 +// 分页计算属性
454 +const total = computed(() => pagination.total)
455 +const currentPage = computed(() => pagination.pageNum)
456 +const pageSize = computed({
457 + get: () => pagination.pageSize,
458 + set: (value: number) => {
459 + pagination.pageSize = value
460 + }
461 +})
462 +const totalPages = computed(() => Math.ceil(pagination.total / pagination.pageSize))
463 +
464 +// 状态相关方法
465 +const getDeliveryStatusText = (status: number) => {
466 + return status === 1 ? '已出库' : '未出库'
467 +}
468 +
469 +const getDeliveryStatusClass = (status: number) => {
470 + return status === 1 ? 'status-active' : 'status-inactive'
471 +}
472 +
473 +// 格式化方法
474 +const formatDate = (date: string) => {
475 + if (!date) return '-'
476 + return new Date(date).toLocaleDateString()
477 +}
478 +
479 +const formatTime = (date: string) => {
480 + if (!date) return '-'
481 + return new Date(date).toLocaleString()
482 +}
483 +
484 +const formatCurrency = (amount: number) => {
485 + if (amount === null || amount === undefined) return '-'
486 + return '¥' + amount.toFixed(2)
487 +}
488 +
489 +// 消息提示
490 +const showMessage = (message: string, type: 'success' | 'error' | 'warning' = 'success') => {
491 + console.log(`${type}: ${message}`)
492 + alert(message)
493 +}
494 +
495 +// 获取页码数组
496 +const getPageNumbers = () => {
497 + const totalPages = Math.ceil(pagination.total / pagination.pageSize)
498 + const currentPage = pagination.pageNum
499 + const maxVisible = 5
500 + const start = Math.max(1, currentPage - Math.floor(maxVisible / 2))
501 + const end = Math.min(totalPages, start + maxVisible - 1)
502 + const pages = []
503 + for (let i = start; i <= end; i++) {
504 + pages.push(i)
505 + }
506 + return pages
507 +}
508 +
509 +// 方法
510 +const fetchOrders = async () => {
511 + try {
512 + loading.value = true
513 + const params = { ...searchParams, pageNum: pagination.pageNum, pageSize: pagination.pageSize }
514 + const response = await orderApi.getOrderList(params) as any
515 + console.log('出库查询API响应:', response)
516 +
517 + // 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
518 + if (response && response.records) {
519 + orderList.value = response.records || []
520 + pagination.total = response.total || 0
521 + pagination.pageNum = response.current || 1
522 + pagination.pageSize = response.size || 10
523 + } else {
524 + orderList.value = []
525 + pagination.total = 0
526 + showMessage('获取出库列表失败', 'error')
527 + }
528 + } catch (error) {
529 + console.error('获取出库列表失败:', error)
530 + showMessage('获取出库列表失败, 请重试', 'error')
531 + } finally {
532 + loading.value = false
533 + }
534 +}
535 +
536 +const handleSearch = () => {
537 + pagination.pageNum = 1
538 + fetchOrders()
539 +}
540 +
541 +const handleReset = () => {
542 + Object.assign(searchParams, {
543 + orderNo: '',
544 + dealerCode: '',
545 + dealerName: '',
546 + deliveryStatus: undefined,
547 + startDate: '',
548 + endDate: ''
549 + })
550 + pagination.pageNum = 1
551 + fetchOrders()
552 +}
553 +
554 +const handleView = async (order: OrderInfo) => {
555 + try {
556 + const response = await orderApi.getOrderDetail(order.orderId) as any
557 + console.log('出库查询详情API响应:', response)
558 +
559 + // 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
560 + if (response && response.orderId) {
561 + orderDetail.value = response
562 + showDetailDialog.value = true
563 + } else {
564 + showMessage('获取订单详情失败', 'error')
565 + }
566 + } catch (error) {
567 + console.error('获取订单详情失败:', error)
568 + showMessage('获取订单详情失败, 请重试', 'error')
569 + }
570 +}
571 +
572 +
573 +const handleExport = () => {
574 + showMessage('导出功能开发中...', 'warning')
575 +}
576 +
577 +// 新增出库相关函数
578 +const handleAdd = () => {
579 + // 重置表单数据
580 + Object.assign(addFormData, {
581 + deliveryNo: '',
582 + dealerCode: '',
583 + dealerName: '',
584 + orderNo: '',
585 + deliveryDate: '',
586 + warehouseCode: '',
587 + warehouseName: '',
588 + dataSource: '',
589 + deliveryItems: []
590 + })
591 + showAddDialog.value = true
592 +}
593 +
594 +const closeAddDialog = () => {
595 + showAddDialog.value = false
596 +}
597 +
598 +const addDeliveryItem = () => {
599 + addFormData.deliveryItems.push({
600 + productCode: '',
601 + productName: '',
602 + deliveryQty: 1,
603 + deliveryPrice: 0,
604 + deliveryAmount: 0
605 + })
606 +}
607 +
608 +const removeDeliveryItem = (index: number) => {
609 + addFormData.deliveryItems.splice(index, 1)
610 +}
611 +
612 +const calculateDeliveryItemTotal = (index: number) => {
613 + const item = addFormData.deliveryItems[index]
614 + if (item.deliveryQty && item.deliveryPrice) {
615 + item.deliveryAmount = item.deliveryQty * item.deliveryPrice
616 + }
617 +}
618 +
619 +const handleSubmitAdd = async () => {
620 + try {
621 + // 这里应该调用新增出库的API
622 + // await deliveryApi.addDelivery(addFormData)
623 + showMessage('新增出库功能开发中...', 'warning')
624 + closeAddDialog()
625 + // fetchOrders() // 刷新列表
626 + } catch (error) {
627 + console.error('新增出库失败:', error)
628 + showMessage('新增出库失败, 请重试', 'error')
629 + }
630 +}
631 +
632 +
633 +
634 +const handlePageChange = (page: number) => {
635 + if (page >= 1 && page <= totalPages.value) {
636 + pagination.pageNum = page
637 + fetchOrders()
638 + }
639 +}
640 +
641 +const handlePageSizeChange = () => {
642 + pagination.pageNum = 1
643 + fetchOrders()
644 +}
645 +
646 +const handleTableSearch = () => {
647 + handleSearch()
648 +}
649 +
650 +const handleTableRefresh = () => {
651 + fetchOrders()
652 +}
653 +
654 +const handleTableExport = () => {
655 + handleExport()
656 +}
657 +
658 +const handleTableViewToggle = () => {
659 + showMessage('视图切换功能开发中...', 'warning')
660 +}
661 +
662 +const closeDetailDialog = () => {
663 + showDetailDialog.value = false
664 + orderDetail.value = null
665 +}
666 +
667 +// 生命周期
668 +onMounted(() => {
669 + fetchOrders()
670 +})
671 +</script>
672 +
673 +<style scoped>
674 +/* 复用订单管理页面的样式 */
675 +.delivery-container {
676 + padding: 0px;
677 + background: #f5f5f5;
678 + min-height: 100vh;
679 +}
680 +
681 +.search-section {
682 + background: white;
683 + padding: 16px 20px;
684 + border-bottom: 1px solid #e0e0e0;
685 +}
686 +
687 +.search-row {
688 + display: flex;
689 + gap: 20px;
690 + align-items: center;
691 + flex-wrap: wrap;
692 + margin-bottom: 12px;
693 +}
694 +
695 +.search-item {
696 + display: flex;
697 + align-items: center;
698 + gap: 8px;
699 +}
700 +
701 +.search-item label {
702 + font-size: 12px;
703 + color: #333;
704 + white-space: nowrap;
705 +}
706 +
707 +.search-input, .search-select {
708 + padding: 6px 8px;
709 + border: 1px solid #d9d9d9;
710 + border-radius: 4px;
711 + font-size: 12px;
712 + width: 120px;
713 +}
714 +
715 +.search-input:focus, .search-select:focus {
716 + outline: none;
717 + border-color: #1890ff;
718 + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
719 +}
720 +
721 +.search-actions {
722 + display: flex;
723 + gap: 10px;
724 + align-items: center;
725 +}
726 +
727 +.search-btn {
728 + background: #1890ff;
729 + color: white;
730 + border: none;
731 + padding: 4px 8px;
732 + border-radius: 3px;
733 + font-size: 11px;
734 + cursor: pointer;
735 + height: 24px;
736 + display: flex;
737 + align-items: center;
738 + gap: 4px;
739 +}
740 +
741 +.reset-btn {
742 + background: #ff7875;
743 + color: white;
744 + border: none;
745 + padding: 4px 8px;
746 + border-radius: 3px;
747 + font-size: 11px;
748 + cursor: pointer;
749 + height: 24px;
750 + display: flex;
751 + align-items: center;
752 + gap: 4px;
753 +}
754 +
755 +.action-section {
756 + background: white;
757 + padding: 12px 16px;
758 + border-bottom: 1px solid #e0e0e0;
759 +}
760 +
761 +.action-buttons {
762 + display: flex;
763 + gap: 6px;
764 +}
765 +
766 +.action-btn {
767 + padding: 4px 8px;
768 + border: none;
769 + border-radius: 3px;
770 + cursor: pointer;
771 + font-size: 11px;
772 + height: 24px;
773 + display: flex;
774 + align-items: center;
775 + gap: 4px;
776 +}
777 +
778 +.action-btn.primary {
779 + background: #007bff;
780 + color: white;
781 +}
782 +
783 +.action-btn.secondary {
784 + background: #6c757d;
785 + color: white;
786 +}
787 +
788 +.table-section {
789 + background: white;
790 + margin: 16px;
791 + border-radius: 6px;
792 + overflow: hidden;
793 + box-shadow: 0 2px 8px rgba(0,0,0,0.1);
794 +}
795 +
796 +.table-header {
797 + padding: 8px 16px;
798 + background: #fafafa;
799 + border-bottom: 1px solid #e0e0e0;
800 + display: flex;
801 + justify-content: flex-end;
802 +}
803 +
804 +.table-controls {
805 + display: flex;
806 + gap: 4px;
807 +}
808 +
809 +.control-btn {
810 + width: 24px;
811 + height: 24px;
812 + border: 1px solid #d9d9d9;
813 + background: white;
814 + border-radius: 3px;
815 + cursor: pointer;
816 + display: flex;
817 + align-items: center;
818 + justify-content: center;
819 + font-size: 11px;
820 +}
821 +
822 +.table-container {
823 + position: relative;
824 + overflow-x: auto;
825 +}
826 +
827 +.loading-overlay {
828 + position: absolute;
829 + top: 0;
830 + left: 0;
831 + right: 0;
832 + bottom: 0;
833 + background: rgba(255,255,255,0.8);
834 + display: flex;
835 + align-items: center;
836 + justify-content: center;
837 + z-index: 10;
838 +}
839 +
840 +.loading-spinner {
841 + padding: 20px;
842 + background: white;
843 + border-radius: 4px;
844 + box-shadow: 0 2px 8px rgba(0,0,0,0.1);
845 +}
846 +
847 +.data-table {
848 + width: 100%;
849 + border-collapse: collapse;
850 + font-size: 11px;
851 +}
852 +
853 +.data-table th,
854 +.data-table td {
855 + padding: 8px 6px;
856 + text-align: left;
857 + border-bottom: 1px solid #e0e0e0;
858 +}
859 +
860 +.data-table th {
861 + background-color: #fafafa;
862 + font-weight: 500;
863 + color: #333;
864 + font-size: 11px;
865 +}
866 +
867 +.data-table tbody tr:hover {
868 + background-color: #f5f5f5;
869 +}
870 +
871 +.action-col {
872 + white-space: nowrap;
873 +}
874 +
875 +.action-col button {
876 + margin-right: 5px;
877 + padding: 2px 6px;
878 + margin: 0 1px;
879 + border: none;
880 + border-radius: 2px;
881 + cursor: pointer;
882 + font-size: 10px;
883 + height: 20px;
884 + display: inline-flex;
885 + align-items: center;
886 + gap: 2px;
887 +}
888 +
889 +.view-btn {
890 + background: #17a2b8;
891 + color: white;
892 +}
893 +
894 +.status-btn {
895 + background: #28a745;
896 + color: white;
897 +}
898 +
899 +.print-btn {
900 + background: #ffc107;
901 + color: #333;
902 +}
903 +
904 +.status-active {
905 + color: #28a745;
906 + font-weight: 500;
907 +}
908 +
909 +.status-inactive {
910 + color: #dc3545;
911 + font-weight: 500;
912 +}
913 +
914 +.table-footer {
915 + padding: 8px 16px;
916 + border-top: 1px solid #e0e0e0;
917 + display: flex;
918 + justify-content: space-between;
919 + align-items: center;
920 + background: #fafafa;
921 +}
922 +
923 +.pagination-left {
924 + display: flex;
925 + align-items: center;
926 + gap: 20px;
927 +}
928 +
929 +.pagination-info {
930 + font-size: 12px;
931 + color: #666;
932 +}
933 +
934 +.page-size-selector {
935 + display: flex;
936 + align-items: center;
937 + gap: 8px;
938 +}
939 +
940 +.page-size-selector label {
941 + font-size: 12px;
942 + color: #666;
943 +}
944 +
945 +.page-size-select {
946 + padding: 4px 8px;
947 + border: 1px solid #d9d9d9;
948 + border-radius: 4px;
949 + font-size: 12px;
950 + background: white;
951 + cursor: pointer;
952 + transition: border-color 0.3s;
953 +}
954 +
955 +.pagination-container {
956 + display: flex;
957 + align-items: center;
958 + background: #f8f9fa;
959 + border: 1px solid #e9ecef;
960 + border-radius: 8px;
961 + overflow: hidden;
962 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
963 +}
964 +
965 +.pagination-btn {
966 + padding: 2px 6px;
967 + border: none;
968 + background: white;
969 + color: #6c757d;
970 + font-size: 11px;
971 + font-weight: 500;
972 + cursor: pointer;
973 + transition: all 0.2s;
974 + border-right: 1px solid #e9ecef;
975 + height: 20px;
976 +}
977 +
978 +.pagination-btn:hover:not(:disabled) {
979 + background: #e9ecef;
980 + color: #495057;
981 +}
982 +
983 +.pagination-btn:disabled {
984 + background: #f8f9fa;
985 + color: #adb5bd;
986 + cursor: not-allowed;
987 +}
988 +
989 +.pagination-pages {
990 + display: flex;
991 + align-items: center;
992 +}
993 +
994 +.page-number {
995 + padding: 2px 6px;
996 + border: none;
997 + background: white;
998 + color: #6c757d;
999 + font-size: 11px;
1000 + font-weight: 500;
1001 + cursor: pointer;
1002 + transition: all 0.2s;
1003 + border-right: 1px solid #e9ecef;
1004 + min-width: 28px;
1005 + text-align: center;
1006 + height: 20px;
1007 +}
1008 +
1009 +.page-number:hover {
1010 + background: #e9ecef;
1011 + color: #495057;
1012 +}
1013 +
1014 +.page-number.active {
1015 + background: #6c757d;
1016 + color: white;
1017 + font-weight: 600;
1018 +}
1019 +
1020 +.page-number.active:hover {
1021 + background: #5a6268;
1022 +}
1023 +
1024 +.dialog-overlay {
1025 + position: fixed;
1026 + top: 0;
1027 + left: 0;
1028 + right: 0;
1029 + bottom: 0;
1030 + background: rgba(0,0,0,0.5);
1031 + display: flex;
1032 + align-items: center;
1033 + justify-content: center;
1034 + z-index: 1000;
1035 +}
1036 +
1037 +.dialog-content {
1038 + background: white;
1039 + border-radius: 8px;
1040 + max-width: 600px;
1041 + width: 90%;
1042 + max-height: 80vh;
1043 + overflow-y: auto;
1044 +}
1045 +
1046 +.dialog-content.large-dialog {
1047 + max-width: 1000px;
1048 +}
1049 +
1050 +.dialog-header {
1051 + padding: 20px;
1052 + border-bottom: 1px solid #eee;
1053 + display: flex;
1054 + justify-content: space-between;
1055 + align-items: center;
1056 +}
1057 +
1058 +.dialog-header h3 {
1059 + margin: 0;
1060 + color: #333;
1061 +}
1062 +
1063 +.close-btn {
1064 + background: none;
1065 + border: none;
1066 + font-size: 24px;
1067 + cursor: pointer;
1068 + color: #999;
1069 +}
1070 +
1071 +.dialog-body {
1072 + padding: 20px;
1073 +}
1074 +
1075 +.detail-content {
1076 + max-height: 60vh;
1077 + overflow-y: auto;
1078 +}
1079 +
1080 +.detail-section {
1081 + margin-bottom: 30px;
1082 +}
1083 +
1084 +.detail-section h4 {
1085 + margin: 0 0 15px 0;
1086 + color: #333;
1087 + border-bottom: 2px solid #007bff;
1088 + padding-bottom: 8px;
1089 +}
1090 +
1091 +.detail-grid {
1092 + display: grid;
1093 + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
1094 + gap: 15px;
1095 +}
1096 +
1097 +.detail-item {
1098 + display: flex;
1099 + flex-direction: column;
1100 + gap: 5px;
1101 +}
1102 +
1103 +.detail-item label {
1104 + font-weight: 500;
1105 + color: #666;
1106 + font-size: 14px;
1107 +}
1108 +
1109 +.detail-item span {
1110 + color: #333;
1111 + font-size: 16px;
1112 +}
1113 +
1114 +.detail-table {
1115 + width: 100%;
1116 + border-collapse: collapse;
1117 + margin-top: 15px;
1118 +}
1119 +
1120 +.detail-table th,
1121 +.detail-table td {
1122 + padding: 10px;
1123 + text-align: left;
1124 + border: 1px solid #ddd;
1125 +}
1126 +
1127 +.detail-table th {
1128 + background: #f8f9fa;
1129 + font-weight: 600;
1130 + color: #333;
1131 +}
1132 +
1133 +.detail-table tr:nth-child(even) {
1134 + background: #f8f9fa;
1135 +}
1136 +
1137 +/* 表单样式 */
1138 +.form-section {
1139 + margin-bottom: 20px;
1140 +}
1141 +
1142 +.form-section h4 {
1143 + margin: 0 0 15px 0;
1144 + color: #333;
1145 + border-bottom: 2px solid #007bff;
1146 + padding-bottom: 8px;
1147 +}
1148 +
1149 +.form-row {
1150 + display: flex;
1151 + gap: 15px;
1152 + margin-bottom: 15px;
1153 +}
1154 +
1155 +.form-item {
1156 + flex: 1;
1157 + display: flex;
1158 + flex-direction: column;
1159 + gap: 5px;
1160 +}
1161 +
1162 +.form-item label {
1163 + font-weight: 500;
1164 + color: #333;
1165 +}
1166 +
1167 +.form-item label.required::after {
1168 + content: ' *';
1169 + color: #dc3545;
1170 +}
1171 +
1172 +.form-input {
1173 + padding: 6px 8px;
1174 + border: 1px solid #ddd;
1175 + border-radius: 4px;
1176 + font-size: 12px;
1177 + width: 100%;
1178 + box-sizing: border-box;
1179 +}
1180 +
1181 +.form-input:focus {
1182 + outline: none;
1183 + border-color: #007bff;
1184 + box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.2);
1185 +}
1186 +
1187 +.form-actions {
1188 + display: flex;
1189 + justify-content: flex-end;
1190 + gap: 10px;
1191 + margin-top: 30px;
1192 + padding-top: 20px;
1193 + border-top: 1px solid #eee;
1194 +}
1195 +
1196 +.cancel-btn, .submit-btn {
1197 + padding: 10px 20px;
1198 + border: none;
1199 + border-radius: 4px;
1200 + cursor: pointer;
1201 + font-weight: 500;
1202 +}
1203 +
1204 +.cancel-btn {
1205 + background: #6c757d;
1206 + color: white;
1207 +}
1208 +
1209 +.submit-btn {
1210 + background: #007bff;
1211 + color: white;
1212 +}
1213 +
1214 +/* 出库明细样式 */
1215 +.order-items-header {
1216 + margin-bottom: 10px;
1217 +}
1218 +
1219 +.add-item-btn {
1220 + padding: 8px 16px;
1221 + background: #28a745;
1222 + color: white;
1223 + border: none;
1224 + border-radius: 4px;
1225 + cursor: pointer;
1226 +}
1227 +
1228 +.order-items-container {
1229 + border: 1px solid #eee;
1230 + border-radius: 4px;
1231 + padding: 6px;
1232 + background: #f8f9fa;
1233 +}
1234 +
1235 +.order-item {
1236 + background: white;
1237 + border: 1px solid #ddd;
1238 + border-radius: 4px;
1239 + padding: 6px;
1240 + margin-bottom: 6px;
1241 +}
1242 +
1243 +.order-item:last-child {
1244 + margin-bottom: 0;
1245 +}
1246 +
1247 +.item-row-single {
1248 + display: flex;
1249 + gap: 4px;
1250 + align-items: end;
1251 + flex-wrap: nowrap;
1252 + justify-content: space-between;
1253 +}
1254 +
1255 +.order-item .form-input {
1256 + width: 100%;
1257 + padding: 3px 5px;
1258 + font-size: 11px;
1259 + box-sizing: border-box;
1260 +}
1261 +
1262 +.order-item .form-item {
1263 + flex: 0 0 auto;
1264 + margin-bottom: 0;
1265 + min-width: 0;
1266 +}
1267 +
1268 +.order-item .form-item:nth-child(1) {
1269 + flex: 0 0 120px;
1270 +}
1271 +
1272 +.order-item .form-item:nth-child(2) {
1273 + flex: 1 1 200px;
1274 + min-width: 150px;
1275 +}
1276 +
1277 +.order-item .form-item:nth-child(3) {
1278 + flex: 0 0 80px;
1279 +}
1280 +
1281 +.order-item .form-item:nth-child(4) {
1282 + flex: 0 0 100px;
1283 +}
1284 +
1285 +.order-item .form-item:nth-child(5) {
1286 + flex: 0 0 100px;
1287 +}
1288 +
1289 +.order-item .form-item label {
1290 + font-size: 11px;
1291 + margin-bottom: 2px;
1292 +}
1293 +
1294 +.remove-item-btn {
1295 + padding: 4px 8px;
1296 + background: #dc3545;
1297 + color: white;
1298 + border: none;
1299 + border-radius: 4px;
1300 + cursor: pointer;
1301 + font-size: 11px;
1302 + height: 28px;
1303 + align-self: end;
1304 +}
1305 +</style>
1 +<template>
2 + <div class="invoice-container">
3 + <!-- 搜索区域 -->
4 + <div class="search-section">
5 + <div class="search-row">
6 + <div class="search-item">
7 + <label>订单编号:</label>
8 + <input
9 + v-model="searchParams.orderNo"
10 + class="search-input"
11 + placeholder="请输入订单编号"
12 + />
13 + </div>
14 + <div class="search-item">
15 + <label>经销商编码:</label>
16 + <input
17 + v-model="searchParams.dealerCode"
18 + class="search-input"
19 + placeholder="请输入经销商编码"
20 + />
21 + </div>
22 + <div class="search-item">
23 + <label>经销商名称:</label>
24 + <input
25 + v-model="searchParams.dealerName"
26 + class="search-input"
27 + placeholder="请输入经销商名称"
28 + />
29 + </div>
30 + <div class="search-item">
31 + <label>开票状态:</label>
32 + <select v-model="searchParams.invoiceStatus" class="search-select">
33 + <option value="">所有</option>
34 + <option value="0">未开票</option>
35 + <option value="1">已开票</option>
36 + </select>
37 + </div>
38 + </div>
39 + <div class="search-row">
40 + <div class="search-item">
41 + <label>开始日期:</label>
42 + <input
43 + v-model="searchParams.startDate"
44 + class="search-input"
45 + type="date"
46 + />
47 + </div>
48 + <div class="search-item">
49 + <label>结束日期:</label>
50 + <input
51 + v-model="searchParams.endDate"
52 + class="search-input"
53 + type="date"
54 + />
55 + </div>
56 + <div class="search-actions">
57 + <button @click="handleSearch" class="search-btn">🔍 搜索</button>
58 + <button @click="handleReset" class="reset-btn">🔄 重置</button>
59 + </div>
60 + </div>
61 + </div>
62 +
63 + <!-- 操作按钮区域 -->
64 + <div class="action-section">
65 + <div class="action-buttons">
66 + <button @click="handleAdd" class="action-btn primary">✨ 新增发票</button>
67 + <button @click="handleExport" class="action-btn secondary">📋 导出</button>
68 + <button @click="handlePrint" class="action-btn secondary">🖨️ 打印</button>
69 + <button @click="handleStatistics" class="action-btn secondary">📊 统计</button>
70 + </div>
71 + </div>
72 +
73 + <!-- 数据表格 -->
74 + <div class="table-section">
75 + <div class="table-header">
76 + <div class="table-controls">
77 + <button @click="handleTableSearch" class="control-btn">🔍</button>
78 + <button @click="handleTableRefresh" class="control-btn">🔄</button>
79 + <button @click="handleTableExport" class="control-btn">📋</button>
80 + <button @click="handleTableViewToggle" class="control-btn">⊞</button>
81 + </div>
82 + </div>
83 +
84 + <div class="table-container">
85 + <div v-if="loading" class="loading-overlay">
86 + <div class="loading-spinner">加载中...</div>
87 + </div>
88 + <table class="data-table">
89 + <thead>
90 + <tr>
91 + <th>订单ID</th>
92 + <th class="sortable">订单编号 ↕️</th>
93 + <th>经销商编码</th>
94 + <th>经销商名称</th>
95 + <th>订单日期</th>
96 + <th>订单金额</th>
97 + <th>开票状态</th>
98 + <th>开票时间</th>
99 + <th>发票号码</th>
100 + <th>操作</th>
101 + </tr>
102 + </thead>
103 + <tbody>
104 + <tr v-for="order in orderList" :key="order.orderId">
105 + <td>{{ order.orderId }}</td>
106 + <td>{{ order.orderNo }}</td>
107 + <td>{{ order.dealerCode }}</td>
108 + <td>{{ order.dealerName }}</td>
109 + <td>{{ formatDate(order.orderDate) }}</td>
110 + <td>{{ formatCurrency(order.totalAmount) }}</td>
111 + <td>
112 + <span :class="getInvoiceStatusClass(order.invoiceStatus)">
113 + {{ getInvoiceStatusText(order.invoiceStatus) }}
114 + </span>
115 + </td>
116 + <td>{{ formatTime('') }}</td>
117 + <td>{{ '-' }}</td>
118 + <td class="action-col">
119 + <button @click="handleView(order)" class="view-btn">👁️ 查看</button>
120 + <button @click="handleEdit(order)" class="edit-btn">✏️ 编辑</button>
121 + <button @click="handleInvoiceStatus(order)" class="status-btn">
122 + {{ order.invoiceStatus === 0 ? '开票' : '撤销开票' }}
123 + </button>
124 + <button @click="handlePrintInvoice(order)" class="print-btn">🖨️ 打印</button>
125 + </td>
126 + </tr>
127 + </tbody>
128 + </table>
129 + </div>
130 +
131 + <!-- 分页信息 -->
132 + <div class="table-footer">
133 + <div class="pagination-left">
134 + <div class="pagination-info">
135 + 共 {{ total }} 条记录,第 {{ currentPage }} / {{ totalPages }} 页
136 + </div>
137 + <div class="page-size-selector">
138 + <label>每页显示:</label>
139 + <select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
140 + <option value="10">10条</option>
141 + <option value="20">20条</option>
142 + <option value="50">50条</option>
143 + <option value="100">100条</option>
144 + </select>
145 + </div>
146 + </div>
147 + <div class="pagination-container">
148 + <button
149 + @click="handlePageChange(currentPage - 1)"
150 + :disabled="currentPage <= 1"
151 + class="pagination-btn prev-btn"
152 + >
153 + 上一页
154 + </button>
155 + <div class="pagination-pages">
156 + <button
157 + v-for="page in getPageNumbers()"
158 + :key="page"
159 + @click="handlePageChange(page)"
160 + :class="['page-number', { active: page === currentPage }]"
161 + >
162 + {{ page }}
163 + </button>
164 + </div>
165 + <button
166 + @click="handlePageChange(currentPage + 1)"
167 + :disabled="currentPage >= totalPages"
168 + class="pagination-btn next-btn"
169 + >
170 + 下一页
171 + </button>
172 + </div>
173 + </div>
174 + </div>
175 +
176 + <!-- 新增/编辑发票对话框 -->
177 + <div v-if="showDialog" class="dialog-overlay" @click="closeDialog">
178 + <div class="dialog-content" @click.stop>
179 + <div class="dialog-header">
180 + <h3>{{ isEdit ? '编辑发票' : '新增发票' }}</h3>
181 + <button @click="closeDialog" class="close-btn">×</button>
182 + </div>
183 + <div class="dialog-body">
184 + <form @submit.prevent="handleSubmit">
185 + <div class="form-row">
186 + <div class="form-item">
187 + <label class="required">订单编号:</label>
188 + <input
189 + v-model="formData.orderNo"
190 + class="form-input"
191 + placeholder="请输入订单编号"
192 + required
193 + />
194 + </div>
195 + <div class="form-item">
196 + <label class="required">发票号码:</label>
197 + <input
198 + v-model="formData.invoiceNo"
199 + class="form-input"
200 + placeholder="请输入发票号码"
201 + required
202 + />
203 + </div>
204 + </div>
205 + <div class="form-row">
206 + <div class="form-item">
207 + <label class="required">开票日期:</label>
208 + <input
209 + v-model="formData.invoiceDate"
210 + class="form-input"
211 + type="datetime-local"
212 + required
213 + />
214 + </div>
215 + <div class="form-item">
216 + <label class="required">开票金额:</label>
217 + <input
218 + v-model.number="formData.invoiceAmount"
219 + class="form-input"
220 + type="number"
221 + step="0.01"
222 + placeholder="请输入开票金额"
223 + required
224 + />
225 + </div>
226 + </div>
227 + <div class="form-row">
228 + <div class="form-item">
229 + <label>税率:</label>
230 + <input
231 + v-model.number="formData.taxRate"
232 + class="form-input"
233 + type="number"
234 + step="0.01"
235 + placeholder="请输入税率"
236 + />
237 + </div>
238 + <div class="form-item">
239 + <label>税额:</label>
240 + <input
241 + v-model.number="formData.taxAmount"
242 + class="form-input"
243 + type="number"
244 + step="0.01"
245 + placeholder="请输入税额"
246 + />
247 + </div>
248 + </div>
249 + <div class="form-row">
250 + <div class="form-item full-width">
251 + <label>备注:</label>
252 + <textarea
253 + v-model="formData.remark"
254 + class="form-textarea"
255 + placeholder="请输入备注"
256 + rows="3"
257 + ></textarea>
258 + </div>
259 + </div>
260 + <div class="form-actions">
261 + <button type="button" @click="closeDialog" class="cancel-btn">取消</button>
262 + <button type="submit" class="submit-btn">确定</button>
263 + </div>
264 + </form>
265 + </div>
266 + </div>
267 + </div>
268 +
269 + <!-- 发票详情对话框 -->
270 + <div v-if="showDetailDialog" class="dialog-overlay" @click="closeDetailDialog">
271 + <div class="dialog-content large-dialog" @click.stop>
272 + <div class="dialog-header">
273 + <h3>发票详情</h3>
274 + <button @click="closeDetailDialog" class="close-btn">×</button>
275 + </div>
276 + <div class="dialog-body">
277 + <div v-if="invoiceDetail" class="detail-content">
278 + <div class="detail-section">
279 + <h4>发票基本信息</h4>
280 + <div class="detail-grid">
281 + <div class="detail-item">
282 + <label>订单编号:</label>
283 + <span>{{ invoiceDetail.orderNo }}</span>
284 + </div>
285 + <div class="detail-item">
286 + <label>发票号码:</label>
287 + <span>{{ invoiceDetail.invoiceNo }}</span>
288 + </div>
289 + <div class="detail-item">
290 + <label>开票日期:</label>
291 + <span>{{ formatDate(invoiceDetail.invoiceDate) }}</span>
292 + </div>
293 + <div class="detail-item">
294 + <label>开票金额:</label>
295 + <span>{{ formatCurrency(invoiceDetail.invoiceAmount) }}</span>
296 + </div>
297 + <div class="detail-item">
298 + <label>税率:</label>
299 + <span>{{ invoiceDetail.taxRate ? (invoiceDetail.taxRate * 100).toFixed(2) + '%' : '-' }}</span>
300 + </div>
301 + <div class="detail-item">
302 + <label>税额:</label>
303 + <span>{{ formatCurrency(invoiceDetail.taxAmount) }}</span>
304 + </div>
305 + <div class="detail-item">
306 + <label>开票状态:</label>
307 + <span :class="getInvoiceStatusClass(invoiceDetail.invoiceStatus)">
308 + {{ getInvoiceStatusText(invoiceDetail.invoiceStatus) }}
309 + </span>
310 + </div>
311 + <div class="detail-item">
312 + <label>备注:</label>
313 + <span>{{ invoiceDetail.remark || '-' }}</span>
314 + </div>
315 + </div>
316 + </div>
317 + </div>
318 + </div>
319 + </div>
320 + </div>
321 + </div>
322 +</template>
323 +
324 +<script setup lang="ts">
325 +import { ref, reactive, computed, onMounted } from 'vue'
326 +import { orderApi, type OrderInfo, type OrderQueryReq } from '../../api/order'
327 +
328 +// 响应式数据
329 +const loading = ref(false)
330 +const orderList = ref<OrderInfo[]>([])
331 +const showDialog = ref(false)
332 +const showDetailDialog = ref(false)
333 +const isEdit = ref(false)
334 +const invoiceDetail = ref<any>(null)
335 +
336 +// 搜索参数
337 +const searchParams = reactive({
338 + orderNo: '',
339 + dealerCode: '',
340 + dealerName: '',
341 + invoiceStatus: undefined,
342 + startDate: '',
343 + endDate: '',
344 + pageNum: 1,
345 + pageSize: 10
346 +})
347 +
348 +// 分页信息
349 +const pagination = reactive({
350 + total: 0,
351 + pageNum: 1,
352 + pageSize: 10
353 +})
354 +
355 +// 分页计算属性
356 +const total = computed(() => pagination.total)
357 +const currentPage = computed(() => pagination.pageNum)
358 +const pageSize = computed({
359 + get: () => pagination.pageSize,
360 + set: (value: number) => {
361 + pagination.pageSize = value
362 + }
363 +})
364 +const totalPages = computed(() => Math.ceil(pagination.total / pagination.pageSize))
365 +
366 +// 表单数据
367 +const formData = reactive({
368 + orderNo: '',
369 + invoiceNo: '',
370 + invoiceDate: '',
371 + invoiceAmount: 0,
372 + taxRate: 0,
373 + taxAmount: 0,
374 + remark: ''
375 +})
376 +
377 +// 状态相关方法
378 +const getInvoiceStatusText = (status: number) => {
379 + return status === 1 ? '已开票' : '未开票'
380 +}
381 +
382 +const getInvoiceStatusClass = (status: number) => {
383 + return status === 1 ? 'status-active' : 'status-inactive'
384 +}
385 +
386 +// 格式化方法
387 +const formatDate = (date: string) => {
388 + if (!date) return '-'
389 + return new Date(date).toLocaleDateString()
390 +}
391 +
392 +const formatTime = (date: string) => {
393 + if (!date) return '-'
394 + return new Date(date).toLocaleString()
395 +}
396 +
397 +const formatCurrency = (amount: number) => {
398 + if (amount === null || amount === undefined) return '-'
399 + return '¥' + amount.toFixed(2)
400 +}
401 +
402 +// 消息提示
403 +const showMessage = (message: string, type: 'success' | 'error' | 'warning' = 'success') => {
404 + console.log(`${type}: ${message}`)
405 + alert(message)
406 +}
407 +
408 +// 获取页码数组
409 +const getPageNumbers = () => {
410 + const totalPages = Math.ceil(pagination.total / pagination.pageSize)
411 + const currentPage = pagination.pageNum
412 + const maxVisible = 5
413 + const start = Math.max(1, currentPage - Math.floor(maxVisible / 2))
414 + const end = Math.min(totalPages, start + maxVisible - 1)
415 + const pages = []
416 + for (let i = start; i <= end; i++) {
417 + pages.push(i)
418 + }
419 + return pages
420 +}
421 +
422 +// 方法
423 +const fetchOrders = async () => {
424 + try {
425 + loading.value = true
426 + const params = { ...searchParams, pageNum: pagination.pageNum, pageSize: pagination.pageSize }
427 + const response = await orderApi.getOrderList(params) as any
428 + console.log('发票管理API响应:', response)
429 +
430 + // 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
431 + if (response && response.records) {
432 + orderList.value = response.records || []
433 + pagination.total = response.total || 0
434 + pagination.pageNum = response.current || 1
435 + pagination.pageSize = response.size || 10
436 + } else {
437 + orderList.value = []
438 + pagination.total = 0
439 + showMessage('获取发票列表失败', 'error')
440 + }
441 + } catch (error) {
442 + console.error('获取发票列表失败:', error)
443 + showMessage('获取发票列表失败, 请重试', 'error')
444 + } finally {
445 + loading.value = false
446 + }
447 +}
448 +
449 +const handleSearch = () => {
450 + pagination.pageNum = 1
451 + fetchOrders()
452 +}
453 +
454 +const handleReset = () => {
455 + Object.assign(searchParams, {
456 + orderNo: '',
457 + dealerCode: '',
458 + dealerName: '',
459 + invoiceStatus: undefined,
460 + startDate: '',
461 + endDate: ''
462 + })
463 + pagination.pageNum = 1
464 + fetchOrders()
465 +}
466 +
467 +const handleAdd = () => {
468 + isEdit.value = false
469 + Object.assign(formData, {
470 + orderNo: '',
471 + invoiceNo: '',
472 + invoiceDate: '',
473 + invoiceAmount: 0,
474 + taxRate: 0,
475 + taxAmount: 0,
476 + remark: ''
477 + })
478 + showDialog.value = true
479 +}
480 +
481 +const handleEdit = (order: OrderInfo) => {
482 + isEdit.value = true
483 + Object.assign(formData, {
484 + orderNo: order.orderNo,
485 + invoiceNo: (order as any).invoiceNo || '',
486 + invoiceDate: (order as any).invoiceTime || '',
487 + invoiceAmount: order.totalAmount,
488 + taxRate: 0,
489 + taxAmount: 0,
490 + remark: ''
491 + })
492 + showDialog.value = true
493 +}
494 +
495 +const handleView = async (order: OrderInfo) => {
496 + try {
497 + const response = await orderApi.getOrderDetail(order.orderId) as any
498 + console.log('发票管理详情API响应:', response)
499 +
500 + // 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
501 + if (response && response.orderId) {
502 + invoiceDetail.value = response
503 + showDetailDialog.value = true
504 + } else {
505 + showMessage('获取发票详情失败', 'error')
506 + }
507 + } catch (error) {
508 + console.error('获取发票详情失败:', error)
509 + showMessage('获取发票详情失败, 请重试', 'error')
510 + }
511 +}
512 +
513 +const handleInvoiceStatus = async (order: OrderInfo) => {
514 + const newStatus = order.invoiceStatus === 0 ? 1 : 0
515 + const action = newStatus === 1 ? '开票' : '撤销开票'
516 + if (confirm(`确定要${action}订单"${order.orderNo}"吗?`)) {
517 + try {
518 + await orderApi.updateInvoiceStatus(order.orderId, newStatus)
519 + showMessage(`${action}成功`, 'success')
520 + fetchOrders()
521 + } catch (error) {
522 + console.error(`${action}失败:`, error)
523 + showMessage(`${action}失败, 请重试`, 'error')
524 + }
525 + }
526 +}
527 +
528 +const handleExport = () => {
529 + showMessage('导出功能开发中...', 'warning')
530 +}
531 +
532 +const handlePrint = () => {
533 + showMessage('打印功能开发中...', 'warning')
534 +}
535 +
536 +const handleStatistics = () => {
537 + showMessage('统计功能开发中...', 'warning')
538 +}
539 +
540 +const handlePrintInvoice = (order: OrderInfo) => {
541 + showMessage(`打印发票 ${order.orderNo} 功能开发中...`, 'warning')
542 +}
543 +
544 +const handleSubmit = async () => {
545 + try {
546 + showMessage('发票保存功能开发中...', 'warning')
547 + closeDialog()
548 + } catch (error) {
549 + console.error('保存发票失败:', error)
550 + showMessage('保存发票失败, 请重试', 'error')
551 + }
552 +}
553 +
554 +const handlePageChange = (page: number) => {
555 + if (page >= 1 && page <= totalPages.value) {
556 + pagination.pageNum = page
557 + fetchOrders()
558 + }
559 +}
560 +
561 +const handlePageSizeChange = () => {
562 + pagination.pageNum = 1
563 + fetchOrders()
564 +}
565 +
566 +const handleTableSearch = () => {
567 + handleSearch()
568 +}
569 +
570 +const handleTableRefresh = () => {
571 + fetchOrders()
572 +}
573 +
574 +const handleTableExport = () => {
575 + handleExport()
576 +}
577 +
578 +const handleTableViewToggle = () => {
579 + showMessage('视图切换功能开发中...', 'warning')
580 +}
581 +
582 +const closeDialog = () => {
583 + showDialog.value = false
584 +}
585 +
586 +const closeDetailDialog = () => {
587 + showDetailDialog.value = false
588 + invoiceDetail.value = null
589 +}
590 +
591 +// 生命周期
592 +onMounted(() => {
593 + fetchOrders()
594 +})
595 +</script>
596 +
597 +<style scoped>
598 +/* 复用订单管理页面的样式 */
599 +.invoice-container {
600 + padding: 0px;
601 + background: #f5f5f5;
602 + min-height: 100vh;
603 +}
604 +
605 +.search-section {
606 + background: white;
607 + padding: 16px 20px;
608 + border-bottom: 1px solid #e0e0e0;
609 +}
610 +
611 +.search-row {
612 + display: flex;
613 + gap: 20px;
614 + align-items: center;
615 + flex-wrap: wrap;
616 + margin-bottom: 12px;
617 +}
618 +
619 +.search-item {
620 + display: flex;
621 + align-items: center;
622 + gap: 8px;
623 +}
624 +
625 +.search-item label {
626 + font-size: 12px;
627 + color: #333;
628 + white-space: nowrap;
629 +}
630 +
631 +.search-input, .search-select {
632 + padding: 6px 8px;
633 + border: 1px solid #d9d9d9;
634 + border-radius: 4px;
635 + font-size: 12px;
636 + width: 120px;
637 +}
638 +
639 +.search-input:focus, .search-select:focus {
640 + outline: none;
641 + border-color: #1890ff;
642 + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
643 +}
644 +
645 +.search-actions {
646 + display: flex;
647 + gap: 10px;
648 + align-items: center;
649 +}
650 +
651 +.search-btn {
652 + background: #1890ff;
653 + color: white;
654 + border: none;
655 + padding: 4px 8px;
656 + border-radius: 3px;
657 + font-size: 11px;
658 + cursor: pointer;
659 + height: 24px;
660 + display: flex;
661 + align-items: center;
662 + gap: 4px;
663 +}
664 +
665 +.reset-btn {
666 + background: #ff7875;
667 + color: white;
668 + border: none;
669 + padding: 4px 8px;
670 + border-radius: 3px;
671 + font-size: 11px;
672 + cursor: pointer;
673 + height: 24px;
674 + display: flex;
675 + align-items: center;
676 + gap: 4px;
677 +}
678 +
679 +.action-section {
680 + background: white;
681 + padding: 12px 16px;
682 + border-bottom: 1px solid #e0e0e0;
683 +}
684 +
685 +.action-buttons {
686 + display: flex;
687 + gap: 6px;
688 +}
689 +
690 +.action-btn {
691 + padding: 4px 8px;
692 + border: none;
693 + border-radius: 3px;
694 + cursor: pointer;
695 + font-size: 11px;
696 + height: 24px;
697 + display: flex;
698 + align-items: center;
699 + gap: 4px;
700 +}
701 +
702 +.action-btn.primary {
703 + background: #28a745;
704 + color: white;
705 +}
706 +
707 +.action-btn.secondary {
708 + background: #6c757d;
709 + color: white;
710 +}
711 +
712 +.table-section {
713 + background: white;
714 + margin: 16px;
715 + border-radius: 6px;
716 + overflow: hidden;
717 + box-shadow: 0 2px 8px rgba(0,0,0,0.1);
718 +}
719 +
720 +.table-header {
721 + padding: 8px 16px;
722 + background: #fafafa;
723 + border-bottom: 1px solid #e0e0e0;
724 + display: flex;
725 + justify-content: flex-end;
726 +}
727 +
728 +.table-controls {
729 + display: flex;
730 + gap: 4px;
731 +}
732 +
733 +.control-btn {
734 + width: 24px;
735 + height: 24px;
736 + border: 1px solid #d9d9d9;
737 + background: white;
738 + border-radius: 3px;
739 + cursor: pointer;
740 + display: flex;
741 + align-items: center;
742 + justify-content: center;
743 + font-size: 11px;
744 +}
745 +
746 +.table-container {
747 + position: relative;
748 + overflow-x: auto;
749 +}
750 +
751 +.loading-overlay {
752 + position: absolute;
753 + top: 0;
754 + left: 0;
755 + right: 0;
756 + bottom: 0;
757 + background: rgba(255,255,255,0.8);
758 + display: flex;
759 + align-items: center;
760 + justify-content: center;
761 + z-index: 10;
762 +}
763 +
764 +.loading-spinner {
765 + padding: 20px;
766 + background: white;
767 + border-radius: 4px;
768 + box-shadow: 0 2px 8px rgba(0,0,0,0.1);
769 +}
770 +
771 +.data-table {
772 + width: 100%;
773 + border-collapse: collapse;
774 + font-size: 11px;
775 +}
776 +
777 +.data-table th,
778 +.data-table td {
779 + padding: 8px 6px;
780 + text-align: left;
781 + border-bottom: 1px solid #e0e0e0;
782 +}
783 +
784 +.data-table th {
785 + background-color: #fafafa;
786 + font-weight: 500;
787 + color: #333;
788 + font-size: 11px;
789 +}
790 +
791 +.data-table tbody tr:hover {
792 + background-color: #f5f5f5;
793 +}
794 +
795 +.action-col {
796 + white-space: nowrap;
797 +}
798 +
799 +.action-col button {
800 + margin-right: 5px;
801 + padding: 2px 6px;
802 + margin: 0 1px;
803 + border: none;
804 + border-radius: 2px;
805 + cursor: pointer;
806 + font-size: 10px;
807 + height: 20px;
808 + display: inline-flex;
809 + align-items: center;
810 + gap: 2px;
811 +}
812 +
813 +.view-btn {
814 + background: #17a2b8;
815 + color: white;
816 +}
817 +
818 +.edit-btn {
819 + background: #ffc107;
820 + color: #333;
821 +}
822 +
823 +.status-btn {
824 + background: #28a745;
825 + color: white;
826 +}
827 +
828 +.print-btn {
829 + background: #6c757d;
830 + color: white;
831 +}
832 +
833 +.status-active {
834 + color: #28a745;
835 + font-weight: 500;
836 +}
837 +
838 +.status-inactive {
839 + color: #dc3545;
840 + font-weight: 500;
841 +}
842 +
843 +.table-footer {
844 + padding: 8px 16px;
845 + border-top: 1px solid #e0e0e0;
846 + display: flex;
847 + justify-content: space-between;
848 + align-items: center;
849 + background: #fafafa;
850 +}
851 +
852 +.pagination-left {
853 + display: flex;
854 + align-items: center;
855 + gap: 20px;
856 +}
857 +
858 +.pagination-info {
859 + font-size: 12px;
860 + color: #666;
861 +}
862 +
863 +.page-size-selector {
864 + display: flex;
865 + align-items: center;
866 + gap: 8px;
867 +}
868 +
869 +.page-size-selector label {
870 + font-size: 12px;
871 + color: #666;
872 +}
873 +
874 +.page-size-select {
875 + padding: 4px 8px;
876 + border: 1px solid #d9d9d9;
877 + border-radius: 4px;
878 + font-size: 12px;
879 + background: white;
880 + cursor: pointer;
881 + transition: border-color 0.3s;
882 +}
883 +
884 +.pagination-container {
885 + display: flex;
886 + align-items: center;
887 + background: #f8f9fa;
888 + border: 1px solid #e9ecef;
889 + border-radius: 8px;
890 + overflow: hidden;
891 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
892 +}
893 +
894 +.pagination-btn {
895 + padding: 2px 6px;
896 + border: none;
897 + background: white;
898 + color: #6c757d;
899 + font-size: 11px;
900 + font-weight: 500;
901 + cursor: pointer;
902 + transition: all 0.2s;
903 + border-right: 1px solid #e9ecef;
904 + height: 20px;
905 +}
906 +
907 +.pagination-btn:hover:not(:disabled) {
908 + background: #e9ecef;
909 + color: #495057;
910 +}
911 +
912 +.pagination-btn:disabled {
913 + background: #f8f9fa;
914 + color: #adb5bd;
915 + cursor: not-allowed;
916 +}
917 +
918 +.pagination-pages {
919 + display: flex;
920 + align-items: center;
921 +}
922 +
923 +.page-number {
924 + padding: 2px 6px;
925 + border: none;
926 + background: white;
927 + color: #6c757d;
928 + font-size: 11px;
929 + font-weight: 500;
930 + cursor: pointer;
931 + transition: all 0.2s;
932 + border-right: 1px solid #e9ecef;
933 + min-width: 28px;
934 + text-align: center;
935 + height: 20px;
936 +}
937 +
938 +.page-number:hover {
939 + background: #e9ecef;
940 + color: #495057;
941 +}
942 +
943 +.page-number.active {
944 + background: #6c757d;
945 + color: white;
946 + font-weight: 600;
947 +}
948 +
949 +.page-number.active:hover {
950 + background: #5a6268;
951 +}
952 +
953 +.dialog-overlay {
954 + position: fixed;
955 + top: 0;
956 + left: 0;
957 + right: 0;
958 + bottom: 0;
959 + background: rgba(0,0,0,0.5);
960 + display: flex;
961 + align-items: center;
962 + justify-content: center;
963 + z-index: 1000;
964 +}
965 +
966 +.dialog-content {
967 + background: white;
968 + border-radius: 8px;
969 + max-width: 600px;
970 + width: 90%;
971 + max-height: 80vh;
972 + overflow-y: auto;
973 +}
974 +
975 +.dialog-content.large-dialog {
976 + max-width: 1000px;
977 +}
978 +
979 +.dialog-header {
980 + padding: 20px;
981 + border-bottom: 1px solid #eee;
982 + display: flex;
983 + justify-content: space-between;
984 + align-items: center;
985 +}
986 +
987 +.dialog-header h3 {
988 + margin: 0;
989 + color: #333;
990 +}
991 +
992 +.close-btn {
993 + background: none;
994 + border: none;
995 + font-size: 24px;
996 + cursor: pointer;
997 + color: #999;
998 +}
999 +
1000 +.dialog-body {
1001 + padding: 20px;
1002 +}
1003 +
1004 +.form-row {
1005 + display: flex;
1006 + gap: 20px;
1007 + margin-bottom: 15px;
1008 +}
1009 +
1010 +.form-item {
1011 + flex: 1;
1012 + display: flex;
1013 + flex-direction: column;
1014 + gap: 5px;
1015 +}
1016 +
1017 +.form-item.full-width {
1018 + flex: 100%;
1019 +}
1020 +
1021 +.form-item label {
1022 + font-weight: 500;
1023 + color: #333;
1024 +}
1025 +
1026 +.form-item label.required::after {
1027 + content: ' *';
1028 + color: #dc3545;
1029 +}
1030 +
1031 +.form-input, .form-select, .form-textarea {
1032 + padding: 8px 12px;
1033 + border: 1px solid #ddd;
1034 + border-radius: 4px;
1035 + font-size: 14px;
1036 +}
1037 +
1038 +.form-textarea {
1039 + resize: vertical;
1040 + min-height: 80px;
1041 +}
1042 +
1043 +.form-actions {
1044 + display: flex;
1045 + justify-content: flex-end;
1046 + gap: 10px;
1047 + margin-top: 30px;
1048 + padding-top: 20px;
1049 + border-top: 1px solid #eee;
1050 +}
1051 +
1052 +.cancel-btn, .submit-btn {
1053 + padding: 10px 20px;
1054 + border: none;
1055 + border-radius: 4px;
1056 + cursor: pointer;
1057 + font-weight: 500;
1058 +}
1059 +
1060 +.cancel-btn {
1061 + background: #6c757d;
1062 + color: white;
1063 +}
1064 +
1065 +.submit-btn {
1066 + background: #007bff;
1067 + color: white;
1068 +}
1069 +
1070 +.detail-content {
1071 + max-height: 60vh;
1072 + overflow-y: auto;
1073 +}
1074 +
1075 +.detail-section {
1076 + margin-bottom: 30px;
1077 +}
1078 +
1079 +.detail-section h4 {
1080 + margin: 0 0 15px 0;
1081 + color: #333;
1082 + border-bottom: 2px solid #007bff;
1083 + padding-bottom: 8px;
1084 +}
1085 +
1086 +.detail-grid {
1087 + display: grid;
1088 + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
1089 + gap: 15px;
1090 +}
1091 +
1092 +.detail-item {
1093 + display: flex;
1094 + flex-direction: column;
1095 + gap: 5px;
1096 +}
1097 +
1098 +.detail-item label {
1099 + font-weight: 500;
1100 + color: #666;
1101 + font-size: 14px;
1102 +}
1103 +
1104 +.detail-item span {
1105 + color: #333;
1106 + font-size: 16px;
1107 +}
1108 +</style>
1 +<template>
2 + <div class="order-container">
3 + <!-- 搜索区域 -->
4 + <div class="search-section">
5 + <div class="search-row">
6 + <div class="search-item">
7 + <label>订单编号:</label>
8 + <input
9 + v-model="searchParams.orderNo"
10 + class="search-input"
11 + placeholder="请输入订单编号"
12 + />
13 + </div>
14 + <div class="search-item">
15 + <label>经销商编码:</label>
16 + <input
17 + v-model="searchParams.dealerCode"
18 + class="search-input"
19 + placeholder="请输入经销商编码"
20 + />
21 + </div>
22 + <div class="search-item">
23 + <label>经销商名称:</label>
24 + <input
25 + v-model="searchParams.dealerName"
26 + class="search-input"
27 + placeholder="请输入经销商名称"
28 + />
29 + </div>
30 + <div class="search-item">
31 + <label>出库状态:</label>
32 + <select v-model="searchParams.deliveryStatus" class="search-select">
33 + <option value="">所有</option>
34 + <option value="0">未出库</option>
35 + <option value="1">已出库</option>
36 + </select>
37 + </div>
38 + </div>
39 + <div class="search-row">
40 + <div class="search-item">
41 + <label>开票状态:</label>
42 + <select v-model="searchParams.invoiceStatus" class="search-select">
43 + <option value="">所有</option>
44 + <option value="0">未开票</option>
45 + <option value="1">已开票</option>
46 + </select>
47 + </div>
48 + <div class="search-item">
49 + <label>返利计算状态:</label>
50 + <select v-model="searchParams.rebateCalcFlag" class="search-select">
51 + <option value="">所有</option>
52 + <option value="0">未计算</option>
53 + <option value="1">已计算</option>
54 + </select>
55 + </div>
56 + <div class="search-item">
57 + <label>数据验证状态:</label>
58 + <select v-model="searchParams.verifyStatus" class="search-select">
59 + <option value="">所有</option>
60 + <option value="0">待验证</option>
61 + <option value="1">验证通过</option>
62 + <option value="2">验证失败</option>
63 + </select>
64 + </div>
65 + <div class="search-actions">
66 + <button @click="handleSearch" class="search-btn">🔍 搜索</button>
67 + <button @click="handleReset" class="reset-btn">🔄 重置</button>
68 + </div>
69 + </div>
70 + </div>
71 +
72 + <!-- 操作按钮区域 -->
73 + <div class="action-section">
74 + <div class="action-buttons">
75 + <button @click="handleAdd" class="action-btn primary">✨ 新增</button>
76 + <button @click="handleBatchDelete" class="action-btn danger">🗑️ 删除</button>
77 + <button @click="handleExport" class="action-btn secondary">📋 导出</button>
78 + </div>
79 + </div>
80 +
81 + <!-- 数据表格 -->
82 + <div class="table-section">
83 + <div class="table-header">
84 + <div class="table-controls">
85 + <button @click="handleTableSearch" class="control-btn">🔍</button>
86 + <button @click="handleTableRefresh" class="control-btn">🔄</button>
87 + <button @click="handleTableExport" class="control-btn">📋</button>
88 + <button @click="handleTableViewToggle" class="control-btn">⊞</button>
89 + </div>
90 + </div>
91 +
92 + <div class="table-container">
93 + <div v-if="loading" class="loading-overlay">
94 + <div class="loading-spinner">加载中...</div>
95 + </div>
96 + <table class="data-table">
97 + <thead>
98 + <tr>
99 + <th>
100 + <input
101 + type="checkbox"
102 + class="select-all"
103 + v-model="selectAll"
104 + @change="handleSelectAll"
105 + />
106 + </th>
107 + <th>订单ID</th>
108 + <th class="sortable">订单编号 ↕️</th>
109 + <th>经销商编码</th>
110 + <th>经销商名称</th>
111 + <th>订单日期</th>
112 + <th>订单金额</th>
113 + <th>返利金额</th>
114 + <th>出库状态</th>
115 + <th>开票状态</th>
116 + <th>返利计算</th>
117 + <th>验证状态</th>
118 + <th class="sortable">创建时间 ↕️</th>
119 + <th>操作</th>
120 + </tr>
121 + </thead>
122 + <tbody>
123 + <tr v-for="order in orderList" :key="order.orderId">
124 + <td>
125 + <input
126 + type="checkbox"
127 + class="row-checkbox"
128 + :value="order.orderId"
129 + v-model="selectedOrders"
130 + />
131 + </td>
132 + <td>{{ order.orderId }}</td>
133 + <td>{{ order.orderNo }}</td>
134 + <td>{{ order.dealerCode }}</td>
135 + <td>{{ order.dealerName }}</td>
136 + <td>{{ formatDate(order.orderDate) }}</td>
137 + <td>{{ formatCurrency(order.totalAmount) }}</td>
138 + <td>{{ formatCurrency(order.rebateAmount || 0) }}</td>
139 + <td>
140 + <span :class="getDeliveryStatusClass(order.deliveryStatus)">
141 + {{ getDeliveryStatusText(order.deliveryStatus) }}
142 + </span>
143 + </td>
144 + <td>
145 + <span :class="getInvoiceStatusClass(order.invoiceStatus)">
146 + {{ getInvoiceStatusText(order.invoiceStatus) }}
147 + </span>
148 + </td>
149 + <td>
150 + <span :class="getRebateCalcClass(order.rebateCalcFlag)">
151 + {{ getRebateCalcText(order.rebateCalcFlag) }}
152 + </span>
153 + </td>
154 + <td>
155 + <span :class="getVerifyStatusClass(order.verifyStatus)">
156 + {{ getVerifyStatusText(order.verifyStatus) }}
157 + </span>
158 + </td>
159 + <td>{{ formatTime(order.createTime || '') }}</td>
160 + <td class="action-col">
161 + <button @click="handleView(order)" class="view-btn">👁️ 查看</button>
162 + <button @click="handleEdit(order)" class="edit-btn">✏️ 编辑</button>
163 + <button @click="handleDelete(order)" class="delete-btn">🗑️ 删除</button>
164 + </td>
165 + </tr>
166 + </tbody>
167 + </table>
168 + </div>
169 +
170 + <!-- 分页信息 -->
171 + <div class="table-footer">
172 + <div class="pagination-left">
173 + <div class="pagination-info">
174 + 共 {{ total }} 条记录,第 {{ currentPage }} / {{ totalPages }} 页
175 + </div>
176 + <div class="page-size-selector">
177 + <label>每页显示:</label>
178 + <select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
179 + <option value="10">10条</option>
180 + <option value="20">20条</option>
181 + <option value="50">50条</option>
182 + <option value="100">100条</option>
183 + </select>
184 + </div>
185 + </div>
186 + <div class="pagination-container">
187 + <button
188 + @click="handlePageChange(currentPage - 1)"
189 + :disabled="currentPage <= 1"
190 + class="pagination-btn prev-btn"
191 + >
192 + 上一页
193 + </button>
194 + <div class="pagination-pages">
195 + <button
196 + v-for="page in getPageNumbers()"
197 + :key="page"
198 + @click="handlePageChange(page)"
199 + :class="['page-number', { active: page === currentPage }]"
200 + >
201 + {{ page }}
202 + </button>
203 + </div>
204 + <button
205 + @click="handlePageChange(currentPage + 1)"
206 + :disabled="currentPage >= totalPages"
207 + class="pagination-btn next-btn"
208 + >
209 + 下一页
210 + </button>
211 + </div>
212 + </div>
213 + </div>
214 +
215 + <!-- 新增/编辑对话框 -->
216 + <div v-if="showDialog" class="dialog-overlay" @click="closeDialog">
217 + <div class="dialog-content large-dialog" @click.stop>
218 + <div class="dialog-header">
219 + <h3>{{ isEdit ? '编辑订单' : '新增订单' }}</h3>
220 + <button @click="closeDialog" class="close-btn">×</button>
221 + </div>
222 + <div class="dialog-body">
223 + <form @submit.prevent="handleSubmit">
224 + <!-- 订单基本信息 -->
225 + <div class="form-section">
226 + <h4>订单基本信息</h4>
227 + <div class="form-row">
228 + <div class="form-item">
229 + <label class="required">订单编号:</label>
230 + <input
231 + v-model="formData.orderNo"
232 + class="form-input"
233 + placeholder="请输入订单编号"
234 + required
235 + />
236 + </div>
237 + <div class="form-item">
238 + <label class="required">经销商编码:</label>
239 + <input
240 + v-model="formData.dealerCode"
241 + class="form-input"
242 + placeholder="请输入经销商编码"
243 + required
244 + />
245 + </div>
246 + </div>
247 + <div class="form-row">
248 + <div class="form-item">
249 + <label class="required">经销商名称:</label>
250 + <input
251 + v-model="formData.dealerName"
252 + class="form-input"
253 + placeholder="请输入经销商名称"
254 + required
255 + />
256 + </div>
257 + <div class="form-item">
258 + <label class="required">订单日期:</label>
259 + <input
260 + v-model="formData.orderDate"
261 + class="form-input"
262 + type="datetime-local"
263 + required
264 + />
265 + </div>
266 + </div>
267 + <div class="form-row">
268 + <div class="form-item">
269 + <label class="required">订单总金额:</label>
270 + <input
271 + v-model.number="formData.totalAmount"
272 + class="form-input"
273 + type="number"
274 + step="0.01"
275 + placeholder="请输入订单总金额"
276 + required
277 + />
278 + </div>
279 + <div class="form-item">
280 + <label>返利金额:</label>
281 + <input
282 + v-model.number="formData.rebateAmount"
283 + class="form-input"
284 + type="number"
285 + step="0.01"
286 + placeholder="请输入返利金额"
287 + />
288 + </div>
289 + </div>
290 + </div>
291 +
292 + <!-- 订单明细 -->
293 + <div class="form-section">
294 + <h4>订单明细</h4>
295 + <div class="order-items-header">
296 + <button type="button" @click="addOrderItem" class="add-item-btn">+ 添加明细</button>
297 + </div>
298 + <div class="order-items-container">
299 + <div v-for="(item, index) in formData.orderItems" :key="index" class="order-item">
300 + <div class="item-row-single">
301 + <div class="form-item">
302 + <label class="required">产品编码:</label>
303 + <input
304 + v-model="item.productCode"
305 + class="form-input"
306 + placeholder="请输入产品编码"
307 + required
308 + />
309 + </div>
310 + <div class="form-item">
311 + <label class="required">产品名称:</label>
312 + <input
313 + v-model="item.productName"
314 + class="form-input"
315 + placeholder="请输入产品名称"
316 + required
317 + />
318 + </div>
319 + <div class="form-item">
320 + <label class="required">商品数量:</label>
321 + <input
322 + v-model.number="item.productQty"
323 + class="form-input"
324 + type="number"
325 + placeholder="请输入商品数量"
326 + @input="calculateItemTotal(index)"
327 + required
328 + />
329 + </div>
330 + <div class="form-item">
331 + <label class="required">单价:</label>
332 + <input
333 + v-model.number="item.unitPrice"
334 + class="form-input"
335 + type="number"
336 + step="0.01"
337 + placeholder="请输入单价"
338 + @input="calculateItemTotal(index)"
339 + required
340 + />
341 + </div>
342 + <div class="form-item">
343 + <label>明细金额:</label>
344 + <input
345 + v-model.number="item.itemAmount"
346 + class="form-input"
347 + type="number"
348 + step="0.01"
349 + readonly
350 + />
351 + </div>
352 + <button type="button" @click="removeOrderItem(index)" class="remove-item-btn">删除</button>
353 + </div>
354 + </div>
355 + </div>
356 + </div>
357 +
358 + <div class="form-actions">
359 + <button type="button" @click="closeDialog" class="cancel-btn">取消</button>
360 + <button type="submit" class="submit-btn">确定</button>
361 + </div>
362 + </form>
363 + </div>
364 + </div>
365 + </div>
366 +
367 + <!-- 订单详情对话框 -->
368 + <div v-if="showDetailDialog" class="dialog-overlay" @click="closeDetailDialog">
369 + <div class="dialog-content large-dialog" @click.stop>
370 + <div class="dialog-header">
371 + <h3>订单详情</h3>
372 + <button @click="closeDetailDialog" class="close-btn">×</button>
373 + </div>
374 + <div class="dialog-body">
375 + <div v-if="orderDetail" class="detail-content">
376 + <div class="detail-section">
377 + <h4>订单基本信息</h4>
378 + <div class="detail-grid">
379 + <div class="detail-item">
380 + <label>订单编号:</label>
381 + <span>{{ orderDetail.orderNo }}</span>
382 + </div>
383 + <div class="detail-item">
384 + <label>经销商编码:</label>
385 + <span>{{ orderDetail.dealerCode }}</span>
386 + </div>
387 + <div class="detail-item">
388 + <label>经销商名称:</label>
389 + <span>{{ orderDetail.dealerName }}</span>
390 + </div>
391 + <div class="detail-item">
392 + <label>订单日期:</label>
393 + <span>{{ formatDate(orderDetail.orderDate) }}</span>
394 + </div>
395 + <div class="detail-item">
396 + <label>订单总金额:</label>
397 + <span>{{ formatCurrency(orderDetail.totalAmount) }}</span>
398 + </div>
399 + <div class="detail-item">
400 + <label>返利金额:</label>
401 + <span>{{ formatCurrency(orderDetail.rebateAmount || 0) }}</span>
402 + </div>
403 + <div class="detail-item">
404 + <label>出库状态:</label>
405 + <span :class="getDeliveryStatusClass(orderDetail.deliveryStatus)">
406 + {{ getDeliveryStatusText(orderDetail.deliveryStatus) }}
407 + </span>
408 + </div>
409 + <div class="detail-item">
410 + <label>开票状态:</label>
411 + <span :class="getInvoiceStatusClass(orderDetail.invoiceStatus)">
412 + {{ getInvoiceStatusText(orderDetail.invoiceStatus) }}
413 + </span>
414 + </div>
415 + </div>
416 + </div>
417 +
418 + <div class="detail-section" v-if="orderDetail.orderItems && orderDetail.orderItems.length > 0">
419 + <h4>订单明细</h4>
420 + <table class="detail-table">
421 + <thead>
422 + <tr>
423 + <th>产品编码</th>
424 + <th>产品名称</th>
425 + <th>商品数量</th>
426 + <th>单价</th>
427 + <th>明细金额</th>
428 + </tr>
429 + </thead>
430 + <tbody>
431 + <tr v-for="item in orderDetail.orderItems" :key="item.itemId">
432 + <td>{{ item.productCode }}</td>
433 + <td>{{ item.productName }}</td>
434 + <td>{{ item.productQty }}</td>
435 + <td>{{ formatCurrency(item.unitPrice) }}</td>
436 + <td>{{ formatCurrency(item.itemAmount) }}</td>
437 + </tr>
438 + </tbody>
439 + </table>
440 + </div>
441 + </div>
442 + </div>
443 + </div>
444 + </div>
445 + </div>
446 +</template>
447 +
448 +<script setup lang="ts">
449 +import { ref, reactive, computed, onMounted } from 'vue'
450 +import { orderApi, type OrderInfo, type OrderQueryReq, type OrderAddReq, type OrderUpdateReq, type OrderItemAddReq } from '../../api/order'
451 +
452 +// 响应式数据
453 +const loading = ref(false)
454 +const orderList = ref<OrderInfo[]>([])
455 +const selectedOrders = ref<number[]>([])
456 +const showDialog = ref(false)
457 +const showDetailDialog = ref(false)
458 +const isEdit = ref(false)
459 +const orderDetail = ref<OrderInfo | null>(null)
460 +
461 +// 搜索参数
462 +const searchParams = reactive<OrderQueryReq>({
463 + orderNo: '',
464 + dealerCode: '',
465 + dealerName: '',
466 + deliveryStatus: undefined,
467 + invoiceStatus: undefined,
468 + rebateCalcFlag: undefined,
469 + verifyStatus: undefined,
470 + pageNum: 1,
471 + pageSize: 10
472 +})
473 +
474 +// 分页信息
475 +const pagination = reactive({
476 + total: 0,
477 + pageNum: 1,
478 + pageSize: 10
479 +})
480 +
481 +// 分页计算属性
482 +const total = computed(() => pagination.total)
483 +const currentPage = computed(() => pagination.pageNum)
484 +const pageSize = computed({
485 + get: () => pagination.pageSize,
486 + set: (value: number) => {
487 + pagination.pageSize = value
488 + }
489 +})
490 +const totalPages = computed(() => Math.ceil(pagination.total / pagination.pageSize))
491 +
492 +// 表单数据
493 +const formData = reactive<OrderAddReq & { orderId?: number }>({
494 + orderNo: '',
495 + dealerCode: '',
496 + dealerName: '',
497 + orderDate: '',
498 + totalAmount: 0,
499 + rebateAmount: 0,
500 + deliveryStatus: 0,
501 + invoiceStatus: 0,
502 + rebateCalcFlag: 0,
503 + dataSource: '',
504 + verifyStatus: 0,
505 + orderItems: []
506 +})
507 +
508 +// 计算属性
509 +const selectAll = computed({
510 + get: () => selectedOrders.value.length === orderList.value.length && orderList.value.length > 0,
511 + set: (value: boolean) => {
512 + if (value) {
513 + selectedOrders.value = orderList.value.map(order => order.orderId)
514 + } else {
515 + selectedOrders.value = []
516 + }
517 + }
518 +})
519 +
520 +// 获取页码数组
521 +const getPageNumbers = () => {
522 + const totalPages = Math.ceil(pagination.total / pagination.pageSize)
523 + const currentPage = pagination.pageNum
524 + const maxVisible = 5
525 + const start = Math.max(1, currentPage - Math.floor(maxVisible / 2))
526 + const end = Math.min(totalPages, start + maxVisible - 1)
527 + const pages = []
528 + for (let i = start; i <= end; i++) {
529 + pages.push(i)
530 + }
531 + return pages
532 +}
533 +
534 +// 状态相关方法
535 +const getDeliveryStatusText = (status: number) => {
536 + return status === 1 ? '已出库' : '未出库'
537 +}
538 +
539 +const getDeliveryStatusClass = (status: number) => {
540 + return status === 1 ? 'status-active' : 'status-inactive'
541 +}
542 +
543 +const getInvoiceStatusText = (status: number) => {
544 + return status === 1 ? '已开票' : '未开票'
545 +}
546 +
547 +const getInvoiceStatusClass = (status: number) => {
548 + return status === 1 ? 'status-active' : 'status-inactive'
549 +}
550 +
551 +const getRebateCalcText = (status: number) => {
552 + return status === 1 ? '已计算' : '未计算'
553 +}
554 +
555 +const getRebateCalcClass = (status: number) => {
556 + return status === 1 ? 'status-active' : 'status-inactive'
557 +}
558 +
559 +const getVerifyStatusText = (status: number) => {
560 + const statusMap = { 0: '待验证', 1: '验证通过', 2: '验证失败' }
561 + return statusMap[status as keyof typeof statusMap] || '未知'
562 +}
563 +
564 +const getVerifyStatusClass = (status: number) => {
565 + const classMap = { 0: 'status-pending', 1: 'status-active', 2: 'status-inactive' }
566 + return classMap[status as keyof typeof classMap] || 'status-inactive'
567 +}
568 +
569 +// 格式化方法
570 +const formatDate = (date: string) => {
571 + if (!date) return '-'
572 + return new Date(date).toLocaleDateString()
573 +}
574 +
575 +const formatTime = (date: string) => {
576 + if (!date) return '-'
577 + return new Date(date).toLocaleString()
578 +}
579 +
580 +const formatCurrency = (amount: number) => {
581 + if (amount === null || amount === undefined) return '-'
582 + return '¥' + amount.toFixed(2)
583 +}
584 +
585 +// 消息提示
586 +const showMessage = (message: string, type: 'success' | 'error' | 'warning' = 'success') => {
587 + // 这里可以集成消息提示组件
588 + console.log(`${type}: ${message}`)
589 + alert(message)
590 +}
591 +
592 +// 方法
593 +const fetchOrders = async () => {
594 + try {
595 + loading.value = true
596 + const params = { ...searchParams, pageNum: pagination.pageNum, pageSize: pagination.pageSize }
597 + const response = await orderApi.getOrderList(params) as any
598 + console.log('API响应:', response) // 添加调试日志
599 +
600 + // 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
601 + // 所以response就是data部分,包含records、total等字段
602 + if (response && response.records) {
603 + orderList.value = response.records || []
604 + pagination.total = response.total || 0
605 + pagination.pageNum = response.current || 1
606 + pagination.pageSize = response.size || 10
607 + console.log('数据加载成功:', orderList.value.length, '条记录')
608 + } else {
609 + orderList.value = []
610 + pagination.total = 0
611 + console.error('API响应错误:', response)
612 + showMessage('获取订单列表失败', 'error')
613 + }
614 + } catch (error) {
615 + console.error('获取订单列表失败:', error)
616 + showMessage('获取订单列表失败, 请重试', 'error')
617 + } finally {
618 + loading.value = false
619 + }
620 +}
621 +
622 +const handleSearch = () => {
623 + pagination.pageNum = 1
624 + fetchOrders()
625 +}
626 +
627 +const handleReset = () => {
628 + Object.assign(searchParams, {
629 + orderNo: '',
630 + dealerCode: '',
631 + dealerName: '',
632 + deliveryStatus: undefined,
633 + invoiceStatus: undefined,
634 + rebateCalcFlag: undefined,
635 + verifyStatus: undefined
636 + })
637 + pagination.pageNum = 1
638 + fetchOrders()
639 +}
640 +
641 +const handleAdd = () => {
642 + isEdit.value = false
643 + Object.assign(formData, {
644 + orderNo: '',
645 + dealerCode: '',
646 + dealerName: '',
647 + orderDate: '',
648 + totalAmount: 0,
649 + rebateAmount: 0,
650 + deliveryStatus: 0,
651 + invoiceStatus: 0,
652 + rebateCalcFlag: 0,
653 + dataSource: '',
654 + verifyStatus: 0,
655 + orderItems: []
656 + })
657 + showDialog.value = true
658 +}
659 +
660 +const handleEdit = async (order: OrderInfo) => {
661 + try {
662 + // 调用后端接口获取完整的订单详情(包括订单明细)
663 + const response = await orderApi.getOrderDetail(order.orderId) as any
664 + console.log('编辑订单API响应:', response)
665 +
666 + if (response && response.orderId) {
667 + isEdit.value = true
668 + Object.assign(formData, {
669 + orderId: response.orderId,
670 + orderNo: response.orderNo,
671 + dealerCode: response.dealerCode,
672 + dealerName: response.dealerName,
673 + orderDate: response.orderDate,
674 + totalAmount: response.totalAmount,
675 + rebateAmount: response.rebateAmount || 0,
676 + deliveryStatus: response.deliveryStatus,
677 + invoiceStatus: response.invoiceStatus,
678 + rebateCalcFlag: response.rebateCalcFlag,
679 + dataSource: response.dataSource || '',
680 + verifyStatus: response.verifyStatus,
681 + orderItems: response.orderItems || []
682 + })
683 + showDialog.value = true
684 + } else {
685 + showMessage('获取订单详情失败', 'error')
686 + }
687 + } catch (error) {
688 + console.error('获取订单详情失败:', error)
689 + showMessage('获取订单详情失败, 请重试', 'error')
690 + }
691 +}
692 +
693 +const handleView = async (order: OrderInfo) => {
694 + try {
695 + const response = await orderApi.getOrderDetail(order.orderId) as any
696 + console.log('订单详情API响应:', response)
697 +
698 + // 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
699 + if (response && response.orderId) {
700 + orderDetail.value = response
701 + showDetailDialog.value = true
702 + } else {
703 + showMessage('获取订单详情失败', 'error')
704 + }
705 + } catch (error) {
706 + console.error('获取订单详情失败:', error)
707 + showMessage('获取订单详情失败, 请重试', 'error')
708 + }
709 +}
710 +
711 +const handleDelete = async (order: OrderInfo) => {
712 + if (confirm(`确定要删除订单"${order.orderNo}"吗?`)) {
713 + try {
714 + await orderApi.deleteOrder(order.orderId)
715 + showMessage('删除成功', 'success')
716 + fetchOrders()
717 + } catch (error) {
718 + console.error('删除订单失败:', error)
719 + showMessage('删除订单失败, 请重试', 'error')
720 + }
721 + }
722 +}
723 +
724 +const handleBatchDelete = async () => {
725 + if (selectedOrders.value.length === 0) {
726 + showMessage('请选择要删除的订单', 'warning')
727 + return
728 + }
729 + if (confirm(`确定要删除选中的 ${selectedOrders.value.length} 个订单吗?`)) {
730 + try {
731 + await orderApi.batchDeleteOrders(selectedOrders.value)
732 + showMessage('批量删除成功', 'success')
733 + selectedOrders.value = []
734 + fetchOrders()
735 + } catch (error) {
736 + console.error('批量删除订单失败:', error)
737 + showMessage('批量删除订单失败, 请重试', 'error')
738 + }
739 + }
740 +}
741 +
742 +
743 +const handleExport = () => {
744 + showMessage('导出功能开发中...', 'warning')
745 +}
746 +
747 +const handleSelectAll = () => {
748 + // 已在计算属性中处理
749 +}
750 +
751 +const handlePageChange = (page: number) => {
752 + if (page >= 1 && page <= totalPages.value) {
753 + pagination.pageNum = page
754 + fetchOrders()
755 + }
756 +}
757 +
758 +const handlePageSizeChange = () => {
759 + pagination.pageNum = 1
760 + fetchOrders()
761 +}
762 +
763 +const handleTableSearch = () => {
764 + handleSearch()
765 +}
766 +
767 +const handleTableRefresh = () => {
768 + fetchOrders()
769 +}
770 +
771 +const handleTableExport = () => {
772 + handleExport()
773 +}
774 +
775 +const handleTableViewToggle = () => {
776 + showMessage('视图切换功能开发中...', 'warning')
777 +}
778 +
779 +// 订单明细相关方法
780 +const addOrderItem = () => {
781 + formData.orderItems.push({
782 + productCode: '',
783 + productName: '',
784 + productQty: 1,
785 + unitPrice: 0,
786 + itemAmount: 0
787 + })
788 +}
789 +
790 +const removeOrderItem = (index: number) => {
791 + formData.orderItems.splice(index, 1)
792 +}
793 +
794 +const calculateItemTotal = (index: number) => {
795 + const item = formData.orderItems[index]
796 + if (item.productQty && item.unitPrice) {
797 + item.itemAmount = item.productQty * item.unitPrice
798 + }
799 +}
800 +
801 +const handleSubmit = async () => {
802 + try {
803 + if (isEdit.value) {
804 + const updateData: OrderUpdateReq = {
805 + orderId: formData.orderId!,
806 + orderNo: formData.orderNo,
807 + dealerCode: formData.dealerCode,
808 + dealerName: formData.dealerName,
809 + orderDate: formData.orderDate,
810 + totalAmount: formData.totalAmount,
811 + rebateAmount: formData.rebateAmount,
812 + deliveryStatus: formData.deliveryStatus,
813 + invoiceStatus: formData.invoiceStatus,
814 + rebateCalcFlag: formData.rebateCalcFlag,
815 + dataSource: formData.dataSource,
816 + verifyStatus: formData.verifyStatus,
817 + orderItems: formData.orderItems.map(item => ({
818 + itemId: (item as any).itemId,
819 + productCode: item.productCode,
820 + productName: item.productName,
821 + productQty: item.productQty,
822 + unitPrice: item.unitPrice,
823 + itemAmount: item.itemAmount
824 + }))
825 + }
826 + await orderApi.updateOrder(updateData)
827 + showMessage('修改订单成功', 'success')
828 + } else {
829 + const addData: OrderAddReq = {
830 + orderNo: formData.orderNo,
831 + dealerCode: formData.dealerCode,
832 + dealerName: formData.dealerName,
833 + orderDate: formData.orderDate,
834 + totalAmount: formData.totalAmount,
835 + rebateAmount: formData.rebateAmount,
836 + deliveryStatus: formData.deliveryStatus,
837 + invoiceStatus: formData.invoiceStatus,
838 + rebateCalcFlag: formData.rebateCalcFlag,
839 + dataSource: formData.dataSource,
840 + verifyStatus: formData.verifyStatus,
841 + orderItems: formData.orderItems.map(item => ({
842 + productCode: item.productCode,
843 + productName: item.productName,
844 + productQty: item.productQty,
845 + unitPrice: item.unitPrice,
846 + itemAmount: item.itemAmount
847 + }))
848 + }
849 + await orderApi.addOrder(addData)
850 + showMessage('新增订单成功', 'success')
851 + }
852 + closeDialog()
853 + fetchOrders()
854 + } catch (error) {
855 + console.error('保存订单失败:', error)
856 + showMessage('保存订单失败, 请重试', 'error')
857 + }
858 +}
859 +
860 +const closeDialog = () => {
861 + showDialog.value = false
862 + formData.orderItems = []
863 +}
864 +
865 +const closeDetailDialog = () => {
866 + showDetailDialog.value = false
867 + orderDetail.value = null
868 +}
869 +
870 +// 生命周期
871 +onMounted(() => {
872 + fetchOrders()
873 +})
874 +</script>
875 +
876 +<style scoped>
877 +.order-container {
878 + padding: 0px;
879 + background: #f5f5f5;
880 + min-height: 100vh;
881 +}
882 +
883 +/* 搜索区域样式 */
884 +.search-section {
885 + background: white;
886 + padding: 16px 20px;
887 + border-bottom: 1px solid #e0e0e0;
888 +}
889 +
890 +.search-row {
891 + display: flex;
892 + gap: 20px;
893 + align-items: center;
894 + flex-wrap: wrap;
895 + margin-bottom: 12px;
896 +}
897 +
898 +.search-item {
899 + display: flex;
900 + align-items: center;
901 + gap: 8px;
902 +}
903 +
904 +.search-item label {
905 + font-size: 12px;
906 + color: #333;
907 + white-space: nowrap;
908 +}
909 +
910 +.search-input, .search-select {
911 + padding: 6px 8px;
912 + border: 1px solid #d9d9d9;
913 + border-radius: 4px;
914 + font-size: 12px;
915 + width: 120px;
916 +}
917 +
918 +.search-input:focus, .search-select:focus {
919 + outline: none;
920 + border-color: #1890ff;
921 + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
922 +}
923 +
924 +.search-actions {
925 + display: flex;
926 + gap: 10px;
927 + align-items: center;
928 +}
929 +
930 +.search-btn {
931 + background: #1890ff;
932 + color: white;
933 + border: none;
934 + padding: 4px 8px;
935 + border-radius: 3px;
936 + font-size: 11px;
937 + cursor: pointer;
938 + height: 24px;
939 + display: flex;
940 + align-items: center;
941 + gap: 4px;
942 +}
943 +
944 +.reset-btn {
945 + background: #ff7875;
946 + color: white;
947 + border: none;
948 + padding: 4px 8px;
949 + border-radius: 3px;
950 + font-size: 11px;
951 + cursor: pointer;
952 + height: 24px;
953 + display: flex;
954 + align-items: center;
955 + gap: 4px;
956 +}
957 +
958 +/* 操作按钮区域 */
959 +.action-section {
960 + background: white;
961 + padding: 12px 16px;
962 + border-bottom: 1px solid #e0e0e0;
963 +}
964 +
965 +.action-buttons {
966 + display: flex;
967 + gap: 6px;
968 +}
969 +
970 +.action-btn {
971 + padding: 4px 8px;
972 + border: none;
973 + border-radius: 3px;
974 + cursor: pointer;
975 + font-size: 11px;
976 + height: 24px;
977 + display: flex;
978 + align-items: center;
979 + gap: 4px;
980 +}
981 +
982 +.action-btn.primary {
983 + background: #28a745;
984 + color: white;
985 +}
986 +
987 +.action-btn.danger {
988 + background: #dc3545;
989 + color: white;
990 +}
991 +
992 +.action-btn.secondary {
993 + background: #6c757d;
994 + color: white;
995 +}
996 +
997 +/* 表格样式 */
998 +.table-section {
999 + background: white;
1000 + margin: 16px;
1001 + border-radius: 6px;
1002 + overflow: hidden;
1003 + box-shadow: 0 2px 8px rgba(0,0,0,0.1);
1004 +}
1005 +
1006 +.table-header {
1007 + padding: 8px 16px;
1008 + background: #fafafa;
1009 + border-bottom: 1px solid #e0e0e0;
1010 + display: flex;
1011 + justify-content: flex-end;
1012 +}
1013 +
1014 +.table-controls {
1015 + display: flex;
1016 + gap: 4px;
1017 +}
1018 +
1019 +.control-btn {
1020 + width: 24px;
1021 + height: 24px;
1022 + border: 1px solid #d9d9d9;
1023 + background: white;
1024 + border-radius: 3px;
1025 + cursor: pointer;
1026 + display: flex;
1027 + align-items: center;
1028 + justify-content: center;
1029 + font-size: 11px;
1030 +}
1031 +
1032 +.table-container {
1033 + position: relative;
1034 + overflow-x: auto;
1035 +}
1036 +
1037 +.loading-overlay {
1038 + position: absolute;
1039 + top: 0;
1040 + left: 0;
1041 + right: 0;
1042 + bottom: 0;
1043 + background: rgba(255,255,255,0.8);
1044 + display: flex;
1045 + align-items: center;
1046 + justify-content: center;
1047 + z-index: 10;
1048 +}
1049 +
1050 +.loading-spinner {
1051 + padding: 20px;
1052 + background: white;
1053 + border-radius: 4px;
1054 + box-shadow: 0 2px 8px rgba(0,0,0,0.1);
1055 +}
1056 +
1057 +.data-table {
1058 + width: 100%;
1059 + border-collapse: collapse;
1060 + font-size: 11px;
1061 +}
1062 +
1063 +.data-table th,
1064 +.data-table td {
1065 + padding: 8px 6px;
1066 + text-align: left;
1067 + border-bottom: 1px solid #e0e0e0;
1068 +}
1069 +
1070 +.data-table th {
1071 + background-color: #fafafa;
1072 + font-weight: 500;
1073 + color: #333;
1074 + font-size: 11px;
1075 +}
1076 +
1077 +.data-table tbody tr:hover {
1078 + background-color: #f5f5f5;
1079 +}
1080 +
1081 +.select-all, .row-checkbox {
1082 + margin: 0;
1083 +}
1084 +
1085 +.action-col {
1086 + white-space: nowrap;
1087 +}
1088 +
1089 +.action-col button {
1090 + margin-right: 5px;
1091 + padding: 2px 6px;
1092 + margin: 0 1px;
1093 + border: none;
1094 + border-radius: 2px;
1095 + cursor: pointer;
1096 + font-size: 10px;
1097 + height: 20px;
1098 + display: inline-flex;
1099 + align-items: center;
1100 + gap: 2px;
1101 +}
1102 +
1103 +.view-btn {
1104 + background: #17a2b8;
1105 + color: white;
1106 +}
1107 +
1108 +.edit-btn {
1109 + background: #ffc107;
1110 + color: #333;
1111 +}
1112 +
1113 +.delete-btn {
1114 + background: #dc3545;
1115 + color: white;
1116 +}
1117 +
1118 +.status-btn {
1119 + background: #28a745;
1120 + color: white;
1121 +}
1122 +
1123 +/* 状态样式 */
1124 +.status-active {
1125 + color: #28a745;
1126 + font-weight: 500;
1127 +}
1128 +
1129 +.status-inactive {
1130 + color: #dc3545;
1131 + font-weight: 500;
1132 +}
1133 +
1134 +.status-pending {
1135 + color: #ffc107;
1136 + font-weight: 500;
1137 +}
1138 +
1139 +/* 分页样式 */
1140 +.table-footer {
1141 + padding: 8px 16px;
1142 + border-top: 1px solid #e0e0e0;
1143 + display: flex;
1144 + justify-content: space-between;
1145 + align-items: center;
1146 + background: #fafafa;
1147 +}
1148 +
1149 +.pagination-left {
1150 + display: flex;
1151 + align-items: center;
1152 + gap: 20px;
1153 +}
1154 +
1155 +.pagination-info {
1156 + font-size: 12px;
1157 + color: #666;
1158 +}
1159 +
1160 +.page-size-selector {
1161 + display: flex;
1162 + align-items: center;
1163 + gap: 8px;
1164 +}
1165 +
1166 +.page-size-selector label {
1167 + font-size: 12px;
1168 + color: #666;
1169 +}
1170 +
1171 +.page-size-select {
1172 + padding: 4px 8px;
1173 + border: 1px solid #d9d9d9;
1174 + border-radius: 4px;
1175 + font-size: 12px;
1176 + background: white;
1177 + cursor: pointer;
1178 + transition: border-color 0.3s;
1179 +}
1180 +
1181 +.pagination-container {
1182 + display: flex;
1183 + align-items: center;
1184 + background: #f8f9fa;
1185 + border: 1px solid #e9ecef;
1186 + border-radius: 8px;
1187 + overflow: hidden;
1188 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
1189 +}
1190 +
1191 +.pagination-btn {
1192 + padding: 2px 6px;
1193 + border: none;
1194 + background: white;
1195 + color: #6c757d;
1196 + font-size: 11px;
1197 + font-weight: 500;
1198 + cursor: pointer;
1199 + transition: all 0.2s;
1200 + border-right: 1px solid #e9ecef;
1201 + height: 20px;
1202 +}
1203 +
1204 +.pagination-btn:hover:not(:disabled) {
1205 + background: #e9ecef;
1206 + color: #495057;
1207 +}
1208 +
1209 +.pagination-btn:disabled {
1210 + background: #f8f9fa;
1211 + color: #adb5bd;
1212 + cursor: not-allowed;
1213 +}
1214 +
1215 +.pagination-pages {
1216 + display: flex;
1217 + align-items: center;
1218 +}
1219 +
1220 +.page-number {
1221 + padding: 2px 6px;
1222 + border: none;
1223 + background: white;
1224 + color: #6c757d;
1225 + font-size: 11px;
1226 + font-weight: 500;
1227 + cursor: pointer;
1228 + transition: all 0.2s;
1229 + border-right: 1px solid #e9ecef;
1230 + min-width: 28px;
1231 + text-align: center;
1232 + height: 20px;
1233 +}
1234 +
1235 +.page-number:hover {
1236 + background: #e9ecef;
1237 + color: #495057;
1238 +}
1239 +
1240 +.page-number.active {
1241 + background: #6c757d;
1242 + color: white;
1243 + font-weight: 600;
1244 +}
1245 +
1246 +.page-number.active:hover {
1247 + background: #5a6268;
1248 +}
1249 +
1250 +/* 对话框样式 */
1251 +.dialog-overlay {
1252 + position: fixed;
1253 + top: 0;
1254 + left: 0;
1255 + right: 0;
1256 + bottom: 0;
1257 + background: rgba(0,0,0,0.5);
1258 + display: flex;
1259 + align-items: center;
1260 + justify-content: center;
1261 + z-index: 1000;
1262 +}
1263 +
1264 +.dialog-content {
1265 + background: white;
1266 + border-radius: 8px;
1267 + max-width: 400px;
1268 + width: 70%;
1269 + max-height: 80vh;
1270 + overflow-y: auto;
1271 +}
1272 +
1273 +.dialog-content.large-dialog {
1274 + max-width: 1000px;
1275 +}
1276 +
1277 +.dialog-header {
1278 + padding: 20px;
1279 + border-bottom: 1px solid #eee;
1280 + display: flex;
1281 + justify-content: space-between;
1282 + align-items: center;
1283 +}
1284 +
1285 +.dialog-header h3 {
1286 + margin: 0;
1287 + color: #333;
1288 +}
1289 +
1290 +.close-btn {
1291 + background: none;
1292 + border: none;
1293 + font-size: 24px;
1294 + cursor: pointer;
1295 + color: #999;
1296 +}
1297 +
1298 +.dialog-body {
1299 + padding: 20px;
1300 +}
1301 +
1302 +/* 表单样式 */
1303 +.form-section {
1304 + margin-bottom: 30px;
1305 +}
1306 +
1307 +.form-section h4 {
1308 + margin: 0 0 15px 0;
1309 + color: #333;
1310 + border-bottom: 2px solid #007bff;
1311 + padding-bottom: 8px;
1312 +}
1313 +
1314 +.form-row {
1315 + display: flex;
1316 + gap: 20px;
1317 + margin-bottom: 15px;
1318 +}
1319 +
1320 +.form-item {
1321 + flex: 1;
1322 + display: flex;
1323 + flex-direction: column;
1324 + gap: 5px;
1325 +}
1326 +
1327 +.form-item.full-width {
1328 + flex: 100%;
1329 +}
1330 +
1331 +.form-item label {
1332 + font-weight: 500;
1333 + color: #333;
1334 +}
1335 +
1336 +.form-item label.required::after {
1337 + content: ' *';
1338 + color: #dc3545;
1339 +}
1340 +
1341 +.form-input, .form-select, .form-textarea {
1342 + padding: 6px 8px;
1343 + border: 1px solid #ddd;
1344 + border-radius: 4px;
1345 + font-size: 12px;
1346 + width: 200px;
1347 +}
1348 +
1349 +/* 订单明细中的输入框样式 - 优化空间利用 */
1350 +.order-item .form-input {
1351 + width: 100%;
1352 + padding: 3px 5px;
1353 + font-size: 11px;
1354 + box-sizing: border-box;
1355 +}
1356 +
1357 +/* 订单明细中的表单项样式 */
1358 +.order-item .form-item {
1359 + flex: 0 0 auto;
1360 + margin-bottom: 0;
1361 + min-width: 0;
1362 +}
1363 +
1364 +/* 产品编码字段 */
1365 +.order-item .form-item:nth-child(1) {
1366 + flex: 0 0 120px;
1367 +}
1368 +
1369 +/* 产品名称字段 - 占用更多空间 */
1370 +.order-item .form-item:nth-child(2) {
1371 + flex: 1 1 200px;
1372 + min-width: 150px;
1373 +}
1374 +
1375 +/* 数量字段 */
1376 +.order-item .form-item:nth-child(3) {
1377 + flex: 0 0 80px;
1378 +}
1379 +
1380 +/* 单价字段 */
1381 +.order-item .form-item:nth-child(4) {
1382 + flex: 0 0 100px;
1383 +}
1384 +
1385 +/* 明细金额字段 */
1386 +.order-item .form-item:nth-child(5) {
1387 + flex: 0 0 100px;
1388 +}
1389 +
1390 +.order-item .form-item label {
1391 + font-size: 11px;
1392 + margin-bottom: 2px;
1393 +}
1394 +
1395 +.form-textarea {
1396 + resize: vertical;
1397 + min-height: 80px;
1398 +}
1399 +
1400 +/* 订单明细样式 */
1401 +.order-items-header {
1402 + margin-bottom: 10px;
1403 +}
1404 +
1405 +.add-item-btn {
1406 + padding: 8px 16px;
1407 + background: #28a745;
1408 + color: white;
1409 + border: none;
1410 + border-radius: 4px;
1411 + cursor: pointer;
1412 +}
1413 +
1414 +.order-items-container {
1415 + border: 1px solid #eee;
1416 + border-radius: 4px;
1417 + padding: 6px;
1418 + background: #f8f9fa;
1419 +}
1420 +
1421 +.order-item {
1422 + background: white;
1423 + border: 1px solid #ddd;
1424 + border-radius: 4px;
1425 + padding: 6px;
1426 + margin-bottom: 6px;
1427 +}
1428 +
1429 +.order-item:last-child {
1430 + margin-bottom: 0;
1431 +}
1432 +
1433 +.item-row {
1434 + display: flex;
1435 + gap: 8px;
1436 + margin-bottom: 6px;
1437 + align-items: end;
1438 +}
1439 +
1440 +.item-row:last-child {
1441 + margin-bottom: 0;
1442 +}
1443 +
1444 +/* 单行布局 - 所有字段在一行显示,优化空间利用 */
1445 +.item-row-single {
1446 + display: flex;
1447 + gap: 4px;
1448 + align-items: end;
1449 + flex-wrap: nowrap;
1450 + justify-content: space-between;
1451 +}
1452 +
1453 +.remove-item-btn {
1454 + padding: 4px 8px;
1455 + background: #dc3545;
1456 + color: white;
1457 + border: none;
1458 + border-radius: 4px;
1459 + cursor: pointer;
1460 + font-size: 11px;
1461 + height: 28px;
1462 + align-self: end;
1463 +}
1464 +
1465 +.form-actions {
1466 + display: flex;
1467 + justify-content: flex-end;
1468 + gap: 10px;
1469 + margin-top: 30px;
1470 + padding-top: 20px;
1471 + border-top: 1px solid #eee;
1472 +}
1473 +
1474 +.cancel-btn, .submit-btn {
1475 + padding: 10px 20px;
1476 + border: none;
1477 + border-radius: 4px;
1478 + cursor: pointer;
1479 + font-weight: 500;
1480 +}
1481 +
1482 +.cancel-btn {
1483 + background: #6c757d;
1484 + color: white;
1485 +}
1486 +
1487 +.submit-btn {
1488 + background: #007bff;
1489 + color: white;
1490 +}
1491 +
1492 +/* 详情样式 */
1493 +.detail-content {
1494 + max-height: 60vh;
1495 + overflow-y: auto;
1496 +}
1497 +
1498 +.detail-section {
1499 + margin-bottom: 30px;
1500 +}
1501 +
1502 +.detail-section h4 {
1503 + margin: 0 0 15px 0;
1504 + color: #333;
1505 + border-bottom: 2px solid #007bff;
1506 + padding-bottom: 8px;
1507 +}
1508 +
1509 +.detail-grid {
1510 + display: grid;
1511 + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
1512 + gap: 15px;
1513 +}
1514 +
1515 +.detail-item {
1516 + display: flex;
1517 + flex-direction: column;
1518 + gap: 5px;
1519 +}
1520 +
1521 +.detail-item label {
1522 + font-weight: 500;
1523 + color: #666;
1524 + font-size: 14px;
1525 +}
1526 +
1527 +.detail-item span {
1528 + color: #333;
1529 + font-size: 16px;
1530 +}
1531 +
1532 +.detail-table {
1533 + width: 100%;
1534 + border-collapse: collapse;
1535 + margin-top: 15px;
1536 +}
1537 +
1538 +.detail-table th,
1539 +.detail-table td {
1540 + padding: 10px;
1541 + text-align: left;
1542 + border: 1px solid #ddd;
1543 +}
1544 +
1545 +.detail-table th {
1546 + background: #f8f9fa;
1547 + font-weight: 600;
1548 + color: #333;
1549 +}
1550 +
1551 +.detail-table tr:nth-child(even) {
1552 + background: #f8f9fa;
1553 +}
1554 +</style>
...@@ -1026,8 +1026,9 @@ onMounted(() => { ...@@ -1026,8 +1026,9 @@ onMounted(() => {
1026 .dialog-content { 1026 .dialog-content {
1027 background: white; 1027 background: white;
1028 border-radius: 8px; 1028 border-radius: 8px;
1029 - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 1029 + box-shadow: 0 4px 20px rgba(0,0,0,0.15);
1030 - width: 600px; 1030 + max-width: 600px;
1031 + width: 90%;
1031 max-height: 80vh; 1032 max-height: 80vh;
1032 overflow-y: auto; 1033 overflow-y: auto;
1033 } 1034 }
...@@ -1095,10 +1096,9 @@ onMounted(() => { ...@@ -1095,10 +1096,9 @@ onMounted(() => {
1095 1096
1096 .form-input, .form-select, .form-textarea { 1097 .form-input, .form-select, .form-textarea {
1097 padding: 8px 12px; 1098 padding: 8px 12px;
1098 - border: 1px solid #d9d9d9; 1099 + border: 1px solid #ddd;
1099 border-radius: 4px; 1100 border-radius: 4px;
1100 font-size: 14px; 1101 font-size: 14px;
1101 - transition: border-color 0.2s;
1102 } 1102 }
1103 1103
1104 .form-input:focus, .form-select:focus, .form-textarea:focus { 1104 .form-input:focus, .form-select:focus, .form-textarea:focus {
...@@ -1122,25 +1122,20 @@ onMounted(() => { ...@@ -1122,25 +1122,20 @@ onMounted(() => {
1122 } 1122 }
1123 1123
1124 .cancel-btn, .submit-btn { 1124 .cancel-btn, .submit-btn {
1125 - padding: 8px 16px; 1125 + padding: 10px 20px;
1126 border: none; 1126 border: none;
1127 border-radius: 4px; 1127 border-radius: 4px;
1128 - font-size: 14px;
1129 cursor: pointer; 1128 cursor: pointer;
1130 - transition: all 0.2s; 1129 + font-weight: 500;
1131 } 1130 }
1132 1131
1133 .cancel-btn { 1132 .cancel-btn {
1134 - background: #f5f5f5; 1133 + background: #6c757d;
1135 - color: #666; 1134 + color: white;
1136 -}
1137 -
1138 -.cancel-btn:hover {
1139 - background: #e6e6e6;
1140 } 1135 }
1141 1136
1142 .submit-btn { 1137 .submit-btn {
1143 - background: #1890ff; 1138 + background: #007bff;
1144 color: white; 1139 color: white;
1145 } 1140 }
1146 1141
......