zhouhui.jiang

update 基础数据添加

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.DealerInfo;
4 +import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5 +import org.apache.ibatis.annotations.Mapper;
6 +
7 +@Mapper
8 +public interface DealerInfoMapper extends BaseMapper<DealerInfo> {
9 +}
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 +-- 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);
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 }
......
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 +}
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'),
......
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
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>
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