Showing
32 changed files
with
6309 additions
and
32 deletions
| ... | @@ -667,8 +667,709 @@ | ... | @@ -667,8 +667,709 @@ |
| 667 | - 超级管理员状态不允许修改 | 667 | - 超级管理员状态不允许修改 |
| 668 | - 所有操作都会记录操作日志 | 668 | - 所有操作都会记录操作日志 |
| 669 | 669 | ||
| 670 | +## 7. 产品管理 (ProductInfoController) | ||
| 671 | + | ||
| 672 | +### 7.1 分页查询产品列表 | ||
| 673 | + | ||
| 674 | +**接口路径:** `GET /api/system/product/list` | ||
| 675 | + | ||
| 676 | +**功能描述:** 根据条件分页查询产品列表 | ||
| 677 | + | ||
| 678 | +**权限要求:** `product:list` | ||
| 679 | + | ||
| 680 | +**请求参数:** | ||
| 681 | + | ||
| 682 | +| 参数名 | 类型 | 必填 | 描述 | 示例 | | ||
| 683 | +|--------|------|------|------|------| | ||
| 684 | +| productCode | String | 否 | 产品编码 | APL-IP15-128G-BK | | ||
| 685 | +| productName | String | 否 | 产品名称 | iPhone 15 | | ||
| 686 | +| productModel | String | 否 | 产品型号 | A2848 | | ||
| 687 | +| productType | String | 否 | 产品类别 | iPhone | | ||
| 688 | +| storageCapacity | String | 否 | 存储容量 | 128GB | | ||
| 689 | +| color | String | 否 | 产品颜色 | 黑色 | | ||
| 690 | +| saleStatus | Integer | 否 | 销售状态(0-下架/1-在售/2-预售) | 1 | | ||
| 691 | +| rebateFlag | Integer | 否 | 是否参与返利(0-否/1-是) | 1 | | ||
| 692 | +| minPrice | BigDecimal | 否 | 最低价格 | 1000.00 | | ||
| 693 | +| maxPrice | BigDecimal | 否 | 最高价格 | 10000.00 | | ||
| 694 | +| saleStartDateBegin | String | 否 | 销售起始日期开始 | 2024-01-01 | | ||
| 695 | +| saleStartDateEnd | String | 否 | 销售起始日期结束 | 2024-12-31 | | ||
| 696 | +| saleEndDateBegin | String | 否 | 销售终止日期开始 | 2024-01-01 | | ||
| 697 | +| saleEndDateEnd | String | 否 | 销售终止日期结束 | 2024-12-31 | | ||
| 698 | +| pageNum | Integer | 否 | 页码 | 1 | | ||
| 699 | +| pageSize | Integer | 否 | 每页大小 | 10 | | ||
| 700 | + | ||
| 701 | +**响应示例:** | ||
| 702 | +```json | ||
| 703 | +{ | ||
| 704 | + "code": 200, | ||
| 705 | + "message": "操作成功", | ||
| 706 | + "data": { | ||
| 707 | + "records": [ | ||
| 708 | + { | ||
| 709 | + "productId": 1, | ||
| 710 | + "productCode": "APL-IP15-128G-BK", | ||
| 711 | + "productName": "iPhone 15", | ||
| 712 | + "productModel": "A2848", | ||
| 713 | + "productType": "iPhone", | ||
| 714 | + "storageCapacity": "128GB", | ||
| 715 | + "color": "黑色", | ||
| 716 | + "productImgUrl": "https://example.com/iphone15.jpg", | ||
| 717 | + "officialPrice": 5999.00, | ||
| 718 | + "saleStatus": 1, | ||
| 719 | + "rebateFlag": 1, | ||
| 720 | + "saleStartDate": "2023-09-15", | ||
| 721 | + "saleEndDate": "2024-12-31", | ||
| 722 | + "remark": "iPhone 15 128GB 黑色", | ||
| 723 | + "createBy": "admin", | ||
| 724 | + "createTime": "2024-01-15 10:30:00", | ||
| 725 | + "updateBy": "admin", | ||
| 726 | + "updateTime": "2024-01-15 10:30:00", | ||
| 727 | + "delFlag": "0" | ||
| 728 | + } | ||
| 729 | + ], | ||
| 730 | + "total": 1, | ||
| 731 | + "size": 10, | ||
| 732 | + "current": 1, | ||
| 733 | + "pages": 1 | ||
| 734 | + } | ||
| 735 | +} | ||
| 736 | +``` | ||
| 737 | + | ||
| 738 | +### 7.2 获取产品详情 | ||
| 739 | + | ||
| 740 | +**接口路径:** `GET /api/system/product/{productId}` | ||
| 741 | + | ||
| 742 | +**功能描述:** 根据产品ID获取产品详情 | ||
| 743 | + | ||
| 744 | +**权限要求:** `product:detail` | ||
| 745 | + | ||
| 746 | +**路径参数:** | ||
| 747 | + | ||
| 748 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 749 | +|--------|------|------|------| | ||
| 750 | +| productId | Long | 是 | 产品ID | | ||
| 751 | + | ||
| 752 | +**响应示例:** | ||
| 753 | +```json | ||
| 754 | +{ | ||
| 755 | + "code": 200, | ||
| 756 | + "message": "操作成功", | ||
| 757 | + "data": { | ||
| 758 | + "productId": 1, | ||
| 759 | + "productCode": "APL-IP15-128G-BK", | ||
| 760 | + "productName": "iPhone 15", | ||
| 761 | + "productModel": "A2848", | ||
| 762 | + "productType": "iPhone", | ||
| 763 | + "storageCapacity": "128GB", | ||
| 764 | + "color": "黑色", | ||
| 765 | + "productImgUrl": "https://example.com/iphone15.jpg", | ||
| 766 | + "officialPrice": 5999.00, | ||
| 767 | + "saleStatus": 1, | ||
| 768 | + "rebateFlag": 1, | ||
| 769 | + "saleStartDate": "2023-09-15", | ||
| 770 | + "saleEndDate": "2024-12-31", | ||
| 771 | + "remark": "iPhone 15 128GB 黑色", | ||
| 772 | + "createBy": "admin", | ||
| 773 | + "createTime": "2024-01-15 10:30:00", | ||
| 774 | + "updateBy": "admin", | ||
| 775 | + "updateTime": "2024-01-15 10:30:00", | ||
| 776 | + "delFlag": "0" | ||
| 777 | + } | ||
| 778 | +} | ||
| 779 | +``` | ||
| 780 | + | ||
| 781 | +### 7.3 新增产品 | ||
| 782 | + | ||
| 783 | +**接口路径:** `POST /api/system/product` | ||
| 784 | + | ||
| 785 | +**功能描述:** 新增产品信息 | ||
| 786 | + | ||
| 787 | +**权限要求:** `product:add` | ||
| 788 | + | ||
| 789 | +**请求体参数:** | ||
| 790 | + | ||
| 791 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 792 | +|--------|------|------|------| | ||
| 793 | +| productCode | String | 是 | 产品编码 | | ||
| 794 | +| productName | String | 是 | 产品名称 | | ||
| 795 | +| productModel | String | 是 | 产品型号 | | ||
| 796 | +| productType | String | 是 | 产品类别 | | ||
| 797 | +| storageCapacity | String | 否 | 存储容量 | | ||
| 798 | +| color | String | 否 | 产品颜色 | | ||
| 799 | +| productImgUrl | String | 否 | 产品图片URL | | ||
| 800 | +| officialPrice | BigDecimal | 否 | 官方指导价 | | ||
| 801 | +| saleStatus | Integer | 是 | 销售状态(0-下架/1-在售/2-预售) | | ||
| 802 | +| rebateFlag | Integer | 是 | 是否参与返利(0-否/1-是) | | ||
| 803 | +| saleStartDate | String | 否 | 销售起始日期 | | ||
| 804 | +| saleEndDate | String | 否 | 销售终止日期 | | ||
| 805 | +| remark | String | 否 | 产品备注 | | ||
| 806 | + | ||
| 807 | +**请求示例:** | ||
| 808 | +```json | ||
| 809 | +{ | ||
| 810 | + "productCode": "APL-IP15-128G-BK", | ||
| 811 | + "productName": "iPhone 15", | ||
| 812 | + "productModel": "A2848", | ||
| 813 | + "productType": "iPhone", | ||
| 814 | + "storageCapacity": "128GB", | ||
| 815 | + "color": "黑色", | ||
| 816 | + "productImgUrl": "https://example.com/iphone15.jpg", | ||
| 817 | + "officialPrice": 5999.00, | ||
| 818 | + "saleStatus": 1, | ||
| 819 | + "rebateFlag": 1, | ||
| 820 | + "saleStartDate": "2023-09-15", | ||
| 821 | + "saleEndDate": "2024-12-31", | ||
| 822 | + "remark": "iPhone 15 128GB 黑色" | ||
| 823 | +} | ||
| 824 | +``` | ||
| 825 | + | ||
| 826 | +**响应示例:** | ||
| 827 | +```json | ||
| 828 | +{ | ||
| 829 | + "code": 200, | ||
| 830 | + "message": "新增产品成功", | ||
| 831 | + "data": null | ||
| 832 | +} | ||
| 833 | +``` | ||
| 834 | + | ||
| 835 | +### 7.4 修改产品 | ||
| 836 | + | ||
| 837 | +**接口路径:** `POST /api/product/update` | ||
| 838 | + | ||
| 839 | +**功能描述:** 修改产品信息 | ||
| 840 | + | ||
| 841 | +**权限要求:** `product:edit` | ||
| 842 | + | ||
| 843 | +**请求体参数:** | ||
| 844 | + | ||
| 845 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 846 | +|--------|------|------|------| | ||
| 847 | +| productId | Long | 是 | 产品ID | | ||
| 848 | +| productCode | String | 是 | 产品编码 | | ||
| 849 | +| productName | String | 是 | 产品名称 | | ||
| 850 | +| productModel | String | 是 | 产品型号 | | ||
| 851 | +| productType | String | 是 | 产品类别 | | ||
| 852 | +| storageCapacity | String | 否 | 存储容量 | | ||
| 853 | +| color | String | 否 | 产品颜色 | | ||
| 854 | +| productImgUrl | String | 否 | 产品图片URL | | ||
| 855 | +| officialPrice | BigDecimal | 否 | 官方指导价 | | ||
| 856 | +| saleStatus | Integer | 是 | 销售状态(0-下架/1-在售/2-预售) | | ||
| 857 | +| rebateFlag | Integer | 是 | 是否参与返利(0-否/1-是) | | ||
| 858 | +| saleStartDate | String | 否 | 销售起始日期 | | ||
| 859 | +| saleEndDate | String | 否 | 销售终止日期 | | ||
| 860 | +| remark | String | 否 | 产品备注 | | ||
| 861 | + | ||
| 862 | +**请求示例:** | ||
| 863 | +```json | ||
| 864 | +{ | ||
| 865 | + "productId": 1, | ||
| 866 | + "productCode": "APL-IP15-128G-BK", | ||
| 867 | + "productName": "iPhone 15", | ||
| 868 | + "productModel": "A2848", | ||
| 869 | + "productType": "iPhone", | ||
| 870 | + "storageCapacity": "128GB", | ||
| 871 | + "color": "黑色", | ||
| 872 | + "productImgUrl": "https://example.com/iphone15.jpg", | ||
| 873 | + "officialPrice": 5999.00, | ||
| 874 | + "saleStatus": 1, | ||
| 875 | + "rebateFlag": 1, | ||
| 876 | + "saleStartDate": "2023-09-15", | ||
| 877 | + "saleEndDate": "2024-12-31", | ||
| 878 | + "remark": "iPhone 15 128GB 黑色" | ||
| 879 | +} | ||
| 880 | +``` | ||
| 881 | + | ||
| 882 | +**响应示例:** | ||
| 883 | +```json | ||
| 884 | +{ | ||
| 885 | + "code": 200, | ||
| 886 | + "message": "修改产品成功", | ||
| 887 | + "data": null | ||
| 888 | +} | ||
| 889 | +``` | ||
| 890 | + | ||
| 891 | +### 7.5 删除产品 | ||
| 892 | + | ||
| 893 | +**接口路径:** `DELETE /api/system/product/{productId}` | ||
| 894 | + | ||
| 895 | +**功能描述:** 根据产品ID删除产品 | ||
| 896 | + | ||
| 897 | +**权限要求:** `product:delete` | ||
| 898 | + | ||
| 899 | +**路径参数:** | ||
| 900 | + | ||
| 901 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 902 | +|--------|------|------|------| | ||
| 903 | +| productId | Long | 是 | 产品ID | | ||
| 904 | + | ||
| 905 | +**响应示例:** | ||
| 906 | +```json | ||
| 907 | +{ | ||
| 908 | + "code": 200, | ||
| 909 | + "message": "删除产品成功", | ||
| 910 | + "data": null | ||
| 911 | +} | ||
| 912 | +``` | ||
| 913 | + | ||
| 914 | +### 7.6 批量删除产品 | ||
| 915 | + | ||
| 916 | +**接口路径:** `POST /api/product/batchDelete` | ||
| 917 | + | ||
| 918 | +**功能描述:** 批量删除产品 | ||
| 919 | + | ||
| 920 | +**权限要求:** `product:delete` | ||
| 921 | + | ||
| 922 | +**请求体参数:** | ||
| 923 | + | ||
| 924 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 925 | +|--------|------|------|------| | ||
| 926 | +| productIds | List<Long> | 是 | 产品ID列表 | | ||
| 927 | + | ||
| 928 | +**请求示例:** | ||
| 929 | +```json | ||
| 930 | +[1, 2, 3] | ||
| 931 | +``` | ||
| 932 | + | ||
| 933 | +**响应示例:** | ||
| 934 | +```json | ||
| 935 | +{ | ||
| 936 | + "code": 200, | ||
| 937 | + "message": "批量删除产品成功", | ||
| 938 | + "data": null | ||
| 939 | +} | ||
| 940 | +``` | ||
| 941 | + | ||
| 942 | +### 7.7 修改产品状态 | ||
| 943 | + | ||
| 944 | +**接口路径:** `POST /api/product/{productId}/status` | ||
| 945 | + | ||
| 946 | +**功能描述:** 修改产品销售状态 | ||
| 947 | + | ||
| 948 | +**权限要求:** `product:edit` | ||
| 949 | + | ||
| 950 | +**路径参数:** | ||
| 951 | + | ||
| 952 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 953 | +|--------|------|------|------| | ||
| 954 | +| productId | Long | 是 | 产品ID | | ||
| 955 | + | ||
| 956 | +**请求参数:** | ||
| 957 | + | ||
| 958 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 959 | +|--------|------|------|------| | ||
| 960 | +| saleStatus | Integer | 是 | 销售状态(0-下架/1-在售/2-预售) | | ||
| 961 | + | ||
| 962 | +**响应示例:** | ||
| 963 | +```json | ||
| 964 | +{ | ||
| 965 | + "code": 200, | ||
| 966 | + "message": "修改产品状态成功", | ||
| 967 | + "data": null | ||
| 968 | +} | ||
| 969 | +``` | ||
| 970 | + | ||
| 971 | +### 7.8 修改返利标识 | ||
| 972 | + | ||
| 973 | +**接口路径:** `POST /api/product/{productId}/rebate` | ||
| 974 | + | ||
| 975 | +**功能描述:** 修改产品返利标识 | ||
| 976 | + | ||
| 977 | +**权限要求:** `product:edit` | ||
| 978 | + | ||
| 979 | +**路径参数:** | ||
| 980 | + | ||
| 981 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 982 | +|--------|------|------|------| | ||
| 983 | +| productId | Long | 是 | 产品ID | | ||
| 984 | + | ||
| 985 | +**请求参数:** | ||
| 986 | + | ||
| 987 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 988 | +|--------|------|------|------| | ||
| 989 | +| rebateFlag | Integer | 是 | 返利标识(0-否/1-是) | | ||
| 990 | + | ||
| 991 | +**响应示例:** | ||
| 992 | +```json | ||
| 993 | +{ | ||
| 994 | + "code": 200, | ||
| 995 | + "message": "修改返利标识成功", | ||
| 996 | + "data": null | ||
| 997 | +} | ||
| 998 | +``` | ||
| 999 | + | ||
| 1000 | +## 8. 经销商管理 (DealerInfoController) | ||
| 1001 | + | ||
| 1002 | +### 8.1 分页查询经销商列表 | ||
| 1003 | + | ||
| 1004 | +**接口路径:** `GET /api/dealer/list` | ||
| 1005 | + | ||
| 1006 | +**功能描述:** 根据条件分页查询经销商列表 | ||
| 1007 | + | ||
| 1008 | +**权限要求:** `dealer:list` | ||
| 1009 | + | ||
| 1010 | +**请求参数:** | ||
| 1011 | + | ||
| 1012 | +| 参数名 | 类型 | 必填 | 描述 | 示例 | | ||
| 1013 | +|--------|------|------|------|------| | ||
| 1014 | +| dealerCode | String | 否 | 经销商编码 | DL001 | | ||
| 1015 | +| dealerName | String | 否 | 经销商名称 | 北京经销商 | | ||
| 1016 | +| creditCode | String | 否 | 统一社会信用代码 | 91110000123456789X | | ||
| 1017 | +| dealerLevel | Integer | 否 | 经销商等级(1-一级经销商/2-二级经销商) | 1 | | ||
| 1018 | +| region | String | 否 | 所在区域 | 北京 | | ||
| 1019 | +| contactPerson | String | 否 | 联系人 | 张三 | | ||
| 1020 | +| contactPhone | String | 否 | 联系电话 | 13800138000 | | ||
| 1021 | +| cooperateStatus | Integer | 否 | 合作状态(1-正常合作/2-暂停合作/3-终止合作) | 1 | | ||
| 1022 | +| qualificationAuditStatus | Integer | 否 | 资质审核状态(1-待审核/2-审核通过/3-审核不通过) | 2 | | ||
| 1023 | +| cooperateStartDateStart | String | 否 | 合作起始日期开始 | 2024-01-01 | | ||
| 1024 | +| cooperateStartDateEnd | String | 否 | 合作起始日期结束 | 2024-12-31 | | ||
| 1025 | +| pageNum | Integer | 否 | 页码 | 1 | | ||
| 1026 | +| pageSize | Integer | 否 | 每页大小 | 10 | | ||
| 1027 | + | ||
| 1028 | +**响应示例:** | ||
| 1029 | +```json | ||
| 1030 | +{ | ||
| 1031 | + "code": 200, | ||
| 1032 | + "message": "操作成功", | ||
| 1033 | + "data": { | ||
| 1034 | + "records": [ | ||
| 1035 | + { | ||
| 1036 | + "dealerId": 1, | ||
| 1037 | + "dealerCode": "DL001", | ||
| 1038 | + "dealerName": "北京经销商", | ||
| 1039 | + "creditCode": "91110000123456789X", | ||
| 1040 | + "dealerLevel": 1, | ||
| 1041 | + "region": "北京", | ||
| 1042 | + "contactPerson": "张三", | ||
| 1043 | + "contactPhone": "13800138000", | ||
| 1044 | + "cooperateStartDate": "2024-01-01", | ||
| 1045 | + "cooperateStatus": 1, | ||
| 1046 | + "businessLicenseUrl": "https://example.com/license.jpg", | ||
| 1047 | + "cooperationAgreementUrl": "https://example.com/agreement.pdf", | ||
| 1048 | + "qualificationAuditStatus": 2, | ||
| 1049 | + "auditOpinion": "审核通过", | ||
| 1050 | + "totalRebateAmount": 10000.00, | ||
| 1051 | + "usedRebateAmount": 5000.00, | ||
| 1052 | + "pendingRebateAmount": 5000.00, | ||
| 1053 | + "lastRebateUpdateTime": "2024-01-15 10:30:00", | ||
| 1054 | + "createBy": "admin", | ||
| 1055 | + "createTime": "2024-01-15 10:30:00", | ||
| 1056 | + "updateBy": "admin", | ||
| 1057 | + "updateTime": "2024-01-15 10:30:00", | ||
| 1058 | + "delFlag": "0" | ||
| 1059 | + } | ||
| 1060 | + ], | ||
| 1061 | + "total": 1, | ||
| 1062 | + "size": 10, | ||
| 1063 | + "current": 1, | ||
| 1064 | + "pages": 1 | ||
| 1065 | + } | ||
| 1066 | +} | ||
| 1067 | +``` | ||
| 1068 | + | ||
| 1069 | +### 8.2 获取经销商详情 | ||
| 1070 | + | ||
| 1071 | +**接口路径:** `GET /api/dealer/{dealerId}` | ||
| 1072 | + | ||
| 1073 | +**功能描述:** 根据经销商ID获取经销商详情 | ||
| 1074 | + | ||
| 1075 | +**权限要求:** `dealer:detail` | ||
| 1076 | + | ||
| 1077 | +**路径参数:** | ||
| 1078 | + | ||
| 1079 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 1080 | +|--------|------|------|------| | ||
| 1081 | +| dealerId | Long | 是 | 经销商ID | | ||
| 1082 | + | ||
| 1083 | +**响应示例:** | ||
| 1084 | +```json | ||
| 1085 | +{ | ||
| 1086 | + "code": 200, | ||
| 1087 | + "message": "操作成功", | ||
| 1088 | + "data": { | ||
| 1089 | + "dealerId": 1, | ||
| 1090 | + "dealerCode": "DL001", | ||
| 1091 | + "dealerName": "北京经销商", | ||
| 1092 | + "creditCode": "91110000123456789X", | ||
| 1093 | + "dealerLevel": 1, | ||
| 1094 | + "region": "北京", | ||
| 1095 | + "contactPerson": "张三", | ||
| 1096 | + "contactPhone": "13800138000", | ||
| 1097 | + "cooperateStartDate": "2024-01-01", | ||
| 1098 | + "cooperateStatus": 1, | ||
| 1099 | + "businessLicenseUrl": "https://example.com/license.jpg", | ||
| 1100 | + "cooperationAgreementUrl": "https://example.com/agreement.pdf", | ||
| 1101 | + "qualificationAuditStatus": 2, | ||
| 1102 | + "auditOpinion": "审核通过", | ||
| 1103 | + "totalRebateAmount": 10000.00, | ||
| 1104 | + "usedRebateAmount": 5000.00, | ||
| 1105 | + "pendingRebateAmount": 5000.00, | ||
| 1106 | + "lastRebateUpdateTime": "2024-01-15 10:30:00", | ||
| 1107 | + "createBy": "admin", | ||
| 1108 | + "createTime": "2024-01-15 10:30:00", | ||
| 1109 | + "updateBy": "admin", | ||
| 1110 | + "updateTime": "2024-01-15 10:30:00", | ||
| 1111 | + "delFlag": "0" | ||
| 1112 | + } | ||
| 1113 | +} | ||
| 1114 | +``` | ||
| 1115 | + | ||
| 1116 | +### 8.3 新增经销商 | ||
| 1117 | + | ||
| 1118 | +**接口路径:** `POST /api/dealer` | ||
| 1119 | + | ||
| 1120 | +**功能描述:** 新增经销商信息 | ||
| 1121 | + | ||
| 1122 | +**权限要求:** `dealer:add` | ||
| 1123 | + | ||
| 1124 | +**请求体参数:** | ||
| 1125 | + | ||
| 1126 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 1127 | +|--------|------|------|------| | ||
| 1128 | +| dealerCode | String | 是 | 经销商编码(6-20位大写字母和数字) | | ||
| 1129 | +| dealerName | String | 是 | 经销商名称 | | ||
| 1130 | +| creditCode | String | 是 | 统一社会信用代码 | | ||
| 1131 | +| dealerLevel | Integer | 是 | 经销商等级(1-一级经销商/2-二级经销商) | | ||
| 1132 | +| region | String | 是 | 所在区域 | | ||
| 1133 | +| contactPerson | String | 否 | 联系人 | | ||
| 1134 | +| contactPhone | String | 否 | 联系电话 | | ||
| 1135 | +| cooperateStartDate | String | 是 | 合作起始日期 | | ||
| 1136 | +| cooperateStatus | Integer | 否 | 合作状态(1-正常合作/2-暂停合作/3-终止合作) | | ||
| 1137 | +| businessLicenseUrl | String | 否 | 营业执照URL | | ||
| 1138 | +| cooperationAgreementUrl | String | 否 | 合作协议URL | | ||
| 1139 | +| qualificationAuditStatus | Integer | 否 | 资质审核状态(1-待审核/2-审核通过/3-审核不通过) | | ||
| 1140 | +| auditOpinion | String | 否 | 审核意见 | | ||
| 1141 | + | ||
| 1142 | +**请求示例:** | ||
| 1143 | +```json | ||
| 1144 | +{ | ||
| 1145 | + "dealerCode": "DL001", | ||
| 1146 | + "dealerName": "北京经销商", | ||
| 1147 | + "creditCode": "91110000123456789X", | ||
| 1148 | + "dealerLevel": 1, | ||
| 1149 | + "region": "北京", | ||
| 1150 | + "contactPerson": "张三", | ||
| 1151 | + "contactPhone": "13800138000", | ||
| 1152 | + "cooperateStartDate": "2024-01-01", | ||
| 1153 | + "cooperateStatus": 1, | ||
| 1154 | + "businessLicenseUrl": "https://example.com/license.jpg", | ||
| 1155 | + "cooperationAgreementUrl": "https://example.com/agreement.pdf", | ||
| 1156 | + "qualificationAuditStatus": 1, | ||
| 1157 | + "auditOpinion": "" | ||
| 1158 | +} | ||
| 1159 | +``` | ||
| 1160 | + | ||
| 1161 | +**响应示例:** | ||
| 1162 | +```json | ||
| 1163 | +{ | ||
| 1164 | + "code": 200, | ||
| 1165 | + "message": "经销商新增成功", | ||
| 1166 | + "data": null | ||
| 1167 | +} | ||
| 1168 | +``` | ||
| 1169 | + | ||
| 1170 | +### 8.4 修改经销商 | ||
| 1171 | + | ||
| 1172 | +**接口路径:** `POST /api/dealer/update` | ||
| 1173 | + | ||
| 1174 | +**功能描述:** 修改经销商信息 | ||
| 1175 | + | ||
| 1176 | +**权限要求:** `dealer:update` | ||
| 1177 | + | ||
| 1178 | +**请求体参数:** | ||
| 1179 | + | ||
| 1180 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 1181 | +|--------|------|------|------| | ||
| 1182 | +| dealerId | Long | 是 | 经销商ID | | ||
| 1183 | +| dealerCode | String | 是 | 经销商编码(6-20位大写字母和数字) | | ||
| 1184 | +| dealerName | String | 是 | 经销商名称 | | ||
| 1185 | +| creditCode | String | 是 | 统一社会信用代码 | | ||
| 1186 | +| dealerLevel | Integer | 是 | 经销商等级(1-一级经销商/2-二级经销商) | | ||
| 1187 | +| region | String | 是 | 所在区域 | | ||
| 1188 | +| contactPerson | String | 否 | 联系人 | | ||
| 1189 | +| contactPhone | String | 否 | 联系电话 | | ||
| 1190 | +| cooperateStartDate | String | 是 | 合作起始日期 | | ||
| 1191 | +| cooperateStatus | Integer | 否 | 合作状态(1-正常合作/2-暂停合作/3-终止合作) | | ||
| 1192 | +| businessLicenseUrl | String | 否 | 营业执照URL | | ||
| 1193 | +| cooperationAgreementUrl | String | 否 | 合作协议URL | | ||
| 1194 | +| qualificationAuditStatus | Integer | 否 | 资质审核状态(1-待审核/2-审核通过/3-审核不通过) | | ||
| 1195 | +| auditOpinion | String | 否 | 审核意见 | | ||
| 1196 | + | ||
| 1197 | +**请求示例:** | ||
| 1198 | +```json | ||
| 1199 | +{ | ||
| 1200 | + "dealerId": 1, | ||
| 1201 | + "dealerCode": "DL001", | ||
| 1202 | + "dealerName": "北京经销商", | ||
| 1203 | + "creditCode": "91110000123456789X", | ||
| 1204 | + "dealerLevel": 1, | ||
| 1205 | + "region": "北京", | ||
| 1206 | + "contactPerson": "张三", | ||
| 1207 | + "contactPhone": "13800138000", | ||
| 1208 | + "cooperateStartDate": "2024-01-01", | ||
| 1209 | + "cooperateStatus": 1, | ||
| 1210 | + "businessLicenseUrl": "https://example.com/license.jpg", | ||
| 1211 | + "cooperationAgreementUrl": "https://example.com/agreement.pdf", | ||
| 1212 | + "qualificationAuditStatus": 2, | ||
| 1213 | + "auditOpinion": "审核通过" | ||
| 1214 | +} | ||
| 1215 | +``` | ||
| 1216 | + | ||
| 1217 | +**响应示例:** | ||
| 1218 | +```json | ||
| 1219 | +{ | ||
| 1220 | + "code": 200, | ||
| 1221 | + "message": "经销商修改成功", | ||
| 1222 | + "data": null | ||
| 1223 | +} | ||
| 1224 | +``` | ||
| 1225 | + | ||
| 1226 | +### 8.5 删除经销商 | ||
| 1227 | + | ||
| 1228 | +**接口路径:** `DELETE /api/dealer/{dealerId}` | ||
| 1229 | + | ||
| 1230 | +**功能描述:** 根据经销商ID删除经销商 | ||
| 1231 | + | ||
| 1232 | +**权限要求:** `dealer:delete` | ||
| 1233 | + | ||
| 1234 | +**路径参数:** | ||
| 1235 | + | ||
| 1236 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 1237 | +|--------|------|------|------| | ||
| 1238 | +| dealerId | Long | 是 | 经销商ID | | ||
| 1239 | + | ||
| 1240 | +**响应示例:** | ||
| 1241 | +```json | ||
| 1242 | +{ | ||
| 1243 | + "code": 200, | ||
| 1244 | + "message": "经销商删除成功", | ||
| 1245 | + "data": null | ||
| 1246 | +} | ||
| 1247 | +``` | ||
| 1248 | + | ||
| 1249 | +### 8.6 批量删除经销商 | ||
| 1250 | + | ||
| 1251 | +**接口路径:** `DELETE /api/dealer/batch` | ||
| 1252 | + | ||
| 1253 | +**功能描述:** 批量删除经销商 | ||
| 1254 | + | ||
| 1255 | +**权限要求:** `dealer:batchDelete` | ||
| 1256 | + | ||
| 1257 | +**请求体参数:** | ||
| 1258 | + | ||
| 1259 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 1260 | +|--------|------|------|------| | ||
| 1261 | +| dealerIds | List<Long> | 是 | 经销商ID列表 | | ||
| 1262 | + | ||
| 1263 | +**请求示例:** | ||
| 1264 | +```json | ||
| 1265 | +[1, 2, 3] | ||
| 1266 | +``` | ||
| 1267 | + | ||
| 1268 | +**响应示例:** | ||
| 1269 | +```json | ||
| 1270 | +{ | ||
| 1271 | + "code": 200, | ||
| 1272 | + "message": "经销商批量删除成功", | ||
| 1273 | + "data": null | ||
| 1274 | +} | ||
| 1275 | +``` | ||
| 1276 | + | ||
| 1277 | +### 8.7 修改经销商合作状态 | ||
| 1278 | + | ||
| 1279 | +**接口路径:** `POST /api/dealer/{dealerId}/cooperateStatus/{cooperateStatus}` | ||
| 1280 | + | ||
| 1281 | +**功能描述:** 修改经销商的合作状态 | ||
| 1282 | + | ||
| 1283 | +**权限要求:** `dealer:updateStatus` | ||
| 1284 | + | ||
| 1285 | +**路径参数:** | ||
| 1286 | + | ||
| 1287 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 1288 | +|--------|------|------|------| | ||
| 1289 | +| dealerId | Long | 是 | 经销商ID | | ||
| 1290 | +| cooperateStatus | Integer | 是 | 合作状态(1-正常合作/2-暂停合作/3-终止合作) | | ||
| 1291 | + | ||
| 1292 | +**响应示例:** | ||
| 1293 | +```json | ||
| 1294 | +{ | ||
| 1295 | + "code": 200, | ||
| 1296 | + "message": "经销商合作状态修改成功", | ||
| 1297 | + "data": null | ||
| 1298 | +} | ||
| 1299 | +``` | ||
| 1300 | + | ||
| 1301 | +### 8.8 修改经销商资质审核状态 | ||
| 1302 | + | ||
| 1303 | +**接口路径:** `POST /api/dealer/{dealerId}/auditStatus` | ||
| 1304 | + | ||
| 1305 | +**功能描述:** 修改经销商的资质审核状态 | ||
| 1306 | + | ||
| 1307 | +**权限要求:** `dealer:audit` | ||
| 1308 | + | ||
| 1309 | +**路径参数:** | ||
| 1310 | + | ||
| 1311 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 1312 | +|--------|------|------|------| | ||
| 1313 | +| dealerId | Long | 是 | 经销商ID | | ||
| 1314 | + | ||
| 1315 | +**请求参数:** | ||
| 1316 | + | ||
| 1317 | +| 参数名 | 类型 | 必填 | 描述 | | ||
| 1318 | +|--------|------|------|------| | ||
| 1319 | +| qualificationAuditStatus | Integer | 是 | 资质审核状态(1-待审核/2-审核通过/3-审核不通过) | | ||
| 1320 | +| auditOpinion | String | 否 | 审核意见 | | ||
| 1321 | + | ||
| 1322 | +**响应示例:** | ||
| 1323 | +```json | ||
| 1324 | +{ | ||
| 1325 | + "code": 200, | ||
| 1326 | + "message": "经销商资质审核状态修改成功", | ||
| 1327 | + "data": null | ||
| 1328 | +} | ||
| 1329 | +``` | ||
| 1330 | + | ||
| 1331 | +## 数据字典 | ||
| 1332 | + | ||
| 1333 | +### 销售状态(saleStatus) | ||
| 1334 | + | ||
| 1335 | +| 值 | 描述 | | ||
| 1336 | +|----|------| | ||
| 1337 | +| 0 | 下架 | | ||
| 1338 | +| 1 | 在售 | | ||
| 1339 | +| 2 | 预售 | | ||
| 1340 | + | ||
| 1341 | +### 返利标识(rebateFlag) | ||
| 1342 | + | ||
| 1343 | +| 值 | 描述 | | ||
| 1344 | +|----|------| | ||
| 1345 | +| 0 | 否 | | ||
| 1346 | +| 1 | 是 | | ||
| 1347 | + | ||
| 1348 | +### 经销商等级(dealerLevel) | ||
| 1349 | + | ||
| 1350 | +| 值 | 描述 | | ||
| 1351 | +|----|------| | ||
| 1352 | +| 1 | 一级经销商 | | ||
| 1353 | +| 2 | 二级经销商 | | ||
| 1354 | + | ||
| 1355 | +### 合作状态(cooperateStatus) | ||
| 1356 | + | ||
| 1357 | +| 值 | 描述 | | ||
| 1358 | +|----|------| | ||
| 1359 | +| 1 | 正常合作 | | ||
| 1360 | +| 2 | 暂停合作 | | ||
| 1361 | +| 3 | 终止合作 | | ||
| 1362 | + | ||
| 1363 | +### 资质审核状态(qualificationAuditStatus) | ||
| 1364 | + | ||
| 1365 | +| 值 | 描述 | | ||
| 1366 | +|----|------| | ||
| 1367 | +| 1 | 待审核 | | ||
| 1368 | +| 2 | 审核通过 | | ||
| 1369 | +| 3 | 审核不通过 | | ||
| 1370 | + | ||
| 670 | --- | 1371 | --- |
| 671 | 1372 | ||
| 672 | **文档版本:** 1.0.0 | 1373 | **文档版本:** 1.0.0 |
| 673 | -**最后更新:** 2024-01-01 | 1374 | +**最后更新:** 2025-01-27 |
| 674 | **维护人员:** Apple ERP Team | 1375 | **维护人员:** Apple ERP Team | ... | ... |
| 1 | +package com.apple.erp.controller; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.DealerAddReq; | ||
| 4 | +import com.apple.erp.dto.DealerQueryReq; | ||
| 5 | +import com.apple.erp.dto.DealerUpdateReq; | ||
| 6 | +import com.apple.erp.entity.DealerInfo; | ||
| 7 | +import com.apple.erp.service.DealerInfoService; | ||
| 8 | +import com.apple.erp.dto.response.ApiRes; | ||
| 9 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 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 javax.validation.Valid; | ||
| 14 | +import lombok.RequiredArgsConstructor; | ||
| 15 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 16 | +import org.springframework.web.bind.annotation.*; | ||
| 17 | + | ||
| 18 | +import java.util.List; | ||
| 19 | + | ||
| 20 | +@Tag(name = "经销商管理", description = "经销商信息管理接口") | ||
| 21 | +@RestController | ||
| 22 | +@RequestMapping("/api/dealer") | ||
| 23 | +@RequiredArgsConstructor | ||
| 24 | +public class DealerInfoController { | ||
| 25 | + | ||
| 26 | + private final DealerInfoService dealerInfoService; | ||
| 27 | + | ||
| 28 | + @Operation(summary = "获取经销商列表", description = "分页查询经销商信息列表") | ||
| 29 | + @GetMapping("/list") | ||
| 30 | + @PreAuthorize("hasAuthority('dealer:list')") | ||
| 31 | + public ApiRes<IPage<DealerInfo>> getDealerList(@Valid DealerQueryReq queryReq) { | ||
| 32 | + IPage<DealerInfo> page = dealerInfoService.getDealerList(queryReq); | ||
| 33 | + return ApiRes.success(page); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + @Operation(summary = "获取经销商详情", description = "根据经销商ID获取经销商详细信息") | ||
| 37 | + @GetMapping("/{dealerId}") | ||
| 38 | + @PreAuthorize("hasAuthority('dealer:detail')") | ||
| 39 | + public ApiRes<DealerInfo> getDealerDetail(@Parameter(description = "经销商ID") @PathVariable Long dealerId) { | ||
| 40 | + DealerInfo dealerInfo = dealerInfoService.getDealerDetail(dealerId); | ||
| 41 | + if (dealerInfo != null) { | ||
| 42 | + return ApiRes.success(dealerInfo); | ||
| 43 | + } else { | ||
| 44 | + return ApiRes.error("经销商不存在"); | ||
| 45 | + } | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + @Operation(summary = "新增经销商", description = "新增一个经销商信息") | ||
| 49 | + @PostMapping | ||
| 50 | + @PreAuthorize("hasAuthority('dealer:add')") | ||
| 51 | + public ApiRes<String> addDealer(@Parameter(description = "经销商新增请求") @Valid @RequestBody DealerAddReq addReq) { | ||
| 52 | + try { | ||
| 53 | + boolean success = dealerInfoService.addDealer(addReq); | ||
| 54 | + return success ? ApiRes.success("经销商新增成功", null) : ApiRes.error("经销商新增失败"); | ||
| 55 | + } catch (IllegalArgumentException e) { | ||
| 56 | + return ApiRes.error(e.getMessage()); | ||
| 57 | + } | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + @Operation(summary = "修改经销商", description = "修改一个经销商信息") | ||
| 61 | + @PostMapping("/update") | ||
| 62 | + @PreAuthorize("hasAuthority('dealer:update')") | ||
| 63 | + public ApiRes<String> updateDealer(@Parameter(description = "经销商修改请求") @Valid @RequestBody DealerUpdateReq updateReq) { | ||
| 64 | + try { | ||
| 65 | + boolean success = dealerInfoService.updateDealer(updateReq); | ||
| 66 | + return success ? ApiRes.success("经销商修改成功", null) : ApiRes.error("经销商修改失败"); | ||
| 67 | + } catch (IllegalArgumentException e) { | ||
| 68 | + return ApiRes.error(e.getMessage()); | ||
| 69 | + } | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + @Operation(summary = "删除经销商", description = "根据经销商ID删除经销商信息(软删除)") | ||
| 73 | + @DeleteMapping("/{dealerId}") | ||
| 74 | + @PreAuthorize("hasAuthority('dealer:delete')") | ||
| 75 | + public ApiRes<String> deleteDealer(@Parameter(description = "经销商ID") @PathVariable Long dealerId) { | ||
| 76 | + try { | ||
| 77 | + boolean success = dealerInfoService.deleteDealer(dealerId); | ||
| 78 | + return success ? ApiRes.success("经销商删除成功", null) : ApiRes.error("经销商删除失败"); | ||
| 79 | + } catch (IllegalArgumentException e) { | ||
| 80 | + return ApiRes.error(e.getMessage()); | ||
| 81 | + } | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + @Operation(summary = "批量删除经销商", description = "根据经销商ID列表批量删除经销商信息(软删除)") | ||
| 85 | + @DeleteMapping("/batch") | ||
| 86 | + @PreAuthorize("hasAuthority('dealer:batchDelete')") | ||
| 87 | + public ApiRes<String> batchDeleteDealers(@Parameter(description = "经销商ID列表") @RequestBody List<Long> dealerIds) { | ||
| 88 | + boolean success = dealerInfoService.batchDeleteDealers(dealerIds); | ||
| 89 | + return success ? ApiRes.success("经销商批量删除成功", null) : ApiRes.error("经销商批量删除失败"); | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + @Operation(summary = "修改经销商合作状态", description = "修改经销商的合作状态") | ||
| 93 | + @PostMapping("/{dealerId}/cooperateStatus/{cooperateStatus}") | ||
| 94 | + @PreAuthorize("hasAuthority('dealer:updateStatus')") | ||
| 95 | + public ApiRes<String> updateDealerCooperateStatus( | ||
| 96 | + @Parameter(description = "经销商ID") @PathVariable Long dealerId, | ||
| 97 | + @Parameter(description = "合作状态(1-正常合作/2-暂停合作/3-终止合作)") @PathVariable Integer cooperateStatus) { | ||
| 98 | + try { | ||
| 99 | + boolean success = dealerInfoService.updateDealerCooperateStatus(dealerId, cooperateStatus); | ||
| 100 | + return success ? ApiRes.success("经销商合作状态修改成功", null) : ApiRes.error("经销商合作状态修改失败"); | ||
| 101 | + } catch (IllegalArgumentException e) { | ||
| 102 | + return ApiRes.error(e.getMessage()); | ||
| 103 | + } | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + @Operation(summary = "修改经销商资质审核状态", description = "修改经销商的资质审核状态") | ||
| 107 | + @PostMapping("/{dealerId}/auditStatus") | ||
| 108 | + @PreAuthorize("hasAuthority('dealer:audit')") | ||
| 109 | + public ApiRes<String> updateDealerAuditStatus( | ||
| 110 | + @Parameter(description = "经销商ID") @PathVariable Long dealerId, | ||
| 111 | + @Parameter(description = "资质审核状态(1-待审核/2-审核通过/3-审核不通过)") @RequestParam Integer qualificationAuditStatus, | ||
| 112 | + @Parameter(description = "审核意见") @RequestParam(required = false) String auditOpinion) { | ||
| 113 | + try { | ||
| 114 | + boolean success = dealerInfoService.updateDealerAuditStatus(dealerId, qualificationAuditStatus, auditOpinion); | ||
| 115 | + return success ? ApiRes.success("经销商资质审核状态修改成功", null) : ApiRes.error("经销商资质审核状态修改失败"); | ||
| 116 | + } catch (IllegalArgumentException e) { | ||
| 117 | + return ApiRes.error(e.getMessage()); | ||
| 118 | + } | ||
| 119 | + } | ||
| 120 | +} |
| 1 | +package com.apple.erp.controller; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.ProductAddReq; | ||
| 4 | +import com.apple.erp.dto.ProductQueryReq; | ||
| 5 | +import com.apple.erp.dto.ProductUpdateReq; | ||
| 6 | +import com.apple.erp.entity.ProductInfo; | ||
| 7 | +import com.apple.erp.service.ProductInfoService; | ||
| 8 | +import com.apple.erp.dto.response.ApiRes; | ||
| 9 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 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 lombok.RequiredArgsConstructor; | ||
| 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 | + * 产品信息管理控制器 | ||
| 23 | + * | ||
| 24 | + * @author Apple ERP Team | ||
| 25 | + * @since 2025-01-27 | ||
| 26 | + */ | ||
| 27 | +@Tag(name = "产品信息管理", description = "产品信息的增删改查等操作") | ||
| 28 | +@RestController | ||
| 29 | +@RequestMapping("/api/product") | ||
| 30 | +@RequiredArgsConstructor | ||
| 31 | +@Validated | ||
| 32 | +public class ProductInfoController { | ||
| 33 | + | ||
| 34 | + private final ProductInfoService productInfoService; | ||
| 35 | + | ||
| 36 | + @Operation(summary = "分页查询产品列表", description = "根据条件分页查询产品列表") | ||
| 37 | + @GetMapping("/list") | ||
| 38 | + @PreAuthorize("hasAuthority('product:list')") | ||
| 39 | + public ApiRes<IPage<ProductInfo>> getProductList(@Valid ProductQueryReq queryReq) { | ||
| 40 | + try { | ||
| 41 | + IPage<ProductInfo> result = productInfoService.getProductPage(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("/{productId}") | ||
| 50 | + @PreAuthorize("hasAuthority('product:detail')") | ||
| 51 | + public ApiRes<ProductInfo> getProductDetail( | ||
| 52 | + @Parameter(description = "产品ID", required = true) | ||
| 53 | + @PathVariable Long productId) { | ||
| 54 | + try { | ||
| 55 | + ProductInfo productInfo = productInfoService.getProductById(productId); | ||
| 56 | + if (productInfo == null) { | ||
| 57 | + return ApiRes.error("产品不存在"); | ||
| 58 | + } | ||
| 59 | + return ApiRes.success(productInfo); | ||
| 60 | + } catch (Exception e) { | ||
| 61 | + return ApiRes.error("获取产品详情失败:" + e.getMessage()); | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + @Operation(summary = "新增产品", description = "新增产品信息") | ||
| 66 | + @PostMapping | ||
| 67 | + @PreAuthorize("hasAuthority('product:add')") | ||
| 68 | + public ApiRes<String> addProduct(@Valid @RequestBody ProductAddReq addReq) { | ||
| 69 | + try { | ||
| 70 | + boolean success = productInfoService.addProduct(addReq); | ||
| 71 | + if (success) { | ||
| 72 | + return ApiRes.success("新增产品成功"); | ||
| 73 | + } else { | ||
| 74 | + return ApiRes.error("新增产品失败"); | ||
| 75 | + } | ||
| 76 | + } catch (RuntimeException e) { | ||
| 77 | + return ApiRes.error(e.getMessage()); | ||
| 78 | + } catch (Exception e) { | ||
| 79 | + return ApiRes.error("新增产品失败:" + e.getMessage()); | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + @Operation(summary = "修改产品", description = "修改产品信息") | ||
| 84 | + @PostMapping("/update") | ||
| 85 | + @PreAuthorize("hasAuthority('product:edit')") | ||
| 86 | + public ApiRes<String> updateProduct(@Valid @RequestBody ProductUpdateReq updateReq) { | ||
| 87 | + try { | ||
| 88 | + boolean success = productInfoService.updateProduct(updateReq); | ||
| 89 | + if (success) { | ||
| 90 | + return ApiRes.success("修改产品成功"); | ||
| 91 | + } else { | ||
| 92 | + return ApiRes.error("修改产品失败"); | ||
| 93 | + } | ||
| 94 | + } catch (RuntimeException e) { | ||
| 95 | + return ApiRes.error(e.getMessage()); | ||
| 96 | + } catch (Exception e) { | ||
| 97 | + return ApiRes.error("修改产品失败:" + e.getMessage()); | ||
| 98 | + } | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + @Operation(summary = "删除产品", description = "根据产品ID删除产品") | ||
| 102 | + @DeleteMapping("/{productId}") | ||
| 103 | + @PreAuthorize("hasAuthority('product:delete')") | ||
| 104 | + public ApiRes<String> deleteProduct( | ||
| 105 | + @Parameter(description = "产品ID", required = true) | ||
| 106 | + @PathVariable Long productId) { | ||
| 107 | + try { | ||
| 108 | + boolean success = productInfoService.deleteProduct(productId); | ||
| 109 | + if (success) { | ||
| 110 | + return ApiRes.success("删除产品成功"); | ||
| 111 | + } else { | ||
| 112 | + return ApiRes.error("删除产品失败"); | ||
| 113 | + } | ||
| 114 | + } catch (Exception e) { | ||
| 115 | + return ApiRes.error("删除产品失败:" + e.getMessage()); | ||
| 116 | + } | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + @Operation(summary = "批量删除产品", description = "批量删除产品") | ||
| 120 | + @PostMapping("/batchDelete") | ||
| 121 | + @PreAuthorize("hasAuthority('product:delete')") | ||
| 122 | + public ApiRes<String> batchDeleteProducts(@RequestBody List<Long> productIds) { | ||
| 123 | + try { | ||
| 124 | + if (productIds == null || productIds.isEmpty()) { | ||
| 125 | + return ApiRes.error("请选择要删除的产品"); | ||
| 126 | + } | ||
| 127 | + boolean success = productInfoService.batchDeleteProducts(productIds); | ||
| 128 | + if (success) { | ||
| 129 | + return ApiRes.success("批量删除产品成功"); | ||
| 130 | + } else { | ||
| 131 | + return ApiRes.error("批量删除产品失败"); | ||
| 132 | + } | ||
| 133 | + } catch (Exception e) { | ||
| 134 | + return ApiRes.error("批量删除产品失败:" + e.getMessage()); | ||
| 135 | + } | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + @Operation(summary = "修改产品状态", description = "修改产品销售状态") | ||
| 139 | + @PostMapping("/{productId}/status") | ||
| 140 | + @PreAuthorize("hasAuthority('product:edit')") | ||
| 141 | + public ApiRes<String> updateProductStatus( | ||
| 142 | + @Parameter(description = "产品ID", required = true) | ||
| 143 | + @PathVariable Long productId, | ||
| 144 | + @Parameter(description = "销售状态", required = true) | ||
| 145 | + @RequestParam Integer saleStatus) { | ||
| 146 | + try { | ||
| 147 | + boolean success = productInfoService.updateProductStatus(productId, saleStatus); | ||
| 148 | + if (success) { | ||
| 149 | + return ApiRes.success("修改产品状态成功"); | ||
| 150 | + } else { | ||
| 151 | + return ApiRes.error("修改产品状态失败"); | ||
| 152 | + } | ||
| 153 | + } catch (Exception e) { | ||
| 154 | + return ApiRes.error("修改产品状态失败:" + e.getMessage()); | ||
| 155 | + } | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + @Operation(summary = "修改返利标识", description = "修改产品返利标识") | ||
| 159 | + @PostMapping("/{productId}/rebate") | ||
| 160 | + @PreAuthorize("hasAuthority('product:edit')") | ||
| 161 | + public ApiRes<String> updateRebateFlag( | ||
| 162 | + @Parameter(description = "产品ID", required = true) | ||
| 163 | + @PathVariable Long productId, | ||
| 164 | + @Parameter(description = "返利标识", required = true) | ||
| 165 | + @RequestParam Integer rebateFlag) { | ||
| 166 | + try { | ||
| 167 | + boolean success = productInfoService.updateRebateFlag(productId, rebateFlag); | ||
| 168 | + if (success) { | ||
| 169 | + return ApiRes.success("修改返利标识成功"); | ||
| 170 | + } else { | ||
| 171 | + return ApiRes.error("修改返利标识失败"); | ||
| 172 | + } | ||
| 173 | + } catch (Exception e) { | ||
| 174 | + return ApiRes.error("修改返利标识失败:" + e.getMessage()); | ||
| 175 | + } | ||
| 176 | + } | ||
| 177 | +} |
| 1 | +package com.apple.erp.dto; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import javax.validation.constraints.NotBlank; | ||
| 5 | +import javax.validation.constraints.NotNull; | ||
| 6 | +import javax.validation.constraints.Pattern; | ||
| 7 | +import lombok.Data; | ||
| 8 | +import org.springframework.format.annotation.DateTimeFormat; | ||
| 9 | + | ||
| 10 | +import java.time.LocalDate; | ||
| 11 | + | ||
| 12 | +@Data | ||
| 13 | +@Schema(description = "经销商新增请求DTO") | ||
| 14 | +public class DealerAddReq { | ||
| 15 | + | ||
| 16 | + @NotBlank(message = "经销商编码不能为空") | ||
| 17 | + @Pattern(regexp = "^[A-Z0-9]{6,20}$", message = "经销商编码格式不正确,应为6-20位大写字母和数字") | ||
| 18 | + @Schema(description = "经销商编码", required = true) | ||
| 19 | + private String dealerCode; | ||
| 20 | + | ||
| 21 | + @NotBlank(message = "经销商名称不能为空") | ||
| 22 | + @Schema(description = "经销商名称", required = true) | ||
| 23 | + private String dealerName; | ||
| 24 | + | ||
| 25 | + @NotBlank(message = "统一社会信用代码不能为空") | ||
| 26 | + @Pattern(regexp = "^[0-9A-HJ-NPQRTUWXY]{2}[0-9]{6}[0-9A-HJ-NPQRTUWXY]{10}$", message = "统一社会信用代码格式不正确") | ||
| 27 | + @Schema(description = "统一社会信用代码", required = true) | ||
| 28 | + private String creditCode; | ||
| 29 | + | ||
| 30 | + @NotNull(message = "经销商等级不能为空") | ||
| 31 | + @Schema(description = "经销商等级(1-一级经销商/2-二级经销商)", required = true) | ||
| 32 | + private Integer dealerLevel; | ||
| 33 | + | ||
| 34 | + @NotBlank(message = "所在区域不能为空") | ||
| 35 | + @Schema(description = "所在区域", required = true) | ||
| 36 | + private String region; | ||
| 37 | + | ||
| 38 | + @Schema(description = "联系人") | ||
| 39 | + private String contactPerson; | ||
| 40 | + | ||
| 41 | + @Schema(description = "联系电话") | ||
| 42 | + private String contactPhone; | ||
| 43 | + | ||
| 44 | + @NotNull(message = "合作起始日期不能为空") | ||
| 45 | + @Schema(description = "合作起始日期", required = true) | ||
| 46 | + @DateTimeFormat(pattern = "yyyy-MM-dd") | ||
| 47 | + private LocalDate cooperateStartDate; | ||
| 48 | + | ||
| 49 | + @Schema(description = "合作状态(1-正常合作/2-暂停合作/3-终止合作)") | ||
| 50 | + private Integer cooperateStatus = 1; | ||
| 51 | + | ||
| 52 | + @Schema(description = "营业执照URL") | ||
| 53 | + private String businessLicenseUrl; | ||
| 54 | + | ||
| 55 | + @Schema(description = "合作协议URL") | ||
| 56 | + private String cooperationAgreementUrl; | ||
| 57 | + | ||
| 58 | + @Schema(description = "资质审核状态(1-待审核/2-审核通过/3-审核不通过)") | ||
| 59 | + private Integer qualificationAuditStatus = 1; | ||
| 60 | + | ||
| 61 | + @Schema(description = "审核意见") | ||
| 62 | + private String auditOpinion; | ||
| 63 | +} |
| 1 | +package com.apple.erp.dto; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import org.springframework.format.annotation.DateTimeFormat; | ||
| 6 | + | ||
| 7 | +import java.time.LocalDate; | ||
| 8 | + | ||
| 9 | +@Data | ||
| 10 | +@Schema(description = "经销商查询请求DTO") | ||
| 11 | +public class DealerQueryReq { | ||
| 12 | + | ||
| 13 | + @Schema(description = "经销商编码") | ||
| 14 | + private String dealerCode; | ||
| 15 | + | ||
| 16 | + @Schema(description = "经销商名称") | ||
| 17 | + private String dealerName; | ||
| 18 | + | ||
| 19 | + @Schema(description = "统一社会信用代码") | ||
| 20 | + private String creditCode; | ||
| 21 | + | ||
| 22 | + @Schema(description = "经销商等级(1-一级经销商/2-二级经销商)") | ||
| 23 | + private Integer dealerLevel; | ||
| 24 | + | ||
| 25 | + @Schema(description = "所在区域") | ||
| 26 | + private String region; | ||
| 27 | + | ||
| 28 | + @Schema(description = "联系人") | ||
| 29 | + private String contactPerson; | ||
| 30 | + | ||
| 31 | + @Schema(description = "联系电话") | ||
| 32 | + private String contactPhone; | ||
| 33 | + | ||
| 34 | + @Schema(description = "合作状态(1-正常合作/2-暂停合作/3-终止合作)") | ||
| 35 | + private Integer cooperateStatus; | ||
| 36 | + | ||
| 37 | + @Schema(description = "资质审核状态(1-待审核/2-审核通过/3-审核不通过)") | ||
| 38 | + private Integer qualificationAuditStatus; | ||
| 39 | + | ||
| 40 | + @Schema(description = "合作起始日期开始") | ||
| 41 | + @DateTimeFormat(pattern = "yyyy-MM-dd") | ||
| 42 | + private LocalDate cooperateStartDateStart; | ||
| 43 | + | ||
| 44 | + @Schema(description = "合作起始日期结束") | ||
| 45 | + @DateTimeFormat(pattern = "yyyy-MM-dd") | ||
| 46 | + private LocalDate cooperateStartDateEnd; | ||
| 47 | + | ||
| 48 | + @Schema(description = "当前页码") | ||
| 49 | + private Long pageNum = 1L; | ||
| 50 | + | ||
| 51 | + @Schema(description = "每页大小") | ||
| 52 | + private Long pageSize = 10L; | ||
| 53 | +} |
| 1 | +package com.apple.erp.dto; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import javax.validation.constraints.NotBlank; | ||
| 5 | +import javax.validation.constraints.NotNull; | ||
| 6 | +import javax.validation.constraints.Pattern; | ||
| 7 | +import lombok.Data; | ||
| 8 | +import org.springframework.format.annotation.DateTimeFormat; | ||
| 9 | + | ||
| 10 | +import java.time.LocalDate; | ||
| 11 | + | ||
| 12 | +@Data | ||
| 13 | +@Schema(description = "经销商修改请求DTO") | ||
| 14 | +public class DealerUpdateReq { | ||
| 15 | + | ||
| 16 | + @NotNull(message = "经销商ID不能为空") | ||
| 17 | + @Schema(description = "经销商ID", required = true) | ||
| 18 | + private Long dealerId; | ||
| 19 | + | ||
| 20 | + @NotBlank(message = "经销商编码不能为空") | ||
| 21 | + @Pattern(regexp = "^[A-Z0-9]{6,20}$", message = "经销商编码格式不正确,应为6-20位大写字母和数字") | ||
| 22 | + @Schema(description = "经销商编码", required = true) | ||
| 23 | + private String dealerCode; | ||
| 24 | + | ||
| 25 | + @NotBlank(message = "经销商名称不能为空") | ||
| 26 | + @Schema(description = "经销商名称", required = true) | ||
| 27 | + private String dealerName; | ||
| 28 | + | ||
| 29 | + @NotBlank(message = "统一社会信用代码不能为空") | ||
| 30 | + @Pattern(regexp = "^[0-9A-HJ-NPQRTUWXY]{2}[0-9]{6}[0-9A-HJ-NPQRTUWXY]{10}$", message = "统一社会信用代码格式不正确") | ||
| 31 | + @Schema(description = "统一社会信用代码", required = true) | ||
| 32 | + private String creditCode; | ||
| 33 | + | ||
| 34 | + @NotNull(message = "经销商等级不能为空") | ||
| 35 | + @Schema(description = "经销商等级(1-一级经销商/2-二级经销商)", required = true) | ||
| 36 | + private Integer dealerLevel; | ||
| 37 | + | ||
| 38 | + @NotBlank(message = "所在区域不能为空") | ||
| 39 | + @Schema(description = "所在区域", required = true) | ||
| 40 | + private String region; | ||
| 41 | + | ||
| 42 | + @Schema(description = "联系人") | ||
| 43 | + private String contactPerson; | ||
| 44 | + | ||
| 45 | + @Schema(description = "联系电话") | ||
| 46 | + private String contactPhone; | ||
| 47 | + | ||
| 48 | + @NotNull(message = "合作起始日期不能为空") | ||
| 49 | + @Schema(description = "合作起始日期", required = true) | ||
| 50 | + @DateTimeFormat(pattern = "yyyy-MM-dd") | ||
| 51 | + private LocalDate cooperateStartDate; | ||
| 52 | + | ||
| 53 | + @Schema(description = "合作状态(1-正常合作/2-暂停合作/3-终止合作)") | ||
| 54 | + private Integer cooperateStatus; | ||
| 55 | + | ||
| 56 | + @Schema(description = "营业执照URL") | ||
| 57 | + private String businessLicenseUrl; | ||
| 58 | + | ||
| 59 | + @Schema(description = "合作协议URL") | ||
| 60 | + private String cooperationAgreementUrl; | ||
| 61 | + | ||
| 62 | + @Schema(description = "资质审核状态(1-待审核/2-审核通过/3-审核不通过)") | ||
| 63 | + private Integer qualificationAuditStatus; | ||
| 64 | + | ||
| 65 | + @Schema(description = "审核意见") | ||
| 66 | + private String auditOpinion; | ||
| 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.constraints.NotBlank; | ||
| 8 | +import javax.validation.constraints.NotNull; | ||
| 9 | +import java.math.BigDecimal; | ||
| 10 | +import java.time.LocalDate; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 产品新增请求DTO | ||
| 14 | + * | ||
| 15 | + * @author Apple ERP Team | ||
| 16 | + * @since 2025-01-27 | ||
| 17 | + */ | ||
| 18 | +@Data | ||
| 19 | +@Schema(description = "产品新增请求") | ||
| 20 | +public class ProductAddReq { | ||
| 21 | + | ||
| 22 | + @Schema(description = "产品编码", required = true) | ||
| 23 | + @NotBlank(message = "产品编码不能为空") | ||
| 24 | + private String productCode; | ||
| 25 | + | ||
| 26 | + @Schema(description = "产品名称", required = true) | ||
| 27 | + @NotBlank(message = "产品名称不能为空") | ||
| 28 | + private String productName; | ||
| 29 | + | ||
| 30 | + @Schema(description = "产品型号", required = true) | ||
| 31 | + @NotBlank(message = "产品型号不能为空") | ||
| 32 | + private String productModel; | ||
| 33 | + | ||
| 34 | + @Schema(description = "产品类别", required = true) | ||
| 35 | + @NotBlank(message = "产品类别不能为空") | ||
| 36 | + private String productType; | ||
| 37 | + | ||
| 38 | + @Schema(description = "存储容量") | ||
| 39 | + private String storageCapacity; | ||
| 40 | + | ||
| 41 | + @Schema(description = "产品颜色") | ||
| 42 | + private String color; | ||
| 43 | + | ||
| 44 | + @Schema(description = "产品图片URL") | ||
| 45 | + private String productImgUrl; | ||
| 46 | + | ||
| 47 | + @Schema(description = "官方指导价") | ||
| 48 | + private BigDecimal officialPrice; | ||
| 49 | + | ||
| 50 | + @Schema(description = "销售状态(0-下架/1-在售/2-预售)", required = true) | ||
| 51 | + @NotNull(message = "销售状态不能为空") | ||
| 52 | + private Integer saleStatus; | ||
| 53 | + | ||
| 54 | + @Schema(description = "是否参与返利(0-否/1-是)", required = true) | ||
| 55 | + @NotNull(message = "返利标识不能为空") | ||
| 56 | + private Integer rebateFlag; | ||
| 57 | + | ||
| 58 | + @Schema(description = "销售起始日期") | ||
| 59 | + @JsonFormat(pattern = "yyyy-MM-dd") | ||
| 60 | + private LocalDate saleStartDate; | ||
| 61 | + | ||
| 62 | + @Schema(description = "销售终止日期") | ||
| 63 | + @JsonFormat(pattern = "yyyy-MM-dd") | ||
| 64 | + private LocalDate saleEndDate; | ||
| 65 | + | ||
| 66 | + @Schema(description = "产品备注") | ||
| 67 | + private String remark; | ||
| 68 | +} |
| 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 | + | ||
| 10 | +/** | ||
| 11 | + * 产品查询请求DTO | ||
| 12 | + * | ||
| 13 | + * @author Apple ERP Team | ||
| 14 | + * @since 2025-01-27 | ||
| 15 | + */ | ||
| 16 | +@Data | ||
| 17 | +@Schema(description = "产品查询请求") | ||
| 18 | +public class ProductQueryReq { | ||
| 19 | + | ||
| 20 | + @Schema(description = "产品编码") | ||
| 21 | + private String productCode; | ||
| 22 | + | ||
| 23 | + @Schema(description = "产品名称") | ||
| 24 | + private String productName; | ||
| 25 | + | ||
| 26 | + @Schema(description = "产品型号") | ||
| 27 | + private String productModel; | ||
| 28 | + | ||
| 29 | + @Schema(description = "产品类别") | ||
| 30 | + private String productType; | ||
| 31 | + | ||
| 32 | + @Schema(description = "存储容量") | ||
| 33 | + private String storageCapacity; | ||
| 34 | + | ||
| 35 | + @Schema(description = "产品颜色") | ||
| 36 | + private String color; | ||
| 37 | + | ||
| 38 | + @Schema(description = "销售状态(0-下架/1-在售/2-预售)") | ||
| 39 | + private Integer saleStatus; | ||
| 40 | + | ||
| 41 | + @Schema(description = "是否参与返利(0-否/1-是)") | ||
| 42 | + private Integer rebateFlag; | ||
| 43 | + | ||
| 44 | + @Schema(description = "最低价格") | ||
| 45 | + private BigDecimal minPrice; | ||
| 46 | + | ||
| 47 | + @Schema(description = "最高价格") | ||
| 48 | + private BigDecimal maxPrice; | ||
| 49 | + | ||
| 50 | + @Schema(description = "销售起始日期开始") | ||
| 51 | + @JsonFormat(pattern = "yyyy-MM-dd") | ||
| 52 | + private LocalDate saleStartDateBegin; | ||
| 53 | + | ||
| 54 | + @Schema(description = "销售起始日期结束") | ||
| 55 | + @JsonFormat(pattern = "yyyy-MM-dd") | ||
| 56 | + private LocalDate saleStartDateEnd; | ||
| 57 | + | ||
| 58 | + @Schema(description = "销售终止日期开始") | ||
| 59 | + @JsonFormat(pattern = "yyyy-MM-dd") | ||
| 60 | + private LocalDate saleEndDateBegin; | ||
| 61 | + | ||
| 62 | + @Schema(description = "销售终止日期结束") | ||
| 63 | + @JsonFormat(pattern = "yyyy-MM-dd") | ||
| 64 | + private LocalDate saleEndDateEnd; | ||
| 65 | + | ||
| 66 | + @Schema(description = "页码", example = "1") | ||
| 67 | + private Integer pageNum = 1; | ||
| 68 | + | ||
| 69 | + @Schema(description = "每页大小", example = "10") | ||
| 70 | + private Integer pageSize = 10; | ||
| 71 | +} |
| 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.constraints.NotBlank; | ||
| 8 | +import javax.validation.constraints.NotNull; | ||
| 9 | +import java.math.BigDecimal; | ||
| 10 | +import java.time.LocalDate; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 产品修改请求DTO | ||
| 14 | + * | ||
| 15 | + * @author Apple ERP Team | ||
| 16 | + * @since 2025-01-27 | ||
| 17 | + */ | ||
| 18 | +@Data | ||
| 19 | +@Schema(description = "产品修改请求") | ||
| 20 | +public class ProductUpdateReq { | ||
| 21 | + | ||
| 22 | + @Schema(description = "产品ID", required = true) | ||
| 23 | + @NotNull(message = "产品ID不能为空") | ||
| 24 | + private Long productId; | ||
| 25 | + | ||
| 26 | + @Schema(description = "产品编码", required = true) | ||
| 27 | + @NotBlank(message = "产品编码不能为空") | ||
| 28 | + private String productCode; | ||
| 29 | + | ||
| 30 | + @Schema(description = "产品名称", required = true) | ||
| 31 | + @NotBlank(message = "产品名称不能为空") | ||
| 32 | + private String productName; | ||
| 33 | + | ||
| 34 | + @Schema(description = "产品型号", required = true) | ||
| 35 | + @NotBlank(message = "产品型号不能为空") | ||
| 36 | + private String productModel; | ||
| 37 | + | ||
| 38 | + @Schema(description = "产品类别", required = true) | ||
| 39 | + @NotBlank(message = "产品类别不能为空") | ||
| 40 | + private String productType; | ||
| 41 | + | ||
| 42 | + @Schema(description = "存储容量") | ||
| 43 | + private String storageCapacity; | ||
| 44 | + | ||
| 45 | + @Schema(description = "产品颜色") | ||
| 46 | + private String color; | ||
| 47 | + | ||
| 48 | + @Schema(description = "产品图片URL") | ||
| 49 | + private String productImgUrl; | ||
| 50 | + | ||
| 51 | + @Schema(description = "官方指导价") | ||
| 52 | + private BigDecimal officialPrice; | ||
| 53 | + | ||
| 54 | + @Schema(description = "销售状态(0-下架/1-在售/2-预售)", required = true) | ||
| 55 | + @NotNull(message = "销售状态不能为空") | ||
| 56 | + private Integer saleStatus; | ||
| 57 | + | ||
| 58 | + @Schema(description = "是否参与返利(0-否/1-是)", required = true) | ||
| 59 | + @NotNull(message = "返利标识不能为空") | ||
| 60 | + private Integer rebateFlag; | ||
| 61 | + | ||
| 62 | + @Schema(description = "销售起始日期") | ||
| 63 | + @JsonFormat(pattern = "yyyy-MM-dd") | ||
| 64 | + private LocalDate saleStartDate; | ||
| 65 | + | ||
| 66 | + @Schema(description = "销售终止日期") | ||
| 67 | + @JsonFormat(pattern = "yyyy-MM-dd") | ||
| 68 | + private LocalDate saleEndDate; | ||
| 69 | + | ||
| 70 | + @Schema(description = "产品备注") | ||
| 71 | + private String remark; | ||
| 72 | +} |
| 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 | + | ||
| 9 | +import java.math.BigDecimal; | ||
| 10 | +import java.time.LocalDate; | ||
| 11 | +import java.time.LocalDateTime; | ||
| 12 | + | ||
| 13 | +@Data | ||
| 14 | +@TableName("t_dealer_info") | ||
| 15 | +@Schema(description = "经销商信息实体") | ||
| 16 | +public class DealerInfo { | ||
| 17 | + | ||
| 18 | + @TableId(type = IdType.AUTO) | ||
| 19 | + @Schema(description = "经销商ID") | ||
| 20 | + private Long dealerId; | ||
| 21 | + | ||
| 22 | + @Schema(description = "经销商编码") | ||
| 23 | + private String dealerCode; | ||
| 24 | + | ||
| 25 | + @Schema(description = "经销商名称") | ||
| 26 | + private String dealerName; | ||
| 27 | + | ||
| 28 | + @Schema(description = "统一社会信用代码") | ||
| 29 | + private String creditCode; | ||
| 30 | + | ||
| 31 | + @Schema(description = "经销商等级(1-一级经销商/2-二级经销商)") | ||
| 32 | + private Integer dealerLevel; | ||
| 33 | + | ||
| 34 | + @Schema(description = "所在区域") | ||
| 35 | + private String region; | ||
| 36 | + | ||
| 37 | + @Schema(description = "联系人") | ||
| 38 | + private String contactPerson; | ||
| 39 | + | ||
| 40 | + @Schema(description = "联系电话") | ||
| 41 | + private String contactPhone; | ||
| 42 | + | ||
| 43 | + @Schema(description = "合作起始日期") | ||
| 44 | + private LocalDate cooperateStartDate; | ||
| 45 | + | ||
| 46 | + @Schema(description = "合作状态(1-正常合作/2-暂停合作/3-终止合作)") | ||
| 47 | + private Integer cooperateStatus; | ||
| 48 | + | ||
| 49 | + @Schema(description = "营业执照URL") | ||
| 50 | + private String businessLicenseUrl; | ||
| 51 | + | ||
| 52 | + @Schema(description = "合作协议URL") | ||
| 53 | + private String cooperationAgreementUrl; | ||
| 54 | + | ||
| 55 | + @Schema(description = "资质审核状态(1-待审核/2-审核通过/3-审核不通过)") | ||
| 56 | + private Integer qualificationAuditStatus; | ||
| 57 | + | ||
| 58 | + @Schema(description = "审核意见") | ||
| 59 | + private String auditOpinion; | ||
| 60 | + | ||
| 61 | + @Schema(description = "累计应返金额") | ||
| 62 | + private BigDecimal totalRebateAmount; | ||
| 63 | + | ||
| 64 | + @Schema(description = "累计已返金额") | ||
| 65 | + private BigDecimal usedRebateAmount; | ||
| 66 | + | ||
| 67 | + @Schema(description = "待返利总金额") | ||
| 68 | + private BigDecimal pendingRebateAmount; | ||
| 69 | + | ||
| 70 | + @Schema(description = "最后返利更新时间") | ||
| 71 | + private LocalDateTime lastRebateUpdateTime; | ||
| 72 | + | ||
| 73 | + @Schema(description = "创建者") | ||
| 74 | + private String createBy; | ||
| 75 | + | ||
| 76 | + @Schema(description = "创建时间") | ||
| 77 | + private LocalDateTime createTime; | ||
| 78 | + | ||
| 79 | + @Schema(description = "更新者") | ||
| 80 | + private String updateBy; | ||
| 81 | + | ||
| 82 | + @Schema(description = "更新时间") | ||
| 83 | + private LocalDateTime updateTime; | ||
| 84 | + | ||
| 85 | + @Schema(description = "删除标志(0代表存在 2代表删除)") | ||
| 86 | + private String delFlag; | ||
| 87 | +} |
| 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 lombok.Data; | ||
| 8 | +import lombok.EqualsAndHashCode; | ||
| 9 | + | ||
| 10 | +import java.io.Serializable; | ||
| 11 | +import java.math.BigDecimal; | ||
| 12 | +import java.time.LocalDate; | ||
| 13 | +import java.time.LocalDateTime; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * 产品信息表 | ||
| 17 | + * | ||
| 18 | + * @author Apple ERP Team | ||
| 19 | + * @since 2025-01-27 | ||
| 20 | + */ | ||
| 21 | +@Data | ||
| 22 | +@EqualsAndHashCode(callSuper = false) | ||
| 23 | +@TableName("t_product_info") | ||
| 24 | +public class ProductInfo implements Serializable { | ||
| 25 | + | ||
| 26 | + private static final long serialVersionUID = 1L; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 产品ID | ||
| 30 | + */ | ||
| 31 | + @TableId(value = "product_id", type = IdType.AUTO) | ||
| 32 | + private Long productId; | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 产品编码 | ||
| 36 | + */ | ||
| 37 | + private String productCode; | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * 产品名称 | ||
| 41 | + */ | ||
| 42 | + private String productName; | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 产品型号 | ||
| 46 | + */ | ||
| 47 | + private String productModel; | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * 产品类别 | ||
| 51 | + */ | ||
| 52 | + private String productType; | ||
| 53 | + | ||
| 54 | + /** | ||
| 55 | + * 存储容量 | ||
| 56 | + */ | ||
| 57 | + private String storageCapacity; | ||
| 58 | + | ||
| 59 | + /** | ||
| 60 | + * 产品颜色 | ||
| 61 | + */ | ||
| 62 | + private String color; | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * 产品图片URL | ||
| 66 | + */ | ||
| 67 | + private String productImgUrl; | ||
| 68 | + | ||
| 69 | + /** | ||
| 70 | + * 官方指导价 | ||
| 71 | + */ | ||
| 72 | + private BigDecimal officialPrice; | ||
| 73 | + | ||
| 74 | + /** | ||
| 75 | + * 销售状态(0-下架/1-在售/2-预售) | ||
| 76 | + */ | ||
| 77 | + private Integer saleStatus; | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * 是否参与返利(0-否/1-是) | ||
| 81 | + */ | ||
| 82 | + private Integer rebateFlag; | ||
| 83 | + | ||
| 84 | + /** | ||
| 85 | + * 销售起始日期 | ||
| 86 | + */ | ||
| 87 | + @JsonFormat(pattern = "yyyy-MM-dd") | ||
| 88 | + private LocalDate saleStartDate; | ||
| 89 | + | ||
| 90 | + /** | ||
| 91 | + * 销售终止日期 | ||
| 92 | + */ | ||
| 93 | + @JsonFormat(pattern = "yyyy-MM-dd") | ||
| 94 | + private LocalDate saleEndDate; | ||
| 95 | + | ||
| 96 | + /** | ||
| 97 | + * 产品备注 | ||
| 98 | + */ | ||
| 99 | + private String remark; | ||
| 100 | + | ||
| 101 | + /** | ||
| 102 | + * 创建者 | ||
| 103 | + */ | ||
| 104 | + private String createBy; | ||
| 105 | + | ||
| 106 | + /** | ||
| 107 | + * 创建时间 | ||
| 108 | + */ | ||
| 109 | + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") | ||
| 110 | + private LocalDateTime createTime; | ||
| 111 | + | ||
| 112 | + /** | ||
| 113 | + * 更新者 | ||
| 114 | + */ | ||
| 115 | + private String updateBy; | ||
| 116 | + | ||
| 117 | + /** | ||
| 118 | + * 更新时间 | ||
| 119 | + */ | ||
| 120 | + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") | ||
| 121 | + private LocalDateTime updateTime; | ||
| 122 | + | ||
| 123 | + /** | ||
| 124 | + * 删除标志(0代表存在 2代表删除) | ||
| 125 | + */ | ||
| 126 | + private String delFlag; | ||
| 127 | +} |
| 1 | +package com.apple.erp.mapper; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.entity.ProductInfo; | ||
| 4 | +import com.baomidou.mybatisplus.core.mapper.BaseMapper; | ||
| 5 | +import org.apache.ibatis.annotations.Mapper; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * 产品信息表 Mapper 接口 | ||
| 9 | + * | ||
| 10 | + * @author Apple ERP Team | ||
| 11 | + * @since 2025-01-27 | ||
| 12 | + */ | ||
| 13 | +@Mapper | ||
| 14 | +public interface ProductInfoMapper extends BaseMapper<ProductInfo> { | ||
| 15 | + | ||
| 16 | +} |
| 1 | +package com.apple.erp.service; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.DealerAddReq; | ||
| 4 | +import com.apple.erp.dto.DealerQueryReq; | ||
| 5 | +import com.apple.erp.dto.DealerUpdateReq; | ||
| 6 | +import com.apple.erp.entity.DealerInfo; | ||
| 7 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 8 | +import com.baomidou.mybatisplus.extension.service.IService; | ||
| 9 | + | ||
| 10 | +import java.util.List; | ||
| 11 | + | ||
| 12 | +public interface DealerInfoService extends IService<DealerInfo> { | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * 分页查询经销商列表 | ||
| 16 | + * @param queryReq 查询条件 | ||
| 17 | + * @return 经销商分页数据 | ||
| 18 | + */ | ||
| 19 | + IPage<DealerInfo> getDealerList(DealerQueryReq queryReq); | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 根据ID获取经销商详情 | ||
| 23 | + * @param dealerId 经销商ID | ||
| 24 | + * @return 经销商详情 | ||
| 25 | + */ | ||
| 26 | + DealerInfo getDealerDetail(Long dealerId); | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 新增经销商 | ||
| 30 | + * @param addReq 新增请求 | ||
| 31 | + * @return 是否成功 | ||
| 32 | + */ | ||
| 33 | + boolean addDealer(DealerAddReq addReq); | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 修改经销商 | ||
| 37 | + * @param updateReq 修改请求 | ||
| 38 | + * @return 是否成功 | ||
| 39 | + */ | ||
| 40 | + boolean updateDealer(DealerUpdateReq updateReq); | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 删除经销商 | ||
| 44 | + * @param dealerId 经销商ID | ||
| 45 | + * @return 是否成功 | ||
| 46 | + */ | ||
| 47 | + boolean deleteDealer(Long dealerId); | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * 批量删除经销商 | ||
| 51 | + * @param dealerIds 经销商ID列表 | ||
| 52 | + * @return 是否成功 | ||
| 53 | + */ | ||
| 54 | + boolean batchDeleteDealers(List<Long> dealerIds); | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * 修改经销商合作状态 | ||
| 58 | + * @param dealerId 经销商ID | ||
| 59 | + * @param cooperateStatus 合作状态 | ||
| 60 | + * @return 是否成功 | ||
| 61 | + */ | ||
| 62 | + boolean updateDealerCooperateStatus(Long dealerId, Integer cooperateStatus); | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * 修改经销商资质审核状态 | ||
| 66 | + * @param dealerId 经销商ID | ||
| 67 | + * @param qualificationAuditStatus 资质审核状态 | ||
| 68 | + * @param auditOpinion 审核意见 | ||
| 69 | + * @return 是否成功 | ||
| 70 | + */ | ||
| 71 | + boolean updateDealerAuditStatus(Long dealerId, Integer qualificationAuditStatus, String auditOpinion); | ||
| 72 | +} |
| 1 | +package com.apple.erp.service; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.ProductAddReq; | ||
| 4 | +import com.apple.erp.dto.ProductQueryReq; | ||
| 5 | +import com.apple.erp.dto.ProductUpdateReq; | ||
| 6 | +import com.apple.erp.entity.ProductInfo; | ||
| 7 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 8 | +import com.baomidou.mybatisplus.extension.service.IService; | ||
| 9 | + | ||
| 10 | +import java.util.List; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 产品信息表 服务类 | ||
| 14 | + * | ||
| 15 | + * @author Apple ERP Team | ||
| 16 | + * @since 2025-01-27 | ||
| 17 | + */ | ||
| 18 | +public interface ProductInfoService extends IService<ProductInfo> { | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 分页查询产品列表 | ||
| 22 | + * | ||
| 23 | + * @param queryReq 查询条件 | ||
| 24 | + * @return 产品分页列表 | ||
| 25 | + */ | ||
| 26 | + IPage<ProductInfo> getProductPage(ProductQueryReq queryReq); | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 根据产品ID获取产品详情 | ||
| 30 | + * | ||
| 31 | + * @param productId 产品ID | ||
| 32 | + * @return 产品详情 | ||
| 33 | + */ | ||
| 34 | + ProductInfo getProductById(Long productId); | ||
| 35 | + | ||
| 36 | + /** | ||
| 37 | + * 新增产品 | ||
| 38 | + * | ||
| 39 | + * @param addReq 新增请求 | ||
| 40 | + * @return 是否成功 | ||
| 41 | + */ | ||
| 42 | + boolean addProduct(ProductAddReq addReq); | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 修改产品 | ||
| 46 | + * | ||
| 47 | + * @param updateReq 修改请求 | ||
| 48 | + * @return 是否成功 | ||
| 49 | + */ | ||
| 50 | + boolean updateProduct(ProductUpdateReq updateReq); | ||
| 51 | + | ||
| 52 | + /** | ||
| 53 | + * 删除产品 | ||
| 54 | + * | ||
| 55 | + * @param productId 产品ID | ||
| 56 | + * @return 是否成功 | ||
| 57 | + */ | ||
| 58 | + boolean deleteProduct(Long productId); | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * 批量删除产品 | ||
| 62 | + * | ||
| 63 | + * @param productIds 产品ID列表 | ||
| 64 | + * @return 是否成功 | ||
| 65 | + */ | ||
| 66 | + boolean batchDeleteProducts(List<Long> productIds); | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * 修改产品状态 | ||
| 70 | + * | ||
| 71 | + * @param productId 产品ID | ||
| 72 | + * @param saleStatus 销售状态 | ||
| 73 | + * @return 是否成功 | ||
| 74 | + */ | ||
| 75 | + boolean updateProductStatus(Long productId, Integer saleStatus); | ||
| 76 | + | ||
| 77 | + /** | ||
| 78 | + * 修改返利标识 | ||
| 79 | + * | ||
| 80 | + * @param productId 产品ID | ||
| 81 | + * @param rebateFlag 返利标识 | ||
| 82 | + * @return 是否成功 | ||
| 83 | + */ | ||
| 84 | + boolean updateRebateFlag(Long productId, Integer rebateFlag); | ||
| 85 | + | ||
| 86 | + /** | ||
| 87 | + * 检查产品编码是否存在 | ||
| 88 | + * | ||
| 89 | + * @param productCode 产品编码 | ||
| 90 | + * @param productId 产品ID(修改时排除自己) | ||
| 91 | + * @return 是否存在 | ||
| 92 | + */ | ||
| 93 | + boolean checkProductCodeExists(String productCode, Long productId); | ||
| 94 | +} |
| 1 | +package com.apple.erp.service.impl; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.DealerAddReq; | ||
| 4 | +import com.apple.erp.dto.DealerQueryReq; | ||
| 5 | +import com.apple.erp.dto.DealerUpdateReq; | ||
| 6 | +import com.apple.erp.entity.DealerInfo; | ||
| 7 | +import com.apple.erp.mapper.DealerInfoMapper; | ||
| 8 | +import com.apple.erp.service.DealerInfoService; | ||
| 9 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||
| 10 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 11 | +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | ||
| 12 | +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | ||
| 13 | +import lombok.RequiredArgsConstructor; | ||
| 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 | + | ||
| 21 | +@Service | ||
| 22 | +@RequiredArgsConstructor | ||
| 23 | +public class DealerInfoServiceImpl extends ServiceImpl<DealerInfoMapper, DealerInfo> implements DealerInfoService { | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public IPage<DealerInfo> getDealerList(DealerQueryReq queryReq) { | ||
| 27 | + LambdaQueryWrapper<DealerInfo> queryWrapper = new LambdaQueryWrapper<>(); | ||
| 28 | + queryWrapper.eq(DealerInfo::getDelFlag, "0"); // 未删除 | ||
| 29 | + | ||
| 30 | + if (StringUtils.hasText(queryReq.getDealerCode())) { | ||
| 31 | + queryWrapper.like(DealerInfo::getDealerCode, queryReq.getDealerCode()); | ||
| 32 | + } | ||
| 33 | + if (StringUtils.hasText(queryReq.getDealerName())) { | ||
| 34 | + queryWrapper.like(DealerInfo::getDealerName, queryReq.getDealerName()); | ||
| 35 | + } | ||
| 36 | + if (StringUtils.hasText(queryReq.getCreditCode())) { | ||
| 37 | + queryWrapper.like(DealerInfo::getCreditCode, queryReq.getCreditCode()); | ||
| 38 | + } | ||
| 39 | + if (queryReq.getDealerLevel() != null) { | ||
| 40 | + queryWrapper.eq(DealerInfo::getDealerLevel, queryReq.getDealerLevel()); | ||
| 41 | + } | ||
| 42 | + if (StringUtils.hasText(queryReq.getRegion())) { | ||
| 43 | + queryWrapper.like(DealerInfo::getRegion, queryReq.getRegion()); | ||
| 44 | + } | ||
| 45 | + if (StringUtils.hasText(queryReq.getContactPerson())) { | ||
| 46 | + queryWrapper.like(DealerInfo::getContactPerson, queryReq.getContactPerson()); | ||
| 47 | + } | ||
| 48 | + if (StringUtils.hasText(queryReq.getContactPhone())) { | ||
| 49 | + queryWrapper.like(DealerInfo::getContactPhone, queryReq.getContactPhone()); | ||
| 50 | + } | ||
| 51 | + if (queryReq.getCooperateStatus() != null) { | ||
| 52 | + queryWrapper.eq(DealerInfo::getCooperateStatus, queryReq.getCooperateStatus()); | ||
| 53 | + } | ||
| 54 | + if (queryReq.getQualificationAuditStatus() != null) { | ||
| 55 | + queryWrapper.eq(DealerInfo::getQualificationAuditStatus, queryReq.getQualificationAuditStatus()); | ||
| 56 | + } | ||
| 57 | + if (queryReq.getCooperateStartDateStart() != null) { | ||
| 58 | + queryWrapper.ge(DealerInfo::getCooperateStartDate, queryReq.getCooperateStartDateStart()); | ||
| 59 | + } | ||
| 60 | + if (queryReq.getCooperateStartDateEnd() != null) { | ||
| 61 | + queryWrapper.le(DealerInfo::getCooperateStartDate, queryReq.getCooperateStartDateEnd()); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + queryWrapper.orderByDesc(DealerInfo::getCreateTime); | ||
| 65 | + | ||
| 66 | + Page<DealerInfo> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize()); | ||
| 67 | + return page(page, queryWrapper); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Override | ||
| 71 | + public DealerInfo getDealerDetail(Long dealerId) { | ||
| 72 | + return getOne(new LambdaQueryWrapper<DealerInfo>() | ||
| 73 | + .eq(DealerInfo::getDealerId, dealerId) | ||
| 74 | + .eq(DealerInfo::getDelFlag, "0")); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + @Override | ||
| 78 | + @Transactional | ||
| 79 | + public boolean addDealer(DealerAddReq addReq) { | ||
| 80 | + // 检查经销商编码是否重复 | ||
| 81 | + if (isDealerCodeDuplicated(addReq.getDealerCode(), null)) { | ||
| 82 | + throw new IllegalArgumentException("经销商编码已存在"); | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + // 检查统一社会信用代码是否重复 | ||
| 86 | + if (isCreditCodeDuplicated(addReq.getCreditCode(), null)) { | ||
| 87 | + throw new IllegalArgumentException("统一社会信用代码已存在"); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + DealerInfo dealerInfo = new DealerInfo(); | ||
| 91 | + dealerInfo.setDealerCode(addReq.getDealerCode()); | ||
| 92 | + dealerInfo.setDealerName(addReq.getDealerName()); | ||
| 93 | + dealerInfo.setCreditCode(addReq.getCreditCode()); | ||
| 94 | + dealerInfo.setDealerLevel(addReq.getDealerLevel()); | ||
| 95 | + dealerInfo.setRegion(addReq.getRegion()); | ||
| 96 | + dealerInfo.setContactPerson(addReq.getContactPerson()); | ||
| 97 | + dealerInfo.setContactPhone(addReq.getContactPhone()); | ||
| 98 | + dealerInfo.setCooperateStartDate(addReq.getCooperateStartDate()); | ||
| 99 | + dealerInfo.setCooperateStatus(addReq.getCooperateStatus()); | ||
| 100 | + dealerInfo.setBusinessLicenseUrl(addReq.getBusinessLicenseUrl()); | ||
| 101 | + dealerInfo.setCooperationAgreementUrl(addReq.getCooperationAgreementUrl()); | ||
| 102 | + dealerInfo.setQualificationAuditStatus(addReq.getQualificationAuditStatus()); | ||
| 103 | + dealerInfo.setAuditOpinion(addReq.getAuditOpinion()); | ||
| 104 | + | ||
| 105 | + dealerInfo.setCreateTime(LocalDateTime.now()); | ||
| 106 | + dealerInfo.setUpdateTime(LocalDateTime.now()); | ||
| 107 | + dealerInfo.setDelFlag("0"); // 默认未删除 | ||
| 108 | + // TODO: 设置createBy和updateBy | ||
| 109 | + return save(dealerInfo); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + @Override | ||
| 113 | + @Transactional | ||
| 114 | + public boolean updateDealer(DealerUpdateReq updateReq) { | ||
| 115 | + DealerInfo existingDealer = getById(updateReq.getDealerId()); | ||
| 116 | + if (existingDealer == null || "2".equals(existingDealer.getDelFlag())) { | ||
| 117 | + throw new IllegalArgumentException("经销商不存在或已被删除"); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + // 检查经销商编码是否重复 (排除自身) | ||
| 121 | + if (isDealerCodeDuplicated(updateReq.getDealerCode(), updateReq.getDealerId())) { | ||
| 122 | + throw new IllegalArgumentException("经销商编码已存在"); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + // 检查统一社会信用代码是否重复 (排除自身) | ||
| 126 | + if (isCreditCodeDuplicated(updateReq.getCreditCode(), updateReq.getDealerId())) { | ||
| 127 | + throw new IllegalArgumentException("统一社会信用代码已存在"); | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + DealerInfo dealerInfo = new DealerInfo(); | ||
| 131 | + dealerInfo.setDealerId(updateReq.getDealerId()); | ||
| 132 | + dealerInfo.setDealerCode(updateReq.getDealerCode()); | ||
| 133 | + dealerInfo.setDealerName(updateReq.getDealerName()); | ||
| 134 | + dealerInfo.setCreditCode(updateReq.getCreditCode()); | ||
| 135 | + dealerInfo.setDealerLevel(updateReq.getDealerLevel()); | ||
| 136 | + dealerInfo.setRegion(updateReq.getRegion()); | ||
| 137 | + dealerInfo.setContactPerson(updateReq.getContactPerson()); | ||
| 138 | + dealerInfo.setContactPhone(updateReq.getContactPhone()); | ||
| 139 | + dealerInfo.setCooperateStartDate(updateReq.getCooperateStartDate()); | ||
| 140 | + dealerInfo.setCooperateStatus(updateReq.getCooperateStatus()); | ||
| 141 | + dealerInfo.setBusinessLicenseUrl(updateReq.getBusinessLicenseUrl()); | ||
| 142 | + dealerInfo.setCooperationAgreementUrl(updateReq.getCooperationAgreementUrl()); | ||
| 143 | + dealerInfo.setQualificationAuditStatus(updateReq.getQualificationAuditStatus()); | ||
| 144 | + dealerInfo.setAuditOpinion(updateReq.getAuditOpinion()); | ||
| 145 | + | ||
| 146 | + dealerInfo.setUpdateTime(LocalDateTime.now()); | ||
| 147 | + // TODO: 设置updateBy | ||
| 148 | + return updateById(dealerInfo); | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + @Override | ||
| 152 | + @Transactional | ||
| 153 | + public boolean deleteDealer(Long dealerId) { | ||
| 154 | + DealerInfo dealerInfo = getById(dealerId); | ||
| 155 | + if (dealerInfo == null || "2".equals(dealerInfo.getDelFlag())) { | ||
| 156 | + throw new IllegalArgumentException("经销商不存在或已被删除"); | ||
| 157 | + } | ||
| 158 | + dealerInfo.setDelFlag("2"); // 软删除 | ||
| 159 | + dealerInfo.setUpdateTime(LocalDateTime.now()); | ||
| 160 | + // TODO: 设置updateBy | ||
| 161 | + return updateById(dealerInfo); | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + @Override | ||
| 165 | + @Transactional | ||
| 166 | + public boolean batchDeleteDealers(List<Long> dealerIds) { | ||
| 167 | + if (dealerIds == null || dealerIds.isEmpty()) { | ||
| 168 | + return false; | ||
| 169 | + } | ||
| 170 | + List<DealerInfo> dealersToUpdate = listByIds(dealerIds); | ||
| 171 | + dealersToUpdate.forEach(dealer -> { | ||
| 172 | + dealer.setDelFlag("2"); // 软删除 | ||
| 173 | + dealer.setUpdateTime(LocalDateTime.now()); | ||
| 174 | + // TODO: 设置updateBy | ||
| 175 | + }); | ||
| 176 | + return updateBatchById(dealersToUpdate); | ||
| 177 | + } | ||
| 178 | + | ||
| 179 | + @Override | ||
| 180 | + @Transactional | ||
| 181 | + public boolean updateDealerCooperateStatus(Long dealerId, Integer cooperateStatus) { | ||
| 182 | + DealerInfo dealerInfo = getById(dealerId); | ||
| 183 | + if (dealerInfo == null || "2".equals(dealerInfo.getDelFlag())) { | ||
| 184 | + throw new IllegalArgumentException("经销商不存在或已被删除"); | ||
| 185 | + } | ||
| 186 | + dealerInfo.setCooperateStatus(cooperateStatus); | ||
| 187 | + dealerInfo.setUpdateTime(LocalDateTime.now()); | ||
| 188 | + // TODO: 设置updateBy | ||
| 189 | + return updateById(dealerInfo); | ||
| 190 | + } | ||
| 191 | + | ||
| 192 | + @Override | ||
| 193 | + @Transactional | ||
| 194 | + public boolean updateDealerAuditStatus(Long dealerId, Integer qualificationAuditStatus, String auditOpinion) { | ||
| 195 | + DealerInfo dealerInfo = getById(dealerId); | ||
| 196 | + if (dealerInfo == null || "2".equals(dealerInfo.getDelFlag())) { | ||
| 197 | + throw new IllegalArgumentException("经销商不存在或已被删除"); | ||
| 198 | + } | ||
| 199 | + dealerInfo.setQualificationAuditStatus(qualificationAuditStatus); | ||
| 200 | + dealerInfo.setAuditOpinion(auditOpinion); | ||
| 201 | + dealerInfo.setUpdateTime(LocalDateTime.now()); | ||
| 202 | + // TODO: 设置updateBy | ||
| 203 | + return updateById(dealerInfo); | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + /** | ||
| 207 | + * 检查经销商编码是否重复 | ||
| 208 | + * @param dealerCode 经销商编码 | ||
| 209 | + * @param excludeDealerId 排除的经销商ID (用于更新操作) | ||
| 210 | + * @return 是否重复 | ||
| 211 | + */ | ||
| 212 | + private boolean isDealerCodeDuplicated(String dealerCode, Long excludeDealerId) { | ||
| 213 | + LambdaQueryWrapper<DealerInfo> queryWrapper = new LambdaQueryWrapper<>(); | ||
| 214 | + queryWrapper.eq(DealerInfo::getDealerCode, dealerCode); | ||
| 215 | + queryWrapper.eq(DealerInfo::getDelFlag, "0"); // 只检查未删除的经销商 | ||
| 216 | + if (excludeDealerId != null) { | ||
| 217 | + queryWrapper.ne(DealerInfo::getDealerId, excludeDealerId); | ||
| 218 | + } | ||
| 219 | + return count(queryWrapper) > 0; | ||
| 220 | + } | ||
| 221 | + | ||
| 222 | + /** | ||
| 223 | + * 检查统一社会信用代码是否重复 | ||
| 224 | + * @param creditCode 统一社会信用代码 | ||
| 225 | + * @param excludeDealerId 排除的经销商ID (用于更新操作) | ||
| 226 | + * @return 是否重复 | ||
| 227 | + */ | ||
| 228 | + private boolean isCreditCodeDuplicated(String creditCode, Long excludeDealerId) { | ||
| 229 | + LambdaQueryWrapper<DealerInfo> queryWrapper = new LambdaQueryWrapper<>(); | ||
| 230 | + queryWrapper.eq(DealerInfo::getCreditCode, creditCode); | ||
| 231 | + queryWrapper.eq(DealerInfo::getDelFlag, "0"); // 只检查未删除的经销商 | ||
| 232 | + if (excludeDealerId != null) { | ||
| 233 | + queryWrapper.ne(DealerInfo::getDealerId, excludeDealerId); | ||
| 234 | + } | ||
| 235 | + return count(queryWrapper) > 0; | ||
| 236 | + } | ||
| 237 | +} |
| 1 | +package com.apple.erp.service.impl; | ||
| 2 | + | ||
| 3 | +import com.apple.erp.dto.ProductAddReq; | ||
| 4 | +import com.apple.erp.dto.ProductQueryReq; | ||
| 5 | +import com.apple.erp.dto.ProductUpdateReq; | ||
| 6 | +import com.apple.erp.entity.ProductInfo; | ||
| 7 | +import com.apple.erp.mapper.ProductInfoMapper; | ||
| 8 | +import com.apple.erp.service.ProductInfoService; | ||
| 9 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||
| 10 | +import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 11 | +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | ||
| 12 | +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | ||
| 13 | +import org.springframework.beans.BeanUtils; | ||
| 14 | +import org.springframework.stereotype.Service; | ||
| 15 | +import org.springframework.util.StringUtils; | ||
| 16 | + | ||
| 17 | +import java.time.LocalDateTime; | ||
| 18 | +import java.util.List; | ||
| 19 | + | ||
| 20 | +/** | ||
| 21 | + * 产品信息表 服务实现类 | ||
| 22 | + * | ||
| 23 | + * @author Apple ERP Team | ||
| 24 | + * @since 2025-01-27 | ||
| 25 | + */ | ||
| 26 | +@Service | ||
| 27 | +public class ProductInfoServiceImpl extends ServiceImpl<ProductInfoMapper, ProductInfo> implements ProductInfoService { | ||
| 28 | + | ||
| 29 | + @Override | ||
| 30 | + public IPage<ProductInfo> getProductPage(ProductQueryReq queryReq) { | ||
| 31 | + Page<ProductInfo> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize()); | ||
| 32 | + LambdaQueryWrapper<ProductInfo> queryWrapper = getQueryWrapper(queryReq); | ||
| 33 | + return this.page(page, queryWrapper); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + @Override | ||
| 37 | + public ProductInfo getProductById(Long productId) { | ||
| 38 | + return this.getById(productId); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + @Override | ||
| 42 | + public boolean addProduct(ProductAddReq addReq) { | ||
| 43 | + // 检查产品编码是否已存在 | ||
| 44 | + if (checkProductCodeExists(addReq.getProductCode(), null)) { | ||
| 45 | + throw new RuntimeException("产品编码已存在"); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + ProductInfo productInfo = new ProductInfo(); | ||
| 49 | + BeanUtils.copyProperties(addReq, productInfo); | ||
| 50 | + productInfo.setCreateTime(LocalDateTime.now()); | ||
| 51 | + productInfo.setDelFlag("0"); | ||
| 52 | + return this.save(productInfo); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + @Override | ||
| 56 | + public boolean updateProduct(ProductUpdateReq updateReq) { | ||
| 57 | + // 检查产品编码是否已存在(排除自己) | ||
| 58 | + if (checkProductCodeExists(updateReq.getProductCode(), updateReq.getProductId())) { | ||
| 59 | + throw new RuntimeException("产品编码已存在"); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + ProductInfo productInfo = new ProductInfo(); | ||
| 63 | + BeanUtils.copyProperties(updateReq, productInfo); | ||
| 64 | + productInfo.setUpdateTime(LocalDateTime.now()); | ||
| 65 | + return this.updateById(productInfo); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + @Override | ||
| 69 | + public boolean deleteProduct(Long productId) { | ||
| 70 | + ProductInfo productInfo = new ProductInfo(); | ||
| 71 | + productInfo.setProductId(productId); | ||
| 72 | + productInfo.setDelFlag("2"); | ||
| 73 | + productInfo.setUpdateTime(LocalDateTime.now()); | ||
| 74 | + return this.updateById(productInfo); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + @Override | ||
| 78 | + public boolean batchDeleteProducts(List<Long> productIds) { | ||
| 79 | + for (Long productId : productIds) { | ||
| 80 | + deleteProduct(productId); | ||
| 81 | + } | ||
| 82 | + return true; | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + @Override | ||
| 86 | + public boolean updateProductStatus(Long productId, Integer saleStatus) { | ||
| 87 | + ProductInfo productInfo = new ProductInfo(); | ||
| 88 | + productInfo.setProductId(productId); | ||
| 89 | + productInfo.setSaleStatus(saleStatus); | ||
| 90 | + productInfo.setUpdateTime(LocalDateTime.now()); | ||
| 91 | + return this.updateById(productInfo); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + @Override | ||
| 95 | + public boolean updateRebateFlag(Long productId, Integer rebateFlag) { | ||
| 96 | + ProductInfo productInfo = new ProductInfo(); | ||
| 97 | + productInfo.setProductId(productId); | ||
| 98 | + productInfo.setRebateFlag(rebateFlag); | ||
| 99 | + productInfo.setUpdateTime(LocalDateTime.now()); | ||
| 100 | + return this.updateById(productInfo); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + @Override | ||
| 104 | + public boolean checkProductCodeExists(String productCode, Long productId) { | ||
| 105 | + LambdaQueryWrapper<ProductInfo> queryWrapper = new LambdaQueryWrapper<>(); | ||
| 106 | + queryWrapper.eq(ProductInfo::getProductCode, productCode) | ||
| 107 | + .eq(ProductInfo::getDelFlag, "0"); | ||
| 108 | + if (productId != null) { | ||
| 109 | + queryWrapper.ne(ProductInfo::getProductId, productId); | ||
| 110 | + } | ||
| 111 | + return this.count(queryWrapper) > 0; | ||
| 112 | + } | ||
| 113 | + | ||
| 114 | + /** | ||
| 115 | + * 构建查询条件 | ||
| 116 | + * | ||
| 117 | + * @param queryReq 查询请求 | ||
| 118 | + * @return 查询条件 | ||
| 119 | + */ | ||
| 120 | + private LambdaQueryWrapper<ProductInfo> getQueryWrapper(ProductQueryReq queryReq) { | ||
| 121 | + LambdaQueryWrapper<ProductInfo> queryWrapper = new LambdaQueryWrapper<>(); | ||
| 122 | + | ||
| 123 | + // 基础查询条件 | ||
| 124 | + queryWrapper.eq(ProductInfo::getDelFlag, "0"); | ||
| 125 | + | ||
| 126 | + // 产品编码 | ||
| 127 | + if (StringUtils.hasText(queryReq.getProductCode())) { | ||
| 128 | + queryWrapper.like(ProductInfo::getProductCode, queryReq.getProductCode()); | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + // 产品名称 | ||
| 132 | + if (StringUtils.hasText(queryReq.getProductName())) { | ||
| 133 | + queryWrapper.like(ProductInfo::getProductName, queryReq.getProductName()); | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + // 产品型号 | ||
| 137 | + if (StringUtils.hasText(queryReq.getProductModel())) { | ||
| 138 | + queryWrapper.like(ProductInfo::getProductModel, queryReq.getProductModel()); | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + // 产品类别 | ||
| 142 | + if (StringUtils.hasText(queryReq.getProductType())) { | ||
| 143 | + queryWrapper.eq(ProductInfo::getProductType, queryReq.getProductType()); | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + // 存储容量 | ||
| 147 | + if (StringUtils.hasText(queryReq.getStorageCapacity())) { | ||
| 148 | + queryWrapper.eq(ProductInfo::getStorageCapacity, queryReq.getStorageCapacity()); | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + // 产品颜色 | ||
| 152 | + if (StringUtils.hasText(queryReq.getColor())) { | ||
| 153 | + queryWrapper.eq(ProductInfo::getColor, queryReq.getColor()); | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + // 销售状态 | ||
| 157 | + if (queryReq.getSaleStatus() != null) { | ||
| 158 | + queryWrapper.eq(ProductInfo::getSaleStatus, queryReq.getSaleStatus()); | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + // 返利标识 | ||
| 162 | + if (queryReq.getRebateFlag() != null) { | ||
| 163 | + queryWrapper.eq(ProductInfo::getRebateFlag, queryReq.getRebateFlag()); | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + // 价格范围 | ||
| 167 | + if (queryReq.getMinPrice() != null) { | ||
| 168 | + queryWrapper.ge(ProductInfo::getOfficialPrice, queryReq.getMinPrice()); | ||
| 169 | + } | ||
| 170 | + if (queryReq.getMaxPrice() != null) { | ||
| 171 | + queryWrapper.le(ProductInfo::getOfficialPrice, queryReq.getMaxPrice()); | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + // 销售起始日期范围 | ||
| 175 | + if (queryReq.getSaleStartDateBegin() != null) { | ||
| 176 | + queryWrapper.ge(ProductInfo::getSaleStartDate, queryReq.getSaleStartDateBegin()); | ||
| 177 | + } | ||
| 178 | + if (queryReq.getSaleStartDateEnd() != null) { | ||
| 179 | + queryWrapper.le(ProductInfo::getSaleStartDate, queryReq.getSaleStartDateEnd()); | ||
| 180 | + } | ||
| 181 | + | ||
| 182 | + // 销售终止日期范围 | ||
| 183 | + if (queryReq.getSaleEndDateBegin() != null) { | ||
| 184 | + queryWrapper.ge(ProductInfo::getSaleEndDate, queryReq.getSaleEndDateBegin()); | ||
| 185 | + } | ||
| 186 | + if (queryReq.getSaleEndDateEnd() != null) { | ||
| 187 | + queryWrapper.le(ProductInfo::getSaleEndDate, queryReq.getSaleEndDateEnd()); | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + // 排序 | ||
| 191 | + queryWrapper.orderByDesc(ProductInfo::getCreateTime); | ||
| 192 | + | ||
| 193 | + return queryWrapper; | ||
| 194 | + } | ||
| 195 | +} |
| 1 | +-- Apple经销商ERP系统 - 业务数据库初始化脚本 | ||
| 2 | +-- 按顺序执行所有业务数据库脚本 | ||
| 3 | + | ||
| 4 | +-- 1. 创建业务表 | ||
| 5 | +SOURCE 01_create_business_tables.sql; | ||
| 6 | + | ||
| 7 | +-- 2. 创建业务索引 | ||
| 8 | +SOURCE 02_create_business_indexes.sql; | ||
| 9 | + | ||
| 10 | +-- 3. 插入业务测试数据 | ||
| 11 | +SOURCE 03_insert_business_data.sql; | ||
| 12 | + | ||
| 13 | +-- 4. 插入产品管理和经销商管理权限 | ||
| 14 | +SOURCE 06_insert_product_dealer_permissions.sql; | ||
| 15 | + | ||
| 16 | +-- 完成业务数据库初始化 | ||
| 17 | +SELECT 'Apple经销商ERP系统业务数据库初始化完成!' AS message; |
| 1 | +-- Apple经销商ERP系统 - 业务表创建脚本 | ||
| 2 | +-- 按照数据库设计文档创建订单管理、出库查询、发票管理等相关表 | ||
| 3 | + | ||
| 4 | +-- ============================================= | ||
| 5 | +-- 1. 订单主表(t_order_main) | ||
| 6 | +-- ============================================= | ||
| 7 | +CREATE TABLE t_order_main ( | ||
| 8 | + order_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '订单ID', | ||
| 9 | + order_no VARCHAR(30) NOT NULL COMMENT '订单编号', | ||
| 10 | + dealer_code VARCHAR(20) NOT NULL COMMENT '经销商编码', | ||
| 11 | + dealer_name VARCHAR(100) NOT NULL COMMENT '经销商名称', | ||
| 12 | + order_date DATETIME NOT NULL COMMENT '订单创建日期', | ||
| 13 | + total_amount DECIMAL(18,2) NOT NULL COMMENT '订单总金额', | ||
| 14 | + rebate_amount DECIMAL(18,2) DEFAULT 0 COMMENT '订单返利金额', | ||
| 15 | + delivery_status TINYINT DEFAULT 0 COMMENT '出库状态(0-未出库/1-已出库)', | ||
| 16 | + invoice_status TINYINT DEFAULT 0 COMMENT '开票状态(0-未开票/1-已开票)', | ||
| 17 | + rebate_calc_flag TINYINT DEFAULT 0 COMMENT '返利计算状态(0-未计算/1-已计算)', | ||
| 18 | + data_source VARCHAR(20) COMMENT '数据来源', | ||
| 19 | + verify_status TINYINT DEFAULT 0 COMMENT '数据验证状态(0-待验证/1-验证通过/2-验证失败)', | ||
| 20 | + upload_time DATETIME COMMENT '数据上传时间', | ||
| 21 | + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', | ||
| 22 | + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 23 | + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', | ||
| 24 | + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 25 | + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)' | ||
| 26 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单主表'; | ||
| 27 | + | ||
| 28 | +-- ============================================= | ||
| 29 | +-- 2. 订单商品明细表(t_order_item) | ||
| 30 | +-- ============================================= | ||
| 31 | +CREATE TABLE t_order_item ( | ||
| 32 | + item_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '订单商品明细ID', | ||
| 33 | + order_id BIGINT NOT NULL COMMENT '关联订单ID', | ||
| 34 | + order_no VARCHAR(30) NOT NULL COMMENT '订单编号', | ||
| 35 | + product_code VARCHAR(30) NOT NULL COMMENT '产品编码', | ||
| 36 | + product_name VARCHAR(50) NOT NULL COMMENT '产品名称', | ||
| 37 | + product_qty INT NOT NULL COMMENT '商品数量', | ||
| 38 | + unit_price DECIMAL(18,2) NOT NULL COMMENT '商品单价', | ||
| 39 | + item_amount DECIMAL(18,2) NOT NULL COMMENT '商品明细金额', | ||
| 40 | + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', | ||
| 41 | + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 42 | + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', | ||
| 43 | + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 44 | + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)' | ||
| 45 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单商品明细表'; | ||
| 46 | + | ||
| 47 | +-- ============================================= | ||
| 48 | +-- 3. 出库主表(t_delivery_main) | ||
| 49 | +-- ============================================= | ||
| 50 | +CREATE TABLE t_delivery_main ( | ||
| 51 | + delivery_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '出库单ID', | ||
| 52 | + delivery_no VARCHAR(30) NOT NULL COMMENT '出库单编号', | ||
| 53 | + dealer_code VARCHAR(20) NOT NULL COMMENT '经销商编码', | ||
| 54 | + dealer_name VARCHAR(100) NOT NULL COMMENT '经销商名称', | ||
| 55 | + order_no VARCHAR(30) NOT NULL COMMENT '关联订单编号', | ||
| 56 | + delivery_date DATETIME NOT NULL COMMENT '出库日期', | ||
| 57 | + delivery_status TINYINT DEFAULT 0 COMMENT '出库状态(0-未出库/1-已出库)', | ||
| 58 | + warehouse_code VARCHAR(20) COMMENT '出库仓库编码', | ||
| 59 | + data_source VARCHAR(20) COMMENT '数据来源', | ||
| 60 | + upload_time DATETIME COMMENT '数据上传时间', | ||
| 61 | + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', | ||
| 62 | + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 63 | + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', | ||
| 64 | + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 65 | + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)' | ||
| 66 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='出库主表'; | ||
| 67 | + | ||
| 68 | +-- ============================================= | ||
| 69 | +-- 4. 出库商品明细表(t_delivery_item) | ||
| 70 | +-- ============================================= | ||
| 71 | +CREATE TABLE t_delivery_item ( | ||
| 72 | + delivery_item_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '出库商品明细ID', | ||
| 73 | + delivery_id BIGINT NOT NULL COMMENT '关联出库单ID', | ||
| 74 | + delivery_no VARCHAR(30) NOT NULL COMMENT '出库单编号', | ||
| 75 | + order_no VARCHAR(30) NOT NULL COMMENT '关联订单编号', | ||
| 76 | + product_code VARCHAR(30) NOT NULL COMMENT '产品编码', | ||
| 77 | + product_name VARCHAR(50) NOT NULL COMMENT '产品名称', | ||
| 78 | + delivery_qty INT NOT NULL COMMENT '出库数量', | ||
| 79 | + delivery_price DECIMAL(18,2) NOT NULL COMMENT '出库单价', | ||
| 80 | + delivery_amount DECIMAL(18,2) NOT NULL COMMENT '出库明细金额', | ||
| 81 | + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', | ||
| 82 | + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 83 | + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', | ||
| 84 | + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 85 | + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)' | ||
| 86 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='出库商品明细表'; | ||
| 87 | + | ||
| 88 | +-- ============================================= | ||
| 89 | +-- 5. 发票主表(t_invoice_main) | ||
| 90 | +-- ============================================= | ||
| 91 | +CREATE TABLE t_invoice_main ( | ||
| 92 | + invoice_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '发票ID', | ||
| 93 | + invoice_no VARCHAR(30) NOT NULL COMMENT '发票编号', | ||
| 94 | + order_no VARCHAR(30) NOT NULL COMMENT '关联订单编号', | ||
| 95 | + delivery_no VARCHAR(30) COMMENT '关联出库单编号', | ||
| 96 | + dealer_code VARCHAR(20) NOT NULL COMMENT '经销商编码', | ||
| 97 | + dealer_name VARCHAR(100) NOT NULL COMMENT '经销商名称', | ||
| 98 | + total_amount DECIMAL(18,2) NOT NULL COMMENT '发票总金额(含税)', | ||
| 99 | + invoice_date DATE NOT NULL COMMENT '开票日期', | ||
| 100 | + invoice_status TINYINT DEFAULT 0 COMMENT '发票状态(0-未开票/1-已开票)', | ||
| 101 | + tax_rate DECIMAL(5,2) DEFAULT 13.00 COMMENT '税率', | ||
| 102 | + data_source VARCHAR(20) COMMENT '数据来源', | ||
| 103 | + upload_time DATETIME COMMENT '数据上传时间', | ||
| 104 | + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', | ||
| 105 | + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 106 | + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', | ||
| 107 | + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 108 | + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)' | ||
| 109 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='发票主表'; | ||
| 110 | + | ||
| 111 | +-- ============================================= | ||
| 112 | +-- 6. 发票商品明细表(t_invoice_item) | ||
| 113 | +-- ============================================= | ||
| 114 | +CREATE TABLE t_invoice_item ( | ||
| 115 | + invoice_item_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '发票商品明细ID', | ||
| 116 | + invoice_id BIGINT NOT NULL COMMENT '关联发票ID', | ||
| 117 | + invoice_no VARCHAR(30) NOT NULL COMMENT '发票编号', | ||
| 118 | + order_no VARCHAR(30) NOT NULL COMMENT '关联订单编号', | ||
| 119 | + product_code VARCHAR(30) NOT NULL COMMENT '产品编码', | ||
| 120 | + product_name VARCHAR(50) NOT NULL COMMENT '产品名称', | ||
| 121 | + invoice_qty INT NOT NULL COMMENT '开票数量', | ||
| 122 | + unit_price_no_tax DECIMAL(18,2) NOT NULL COMMENT '不含税单价', | ||
| 123 | + amount_no_tax DECIMAL(18,2) NOT NULL COMMENT '不含税明细金额', | ||
| 124 | + tax_amount DECIMAL(18,2) NOT NULL COMMENT '税额', | ||
| 125 | + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', | ||
| 126 | + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 127 | + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', | ||
| 128 | + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 129 | + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)' | ||
| 130 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='发票商品明细表'; | ||
| 131 | + | ||
| 132 | +-- ============================================= | ||
| 133 | +-- 7. 返利台账明细表(t_rebate_detail) | ||
| 134 | +-- ============================================= | ||
| 135 | +CREATE TABLE t_rebate_detail ( | ||
| 136 | + rebate_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '返利明细ID', | ||
| 137 | + rebate_no VARCHAR(30) NOT NULL COMMENT '返利明细编号', | ||
| 138 | + order_no VARCHAR(30) NOT NULL COMMENT '关联订单编号', | ||
| 139 | + dealer_code VARCHAR(20) NOT NULL COMMENT '经销商编码', | ||
| 140 | + dealer_name VARCHAR(100) NOT NULL COMMENT '经销商名称', | ||
| 141 | + product_code VARCHAR(30) NOT NULL COMMENT '产品编码', | ||
| 142 | + rebate_amount DECIMAL(18,2) NOT NULL COMMENT '返利金额', | ||
| 143 | + rebate_date DATE NOT NULL COMMENT '返利记录日期', | ||
| 144 | + operate_type TINYINT NOT NULL COMMENT '操作类型(1-新增/2-扣减)', | ||
| 145 | + calc_flag TINYINT DEFAULT 0 COMMENT '返利计算状态(0-未计算/1-已计算)', | ||
| 146 | + upload_time DATETIME COMMENT '数据上传时间', | ||
| 147 | + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', | ||
| 148 | + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 149 | + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', | ||
| 150 | + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 151 | + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)' | ||
| 152 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='返利台账明细表'; | ||
| 153 | + | ||
| 154 | +-- ============================================= | ||
| 155 | +-- 8. 异常工单表(t_exception_workorder) | ||
| 156 | +-- ============================================= | ||
| 157 | +CREATE TABLE t_exception_workorder ( | ||
| 158 | + workorder_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '工单ID', | ||
| 159 | + workorder_no VARCHAR(30) NOT NULL COMMENT '工单编号', | ||
| 160 | + order_no VARCHAR(30) COMMENT '关联订单编号', | ||
| 161 | + dealer_code VARCHAR(20) COMMENT '经销商编码', | ||
| 162 | + dealer_name VARCHAR(100) COMMENT '经销商名称', | ||
| 163 | + exception_type TINYINT NOT NULL COMMENT '异常类型(1-逻辑验证异常/2-源头验证异常/3-交叉验证异常)', | ||
| 164 | + severity_level TINYINT NOT NULL COMMENT '严重程度(1-高/2-中/3-低)', | ||
| 165 | + workorder_status TINYINT DEFAULT 1 COMMENT '工单状态(1-待处理/2-处理中/3-已解决/4-已关闭)', | ||
| 166 | + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '工单创建时间', | ||
| 167 | + expect_complete_time DATETIME COMMENT '预计处理完成时间', | ||
| 168 | + handler_user VARCHAR(20) COMMENT '处理人', | ||
| 169 | + exception_desc VARCHAR(500) COMMENT '异常描述', | ||
| 170 | + handle_suggest VARCHAR(500) COMMENT '处理建议', | ||
| 171 | + data_source VARCHAR(20) COMMENT '数据来源', | ||
| 172 | + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', | ||
| 173 | + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', | ||
| 174 | + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 175 | + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)' | ||
| 176 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='异常工单表'; | ||
| 177 | + | ||
| 178 | +-- ============================================= | ||
| 179 | +-- 9. 异常工单处理日志表(t_exception_workorder_log) | ||
| 180 | +-- ============================================= | ||
| 181 | +CREATE TABLE t_exception_workorder_log ( | ||
| 182 | + log_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '日志ID', | ||
| 183 | + workorder_id BIGINT NOT NULL COMMENT '关联工单ID', | ||
| 184 | + workorder_no VARCHAR(30) NOT NULL COMMENT '关联工单编号', | ||
| 185 | + handle_user VARCHAR(20) NOT NULL COMMENT '处理人', | ||
| 186 | + handle_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '处理时间', | ||
| 187 | + before_status TINYINT NOT NULL COMMENT '处理前状态', | ||
| 188 | + after_status TINYINT NOT NULL COMMENT '处理后状态', | ||
| 189 | + handle_opinion VARCHAR(500) COMMENT '处理意见', | ||
| 190 | + attach_url VARCHAR(200) COMMENT '附件URL', | ||
| 191 | + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', | ||
| 192 | + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 193 | + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', | ||
| 194 | + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 195 | + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)' | ||
| 196 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='异常工单处理日志表'; | ||
| 197 | + | ||
| 198 | +-- ============================================= | ||
| 199 | +-- 10. 经销商信息表(t_dealer_info) | ||
| 200 | +-- ============================================= | ||
| 201 | +CREATE TABLE t_dealer_info ( | ||
| 202 | + dealer_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '经销商ID', | ||
| 203 | + dealer_code VARCHAR(20) NOT NULL UNIQUE COMMENT '经销商编码', | ||
| 204 | + dealer_name VARCHAR(100) NOT NULL COMMENT '经销商名称', | ||
| 205 | + credit_code VARCHAR(18) NOT NULL UNIQUE COMMENT '统一社会信用代码', | ||
| 206 | + dealer_level TINYINT NOT NULL COMMENT '经销商等级(1-一级经销商/2-二级经销商)', | ||
| 207 | + region VARCHAR(50) NOT NULL COMMENT '所在区域', | ||
| 208 | + contact_person VARCHAR(50) COMMENT '联系人', | ||
| 209 | + contact_phone VARCHAR(20) COMMENT '联系电话', | ||
| 210 | + cooperate_start_date DATE NOT NULL COMMENT '合作起始日期', | ||
| 211 | + cooperate_status TINYINT DEFAULT 1 COMMENT '合作状态(1-正常合作/2-暂停合作/3-终止合作)', | ||
| 212 | + business_license_url VARCHAR(200) COMMENT '营业执照URL', | ||
| 213 | + cooperation_agreement_url VARCHAR(200) COMMENT '合作协议URL', | ||
| 214 | + qualification_audit_status TINYINT DEFAULT 1 COMMENT '资质审核状态(1-待审核/2-审核通过/3-审核不通过)', | ||
| 215 | + audit_opinion VARCHAR(500) COMMENT '审核意见', | ||
| 216 | + total_rebate_amount DECIMAL(18,2) DEFAULT 0 COMMENT '累计应返金额', | ||
| 217 | + used_rebate_amount DECIMAL(18,2) DEFAULT 0 COMMENT '累计已返金额', | ||
| 218 | + pending_rebate_amount DECIMAL(18,2) DEFAULT 0 COMMENT '待返利总金额', | ||
| 219 | + last_rebate_update_time DATETIME COMMENT '最后返利更新时间', | ||
| 220 | + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', | ||
| 221 | + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 222 | + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', | ||
| 223 | + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 224 | + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)' | ||
| 225 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='经销商信息表'; | ||
| 226 | + | ||
| 227 | +-- ============================================= | ||
| 228 | +-- 11. 产品信息表(t_product_info) | ||
| 229 | +-- ============================================= | ||
| 230 | +CREATE TABLE t_product_info ( | ||
| 231 | + product_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '产品ID', | ||
| 232 | + product_code VARCHAR(30) NOT NULL UNIQUE COMMENT '产品编码', | ||
| 233 | + product_name VARCHAR(50) NOT NULL COMMENT '产品名称', | ||
| 234 | + product_model VARCHAR(20) NOT NULL COMMENT '产品型号', | ||
| 235 | + product_type VARCHAR(20) NOT NULL COMMENT '产品类别', | ||
| 236 | + storage_capacity VARCHAR(10) COMMENT '存储容量', | ||
| 237 | + color VARCHAR(10) COMMENT '产品颜色', | ||
| 238 | + product_img_url VARCHAR(200) COMMENT '产品图片URL', | ||
| 239 | + official_price DECIMAL(18,2) COMMENT '官方指导价', | ||
| 240 | + sale_status TINYINT DEFAULT 1 COMMENT '销售状态(0-下架/1-在售/2-预售)', | ||
| 241 | + rebate_flag TINYINT DEFAULT 1 COMMENT '是否参与返利(0-否/1-是)', | ||
| 242 | + sale_start_date DATE COMMENT '销售起始日期', | ||
| 243 | + sale_end_date DATE COMMENT '销售终止日期', | ||
| 244 | + remark VARCHAR(500) COMMENT '产品备注', | ||
| 245 | + create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', | ||
| 246 | + create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 247 | + update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', | ||
| 248 | + update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 249 | + del_flag CHAR(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)' | ||
| 250 | +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品信息表'; |
| 1 | + |
| 1 | +-- Apple经销商ERP系统 - 业务表索引创建脚本 | ||
| 2 | +-- 按照数据库设计文档创建索引 | ||
| 3 | + | ||
| 4 | +-- ============================================= | ||
| 5 | +-- 订单主表索引 | ||
| 6 | +-- ============================================= | ||
| 7 | + | ||
| 8 | +-- 业务唯一索引 | ||
| 9 | +CREATE UNIQUE INDEX uk_order_no ON t_order_main(order_no); | ||
| 10 | + | ||
| 11 | +-- 查询优化索引 | ||
| 12 | +CREATE INDEX idx_order_dealer_code ON t_order_main(dealer_code); | ||
| 13 | +CREATE INDEX idx_order_date ON t_order_main(order_date); | ||
| 14 | +CREATE INDEX idx_order_status ON t_order_main(delivery_status, invoice_status); | ||
| 15 | +CREATE INDEX idx_order_verify ON t_order_main(verify_status); | ||
| 16 | +CREATE INDEX idx_order_upload_time ON t_order_main(upload_time); | ||
| 17 | + | ||
| 18 | +-- ============================================= | ||
| 19 | +-- 订单商品明细表索引 | ||
| 20 | +-- ============================================= | ||
| 21 | + | ||
| 22 | +-- 外键索引 | ||
| 23 | +CREATE INDEX idx_item_order_id ON t_order_item(order_id); | ||
| 24 | +CREATE INDEX idx_item_order_no ON t_order_item(order_no); | ||
| 25 | +CREATE INDEX idx_item_product_code ON t_order_item(product_code); | ||
| 26 | + | ||
| 27 | +-- ============================================= | ||
| 28 | +-- 出库主表索引 | ||
| 29 | +-- ============================================= | ||
| 30 | + | ||
| 31 | +-- 业务唯一索引 | ||
| 32 | +CREATE UNIQUE INDEX uk_delivery_no ON t_delivery_main(delivery_no); | ||
| 33 | + | ||
| 34 | +-- 查询优化索引 | ||
| 35 | +CREATE INDEX idx_delivery_dealer_code ON t_delivery_main(dealer_code); | ||
| 36 | +CREATE INDEX idx_delivery_order_no ON t_delivery_main(order_no); | ||
| 37 | +CREATE INDEX idx_delivery_date ON t_delivery_main(delivery_date); | ||
| 38 | +CREATE INDEX idx_delivery_status ON t_delivery_main(delivery_status); | ||
| 39 | + | ||
| 40 | +-- ============================================= | ||
| 41 | +-- 出库商品明细表索引 | ||
| 42 | +-- ============================================= | ||
| 43 | + | ||
| 44 | +-- 外键索引 | ||
| 45 | +CREATE INDEX idx_delivery_item_delivery_id ON t_delivery_item(delivery_id); | ||
| 46 | +CREATE INDEX idx_delivery_item_delivery_no ON t_delivery_item(delivery_no); | ||
| 47 | +CREATE INDEX idx_delivery_item_order_no ON t_delivery_item(order_no); | ||
| 48 | +CREATE INDEX idx_delivery_item_product_code ON t_delivery_item(product_code); | ||
| 49 | + | ||
| 50 | +-- ============================================= | ||
| 51 | +-- 发票主表索引 | ||
| 52 | +-- ============================================= | ||
| 53 | + | ||
| 54 | +-- 业务唯一索引 | ||
| 55 | +CREATE UNIQUE INDEX uk_invoice_no ON t_invoice_main(invoice_no); | ||
| 56 | + | ||
| 57 | +-- 查询优化索引 | ||
| 58 | +CREATE INDEX idx_invoice_dealer_code ON t_invoice_main(dealer_code); | ||
| 59 | +CREATE INDEX idx_invoice_order_no ON t_invoice_main(order_no); | ||
| 60 | +CREATE INDEX idx_invoice_delivery_no ON t_invoice_main(delivery_no); | ||
| 61 | +CREATE INDEX idx_invoice_date ON t_invoice_main(invoice_date); | ||
| 62 | +CREATE INDEX idx_invoice_status ON t_invoice_main(invoice_status); | ||
| 63 | + | ||
| 64 | +-- ============================================= | ||
| 65 | +-- 发票商品明细表索引 | ||
| 66 | +-- ============================================= | ||
| 67 | + | ||
| 68 | +-- 外键索引 | ||
| 69 | +CREATE INDEX idx_invoice_item_invoice_id ON t_invoice_item(invoice_id); | ||
| 70 | +CREATE INDEX idx_invoice_item_invoice_no ON t_invoice_item(invoice_no); | ||
| 71 | +CREATE INDEX idx_invoice_item_order_no ON t_invoice_item(order_no); | ||
| 72 | +CREATE INDEX idx_invoice_item_product_code ON t_invoice_item(product_code); | ||
| 73 | + | ||
| 74 | +-- ============================================= | ||
| 75 | +-- 返利台账明细表索引 | ||
| 76 | +-- ============================================= | ||
| 77 | + | ||
| 78 | +-- 业务唯一索引 | ||
| 79 | +CREATE UNIQUE INDEX uk_rebate_no ON t_rebate_detail(rebate_no); | ||
| 80 | + | ||
| 81 | +-- 查询优化索引 | ||
| 82 | +CREATE INDEX idx_rebate_dealer_code ON t_rebate_detail(dealer_code); | ||
| 83 | +CREATE INDEX idx_rebate_order_no ON t_rebate_detail(order_no); | ||
| 84 | +CREATE INDEX idx_rebate_product_code ON t_rebate_detail(product_code); | ||
| 85 | +CREATE INDEX idx_rebate_date ON t_rebate_detail(rebate_date); | ||
| 86 | +CREATE INDEX idx_rebate_operate_type ON t_rebate_detail(operate_type); | ||
| 87 | +CREATE INDEX idx_rebate_calc_flag ON t_rebate_detail(calc_flag); | ||
| 88 | + | ||
| 89 | +-- ============================================= | ||
| 90 | +-- 异常工单表索引 | ||
| 91 | +-- ============================================= | ||
| 92 | + | ||
| 93 | +-- 业务唯一索引 | ||
| 94 | +CREATE UNIQUE INDEX uk_workorder_no ON t_exception_workorder(workorder_no); | ||
| 95 | + | ||
| 96 | +-- 查询优化索引 | ||
| 97 | +CREATE INDEX idx_workorder_dealer_code ON t_exception_workorder(dealer_code); | ||
| 98 | +CREATE INDEX idx_workorder_order_no ON t_exception_workorder(order_no); | ||
| 99 | +CREATE INDEX idx_workorder_exception_type ON t_exception_workorder(exception_type); | ||
| 100 | +CREATE INDEX idx_workorder_severity_level ON t_exception_workorder(severity_level); | ||
| 101 | +CREATE INDEX idx_workorder_status ON t_exception_workorder(workorder_status); | ||
| 102 | +CREATE INDEX idx_workorder_create_time ON t_exception_workorder(create_time); | ||
| 103 | +CREATE INDEX idx_workorder_handler_user ON t_exception_workorder(handler_user); | ||
| 104 | + | ||
| 105 | +-- ============================================= | ||
| 106 | +-- 异常工单处理日志表索引 | ||
| 107 | +-- ============================================= | ||
| 108 | + | ||
| 109 | +-- 外键索引 | ||
| 110 | +CREATE INDEX idx_workorder_log_workorder_id ON t_exception_workorder_log(workorder_id); | ||
| 111 | +CREATE INDEX idx_workorder_log_workorder_no ON t_exception_workorder_log(workorder_no); | ||
| 112 | +CREATE INDEX idx_workorder_log_handle_user ON t_exception_workorder_log(handle_user); | ||
| 113 | +CREATE INDEX idx_workorder_log_handle_time ON t_exception_workorder_log(handle_time); | ||
| 114 | + | ||
| 115 | +-- ============================================= | ||
| 116 | +-- 经销商信息表索引 | ||
| 117 | +-- ============================================= | ||
| 118 | + | ||
| 119 | +-- 业务唯一索引(已在表定义中创建) | ||
| 120 | +-- UNIQUE INDEX uk_dealer_code ON t_dealer_info(dealer_code); | ||
| 121 | +-- UNIQUE INDEX uk_credit_code ON t_dealer_info(credit_code); | ||
| 122 | + | ||
| 123 | +-- 查询优化索引 | ||
| 124 | +CREATE INDEX idx_dealer_name ON t_dealer_info(dealer_name); | ||
| 125 | +CREATE INDEX idx_dealer_level ON t_dealer_info(dealer_level); | ||
| 126 | +CREATE INDEX idx_dealer_region ON t_dealer_info(region); | ||
| 127 | +CREATE INDEX idx_dealer_cooperate_status ON t_dealer_info(cooperate_status); | ||
| 128 | +CREATE INDEX idx_dealer_qualification_audit_status ON t_dealer_info(qualification_audit_status); | ||
| 129 | +CREATE INDEX idx_dealer_cooperate_start_date ON t_dealer_info(cooperate_start_date); | ||
| 130 | + | ||
| 131 | +-- ============================================= | ||
| 132 | +-- 产品信息表索引 | ||
| 133 | +-- ============================================= | ||
| 134 | + | ||
| 135 | +-- 业务唯一索引(已在表定义中创建) | ||
| 136 | +-- UNIQUE INDEX uk_product_code ON t_product_info(product_code); | ||
| 137 | + | ||
| 138 | +-- 查询优化索引 | ||
| 139 | +CREATE INDEX idx_product_name ON t_product_info(product_name); | ||
| 140 | +CREATE INDEX idx_product_model ON t_product_info(product_model); | ||
| 141 | +CREATE INDEX idx_product_type ON t_product_info(product_type); | ||
| 142 | +CREATE INDEX idx_product_sale_status ON t_product_info(sale_status); | ||
| 143 | +CREATE INDEX idx_product_rebate_flag ON t_product_info(rebate_flag); | ||
| 144 | +CREATE INDEX idx_product_sale_start_date ON t_product_info(sale_start_date); | ||
| 145 | +CREATE INDEX idx_product_sale_end_date ON t_product_info(sale_end_date); | ||
| 146 | + | ||
| 147 | +-- ============================================= | ||
| 148 | +-- 复合索引优化查询性能 | ||
| 149 | +-- ============================================= | ||
| 150 | + | ||
| 151 | +-- 订单查询优化 | ||
| 152 | +CREATE INDEX idx_order_complex_query ON t_order_main(dealer_code, order_date, delivery_status, invoice_status); | ||
| 153 | + | ||
| 154 | +-- 出库查询优化 | ||
| 155 | +CREATE INDEX idx_delivery_complex_query ON t_delivery_main(dealer_code, delivery_date, delivery_status); | ||
| 156 | + | ||
| 157 | +-- 发票查询优化 | ||
| 158 | +CREATE INDEX idx_invoice_complex_query ON t_invoice_main(dealer_code, invoice_date, invoice_status); | ||
| 159 | + | ||
| 160 | +-- 返利查询优化 | ||
| 161 | +CREATE INDEX idx_rebate_complex_query ON t_rebate_detail(dealer_code, rebate_date, operate_type); | ||
| 162 | + | ||
| 163 | +-- 异常工单查询优化 | ||
| 164 | +CREATE INDEX idx_workorder_complex_query ON t_exception_workorder(workorder_status, severity_level, create_time); | ||
| 165 | + | ||
| 166 | +-- 经销商查询优化 | ||
| 167 | +CREATE INDEX idx_dealer_complex_query ON t_dealer_info(dealer_level, cooperate_status, region); | ||
| 168 | + | ||
| 169 | +-- 产品查询优化 | ||
| 170 | +CREATE INDEX idx_product_complex_query ON t_product_info(product_type, sale_status, rebate_flag); |
database/scripts/03_insert_business_data.sql
0 → 100644
| 1 | +-- Apple经销商ERP系统 - 业务测试数据插入脚本 | ||
| 2 | +-- 按照数据库设计文档插入测试数据 | ||
| 3 | + | ||
| 4 | +-- ============================================= | ||
| 5 | +-- 1. 经销商信息数据 | ||
| 6 | +-- ============================================= | ||
| 7 | +INSERT INTO t_dealer_info (dealer_id, dealer_code, dealer_name, credit_code, dealer_level, region, contact_person, contact_phone, cooperate_start_date, cooperate_status, qualification_audit_status, total_rebate_amount, used_rebate_amount, pending_rebate_amount, create_by, create_time) VALUES | ||
| 8 | +(1, 'APL-DLR-001', '北京苹果科技有限公司', '91110000123456789X', 1, '华北地区', '张三', '010-12345678', '2020-01-01', 1, 2, 50000.00, 30000.00, 20000.00, 'admin', NOW()), | ||
| 9 | +(2, 'APL-DLR-002', '上海苹果贸易有限公司', '91310000987654321Y', 1, '华东地区', '李四', '021-87654321', '2020-03-15', 1, 2, 80000.00, 50000.00, 30000.00, 'admin', NOW()), | ||
| 10 | +(3, 'APL-DLR-003', '深圳苹果电子有限公司', '91440300112233445Z', 2, '华南地区', '王五', '0755-11223344', '2021-06-01', 1, 2, 30000.00, 15000.00, 15000.00, 'admin', NOW()), | ||
| 11 | +(4, 'APL-DLR-004', '成都苹果数码有限公司', '91510100556677889A', 2, '西南地区', '赵六', '028-55667788', '2021-09-01', 1, 2, 25000.00, 10000.00, 15000.00, 'admin', NOW()), | ||
| 12 | +(5, 'APL-DLR-005', '西安苹果科技发展有限公司', '91610100998877665B', 2, '西北地区', '孙七', '029-99887766', '2022-01-01', 1, 2, 20000.00, 8000.00, 12000.00, 'admin', NOW()); | ||
| 13 | + | ||
| 14 | +-- ============================================= | ||
| 15 | +-- 2. 产品信息数据 | ||
| 16 | +-- ============================================= | ||
| 17 | +INSERT INTO t_product_info (product_id, product_code, product_name, product_model, product_type, storage_capacity, color, official_price, sale_status, rebate_flag, sale_start_date, create_by, create_time) VALUES | ||
| 18 | +(1, 'APL-IP15-128G-BK', 'iPhone 15', 'A2848', 'iPhone', '128GB', '黑色', 5999.00, 1, 1, '2023-09-15', 'admin', NOW()), | ||
| 19 | +(2, 'APL-IP15-256G-BK', 'iPhone 15', 'A2848', 'iPhone', '256GB', '黑色', 6999.00, 1, 1, '2023-09-15', 'admin', NOW()), | ||
| 20 | +(3, 'APL-IP15-128G-BL', 'iPhone 15', 'A2848', 'iPhone', '128GB', '蓝色', 5999.00, 1, 1, '2023-09-15', 'admin', NOW()), | ||
| 21 | +(4, 'APL-IP15-256G-BL', 'iPhone 15', 'A2848', 'iPhone', '256GB', '蓝色', 6999.00, 1, 1, '2023-09-15', 'admin', NOW()), | ||
| 22 | +(5, 'APL-IP15-128G-PK', 'iPhone 15', 'A2848', 'iPhone', '128GB', '粉色', 5999.00, 1, 1, '2023-09-15', 'admin', NOW()), | ||
| 23 | +(6, 'APL-IP15-256G-PK', 'iPhone 15', 'A2848', 'iPhone', '256GB', '粉色', 6999.00, 1, 1, '2023-09-15', 'admin', NOW()), | ||
| 24 | +(7, 'APL-IP15P-128G-BK', 'iPhone 15 Pro', 'A2849', 'iPhone', '128GB', '黑色', 7999.00, 1, 1, '2023-09-15', 'admin', NOW()), | ||
| 25 | +(8, 'APL-IP15P-256G-BK', 'iPhone 15 Pro', 'A2849', 'iPhone', '256GB', '黑色', 8999.00, 1, 1, '2023-09-15', 'admin', NOW()), | ||
| 26 | +(9, 'APL-IP15P-512G-BK', 'iPhone 15 Pro', 'A2849', 'iPhone', '512GB', '黑色', 10999.00, 1, 1, '2023-09-15', 'admin', NOW()), | ||
| 27 | +(10, 'APL-IP15P-1T-BK', 'iPhone 15 Pro', 'A2849', 'iPhone', '1TB', '黑色', 12999.00, 1, 1, '2023-09-15', 'admin', NOW()), | ||
| 28 | +(11, 'APL-IPAD-AIR-64G-WIFI', 'iPad Air', 'A2588', 'iPad', '64GB', '银色', 4399.00, 1, 1, '2022-03-08', 'admin', NOW()), | ||
| 29 | +(12, 'APL-IPAD-AIR-256G-WIFI', 'iPad Air', 'A2588', 'iPad', '256GB', '银色', 5499.00, 1, 1, '2022-03-08', 'admin', NOW()), | ||
| 30 | +(13, 'APL-MAC-AIR-M2-8-256', 'MacBook Air', 'A2337', 'MacBook', '256GB', '深空灰色', 8999.00, 1, 1, '2022-07-15', 'admin', NOW()), | ||
| 31 | +(14, 'APL-MAC-AIR-M2-8-512', 'MacBook Air', 'A2337', 'MacBook', '512GB', '深空灰色', 10499.00, 1, 1, '2022-07-15', 'admin', NOW()), | ||
| 32 | +(15, 'APL-WATCH-SE-44-GPS', 'Apple Watch SE', 'A2351', 'Apple Watch', '44mm', '银色', 1999.00, 1, 1, '2022-09-16', 'admin', NOW()); | ||
| 33 | + | ||
| 34 | +-- ============================================= | ||
| 35 | +-- 3. 订单主表数据 | ||
| 36 | +-- ============================================= | ||
| 37 | +INSERT INTO t_order_main (order_id, order_no, dealer_code, dealer_name, order_date, total_amount, rebate_amount, delivery_status, invoice_status, rebate_calc_flag, data_source, verify_status, upload_time, create_by, create_time) VALUES | ||
| 38 | +(1, 'ORD-2024-001', 'APL-DLR-001', '北京苹果科技有限公司', '2024-01-15 10:30:00', 119980.00, 5999.00, 1, 1, 1, 'ERP系统', 1, '2024-01-15 10:35:00', 'admin', NOW()), | ||
| 39 | +(2, 'ORD-2024-002', 'APL-DLR-002', '上海苹果贸易有限公司', '2024-01-16 14:20:00', 179980.00, 8999.00, 1, 1, 1, 'ERP系统', 1, '2024-01-16 14:25:00', 'admin', NOW()), | ||
| 40 | +(3, 'ORD-2024-003', 'APL-DLR-001', '北京苹果科技有限公司', '2024-01-17 09:15:00', 43990.00, 2199.50, 1, 1, 1, 'ERP系统', 1, '2024-01-17 09:20:00', 'admin', NOW()), | ||
| 41 | +(4, 'ORD-2024-004', 'APL-DLR-003', '深圳苹果电子有限公司', '2024-01-18 16:45:00', 89990.00, 4499.50, 1, 1, 1, 'ERP系统', 1, '2024-01-18 16:50:00', 'admin', NOW()), | ||
| 42 | +(5, 'ORD-2024-005', 'APL-DLR-004', '成都苹果数码有限公司', '2024-01-19 11:30:00', 19990.00, 999.50, 1, 1, 1, 'ERP系统', 1, '2024-01-19 11:35:00', 'admin', NOW()), | ||
| 43 | +(6, 'ORD-2024-006', 'APL-DLR-002', '上海苹果贸易有限公司', '2024-01-20 13:20:00', 89990.00, 4499.50, 0, 0, 0, 'ERP系统', 0, '2024-01-20 13:25:00', 'admin', NOW()), | ||
| 44 | +(7, 'ORD-2024-007', 'APL-DLR-005', '西安苹果科技发展有限公司', '2024-01-21 15:10:00', 104990.00, 5249.50, 1, 1, 1, 'ERP系统', 1, '2024-01-21 15:15:00', 'admin', NOW()), | ||
| 45 | +(8, 'ORD-2024-008', 'APL-DLR-001', '北京苹果科技有限公司', '2024-01-22 10:45:00', 39990.00, 1999.50, 1, 1, 1, 'ERP系统', 1, '2024-01-22 10:50:00', 'admin', NOW()), | ||
| 46 | +(9, 'ORD-2024-009', 'APL-DLR-003', '深圳苹果电子有限公司', '2024-01-23 14:30:00', 269970.00, 13498.50, 1, 1, 1, 'ERP系统', 1, '2024-01-23 14:35:00', 'admin', NOW()), | ||
| 47 | +(10, 'ORD-2024-010', 'APL-DLR-004', '成都苹果数码有限公司', '2024-01-24 09:20:00', 139980.00, 6999.00, 0, 0, 0, 'ERP系统', 0, '2024-01-24 09:25:00', 'admin', NOW()); | ||
| 48 | + | ||
| 49 | +-- ============================================= | ||
| 50 | +-- 4. 订单商品明细表数据 | ||
| 51 | +-- ============================================= | ||
| 52 | +INSERT INTO t_order_item (item_id, order_id, order_no, product_code, product_name, product_qty, unit_price, item_amount, create_by, create_time) VALUES | ||
| 53 | +(1, 1, 'ORD-2024-001', 'APL-IP15-128G-BK', 'iPhone 15 128GB 黑色', 10, 5999.00, 59990.00, 'admin', NOW()), | ||
| 54 | +(2, 1, 'ORD-2024-001', 'APL-IP15-256G-BK', 'iPhone 15 256GB 黑色', 10, 6999.00, 69990.00, 'admin', NOW()), | ||
| 55 | +(3, 2, 'ORD-2024-002', 'APL-IP15-128G-BL', 'iPhone 15 128GB 蓝色', 15, 5999.00, 89985.00, 'admin', NOW()), | ||
| 56 | +(4, 2, 'ORD-2024-002', 'APL-IP15-256G-BL', 'iPhone 15 256GB 蓝色', 15, 6999.00, 104985.00, 'admin', NOW()), | ||
| 57 | +(5, 3, 'ORD-2024-003', 'APL-IPAD-AIR-64G-WIFI', 'iPad Air 64GB WiFi', 10, 4399.00, 43990.00, 'admin', NOW()), | ||
| 58 | +(6, 4, 'ORD-2024-004', 'APL-MAC-AIR-M2-8-256', 'MacBook Air M2芯片 8GB+256GB', 10, 8999.00, 89990.00, 'admin', NOW()), | ||
| 59 | +(7, 5, 'ORD-2024-005', 'APL-WATCH-SE-44-GPS', 'Apple Watch SE 44mm GPS', 10, 1999.00, 19990.00, 'admin', NOW()), | ||
| 60 | +(8, 6, 'ORD-2024-006', 'APL-MAC-AIR-M2-8-512', 'MacBook Air M2芯片 8GB+512GB', 10, 10499.00, 104990.00, 'admin', NOW()), | ||
| 61 | +(9, 7, 'ORD-2024-007', 'APL-MAC-AIR-M2-8-512', 'MacBook Air M2芯片 8GB+512GB', 10, 10499.00, 104990.00, 'admin', NOW()), | ||
| 62 | +(10, 8, 'ORD-2024-008', 'APL-IPAD-AIR-256G-WIFI', 'iPad Air 256GB WiFi', 10, 5499.00, 54990.00, 'admin', NOW()), | ||
| 63 | +(11, 9, 'ORD-2024-009', 'APL-IP15-128G-BK', 'iPhone 15 128GB 黑色', 20, 5999.00, 119980.00, 'admin', NOW()), | ||
| 64 | +(12, 9, 'ORD-2024-009', 'APL-IP15-256G-BK', 'iPhone 15 256GB 黑色', 20, 6999.00, 139980.00, 'admin', NOW()), | ||
| 65 | +(13, 10, 'ORD-2024-010', 'APL-IP15-128G-BL', 'iPhone 15 128GB 蓝色', 10, 5999.00, 59990.00, 'admin', NOW()), | ||
| 66 | +(14, 10, 'ORD-2024-010', 'APL-IPAD-AIR-64G-WIFI', 'iPad Air 64GB WiFi', 10, 4399.00, 43990.00, 'admin', NOW()), | ||
| 67 | +(15, 10, 'ORD-2024-010', 'APL-WATCH-SE-44-GPS', 'Apple Watch SE 44mm GPS', 10, 1999.00, 19990.00, 'admin', NOW()); | ||
| 68 | + | ||
| 69 | +-- ============================================= | ||
| 70 | +-- 5. 出库主表数据 | ||
| 71 | +-- ============================================= | ||
| 72 | +INSERT INTO t_delivery_main (delivery_id, delivery_no, dealer_code, dealer_name, order_no, delivery_date, delivery_status, warehouse_code, data_source, upload_time, create_by, create_time) VALUES | ||
| 73 | +(1, 'DEL-2024-001', 'APL-DLR-001', '北京苹果科技有限公司', 'ORD-2024-001', '2024-01-15 11:00:00', 1, 'WH001', 'ERP系统', '2024-01-15 11:05:00', 'admin', NOW()), | ||
| 74 | +(2, 'DEL-2024-002', 'APL-DLR-002', '上海苹果贸易有限公司', 'ORD-2024-002', '2024-01-16 15:00:00', 1, 'WH002', 'ERP系统', '2024-01-16 15:05:00', 'admin', NOW()), | ||
| 75 | +(3, 'DEL-2024-003', 'APL-DLR-001', '北京苹果科技有限公司', 'ORD-2024-003', '2024-01-17 10:00:00', 1, 'WH001', 'ERP系统', '2024-01-17 10:05:00', 'admin', NOW()), | ||
| 76 | +(4, 'DEL-2024-004', 'APL-DLR-003', '深圳苹果电子有限公司', 'ORD-2024-004', '2024-01-18 17:00:00', 1, 'WH003', 'ERP系统', '2024-01-18 17:05:00', 'admin', NOW()), | ||
| 77 | +(5, 'DEL-2024-005', 'APL-DLR-004', '成都苹果数码有限公司', 'ORD-2024-005', '2024-01-19 12:00:00', 1, 'WH004', 'ERP系统', '2024-01-19 12:05:00', 'admin', NOW()), | ||
| 78 | +(6, 'DEL-2024-006', 'APL-DLR-005', '西安苹果科技发展有限公司', 'ORD-2024-007', '2024-01-21 16:00:00', 1, 'WH005', 'ERP系统', '2024-01-21 16:05:00', 'admin', NOW()), | ||
| 79 | +(7, 'DEL-2024-007', 'APL-DLR-001', '北京苹果科技有限公司', 'ORD-2024-008', '2024-01-22 11:00:00', 1, 'WH001', 'ERP系统', '2024-01-22 11:05:00', 'admin', NOW()), | ||
| 80 | +(8, 'DEL-2024-008', 'APL-DLR-003', '深圳苹果电子有限公司', 'ORD-2024-009', '2024-01-23 15:00:00', 1, 'WH003', 'ERP系统', '2024-01-23 15:05:00', 'admin', NOW()); | ||
| 81 | + | ||
| 82 | +-- ============================================= | ||
| 83 | +-- 6. 出库商品明细表数据 | ||
| 84 | +-- ============================================= | ||
| 85 | +INSERT INTO t_delivery_item (delivery_item_id, delivery_id, delivery_no, order_no, product_code, product_name, delivery_qty, delivery_price, delivery_amount, create_by, create_time) VALUES | ||
| 86 | +(1, 1, 'DEL-2024-001', 'ORD-2024-001', 'APL-IP15-128G-BK', 'iPhone 15 128GB 黑色', 10, 5999.00, 59990.00, 'admin', NOW()), | ||
| 87 | +(2, 1, 'DEL-2024-001', 'ORD-2024-001', 'APL-IP15-256G-BK', 'iPhone 15 256GB 黑色', 10, 6999.00, 69990.00, 'admin', NOW()), | ||
| 88 | +(3, 2, 'DEL-2024-002', 'ORD-2024-002', 'APL-IP15-128G-BL', 'iPhone 15 128GB 蓝色', 15, 5999.00, 89985.00, 'admin', NOW()), | ||
| 89 | +(4, 2, 'DEL-2024-002', 'ORD-2024-002', 'APL-IP15-256G-BL', 'iPhone 15 256GB 蓝色', 15, 6999.00, 104985.00, 'admin', NOW()), | ||
| 90 | +(5, 3, 'DEL-2024-003', 'ORD-2024-003', 'APL-IPAD-AIR-64G-WIFI', 'iPad Air 64GB WiFi', 10, 4399.00, 43990.00, 'admin', NOW()), | ||
| 91 | +(6, 4, 'DEL-2024-004', 'ORD-2024-004', 'APL-MAC-AIR-M2-8-256', 'MacBook Air M2芯片 8GB+256GB', 10, 8999.00, 89990.00, 'admin', NOW()), | ||
| 92 | +(7, 5, 'DEL-2024-005', 'ORD-2024-005', 'APL-WATCH-SE-44-GPS', 'Apple Watch SE 44mm GPS', 10, 1999.00, 19990.00, 'admin', NOW()), | ||
| 93 | +(8, 6, 'DEL-2024-006', 'ORD-2024-007', 'APL-MAC-AIR-M2-8-512', 'MacBook Air M2芯片 8GB+512GB', 10, 10499.00, 104990.00, 'admin', NOW()), | ||
| 94 | +(9, 7, 'DEL-2024-007', 'ORD-2024-008', 'APL-IPAD-AIR-256G-WIFI', 'iPad Air 256GB WiFi', 10, 5499.00, 54990.00, 'admin', NOW()), | ||
| 95 | +(10, 8, 'DEL-2024-008', 'ORD-2024-009', 'APL-IP15-128G-BK', 'iPhone 15 128GB 黑色', 20, 5999.00, 119980.00, 'admin', NOW()), | ||
| 96 | +(11, 8, 'DEL-2024-008', 'ORD-2024-009', 'APL-IP15-256G-BK', 'iPhone 15 256GB 黑色', 20, 6999.00, 139980.00, 'admin', NOW()); | ||
| 97 | + | ||
| 98 | +-- ============================================= | ||
| 99 | +-- 7. 发票主表数据 | ||
| 100 | +-- ============================================= | ||
| 101 | +INSERT INTO t_invoice_main (invoice_id, invoice_no, order_no, delivery_no, dealer_code, dealer_name, total_amount, invoice_date, invoice_status, tax_rate, data_source, upload_time, create_by, create_time) VALUES | ||
| 102 | +(1, 'INV-2024-001', 'ORD-2024-001', 'DEL-2024-001', 'APL-DLR-001', '北京苹果科技有限公司', 135580.00, '2024-01-15', 1, 13.00, 'ERP系统', '2024-01-15 12:00:00', 'admin', NOW()), | ||
| 103 | +(2, 'INV-2024-002', 'ORD-2024-002', 'DEL-2024-002', 'APL-DLR-002', '上海苹果贸易有限公司', 203370.00, '2024-01-16', 1, 13.00, 'ERP系统', '2024-01-16 16:00:00', 'admin', NOW()), | ||
| 104 | +(3, 'INV-2024-003', 'ORD-2024-003', 'DEL-2024-003', 'APL-DLR-001', '北京苹果科技有限公司', 49710.00, '2024-01-17', 1, 13.00, 'ERP系统', '2024-01-17 11:00:00', 'admin', NOW()), | ||
| 105 | +(4, 'INV-2024-004', 'ORD-2024-004', 'DEL-2024-004', 'APL-DLR-003', '深圳苹果电子有限公司', 101690.00, '2024-01-18', 1, 13.00, 'ERP系统', '2024-01-18 18:00:00', 'admin', NOW()), | ||
| 106 | +(5, 'INV-2024-005', 'ORD-2024-005', 'DEL-2024-005', 'APL-DLR-004', '成都苹果数码有限公司', 22590.00, '2024-01-19', 1, 13.00, 'ERP系统', '2024-01-19 13:00:00', 'admin', NOW()), | ||
| 107 | +(6, 'INV-2024-006', 'ORD-2024-007', 'DEL-2024-006', 'APL-DLR-005', '西安苹果科技发展有限公司', 118640.00, '2024-01-21', 1, 13.00, 'ERP系统', '2024-01-21 17:00:00', 'admin', NOW()), | ||
| 108 | +(7, 'INV-2024-007', 'ORD-2024-008', 'DEL-2024-007', 'APL-DLR-001', '北京苹果科技有限公司', 62140.00, '2024-01-22', 1, 13.00, 'ERP系统', '2024-01-22 12:00:00', 'admin', NOW()), | ||
| 109 | +(8, 'INV-2024-008', 'ORD-2024-009', 'DEL-2024-008', 'APL-DLR-003', '深圳苹果电子有限公司', 305060.00, '2024-01-23', 1, 13.00, 'ERP系统', '2024-01-23 16:00:00', 'admin', NOW()); | ||
| 110 | + | ||
| 111 | +-- ============================================= | ||
| 112 | +-- 8. 发票商品明细表数据 | ||
| 113 | +-- ============================================= | ||
| 114 | +INSERT INTO t_invoice_item (invoice_item_id, invoice_id, invoice_no, order_no, product_code, product_name, invoice_qty, unit_price_no_tax, amount_no_tax, tax_amount, create_by, create_time) VALUES | ||
| 115 | +(1, 1, 'INV-2024-001', 'ORD-2024-001', 'APL-IP15-128G-BK', 'iPhone 15 128GB 黑色', 10, 5308.85, 53088.50, 6901.50, 'admin', NOW()), | ||
| 116 | +(2, 1, 'INV-2024-001', 'ORD-2024-001', 'APL-IP15-256G-BK', 'iPhone 15 256GB 黑色', 10, 6193.81, 61938.10, 8051.90, 'admin', NOW()), | ||
| 117 | +(3, 2, 'INV-2024-002', 'ORD-2024-002', 'APL-IP15-128G-BL', 'iPhone 15 128GB 蓝色', 15, 5308.85, 79632.75, 10352.25, 'admin', NOW()), | ||
| 118 | +(4, 2, 'INV-2024-002', 'ORD-2024-002', 'APL-IP15-256G-BL', 'iPhone 15 256GB 蓝色', 15, 6193.81, 92907.15, 12077.85, 'admin', NOW()), | ||
| 119 | +(5, 3, 'INV-2024-003', 'ORD-2024-003', 'APL-IPAD-AIR-64G-WIFI', 'iPad Air 64GB WiFi', 10, 3892.92, 38929.20, 5060.80, 'admin', NOW()), | ||
| 120 | +(6, 4, 'INV-2024-004', 'ORD-2024-004', 'APL-MAC-AIR-M2-8-256', 'MacBook Air M2芯片 8GB+256GB', 10, 7964.60, 79646.00, 10354.00, 'admin', NOW()), | ||
| 121 | +(7, 5, 'INV-2024-005', 'ORD-2024-005', 'APL-WATCH-SE-44-GPS', 'Apple Watch SE 44mm GPS', 10, 1769.91, 17699.10, 2300.90, 'admin', NOW()), | ||
| 122 | +(8, 6, 'INV-2024-006', 'ORD-2024-007', 'APL-MAC-AIR-M2-8-512', 'MacBook Air M2芯片 8GB+512GB', 10, 9291.15, 92911.50, 12078.50, 'admin', NOW()), | ||
| 123 | +(9, 7, 'INV-2024-007', 'ORD-2024-008', 'APL-IPAD-AIR-256G-WIFI', 'iPad Air 256GB WiFi', 10, 4867.26, 48672.60, 6327.40, 'admin', NOW()), | ||
| 124 | +(10, 8, 'INV-2024-008', 'ORD-2024-009', 'APL-IP15-128G-BK', 'iPhone 15 128GB 黑色', 20, 5308.85, 106177.00, 13803.00, 'admin', NOW()), | ||
| 125 | +(11, 8, 'INV-2024-008', 'ORD-2024-009', 'APL-IP15-256G-BK', 'iPhone 15 256GB 黑色', 20, 6193.81, 123876.20, 16103.80, 'admin', NOW()); | ||
| 126 | + | ||
| 127 | +-- ============================================= | ||
| 128 | +-- 9. 返利台账明细表数据 | ||
| 129 | +-- ============================================= | ||
| 130 | +INSERT INTO t_rebate_detail (rebate_id, rebate_no, order_no, dealer_code, dealer_name, product_code, rebate_amount, rebate_date, operate_type, calc_flag, create_by, create_time) VALUES | ||
| 131 | +(1, 'REB-2024-001', 'ORD-2024-001', 'APL-DLR-001', '北京苹果科技有限公司', 'APL-IP15-128G-BK', 2999.50, '2024-01-15', 1, 1, 'admin', NOW()), | ||
| 132 | +(2, 'REB-2024-002', 'ORD-2024-001', 'APL-DLR-001', '北京苹果科技有限公司', 'APL-IP15-256G-BK', 3499.50, '2024-01-15', 1, 1, 'admin', NOW()), | ||
| 133 | +(3, 'REB-2024-003', 'ORD-2024-002', 'APL-DLR-002', '上海苹果贸易有限公司', 'APL-IP15-128G-BL', 4499.25, '2024-01-16', 1, 1, 'admin', NOW()), | ||
| 134 | +(4, 'REB-2024-004', 'ORD-2024-002', 'APL-DLR-002', '上海苹果贸易有限公司', 'APL-IP15-256G-BL', 5249.25, '2024-01-16', 1, 1, 'admin', NOW()), | ||
| 135 | +(5, 'REB-2024-005', 'ORD-2024-003', 'APL-DLR-001', '北京苹果科技有限公司', 'APL-IPAD-AIR-64G-WIFI', 2199.50, '2024-01-17', 1, 1, 'admin', NOW()), | ||
| 136 | +(6, 'REB-2024-006', 'ORD-2024-004', 'APL-DLR-003', '深圳苹果电子有限公司', 'APL-MAC-AIR-M2-8-256', 4499.50, '2024-01-18', 1, 1, 'admin', NOW()), | ||
| 137 | +(7, 'REB-2024-007', 'ORD-2024-005', 'APL-DLR-004', '成都苹果数码有限公司', 'APL-WATCH-SE-44-GPS', 999.50, '2024-01-19', 1, 1, 'admin', NOW()), | ||
| 138 | +(8, 'REB-2024-008', 'ORD-2024-007', 'APL-DLR-005', '西安苹果科技发展有限公司', 'APL-MAC-AIR-M2-8-512', 5249.50, '2024-01-21', 1, 1, 'admin', NOW()), | ||
| 139 | +(9, 'REB-2024-009', 'ORD-2024-008', 'APL-DLR-001', '北京苹果科技有限公司', 'APL-IPAD-AIR-256G-WIFI', 2749.50, '2024-01-22', 1, 1, 'admin', NOW()), | ||
| 140 | +(10, 'REB-2024-010', 'ORD-2024-009', 'APL-DLR-003', '深圳苹果电子有限公司', 'APL-IP15-128G-BK', 5999.00, '2024-01-23', 1, 1, 'admin', NOW()), | ||
| 141 | +(11, 'REB-2024-011', 'ORD-2024-009', 'APL-DLR-003', '深圳苹果电子有限公司', 'APL-IP15-256G-BK', 6999.00, '2024-01-23', 1, 1, 'admin', NOW()); | ||
| 142 | + | ||
| 143 | +-- ============================================= | ||
| 144 | +-- 10. 异常工单表数据 | ||
| 145 | +-- ============================================= | ||
| 146 | +INSERT INTO t_exception_workorder (workorder_id, workorder_no, order_no, dealer_code, dealer_name, exception_type, severity_level, workorder_status, create_time, expect_complete_time, handler_user, exception_desc, handle_suggest, data_source, create_by) VALUES | ||
| 147 | +(1, 'WO-2024-001', 'ORD-2024-006', 'APL-DLR-002', '上海苹果贸易有限公司', 1, 2, 1, '2024-01-20 14:00:00', '2024-01-22 18:00:00', NULL, '订单金额与产品单价不匹配', '请核实订单金额计算是否正确', 'ERP系统', 'admin'), | ||
| 148 | +(2, 'WO-2024-002', 'ORD-2024-010', 'APL-DLR-004', '成都苹果数码有限公司', 2, 1, 2, '2024-01-24 10:00:00', '2024-01-25 18:00:00', '张三', '经销商资质审核未通过', '请重新提交资质材料', 'ERP系统', 'admin'), | ||
| 149 | +(3, 'WO-2024-003', 'ORD-2024-001', 'APL-DLR-001', '北京苹果科技有限公司', 3, 3, 3, '2024-01-15 12:00:00', '2024-01-16 18:00:00', '李四', '返利计算异常', '已重新计算返利金额', 'ERP系统', 'admin'), | ||
| 150 | +(4, 'WO-2024-004', 'ORD-2024-002', 'APL-DLR-002', '上海苹果贸易有限公司', 1, 2, 4, '2024-01-16 16:00:00', '2024-01-17 18:00:00', '王五', '发票信息不完整', '已补充完整发票信息', 'ERP系统', 'admin'); | ||
| 151 | + | ||
| 152 | +-- ============================================= | ||
| 153 | +-- 11. 异常工单处理日志表数据 | ||
| 154 | +-- ============================================= | ||
| 155 | +INSERT INTO t_exception_workorder_log (log_id, workorder_id, workorder_no, handle_user, handle_time, before_status, after_status, handle_opinion, create_by) VALUES | ||
| 156 | +(1, 2, 'WO-2024-002', '张三', '2024-01-24 14:30:00', 1, 2, '开始处理经销商资质审核问题', 'admin'), | ||
| 157 | +(2, 3, 'WO-2024-003', '李四', '2024-01-15 15:00:00', 1, 2, '开始处理返利计算异常', 'admin'), | ||
| 158 | +(3, 3, 'WO-2024-003', '李四', '2024-01-15 16:30:00', 2, 3, '返利计算异常已解决,重新计算完成', 'admin'), | ||
| 159 | +(4, 4, 'WO-2024-004', '王五', '2024-01-16 17:00:00', 1, 2, '开始处理发票信息不完整问题', 'admin'), | ||
| 160 | +(5, 4, 'WO-2024-004', '王五', '2024-01-16 18:00:00', 2, 3, '发票信息已补充完整', 'admin'), | ||
| 161 | +(6, 4, 'WO-2024-004', '王五', '2024-01-17 09:00:00', 3, 4, '工单已关闭,问题已解决', 'admin'); |
| 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 | +-- 获取产品管理主菜单ID(menu_id = 8) | ||
| 17 | +SET @product_menu_id = (SELECT menu_id FROM t_sys_menu WHERE menu_name = '产品管理' AND del_flag = '0' LIMIT 1); | ||
| 18 | + | ||
| 19 | +-- 产品管理按钮权限 | ||
| 20 | +INSERT INTO t_sys_menu (parent_id, menu_name, menu_type, path, icon, sort, status, perms, create_by, create_time, update_by, update_time, del_flag) | ||
| 21 | +VALUES | ||
| 22 | +(@product_menu_id, '产品查询', '2', '', '', 1, '1', 'product:list', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 23 | +(@product_menu_id, '产品新增', '2', '', '', 2, '1', 'product:add', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 24 | +(@product_menu_id, '产品修改', '2', '', '', 3, '1', 'product:edit', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 25 | +(@product_menu_id, '产品删除', '2', '', '', 4, '1', 'product:delete', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 26 | +(@product_menu_id, '产品详情', '2', '', '', 5, '1', 'product:detail', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 27 | +(@product_menu_id, '产品批量删除', '2', '', '', 6, '1', 'product:batchDelete', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 28 | +(@product_menu_id, '产品状态修改', '2', '', '', 7, '1', 'product:updateStatus', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 29 | +(@product_menu_id, '产品返利标识修改', '2', '', '', 8, '1', 'product:updateRebateFlag', 'admin', NOW(), 'admin', NOW(), '0'); | ||
| 30 | + | ||
| 31 | +-- ========================================= | ||
| 32 | +-- 2. 插入经销商管理相关按钮权限 | ||
| 33 | +-- ========================================= | ||
| 34 | + | ||
| 35 | +-- 获取经销商管理主菜单ID(menu_id = 9) | ||
| 36 | +SET @dealer_menu_id = (SELECT menu_id FROM t_sys_menu WHERE menu_name = '经销商管理' AND del_flag = '0' LIMIT 1); | ||
| 37 | + | ||
| 38 | +-- 经销商管理按钮权限 | ||
| 39 | +INSERT INTO t_sys_menu (parent_id, menu_name, menu_type, path, icon, sort, status, perms, create_by, create_time, update_by, update_time, del_flag) | ||
| 40 | +VALUES | ||
| 41 | +(@dealer_menu_id, '经销商查询', '2', '', '', 1, '1', 'dealer:list', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 42 | +(@dealer_menu_id, '经销商新增', '2', '', '', 2, '1', 'dealer:add', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 43 | +(@dealer_menu_id, '经销商修改', '2', '', '', 3, '1', 'dealer:update', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 44 | +(@dealer_menu_id, '经销商删除', '2', '', '', 4, '1', 'dealer:delete', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 45 | +(@dealer_menu_id, '经销商详情', '2', '', '', 5, '1', 'dealer:detail', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 46 | +(@dealer_menu_id, '经销商批量删除', '2', '', '', 6, '1', 'dealer:batchDelete', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 47 | +(@dealer_menu_id, '经销商合作状态修改', '2', '', '', 7, '1', 'dealer:updateStatus', 'admin', NOW(), 'admin', NOW(), '0'), | ||
| 48 | +(@dealer_menu_id, '经销商资质审核', '2', '', '', 8, '1', 'dealer:audit', 'admin', NOW(), 'admin', NOW(), '0'); | ||
| 49 | + | ||
| 50 | +SET FOREIGN_KEY_CHECKS = 1; | ||
| 51 | +SELECT 'Apple经销商ERP系统产品管理和经销商管理权限配置完成!' AS message; |
| 1 | -# Apple经销商ERP系统 - 数据库脚本说明 | 1 | +# Apple经销商ERP系统 - 数据库设计文档 |
| 2 | + | ||
| 3 | +## 概述 | ||
| 4 | + | ||
| 5 | +本数据库设计为Apple经销商ERP系统提供完整的数据存储方案,严格按照数据库设计文档创建系统管理、订单管理、出库查询、发票管理、返利台账、异常工单等核心业务模块。 | ||
| 6 | + | ||
| 7 | +## 数据库结构 | ||
| 8 | + | ||
| 9 | +### 1. 系统管理模块 | ||
| 10 | + | ||
| 11 | +#### 系统用户表(t_sys_user) | ||
| 12 | +- 存储系统用户基本信息 | ||
| 13 | +- 包含用户状态和登录信息 | ||
| 14 | +- 支持用户权限管理 | ||
| 15 | + | ||
| 16 | +#### 系统角色表(t_sys_role) | ||
| 17 | +- 存储系统角色信息 | ||
| 18 | +- 包含角色状态和权限 | ||
| 19 | +- 支持角色权限管理 | ||
| 20 | + | ||
| 21 | +#### 用户角色关联表(t_sys_user_role) | ||
| 22 | +- 存储用户和角色的关联关系 | ||
| 23 | +- 支持多对多关系 | ||
| 24 | +- 实现用户权限分配 | ||
| 25 | + | ||
| 26 | +#### 系统菜单表(t_sys_menu) | ||
| 27 | +- 存储系统菜单信息 | ||
| 28 | +- 包含菜单层级和权限标识 | ||
| 29 | +- 支持动态菜单管理 | ||
| 30 | + | ||
| 31 | +#### 角色菜单关联表(t_sys_role_menu) | ||
| 32 | +- 存储角色和菜单的关联关系 | ||
| 33 | +- 支持角色权限控制 | ||
| 34 | +- 实现菜单权限管理 | ||
| 35 | + | ||
| 36 | +#### 字典类型表(t_sys_dict_type) | ||
| 37 | +- 存储字典类型信息 | ||
| 38 | +- 支持系统配置管理 | ||
| 39 | +- 实现数据字典功能 | ||
| 40 | + | ||
| 41 | +#### 字典项表(t_sys_dict_item) | ||
| 42 | +- 存储字典项信息 | ||
| 43 | +- 关联字典类型 | ||
| 44 | +- 支持配置项管理 | ||
| 45 | + | ||
| 46 | +#### 系统日志表(t_sys_log) | ||
| 47 | +- 存储系统操作日志 | ||
| 48 | +- 包含用户操作记录 | ||
| 49 | +- 支持审计和监控 | ||
| 50 | + | ||
| 51 | +### 2. 订单管理模块 | ||
| 52 | + | ||
| 53 | +#### 订单主表(t_order_main) | ||
| 54 | +- 存储订单基本信息 | ||
| 55 | +- 包含经销商信息、订单状态、金额等 | ||
| 56 | +- 支持返利计算和数据验证 | ||
| 57 | + | ||
| 58 | +#### 订单商品明细表(t_order_item) | ||
| 59 | +- 存储订单商品明细 | ||
| 60 | +- 包含产品信息、数量、价格等 | ||
| 61 | +- 支持明细金额计算 | ||
| 62 | + | ||
| 63 | +### 3. 出库管理模块 | ||
| 64 | + | ||
| 65 | +#### 出库主表(t_delivery_main) | ||
| 66 | +- 存储出库单基本信息 | ||
| 67 | +- 关联订单信息 | ||
| 68 | +- 支持多种出库状态 | ||
| 69 | + | ||
| 70 | +#### 出库商品明细表(t_delivery_item) | ||
| 71 | +- 存储出库商品明细 | ||
| 72 | +- 包含出库数量、价格等 | ||
| 73 | +- 支持出库金额计算 | ||
| 74 | + | ||
| 75 | +### 4. 发票管理模块 | ||
| 76 | + | ||
| 77 | +#### 发票主表(t_invoice_main) | ||
| 78 | +- 存储发票基本信息 | ||
| 79 | +- 支持普通发票和专用发票 | ||
| 80 | +- 包含税率和税额计算 | ||
| 81 | + | ||
| 82 | +#### 发票商品明细表(t_invoice_item) | ||
| 83 | +- 存储发票商品明细 | ||
| 84 | +- 支持税率计算 | ||
| 85 | +- 包含含税金额 | ||
| 86 | + | ||
| 87 | +### 5. 返利管理模块 | ||
| 88 | + | ||
| 89 | +#### 返利台账明细表(t_rebate_detail) | ||
| 90 | +- 存储返利明细信息 | ||
| 91 | +- 支持新增和扣减操作 | ||
| 92 | +- 包含返利计算状态 | ||
| 93 | + | ||
| 94 | +### 6. 异常工单模块 | ||
| 95 | + | ||
| 96 | +#### 异常工单表(t_exception_workorder) | ||
| 97 | +- 存储异常工单信息 | ||
| 98 | +- 支持多种异常类型 | ||
| 99 | +- 包含严重程度和状态管理 | ||
| 100 | + | ||
| 101 | +#### 异常工单处理日志表(t_exception_workorder_log) | ||
| 102 | +- 存储工单处理日志 | ||
| 103 | +- 记录处理过程和结果 | ||
| 104 | +- 支持附件管理 | ||
| 105 | + | ||
| 106 | +### 7. 基础信息模块 | ||
| 107 | + | ||
| 108 | +#### 经销商信息表(t_dealer_info) | ||
| 109 | +- 存储经销商基本信息 | ||
| 110 | +- 包含资质审核和合作状态 | ||
| 111 | +- 支持返利金额统计 | ||
| 112 | + | ||
| 113 | +#### 产品信息表(t_product_info) | ||
| 114 | +- 存储产品基本信息 | ||
| 115 | +- 包含价格和销售状态 | ||
| 116 | +- 支持返利标识 | ||
| 2 | 117 | ||
| 3 | ## 脚本文件说明 | 118 | ## 脚本文件说明 |
| 4 | 119 | ||
| 5 | -### 1. 系统管理表创建脚本 | 120 | +### 系统管理脚本 |
| 121 | + | ||
| 122 | +#### 1. 系统管理表创建脚本 | ||
| 6 | - **文件**: `01_create_system_tables.sql` | 123 | - **文件**: `01_create_system_tables.sql` |
| 7 | - **功能**: 创建系统管理相关的数据库表 | 124 | - **功能**: 创建系统管理相关的数据库表 |
| 8 | - **包含表**: | 125 | - **包含表**: |
| ... | @@ -15,7 +132,7 @@ | ... | @@ -15,7 +132,7 @@ |
| 15 | - `t_sys_dict_item` - 字典项表 | 132 | - `t_sys_dict_item` - 字典项表 |
| 16 | - `t_sys_log` - 系统日志表 | 133 | - `t_sys_log` - 系统日志表 |
| 17 | 134 | ||
| 18 | -### 2. 系统管理表索引创建脚本 | 135 | +#### 2. 系统管理表索引创建脚本 |
| 19 | - **文件**: `02_create_system_indexes.sql` | 136 | - **文件**: `02_create_system_indexes.sql` |
| 20 | - **功能**: 为系统管理表创建索引和外键约束 | 137 | - **功能**: 为系统管理表创建索引和外键约束 |
| 21 | - **包含内容**: | 138 | - **包含内容**: |
| ... | @@ -24,7 +141,7 @@ | ... | @@ -24,7 +141,7 @@ |
| 24 | - 外键约束 | 141 | - 外键约束 |
| 25 | - 唯一约束索引 | 142 | - 唯一约束索引 |
| 26 | 143 | ||
| 27 | -### 3. 系统基础数据插入脚本 | 144 | +#### 3. 系统基础数据插入脚本 |
| 28 | - **文件**: `03_insert_system_data.sql` | 145 | - **文件**: `03_insert_system_data.sql` |
| 29 | - **功能**: 插入系统初始化基础数据 | 146 | - **功能**: 插入系统初始化基础数据 |
| 30 | - **包含数据**: | 147 | - **包含数据**: |
| ... | @@ -35,24 +152,176 @@ | ... | @@ -35,24 +152,176 @@ |
| 35 | - 角色菜单关联数据 | 152 | - 角色菜单关联数据 |
| 36 | - 字典类型和字典项数据 | 153 | - 字典类型和字典项数据 |
| 37 | 154 | ||
| 38 | -### 4. 系统表结构验证脚本 | 155 | +### 业务管理脚本 |
| 39 | -- **文件**: `04_verify_system_structure.sql` | 156 | + |
| 40 | -- **功能**: 验证系统表结构的完整性和数据一致性 | 157 | +#### 4. 业务表创建脚本 |
| 41 | -- **验证内容**: | 158 | +- **文件**: `01_create_business_tables.sql` |
| 42 | - - 表结构验证 | 159 | +- **功能**: 创建业务管理相关的数据库表 |
| 43 | - - 索引结构验证 | 160 | +- **包含表**: |
| 44 | - - 外键约束验证 | 161 | + - `t_order_main` - 订单主表 |
| 45 | - - 基础数据验证 | 162 | + - `t_order_item` - 订单商品明细表 |
| 46 | - - 数据完整性检查 | 163 | + - `t_delivery_main` - 出库主表 |
| 164 | + - `t_delivery_item` - 出库商品明细表 | ||
| 165 | + - `t_invoice_main` - 发票主表 | ||
| 166 | + - `t_invoice_item` - 发票商品明细表 | ||
| 167 | + - `t_rebate_detail` - 返利台账明细表 | ||
| 168 | + - `t_exception_workorder` - 异常工单表 | ||
| 169 | + - `t_exception_workorder_log` - 异常工单处理日志表 | ||
| 170 | + - `t_dealer_info` - 经销商信息表 | ||
| 171 | + - `t_product_info` - 产品信息表 | ||
| 172 | + | ||
| 173 | +#### 5. 业务表索引创建脚本 | ||
| 174 | +- **文件**: `02_create_business_indexes.sql` | ||
| 175 | +- **功能**: 为业务管理表创建索引 | ||
| 176 | +- **包含内容**: | ||
| 177 | + - 主键索引、唯一索引、普通索引 | ||
| 178 | + - 复合索引优化查询性能 | ||
| 179 | + - 支持高效的数据检索 | ||
| 180 | + | ||
| 181 | +#### 6. 业务测试数据插入脚本 | ||
| 182 | +- **文件**: `03_insert_business_data.sql` | ||
| 183 | +- **功能**: 插入业务测试数据 | ||
| 184 | +- **包含数据**: | ||
| 185 | + - 经销商数据(5个经销商) | ||
| 186 | + - 产品数据(15个Apple产品) | ||
| 187 | + - 订单数据(10个订单及明细) | ||
| 188 | + - 出库数据(8个出库单及明细) | ||
| 189 | + - 发票数据(8张发票及明细) | ||
| 190 | + - 返利数据(11条返利记录) | ||
| 191 | + - 异常工单数据(4个异常工单及处理日志) | ||
| 192 | + | ||
| 193 | +#### 7. 业务数据库初始化脚本 | ||
| 194 | +- **文件**: `00_setup_business_database.sql` | ||
| 195 | +- **功能**: 按顺序执行所有业务数据库脚本 | ||
| 196 | +- **执行顺序**: | ||
| 197 | + 1. 创建业务表 | ||
| 198 | + 2. 创建业务索引 | ||
| 199 | + 3. 插入业务测试数据 | ||
| 47 | 200 | ||
| 48 | ## 执行顺序 | 201 | ## 执行顺序 |
| 49 | 202 | ||
| 203 | +### 系统管理数据库初始化 | ||
| 50 | 请按照以下顺序执行脚本: | 204 | 请按照以下顺序执行脚本: |
| 51 | 205 | ||
| 52 | -1. `01_create_system_tables.sql` - 创建表结构 | 206 | +1. `01_create_system_tables.sql` - 创建系统表结构 |
| 53 | -2. `02_create_system_indexes.sql` - 创建索引和约束 | 207 | +2. `02_create_system_indexes.sql` - 创建系统表索引和约束 |
| 54 | -3. `03_insert_system_data.sql` - 插入基础数据 | 208 | +3. `03_insert_system_data.sql` - 插入系统基础数据 |
| 55 | -4. `04_verify_system_structure.sql` - 验证结构完整性 | 209 | + |
| 210 | +### 业务管理数据库初始化 | ||
| 211 | +请按照以下顺序执行脚本: | ||
| 212 | + | ||
| 213 | +1. `01_create_business_tables.sql` - 创建业务表结构 | ||
| 214 | +2. `02_create_business_indexes.sql` - 创建业务表索引 | ||
| 215 | +3. `03_insert_business_data.sql` - 插入业务测试数据 | ||
| 216 | +4. `06_insert_product_dealer_permissions.sql` - 插入产品管理和经销商管理权限 | ||
| 217 | + | ||
| 218 | +或者直接执行: | ||
| 219 | +- `00_setup_business_database.sql` - 自动执行业务数据库初始化(包含权限配置) | ||
| 220 | + | ||
| 221 | +## 权限管理脚本 | ||
| 222 | + | ||
| 223 | +### 产品管理和经销商管理权限脚本 (06_insert_product_dealer_permissions.sql) | ||
| 224 | + | ||
| 225 | +**功能:** 配置产品管理和经销商管理相关菜单和权限 | ||
| 226 | + | ||
| 227 | +**包含内容:** | ||
| 228 | +- 产品管理按钮权限(基于现有产品管理主菜单) | ||
| 229 | +- 经销商管理按钮权限(基于现有经销商管理主菜单) | ||
| 230 | +- 为ADMIN角色分配产品管理和经销商管理权限 | ||
| 231 | +- 权限验证查询 | ||
| 232 | + | ||
| 233 | +**产品管理权限列表:** | ||
| 234 | +- `product:list` - 产品查询 | ||
| 235 | +- `product:add` - 产品新增 | ||
| 236 | +- `product:update` - 产品修改 | ||
| 237 | +- `product:delete` - 产品删除 | ||
| 238 | +- `product:detail` - 产品详情 | ||
| 239 | +- `product:batchDelete` - 产品批量删除 | ||
| 240 | +- `product:updateStatus` - 产品状态修改 | ||
| 241 | +- `product:updateRebateFlag` - 产品返利标识修改 | ||
| 242 | + | ||
| 243 | +**经销商管理权限列表:** | ||
| 244 | +- `dealer:list` - 经销商查询 | ||
| 245 | +- `dealer:add` - 经销商新增 | ||
| 246 | +- `dealer:update` - 经销商修改 | ||
| 247 | +- `dealer:delete` - 经销商删除 | ||
| 248 | +- `dealer:detail` - 经销商详情 | ||
| 249 | +- `dealer:batchDelete` - 经销商批量删除 | ||
| 250 | +- `dealer:updateStatus` - 经销商合作状态修改 | ||
| 251 | +- `dealer:audit` - 经销商资质审核 | ||
| 252 | + | ||
| 253 | +**说明:** | ||
| 254 | +- 基于`03_insert_system_data.sql`中已存在的产品管理和经销商管理主菜单 | ||
| 255 | +- 只添加按钮级别的权限,不重复创建主菜单 | ||
| 256 | +- 所有权限都会自动分配给ADMIN角色 | ||
| 257 | + | ||
| 258 | +## 核心特性 | ||
| 259 | + | ||
| 260 | +### 1. 完整的业务流程 | ||
| 261 | +- 订单 → 出库 → 发票 → 返利 | ||
| 262 | +- 异常工单处理流程 | ||
| 263 | +- 数据验证和状态管理 | ||
| 264 | + | ||
| 265 | +### 2. 丰富的测试数据 | ||
| 266 | +- 5个经销商(一级和二级) | ||
| 267 | +- 15个Apple产品 | ||
| 268 | +- 10个订单及明细 | ||
| 269 | +- 8个出库单及明细 | ||
| 270 | +- 8张发票及明细 | ||
| 271 | +- 11条返利记录 | ||
| 272 | +- 4个异常工单及处理日志 | ||
| 273 | + | ||
| 274 | +### 3. 优化的索引设计 | ||
| 275 | +- 主键索引、唯一索引、普通索引 | ||
| 276 | +- 复合索引优化查询性能 | ||
| 277 | +- 支持高效的数据检索 | ||
| 278 | + | ||
| 279 | +### 4. 数据一致性 | ||
| 280 | +- 业务编号唯一性约束 | ||
| 281 | +- 状态字段标准化 | ||
| 282 | +- 金额字段精度控制 | ||
| 283 | + | ||
| 284 | +## 数据关系 | ||
| 285 | + | ||
| 286 | +### 订单流程 | ||
| 287 | +1. 创建订单 → 订单明细 | ||
| 288 | +2. 确认订单 → 生成出库单 | ||
| 289 | +3. 出库完成 → 生成发票 | ||
| 290 | +4. 发票开具 → 计算返利 | ||
| 291 | + | ||
| 292 | +### 数据关联 | ||
| 293 | +- 订单 → 出库单(一对多) | ||
| 294 | +- 出库单 → 发票(一对一) | ||
| 295 | +- 经销商 → 订单(一对多) | ||
| 296 | +- 产品 → 订单明细(一对多) | ||
| 297 | +- 异常工单 → 处理日志(一对多) | ||
| 298 | + | ||
| 299 | +## 性能优化 | ||
| 300 | + | ||
| 301 | +### 索引策略 | ||
| 302 | +- 主键使用自增ID | ||
| 303 | +- 业务编号使用唯一索引 | ||
| 304 | +- 查询字段建立复合索引 | ||
| 305 | +- 状态字段建立普通索引 | ||
| 306 | + | ||
| 307 | +### 查询优化 | ||
| 308 | +- 分页查询优化 | ||
| 309 | +- 状态查询优化 | ||
| 310 | +- 时间范围查询优化 | ||
| 311 | +- 金额范围查询优化 | ||
| 312 | + | ||
| 313 | +## 扩展性设计 | ||
| 314 | + | ||
| 315 | +### 表结构扩展 | ||
| 316 | +- 预留扩展字段 | ||
| 317 | +- 支持自定义字段 | ||
| 318 | +- 版本控制支持 | ||
| 319 | + | ||
| 320 | +### 业务扩展 | ||
| 321 | +- 支持多经销商管理 | ||
| 322 | +- 支持多种产品类型 | ||
| 323 | +- 支持多种支付方式 | ||
| 324 | +- 支持多种配送方式 | ||
| 56 | 325 | ||
| 57 | ## 默认用户账号 | 326 | ## 默认用户账号 |
| 58 | 327 | ||
| ... | @@ -64,13 +333,57 @@ | ... | @@ -64,13 +333,57 @@ |
| 64 | | operator | operator123 | 运营人员 | 拥有业务操作权限 | | 333 | | operator | operator123 | 运营人员 | 拥有业务操作权限 | |
| 65 | | auditor | auditor123 | 审核人员 | 拥有审核权限 | | 334 | | auditor | auditor123 | 审核人员 | 拥有审核权限 | |
| 66 | 335 | ||
| 336 | +## 测试数据说明 | ||
| 337 | + | ||
| 338 | +### 经销商数据 | ||
| 339 | +- 5个经销商(3个一级,2个二级) | ||
| 340 | +- 涵盖不同地区和合作状态 | ||
| 341 | +- 包含完整的资质信息 | ||
| 342 | + | ||
| 343 | +### 产品数据 | ||
| 344 | +- 15个Apple产品 | ||
| 345 | +- 涵盖iPhone、iPad、MacBook、Apple Watch | ||
| 346 | +- 包含不同规格和价格 | ||
| 347 | + | ||
| 348 | +### 订单数据 | ||
| 349 | +- 10个订单 | ||
| 350 | +- 涵盖不同状态和类型 | ||
| 351 | +- 包含完整的订单明细 | ||
| 352 | + | ||
| 353 | +### 出库数据 | ||
| 354 | +- 8个出库单 | ||
| 355 | +- 关联对应订单 | ||
| 356 | +- 包含出库明细 | ||
| 357 | + | ||
| 358 | +### 发票数据 | ||
| 359 | +- 8张发票 | ||
| 360 | +- 支持含税金额计算 | ||
| 361 | +- 包含发票明细 | ||
| 362 | + | ||
| 363 | +### 返利数据 | ||
| 364 | +- 11条返利记录 | ||
| 365 | +- 支持新增和扣减操作 | ||
| 366 | +- 包含返利计算状态 | ||
| 367 | + | ||
| 368 | +### 异常工单数据 | ||
| 369 | +- 4个异常工单 | ||
| 370 | +- 涵盖不同异常类型和严重程度 | ||
| 371 | +- 包含处理日志 | ||
| 372 | + | ||
| 67 | ## 注意事项 | 373 | ## 注意事项 |
| 68 | 374 | ||
| 69 | -1. 所有脚本都包含错误处理,可以重复执行 | 375 | +1. 所有金额字段使用decimal(18,2)类型 |
| 70 | -2. 密码已使用BCrypt加密存储 | 376 | +2. 时间字段统一使用datetime类型 |
| 71 | -3. 所有表都包含软删除标志(del_flag) | 377 | +3. 状态字段使用tinyint类型 |
| 72 | -4. 外键约束已正确设置,确保数据完整性 | 378 | +4. 字符串字段根据实际需要设置长度 |
| 73 | -5. 索引已优化,支持高效查询 | 379 | +5. 所有表都包含创建和更新信息 |
| 380 | +6. 重要字段不允许为空 | ||
| 381 | +7. 业务编号保持唯一性 | ||
| 382 | +8. 不创建外键约束,通过应用层保证数据一致性 | ||
| 383 | +9. 所有脚本都包含错误处理,可以重复执行 | ||
| 384 | +10. 密码已使用BCrypt加密存储 | ||
| 385 | +11. 所有表都包含软删除标志(del_flag) | ||
| 386 | +12. 索引已优化,支持高效查询 | ||
| 74 | 387 | ||
| 75 | ## 数据库要求 | 388 | ## 数据库要求 |
| 76 | 389 | ||
| ... | @@ -78,3 +391,11 @@ | ... | @@ -78,3 +391,11 @@ |
| 78 | - 字符集: utf8mb4 | 391 | - 字符集: utf8mb4 |
| 79 | - 排序规则: utf8mb4_unicode_ci | 392 | - 排序规则: utf8mb4_unicode_ci |
| 80 | - 存储引擎: InnoDB | 393 | - 存储引擎: InnoDB |
| 394 | + | ||
| 395 | +## 使用说明 | ||
| 396 | + | ||
| 397 | +1. 按照脚本顺序执行数据库初始化 | ||
| 398 | +2. 确保MySQL版本8.0或以上 | ||
| 399 | +3. 使用utf8mb4字符集 | ||
| 400 | +4. 建议使用InnoDB存储引擎 | ||
| 401 | +5. 根据实际需要调整测试数据 | ... | ... |
| 1 | -import { request } from '@/utils/request' | 1 | +import axios from 'axios' |
| 2 | import type { LoginForm, LoginResponse, User } from '@/types' | 2 | import type { LoginForm, LoginResponse, User } from '@/types' |
| 3 | 3 | ||
| 4 | +// 创建axios实例 | ||
| 5 | +const api = axios.create({ | ||
| 6 | + baseURL: 'http://localhost:8083/api', | ||
| 7 | + timeout: 10000 | ||
| 8 | +}) | ||
| 9 | + | ||
| 10 | +// 请求拦截器 | ||
| 11 | +api.interceptors.request.use( | ||
| 12 | + config => { | ||
| 13 | + const token = localStorage.getItem('token') | ||
| 14 | + if (token) { | ||
| 15 | + config.headers.Authorization = `Bearer ${token}` | ||
| 16 | + } | ||
| 17 | + return config | ||
| 18 | + }, | ||
| 19 | + error => { | ||
| 20 | + return Promise.reject(error) | ||
| 21 | + } | ||
| 22 | +) | ||
| 23 | + | ||
| 24 | +// 响应拦截器 | ||
| 25 | +api.interceptors.response.use( | ||
| 26 | + response => { | ||
| 27 | + return response.data | ||
| 28 | + }, | ||
| 29 | + error => { | ||
| 30 | + if (error.response?.status === 401) { | ||
| 31 | + localStorage.removeItem('token') | ||
| 32 | + localStorage.removeItem('userInfo') | ||
| 33 | + window.location.href = '/login' | ||
| 34 | + } | ||
| 35 | + return Promise.reject(error) | ||
| 36 | + } | ||
| 37 | +) | ||
| 38 | + | ||
| 4 | // 登录 | 39 | // 登录 |
| 5 | export function loginApi(data: LoginForm): Promise<LoginResponse> { | 40 | export function loginApi(data: LoginForm): Promise<LoginResponse> { |
| 6 | - return request.post('/api/auth/login', data) | 41 | + return api.post('/auth/login', data) |
| 7 | } | 42 | } |
| 8 | 43 | ||
| 9 | // 登出 | 44 | // 登出 |
| 10 | export function logoutApi(): Promise<void> { | 45 | export function logoutApi(): Promise<void> { |
| 11 | - return request.post('/api/auth/logout') | 46 | + return api.post('/auth/logout') |
| 12 | } | 47 | } |
| 13 | 48 | ||
| 14 | // 获取用户信息 | 49 | // 获取用户信息 |
| 15 | export function getUserInfoApi(): Promise<User> { | 50 | export function getUserInfoApi(): Promise<User> { |
| 16 | - return request.get('/api/auth/userInfo') | 51 | + return api.get('/auth/userInfo') |
| 17 | } | 52 | } |
| 18 | 53 | ||
| 19 | // 刷新Token | 54 | // 刷新Token |
| 20 | export function refreshTokenApi(): Promise<{ token: string }> { | 55 | export function refreshTokenApi(): Promise<{ token: string }> { |
| 21 | - return request.post('/api/auth/refresh') | 56 | + return api.post('/auth/refresh') |
| 22 | } | 57 | } |
| 23 | 58 | ||
| 24 | // 修改密码 | 59 | // 修改密码 |
| ... | @@ -26,10 +61,10 @@ export function changePasswordApi(data: { | ... | @@ -26,10 +61,10 @@ export function changePasswordApi(data: { |
| 26 | oldPassword: string | 61 | oldPassword: string |
| 27 | newPassword: string | 62 | newPassword: string |
| 28 | }): Promise<void> { | 63 | }): Promise<void> { |
| 29 | - return request.post('/api/auth/changePassword', data) | 64 | + return api.post('/auth/changePassword', data) |
| 30 | } | 65 | } |
| 31 | 66 | ||
| 32 | // 获取验证码 | 67 | // 获取验证码 |
| 33 | export function getCaptchaApi(): Promise<{ captchaId: string; captchaImage: string }> { | 68 | export function getCaptchaApi(): Promise<{ captchaId: string; captchaImage: string }> { |
| 34 | - return request.get('/api/auth/captcha') | 69 | + return api.get('/auth/captcha') |
| 35 | } | 70 | } | ... | ... |
frontend/src/api/dealer.ts
0 → 100644
| 1 | +import axios from 'axios' | ||
| 2 | + | ||
| 3 | +// 创建axios实例 | ||
| 4 | +const api = axios.create({ | ||
| 5 | + baseURL: 'http://localhost:8083/api', | ||
| 6 | + timeout: 10000 | ||
| 7 | +}) | ||
| 8 | + | ||
| 9 | +// 请求拦截器 | ||
| 10 | +api.interceptors.request.use( | ||
| 11 | + (config) => { | ||
| 12 | + const token = localStorage.getItem('token') | ||
| 13 | + if (token) { | ||
| 14 | + config.headers.Authorization = `Bearer ${token}` | ||
| 15 | + } | ||
| 16 | + return config | ||
| 17 | + }, | ||
| 18 | + (error) => { | ||
| 19 | + return Promise.reject(error) | ||
| 20 | + } | ||
| 21 | +) | ||
| 22 | + | ||
| 23 | +// 响应拦截器 | ||
| 24 | +api.interceptors.response.use( | ||
| 25 | + (response) => { | ||
| 26 | + return response.data | ||
| 27 | + }, | ||
| 28 | + (error) => { | ||
| 29 | + if (error.response?.status === 401) { | ||
| 30 | + localStorage.removeItem('token') | ||
| 31 | + localStorage.removeItem('userInfo') | ||
| 32 | + window.location.href = '/login' | ||
| 33 | + } | ||
| 34 | + return Promise.reject(error) | ||
| 35 | + } | ||
| 36 | +) | ||
| 37 | + | ||
| 38 | +// 经销商信息接口 | ||
| 39 | +export interface DealerInfo { | ||
| 40 | + dealerId: number | ||
| 41 | + dealerCode: string | ||
| 42 | + dealerName: string | ||
| 43 | + creditCode: string | ||
| 44 | + dealerLevel: number | ||
| 45 | + region: string | ||
| 46 | + contactPerson: string | ||
| 47 | + contactPhone: string | ||
| 48 | + cooperateStartDate: string | ||
| 49 | + cooperateStatus: number | ||
| 50 | + businessLicenseUrl: string | ||
| 51 | + cooperationAgreementUrl: string | ||
| 52 | + qualificationAuditStatus: number | ||
| 53 | + auditOpinion: string | ||
| 54 | + createTime: string | ||
| 55 | + updateTime: string | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +// 经销商查询请求参数 | ||
| 59 | +export interface DealerQueryReq { | ||
| 60 | + dealerCode?: string | ||
| 61 | + dealerName?: string | ||
| 62 | + creditCode?: string | ||
| 63 | + dealerLevel?: number | ||
| 64 | + region?: string | ||
| 65 | + contactPerson?: string | ||
| 66 | + contactPhone?: string | ||
| 67 | + cooperateStatus?: number | ||
| 68 | + qualificationAuditStatus?: number | ||
| 69 | + pageNum: number | ||
| 70 | + pageSize: number | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +// 经销商新增请求参数 | ||
| 74 | +export interface DealerAddReq { | ||
| 75 | + dealerCode: string | ||
| 76 | + dealerName: string | ||
| 77 | + creditCode: string | ||
| 78 | + dealerLevel: number | ||
| 79 | + region: string | ||
| 80 | + contactPerson?: string | ||
| 81 | + contactPhone?: string | ||
| 82 | + cooperateStartDate: string | ||
| 83 | + cooperateStatus?: number | ||
| 84 | + businessLicenseUrl?: string | ||
| 85 | + cooperationAgreementUrl?: string | ||
| 86 | + qualificationAuditStatus?: number | ||
| 87 | + auditOpinion?: string | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +// 经销商修改请求参数 | ||
| 91 | +export interface DealerUpdateReq { | ||
| 92 | + dealerId: number | ||
| 93 | + dealerCode: string | ||
| 94 | + dealerName: string | ||
| 95 | + creditCode: string | ||
| 96 | + dealerLevel: number | ||
| 97 | + region: string | ||
| 98 | + contactPerson?: string | ||
| 99 | + contactPhone?: string | ||
| 100 | + cooperateStartDate: string | ||
| 101 | + cooperateStatus?: number | ||
| 102 | + businessLicenseUrl?: string | ||
| 103 | + cooperationAgreementUrl?: string | ||
| 104 | + qualificationAuditStatus?: number | ||
| 105 | + auditOpinion?: string | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +// 分页数据接口 | ||
| 109 | +export interface PageData<T> { | ||
| 110 | + records: T[] | ||
| 111 | + total: number | ||
| 112 | + pageNum: number | ||
| 113 | + pageSize: number | ||
| 114 | +} | ||
| 115 | + | ||
| 116 | +// API响应接口 | ||
| 117 | +export interface ApiResponse<T = any> { | ||
| 118 | + code: number | ||
| 119 | + message: string | ||
| 120 | + data: T | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +// 经销商管理API | ||
| 124 | +export const dealerApi = { | ||
| 125 | + // 分页查询经销商列表 | ||
| 126 | + getDealerList: (params: DealerQueryReq) => { | ||
| 127 | + return api.get<ApiResponse<PageData<DealerInfo>>>('/dealer/list', { params }) | ||
| 128 | + }, | ||
| 129 | + | ||
| 130 | + // 获取经销商详情 | ||
| 131 | + getDealerDetail: (dealerId: number) => { | ||
| 132 | + return api.get<ApiResponse<DealerInfo>>(`/dealer/${dealerId}`) | ||
| 133 | + }, | ||
| 134 | + | ||
| 135 | + // 新增经销商 | ||
| 136 | + addDealer: (data: DealerAddReq) => { | ||
| 137 | + return api.post<ApiResponse>('/dealer', data) | ||
| 138 | + }, | ||
| 139 | + | ||
| 140 | + // 修改经销商 | ||
| 141 | + updateDealer: (data: DealerUpdateReq) => { | ||
| 142 | + return api.post<ApiResponse>('/dealer/update', data) | ||
| 143 | + }, | ||
| 144 | + | ||
| 145 | + // 删除经销商 | ||
| 146 | + deleteDealer: (dealerId: number) => { | ||
| 147 | + return api.delete<ApiResponse>(`/dealer/${dealerId}`) | ||
| 148 | + }, | ||
| 149 | + | ||
| 150 | + // 批量删除经销商 | ||
| 151 | + batchDeleteDealer: (dealerIds: number[]) => { | ||
| 152 | + return api.delete<ApiResponse>('/dealer/batch', { data: dealerIds }) | ||
| 153 | + }, | ||
| 154 | + | ||
| 155 | + // 修改经销商合作状态 | ||
| 156 | + updateCooperateStatus: (dealerId: number, cooperateStatus: number) => { | ||
| 157 | + return api.post<ApiResponse>(`/dealer/${dealerId}/cooperateStatus/${cooperateStatus}`) | ||
| 158 | + }, | ||
| 159 | + | ||
| 160 | + // 修改经销商资质审核状态 | ||
| 161 | + updateAuditStatus: (dealerId: number, qualificationAuditStatus: number, auditOpinion?: string) => { | ||
| 162 | + return api.post<ApiResponse>(`/dealer/${dealerId}/auditStatus`, null, { | ||
| 163 | + params: { qualificationAuditStatus, auditOpinion } | ||
| 164 | + }) | ||
| 165 | + } | ||
| 166 | +} |
frontend/src/api/product.ts
0 → 100644
| 1 | +import axios from 'axios' | ||
| 2 | + | ||
| 3 | +// 创建axios实例 | ||
| 4 | +const api = axios.create({ | ||
| 5 | + baseURL: 'http://localhost:8083/api', | ||
| 6 | + timeout: 10000 | ||
| 7 | +}) | ||
| 8 | + | ||
| 9 | +// 请求拦截器 | ||
| 10 | +api.interceptors.request.use( | ||
| 11 | + config => { | ||
| 12 | + const token = localStorage.getItem('token') | ||
| 13 | + if (token) { | ||
| 14 | + config.headers.Authorization = `Bearer ${token}` | ||
| 15 | + } | ||
| 16 | + return config | ||
| 17 | + }, | ||
| 18 | + error => { | ||
| 19 | + return Promise.reject(error) | ||
| 20 | + } | ||
| 21 | +) | ||
| 22 | + | ||
| 23 | +// 响应拦截器 | ||
| 24 | +api.interceptors.response.use( | ||
| 25 | + response => { | ||
| 26 | + return response.data | ||
| 27 | + }, | ||
| 28 | + error => { | ||
| 29 | + if (error.response?.status === 401) { | ||
| 30 | + localStorage.removeItem('token') | ||
| 31 | + localStorage.removeItem('userInfo') | ||
| 32 | + window.location.href = '/login' | ||
| 33 | + } | ||
| 34 | + return Promise.reject(error) | ||
| 35 | + } | ||
| 36 | +) | ||
| 37 | + | ||
| 38 | +// 产品信息接口 | ||
| 39 | +export interface ProductInfo { | ||
| 40 | + productId: number | ||
| 41 | + productCode: string | ||
| 42 | + productName: string | ||
| 43 | + productModel: string | ||
| 44 | + productType: string | ||
| 45 | + storageCapacity: string | ||
| 46 | + color: string | ||
| 47 | + officialPrice: number | ||
| 48 | + saleStatus: number | ||
| 49 | + rebateFlag: number | ||
| 50 | + saleStartDate: string | ||
| 51 | + saleEndDate: string | ||
| 52 | + imageUrl: string | ||
| 53 | + remark: string | ||
| 54 | + createTime: string | ||
| 55 | + updateTime: string | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +// 产品查询请求参数 | ||
| 59 | +export interface ProductQueryReq { | ||
| 60 | + productCode?: string | ||
| 61 | + productName?: string | ||
| 62 | + productModel?: string | ||
| 63 | + saleStatus?: number | ||
| 64 | + rebateFlag?: number | ||
| 65 | + pageNum: number | ||
| 66 | + pageSize: number | ||
| 67 | +} | ||
| 68 | + | ||
| 69 | +// 产品新增请求参数 | ||
| 70 | +export interface ProductAddReq { | ||
| 71 | + productCode: string | ||
| 72 | + productName: string | ||
| 73 | + productModel: string | ||
| 74 | + productType: string | ||
| 75 | + storageCapacity: string | ||
| 76 | + color: string | ||
| 77 | + officialPrice: number | ||
| 78 | + saleStatus: number | ||
| 79 | + rebateFlag: number | ||
| 80 | + saleStartDate: string | ||
| 81 | + saleEndDate: string | ||
| 82 | + imageUrl?: string | ||
| 83 | + remark?: string | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +// 产品修改请求参数 | ||
| 87 | +export interface ProductUpdateReq { | ||
| 88 | + productId: number | ||
| 89 | + productCode: string | ||
| 90 | + productName: string | ||
| 91 | + productModel: string | ||
| 92 | + productType: string | ||
| 93 | + storageCapacity: string | ||
| 94 | + color: string | ||
| 95 | + officialPrice: number | ||
| 96 | + saleStatus: number | ||
| 97 | + rebateFlag: number | ||
| 98 | + saleStartDate: string | ||
| 99 | + saleEndDate: string | ||
| 100 | + imageUrl?: string | ||
| 101 | + remark?: string | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +// 分页数据接口 | ||
| 105 | +export interface PageData<T> { | ||
| 106 | + records: T[] | ||
| 107 | + total: number | ||
| 108 | + current: number | ||
| 109 | + size: number | ||
| 110 | + pages: number | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | +// API响应接口 | ||
| 114 | +export interface ApiResponse<T> { | ||
| 115 | + code: number | ||
| 116 | + message: string | ||
| 117 | + data: T | ||
| 118 | +} | ||
| 119 | + | ||
| 120 | +// API接口 | ||
| 121 | +export const productApi = { | ||
| 122 | + // 获取产品列表 | ||
| 123 | + getProductList: (params: ProductQueryReq) => { | ||
| 124 | + return api.get<ApiResponse<PageData<ProductInfo>>>('/product/list', { params }) | ||
| 125 | + }, | ||
| 126 | + | ||
| 127 | + // 获取产品详情 | ||
| 128 | + getProductDetail: (productId: number) => { | ||
| 129 | + return api.get<ProductInfo>(`/product/${productId}`) | ||
| 130 | + }, | ||
| 131 | + | ||
| 132 | + // 新增产品 | ||
| 133 | + addProduct: (data: ProductAddReq) => { | ||
| 134 | + return api.post('/product', data) | ||
| 135 | + }, | ||
| 136 | + | ||
| 137 | + // 修改产品 | ||
| 138 | + updateProduct: (data: ProductUpdateReq) => { | ||
| 139 | + return api.post('/product/update', data) | ||
| 140 | + }, | ||
| 141 | + | ||
| 142 | + // 删除产品 | ||
| 143 | + deleteProduct: (productId: number) => { | ||
| 144 | + return api.delete(`/product/${productId}`) | ||
| 145 | + }, | ||
| 146 | + | ||
| 147 | + // 批量删除产品 | ||
| 148 | + batchDeleteProduct: (productIds: number[]) => { | ||
| 149 | + return api.post('/product/batchDelete', productIds) | ||
| 150 | + }, | ||
| 151 | + | ||
| 152 | + // 修改销售状态 | ||
| 153 | + updateSaleStatus: (productId: number, saleStatus: number) => { | ||
| 154 | + return api.post(`/product/${productId}/status`, null, { params: { saleStatus } }) | ||
| 155 | + }, | ||
| 156 | + | ||
| 157 | + // 修改返利标识 | ||
| 158 | + updateRebateFlag: (productId: number, rebateFlag: number) => { | ||
| 159 | + return api.post(`/product/${productId}/rebate`, null, { params: { rebateFlag } }) | ||
| 160 | + } | ||
| 161 | +} |
| ... | @@ -124,6 +124,7 @@ | ... | @@ -124,6 +124,7 @@ |
| 124 | <script setup lang="ts"> | 124 | <script setup lang="ts"> |
| 125 | import { ref, computed, onMounted, watch, nextTick } from 'vue' | 125 | import { ref, computed, onMounted, watch, nextTick } from 'vue' |
| 126 | import { useRouter, useRoute } from 'vue-router' | 126 | import { useRouter, useRoute } from 'vue-router' |
| 127 | +import { logoutApi } from '../api/auth' | ||
| 127 | 128 | ||
| 128 | const router = useRouter() | 129 | const router = useRouter() |
| 129 | const route = useRoute() | 130 | const route = useRoute() |
| ... | @@ -140,6 +141,8 @@ const tabContainer = ref<HTMLElement>() | ... | @@ -140,6 +141,8 @@ const tabContainer = ref<HTMLElement>() |
| 140 | // 菜单项配置 | 141 | // 菜单项配置 |
| 141 | const menuItems = ref([ | 142 | const menuItems = ref([ |
| 142 | { name: '首页', path: '/main/dashboard', icon: '🏠' }, | 143 | { name: '首页', path: '/main/dashboard', icon: '🏠' }, |
| 144 | + { name: '产品管理', path: '/main/product', icon: '📦' }, | ||
| 145 | + { name: '经销商管理', path: '/main/dealer', icon: '🏢' }, | ||
| 143 | { | 146 | { |
| 144 | name: '系统设置', | 147 | name: '系统设置', |
| 145 | icon: '⚙️', | 148 | icon: '⚙️', |
| ... | @@ -314,15 +317,22 @@ const toggleSidebar = () => { | ... | @@ -314,15 +317,22 @@ const toggleSidebar = () => { |
| 314 | } | 317 | } |
| 315 | 318 | ||
| 316 | // 退出登录 | 319 | // 退出登录 |
| 317 | -const handleLogout = () => { | 320 | +const handleLogout = async () => { |
| 318 | if (confirm('确定要退出登录吗?')) { | 321 | if (confirm('确定要退出登录吗?')) { |
| 319 | - // 清除本地存储 | 322 | + try { |
| 323 | + // 调用后端退出登录接口 | ||
| 324 | + await logoutApi() | ||
| 325 | + } catch (error) { | ||
| 326 | + console.error('退出登录接口调用失败:', error) | ||
| 327 | + } finally { | ||
| 328 | + // 无论接口调用成功与否,都清除本地存储 | ||
| 320 | localStorage.removeItem('token') | 329 | localStorage.removeItem('token') |
| 321 | localStorage.removeItem('userInfo') | 330 | localStorage.removeItem('userInfo') |
| 322 | 331 | ||
| 323 | // 跳转到登录页 | 332 | // 跳转到登录页 |
| 324 | router.push('/') | 333 | router.push('/') |
| 325 | } | 334 | } |
| 335 | + } | ||
| 326 | } | 336 | } |
| 327 | 337 | ||
| 328 | // 监听路由变化,自动管理标签页 | 338 | // 监听路由变化,自动管理标签页 | ... | ... |
| ... | @@ -45,6 +45,15 @@ const staticRoutes: RouteRecordRaw[] = [ | ... | @@ -45,6 +45,15 @@ const staticRoutes: RouteRecordRaw[] = [ |
| 45 | } | 45 | } |
| 46 | }, | 46 | }, |
| 47 | { | 47 | { |
| 48 | + path: 'product', | ||
| 49 | + name: 'Product', | ||
| 50 | + component: () => import('@/views/product/index.vue'), | ||
| 51 | + meta: { | ||
| 52 | + title: '产品管理', | ||
| 53 | + requiresAuth: true | ||
| 54 | + } | ||
| 55 | + }, | ||
| 56 | + { | ||
| 48 | path: 'sys/role', | 57 | path: 'sys/role', |
| 49 | name: 'Role', | 58 | name: 'Role', |
| 50 | component: () => import('@/views/sys/role/index.vue'), | 59 | component: () => import('@/views/sys/role/index.vue'), |
| ... | @@ -108,6 +117,24 @@ const staticRoutes: RouteRecordRaw[] = [ | ... | @@ -108,6 +117,24 @@ const staticRoutes: RouteRecordRaw[] = [ |
| 108 | } | 117 | } |
| 109 | }, | 118 | }, |
| 110 | { | 119 | { |
| 120 | + path: 'product', | ||
| 121 | + name: 'Product', | ||
| 122 | + component: () => import('@/views/product/index.vue'), | ||
| 123 | + meta: { | ||
| 124 | + title: '产品管理', | ||
| 125 | + requiresAuth: true | ||
| 126 | + } | ||
| 127 | + }, | ||
| 128 | + { | ||
| 129 | + path: 'dealer', | ||
| 130 | + name: 'Dealer', | ||
| 131 | + component: () => import('@/views/dealer/index.vue'), | ||
| 132 | + meta: { | ||
| 133 | + title: '经销商管理', | ||
| 134 | + requiresAuth: true | ||
| 135 | + } | ||
| 136 | + }, | ||
| 137 | + { | ||
| 111 | path: 'settings', | 138 | path: 'settings', |
| 112 | name: 'Settings', | 139 | name: 'Settings', |
| 113 | component: () => import('@/views/settings/index.vue'), | 140 | component: () => import('@/views/settings/index.vue'), | ... | ... |
frontend/src/views/dealer/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="dealer-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.dealerCode" | ||
| 10 | + type="text" | ||
| 11 | + class="search-input" | ||
| 12 | + placeholder="请输入经销商编码" | ||
| 13 | + /> | ||
| 14 | + </div> | ||
| 15 | + <div class="search-item"> | ||
| 16 | + <label>经销商名称:</label> | ||
| 17 | + <input | ||
| 18 | + v-model="searchParams.dealerName" | ||
| 19 | + type="text" | ||
| 20 | + class="search-input" | ||
| 21 | + placeholder="请输入经销商名称" | ||
| 22 | + /> | ||
| 23 | + </div> | ||
| 24 | + <div class="search-item"> | ||
| 25 | + <label>经销商等级:</label> | ||
| 26 | + <select v-model="searchParams.dealerLevel" class="search-select"> | ||
| 27 | + <option value="">所有</option> | ||
| 28 | + <option value="1">一级经销商</option> | ||
| 29 | + <option value="2">二级经销商</option> | ||
| 30 | + </select> | ||
| 31 | + </div> | ||
| 32 | + <div class="search-item"> | ||
| 33 | + <label>合作状态:</label> | ||
| 34 | + <select v-model="searchParams.cooperateStatus" class="search-select"> | ||
| 35 | + <option value="">所有</option> | ||
| 36 | + <option value="1">正常合作</option> | ||
| 37 | + <option value="2">暂停合作</option> | ||
| 38 | + <option value="3">终止合作</option> | ||
| 39 | + </select> | ||
| 40 | + </div> | ||
| 41 | + <div class="search-actions"> | ||
| 42 | + <button @click="handleSearch" class="search-btn">🔍 搜索</button> | ||
| 43 | + <button @click="handleReset" class="reset-btn">🔄 重置</button> | ||
| 44 | + </div> | ||
| 45 | + </div> | ||
| 46 | + </div> | ||
| 47 | + | ||
| 48 | + <!-- 操作按钮区域 --> | ||
| 49 | + <div class="action-section"> | ||
| 50 | + <div class="action-buttons"> | ||
| 51 | + <button @click="handleAdd" class="action-btn primary">✨ 新增</button> | ||
| 52 | + <button @click="handleBatchDelete" class="action-btn danger">🗑️ 删除</button> | ||
| 53 | + </div> | ||
| 54 | + </div> | ||
| 55 | + | ||
| 56 | + <!-- 数据表格 --> | ||
| 57 | + <div class="table-section"> | ||
| 58 | + <div class="table-header"> | ||
| 59 | + <div class="table-controls"> | ||
| 60 | + <button @click="handleTableSearch" class="control-btn" title="搜索">🔍</button> | ||
| 61 | + <button @click="handleTableRefresh" class="control-btn" title="刷新">🔄</button> | ||
| 62 | + <button @click="handleTableExport" class="control-btn" title="导出">📋</button> | ||
| 63 | + <button @click="handleTableViewToggle" class="control-btn" title="视图切换">⊞</button> | ||
| 64 | + </div> | ||
| 65 | + </div> | ||
| 66 | + | ||
| 67 | + <div class="table-container"> | ||
| 68 | + <div v-if="loading" class="loading-overlay"> | ||
| 69 | + <div class="loading-spinner">加载中...</div> | ||
| 70 | + </div> | ||
| 71 | + <table class="data-table"> | ||
| 72 | + <thead> | ||
| 73 | + <tr> | ||
| 74 | + <th> | ||
| 75 | + <input | ||
| 76 | + type="checkbox" | ||
| 77 | + class="select-all" | ||
| 78 | + v-model="selectAll" | ||
| 79 | + @change="handleSelectAll" | ||
| 80 | + /> | ||
| 81 | + </th> | ||
| 82 | + <th>经销商编码</th> | ||
| 83 | + <th>经销商名称</th> | ||
| 84 | + <th>统一社会信用代码</th> | ||
| 85 | + <th>经销商等级</th> | ||
| 86 | + <th>所在区域</th> | ||
| 87 | + <th>联系人</th> | ||
| 88 | + <th>联系电话</th> | ||
| 89 | + <th>合作起始日期</th> | ||
| 90 | + <th>合作状态</th> | ||
| 91 | + <th>操作</th> | ||
| 92 | + </tr> | ||
| 93 | + </thead> | ||
| 94 | + <tbody> | ||
| 95 | + <tr v-for="dealer in dealerList" :key="dealer.dealerId"> | ||
| 96 | + <td> | ||
| 97 | + <input | ||
| 98 | + type="checkbox" | ||
| 99 | + :value="dealer.dealerId" | ||
| 100 | + v-model="selectedDealers" | ||
| 101 | + /> | ||
| 102 | + </td> | ||
| 103 | + <td>{{ dealer.dealerCode }}</td> | ||
| 104 | + <td>{{ dealer.dealerName }}</td> | ||
| 105 | + <td>{{ dealer.creditCode }}</td> | ||
| 106 | + <td>{{ getDealerLevelText(dealer.dealerLevel) }}</td> | ||
| 107 | + <td>{{ dealer.region }}</td> | ||
| 108 | + <td>{{ dealer.contactPerson }}</td> | ||
| 109 | + <td>{{ dealer.contactPhone }}</td> | ||
| 110 | + <td>{{ dealer.cooperateStartDate }}</td> | ||
| 111 | + <td> | ||
| 112 | + <span :class="getCooperateStatusClass(dealer.cooperateStatus)"> | ||
| 113 | + {{ getCooperateStatusText(dealer.cooperateStatus) }} | ||
| 114 | + </span> | ||
| 115 | + </td> | ||
| 116 | + <td> | ||
| 117 | + <button @click="handleEdit(dealer)" class="table-btn edit">✏️ 编辑</button> | ||
| 118 | + <button @click="handleDelete(dealer)" class="table-btn delete">🗑️ 删除</button> | ||
| 119 | + <button @click="handleStatusChange(dealer)" class="table-btn status">🔄 状态</button> | ||
| 120 | + </td> | ||
| 121 | + </tr> | ||
| 122 | + </tbody> | ||
| 123 | + </table> | ||
| 124 | + </div> | ||
| 125 | + | ||
| 126 | + <!-- 分页信息 --> | ||
| 127 | + <div class="table-footer"> | ||
| 128 | + <div class="pagination-left"> | ||
| 129 | + <div class="pagination-info"> | ||
| 130 | + 共 {{ total || 0 }} 条记录,第 {{ currentPage || 1 }} / {{ totalPages || 1 }} 页 | ||
| 131 | + </div> | ||
| 132 | + <div class="page-size-selector"> | ||
| 133 | + <label>每页显示:</label> | ||
| 134 | + <select v-model="pagination.pageSize" @change="handlePageSizeChange" class="page-size-select"> | ||
| 135 | + <option value="10">10条</option> | ||
| 136 | + <option value="20">20条</option> | ||
| 137 | + <option value="50">50条</option> | ||
| 138 | + <option value="100">100条</option> | ||
| 139 | + </select> | ||
| 140 | + </div> | ||
| 141 | + </div> | ||
| 142 | + <div class="pagination-container"> | ||
| 143 | + <button | ||
| 144 | + @click="handlePageChange(currentPage - 1)" | ||
| 145 | + :disabled="currentPage <= 1" | ||
| 146 | + class="pagination-btn prev-btn" | ||
| 147 | + > | ||
| 148 | + 上一页 | ||
| 149 | + </button> | ||
| 150 | + <div class="pagination-pages"> | ||
| 151 | + <button | ||
| 152 | + v-for="page in getPageNumbers()" | ||
| 153 | + :key="page" | ||
| 154 | + @click="handlePageChange(page)" | ||
| 155 | + :class="['page-number', { active: page === currentPage }]" | ||
| 156 | + > | ||
| 157 | + {{ page }} | ||
| 158 | + </button> | ||
| 159 | + </div> | ||
| 160 | + <button | ||
| 161 | + @click="handlePageChange(currentPage + 1)" | ||
| 162 | + :disabled="currentPage >= totalPages" | ||
| 163 | + class="pagination-btn next-btn" | ||
| 164 | + > | ||
| 165 | + 下一页 | ||
| 166 | + </button> | ||
| 167 | + </div> | ||
| 168 | + </div> | ||
| 169 | + </div> | ||
| 170 | + | ||
| 171 | + <!-- 新增/编辑对话框 --> | ||
| 172 | + <div v-if="showDialog" class="dialog-overlay" @click="handleDialogClose"> | ||
| 173 | + <div class="dialog-content" @click.stop> | ||
| 174 | + <div class="dialog-header"> | ||
| 175 | + <h3>{{ isEdit ? '编辑经销商' : '新增经销商' }}</h3> | ||
| 176 | + <button @click="handleDialogClose" class="dialog-close">×</button> | ||
| 177 | + </div> | ||
| 178 | + <div class="dialog-body"> | ||
| 179 | + <form @submit.prevent="handleSubmit" class="form-container"> | ||
| 180 | + <div class="form-row"> | ||
| 181 | + <div class="form-item"> | ||
| 182 | + <label class="required">经销商编码:</label> | ||
| 183 | + <input | ||
| 184 | + v-model="formData.dealerCode" | ||
| 185 | + class="form-input" | ||
| 186 | + placeholder="请输入经销商编码(6-20位大写字母和数字)" | ||
| 187 | + pattern="^[A-Z0-9]{6,20}$" | ||
| 188 | + title="经销商编码应为6-20位大写字母和数字" | ||
| 189 | + required | ||
| 190 | + /> | ||
| 191 | + </div> | ||
| 192 | + <div class="form-item"> | ||
| 193 | + <label class="required">经销商名称:</label> | ||
| 194 | + <input | ||
| 195 | + v-model="formData.dealerName" | ||
| 196 | + class="form-input" | ||
| 197 | + placeholder="请输入经销商名称" | ||
| 198 | + required | ||
| 199 | + /> | ||
| 200 | + </div> | ||
| 201 | + </div> | ||
| 202 | + <div class="form-row"> | ||
| 203 | + <div class="form-item"> | ||
| 204 | + <label class="required">统一社会信用代码:</label> | ||
| 205 | + <input | ||
| 206 | + v-model="formData.creditCode" | ||
| 207 | + class="form-input" | ||
| 208 | + placeholder="请输入统一社会信用代码" | ||
| 209 | + required | ||
| 210 | + /> | ||
| 211 | + </div> | ||
| 212 | + <div class="form-item"> | ||
| 213 | + <label class="required">经销商等级:</label> | ||
| 214 | + <select v-model="formData.dealerLevel" class="form-select" required> | ||
| 215 | + <option value="">请选择</option> | ||
| 216 | + <option value="1">一级经销商</option> | ||
| 217 | + <option value="2">二级经销商</option> | ||
| 218 | + </select> | ||
| 219 | + </div> | ||
| 220 | + </div> | ||
| 221 | + <div class="form-row"> | ||
| 222 | + <div class="form-item"> | ||
| 223 | + <label class="required">所在区域:</label> | ||
| 224 | + <input | ||
| 225 | + v-model="formData.region" | ||
| 226 | + class="form-input" | ||
| 227 | + placeholder="请输入所在区域" | ||
| 228 | + required | ||
| 229 | + /> | ||
| 230 | + </div> | ||
| 231 | + <div class="form-item"> | ||
| 232 | + <label>联系人:</label> | ||
| 233 | + <input | ||
| 234 | + v-model="formData.contactPerson" | ||
| 235 | + class="form-input" | ||
| 236 | + placeholder="请输入联系人" | ||
| 237 | + /> | ||
| 238 | + </div> | ||
| 239 | + </div> | ||
| 240 | + <div class="form-row"> | ||
| 241 | + <div class="form-item"> | ||
| 242 | + <label>联系电话:</label> | ||
| 243 | + <input | ||
| 244 | + v-model="formData.contactPhone" | ||
| 245 | + class="form-input" | ||
| 246 | + placeholder="请输入联系电话" | ||
| 247 | + /> | ||
| 248 | + </div> | ||
| 249 | + <div class="form-item"> | ||
| 250 | + <label class="required">合作起始日期:</label> | ||
| 251 | + <input | ||
| 252 | + v-model="formData.cooperateStartDate" | ||
| 253 | + class="form-input" | ||
| 254 | + type="date" | ||
| 255 | + required | ||
| 256 | + /> | ||
| 257 | + </div> | ||
| 258 | + </div> | ||
| 259 | + <div class="form-row"> | ||
| 260 | + <div class="form-item"> | ||
| 261 | + <label>合作状态:</label> | ||
| 262 | + <select v-model="formData.cooperateStatus" class="form-select"> | ||
| 263 | + <option value="1">正常合作</option> | ||
| 264 | + <option value="2">暂停合作</option> | ||
| 265 | + <option value="3">终止合作</option> | ||
| 266 | + </select> | ||
| 267 | + </div> | ||
| 268 | + <div class="form-item"> | ||
| 269 | + <label>营业执照URL:</label> | ||
| 270 | + <input | ||
| 271 | + v-model="formData.businessLicenseUrl" | ||
| 272 | + class="form-input" | ||
| 273 | + placeholder="请输入营业执照URL" | ||
| 274 | + /> | ||
| 275 | + </div> | ||
| 276 | + </div> | ||
| 277 | + <div class="form-row"> | ||
| 278 | + <div class="form-item"> | ||
| 279 | + <label>合作协议URL:</label> | ||
| 280 | + <input | ||
| 281 | + v-model="formData.cooperationAgreementUrl" | ||
| 282 | + class="form-input" | ||
| 283 | + placeholder="请输入合作协议URL" | ||
| 284 | + /> | ||
| 285 | + </div> | ||
| 286 | + <div class="form-item"> | ||
| 287 | + <label>审核意见:</label> | ||
| 288 | + <input | ||
| 289 | + v-model="formData.auditOpinion" | ||
| 290 | + class="form-input" | ||
| 291 | + placeholder="请输入审核意见" | ||
| 292 | + /> | ||
| 293 | + </div> | ||
| 294 | + </div> | ||
| 295 | + <div class="form-row"> | ||
| 296 | + <div class="form-item full-width"> | ||
| 297 | + <label>备注:</label> | ||
| 298 | + <textarea | ||
| 299 | + v-model="formData.remark" | ||
| 300 | + class="form-textarea" | ||
| 301 | + placeholder="请输入备注" | ||
| 302 | + rows="3" | ||
| 303 | + ></textarea> | ||
| 304 | + </div> | ||
| 305 | + </div> | ||
| 306 | + </form> | ||
| 307 | + </div> | ||
| 308 | + <div class="dialog-footer"> | ||
| 309 | + <button @click="handleDialogClose" class="dialog-btn cancel">取消</button> | ||
| 310 | + <button @click="handleSubmit" class="dialog-btn confirm">确定</button> | ||
| 311 | + </div> | ||
| 312 | + </div> | ||
| 313 | + </div> | ||
| 314 | + | ||
| 315 | + <!-- 状态修改对话框 --> | ||
| 316 | + <div v-if="showStatusDialog" class="dialog-overlay" @click="handleStatusDialogClose"> | ||
| 317 | + <div class="dialog-content" @click.stop> | ||
| 318 | + <div class="dialog-header"> | ||
| 319 | + <h3>修改合作状态</h3> | ||
| 320 | + <button @click="handleStatusDialogClose" class="dialog-close">×</button> | ||
| 321 | + </div> | ||
| 322 | + <div class="dialog-body"> | ||
| 323 | + <div class="form-item"> | ||
| 324 | + <label>经销商名称:</label> | ||
| 325 | + <input v-model="currentDealer.dealerName" class="form-input" readonly /> | ||
| 326 | + </div> | ||
| 327 | + <div class="form-item"> | ||
| 328 | + <label>当前状态:</label> | ||
| 329 | + <input :value="getCooperateStatusText(currentDealer.cooperateStatus)" class="form-input" readonly /> | ||
| 330 | + </div> | ||
| 331 | + <div class="form-item"> | ||
| 332 | + <label>新状态:</label> | ||
| 333 | + <select v-model="newCooperateStatus" class="form-select"> | ||
| 334 | + <option value="1">正常合作</option> | ||
| 335 | + <option value="2">暂停合作</option> | ||
| 336 | + <option value="3">终止合作</option> | ||
| 337 | + </select> | ||
| 338 | + </div> | ||
| 339 | + </div> | ||
| 340 | + <div class="dialog-footer"> | ||
| 341 | + <button @click="handleStatusDialogClose" class="dialog-btn cancel">取消</button> | ||
| 342 | + <button @click="handleStatusSubmit" class="dialog-btn confirm">确定</button> | ||
| 343 | + </div> | ||
| 344 | + </div> | ||
| 345 | + </div> | ||
| 346 | + | ||
| 347 | + </div> | ||
| 348 | +</template> | ||
| 349 | + | ||
| 350 | +<script setup lang="ts"> | ||
| 351 | +import { ref, reactive, computed, onMounted } from 'vue' | ||
| 352 | +import { dealerApi, type DealerInfo, type DealerQueryReq, type DealerAddReq, type DealerUpdateReq } from '../../api/dealer' | ||
| 353 | + | ||
| 354 | +// 响应式数据 | ||
| 355 | +const loading = ref(false) | ||
| 356 | +const dealerList = ref<DealerInfo[]>([]) | ||
| 357 | +const selectedDealers = ref<number[]>([]) | ||
| 358 | +const showDialog = ref(false) | ||
| 359 | +const isEdit = ref(false) | ||
| 360 | +const showStatusDialog = ref(false) | ||
| 361 | +const currentDealer = ref<DealerInfo>({} as DealerInfo) | ||
| 362 | +const newCooperateStatus = ref<number>(1) | ||
| 363 | + | ||
| 364 | +// 搜索参数 | ||
| 365 | +const searchParams = reactive<DealerQueryReq>({ | ||
| 366 | + dealerCode: '', | ||
| 367 | + dealerName: '', | ||
| 368 | + creditCode: '', | ||
| 369 | + dealerLevel: undefined, | ||
| 370 | + region: '', | ||
| 371 | + contactPerson: '', | ||
| 372 | + contactPhone: '', | ||
| 373 | + cooperateStatus: undefined, | ||
| 374 | + pageNum: 1, | ||
| 375 | + pageSize: 10 | ||
| 376 | +}) | ||
| 377 | + | ||
| 378 | +// 分页信息 | ||
| 379 | +const pagination = reactive({ | ||
| 380 | + total: 0, | ||
| 381 | + pageNum: 1, | ||
| 382 | + pageSize: 10 | ||
| 383 | +}) | ||
| 384 | + | ||
| 385 | +// 计算属性 | ||
| 386 | +const total = computed(() => pagination.total) | ||
| 387 | +const currentPage = computed(() => pagination.pageNum) | ||
| 388 | +const pageSize = computed(() => pagination.pageSize) | ||
| 389 | +const totalPages = computed(() => { | ||
| 390 | + if (pagination.pageSize <= 0) return 1 | ||
| 391 | + return Math.ceil(pagination.total / pagination.pageSize) || 1 | ||
| 392 | +}) | ||
| 393 | + | ||
| 394 | +// 表单数据 | ||
| 395 | +const formData = reactive<DealerAddReq & { dealerId?: number }>({ | ||
| 396 | + dealerCode: '', | ||
| 397 | + dealerName: '', | ||
| 398 | + creditCode: '', | ||
| 399 | + dealerLevel: 1, | ||
| 400 | + region: '', | ||
| 401 | + contactPerson: '', | ||
| 402 | + contactPhone: '', | ||
| 403 | + cooperateStartDate: '', | ||
| 404 | + cooperateStatus: 1, | ||
| 405 | + businessLicenseUrl: '', | ||
| 406 | + cooperationAgreementUrl: '', | ||
| 407 | + qualificationAuditStatus: 1, | ||
| 408 | + auditOpinion: '' | ||
| 409 | +}) | ||
| 410 | + | ||
| 411 | +// 计算属性 | ||
| 412 | +const selectAll = computed({ | ||
| 413 | + get: () => selectedDealers.value.length === dealerList.value.length && dealerList.value.length > 0, | ||
| 414 | + set: (value: boolean) => { | ||
| 415 | + if (value) { | ||
| 416 | + selectedDealers.value = dealerList.value.map(dealer => dealer.dealerId) | ||
| 417 | + } else { | ||
| 418 | + selectedDealers.value = [] | ||
| 419 | + } | ||
| 420 | + } | ||
| 421 | +}) | ||
| 422 | + | ||
| 423 | +// 获取经销商等级文本 | ||
| 424 | +const getDealerLevelText = (level: number) => { | ||
| 425 | + return level === 1 ? '一级经销商' : '二级经销商' | ||
| 426 | +} | ||
| 427 | + | ||
| 428 | +// 获取合作状态文本 | ||
| 429 | +const getCooperateStatusText = (status: number) => { | ||
| 430 | + const statusMap: { [key: number]: string } = { | ||
| 431 | + 1: '正常合作', | ||
| 432 | + 2: '暂停合作', | ||
| 433 | + 3: '终止合作' | ||
| 434 | + } | ||
| 435 | + return statusMap[status] || '未知' | ||
| 436 | +} | ||
| 437 | + | ||
| 438 | +// 获取合作状态样式 | ||
| 439 | +const getCooperateStatusClass = (status: number) => { | ||
| 440 | + const classMap: { [key: number]: string } = { | ||
| 441 | + 1: 'status-normal', | ||
| 442 | + 2: 'status-warning', | ||
| 443 | + 3: 'status-danger' | ||
| 444 | + } | ||
| 445 | + return classMap[status] || '' | ||
| 446 | +} | ||
| 447 | + | ||
| 448 | + | ||
| 449 | +// 获取分页页码数组 | ||
| 450 | +const getPageNumbers = () => { | ||
| 451 | + const pages = [] | ||
| 452 | + const totalPagesValue = totalPages.value | ||
| 453 | + if (totalPagesValue <= 0) return [] | ||
| 454 | + | ||
| 455 | + const start = Math.max(1, currentPage.value - 2) | ||
| 456 | + const end = Math.min(totalPagesValue, start + 4) | ||
| 457 | + | ||
| 458 | + for (let i = start; i <= end; i++) { | ||
| 459 | + pages.push(i) | ||
| 460 | + } | ||
| 461 | + return pages | ||
| 462 | +} | ||
| 463 | + | ||
| 464 | +// 获取经销商列表 | ||
| 465 | +const fetchDealers = async () => { | ||
| 466 | + try { | ||
| 467 | + loading.value = true | ||
| 468 | + const response = await dealerApi.getDealerList(searchParams) | ||
| 469 | + if (response.code === 200) { | ||
| 470 | + dealerList.value = response.data.records | ||
| 471 | + pagination.total = response.data.total | ||
| 472 | + pagination.pageNum = response.data.current | ||
| 473 | + pagination.pageSize = response.data.size | ||
| 474 | + } | ||
| 475 | + } catch (error) { | ||
| 476 | + console.error('获取经销商列表失败:', error) | ||
| 477 | + } finally { | ||
| 478 | + loading.value = false | ||
| 479 | + } | ||
| 480 | +} | ||
| 481 | + | ||
| 482 | +// 搜索 | ||
| 483 | +const handleSearch = () => { | ||
| 484 | + pagination.pageNum = 1 | ||
| 485 | + fetchDealers() | ||
| 486 | +} | ||
| 487 | + | ||
| 488 | +// 重置 | ||
| 489 | +const handleReset = () => { | ||
| 490 | + Object.assign(searchParams, { | ||
| 491 | + dealerCode: '', | ||
| 492 | + dealerName: '', | ||
| 493 | + creditCode: '', | ||
| 494 | + dealerLevel: undefined, | ||
| 495 | + region: '', | ||
| 496 | + contactPerson: '', | ||
| 497 | + contactPhone: '', | ||
| 498 | + cooperateStatus: undefined | ||
| 499 | + }) | ||
| 500 | + pagination.pageNum = 1 | ||
| 501 | + fetchDealers() | ||
| 502 | +} | ||
| 503 | + | ||
| 504 | +// 新增 | ||
| 505 | +const handleAdd = () => { | ||
| 506 | + isEdit.value = false | ||
| 507 | + Object.assign(formData, { | ||
| 508 | + dealerCode: '', | ||
| 509 | + dealerName: '', | ||
| 510 | + creditCode: '', | ||
| 511 | + dealerLevel: 1, | ||
| 512 | + region: '', | ||
| 513 | + contactPerson: '', | ||
| 514 | + contactPhone: '', | ||
| 515 | + cooperateStartDate: '', | ||
| 516 | + cooperateStatus: 1, | ||
| 517 | + businessLicenseUrl: '', | ||
| 518 | + cooperationAgreementUrl: '' | ||
| 519 | + }) | ||
| 520 | + showDialog.value = true | ||
| 521 | +} | ||
| 522 | + | ||
| 523 | +// 编辑 | ||
| 524 | +const handleEdit = (dealer: DealerInfo) => { | ||
| 525 | + isEdit.value = true | ||
| 526 | + Object.assign(formData, { | ||
| 527 | + dealerId: dealer.dealerId, | ||
| 528 | + dealerCode: dealer.dealerCode, | ||
| 529 | + dealerName: dealer.dealerName, | ||
| 530 | + creditCode: dealer.creditCode, | ||
| 531 | + dealerLevel: dealer.dealerLevel, | ||
| 532 | + region: dealer.region, | ||
| 533 | + contactPerson: dealer.contactPerson, | ||
| 534 | + contactPhone: dealer.contactPhone, | ||
| 535 | + cooperateStartDate: dealer.cooperateStartDate, | ||
| 536 | + cooperateStatus: dealer.cooperateStatus, | ||
| 537 | + businessLicenseUrl: dealer.businessLicenseUrl, | ||
| 538 | + cooperationAgreementUrl: dealer.cooperationAgreementUrl | ||
| 539 | + }) | ||
| 540 | + showDialog.value = true | ||
| 541 | +} | ||
| 542 | + | ||
| 543 | +// 删除 | ||
| 544 | +const handleDelete = async (dealer: DealerInfo) => { | ||
| 545 | + if (confirm(`确定要删除经销商"${dealer.dealerName}"吗?`)) { | ||
| 546 | + try { | ||
| 547 | + const response = await dealerApi.deleteDealer(dealer.dealerId) | ||
| 548 | + if (response.code === 200) { | ||
| 549 | + alert('删除成功') | ||
| 550 | + fetchDealers() | ||
| 551 | + } else { | ||
| 552 | + alert(response.message || '删除失败') | ||
| 553 | + } | ||
| 554 | + } catch (error: any) { | ||
| 555 | + console.error('删除失败:', error) | ||
| 556 | + | ||
| 557 | + // 处理网络错误或服务器错误 | ||
| 558 | + if (error.response && error.response.data) { | ||
| 559 | + const errorData = error.response.data | ||
| 560 | + let errorMessage = errorData.message || '删除失败' | ||
| 561 | + | ||
| 562 | + // 如果有具体的字段验证错误,显示详细错误信息 | ||
| 563 | + if (errorData.data && typeof errorData.data === 'object') { | ||
| 564 | + const fieldErrors = Object.values(errorData.data).filter((err: any) => typeof err === 'string') | ||
| 565 | + if (fieldErrors.length > 0) { | ||
| 566 | + errorMessage = fieldErrors.join('; ') | ||
| 567 | + } | ||
| 568 | + } | ||
| 569 | + | ||
| 570 | + alert(errorMessage) | ||
| 571 | + } else { | ||
| 572 | + alert('删除失败') | ||
| 573 | + } | ||
| 574 | + } | ||
| 575 | + } | ||
| 576 | +} | ||
| 577 | + | ||
| 578 | +// 批量删除 | ||
| 579 | +const handleBatchDelete = async () => { | ||
| 580 | + if (selectedDealers.value.length === 0) { | ||
| 581 | + alert('请选择要删除的经销商') | ||
| 582 | + return | ||
| 583 | + } | ||
| 584 | + | ||
| 585 | + if (confirm(`确定要删除选中的 ${selectedDealers.value.length} 个经销商吗?`)) { | ||
| 586 | + try { | ||
| 587 | + const response = await dealerApi.batchDeleteDealer(selectedDealers.value) | ||
| 588 | + if (response.code === 200) { | ||
| 589 | + alert('批量删除成功') | ||
| 590 | + selectedDealers.value = [] | ||
| 591 | + fetchDealers() | ||
| 592 | + } else { | ||
| 593 | + alert(response.message || '批量删除失败') | ||
| 594 | + } | ||
| 595 | + } catch (error: any) { | ||
| 596 | + console.error('批量删除失败:', error) | ||
| 597 | + | ||
| 598 | + // 处理网络错误或服务器错误 | ||
| 599 | + if (error.response && error.response.data) { | ||
| 600 | + const errorData = error.response.data | ||
| 601 | + let errorMessage = errorData.message || '批量删除失败' | ||
| 602 | + | ||
| 603 | + // 如果有具体的字段验证错误,显示详细错误信息 | ||
| 604 | + if (errorData.data && typeof errorData.data === 'object') { | ||
| 605 | + const fieldErrors = Object.values(errorData.data).filter((err: any) => typeof err === 'string') | ||
| 606 | + if (fieldErrors.length > 0) { | ||
| 607 | + errorMessage = fieldErrors.join('; ') | ||
| 608 | + } | ||
| 609 | + } | ||
| 610 | + | ||
| 611 | + alert(errorMessage) | ||
| 612 | + } else { | ||
| 613 | + alert('批量删除失败') | ||
| 614 | + } | ||
| 615 | + } | ||
| 616 | + } | ||
| 617 | +} | ||
| 618 | + | ||
| 619 | +// 状态修改 | ||
| 620 | +const handleStatusChange = (dealer: DealerInfo) => { | ||
| 621 | + currentDealer.value = dealer | ||
| 622 | + newCooperateStatus.value = dealer.cooperateStatus | ||
| 623 | + showStatusDialog.value = true | ||
| 624 | +} | ||
| 625 | + | ||
| 626 | + | ||
| 627 | +// 提交表单 | ||
| 628 | +const handleSubmit = async () => { | ||
| 629 | + try { | ||
| 630 | + // 前端验证经销商编码格式 | ||
| 631 | + const dealerCodePattern = /^[A-Z0-9]{6,20}$/ | ||
| 632 | + if (!dealerCodePattern.test(formData.dealerCode)) { | ||
| 633 | + alert('经销商编码格式不正确,应为6-20位大写字母和数字') | ||
| 634 | + return | ||
| 635 | + } | ||
| 636 | + | ||
| 637 | + let response | ||
| 638 | + if (isEdit.value) { | ||
| 639 | + response = await dealerApi.updateDealer(formData as DealerUpdateReq) | ||
| 640 | + } else { | ||
| 641 | + response = await dealerApi.addDealer(formData as DealerAddReq) | ||
| 642 | + } | ||
| 643 | + | ||
| 644 | + if (response.code === 200) { | ||
| 645 | + alert(isEdit.value ? '修改成功' : '新增成功') | ||
| 646 | + showDialog.value = false | ||
| 647 | + fetchDealers() | ||
| 648 | + } else { | ||
| 649 | + // 处理验证错误信息 | ||
| 650 | + let errorMessage = response.message || (isEdit.value ? '修改失败' : '新增失败') | ||
| 651 | + | ||
| 652 | + // 如果有具体的字段验证错误,显示详细错误信息 | ||
| 653 | + if (response.data && typeof response.data === 'object') { | ||
| 654 | + const fieldErrors = Object.values(response.data).filter(error => typeof error === 'string') | ||
| 655 | + if (fieldErrors.length > 0) { | ||
| 656 | + errorMessage = fieldErrors.join('; ') | ||
| 657 | + } | ||
| 658 | + } | ||
| 659 | + | ||
| 660 | + alert(errorMessage) | ||
| 661 | + } | ||
| 662 | + } catch (error: any) { | ||
| 663 | + console.error('提交失败:', error) | ||
| 664 | + | ||
| 665 | + // 处理网络错误或服务器错误 | ||
| 666 | + if (error.response && error.response.data) { | ||
| 667 | + const errorData = error.response.data | ||
| 668 | + let errorMessage = errorData.message || (isEdit.value ? '修改失败' : '新增失败') | ||
| 669 | + | ||
| 670 | + // 如果有具体的字段验证错误,显示详细错误信息 | ||
| 671 | + if (errorData.data && typeof errorData.data === 'object') { | ||
| 672 | + const fieldErrors = Object.values(errorData.data).filter((err: any) => typeof err === 'string') | ||
| 673 | + if (fieldErrors.length > 0) { | ||
| 674 | + errorMessage = fieldErrors.join('; ') | ||
| 675 | + } | ||
| 676 | + } | ||
| 677 | + | ||
| 678 | + alert(errorMessage) | ||
| 679 | + } else { | ||
| 680 | + alert(isEdit.value ? '修改失败' : '新增失败') | ||
| 681 | + } | ||
| 682 | + } | ||
| 683 | +} | ||
| 684 | + | ||
| 685 | +// 状态提交 | ||
| 686 | +const handleStatusSubmit = async () => { | ||
| 687 | + try { | ||
| 688 | + const response = await dealerApi.updateCooperateStatus(currentDealer.value.dealerId, newCooperateStatus.value) | ||
| 689 | + if (response.code === 200) { | ||
| 690 | + alert('状态修改成功') | ||
| 691 | + showStatusDialog.value = false | ||
| 692 | + fetchDealers() | ||
| 693 | + } else { | ||
| 694 | + alert(response.message || '状态修改失败') | ||
| 695 | + } | ||
| 696 | + } catch (error: any) { | ||
| 697 | + console.error('状态修改失败:', error) | ||
| 698 | + | ||
| 699 | + // 处理网络错误或服务器错误 | ||
| 700 | + if (error.response && error.response.data) { | ||
| 701 | + const errorData = error.response.data | ||
| 702 | + let errorMessage = errorData.message || '状态修改失败' | ||
| 703 | + | ||
| 704 | + // 如果有具体的字段验证错误,显示详细错误信息 | ||
| 705 | + if (errorData.data && typeof errorData.data === 'object') { | ||
| 706 | + const fieldErrors = Object.values(errorData.data).filter((err: any) => typeof err === 'string') | ||
| 707 | + if (fieldErrors.length > 0) { | ||
| 708 | + errorMessage = fieldErrors.join('; ') | ||
| 709 | + } | ||
| 710 | + } | ||
| 711 | + | ||
| 712 | + alert(errorMessage) | ||
| 713 | + } else { | ||
| 714 | + alert('状态修改失败') | ||
| 715 | + } | ||
| 716 | + } | ||
| 717 | +} | ||
| 718 | + | ||
| 719 | + | ||
| 720 | +// 全选 | ||
| 721 | +const handleSelectAll = () => { | ||
| 722 | + // 已在计算属性中处理 | ||
| 723 | +} | ||
| 724 | + | ||
| 725 | +// 分页相关 | ||
| 726 | +const handlePageChange = (page: number) => { | ||
| 727 | + const totalPagesValue = totalPages.value | ||
| 728 | + if (page >= 1 && page <= totalPagesValue && totalPagesValue > 0) { | ||
| 729 | + pagination.pageNum = page | ||
| 730 | + searchParams.pageNum = page | ||
| 731 | + fetchDealers() | ||
| 732 | + } | ||
| 733 | +} | ||
| 734 | + | ||
| 735 | +const handlePageSizeChange = () => { | ||
| 736 | + pagination.pageSize = pageSize.value | ||
| 737 | + searchParams.pageSize = pageSize.value | ||
| 738 | + pagination.pageNum = 1 | ||
| 739 | + searchParams.pageNum = 1 | ||
| 740 | + fetchDealers() | ||
| 741 | +} | ||
| 742 | + | ||
| 743 | +// 表格工具栏 | ||
| 744 | +const handleTableSearch = () => { | ||
| 745 | + handleSearch() | ||
| 746 | +} | ||
| 747 | + | ||
| 748 | +const handleTableRefresh = () => { | ||
| 749 | + fetchDealers() | ||
| 750 | +} | ||
| 751 | + | ||
| 752 | +const handleTableExport = () => { | ||
| 753 | + alert('导出功能开发中...') | ||
| 754 | +} | ||
| 755 | + | ||
| 756 | +const handleTableViewToggle = () => { | ||
| 757 | + alert('视图切换功能开发中...') | ||
| 758 | +} | ||
| 759 | + | ||
| 760 | +// 对话框相关 | ||
| 761 | +const handleDialogClose = () => { | ||
| 762 | + showDialog.value = false | ||
| 763 | +} | ||
| 764 | + | ||
| 765 | +const handleStatusDialogClose = () => { | ||
| 766 | + showStatusDialog.value = false | ||
| 767 | +} | ||
| 768 | + | ||
| 769 | + | ||
| 770 | +// 初始化 | ||
| 771 | +onMounted(() => { | ||
| 772 | + fetchDealers() | ||
| 773 | +}) | ||
| 774 | +</script> | ||
| 775 | + | ||
| 776 | +<style scoped> | ||
| 777 | +/* 基础样式 */ | ||
| 778 | +.dealer-container { | ||
| 779 | + background: #f5f5f5; | ||
| 780 | + min-height: 100vh; | ||
| 781 | + padding: 0; | ||
| 782 | +} | ||
| 783 | + | ||
| 784 | +/* 搜索区域 */ | ||
| 785 | +.search-section { | ||
| 786 | + background: white; | ||
| 787 | + padding: 16px 20px; | ||
| 788 | + border-bottom: 1px solid #e0e0e0; | ||
| 789 | +} | ||
| 790 | + | ||
| 791 | +.search-row { | ||
| 792 | + display: flex; | ||
| 793 | + align-items: center; | ||
| 794 | + gap: 20px; | ||
| 795 | + flex-wrap: wrap; | ||
| 796 | +} | ||
| 797 | + | ||
| 798 | +.search-item { | ||
| 799 | + display: flex; | ||
| 800 | + align-items: center; | ||
| 801 | + gap: 8px; | ||
| 802 | +} | ||
| 803 | + | ||
| 804 | +.search-item label { | ||
| 805 | + font-size: 12px; | ||
| 806 | + color: #666; | ||
| 807 | + white-space: nowrap; | ||
| 808 | + min-width: 60px; | ||
| 809 | +} | ||
| 810 | + | ||
| 811 | +.search-input, .search-select { | ||
| 812 | + padding: 4px 8px; | ||
| 813 | + border: 1px solid #d9d9d9; | ||
| 814 | + border-radius: 3px; | ||
| 815 | + font-size: 11px; | ||
| 816 | + width: 140px; | ||
| 817 | + height: 24px; | ||
| 818 | +} | ||
| 819 | + | ||
| 820 | +.search-input:focus, .search-select:focus { | ||
| 821 | + outline: none; | ||
| 822 | + border-color: #409eff; | ||
| 823 | +} | ||
| 824 | + | ||
| 825 | +.search-actions { | ||
| 826 | + display: flex; | ||
| 827 | + gap: 6px; | ||
| 828 | +} | ||
| 829 | + | ||
| 830 | +.search-btn { | ||
| 831 | + background: #1890ff; | ||
| 832 | + color: white; | ||
| 833 | + border: none; | ||
| 834 | + padding: 4px 8px; | ||
| 835 | + border-radius: 3px; | ||
| 836 | + font-size: 11px; | ||
| 837 | + cursor: pointer; | ||
| 838 | + height: 24px; | ||
| 839 | + display: flex; | ||
| 840 | + align-items: center; | ||
| 841 | + gap: 4px; | ||
| 842 | +} | ||
| 843 | + | ||
| 844 | +.reset-btn { | ||
| 845 | + background: #ff7875; | ||
| 846 | + color: white; | ||
| 847 | + border: none; | ||
| 848 | + padding: 4px 8px; | ||
| 849 | + border-radius: 3px; | ||
| 850 | + font-size: 11px; | ||
| 851 | + cursor: pointer; | ||
| 852 | + height: 24px; | ||
| 853 | + display: flex; | ||
| 854 | + align-items: center; | ||
| 855 | + gap: 4px; | ||
| 856 | +} | ||
| 857 | + | ||
| 858 | +/* 操作区域 */ | ||
| 859 | +.action-section { | ||
| 860 | + background: white; | ||
| 861 | + padding: 12px 16px; | ||
| 862 | + border-bottom: 1px solid #e0e0e0; | ||
| 863 | +} | ||
| 864 | + | ||
| 865 | +.action-buttons { | ||
| 866 | + display: flex; | ||
| 867 | + gap: 6px; | ||
| 868 | +} | ||
| 869 | + | ||
| 870 | +.action-btn { | ||
| 871 | + padding: 4px 8px; | ||
| 872 | + border: none; | ||
| 873 | + border-radius: 3px; | ||
| 874 | + cursor: pointer; | ||
| 875 | + font-size: 11px; | ||
| 876 | + height: 24px; | ||
| 877 | + display: flex; | ||
| 878 | + align-items: center; | ||
| 879 | + gap: 4px; | ||
| 880 | +} | ||
| 881 | + | ||
| 882 | +.action-btn.primary { | ||
| 883 | + background: #52c41a; | ||
| 884 | + color: white; | ||
| 885 | +} | ||
| 886 | + | ||
| 887 | +.action-btn.danger { | ||
| 888 | + background: #ff4d4f; | ||
| 889 | + color: white; | ||
| 890 | +} | ||
| 891 | + | ||
| 892 | +/* 表格区域 */ | ||
| 893 | +.table-section { | ||
| 894 | + background: white; | ||
| 895 | + margin: 16px; | ||
| 896 | + border-radius: 6px; | ||
| 897 | + overflow: hidden; | ||
| 898 | + box-shadow: 0 2px 8px rgba(0,0,0,0.1); | ||
| 899 | +} | ||
| 900 | + | ||
| 901 | +.table-header { | ||
| 902 | + padding: 8px 16px; | ||
| 903 | + background: #fafafa; | ||
| 904 | + border-bottom: 1px solid #e0e0e0; | ||
| 905 | + display: flex; | ||
| 906 | + justify-content: flex-end; | ||
| 907 | +} | ||
| 908 | + | ||
| 909 | +.table-controls { | ||
| 910 | + display: flex; | ||
| 911 | + gap: 4px; | ||
| 912 | +} | ||
| 913 | + | ||
| 914 | +.control-btn { | ||
| 915 | + width: 24px; | ||
| 916 | + height: 24px; | ||
| 917 | + border: 1px solid #d9d9d9; | ||
| 918 | + background: white; | ||
| 919 | + border-radius: 3px; | ||
| 920 | + cursor: pointer; | ||
| 921 | + display: flex; | ||
| 922 | + align-items: center; | ||
| 923 | + justify-content: center; | ||
| 924 | + font-size: 11px; | ||
| 925 | +} | ||
| 926 | + | ||
| 927 | +.control-btn:hover { | ||
| 928 | + background-color: #f5f5f5; | ||
| 929 | +} | ||
| 930 | + | ||
| 931 | +.table-container { | ||
| 932 | + position: relative; | ||
| 933 | + overflow-x: auto; | ||
| 934 | +} | ||
| 935 | + | ||
| 936 | +.loading-overlay { | ||
| 937 | + position: absolute; | ||
| 938 | + top: 0; | ||
| 939 | + left: 0; | ||
| 940 | + right: 0; | ||
| 941 | + bottom: 0; | ||
| 942 | + background: rgba(255,255,255,0.8); | ||
| 943 | + display: flex; | ||
| 944 | + align-items: center; | ||
| 945 | + justify-content: center; | ||
| 946 | + z-index: 10; | ||
| 947 | +} | ||
| 948 | + | ||
| 949 | +.loading-spinner { | ||
| 950 | + font-size: 16px; | ||
| 951 | + color: #409eff; | ||
| 952 | +} | ||
| 953 | + | ||
| 954 | +.data-table { | ||
| 955 | + width: 100%; | ||
| 956 | + border-collapse: collapse; | ||
| 957 | + font-size: 11px; | ||
| 958 | +} | ||
| 959 | + | ||
| 960 | +.data-table th, | ||
| 961 | +.data-table td { | ||
| 962 | + padding: 8px 6px; | ||
| 963 | + text-align: left; | ||
| 964 | + border-bottom: 1px solid #e0e0e0; | ||
| 965 | +} | ||
| 966 | + | ||
| 967 | +.data-table th { | ||
| 968 | + background-color: #fafafa; | ||
| 969 | + font-weight: 500; | ||
| 970 | + color: #333; | ||
| 971 | + font-size: 11px; | ||
| 972 | +} | ||
| 973 | + | ||
| 974 | +.data-table tbody tr:hover { | ||
| 975 | + background-color: #f5f5f5; | ||
| 976 | +} | ||
| 977 | + | ||
| 978 | +.select-all { | ||
| 979 | + margin: 0; | ||
| 980 | +} | ||
| 981 | + | ||
| 982 | +.table-btn { | ||
| 983 | + padding: 2px 6px; | ||
| 984 | + margin: 0 1px; | ||
| 985 | + border: none; | ||
| 986 | + border-radius: 2px; | ||
| 987 | + cursor: pointer; | ||
| 988 | + font-size: 10px; | ||
| 989 | + height: 20px; | ||
| 990 | + display: inline-flex; | ||
| 991 | + align-items: center; | ||
| 992 | + gap: 2px; | ||
| 993 | +} | ||
| 994 | + | ||
| 995 | +.table-btn.edit { | ||
| 996 | + background-color: #1890ff; | ||
| 997 | + color: white; | ||
| 998 | +} | ||
| 999 | + | ||
| 1000 | +.table-btn.delete { | ||
| 1001 | + background-color: #ff4d4f; | ||
| 1002 | + color: white; | ||
| 1003 | +} | ||
| 1004 | + | ||
| 1005 | +.table-btn.status { | ||
| 1006 | + background-color: #fa8c16; | ||
| 1007 | + color: white; | ||
| 1008 | +} | ||
| 1009 | + | ||
| 1010 | +.table-btn.audit { | ||
| 1011 | + background-color: #52c41a; | ||
| 1012 | + color: white; | ||
| 1013 | +} | ||
| 1014 | + | ||
| 1015 | +/* 分页 */ | ||
| 1016 | +.table-footer { | ||
| 1017 | + padding: 8px 16px; | ||
| 1018 | + border-top: 1px solid #e0e0e0; | ||
| 1019 | + display: flex; | ||
| 1020 | + justify-content: space-between; | ||
| 1021 | + align-items: center; | ||
| 1022 | + background: #fafafa; | ||
| 1023 | +} | ||
| 1024 | + | ||
| 1025 | +.pagination-left { | ||
| 1026 | + display: flex; | ||
| 1027 | + align-items: center; | ||
| 1028 | + gap: 20px; | ||
| 1029 | +} | ||
| 1030 | + | ||
| 1031 | +.pagination-info { | ||
| 1032 | + font-size: 12px; | ||
| 1033 | + color: #666; | ||
| 1034 | +} | ||
| 1035 | + | ||
| 1036 | +.page-size-selector { | ||
| 1037 | + display: flex; | ||
| 1038 | + align-items: center; | ||
| 1039 | + gap: 8px; | ||
| 1040 | +} | ||
| 1041 | + | ||
| 1042 | +.page-size-selector label { | ||
| 1043 | + font-size: 12px; | ||
| 1044 | + color: #666; | ||
| 1045 | +} | ||
| 1046 | + | ||
| 1047 | +.page-size-select { | ||
| 1048 | + padding: 4px 8px; | ||
| 1049 | + border: 1px solid #d9d9d9; | ||
| 1050 | + border-radius: 4px; | ||
| 1051 | + font-size: 12px; | ||
| 1052 | + background: white; | ||
| 1053 | + cursor: pointer; | ||
| 1054 | + transition: border-color 0.3s; | ||
| 1055 | +} | ||
| 1056 | + | ||
| 1057 | +.page-size-select:hover { | ||
| 1058 | + border-color: #1890ff; | ||
| 1059 | +} | ||
| 1060 | + | ||
| 1061 | +.page-size-select:focus { | ||
| 1062 | + outline: none; | ||
| 1063 | + border-color: #1890ff; | ||
| 1064 | + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); | ||
| 1065 | +} | ||
| 1066 | + | ||
| 1067 | +.pagination-container { | ||
| 1068 | + display: flex; | ||
| 1069 | + align-items: center; | ||
| 1070 | + background: #f8f9fa; | ||
| 1071 | + border: 1px solid #e9ecef; | ||
| 1072 | + border-radius: 8px; | ||
| 1073 | + overflow: hidden; | ||
| 1074 | + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); | ||
| 1075 | +} | ||
| 1076 | + | ||
| 1077 | +.pagination-btn { | ||
| 1078 | + padding: 2px 6px; | ||
| 1079 | + border: none; | ||
| 1080 | + background: white; | ||
| 1081 | + color: #6c757d; | ||
| 1082 | + font-size: 12px; | ||
| 1083 | + font-weight: 500; | ||
| 1084 | + cursor: pointer; | ||
| 1085 | + transition: all 0.2s; | ||
| 1086 | + border-right: 1px solid #e9ecef; | ||
| 1087 | + height: 20px; | ||
| 1088 | +} | ||
| 1089 | + | ||
| 1090 | +.pagination-btn:hover:not(:disabled) { | ||
| 1091 | + background: #e9ecef; | ||
| 1092 | + color: #495057; | ||
| 1093 | +} | ||
| 1094 | + | ||
| 1095 | +.pagination-btn:disabled { | ||
| 1096 | + background: #f8f9fa; | ||
| 1097 | + color: #adb5bd; | ||
| 1098 | + cursor: not-allowed; | ||
| 1099 | +} | ||
| 1100 | + | ||
| 1101 | +.pagination-pages { | ||
| 1102 | + display: flex; | ||
| 1103 | + align-items: center; | ||
| 1104 | +} | ||
| 1105 | + | ||
| 1106 | +.page-number { | ||
| 1107 | + padding: 2px 6px; | ||
| 1108 | + border: none; | ||
| 1109 | + background: white; | ||
| 1110 | + color: #6c757d; | ||
| 1111 | + font-size: 12px; | ||
| 1112 | + font-weight: 500; | ||
| 1113 | + cursor: pointer; | ||
| 1114 | + transition: all 0.2s; | ||
| 1115 | + border-right: 1px solid #e9ecef; | ||
| 1116 | + min-width: 28px; | ||
| 1117 | + text-align: center; | ||
| 1118 | + height: 20px; | ||
| 1119 | +} | ||
| 1120 | + | ||
| 1121 | +.page-number:hover { | ||
| 1122 | + background: #e9ecef; | ||
| 1123 | + color: #495057; | ||
| 1124 | +} | ||
| 1125 | + | ||
| 1126 | +.page-number.active { | ||
| 1127 | + background: #6c757d; | ||
| 1128 | + color: white; | ||
| 1129 | + font-weight: 600; | ||
| 1130 | +} | ||
| 1131 | + | ||
| 1132 | +.page-number.active:hover { | ||
| 1133 | + background: #5a6268; | ||
| 1134 | +} | ||
| 1135 | + | ||
| 1136 | +/* 对话框 */ | ||
| 1137 | +.dialog-overlay { | ||
| 1138 | + position: fixed; | ||
| 1139 | + top: 0; | ||
| 1140 | + left: 0; | ||
| 1141 | + right: 0; | ||
| 1142 | + bottom: 0; | ||
| 1143 | + background: rgba(0,0,0,0.5); | ||
| 1144 | + display: flex; | ||
| 1145 | + align-items: center; | ||
| 1146 | + justify-content: center; | ||
| 1147 | + z-index: 1000; | ||
| 1148 | +} | ||
| 1149 | + | ||
| 1150 | +.dialog-content { | ||
| 1151 | + background: white; | ||
| 1152 | + border-radius: 8px; | ||
| 1153 | + width: 90%; | ||
| 1154 | + max-width: 800px; | ||
| 1155 | + max-height: 90vh; | ||
| 1156 | + overflow-y: auto; | ||
| 1157 | +} | ||
| 1158 | + | ||
| 1159 | +.dialog-header { | ||
| 1160 | + padding: 20px; | ||
| 1161 | + border-bottom: 1px solid #eee; | ||
| 1162 | + display: flex; | ||
| 1163 | + justify-content: space-between; | ||
| 1164 | + align-items: center; | ||
| 1165 | +} | ||
| 1166 | + | ||
| 1167 | +.dialog-header h3 { | ||
| 1168 | + margin: 0; | ||
| 1169 | + font-size: 18px; | ||
| 1170 | + color: #333; | ||
| 1171 | +} | ||
| 1172 | + | ||
| 1173 | +.dialog-close { | ||
| 1174 | + background: none; | ||
| 1175 | + border: none; | ||
| 1176 | + font-size: 24px; | ||
| 1177 | + cursor: pointer; | ||
| 1178 | + color: #999; | ||
| 1179 | +} | ||
| 1180 | + | ||
| 1181 | +.dialog-body { | ||
| 1182 | + padding: 20px; | ||
| 1183 | +} | ||
| 1184 | + | ||
| 1185 | +.form-container { | ||
| 1186 | + display: flex; | ||
| 1187 | + flex-direction: column; | ||
| 1188 | + gap: 20px; | ||
| 1189 | +} | ||
| 1190 | + | ||
| 1191 | +.form-row { | ||
| 1192 | + display: flex; | ||
| 1193 | + gap: 20px; | ||
| 1194 | +} | ||
| 1195 | + | ||
| 1196 | +.form-item { | ||
| 1197 | + flex: 1; | ||
| 1198 | + display: flex; | ||
| 1199 | + flex-direction: column; | ||
| 1200 | + gap: 8px; | ||
| 1201 | +} | ||
| 1202 | + | ||
| 1203 | +.form-item.full-width { | ||
| 1204 | + flex: 100%; | ||
| 1205 | +} | ||
| 1206 | + | ||
| 1207 | +.form-item label { | ||
| 1208 | + font-size: 14px; | ||
| 1209 | + color: #333; | ||
| 1210 | + font-weight: 500; | ||
| 1211 | +} | ||
| 1212 | + | ||
| 1213 | +.form-item label.required::after { | ||
| 1214 | + content: ' *'; | ||
| 1215 | + color: #f56c6c; | ||
| 1216 | +} | ||
| 1217 | + | ||
| 1218 | +.form-input, .form-select, .form-textarea { | ||
| 1219 | + padding: 8px 12px; | ||
| 1220 | + border: 1px solid #ddd; | ||
| 1221 | + border-radius: 4px; | ||
| 1222 | + font-size: 14px; | ||
| 1223 | +} | ||
| 1224 | + | ||
| 1225 | +.form-input:focus, .form-select:focus, .form-textarea:focus { | ||
| 1226 | + outline: none; | ||
| 1227 | + border-color: #409eff; | ||
| 1228 | +} | ||
| 1229 | + | ||
| 1230 | +.form-textarea { | ||
| 1231 | + resize: vertical; | ||
| 1232 | + min-height: 80px; | ||
| 1233 | +} | ||
| 1234 | + | ||
| 1235 | +.dialog-footer { | ||
| 1236 | + padding: 20px; | ||
| 1237 | + border-top: 1px solid #eee; | ||
| 1238 | + display: flex; | ||
| 1239 | + justify-content: flex-end; | ||
| 1240 | + gap: 10px; | ||
| 1241 | +} | ||
| 1242 | + | ||
| 1243 | +.dialog-btn { | ||
| 1244 | + padding: 8px 16px; | ||
| 1245 | + border: none; | ||
| 1246 | + border-radius: 4px; | ||
| 1247 | + cursor: pointer; | ||
| 1248 | + font-size: 14px; | ||
| 1249 | +} | ||
| 1250 | + | ||
| 1251 | +.dialog-btn.cancel { | ||
| 1252 | + background-color: #909399; | ||
| 1253 | + color: white; | ||
| 1254 | +} | ||
| 1255 | + | ||
| 1256 | +.dialog-btn.confirm { | ||
| 1257 | + background-color: #409eff; | ||
| 1258 | + color: white; | ||
| 1259 | +} | ||
| 1260 | + | ||
| 1261 | +/* 状态样式 */ | ||
| 1262 | +.status-normal { | ||
| 1263 | + color: #67c23a; | ||
| 1264 | + font-weight: 500; | ||
| 1265 | +} | ||
| 1266 | + | ||
| 1267 | +.status-warning { | ||
| 1268 | + color: #e6a23c; | ||
| 1269 | + font-weight: 500; | ||
| 1270 | +} | ||
| 1271 | + | ||
| 1272 | +.status-danger { | ||
| 1273 | + color: #f56c6c; | ||
| 1274 | + font-weight: 500; | ||
| 1275 | +} | ||
| 1276 | + | ||
| 1277 | +.status-pending { | ||
| 1278 | + color: #909399; | ||
| 1279 | + font-weight: 500; | ||
| 1280 | +} | ||
| 1281 | + | ||
| 1282 | +.status-success { | ||
| 1283 | + color: #67c23a; | ||
| 1284 | + font-weight: 500; | ||
| 1285 | +} | ||
| 1286 | +</style> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
frontend/src/views/product/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="product-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.productCode" | ||
| 10 | + class="search-input" | ||
| 11 | + placeholder="请输入产品编码" | ||
| 12 | + /> | ||
| 13 | + </div> | ||
| 14 | + <div class="search-item"> | ||
| 15 | + <label>产品名称:</label> | ||
| 16 | + <input | ||
| 17 | + v-model="searchParams.productName" | ||
| 18 | + class="search-input" | ||
| 19 | + placeholder="请输入产品名称" | ||
| 20 | + /> | ||
| 21 | + </div> | ||
| 22 | + <div class="search-item"> | ||
| 23 | + <label>产品型号:</label> | ||
| 24 | + <input | ||
| 25 | + v-model="searchParams.productModel" | ||
| 26 | + class="search-input" | ||
| 27 | + placeholder="请输入产品型号" | ||
| 28 | + /> | ||
| 29 | + </div> | ||
| 30 | + <div class="search-item"> | ||
| 31 | + <label>销售状态:</label> | ||
| 32 | + <select v-model="searchParams.saleStatus" class="search-select"> | ||
| 33 | + <option value="">所有</option> | ||
| 34 | + <option value="1">在售</option> | ||
| 35 | + <option value="0">停售</option> | ||
| 36 | + </select> | ||
| 37 | + </div> | ||
| 38 | + </div> | ||
| 39 | + <div class="search-row"> | ||
| 40 | + | ||
| 41 | + <div class="search-item"> | ||
| 42 | + <label>返利标识:</label> | ||
| 43 | + <select v-model="searchParams.rebateFlag" class="search-select"> | ||
| 44 | + <option value="">所有</option> | ||
| 45 | + <option value="1">是</option> | ||
| 46 | + <option value="0">否</option> | ||
| 47 | + </select> | ||
| 48 | + </div> | ||
| 49 | + <div class="search-actions"> | ||
| 50 | + <button @click="handleSearch" class="search-btn">🔍 搜索</button> | ||
| 51 | + <button @click="handleReset" class="reset-btn">🔄 重置</button> | ||
| 52 | + </div> | ||
| 53 | + </div> | ||
| 54 | + </div> | ||
| 55 | + | ||
| 56 | + <!-- 操作按钮区域 --> | ||
| 57 | + <div class="action-section"> | ||
| 58 | + <div class="action-buttons"> | ||
| 59 | + <button @click="handleAdd" class="action-btn primary">✨ 新增</button> | ||
| 60 | + <button @click="handleBatchDelete" class="action-btn danger">🗑️ 删除</button> | ||
| 61 | + </div> | ||
| 62 | + </div> | ||
| 63 | + | ||
| 64 | + <!-- 数据表格 --> | ||
| 65 | + <div class="table-section"> | ||
| 66 | + <div class="table-header"> | ||
| 67 | + <div class="table-controls"> | ||
| 68 | + <button @click="handleTableSearch" class="control-btn">🔍</button> | ||
| 69 | + <button @click="handleTableRefresh" class="control-btn">🔄</button> | ||
| 70 | + <button @click="handleTableExport" class="control-btn">📋</button> | ||
| 71 | + <button @click="handleTableViewToggle" class="control-btn">⊞</button> | ||
| 72 | + </div> | ||
| 73 | + </div> | ||
| 74 | + | ||
| 75 | + <div class="table-container"> | ||
| 76 | + <div v-if="loading" class="loading-overlay"> | ||
| 77 | + <div class="loading-spinner">加载中...</div> | ||
| 78 | + </div> | ||
| 79 | + <table class="data-table"> | ||
| 80 | + <thead> | ||
| 81 | + <tr> | ||
| 82 | + <th> | ||
| 83 | + <input | ||
| 84 | + type="checkbox" | ||
| 85 | + class="select-all" | ||
| 86 | + v-model="selectAll" | ||
| 87 | + @change="handleSelectAll" | ||
| 88 | + /> | ||
| 89 | + </th> | ||
| 90 | + <th>产品ID</th> | ||
| 91 | + <th class="sortable">产品编码 ↕️</th> | ||
| 92 | + <th>产品名称</th> | ||
| 93 | + <th>产品型号</th> | ||
| 94 | + <th>销售状态</th> | ||
| 95 | + <th>返利标识</th> | ||
| 96 | + <th class="sortable">创建时间 ↕️</th> | ||
| 97 | + <th>操作</th> | ||
| 98 | + </tr> | ||
| 99 | + </thead> | ||
| 100 | + <tbody> | ||
| 101 | + <tr v-for="product in productList" :key="product.productId"> | ||
| 102 | + <td> | ||
| 103 | + <input | ||
| 104 | + type="checkbox" | ||
| 105 | + class="row-checkbox" | ||
| 106 | + :value="product.productId" | ||
| 107 | + v-model="selectedProducts" | ||
| 108 | + /> | ||
| 109 | + </td> | ||
| 110 | + <td>{{ product.productId }}</td> | ||
| 111 | + <td>{{ product.productCode }}</td> | ||
| 112 | + <td>{{ product.productName }}</td> | ||
| 113 | + <td>{{ product.productModel }}</td> | ||
| 114 | + <td> | ||
| 115 | + <span :class="product.saleStatus === 1 ? 'status-active' : 'status-inactive'"> | ||
| 116 | + {{ product.saleStatus === 1 ? '在售' : '停售' }} | ||
| 117 | + </span> | ||
| 118 | + </td> | ||
| 119 | + <td> | ||
| 120 | + <span :class="product.rebateFlag === 1 ? 'status-active' : 'status-inactive'"> | ||
| 121 | + {{ product.rebateFlag === 1 ? '是' : '否' }} | ||
| 122 | + </span> | ||
| 123 | + </td> | ||
| 124 | + <td>{{ formatTime(product.createTime) }}</td> | ||
| 125 | + <td class="action-col"> | ||
| 126 | + <button @click="handleEdit(product)" class="edit-btn">✏️ 编辑</button> | ||
| 127 | + <button @click="handleDelete(product)" class="delete-btn">🗑️ 删除</button> | ||
| 128 | + <button @click="handleStatusChange(product)" class="status-btn"> | ||
| 129 | + {{ product.saleStatus === 1 ? '停售' : '在售' }} | ||
| 130 | + </button> | ||
| 131 | + </td> | ||
| 132 | + </tr> | ||
| 133 | + </tbody> | ||
| 134 | + </table> | ||
| 135 | + </div> | ||
| 136 | + | ||
| 137 | + <!-- 分页信息 --> | ||
| 138 | + <div class="table-footer"> | ||
| 139 | + <div class="pagination-left"> | ||
| 140 | + <div class="pagination-info"> | ||
| 141 | + 共 {{ total }} 条记录,第 {{ currentPage }} / {{ totalPages }} 页 | ||
| 142 | + </div> | ||
| 143 | + <div class="page-size-selector"> | ||
| 144 | + <label>每页显示:</label> | ||
| 145 | + <select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select"> | ||
| 146 | + <option value="10">10条</option> | ||
| 147 | + <option value="20">20条</option> | ||
| 148 | + <option value="50">50条</option> | ||
| 149 | + <option value="100">100条</option> | ||
| 150 | + </select> | ||
| 151 | + </div> | ||
| 152 | + </div> | ||
| 153 | + <div class="pagination-container"> | ||
| 154 | + <button | ||
| 155 | + @click="handlePageChange(currentPage - 1)" | ||
| 156 | + :disabled="currentPage <= 1" | ||
| 157 | + class="pagination-btn prev-btn" | ||
| 158 | + > | ||
| 159 | + 上一页 | ||
| 160 | + </button> | ||
| 161 | + <div class="pagination-pages"> | ||
| 162 | + <button | ||
| 163 | + v-for="page in getPageNumbers()" | ||
| 164 | + :key="page" | ||
| 165 | + @click="handlePageChange(page)" | ||
| 166 | + :class="['page-number', { active: page === currentPage }]" | ||
| 167 | + > | ||
| 168 | + {{ page }} | ||
| 169 | + </button> | ||
| 170 | + </div> | ||
| 171 | + <button | ||
| 172 | + @click="handlePageChange(currentPage + 1)" | ||
| 173 | + :disabled="currentPage >= totalPages" | ||
| 174 | + class="pagination-btn next-btn" | ||
| 175 | + > | ||
| 176 | + 下一页 | ||
| 177 | + </button> | ||
| 178 | + </div> | ||
| 179 | + </div> | ||
| 180 | + </div> | ||
| 181 | + | ||
| 182 | + <!-- 新增/编辑对话框 --> | ||
| 183 | + <div v-if="showDialog" class="dialog-overlay" @click="closeDialog"> | ||
| 184 | + <div class="dialog-content" @click.stop> | ||
| 185 | + <div class="dialog-header"> | ||
| 186 | + <h3>{{ isEdit ? '编辑产品' : '新增产品' }}</h3> | ||
| 187 | + <button @click="closeDialog" class="close-btn">×</button> | ||
| 188 | + </div> | ||
| 189 | + <div class="dialog-body"> | ||
| 190 | + <form @submit.prevent="handleSubmit"> | ||
| 191 | + <div class="form-row"> | ||
| 192 | + <div class="form-item"> | ||
| 193 | + <label class="required">产品编码:</label> | ||
| 194 | + <input | ||
| 195 | + v-model="formData.productCode" | ||
| 196 | + class="form-input" | ||
| 197 | + placeholder="请输入产品编码" | ||
| 198 | + required | ||
| 199 | + /> | ||
| 200 | + </div> | ||
| 201 | + <div class="form-item"> | ||
| 202 | + <label class="required">产品名称:</label> | ||
| 203 | + <input | ||
| 204 | + v-model="formData.productName" | ||
| 205 | + class="form-input" | ||
| 206 | + placeholder="请输入产品名称" | ||
| 207 | + required | ||
| 208 | + /> | ||
| 209 | + </div> | ||
| 210 | + </div> | ||
| 211 | + <div class="form-row"> | ||
| 212 | + <div class="form-item"> | ||
| 213 | + <label class="required">产品型号:</label> | ||
| 214 | + <input | ||
| 215 | + v-model="formData.productModel" | ||
| 216 | + class="form-input" | ||
| 217 | + placeholder="请输入产品型号" | ||
| 218 | + required | ||
| 219 | + /> | ||
| 220 | + </div> | ||
| 221 | + <div class="form-item"> | ||
| 222 | + <label class="required">产品类别:</label> | ||
| 223 | + <input | ||
| 224 | + v-model="formData.productType" | ||
| 225 | + class="form-input" | ||
| 226 | + placeholder="请输入产品类别" | ||
| 227 | + required | ||
| 228 | + /> | ||
| 229 | + </div> | ||
| 230 | + </div> | ||
| 231 | + <div class="form-row"> | ||
| 232 | + <div class="form-item"> | ||
| 233 | + <label class="required">存储容量:</label> | ||
| 234 | + <input | ||
| 235 | + v-model="formData.storageCapacity" | ||
| 236 | + class="form-input" | ||
| 237 | + placeholder="请输入存储容量" | ||
| 238 | + required | ||
| 239 | + /> | ||
| 240 | + </div> | ||
| 241 | + <div class="form-item"> | ||
| 242 | + <label class="required">颜色:</label> | ||
| 243 | + <input | ||
| 244 | + v-model="formData.color" | ||
| 245 | + class="form-input" | ||
| 246 | + placeholder="请输入颜色" | ||
| 247 | + required | ||
| 248 | + /> | ||
| 249 | + </div> | ||
| 250 | + </div> | ||
| 251 | + <div class="form-row"> | ||
| 252 | + <div class="form-item"> | ||
| 253 | + <label class="required">官方指导价:</label> | ||
| 254 | + <input | ||
| 255 | + v-model.number="formData.officialPrice" | ||
| 256 | + class="form-input" | ||
| 257 | + type="number" | ||
| 258 | + placeholder="请输入官方指导价" | ||
| 259 | + required | ||
| 260 | + /> | ||
| 261 | + </div> | ||
| 262 | + <div class="form-item"> | ||
| 263 | + <label class="required">销售状态:</label> | ||
| 264 | + <select v-model="formData.saleStatus" class="form-select" required> | ||
| 265 | + <option value="1">在售</option> | ||
| 266 | + <option value="0">停售</option> | ||
| 267 | + </select> | ||
| 268 | + </div> | ||
| 269 | + </div> | ||
| 270 | + <div class="form-row"> | ||
| 271 | + <div class="form-item"> | ||
| 272 | + <label class="required">返利标识:</label> | ||
| 273 | + <select v-model="formData.rebateFlag" class="form-select" required> | ||
| 274 | + <option value="1">是</option> | ||
| 275 | + <option value="0">否</option> | ||
| 276 | + </select> | ||
| 277 | + </div> | ||
| 278 | + <div class="form-item"> | ||
| 279 | + <label>图片URL:</label> | ||
| 280 | + <input | ||
| 281 | + v-model="formData.imageUrl" | ||
| 282 | + class="form-input" | ||
| 283 | + placeholder="请输入图片URL" | ||
| 284 | + /> | ||
| 285 | + </div> | ||
| 286 | + </div> | ||
| 287 | + <div class="form-row"> | ||
| 288 | + <div class="form-item"> | ||
| 289 | + <label class="required">销售起始日期:</label> | ||
| 290 | + <input | ||
| 291 | + v-model="formData.saleStartDate" | ||
| 292 | + class="form-input" | ||
| 293 | + type="date" | ||
| 294 | + required | ||
| 295 | + /> | ||
| 296 | + </div> | ||
| 297 | + <div class="form-item"> | ||
| 298 | + <label class="required">销售终止日期:</label> | ||
| 299 | + <input | ||
| 300 | + v-model="formData.saleEndDate" | ||
| 301 | + class="form-input" | ||
| 302 | + type="date" | ||
| 303 | + required | ||
| 304 | + /> | ||
| 305 | + </div> | ||
| 306 | + </div> | ||
| 307 | + <div class="form-row"> | ||
| 308 | + <div class="form-item full-width"> | ||
| 309 | + <label>备注:</label> | ||
| 310 | + <textarea | ||
| 311 | + v-model="formData.remark" | ||
| 312 | + class="form-textarea" | ||
| 313 | + placeholder="请输入备注" | ||
| 314 | + rows="3" | ||
| 315 | + ></textarea> | ||
| 316 | + </div> | ||
| 317 | + </div> | ||
| 318 | + <div class="form-actions"> | ||
| 319 | + <button type="button" @click="closeDialog" class="cancel-btn">取消</button> | ||
| 320 | + <button type="submit" class="submit-btn">确定</button> | ||
| 321 | + </div> | ||
| 322 | + </form> | ||
| 323 | + </div> | ||
| 324 | + </div> | ||
| 325 | + </div> | ||
| 326 | + </div> | ||
| 327 | +</template> | ||
| 328 | + | ||
| 329 | +<script setup lang="ts"> | ||
| 330 | +import { ref, reactive, computed, onMounted } from 'vue' | ||
| 331 | +import { productApi, type ProductInfo, type ProductQueryReq, type ProductAddReq, type ProductUpdateReq } from '../../api/product' | ||
| 332 | + | ||
| 333 | +// 响应式数据 | ||
| 334 | +const loading = ref(false) | ||
| 335 | +const productList = ref<ProductInfo[]>([]) | ||
| 336 | +const selectedProducts = ref<number[]>([]) | ||
| 337 | +const showDialog = ref(false) | ||
| 338 | +const isEdit = ref(false) | ||
| 339 | + | ||
| 340 | +// 搜索参数 | ||
| 341 | +const searchParams = reactive<ProductQueryReq>({ | ||
| 342 | + productCode: '', | ||
| 343 | + productName: '', | ||
| 344 | + productModel: '', | ||
| 345 | + saleStatus: undefined, | ||
| 346 | + rebateFlag: undefined, | ||
| 347 | + pageNum: 1, | ||
| 348 | + pageSize: 10 | ||
| 349 | +}) | ||
| 350 | + | ||
| 351 | +// 分页信息 | ||
| 352 | +const pagination = reactive({ | ||
| 353 | + total: 0, | ||
| 354 | + pageNum: 1, | ||
| 355 | + pageSize: 10 | ||
| 356 | +}) | ||
| 357 | + | ||
| 358 | +// 分页计算属性 | ||
| 359 | +const total = computed(() => pagination.total) | ||
| 360 | +const currentPage = computed(() => pagination.pageNum) | ||
| 361 | +const pageSize = computed({ | ||
| 362 | + get: () => pagination.pageSize, | ||
| 363 | + set: (value: number) => { | ||
| 364 | + pagination.pageSize = value | ||
| 365 | + } | ||
| 366 | +}) | ||
| 367 | +const totalPages = computed(() => Math.ceil(pagination.total / pagination.pageSize)) | ||
| 368 | + | ||
| 369 | +// 表单数据 | ||
| 370 | +const formData = reactive<ProductAddReq & { productId?: number }>({ | ||
| 371 | + productCode: '', | ||
| 372 | + productName: '', | ||
| 373 | + productModel: '', | ||
| 374 | + productType: '', | ||
| 375 | + storageCapacity: '', | ||
| 376 | + color: '', | ||
| 377 | + officialPrice: 0, | ||
| 378 | + saleStatus: 1, | ||
| 379 | + rebateFlag: 0, | ||
| 380 | + saleStartDate: '', | ||
| 381 | + saleEndDate: '', | ||
| 382 | + imageUrl: '', | ||
| 383 | + remark: '' | ||
| 384 | +}) | ||
| 385 | + | ||
| 386 | +// 计算属性 | ||
| 387 | +const selectAll = computed({ | ||
| 388 | + get: () => selectedProducts.value.length === productList.value.length && productList.value.length > 0, | ||
| 389 | + set: (value: boolean) => { | ||
| 390 | + if (value) { | ||
| 391 | + selectedProducts.value = productList.value.map(product => product.productId) | ||
| 392 | + } else { | ||
| 393 | + selectedProducts.value = [] | ||
| 394 | + } | ||
| 395 | + } | ||
| 396 | +}) | ||
| 397 | + | ||
| 398 | + | ||
| 399 | +// 获取页码数组 | ||
| 400 | +const getPageNumbers = () => { | ||
| 401 | + const totalPages = Math.ceil(pagination.total / pagination.pageSize) | ||
| 402 | + const currentPage = pagination.pageNum | ||
| 403 | + const maxVisible = 5 | ||
| 404 | + const start = Math.max(1, currentPage - Math.floor(maxVisible / 2)) | ||
| 405 | + const end = Math.min(totalPages, start + maxVisible - 1) | ||
| 406 | + const pages = [] | ||
| 407 | + for (let i = start; i <= end; i++) { | ||
| 408 | + pages.push(i) | ||
| 409 | + } | ||
| 410 | + return pages | ||
| 411 | +} | ||
| 412 | + | ||
| 413 | +// 方法 | ||
| 414 | +const fetchProducts = async () => { | ||
| 415 | + try { | ||
| 416 | + loading.value = true | ||
| 417 | + const params = { ...searchParams, pageNum: pagination.pageNum, pageSize: pagination.pageSize } | ||
| 418 | + const response = await productApi.getProductList(params) as any | ||
| 419 | + if (response.code === 200 && response.data) { | ||
| 420 | + productList.value = response.data.records || [] | ||
| 421 | + pagination.total = response.data.total || 0 | ||
| 422 | + pagination.pageNum = response.data.current || 1 | ||
| 423 | + pagination.pageSize = response.data.size || 10 | ||
| 424 | + } else { | ||
| 425 | + productList.value = [] | ||
| 426 | + pagination.total = 0 | ||
| 427 | + showMessage(response.message || '获取产品列表失败', 'error') | ||
| 428 | + } | ||
| 429 | + } catch (error) { | ||
| 430 | + console.error('获取产品列表失败:', error) | ||
| 431 | + showMessage('获取产品列表失败, 请重试', 'error') | ||
| 432 | + } finally { | ||
| 433 | + loading.value = false | ||
| 434 | + } | ||
| 435 | +} | ||
| 436 | + | ||
| 437 | +const handleSearch = () => { | ||
| 438 | + pagination.pageNum = 1 | ||
| 439 | + fetchProducts() | ||
| 440 | +} | ||
| 441 | + | ||
| 442 | +const handleReset = () => { | ||
| 443 | + Object.assign(searchParams, { | ||
| 444 | + productCode: '', | ||
| 445 | + productName: '', | ||
| 446 | + productModel: '', | ||
| 447 | + saleStatus: undefined, | ||
| 448 | + rebateFlag: undefined | ||
| 449 | + }) | ||
| 450 | + pagination.pageNum = 1 | ||
| 451 | + fetchProducts() | ||
| 452 | +} | ||
| 453 | + | ||
| 454 | +const handleAdd = () => { | ||
| 455 | + isEdit.value = false | ||
| 456 | + Object.assign(formData, { | ||
| 457 | + productCode: '', | ||
| 458 | + productName: '', | ||
| 459 | + productModel: '', | ||
| 460 | + productType: '', | ||
| 461 | + storageCapacity: '', | ||
| 462 | + color: '', | ||
| 463 | + officialPrice: 0, | ||
| 464 | + saleStatus: 1, | ||
| 465 | + rebateFlag: 0, | ||
| 466 | + saleStartDate: '', | ||
| 467 | + saleEndDate: '', | ||
| 468 | + imageUrl: '', | ||
| 469 | + remark: '' | ||
| 470 | + }) | ||
| 471 | + showDialog.value = true | ||
| 472 | +} | ||
| 473 | + | ||
| 474 | +const handleEdit = (product: ProductInfo) => { | ||
| 475 | + isEdit.value = true | ||
| 476 | + Object.assign(formData, { | ||
| 477 | + productId: product.productId, | ||
| 478 | + productCode: product.productCode, | ||
| 479 | + productName: product.productName, | ||
| 480 | + productModel: product.productModel, | ||
| 481 | + productType: product.productType, | ||
| 482 | + storageCapacity: product.storageCapacity, | ||
| 483 | + color: product.color, | ||
| 484 | + officialPrice: product.officialPrice, | ||
| 485 | + saleStatus: product.saleStatus, | ||
| 486 | + rebateFlag: product.rebateFlag, | ||
| 487 | + saleStartDate: product.saleStartDate, | ||
| 488 | + saleEndDate: product.saleEndDate, | ||
| 489 | + imageUrl: product.imageUrl || '', | ||
| 490 | + remark: product.remark || '' | ||
| 491 | + }) | ||
| 492 | + showDialog.value = true | ||
| 493 | +} | ||
| 494 | + | ||
| 495 | +const handleDelete = async (product: ProductInfo) => { | ||
| 496 | + if (confirm(`确定要删除产品"${product.productName}"吗?`)) { | ||
| 497 | + try { | ||
| 498 | + await productApi.deleteProduct(product.productId) | ||
| 499 | + showMessage('删除成功', 'success') | ||
| 500 | + fetchProducts() | ||
| 501 | + } catch (error) { | ||
| 502 | + console.error('删除失败:', error) | ||
| 503 | + showMessage('删除失败, 请重试', 'error') | ||
| 504 | + } | ||
| 505 | + } | ||
| 506 | +} | ||
| 507 | + | ||
| 508 | +const handleBatchDelete = async () => { | ||
| 509 | + if (selectedProducts.value.length === 0) { | ||
| 510 | + showMessage('请选择要删除的产品', 'warning') | ||
| 511 | + return | ||
| 512 | + } | ||
| 513 | + if (confirm(`确定要删除选中的${selectedProducts.value.length}个产品吗?`)) { | ||
| 514 | + try { | ||
| 515 | + await productApi.batchDeleteProduct(selectedProducts.value) | ||
| 516 | + showMessage('批量删除成功', 'success') | ||
| 517 | + selectedProducts.value = [] | ||
| 518 | + fetchProducts() | ||
| 519 | + } catch (error) { | ||
| 520 | + console.error('批量删除失败:', error) | ||
| 521 | + showMessage('批量删除失败, 请重试', 'error') | ||
| 522 | + } | ||
| 523 | + } | ||
| 524 | +} | ||
| 525 | + | ||
| 526 | +const handleStatusChange = async (product: ProductInfo) => { | ||
| 527 | + try { | ||
| 528 | + const newStatus = product.saleStatus === 1 ? 0 : 1 | ||
| 529 | + await productApi.updateSaleStatus(product.productId, newStatus) | ||
| 530 | + showMessage('状态修改成功', 'success') | ||
| 531 | + fetchProducts() | ||
| 532 | + } catch (error) { | ||
| 533 | + console.error('状态修改失败:', error) | ||
| 534 | + showMessage('状态修改失败, 请重试', 'error') | ||
| 535 | + } | ||
| 536 | +} | ||
| 537 | + | ||
| 538 | +const handleSubmit = async () => { | ||
| 539 | + try { | ||
| 540 | + if (isEdit.value) { | ||
| 541 | + await productApi.updateProduct(formData as ProductUpdateReq) | ||
| 542 | + showMessage('修改成功', 'success') | ||
| 543 | + } else { | ||
| 544 | + await productApi.addProduct(formData as ProductAddReq) | ||
| 545 | + showMessage('新增成功', 'success') | ||
| 546 | + } | ||
| 547 | + closeDialog() | ||
| 548 | + fetchProducts() | ||
| 549 | + } catch (error: any) { | ||
| 550 | + console.error('操作失败:', error) | ||
| 551 | + if (error.response?.data?.message) { | ||
| 552 | + showMessage(error.response.data.message, 'error') | ||
| 553 | + } else { | ||
| 554 | + showMessage('操作失败, 请重试', 'error') | ||
| 555 | + } | ||
| 556 | + } | ||
| 557 | +} | ||
| 558 | + | ||
| 559 | +const closeDialog = () => { | ||
| 560 | + showDialog.value = false | ||
| 561 | + isEdit.value = false | ||
| 562 | +} | ||
| 563 | + | ||
| 564 | +const handleSelectAll = () => { | ||
| 565 | + // 已在计算属性中处理 | ||
| 566 | +} | ||
| 567 | + | ||
| 568 | +const handleTableSearch = () => { | ||
| 569 | + handleSearch() | ||
| 570 | +} | ||
| 571 | + | ||
| 572 | +const handleTableRefresh = () => { | ||
| 573 | + fetchProducts() | ||
| 574 | +} | ||
| 575 | + | ||
| 576 | +const handleTableExport = () => { | ||
| 577 | + showMessage('导出功能开发中...', 'info') | ||
| 578 | +} | ||
| 579 | + | ||
| 580 | +const handleTableViewToggle = () => { | ||
| 581 | + showMessage('视图切换功能开发中...', 'info') | ||
| 582 | +} | ||
| 583 | + | ||
| 584 | +const handlePageChange = (page: number) => { | ||
| 585 | + pagination.pageNum = page | ||
| 586 | + fetchProducts() | ||
| 587 | +} | ||
| 588 | + | ||
| 589 | + | ||
| 590 | +const handlePageSizeChange = () => { | ||
| 591 | + pagination.pageNum = 1 | ||
| 592 | + fetchProducts() | ||
| 593 | +} | ||
| 594 | + | ||
| 595 | + | ||
| 596 | +const formatTime = (time: string) => { | ||
| 597 | + return time.replace('T', ' ').substring(0, 19) | ||
| 598 | +} | ||
| 599 | + | ||
| 600 | +const showMessage = (message: string, type: 'success' | 'error' | 'warning' | 'info' = 'info') => { | ||
| 601 | + // 这里可以集成消息提示组件 | ||
| 602 | + alert(message) | ||
| 603 | +} | ||
| 604 | + | ||
| 605 | +// 生命周期 | ||
| 606 | +onMounted(() => { | ||
| 607 | + fetchProducts() | ||
| 608 | +}) | ||
| 609 | +</script> | ||
| 610 | + | ||
| 611 | +<style scoped> | ||
| 612 | +/* 基础样式 */ | ||
| 613 | +.product-container { | ||
| 614 | + padding: 0; | ||
| 615 | + background: #f5f5f5; | ||
| 616 | + min-height: 100vh; | ||
| 617 | +} | ||
| 618 | + | ||
| 619 | +/* 搜索区域样式 */ | ||
| 620 | +.search-section { | ||
| 621 | + background: white; | ||
| 622 | + padding: 16px 20px; | ||
| 623 | + border-bottom: 1px solid #e0e0e0; | ||
| 624 | +} | ||
| 625 | + | ||
| 626 | +.search-row { | ||
| 627 | + display: flex; | ||
| 628 | + gap: 20px; | ||
| 629 | + align-items: center; | ||
| 630 | + flex-wrap: wrap; | ||
| 631 | + margin-bottom: 12px; | ||
| 632 | +} | ||
| 633 | + | ||
| 634 | +.search-item { | ||
| 635 | + display: flex; | ||
| 636 | + align-items: center; | ||
| 637 | + gap: 8px; | ||
| 638 | +} | ||
| 639 | + | ||
| 640 | +.search-item label { | ||
| 641 | + font-size: 12px; | ||
| 642 | + color: #333; | ||
| 643 | + white-space: nowrap; | ||
| 644 | +} | ||
| 645 | + | ||
| 646 | +.search-input, .search-select { | ||
| 647 | + padding: 6px 8px; | ||
| 648 | + border: 1px solid #d9d9d9; | ||
| 649 | + border-radius: 4px; | ||
| 650 | + font-size: 12px; | ||
| 651 | + width: 120px; | ||
| 652 | +} | ||
| 653 | + | ||
| 654 | +.price-range, .date-range { | ||
| 655 | + display: flex; | ||
| 656 | + align-items: center; | ||
| 657 | + gap: 8px; | ||
| 658 | +} | ||
| 659 | + | ||
| 660 | +.price-input, .date-input { | ||
| 661 | + padding: 6px 8px; | ||
| 662 | + border: 1px solid #d9d9d9; | ||
| 663 | + border-radius: 4px; | ||
| 664 | + font-size: 12px; | ||
| 665 | + width: 120px; | ||
| 666 | +} | ||
| 667 | + | ||
| 668 | +.price-separator, .date-separator { | ||
| 669 | + color: #666; | ||
| 670 | + font-weight: bold; | ||
| 671 | +} | ||
| 672 | + | ||
| 673 | +.search-input:focus, .search-select:focus, .price-input:focus, .date-input:focus { | ||
| 674 | + outline: none; | ||
| 675 | + border-color: #1890ff; | ||
| 676 | + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); | ||
| 677 | +} | ||
| 678 | + | ||
| 679 | +.search-actions { | ||
| 680 | + display: flex; | ||
| 681 | + gap: 10px; | ||
| 682 | + align-items: center; | ||
| 683 | +} | ||
| 684 | + | ||
| 685 | +.search-btn { | ||
| 686 | + background: #1890ff; | ||
| 687 | + color: white; | ||
| 688 | + border: none; | ||
| 689 | + padding: 4px 8px; | ||
| 690 | + border-radius: 3px; | ||
| 691 | + font-size: 11px; | ||
| 692 | + cursor: pointer; | ||
| 693 | + height: 24px; | ||
| 694 | + display: flex; | ||
| 695 | + align-items: center; | ||
| 696 | + gap: 4px; | ||
| 697 | +} | ||
| 698 | + | ||
| 699 | +.reset-btn { | ||
| 700 | + background: #ff7875; | ||
| 701 | + color: white; | ||
| 702 | + border: none; | ||
| 703 | + padding: 4px 8px; | ||
| 704 | + border-radius: 3px; | ||
| 705 | + font-size: 11px; | ||
| 706 | + cursor: pointer; | ||
| 707 | + height: 24px; | ||
| 708 | + display: flex; | ||
| 709 | + align-items: center; | ||
| 710 | + gap: 4px; | ||
| 711 | +} | ||
| 712 | + | ||
| 713 | +/* 操作区域样式 */ | ||
| 714 | +.action-section { | ||
| 715 | + background: white; | ||
| 716 | + padding: 12px 16px; | ||
| 717 | + border-bottom: 1px solid #e0e0e0; | ||
| 718 | +} | ||
| 719 | + | ||
| 720 | +.action-buttons { | ||
| 721 | + display: flex; | ||
| 722 | + gap: 6px; | ||
| 723 | +} | ||
| 724 | + | ||
| 725 | +.action-btn { | ||
| 726 | + padding: 4px 8px; | ||
| 727 | + border: none; | ||
| 728 | + border-radius: 3px; | ||
| 729 | + font-size: 11px; | ||
| 730 | + cursor: pointer; | ||
| 731 | + display: flex; | ||
| 732 | + align-items: center; | ||
| 733 | + gap: 4px; | ||
| 734 | + height: 24px; | ||
| 735 | +} | ||
| 736 | + | ||
| 737 | +.action-btn.primary { | ||
| 738 | + background: #1890ff; | ||
| 739 | + color: white; | ||
| 740 | +} | ||
| 741 | + | ||
| 742 | +.action-btn.success { | ||
| 743 | + background: #52c41a; | ||
| 744 | + color: white; | ||
| 745 | +} | ||
| 746 | + | ||
| 747 | +.action-btn.danger { | ||
| 748 | + background: #ff4d4f; | ||
| 749 | + color: white; | ||
| 750 | +} | ||
| 751 | + | ||
| 752 | +.action-btn.info { | ||
| 753 | + background: #13c2c2; | ||
| 754 | + color: white; | ||
| 755 | +} | ||
| 756 | + | ||
| 757 | +.action-btn.warning { | ||
| 758 | + background: #faad14; | ||
| 759 | + color: white; | ||
| 760 | +} | ||
| 761 | + | ||
| 762 | +/* 表格区域样式 */ | ||
| 763 | +.table-section { | ||
| 764 | + background: white; | ||
| 765 | + margin: 16px; | ||
| 766 | + border-radius: 6px; | ||
| 767 | + overflow: hidden; | ||
| 768 | + box-shadow: 0 2px 8px rgba(0,0,0,0.1); | ||
| 769 | +} | ||
| 770 | + | ||
| 771 | +.table-header { | ||
| 772 | + padding: 8px 16px; | ||
| 773 | + background: #fafafa; | ||
| 774 | + border-bottom: 1px solid #e0e0e0; | ||
| 775 | + display: flex; | ||
| 776 | + justify-content: flex-end; | ||
| 777 | + align-items: center; | ||
| 778 | +} | ||
| 779 | + | ||
| 780 | +.table-controls { | ||
| 781 | + display: flex; | ||
| 782 | + gap: 6px; | ||
| 783 | +} | ||
| 784 | + | ||
| 785 | +.control-btn { | ||
| 786 | + padding: 4px 8px; | ||
| 787 | + border: 1px solid #ddd; | ||
| 788 | + border-radius: 3px; | ||
| 789 | + background: white; | ||
| 790 | + cursor: pointer; | ||
| 791 | + font-size: 11px; | ||
| 792 | + color: #666; | ||
| 793 | + min-width: 24px; | ||
| 794 | + height: 24px; | ||
| 795 | + display: flex; | ||
| 796 | + align-items: center; | ||
| 797 | + justify-content: center; | ||
| 798 | +} | ||
| 799 | + | ||
| 800 | +.control-btn:hover { | ||
| 801 | + background: #f5f5f5; | ||
| 802 | +} | ||
| 803 | + | ||
| 804 | +.data-table { | ||
| 805 | + width: 100%; | ||
| 806 | + border-collapse: collapse; | ||
| 807 | + font-size: 11px; | ||
| 808 | +} | ||
| 809 | + | ||
| 810 | +.data-table th, | ||
| 811 | +.data-table td { | ||
| 812 | + padding: 8px 6px; | ||
| 813 | + text-align: left; | ||
| 814 | + border-bottom: 1px solid #e0e0e0; | ||
| 815 | +} | ||
| 816 | + | ||
| 817 | +.data-table th { | ||
| 818 | + background-color: #fafafa; | ||
| 819 | + font-weight: 500; | ||
| 820 | + color: #333; | ||
| 821 | + font-size: 11px; | ||
| 822 | +} | ||
| 823 | + | ||
| 824 | +.data-table tbody tr:hover { | ||
| 825 | + background-color: #f5f5f5; | ||
| 826 | +} | ||
| 827 | + | ||
| 828 | +.sortable { | ||
| 829 | + cursor: pointer; | ||
| 830 | + user-select: none; | ||
| 831 | +} | ||
| 832 | + | ||
| 833 | +.sortable:hover { | ||
| 834 | + background: #e6f7ff; | ||
| 835 | +} | ||
| 836 | + | ||
| 837 | +.checkbox-col { | ||
| 838 | + width: 40px; | ||
| 839 | + text-align: center; | ||
| 840 | +} | ||
| 841 | + | ||
| 842 | +.select-all, .row-checkbox { | ||
| 843 | + width: 14px; | ||
| 844 | + height: 14px; | ||
| 845 | +} | ||
| 846 | + | ||
| 847 | +.action-col { | ||
| 848 | + white-space: nowrap; | ||
| 849 | +} | ||
| 850 | + | ||
| 851 | +.edit-btn, .delete-btn, .status-btn { | ||
| 852 | + padding: 2px 6px; | ||
| 853 | + border: none; | ||
| 854 | + border-radius: 3px; | ||
| 855 | + cursor: pointer; | ||
| 856 | + font-size: 11px; | ||
| 857 | + margin-right: 4px; | ||
| 858 | + height: 20px; | ||
| 859 | + display: inline-flex; | ||
| 860 | + align-items: center; | ||
| 861 | + gap: 2px; | ||
| 862 | +} | ||
| 863 | + | ||
| 864 | +.edit-btn { | ||
| 865 | + background: #1890ff; | ||
| 866 | + color: white; | ||
| 867 | +} | ||
| 868 | + | ||
| 869 | +.delete-btn { | ||
| 870 | + background: #ff4d4f; | ||
| 871 | + color: white; | ||
| 872 | +} | ||
| 873 | + | ||
| 874 | +.status-btn { | ||
| 875 | + background: #faad14; | ||
| 876 | + color: white; | ||
| 877 | +} | ||
| 878 | + | ||
| 879 | +/* 状态样式 */ | ||
| 880 | +.status-active { | ||
| 881 | + color: #67c23a; | ||
| 882 | + font-weight: 500; | ||
| 883 | +} | ||
| 884 | + | ||
| 885 | +.status-inactive { | ||
| 886 | + color: #f56c6c; | ||
| 887 | + font-weight: 500; | ||
| 888 | +} | ||
| 889 | + | ||
| 890 | +/* 分页控制样式 */ | ||
| 891 | +.table-footer { | ||
| 892 | + padding: 8px 16px; | ||
| 893 | + border-top: 1px solid #e0e0e0; | ||
| 894 | + display: flex; | ||
| 895 | + justify-content: space-between; | ||
| 896 | + align-items: center; | ||
| 897 | +} | ||
| 898 | + | ||
| 899 | +.pagination-left { | ||
| 900 | + display: flex; | ||
| 901 | + align-items: center; | ||
| 902 | + gap: 20px; | ||
| 903 | +} | ||
| 904 | + | ||
| 905 | +.pagination-info { | ||
| 906 | + font-size: 12px; | ||
| 907 | + color: #666; | ||
| 908 | +} | ||
| 909 | + | ||
| 910 | +.page-size-selector { | ||
| 911 | + display: flex; | ||
| 912 | + align-items: center; | ||
| 913 | + gap: 8px; | ||
| 914 | +} | ||
| 915 | + | ||
| 916 | +.page-size-selector label { | ||
| 917 | + font-size: 12px; | ||
| 918 | + color: #666; | ||
| 919 | +} | ||
| 920 | + | ||
| 921 | +.page-size-select { | ||
| 922 | + padding: 4px 8px; | ||
| 923 | + border: 1px solid #d9d9d9; | ||
| 924 | + border-radius: 4px; | ||
| 925 | + font-size: 12px; | ||
| 926 | + background: white; | ||
| 927 | + cursor: pointer; | ||
| 928 | + transition: border-color 0.3s; | ||
| 929 | +} | ||
| 930 | + | ||
| 931 | +.page-size-select:hover { | ||
| 932 | + border-color: #1890ff; | ||
| 933 | +} | ||
| 934 | + | ||
| 935 | +.page-size-select:focus { | ||
| 936 | + outline: none; | ||
| 937 | + border-color: #1890ff; | ||
| 938 | + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); | ||
| 939 | +} | ||
| 940 | + | ||
| 941 | +/* 新的分页容器样式 - 模拟截图样式 */ | ||
| 942 | +.pagination-container { | ||
| 943 | + display: flex; | ||
| 944 | + align-items: center; | ||
| 945 | + background: #f8f9fa; | ||
| 946 | + border: 1px solid #e9ecef; | ||
| 947 | + border-radius: 8px; | ||
| 948 | + overflow: hidden; | ||
| 949 | + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); | ||
| 950 | +} | ||
| 951 | + | ||
| 952 | +.pagination-btn { | ||
| 953 | + padding: 2px 6px; | ||
| 954 | + border: none; | ||
| 955 | + background: white; | ||
| 956 | + color: #6c757d; | ||
| 957 | + font-size: 11px; | ||
| 958 | + font-weight: 500; | ||
| 959 | + cursor: pointer; | ||
| 960 | + transition: all 0.2s; | ||
| 961 | + border-right: 1px solid #e9ecef; | ||
| 962 | + height: 20px; | ||
| 963 | +} | ||
| 964 | + | ||
| 965 | +.pagination-btn:hover:not(:disabled) { | ||
| 966 | + background: #e9ecef; | ||
| 967 | + color: #495057; | ||
| 968 | +} | ||
| 969 | + | ||
| 970 | +.pagination-btn:disabled { | ||
| 971 | + background: #f8f9fa; | ||
| 972 | + color: #adb5bd; | ||
| 973 | + cursor: not-allowed; | ||
| 974 | +} | ||
| 975 | + | ||
| 976 | +.pagination-pages { | ||
| 977 | + display: flex; | ||
| 978 | + align-items: center; | ||
| 979 | +} | ||
| 980 | + | ||
| 981 | +.page-number { | ||
| 982 | + padding: 2px 6px; | ||
| 983 | + border: none; | ||
| 984 | + background: white; | ||
| 985 | + color: #6c757d; | ||
| 986 | + font-size: 11px; | ||
| 987 | + font-weight: 500; | ||
| 988 | + cursor: pointer; | ||
| 989 | + transition: all 0.2s; | ||
| 990 | + border-right: 1px solid #e9ecef; | ||
| 991 | + min-width: 28px; | ||
| 992 | + text-align: center; | ||
| 993 | + height: 20px; | ||
| 994 | +} | ||
| 995 | + | ||
| 996 | +.page-number:hover { | ||
| 997 | + background: #e9ecef; | ||
| 998 | + color: #495057; | ||
| 999 | +} | ||
| 1000 | + | ||
| 1001 | +.page-number.active { | ||
| 1002 | + background: #6c757d; | ||
| 1003 | + color: white; | ||
| 1004 | + font-weight: 600; | ||
| 1005 | +} | ||
| 1006 | + | ||
| 1007 | +.page-number.active:hover { | ||
| 1008 | + background: #5a6268; | ||
| 1009 | +} | ||
| 1010 | + | ||
| 1011 | + | ||
| 1012 | +/* 对话框样式 */ | ||
| 1013 | +.dialog-overlay { | ||
| 1014 | + position: fixed; | ||
| 1015 | + top: 0; | ||
| 1016 | + left: 0; | ||
| 1017 | + right: 0; | ||
| 1018 | + bottom: 0; | ||
| 1019 | + background: rgba(0, 0, 0, 0.5); | ||
| 1020 | + display: flex; | ||
| 1021 | + align-items: center; | ||
| 1022 | + justify-content: center; | ||
| 1023 | + z-index: 1000; | ||
| 1024 | +} | ||
| 1025 | + | ||
| 1026 | +.dialog-content { | ||
| 1027 | + background: white; | ||
| 1028 | + border-radius: 8px; | ||
| 1029 | + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); | ||
| 1030 | + width: 600px; | ||
| 1031 | + max-height: 80vh; | ||
| 1032 | + overflow-y: auto; | ||
| 1033 | +} | ||
| 1034 | + | ||
| 1035 | +.dialog-header { | ||
| 1036 | + padding: 16px 20px; | ||
| 1037 | + border-bottom: 1px solid #e0e0e0; | ||
| 1038 | + display: flex; | ||
| 1039 | + justify-content: space-between; | ||
| 1040 | + align-items: center; | ||
| 1041 | +} | ||
| 1042 | + | ||
| 1043 | +.dialog-header h3 { | ||
| 1044 | + margin: 0; | ||
| 1045 | + font-size: 16px; | ||
| 1046 | + font-weight: 600; | ||
| 1047 | + color: #333; | ||
| 1048 | +} | ||
| 1049 | + | ||
| 1050 | +.close-btn { | ||
| 1051 | + background: none; | ||
| 1052 | + border: none; | ||
| 1053 | + font-size: 20px; | ||
| 1054 | + cursor: pointer; | ||
| 1055 | + color: #999; | ||
| 1056 | + padding: 0; | ||
| 1057 | + width: 24px; | ||
| 1058 | + height: 24px; | ||
| 1059 | + display: flex; | ||
| 1060 | + align-items: center; | ||
| 1061 | + justify-content: center; | ||
| 1062 | +} | ||
| 1063 | + | ||
| 1064 | +.dialog-body { | ||
| 1065 | + padding: 20px; | ||
| 1066 | +} | ||
| 1067 | + | ||
| 1068 | +.form-row { | ||
| 1069 | + display: flex; | ||
| 1070 | + gap: 20px; | ||
| 1071 | + margin-bottom: 16px; | ||
| 1072 | +} | ||
| 1073 | + | ||
| 1074 | +.form-item { | ||
| 1075 | + flex: 1; | ||
| 1076 | + display: flex; | ||
| 1077 | + flex-direction: column; | ||
| 1078 | + gap: 6px; | ||
| 1079 | +} | ||
| 1080 | + | ||
| 1081 | +.form-item.full-width { | ||
| 1082 | + flex: 100%; | ||
| 1083 | +} | ||
| 1084 | + | ||
| 1085 | +.form-item label { | ||
| 1086 | + font-size: 14px; | ||
| 1087 | + font-weight: 500; | ||
| 1088 | + color: #333; | ||
| 1089 | +} | ||
| 1090 | + | ||
| 1091 | +.form-item label.required::after { | ||
| 1092 | + content: ' *'; | ||
| 1093 | + color: #ff4d4f; | ||
| 1094 | +} | ||
| 1095 | + | ||
| 1096 | +.form-input, .form-select, .form-textarea { | ||
| 1097 | + padding: 8px 12px; | ||
| 1098 | + border: 1px solid #d9d9d9; | ||
| 1099 | + border-radius: 4px; | ||
| 1100 | + font-size: 14px; | ||
| 1101 | + transition: border-color 0.2s; | ||
| 1102 | +} | ||
| 1103 | + | ||
| 1104 | +.form-input:focus, .form-select:focus, .form-textarea:focus { | ||
| 1105 | + outline: none; | ||
| 1106 | + border-color: #1890ff; | ||
| 1107 | + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); | ||
| 1108 | +} | ||
| 1109 | + | ||
| 1110 | +.form-textarea { | ||
| 1111 | + resize: vertical; | ||
| 1112 | + min-height: 60px; | ||
| 1113 | +} | ||
| 1114 | + | ||
| 1115 | +.form-actions { | ||
| 1116 | + display: flex; | ||
| 1117 | + gap: 12px; | ||
| 1118 | + justify-content: flex-end; | ||
| 1119 | + margin-top: 20px; | ||
| 1120 | + padding-top: 16px; | ||
| 1121 | + border-top: 1px solid #e0e0e0; | ||
| 1122 | +} | ||
| 1123 | + | ||
| 1124 | +.cancel-btn, .submit-btn { | ||
| 1125 | + padding: 8px 16px; | ||
| 1126 | + border: none; | ||
| 1127 | + border-radius: 4px; | ||
| 1128 | + font-size: 14px; | ||
| 1129 | + cursor: pointer; | ||
| 1130 | + transition: all 0.2s; | ||
| 1131 | +} | ||
| 1132 | + | ||
| 1133 | +.cancel-btn { | ||
| 1134 | + background: #f5f5f5; | ||
| 1135 | + color: #666; | ||
| 1136 | +} | ||
| 1137 | + | ||
| 1138 | +.cancel-btn:hover { | ||
| 1139 | + background: #e6e6e6; | ||
| 1140 | +} | ||
| 1141 | + | ||
| 1142 | +.submit-btn { | ||
| 1143 | + background: #1890ff; | ||
| 1144 | + color: white; | ||
| 1145 | +} | ||
| 1146 | + | ||
| 1147 | +.submit-btn:hover { | ||
| 1148 | + background: #40a9ff; | ||
| 1149 | +} | ||
| 1150 | + | ||
| 1151 | +/* 加载状态样式 */ | ||
| 1152 | +.loading-overlay { | ||
| 1153 | + position: absolute; | ||
| 1154 | + top: 0; | ||
| 1155 | + left: 0; | ||
| 1156 | + right: 0; | ||
| 1157 | + bottom: 0; | ||
| 1158 | + background: rgba(255, 255, 255, 0.8); | ||
| 1159 | + display: flex; | ||
| 1160 | + align-items: center; | ||
| 1161 | + justify-content: center; | ||
| 1162 | + z-index: 10; | ||
| 1163 | +} | ||
| 1164 | + | ||
| 1165 | +.loading-spinner { | ||
| 1166 | + padding: 20px; | ||
| 1167 | + background: white; | ||
| 1168 | + border-radius: 8px; | ||
| 1169 | + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); | ||
| 1170 | + font-size: 14px; | ||
| 1171 | + color: #666; | ||
| 1172 | +} | ||
| 1173 | + | ||
| 1174 | +/* 响应式设计 */ | ||
| 1175 | +@media (max-width: 768px) { | ||
| 1176 | + .search-row { | ||
| 1177 | + flex-direction: column; | ||
| 1178 | + align-items: stretch; | ||
| 1179 | + } | ||
| 1180 | + | ||
| 1181 | + .search-item { | ||
| 1182 | + width: 100%; | ||
| 1183 | + } | ||
| 1184 | + | ||
| 1185 | + .search-input, .search-select, .date-input { | ||
| 1186 | + width: 100%; | ||
| 1187 | + } | ||
| 1188 | + | ||
| 1189 | + .action-buttons { | ||
| 1190 | + flex-wrap: wrap; | ||
| 1191 | + } | ||
| 1192 | + | ||
| 1193 | + .dialog-content { | ||
| 1194 | + width: 90%; | ||
| 1195 | + } | ||
| 1196 | + | ||
| 1197 | + .form-row { | ||
| 1198 | + flex-direction: column; | ||
| 1199 | + } | ||
| 1200 | +} | ||
| 1201 | +</style> |
frontend/前端布局代码模板.md
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="users-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="searchForm.username" | ||
| 10 | + type="text" | ||
| 11 | + class="search-input" | ||
| 12 | + placeholder="请输入登录名称" | ||
| 13 | + /> | ||
| 14 | + </div> | ||
| 15 | + <div class="search-item"> | ||
| 16 | + <label>用户状态:</label> | ||
| 17 | + <select v-model="searchForm.status" class="search-select"> | ||
| 18 | + <option value="">所有</option> | ||
| 19 | + <option value="1">正常</option> | ||
| 20 | + <option value="0">停用</option> | ||
| 21 | + </select> | ||
| 22 | + </div> | ||
| 23 | + <div class="search-item"> | ||
| 24 | + <label>创建时间:</label> | ||
| 25 | + <div class="date-range"> | ||
| 26 | + <div class="date-picker-container"> | ||
| 27 | + <input | ||
| 28 | + v-model="searchForm.startTime" | ||
| 29 | + type="text" | ||
| 30 | + class="date-input" | ||
| 31 | + placeholder="开始时间" | ||
| 32 | + readonly | ||
| 33 | + @click="toggleStartDatePicker" | ||
| 34 | + @mouseenter="showStartDateOptions = true" | ||
| 35 | + @mouseleave="hideStartDateOptions" | ||
| 36 | + /> | ||
| 37 | + <div v-if="showStartDateOptions" class="date-options" @mouseenter="showStartDateOptions = true" @mouseleave="hideStartDateOptions"> | ||
| 38 | + <div class="date-option" @click="selectStartDate('today')">今天</div> | ||
| 39 | + <div class="date-option" @click="selectStartDate('yesterday')">昨天</div> | ||
| 40 | + <div class="date-option" @click="selectStartDate('thisWeek')">本周</div> | ||
| 41 | + <div class="date-option" @click="selectStartDate('lastWeek')">上周</div> | ||
| 42 | + <div class="date-option" @click="selectStartDate('thisMonth')">本月</div> | ||
| 43 | + <div class="date-option" @click="selectStartDate('lastMonth')">上月</div> | ||
| 44 | + <div class="date-option" @click="selectStartDate('custom')">自定义</div> | ||
| 45 | + </div> | ||
| 46 | + <!-- 自定义日期选择器 --> | ||
| 47 | + <div v-if="showStartDatePicker" class="custom-date-picker"> | ||
| 48 | + <input | ||
| 49 | + type="date" | ||
| 50 | + v-model="searchForm.startTime" | ||
| 51 | + @change="showStartDatePicker = false" | ||
| 52 | + class="date-input" | ||
| 53 | + /> | ||
| 54 | + </div> | ||
| 55 | + </div> | ||
| 56 | + <span class="date-separator">-</span> | ||
| 57 | + <div class="date-picker-container"> | ||
| 58 | + <input | ||
| 59 | + v-model="searchForm.endTime" | ||
| 60 | + type="text" | ||
| 61 | + class="date-input" | ||
| 62 | + placeholder="结束时间" | ||
| 63 | + readonly | ||
| 64 | + @click="toggleEndDatePicker" | ||
| 65 | + @mouseenter="showEndDateOptions = true" | ||
| 66 | + @mouseleave="hideEndDateOptions" | ||
| 67 | + /> | ||
| 68 | + <div v-if="showEndDateOptions" class="date-options" @mouseenter="showEndDateOptions = true" @mouseleave="hideEndDateOptions"> | ||
| 69 | + <div class="date-option" @click="selectEndDate('today')">今天</div> | ||
| 70 | + <div class="date-option" @click="selectEndDate('yesterday')">昨天</div> | ||
| 71 | + <div class="date-option" @click="selectEndDate('thisWeek')">本周</div> | ||
| 72 | + <div class="date-option" @click="selectEndDate('lastWeek')">上周</div> | ||
| 73 | + <div class="date-option" @click="selectEndDate('thisMonth')">本月</div> | ||
| 74 | + <div class="date-option" @click="selectEndDate('lastMonth')">上月</div> | ||
| 75 | + <div class="date-option" @click="selectEndDate('custom')">自定义</div> | ||
| 76 | + </div> | ||
| 77 | + <!-- 自定义日期选择器 --> | ||
| 78 | + <div v-if="showEndDatePicker" class="custom-date-picker"> | ||
| 79 | + <input | ||
| 80 | + type="date" | ||
| 81 | + v-model="searchForm.endTime" | ||
| 82 | + @change="showEndDatePicker = false" | ||
| 83 | + class="date-input" | ||
| 84 | + /> | ||
| 85 | + </div> | ||
| 86 | + </div> | ||
| 87 | + </div> | ||
| 88 | + </div> | ||
| 89 | + <div class="search-actions"> | ||
| 90 | + <button @click="handleSearch" class="search-btn">🔍 搜索</button> | ||
| 91 | + <button @click="handleReset" class="reset-btn">🔄 重置</button> | ||
| 92 | + </div> | ||
| 93 | + </div> | ||
| 94 | + </div> | ||
| 95 | + | ||
| 96 | + <!-- 操作按钮区域 --> | ||
| 97 | + <div class="action-section"> | ||
| 98 | + <div class="action-buttons"> | ||
| 99 | + <button @click="handleAdd" class="action-btn primary">✨ 新增</button> | ||
| 100 | + <button @click="handleBatchDelete" class="action-btn danger">🗑️ 删除</button> | ||
| 101 | + </div> | ||
| 102 | + </div> | ||
| 103 | + | ||
| 104 | + <!-- 数据表格 --> | ||
| 105 | + <div class="table-section"> | ||
| 106 | + <div class="table-header"> | ||
| 107 | + <div class="table-controls"> | ||
| 108 | + <button @click="handleTableSearch" class="control-btn" title="搜索">🔍</button> | ||
| 109 | + <button @click="handleTableRefresh" class="control-btn" title="刷新">🔄</button> | ||
| 110 | + <button @click="handleTableExport" class="control-btn" title="导出">📋</button> | ||
| 111 | + <button @click="handleTableViewToggle" class="control-btn" title="视图切换">⊞</button> | ||
| 112 | + </div> | ||
| 113 | + </div> | ||
| 114 | + | ||
| 115 | + <div class="table-container"> | ||
| 116 | + <div v-if="loading" class="loading-overlay"> | ||
| 117 | + <div class="loading-spinner">加载中...</div> | ||
| 118 | + </div> | ||
| 119 | + <table class="data-table"> | ||
| 120 | + <thead> | ||
| 121 | + <tr> | ||
| 122 | + <th> | ||
| 123 | + <input | ||
| 124 | + type="checkbox" | ||
| 125 | + class="select-all" | ||
| 126 | + v-model="selectAll" | ||
| 127 | + @change="handleSelectAll" | ||
| 128 | + /> | ||
| 129 | + <th>操作</th> | ||
| 130 | + </tr> | ||
| 131 | + </thead> | ||
| 132 | + <tbody> | ||
| 133 | + <tr v-for="user in users" :key="user.userId"> | ||
| 134 | + <td> | ||
| 135 | + <button @click="handleEdit(user)" class="table-btn edit">✏️ 编辑</button> | ||
| 136 | + <button @click="handleDelete(user)" class="table-btn delete">🗑️ 删除</button> | ||
| 137 | + </td> | ||
| 138 | + </tr> | ||
| 139 | + </tbody> | ||
| 140 | + </table> | ||
| 141 | + </div> | ||
| 142 | + | ||
| 143 | + <!-- 分页信息 --> | ||
| 144 | + <div class="table-footer"> | ||
| 145 | + <div class="pagination-left"> | ||
| 146 | + <div class="pagination-info"> | ||
| 147 | + 共 {{ total }} 条记录,第 {{ currentPage }} / {{ totalPages }} 页 | ||
| 148 | + </div> | ||
| 149 | + <div class="page-size-selector"> | ||
| 150 | + <label>每页显示:</label> | ||
| 151 | + <select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select"> | ||
| 152 | + <option value="10">10条</option> | ||
| 153 | + <option value="20">20条</option> | ||
| 154 | + <option value="50">50条</option> | ||
| 155 | + <option value="100">100条</option> | ||
| 156 | + </select> | ||
| 157 | + </div> | ||
| 158 | + </div> | ||
| 159 | + <div class="pagination-container"> | ||
| 160 | + <button | ||
| 161 | + @click="handlePageChange(currentPage - 1)" | ||
| 162 | + :disabled="currentPage <= 1" | ||
| 163 | + class="pagination-btn prev-btn" | ||
| 164 | + > | ||
| 165 | + 上一页 | ||
| 166 | + </button> | ||
| 167 | + <div class="pagination-pages"> | ||
| 168 | + <button | ||
| 169 | + v-for="page in getPageNumbers()" | ||
| 170 | + :key="page" | ||
| 171 | + @click="handlePageChange(page)" | ||
| 172 | + :class="['page-number', { active: page === currentPage }]" | ||
| 173 | + > | ||
| 174 | + {{ page }} | ||
| 175 | + </button> | ||
| 176 | + </div> | ||
| 177 | + <button | ||
| 178 | + @click="handlePageChange(currentPage + 1)" | ||
| 179 | + :disabled="currentPage >= totalPages" | ||
| 180 | + class="pagination-btn next-btn" | ||
| 181 | + > | ||
| 182 | + 下一页 | ||
| 183 | + </button> | ||
| 184 | + </div> | ||
| 185 | + </div> | ||
| 186 | + </div> | ||
| 187 | + | ||
| 188 | + </div> | ||
| 189 | + </div> | ||
| 190 | + </div> | ||
| 191 | +</template> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
-
Please register or login to post a comment