zhouhui.jiang

订单、出库 ,发票

Showing 53 changed files with 8494 additions and 93 deletions
......@@ -1370,6 +1370,934 @@
---
## 订单管理接口
### 1. 分页查询订单列表
**接口路径:** `GET /api/order/list`
**请求方法:** GET
**权限要求:** `order:list`
**请求参数:**
| 参数名 | 类型 | 必填 | 描述 | 示例 |
|--------|------|------|------|------|
| orderNo | String | 否 | 订单编号 | ORD-2024-001 |
| dealerCode | String | 否 | 经销商编码 | APL-DLR-001 |
| dealerName | String | 否 | 经销商名称 | 北京经销商 |
| deliveryStatus | Integer | 否 | 出库状态(0-未出库/1-已出库) | 1 |
| invoiceStatus | Integer | 否 | 开票状态(0-未开票/1-已开票) | 1 |
| rebateCalcFlag | Integer | 否 | 返利计算状态(0-未计算/1-已计算) | 1 |
| dataSource | String | 否 | 数据来源 | ERP系统 |
| verifyStatus | Integer | 否 | 数据验证状态(0-待验证/1-验证通过/2-验证失败) | 1 |
| orderStartDate | String | 否 | 订单开始日期 | 2024-01-01T00:00:00 |
| orderEndDate | String | 否 | 订单结束日期 | 2024-12-31T23:59:59 |
| minAmount | BigDecimal | 否 | 金额最小值 | 1000.00 |
| maxAmount | BigDecimal | 否 | 金额最大值 | 100000.00 |
| pageNum | Integer | 是 | 页码 | 1 |
| pageSize | Integer | 是 | 每页大小 | 10 |
**响应示例:**
```json
{
"code": 200,
"message": "操作成功",
"data": {
"records": [
{
"orderId": 1,
"orderNo": "ORD-2024-001",
"dealerCode": "APL-DLR-001",
"dealerName": "北京经销商",
"orderDate": "2024-01-15T10:30:00",
"totalAmount": 119980.00,
"rebateAmount": 5999.00,
"deliveryStatus": 1,
"invoiceStatus": 1,
"rebateCalcFlag": 1,
"dataSource": "ERP系统",
"verifyStatus": 1,
"uploadTime": "2024-01-15T10:35:00",
"createBy": "admin",
"createTime": "2024-01-15T10:30:00",
"updateBy": "",
"updateTime": "2024-01-15T10:30:00"
}
],
"total": 1,
"size": 10,
"current": 1,
"pages": 1
}
}
```
### 2. 获取订单详情
**接口路径:** `GET /api/order/{orderId}`
**请求方法:** GET
**权限要求:** `order:detail`
**路径参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| orderId | Long | 是 | 订单ID |
**响应示例:**
```json
{
"code": 200,
"message": "操作成功",
"data": {
"orderId": 1,
"orderNo": "ORD-2024-001",
"dealerCode": "APL-DLR-001",
"dealerName": "北京经销商",
"orderDate": "2024-01-15T10:30:00",
"totalAmount": 119980.00,
"rebateAmount": 5999.00,
"deliveryStatus": 1,
"invoiceStatus": 1,
"rebateCalcFlag": 1,
"dataSource": "ERP系统",
"verifyStatus": 1,
"uploadTime": "2024-01-15T10:35:00",
"createBy": "admin",
"createTime": "2024-01-15T10:30:00",
"updateBy": "",
"updateTime": "2024-01-15T10:30:00",
"orderItems": [
{
"itemId": 1,
"orderId": 1,
"orderNo": "ORD-2024-001",
"productCode": "APL-IP15-128G-BK",
"productName": "iPhone 15 128GB 黑色",
"productSpec": "128GB/黑色",
"productType": "手机",
"unitPrice": 5999.00,
"quantity": 20,
"itemAmount": 119980.00,
"rebateRate": 0.05,
"rebateAmount": 5999.00,
"createBy": "admin",
"createTime": "2024-01-15T10:30:00",
"updateBy": "",
"updateTime": "2024-01-15T10:30:00"
}
]
}
}
```
### 3. 新增订单
**接口路径:** `POST /api/order`
**请求方法:** POST
**权限要求:** `order:add`
**请求体参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| orderNo | String | 是 | 订单编号 |
| dealerCode | String | 是 | 经销商编码 |
| dealerName | String | 是 | 经销商名称 |
| orderDate | String | 是 | 订单创建日期 |
| totalAmount | BigDecimal | 是 | 订单总金额 |
| rebateAmount | BigDecimal | 否 | 订单返利金额 |
| deliveryStatus | Integer | 否 | 出库状态(0-未出库/1-已出库) |
| invoiceStatus | Integer | 否 | 开票状态(0-未开票/1-已开票) |
| rebateCalcFlag | Integer | 否 | 返利计算状态(0-未计算/1-已计算) |
| dataSource | String | 否 | 数据来源 |
| verifyStatus | Integer | 否 | 数据验证状态(0-待验证/1-验证通过/2-验证失败) |
| uploadTime | String | 否 | 数据上传时间 |
| orderItems | Array | 是 | 订单明细列表 |
**订单明细参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| productCode | String | 是 | 产品编码 |
| productName | String | 是 | 产品名称 |
| productSpec | String | 否 | 产品规格 |
| productType | String | 否 | 产品类别 |
| unitPrice | BigDecimal | 是 | 产品单价 |
| quantity | Integer | 是 | 订购数量 |
| itemAmount | BigDecimal | 否 | 明细金额 |
| rebateRate | BigDecimal | 否 | 返利比例 |
| rebateAmount | BigDecimal | 否 | 返利金额 |
**请求示例:**
```json
{
"orderNo": "ORD-2024-001",
"dealerCode": "APL-DLR-001",
"dealerName": "北京经销商",
"orderDate": "2024-01-15T10:30:00",
"totalAmount": 119980.00,
"rebateAmount": 5999.00,
"deliveryStatus": 0,
"invoiceStatus": 0,
"rebateCalcFlag": 0,
"dataSource": "ERP系统",
"verifyStatus": 0,
"orderItems": [
{
"productCode": "APL-IP15-128G-BK",
"productName": "iPhone 15 128GB 黑色",
"productSpec": "128GB/黑色",
"productType": "手机",
"unitPrice": 5999.00,
"quantity": 20,
"itemAmount": 119980.00,
"rebateRate": 0.05,
"rebateAmount": 5999.00
}
]
}
```
**响应示例:**
```json
{
"code": 200,
"message": "新增订单成功",
"data": null
}
```
### 4. 修改订单
**接口路径:** `POST /api/order/update`
**请求方法:** POST
**权限要求:** `order:edit`
**请求体参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| orderId | Long | 是 | 订单ID |
| orderNo | String | 是 | 订单编号 |
| dealerCode | String | 是 | 经销商编码 |
| dealerName | String | 是 | 经销商名称 |
| orderDate | String | 是 | 订单创建日期 |
| totalAmount | BigDecimal | 是 | 订单总金额 |
| rebateAmount | BigDecimal | 否 | 订单返利金额 |
| deliveryStatus | Integer | 否 | 出库状态(0-未出库/1-已出库) |
| invoiceStatus | Integer | 否 | 开票状态(0-未开票/1-已开票) |
| rebateCalcFlag | Integer | 否 | 返利计算状态(0-未计算/1-已计算) |
| dataSource | String | 否 | 数据来源 |
| verifyStatus | Integer | 否 | 数据验证状态(0-待验证/1-验证通过/2-验证失败) |
| uploadTime | String | 否 | 数据上传时间 |
| orderItems | Array | 是 | 订单明细列表 |
**响应示例:**
```json
{
"code": 200,
"message": "修改订单成功",
"data": null
}
```
### 5. 删除订单
**接口路径:** `DELETE /api/order/{orderId}`
**请求方法:** DELETE
**权限要求:** `order:delete`
**路径参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| orderId | Long | 是 | 订单ID |
**响应示例:**
```json
{
"code": 200,
"message": "删除订单成功",
"data": null
}
```
### 6. 批量删除订单
**接口路径:** `POST /api/order/batchDelete`
**请求方法:** POST
**权限要求:** `order:delete`
**请求体参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| - | Array | 是 | 订单ID列表 |
**请求示例:**
```json
[1, 2, 3]
```
**响应示例:**
```json
{
"code": 200,
"message": "批量删除订单成功",
"data": null
}
```
### 7. 修改订单出库状态
**接口路径:** `POST /api/order/{orderId}/deliveryStatus`
**请求方法:** POST
**权限要求:** `order:edit`
**路径参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| orderId | Long | 是 | 订单ID |
**请求参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| deliveryStatus | Integer | 是 | 出库状态(0-未出库/1-已出库) |
**响应示例:**
```json
{
"code": 200,
"message": "修改出库状态成功",
"data": null
}
```
### 8. 修改订单开票状态
**接口路径:** `POST /api/order/{orderId}/invoiceStatus`
**请求方法:** POST
**权限要求:** `order:edit`
**路径参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| orderId | Long | 是 | 订单ID |
**请求参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| invoiceStatus | Integer | 是 | 开票状态(0-未开票/1-已开票) |
**响应示例:**
```json
{
"code": 200,
"message": "修改开票状态成功",
"data": null
}
```
### 9. 修改订单返利计算状态
**接口路径:** `POST /api/order/{orderId}/rebateCalcFlag`
**请求方法:** POST
**权限要求:** `order:edit`
**路径参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| orderId | Long | 是 | 订单ID |
**请求参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| rebateCalcFlag | Integer | 是 | 返利计算状态(0-未计算/1-已计算) |
**响应示例:**
```json
{
"code": 200,
"message": "修改返利计算状态成功",
"data": null
}
```
---
## 出库管理接口
### 1. 分页查询出库列表
**接口路径:** `GET /api/delivery/list`
**请求方法:** GET
**权限要求:** `delivery:list`
**请求参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| pageNum | Integer | 是 | 页码,从1开始 |
| pageSize | Integer | 是 | 每页大小 |
| deliveryNo | String | 否 | 出库单编号 |
| dealerCode | String | 否 | 经销商编码 |
| dealerName | String | 否 | 经销商名称 |
| orderNo | String | 否 | 关联订单编号 |
| deliveryStatus | Integer | 否 | 出库状态(0-未出库/1-已出库) |
| warehouseCode | String | 否 | 出库仓库编码 |
| dataSource | String | 否 | 数据来源 |
| deliveryStartDate | String | 否 | 出库开始日期(格式:yyyy-MM-dd HH:mm:ss) |
| deliveryEndDate | String | 否 | 出库结束日期(格式:yyyy-MM-dd HH:mm:ss) |
**响应示例:**
```json
{
"code": 200,
"message": "查询成功",
"data": {
"records": [
{
"deliveryId": 1,
"deliveryNo": "DL202501270001",
"dealerCode": "DL001",
"dealerName": "北京经销商",
"orderNo": "ORD202501270001",
"deliveryDate": "2025-01-27 10:00:00",
"deliveryStatus": 1,
"warehouseCode": "WH001",
"dataSource": "系统录入",
"uploadTime": "2025-01-27 09:30:00",
"createBy": "admin",
"createTime": "2025-01-27 09:30:00",
"updateBy": "admin",
"updateTime": "2025-01-27 10:00:00",
"deliveryItems": [
{
"deliveryItemId": 1,
"deliveryId": 1,
"deliveryNo": "DL202501270001",
"orderNo": "ORD202501270001",
"productCode": "P001",
"productName": "iPhone 15",
"deliveryQty": 10,
"deliveryPrice": 5999.00,
"deliveryAmount": 59990.00,
"createBy": "admin",
"createTime": "2025-01-27 09:30:00",
"updateBy": "admin",
"updateTime": "2025-01-27 10:00:00"
}
]
}
],
"total": 1,
"size": 10,
"current": 1,
"pages": 1
}
}
```
### 2. 获取出库详情
**接口路径:** `GET /api/delivery/{deliveryId}`
**请求方法:** GET
**权限要求:** `delivery:detail`
**路径参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| deliveryId | Long | 是 | 出库单ID |
**响应示例:**
```json
{
"code": 200,
"message": "查询成功",
"data": {
"deliveryId": 1,
"deliveryNo": "DL202501270001",
"dealerCode": "DL001",
"dealerName": "北京经销商",
"orderNo": "ORD202501270001",
"deliveryDate": "2025-01-27 10:00:00",
"deliveryStatus": 1,
"warehouseCode": "WH001",
"dataSource": "系统录入",
"uploadTime": "2025-01-27 09:30:00",
"createBy": "admin",
"createTime": "2025-01-27 09:30:00",
"updateBy": "admin",
"updateTime": "2025-01-27 10:00:00",
"deliveryItems": [
{
"deliveryItemId": 1,
"deliveryId": 1,
"deliveryNo": "DL202501270001",
"orderNo": "ORD202501270001",
"productCode": "P001",
"productName": "iPhone 15",
"deliveryQty": 10,
"deliveryPrice": 5999.00,
"deliveryAmount": 59990.00,
"createBy": "admin",
"createTime": "2025-01-27 09:30:00",
"updateBy": "admin",
"updateTime": "2025-01-27 10:00:00"
}
]
}
}
```
### 3. 新增出库
**接口路径:** `POST /api/delivery`
**请求方法:** POST
**权限要求:** `delivery:add`
**请求体:**
```json
{
"deliveryNo": "DL202501270001",
"dealerCode": "DL001",
"dealerName": "北京经销商",
"orderNo": "ORD202501270001",
"deliveryDate": "2025-01-27 10:00:00",
"deliveryStatus": 0,
"warehouseCode": "WH001",
"dataSource": "系统录入",
"uploadTime": "2025-01-27 09:30:00",
"deliveryItems": [
{
"productCode": "P001",
"productName": "iPhone 15",
"deliveryQty": 10,
"deliveryPrice": 5999.00,
"deliveryAmount": 59990.00
}
]
}
```
**响应示例:**
```json
{
"code": 200,
"message": "新增出库单成功",
"data": null
}
```
### 4. 修改出库
**接口路径:** `POST /api/delivery/update`
**请求方法:** POST
**权限要求:** `delivery:edit`
**请求体:**
```json
{
"deliveryId": 1,
"deliveryNo": "DL202501270001",
"dealerCode": "DL001",
"dealerName": "北京经销商",
"orderNo": "ORD202501270001",
"deliveryDate": "2025-01-27 10:00:00",
"deliveryStatus": 1,
"warehouseCode": "WH001",
"dataSource": "系统录入",
"uploadTime": "2025-01-27 09:30:00",
"deliveryItems": [
{
"deliveryItemId": 1,
"productCode": "P001",
"productName": "iPhone 15",
"deliveryQty": 15,
"deliveryPrice": 5999.00,
"deliveryAmount": 89985.00
}
]
}
```
**响应示例:**
```json
{
"code": 200,
"message": "修改出库单成功",
"data": null
}
```
### 5. 删除出库
**接口路径:** `DELETE /api/delivery/{deliveryId}`
**请求方法:** DELETE
**权限要求:** `delivery:delete`
**路径参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| deliveryId | Long | 是 | 出库单ID |
**响应示例:**
```json
{
"code": 200,
"message": "删除出库单成功",
"data": null
}
```
### 6. 批量删除出库
**接口路径:** `POST /api/delivery/batchDelete`
**请求方法:** POST
**权限要求:** `delivery:delete`
**请求体:**
```json
[1, 2, 3]
```
**响应示例:**
```json
{
"code": 200,
"message": "批量删除出库单成功",
"data": null
}
```
### 7. 修改出库状态
**接口路径:** `POST /api/delivery/{deliveryId}/deliveryStatus`
**请求方法:** POST
**权限要求:** `delivery:edit`
**路径参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| deliveryId | Long | 是 | 出库单ID |
**请求参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| deliveryStatus | Integer | 是 | 出库状态(0-未出库/1-已出库) |
**响应示例:**
```json
{
"code": 200,
"message": "修改出库状态成功",
"data": null
}
```
---
## 发票管理接口
### 1. 分页查询发票列表
**接口路径:** `GET /api/invoice/list`
**请求方法:** GET
**权限要求:** `invoice:list`
**请求参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| pageNum | Integer | 是 | 页码,从1开始 |
| pageSize | Integer | 是 | 每页大小 |
| invoiceNo | String | 否 | 发票编号 |
| orderNo | String | 否 | 关联订单编号 |
| deliveryNo | String | 否 | 关联出库单编号 |
| dealerCode | String | 否 | 经销商编码 |
| dealerName | String | 否 | 经销商名称 |
| invoiceStatus | Integer | 否 | 发票状态(0-未开票/1-已开票) |
| dataSource | String | 否 | 数据来源 |
| invoiceStartDate | String | 否 | 开票开始日期(格式:yyyy-MM-dd) |
| invoiceEndDate | String | 否 | 开票结束日期(格式:yyyy-MM-dd) |
| minAmount | BigDecimal | 否 | 金额最小值 |
| maxAmount | BigDecimal | 否 | 金额最大值 |
**响应示例:**
```json
{
"code": 200,
"message": "查询成功",
"data": {
"records": [
{
"invoiceId": 1,
"invoiceNo": "INV202501270001",
"orderNo": "ORD202501270001",
"deliveryNo": "DL202501270001",
"dealerCode": "DL001",
"dealerName": "北京经销商",
"totalAmount": 67890.00,
"invoiceDate": "2025-01-27",
"invoiceStatus": 1,
"taxRate": 13.00,
"dataSource": "系统录入",
"uploadTime": "2025-01-27 09:30:00",
"createBy": "admin",
"createTime": "2025-01-27 09:30:00",
"updateBy": "admin",
"updateTime": "2025-01-27 10:00:00",
"invoiceItems": [
{
"invoiceItemId": 1,
"invoiceId": 1,
"invoiceNo": "INV202501270001",
"orderNo": "ORD202501270001",
"productCode": "P001",
"productName": "iPhone 15",
"invoiceQty": 10,
"unitPriceNoTax": 5309.73,
"amountNoTax": 53097.30,
"taxAmount": 6902.70,
"createBy": "admin",
"createTime": "2025-01-27 09:30:00",
"updateBy": "admin",
"updateTime": "2025-01-27 10:00:00"
}
]
}
],
"total": 1,
"size": 10,
"current": 1,
"pages": 1
}
}
```
### 2. 获取发票详情
**接口路径:** `GET /api/invoice/{invoiceId}`
**请求方法:** GET
**权限要求:** `invoice:detail`
**路径参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| invoiceId | Long | 是 | 发票ID |
**响应示例:**
```json
{
"code": 200,
"message": "查询成功",
"data": {
"invoiceId": 1,
"invoiceNo": "INV202501270001",
"orderNo": "ORD202501270001",
"deliveryNo": "DL202501270001",
"dealerCode": "DL001",
"dealerName": "北京经销商",
"totalAmount": 67890.00,
"invoiceDate": "2025-01-27",
"invoiceStatus": 1,
"taxRate": 13.00,
"dataSource": "系统录入",
"uploadTime": "2025-01-27 09:30:00",
"createBy": "admin",
"createTime": "2025-01-27 09:30:00",
"updateBy": "admin",
"updateTime": "2025-01-27 10:00:00",
"invoiceItems": [
{
"invoiceItemId": 1,
"invoiceId": 1,
"invoiceNo": "INV202501270001",
"orderNo": "ORD202501270001",
"productCode": "P001",
"productName": "iPhone 15",
"invoiceQty": 10,
"unitPriceNoTax": 5309.73,
"amountNoTax": 53097.30,
"taxAmount": 6902.70,
"createBy": "admin",
"createTime": "2025-01-27 09:30:00",
"updateBy": "admin",
"updateTime": "2025-01-27 10:00:00"
}
]
}
}
```
### 3. 新增发票
**接口路径:** `POST /api/invoice`
**请求方法:** POST
**权限要求:** `invoice:add`
**请求体:**
```json
{
"invoiceNo": "INV202501270001",
"orderNo": "ORD202501270001",
"deliveryNo": "DL202501270001",
"dealerCode": "DL001",
"dealerName": "北京经销商",
"totalAmount": 67890.00,
"invoiceDate": "2025-01-27",
"invoiceStatus": 0,
"taxRate": 13.00,
"dataSource": "系统录入",
"uploadTime": "2025-01-27 09:30:00",
"invoiceItems": [
{
"productCode": "P001",
"productName": "iPhone 15",
"invoiceQty": 10,
"unitPriceNoTax": 5309.73,
"amountNoTax": 53097.30,
"taxAmount": 6902.70
}
]
}
```
**响应示例:**
```json
{
"code": 200,
"message": "新增发票成功",
"data": null
}
```
### 4. 修改发票
**接口路径:** `POST /api/invoice/update`
**请求方法:** POST
**权限要求:** `invoice:edit`
**请求体:**
```json
{
"invoiceId": 1,
"invoiceNo": "INV202501270001",
"orderNo": "ORD202501270001",
"deliveryNo": "DL202501270001",
"dealerCode": "DL001",
"dealerName": "北京经销商",
"totalAmount": 67890.00,
"invoiceDate": "2025-01-27",
"invoiceStatus": 1,
"taxRate": 13.00,
"dataSource": "系统录入",
"uploadTime": "2025-01-27 09:30:00",
"invoiceItems": [
{
"invoiceItemId": 1,
"productCode": "P001",
"productName": "iPhone 15",
"invoiceQty": 15,
"unitPriceNoTax": 5309.73,
"amountNoTax": 79645.95,
"taxAmount": 10353.05
}
]
}
```
**响应示例:**
```json
{
"code": 200,
"message": "修改发票成功",
"data": null
}
```
### 5. 删除发票
**接口路径:** `DELETE /api/invoice/{invoiceId}`
**请求方法:** DELETE
**权限要求:** `invoice:delete`
**路径参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| invoiceId | Long | 是 | 发票ID |
**响应示例:**
```json
{
"code": 200,
"message": "删除发票成功",
"data": null
}
```
### 6. 批量删除发票
**接口路径:** `POST /api/invoice/batchDelete`
**请求方法:** POST
**权限要求:** `invoice:delete`
**请求体:**
```json
[1, 2, 3]
```
**响应示例:**
```json
{
"code": 200,
"message": "批量删除发票成功",
"data": null
}
```
### 7. 修改发票状态
**接口路径:** `POST /api/invoice/{invoiceId}/invoiceStatus`
**请求方法:** POST
**权限要求:** `invoice:edit`
**路径参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| invoiceId | Long | 是 | 发票ID |
**请求参数:**
| 参数名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| invoiceStatus | Integer | 是 | 发票状态(0-未开票/1-已开票) |
**响应示例:**
```json
{
"code": 200,
"message": "修改发票状态成功",
"data": null
}
```
---
**文档版本:** 1.0.0
**最后更新:** 2025-01-27
**维护人员:** Apple ERP Team
......
package com.apple.erp.controller;
import com.apple.erp.dto.DeliveryAddReq;
import com.apple.erp.dto.DeliveryQueryReq;
import com.apple.erp.dto.DeliveryRes;
import com.apple.erp.dto.DeliveryUpdateReq;
import com.apple.erp.service.DeliveryMainService;
import com.apple.erp.dto.response.ApiRes;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
/**
* 出库管理Controller
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Tag(name = "出库管理", description = "出库管理相关接口")
@RestController
@RequestMapping("/api/delivery")
@Validated
public class DeliveryMainController {
@Autowired
private DeliveryMainService deliveryMainService;
@Operation(summary = "分页查询出库列表", description = "根据查询条件分页获取出库列表")
@GetMapping("/list")
@PreAuthorize("hasAuthority('delivery:list')")
public ApiRes<Page<DeliveryRes>> getDeliveryList(@Valid DeliveryQueryReq queryReq) {
Page<DeliveryRes> page = deliveryMainService.getDeliveryList(queryReq);
return ApiRes.success(page);
}
@Operation(summary = "获取出库详情", description = "根据出库单ID获取出库详细信息,包含出库明细")
@GetMapping("/{deliveryId}")
@PreAuthorize("hasAuthority('delivery:detail')")
public ApiRes<DeliveryRes> getDeliveryDetail(
@Parameter(description = "出库单ID", required = true) @PathVariable Long deliveryId) {
DeliveryRes deliveryRes = deliveryMainService.getDeliveryDetail(deliveryId);
if (deliveryRes != null) {
return ApiRes.success(deliveryRes);
}
return ApiRes.error("出库单不存在或已删除");
}
@Operation(summary = "新增出库", description = "新增一个出库单,包含出库主表和明细")
@PostMapping
@PreAuthorize("hasAuthority('delivery:add')")
public ApiRes<Void> addDelivery(@Valid @RequestBody DeliveryAddReq addReq) {
try {
boolean result = deliveryMainService.addDelivery(addReq);
if (result) {
return ApiRes.success("新增出库单成功", null);
} else {
return ApiRes.error("新增出库单失败");
}
} catch (Exception e) {
return ApiRes.error("新增出库单失败: " + e.getMessage());
}
}
@Operation(summary = "修改出库", description = "修改现有出库单信息,包含出库主表和明细")
@PostMapping("/update")
@PreAuthorize("hasAuthority('delivery:edit')")
public ApiRes<Void> updateDelivery(@Valid @RequestBody DeliveryUpdateReq updateReq) {
try {
boolean result = deliveryMainService.updateDelivery(updateReq);
if (result) {
return ApiRes.success("修改出库单成功", null);
} else {
return ApiRes.error("修改出库单失败");
}
} catch (Exception e) {
return ApiRes.error("修改出库单失败: " + e.getMessage());
}
}
@Operation(summary = "删除出库", description = "根据出库单ID逻辑删除出库单")
@DeleteMapping("/{deliveryId}")
@PreAuthorize("hasAuthority('delivery:delete')")
public ApiRes<Void> deleteDelivery(
@Parameter(description = "出库单ID", required = true) @PathVariable Long deliveryId) {
try {
boolean result = deliveryMainService.deleteDelivery(deliveryId);
if (result) {
return ApiRes.success("删除出库单成功", null);
} else {
return ApiRes.error("删除出库单失败");
}
} catch (Exception e) {
return ApiRes.error("删除出库单失败: " + e.getMessage());
}
}
@Operation(summary = "批量删除出库", description = "根据出库单ID列表批量逻辑删除出库单")
@PostMapping("/batchDelete")
@PreAuthorize("hasAuthority('delivery:delete')")
public ApiRes<Void> batchDeleteDeliveries(
@Parameter(description = "出库单ID列表", required = true) @RequestBody List<Long> deliveryIds) {
try {
if (deliveryIds == null || deliveryIds.isEmpty()) {
return ApiRes.error("出库单ID列表不能为空");
}
boolean result = deliveryMainService.batchDeleteDeliveries(deliveryIds);
if (result) {
return ApiRes.success("批量删除出库单成功", null);
} else {
return ApiRes.error("批量删除出库单失败");
}
} catch (Exception e) {
return ApiRes.error("批量删除出库单失败: " + e.getMessage());
}
}
@Operation(summary = "修改出库状态", description = "修改指定出库单的出库状态")
@PostMapping("/{deliveryId}/deliveryStatus")
@PreAuthorize("hasAuthority('delivery:edit')")
public ApiRes<Void> updateDeliveryStatus(
@Parameter(description = "出库单ID", required = true) @PathVariable Long deliveryId,
@Parameter(description = "出库状态", required = true) @RequestParam Integer deliveryStatus) {
try {
boolean result = deliveryMainService.updateDeliveryStatus(deliveryId, deliveryStatus);
if (result) {
return ApiRes.success("修改出库状态成功", null);
} else {
return ApiRes.error("修改出库状态失败");
}
} catch (Exception e) {
return ApiRes.error("修改出库状态失败: " + e.getMessage());
}
}
}
package com.apple.erp.controller;
import com.apple.erp.dto.InvoiceAddReq;
import com.apple.erp.dto.InvoiceQueryReq;
import com.apple.erp.dto.InvoiceRes;
import com.apple.erp.dto.InvoiceUpdateReq;
import com.apple.erp.service.InvoiceMainService;
import com.apple.erp.dto.response.ApiRes;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
/**
* 发票管理Controller
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Tag(name = "发票管理", description = "发票管理相关接口")
@RestController
@RequestMapping("/api/invoice")
@Validated
public class InvoiceMainController {
@Autowired
private InvoiceMainService invoiceMainService;
@Operation(summary = "分页查询发票列表", description = "根据查询条件分页获取发票列表")
@GetMapping("/list")
@PreAuthorize("hasAuthority('invoice:list')")
public ApiRes<Page<InvoiceRes>> getInvoiceList(@Valid InvoiceQueryReq queryReq) {
Page<InvoiceRes> page = invoiceMainService.getInvoiceList(queryReq);
return ApiRes.success(page);
}
@Operation(summary = "获取发票详情", description = "根据发票ID获取发票详细信息,包含发票明细")
@GetMapping("/{invoiceId}")
@PreAuthorize("hasAuthority('invoice:detail')")
public ApiRes<InvoiceRes> getInvoiceDetail(
@Parameter(description = "发票ID", required = true) @PathVariable Long invoiceId) {
InvoiceRes invoiceRes = invoiceMainService.getInvoiceDetail(invoiceId);
if (invoiceRes != null) {
return ApiRes.success(invoiceRes);
}
return ApiRes.error("发票不存在或已删除");
}
@Operation(summary = "新增发票", description = "新增一个发票,包含发票主表和明细")
@PostMapping
@PreAuthorize("hasAuthority('invoice:add')")
public ApiRes<Void> addInvoice(@Valid @RequestBody InvoiceAddReq addReq) {
try {
boolean result = invoiceMainService.addInvoice(addReq);
if (result) {
return ApiRes.success("新增发票成功", null);
} else {
return ApiRes.error("新增发票失败");
}
} catch (Exception e) {
return ApiRes.error("新增发票失败: " + e.getMessage());
}
}
@Operation(summary = "修改发票", description = "修改现有发票信息,包含发票主表和明细")
@PostMapping("/update")
@PreAuthorize("hasAuthority('invoice:edit')")
public ApiRes<Void> updateInvoice(@Valid @RequestBody InvoiceUpdateReq updateReq) {
try {
boolean result = invoiceMainService.updateInvoice(updateReq);
if (result) {
return ApiRes.success("修改发票成功", null);
} else {
return ApiRes.error("修改发票失败");
}
} catch (Exception e) {
return ApiRes.error("修改发票失败: " + e.getMessage());
}
}
@Operation(summary = "删除发票", description = "根据发票ID逻辑删除发票")
@DeleteMapping("/{invoiceId}")
@PreAuthorize("hasAuthority('invoice:delete')")
public ApiRes<Void> deleteInvoice(
@Parameter(description = "发票ID", required = true) @PathVariable Long invoiceId) {
try {
boolean result = invoiceMainService.deleteInvoice(invoiceId);
if (result) {
return ApiRes.success("删除发票成功", null);
} else {
return ApiRes.error("删除发票失败");
}
} catch (Exception e) {
return ApiRes.error("删除发票失败: " + e.getMessage());
}
}
@Operation(summary = "批量删除发票", description = "根据发票ID列表批量逻辑删除发票")
@PostMapping("/batchDelete")
@PreAuthorize("hasAuthority('invoice:delete')")
public ApiRes<Void> batchDeleteInvoices(
@Parameter(description = "发票ID列表", required = true) @RequestBody List<Long> invoiceIds) {
try {
if (invoiceIds == null || invoiceIds.isEmpty()) {
return ApiRes.error("发票ID列表不能为空");
}
boolean result = invoiceMainService.batchDeleteInvoices(invoiceIds);
if (result) {
return ApiRes.success("批量删除发票成功", null);
} else {
return ApiRes.error("批量删除发票失败");
}
} catch (Exception e) {
return ApiRes.error("批量删除发票失败: " + e.getMessage());
}
}
@Operation(summary = "修改发票状态", description = "修改指定发票的发票状态")
@PostMapping("/{invoiceId}/invoiceStatus")
@PreAuthorize("hasAuthority('invoice:edit')")
public ApiRes<Void> updateInvoiceStatus(
@Parameter(description = "发票ID", required = true) @PathVariable Long invoiceId,
@Parameter(description = "发票状态", required = true) @RequestParam Integer invoiceStatus) {
try {
boolean result = invoiceMainService.updateInvoiceStatus(invoiceId, invoiceStatus);
if (result) {
return ApiRes.success("修改发票状态成功", null);
} else {
return ApiRes.error("修改发票状态失败");
}
} catch (Exception e) {
return ApiRes.error("修改发票状态失败: " + e.getMessage());
}
}
}
package com.apple.erp.controller;
import com.apple.erp.dto.OrderAddReq;
import com.apple.erp.dto.OrderQueryReq;
import com.apple.erp.dto.OrderRes;
import com.apple.erp.dto.OrderUpdateReq;
import com.apple.erp.service.OrderMainService;
import com.apple.erp.dto.response.ApiRes;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
/**
* 订单管理Controller
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Tag(name = "订单管理", description = "订单管理相关接口")
@RestController
@RequestMapping("/order")
@Validated
public class OrderMainController {
@Autowired
private OrderMainService orderMainService;
@Operation(summary = "分页查询订单列表", description = "根据条件分页查询订单列表")
@GetMapping("/list")
@PreAuthorize("hasAuthority('order:list')")
public ApiRes<Page<OrderRes>> getOrderList(@Valid OrderQueryReq queryReq) {
try {
Page<OrderRes> result = orderMainService.getOrderList(queryReq);
return ApiRes.success(result);
} catch (Exception e) {
return ApiRes.error("查询订单列表失败: " + e.getMessage());
}
}
@Operation(summary = "获取订单详情", description = "根据订单ID获取订单详情")
@GetMapping("/{orderId}")
@PreAuthorize("hasAuthority('order:detail')")
public ApiRes<OrderRes> getOrderDetail(
@Parameter(description = "订单ID", required = true) @PathVariable Long orderId) {
try {
OrderRes result = orderMainService.getOrderDetail(orderId);
if (result == null) {
return ApiRes.error("订单不存在");
}
return ApiRes.success(result);
} catch (Exception e) {
return ApiRes.error("获取订单详情失败: " + e.getMessage());
}
}
@Operation(summary = "新增订单", description = "新增订单信息")
@PostMapping
@PreAuthorize("hasAuthority('order:add')")
public ApiRes<Void> addOrder(@Valid @RequestBody OrderAddReq addReq) {
try {
boolean result = orderMainService.addOrder(addReq);
if (result) {
return ApiRes.success("新增订单成功", null);
} else {
return ApiRes.error("新增订单失败");
}
} catch (Exception e) {
return ApiRes.error("新增订单失败: " + e.getMessage());
}
}
@Operation(summary = "修改订单", description = "修改订单信息")
@PostMapping("/update")
@PreAuthorize("hasAuthority('order:edit')")
public ApiRes<Void> updateOrder(@Valid @RequestBody OrderUpdateReq updateReq) {
try {
boolean result = orderMainService.updateOrder(updateReq);
if (result) {
return ApiRes.success("修改订单成功", null);
} else {
return ApiRes.error("修改订单失败");
}
} catch (Exception e) {
return ApiRes.error("修改订单失败: " + e.getMessage());
}
}
@Operation(summary = "删除订单", description = "根据订单ID删除订单")
@DeleteMapping("/{orderId}")
@PreAuthorize("hasAuthority('order:delete')")
public ApiRes<Void> deleteOrder(
@Parameter(description = "订单ID", required = true) @PathVariable Long orderId) {
try {
boolean result = orderMainService.deleteOrder(orderId);
if (result) {
return ApiRes.success("删除订单成功", null);
} else {
return ApiRes.error("删除订单失败");
}
} catch (Exception e) {
return ApiRes.error("删除订单失败: " + e.getMessage());
}
}
@Operation(summary = "批量删除订单", description = "批量删除订单")
@PostMapping("/batchDelete")
@PreAuthorize("hasAuthority('order:delete')")
public ApiRes<Void> batchDeleteOrders(@RequestBody List<Long> orderIds) {
try {
if (orderIds == null || orderIds.isEmpty()) {
return ApiRes.error("订单ID列表不能为空");
}
boolean result = orderMainService.batchDeleteOrders(orderIds);
if (result) {
return ApiRes.success("批量删除订单成功", null);
} else {
return ApiRes.error("批量删除订单失败");
}
} catch (Exception e) {
return ApiRes.error("批量删除订单失败: " + e.getMessage());
}
}
@Operation(summary = "修改订单出库状态", description = "修改订单出库状态")
@PostMapping("/{orderId}/deliveryStatus")
@PreAuthorize("hasAuthority('order:edit')")
public ApiRes<Void> updateDeliveryStatus(
@Parameter(description = "订单ID", required = true) @PathVariable Long orderId,
@Parameter(description = "出库状态", required = true) @RequestParam Integer deliveryStatus) {
try {
boolean result = orderMainService.updateDeliveryStatus(orderId, deliveryStatus);
if (result) {
return ApiRes.success("修改出库状态成功", null);
} else {
return ApiRes.error("修改出库状态失败");
}
} catch (Exception e) {
return ApiRes.error("修改出库状态失败: " + e.getMessage());
}
}
@Operation(summary = "修改订单开票状态", description = "修改订单开票状态")
@PostMapping("/{orderId}/invoiceStatus")
@PreAuthorize("hasAuthority('order:edit')")
public ApiRes<Void> updateInvoiceStatus(
@Parameter(description = "订单ID", required = true) @PathVariable Long orderId,
@Parameter(description = "开票状态", required = true) @RequestParam Integer invoiceStatus) {
try {
boolean result = orderMainService.updateInvoiceStatus(orderId, invoiceStatus);
if (result) {
return ApiRes.success("修改开票状态成功", null);
} else {
return ApiRes.error("修改开票状态失败");
}
} catch (Exception e) {
return ApiRes.error("修改开票状态失败: " + e.getMessage());
}
}
@Operation(summary = "修改订单返利计算状态", description = "修改订单返利计算状态")
@PostMapping("/{orderId}/rebateCalcFlag")
@PreAuthorize("hasAuthority('order:edit')")
public ApiRes<Void> updateRebateCalcFlag(
@Parameter(description = "订单ID", required = true) @PathVariable Long orderId,
@Parameter(description = "返利计算状态", required = true) @RequestParam Integer rebateCalcFlag) {
try {
boolean result = orderMainService.updateRebateCalcFlag(orderId, rebateCalcFlag);
if (result) {
return ApiRes.success("修改返利计算状态成功", null);
} else {
return ApiRes.error("修改返利计算状态失败");
}
} catch (Exception e) {
return ApiRes.error("修改返利计算状态失败: " + e.getMessage());
}
}
}
package com.apple.erp.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.List;
/**
* 出库新增请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "出库新增请求DTO")
public class DeliveryAddReq {
@NotBlank(message = "出库单编号不能为空")
@Schema(description = "出库单编号", required = true)
private String deliveryNo;
@NotBlank(message = "经销商编码不能为空")
@Schema(description = "经销商编码", required = true)
private String dealerCode;
@NotBlank(message = "经销商名称不能为空")
@Schema(description = "经销商名称", required = true)
private String dealerName;
@NotBlank(message = "关联订单编号不能为空")
@Schema(description = "关联订单编号", required = true)
private String orderNo;
@NotNull(message = "出库日期不能为空")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "出库日期", required = true)
private LocalDateTime deliveryDate;
@Schema(description = "出库状态(0-未出库/1-已出库)")
private Integer deliveryStatus;
@Schema(description = "出库仓库编码")
private String warehouseCode;
@Schema(description = "数据来源")
private String dataSource;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "数据上传时间")
private LocalDateTime uploadTime;
@Valid
@Size(min = 1, message = "出库明细不能为空")
@Schema(description = "出库明细列表", required = true)
private List<DeliveryItemAddReq> deliveryItems;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* 出库明细新增请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "出库明细新增请求DTO")
public class DeliveryItemAddReq {
@NotBlank(message = "产品编码不能为空")
@Schema(description = "产品编码", required = true)
private String productCode;
@NotBlank(message = "产品名称不能为空")
@Schema(description = "产品名称", required = true)
private String productName;
@NotNull(message = "出库数量不能为空")
@Schema(description = "出库数量", required = true)
private Integer deliveryQty;
@NotNull(message = "出库单价不能为空")
@Schema(description = "出库单价", required = true)
private BigDecimal deliveryPrice;
@Schema(description = "出库金额")
private BigDecimal deliveryAmount;
}
package com.apple.erp.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 出库明细响应DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "出库明细响应DTO")
public class DeliveryItemRes {
@Schema(description = "出库明细ID")
private Long deliveryItemId;
@Schema(description = "关联出库单ID")
private Long deliveryId;
@Schema(description = "出库单编号")
private String deliveryNo;
@Schema(description = "关联订单编号")
private String orderNo;
@Schema(description = "产品编码")
private String productCode;
@Schema(description = "产品名称")
private String productName;
@Schema(description = "出库数量")
private Integer deliveryQty;
@Schema(description = "出库单价")
private BigDecimal deliveryPrice;
@Schema(description = "出库金额")
private BigDecimal deliveryAmount;
@Schema(description = "创建者")
private String createBy;
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@Schema(description = "更新者")
private String updateBy;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* 出库明细修改请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "出库明细修改请求DTO")
public class DeliveryItemUpdateReq {
@Schema(description = "出库明细ID")
private Long deliveryItemId; // 修改时可能需要,新增时不需要
@NotBlank(message = "产品编码不能为空")
@Schema(description = "产品编码", required = true)
private String productCode;
@NotBlank(message = "产品名称不能为空")
@Schema(description = "产品名称", required = true)
private String productName;
@NotNull(message = "出库数量不能为空")
@Schema(description = "出库数量", required = true)
private Integer deliveryQty;
@NotNull(message = "出库单价不能为空")
@Schema(description = "出库单价", required = true)
private BigDecimal deliveryPrice;
@Schema(description = "出库金额")
private BigDecimal deliveryAmount;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 出库查询请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "出库查询请求DTO")
public class DeliveryQueryReq {
@Schema(description = "出库单编号")
private String deliveryNo;
@Schema(description = "经销商编码")
private String dealerCode;
@Schema(description = "经销商名称")
private String dealerName;
@Schema(description = "关联订单编号")
private String orderNo;
@Schema(description = "出库状态(0-未出库/1-已出库)")
private Integer deliveryStatus;
@Schema(description = "出库仓库编码")
private String warehouseCode;
@Schema(description = "数据来源")
private String dataSource;
@Schema(description = "出库开始日期")
private String deliveryStartDate;
@Schema(description = "出库结束日期")
private String deliveryEndDate;
@Schema(description = "页码")
private Integer pageNum = 1;
@Schema(description = "每页大小")
private Integer pageSize = 10;
}
package com.apple.erp.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* 出库响应DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "出库响应DTO")
public class DeliveryRes {
@Schema(description = "出库单ID")
private Long deliveryId;
@Schema(description = "出库单编号")
private String deliveryNo;
@Schema(description = "经销商编码")
private String dealerCode;
@Schema(description = "经销商名称")
private String dealerName;
@Schema(description = "关联订单编号")
private String orderNo;
@Schema(description = "出库日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime deliveryDate;
@Schema(description = "出库状态(0-未出库/1-已出库)")
private Integer deliveryStatus;
@Schema(description = "出库仓库编码")
private String warehouseCode;
@Schema(description = "数据来源")
private String dataSource;
@Schema(description = "数据上传时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime uploadTime;
@Schema(description = "创建者")
private String createBy;
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@Schema(description = "更新者")
private String updateBy;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
@Schema(description = "出库明细列表")
private List<DeliveryItemRes> deliveryItems;
}
package com.apple.erp.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.time.LocalDateTime;
import java.util.List;
/**
* 出库修改请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "出库修改请求DTO")
public class DeliveryUpdateReq {
@NotNull(message = "出库单ID不能为空")
@Schema(description = "出库单ID", required = true)
private Long deliveryId;
@NotBlank(message = "出库单编号不能为空")
@Schema(description = "出库单编号", required = true)
private String deliveryNo;
@NotBlank(message = "经销商编码不能为空")
@Schema(description = "经销商编码", required = true)
private String dealerCode;
@NotBlank(message = "经销商名称不能为空")
@Schema(description = "经销商名称", required = true)
private String dealerName;
@NotBlank(message = "关联订单编号不能为空")
@Schema(description = "关联订单编号", required = true)
private String orderNo;
@NotNull(message = "出库日期不能为空")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "出库日期", required = true)
private LocalDateTime deliveryDate;
@Schema(description = "出库状态(0-未出库/1-已出库)")
private Integer deliveryStatus;
@Schema(description = "出库仓库编码")
private String warehouseCode;
@Schema(description = "数据来源")
private String dataSource;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "数据上传时间")
private LocalDateTime uploadTime;
@Valid
@Size(min = 1, message = "出库明细不能为空")
@Schema(description = "出库明细列表", required = true)
private List<DeliveryItemUpdateReq> deliveryItems;
}
package com.apple.erp.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
/**
* 发票新增请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "发票新增请求DTO")
public class InvoiceAddReq {
@NotBlank(message = "发票编号不能为空")
@Schema(description = "发票编号", required = true)
private String invoiceNo;
@NotBlank(message = "关联订单编号不能为空")
@Schema(description = "关联订单编号", required = true)
private String orderNo;
@Schema(description = "关联出库单编号")
private String deliveryNo;
@NotBlank(message = "经销商编码不能为空")
@Schema(description = "经销商编码", required = true)
private String dealerCode;
@NotBlank(message = "经销商名称不能为空")
@Schema(description = "经销商名称", required = true)
private String dealerName;
@NotNull(message = "发票总金额不能为空")
@Schema(description = "发票总金额(含税)", required = true)
private BigDecimal totalAmount;
@NotNull(message = "开票日期不能为空")
@JsonFormat(pattern = "yyyy-MM-dd")
@Schema(description = "开票日期", required = true)
private LocalDate invoiceDate;
@Schema(description = "发票状态(0-未开票/1-已开票)")
private Integer invoiceStatus;
@Schema(description = "税率")
private BigDecimal taxRate;
@Schema(description = "数据来源")
private String dataSource;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "数据上传时间")
private LocalDateTime uploadTime;
@Valid
@Size(min = 1, message = "发票明细不能为空")
@Schema(description = "发票明细列表", required = true)
private List<InvoiceItemAddReq> invoiceItems;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* 发票明细新增请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "发票明细新增请求DTO")
public class InvoiceItemAddReq {
@NotBlank(message = "产品编码不能为空")
@Schema(description = "产品编码", required = true)
private String productCode;
@NotBlank(message = "产品名称不能为空")
@Schema(description = "产品名称", required = true)
private String productName;
@NotNull(message = "开票数量不能为空")
@Schema(description = "开票数量", required = true)
private Integer invoiceQty;
@NotNull(message = "不含税单价不能为空")
@Schema(description = "不含税单价", required = true)
private BigDecimal unitPriceNoTax;
@NotNull(message = "不含税明细金额不能为空")
@Schema(description = "不含税明细金额", required = true)
private BigDecimal amountNoTax;
@NotNull(message = "税额不能为空")
@Schema(description = "税额", required = true)
private BigDecimal taxAmount;
}
package com.apple.erp.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 发票明细响应DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "发票明细响应DTO")
public class InvoiceItemRes {
@Schema(description = "发票明细ID")
private Long invoiceItemId;
@Schema(description = "关联发票ID")
private Long invoiceId;
@Schema(description = "发票编号")
private String invoiceNo;
@Schema(description = "关联订单编号")
private String orderNo;
@Schema(description = "产品编码")
private String productCode;
@Schema(description = "产品名称")
private String productName;
@Schema(description = "开票数量")
private Integer invoiceQty;
@Schema(description = "不含税单价")
private BigDecimal unitPriceNoTax;
@Schema(description = "不含税明细金额")
private BigDecimal amountNoTax;
@Schema(description = "税额")
private BigDecimal taxAmount;
@Schema(description = "创建者")
private String createBy;
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@Schema(description = "更新者")
private String updateBy;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
/**
* 发票明细修改请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "发票明细修改请求DTO")
public class InvoiceItemUpdateReq {
@Schema(description = "发票明细ID")
private Long invoiceItemId; // 修改时可能需要,新增时不需要
@NotBlank(message = "产品编码不能为空")
@Schema(description = "产品编码", required = true)
private String productCode;
@NotBlank(message = "产品名称不能为空")
@Schema(description = "产品名称", required = true)
private String productName;
@NotNull(message = "开票数量不能为空")
@Schema(description = "开票数量", required = true)
private Integer invoiceQty;
@NotNull(message = "不含税单价不能为空")
@Schema(description = "不含税单价", required = true)
private BigDecimal unitPriceNoTax;
@NotNull(message = "不含税明细金额不能为空")
@Schema(description = "不含税明细金额", required = true)
private BigDecimal amountNoTax;
@NotNull(message = "税额不能为空")
@Schema(description = "税额", required = true)
private BigDecimal taxAmount;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
/**
* 发票查询请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "发票查询请求DTO")
public class InvoiceQueryReq {
@Schema(description = "发票编号")
private String invoiceNo;
@Schema(description = "关联订单编号")
private String orderNo;
@Schema(description = "关联出库单编号")
private String deliveryNo;
@Schema(description = "经销商编码")
private String dealerCode;
@Schema(description = "经销商名称")
private String dealerName;
@Schema(description = "发票状态(0-未开票/1-已开票)")
private Integer invoiceStatus;
@Schema(description = "数据来源")
private String dataSource;
@Schema(description = "开票开始日期")
private String invoiceStartDate;
@Schema(description = "开票结束日期")
private String invoiceEndDate;
@Schema(description = "金额最小值")
private BigDecimal minAmount;
@Schema(description = "金额最大值")
private BigDecimal maxAmount;
@Schema(description = "页码")
private Integer pageNum = 1;
@Schema(description = "每页大小")
private Integer pageSize = 10;
}
package com.apple.erp.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
/**
* 发票响应DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "发票响应DTO")
public class InvoiceRes {
@Schema(description = "发票ID")
private Long invoiceId;
@Schema(description = "发票编号")
private String invoiceNo;
@Schema(description = "关联订单编号")
private String orderNo;
@Schema(description = "关联出库单编号")
private String deliveryNo;
@Schema(description = "经销商编码")
private String dealerCode;
@Schema(description = "经销商名称")
private String dealerName;
@Schema(description = "发票总金额(含税)")
private BigDecimal totalAmount;
@Schema(description = "开票日期")
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate invoiceDate;
@Schema(description = "发票状态(0-未开票/1-已开票)")
private Integer invoiceStatus;
@Schema(description = "税率")
private BigDecimal taxRate;
@Schema(description = "数据来源")
private String dataSource;
@Schema(description = "数据上传时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime uploadTime;
@Schema(description = "创建者")
private String createBy;
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@Schema(description = "更新者")
private String updateBy;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
@Schema(description = "发票明细列表")
private List<InvoiceItemRes> invoiceItems;
}
package com.apple.erp.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
/**
* 发票修改请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "发票修改请求DTO")
public class InvoiceUpdateReq {
@NotNull(message = "发票ID不能为空")
@Schema(description = "发票ID", required = true)
private Long invoiceId;
@NotBlank(message = "发票编号不能为空")
@Schema(description = "发票编号", required = true)
private String invoiceNo;
@NotBlank(message = "关联订单编号不能为空")
@Schema(description = "关联订单编号", required = true)
private String orderNo;
@Schema(description = "关联出库单编号")
private String deliveryNo;
@NotBlank(message = "经销商编码不能为空")
@Schema(description = "经销商编码", required = true)
private String dealerCode;
@NotBlank(message = "经销商名称不能为空")
@Schema(description = "经销商名称", required = true)
private String dealerName;
@NotNull(message = "发票总金额不能为空")
@Schema(description = "发票总金额(含税)", required = true)
private BigDecimal totalAmount;
@NotNull(message = "开票日期不能为空")
@JsonFormat(pattern = "yyyy-MM-dd")
@Schema(description = "开票日期", required = true)
private LocalDate invoiceDate;
@Schema(description = "发票状态(0-未开票/1-已开票)")
private Integer invoiceStatus;
@Schema(description = "税率")
private BigDecimal taxRate;
@Schema(description = "数据来源")
private String dataSource;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "数据上传时间")
private LocalDateTime uploadTime;
@Valid
@Size(min = 1, message = "发票明细不能为空")
@Schema(description = "发票明细列表", required = true)
private List<InvoiceItemUpdateReq> invoiceItems;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* 订单新增请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "订单新增请求DTO")
public class OrderAddReq {
@NotBlank(message = "订单编号不能为空")
@Schema(description = "订单编号", required = true)
private String orderNo;
@NotBlank(message = "经销商编码不能为空")
@Schema(description = "经销商编码", required = true)
private String dealerCode;
@NotBlank(message = "经销商名称不能为空")
@Schema(description = "经销商名称", required = true)
private String dealerName;
@NotNull(message = "订单日期不能为空")
@Schema(description = "订单创建日期", required = true)
private LocalDateTime orderDate;
@NotNull(message = "订单总金额不能为空")
@Schema(description = "订单总金额", required = true)
private BigDecimal totalAmount;
@Schema(description = "订单返利金额")
private BigDecimal rebateAmount;
@Schema(description = "出库状态(0-未出库/1-已出库)")
private Integer deliveryStatus = 0;
@Schema(description = "开票状态(0-未开票/1-已开票)")
private Integer invoiceStatus = 0;
@Schema(description = "返利计算状态(0-未计算/1-已计算)")
private Integer rebateCalcFlag = 0;
@Schema(description = "数据来源")
private String dataSource;
@Schema(description = "数据验证状态(0-待验证/1-验证通过/2-验证失败)")
private Integer verifyStatus = 0;
@Schema(description = "数据上传时间")
private LocalDateTime uploadTime;
@NotEmpty(message = "订单明细不能为空")
@Valid
@Schema(description = "订单明细列表", required = true)
private List<OrderItemAddReq> orderItems;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
import java.math.BigDecimal;
/**
* 订单明细新增请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "订单明细新增请求DTO")
public class OrderItemAddReq {
@NotBlank(message = "产品编码不能为空")
@Schema(description = "产品编码", required = true)
private String productCode;
@NotBlank(message = "产品名称不能为空")
@Schema(description = "产品名称", required = true)
private String productName;
@NotNull(message = "产品单价不能为空")
@Schema(description = "产品单价", required = true)
private BigDecimal unitPrice;
@NotNull(message = "商品数量不能为空")
@Positive(message = "商品数量必须大于0")
@Schema(description = "商品数量", required = true)
private Integer productQty;
@Schema(description = "商品明细金额")
private BigDecimal itemAmount;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 订单明细响应DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "订单明细响应DTO")
public class OrderItemRes {
@Schema(description = "明细ID")
private Long itemId;
@Schema(description = "订单ID")
private Long orderId;
@Schema(description = "订单编号")
private String orderNo;
@Schema(description = "产品编码")
private String productCode;
@Schema(description = "产品名称")
private String productName;
@Schema(description = "产品单价")
private BigDecimal unitPrice;
@Schema(description = "商品数量")
private Integer productQty;
@Schema(description = "商品明细金额")
private BigDecimal itemAmount;
@Schema(description = "创建者")
private String createBy;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "更新者")
private String updateBy;
@Schema(description = "更新时间")
private LocalDateTime updateTime;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
import java.math.BigDecimal;
/**
* 订单明细修改请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "订单明细修改请求DTO")
public class OrderItemUpdateReq {
@Schema(description = "明细ID(新增时为空,修改时必填)")
private Long itemId;
@NotBlank(message = "产品编码不能为空")
@Schema(description = "产品编码", required = true)
private String productCode;
@NotBlank(message = "产品名称不能为空")
@Schema(description = "产品名称", required = true)
private String productName;
@NotNull(message = "产品单价不能为空")
@Schema(description = "产品单价", required = true)
private BigDecimal unitPrice;
@NotNull(message = "商品数量不能为空")
@Positive(message = "商品数量必须大于0")
@Schema(description = "商品数量", required = true)
private Integer productQty;
@Schema(description = "商品明细金额")
private BigDecimal itemAmount;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 订单查询请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "订单查询请求DTO")
public class OrderQueryReq {
@Schema(description = "订单编号")
private String orderNo;
@Schema(description = "经销商编码")
private String dealerCode;
@Schema(description = "经销商名称")
private String dealerName;
@Schema(description = "出库状态(0-未出库/1-已出库)")
private Integer deliveryStatus;
@Schema(description = "开票状态(0-未开票/1-已开票)")
private Integer invoiceStatus;
@Schema(description = "返利计算状态(0-未计算/1-已计算)")
private Integer rebateCalcFlag;
@Schema(description = "数据来源")
private String dataSource;
@Schema(description = "数据验证状态(0-待验证/1-验证通过/2-验证失败)")
private Integer verifyStatus;
@Schema(description = "订单开始日期")
private LocalDateTime orderStartDate;
@Schema(description = "订单结束日期")
private LocalDateTime orderEndDate;
@Schema(description = "金额最小值")
private java.math.BigDecimal minAmount;
@Schema(description = "金额最大值")
private java.math.BigDecimal maxAmount;
@Schema(description = "页码")
private Integer pageNum = 1;
@Schema(description = "每页大小")
private Integer pageSize = 10;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* 订单响应DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "订单响应DTO")
public class OrderRes {
@Schema(description = "订单ID")
private Long orderId;
@Schema(description = "订单编号")
private String orderNo;
@Schema(description = "经销商编码")
private String dealerCode;
@Schema(description = "经销商名称")
private String dealerName;
@Schema(description = "订单创建日期")
private LocalDateTime orderDate;
@Schema(description = "订单总金额")
private BigDecimal totalAmount;
@Schema(description = "订单返利金额")
private BigDecimal rebateAmount;
@Schema(description = "出库状态(0-未出库/1-已出库)")
private Integer deliveryStatus;
@Schema(description = "开票状态(0-未开票/1-已开票)")
private Integer invoiceStatus;
@Schema(description = "返利计算状态(0-未计算/1-已计算)")
private Integer rebateCalcFlag;
@Schema(description = "数据来源")
private String dataSource;
@Schema(description = "数据验证状态(0-待验证/1-验证通过/2-验证失败)")
private Integer verifyStatus;
@Schema(description = "数据上传时间")
private LocalDateTime uploadTime;
@Schema(description = "创建者")
private String createBy;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "更新者")
private String updateBy;
@Schema(description = "更新时间")
private LocalDateTime updateTime;
@Schema(description = "订单明细列表")
private List<OrderItemRes> orderItems;
}
package com.apple.erp.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* 订单修改请求DTO
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@Schema(description = "订单修改请求DTO")
public class OrderUpdateReq {
@NotNull(message = "订单ID不能为空")
@Schema(description = "订单ID", required = true)
private Long orderId;
@NotBlank(message = "订单编号不能为空")
@Schema(description = "订单编号", required = true)
private String orderNo;
@NotBlank(message = "经销商编码不能为空")
@Schema(description = "经销商编码", required = true)
private String dealerCode;
@NotBlank(message = "经销商名称不能为空")
@Schema(description = "经销商名称", required = true)
private String dealerName;
@NotNull(message = "订单日期不能为空")
@Schema(description = "订单创建日期", required = true)
private LocalDateTime orderDate;
@NotNull(message = "订单总金额不能为空")
@Schema(description = "订单总金额", required = true)
private BigDecimal totalAmount;
@Schema(description = "订单返利金额")
private BigDecimal rebateAmount;
@Schema(description = "出库状态(0-未出库/1-已出库)")
private Integer deliveryStatus;
@Schema(description = "开票状态(0-未开票/1-已开票)")
private Integer invoiceStatus;
@Schema(description = "返利计算状态(0-未计算/1-已计算)")
private Integer rebateCalcFlag;
@Schema(description = "数据来源")
private String dataSource;
@Schema(description = "数据验证状态(0-待验证/1-验证通过/2-验证失败)")
private Integer verifyStatus;
@Schema(description = "数据上传时间")
private LocalDateTime uploadTime;
@NotEmpty(message = "订单明细不能为空")
@Valid
@Schema(description = "订单明细列表", required = true)
private List<OrderItemUpdateReq> orderItems;
}
package com.apple.erp.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 出库商品明细表实体类
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_delivery_item")
@Schema(description = "出库商品明细表")
public class DeliveryItem {
@TableId(value = "delivery_item_id", type = IdType.AUTO)
@Schema(description = "出库商品明细ID")
private Long deliveryItemId;
@Schema(description = "关联出库单ID")
private Long deliveryId;
@Schema(description = "出库单编号")
private String deliveryNo;
@Schema(description = "关联订单编号")
private String orderNo;
@Schema(description = "产品编码")
private String productCode;
@Schema(description = "产品名称")
private String productName;
@Schema(description = "出库数量")
private Integer deliveryQty;
@Schema(description = "出库单价")
private BigDecimal deliveryPrice;
@Schema(description = "出库金额")
private BigDecimal deliveryAmount;
@Schema(description = "创建者")
private String createBy;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "更新者")
private String updateBy;
@Schema(description = "更新时间")
private LocalDateTime updateTime;
@Schema(description = "删除标志(0代表存在 2代表删除)")
private String delFlag;
}
package com.apple.erp.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 出库主表实体类
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_delivery_main")
@Schema(description = "出库主表")
public class DeliveryMain {
@TableId(value = "delivery_id", type = IdType.AUTO)
@Schema(description = "出库单ID")
private Long deliveryId;
@Schema(description = "出库单编号")
private String deliveryNo;
@Schema(description = "经销商编码")
private String dealerCode;
@Schema(description = "经销商名称")
private String dealerName;
@Schema(description = "关联订单编号")
private String orderNo;
@Schema(description = "出库日期")
private LocalDateTime deliveryDate;
@Schema(description = "出库状态(0-未出库/1-已出库)")
private Integer deliveryStatus;
@Schema(description = "出库总金额")
private BigDecimal totalAmount;
@Schema(description = "仓库编码")
private String warehouseCode;
@Schema(description = "仓库名称")
private String warehouseName;
@Schema(description = "数据来源")
private String dataSource;
@Schema(description = "上传时间")
private LocalDateTime uploadTime;
@Schema(description = "创建者")
private String createBy;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "更新者")
private String updateBy;
@Schema(description = "更新时间")
private LocalDateTime updateTime;
@Schema(description = "删除标志(0代表存在 2代表删除)")
private String delFlag;
}
package com.apple.erp.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 发票商品明细表实体类
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_invoice_item")
@Schema(description = "发票商品明细表")
public class InvoiceItem {
@TableId(value = "invoice_item_id", type = IdType.AUTO)
@Schema(description = "发票商品明细ID")
private Long invoiceItemId;
@Schema(description = "关联发票ID")
private Long invoiceId;
@Schema(description = "发票编号")
private String invoiceNo;
@Schema(description = "关联订单编号")
private String orderNo;
@Schema(description = "产品编码")
private String productCode;
@Schema(description = "产品名称")
private String productName;
@Schema(description = "开票数量")
private Integer invoiceQty;
@Schema(description = "不含税单价")
private BigDecimal unitPriceNoTax;
@Schema(description = "不含税明细金额")
private BigDecimal amountNoTax;
@Schema(description = "税额")
private BigDecimal taxAmount;
@Schema(description = "创建者")
private String createBy;
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@Schema(description = "更新者")
private String updateBy;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
@Schema(description = "删除标志(0代表存在 2代表删除)")
private String delFlag;
}
package com.apple.erp.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* 发票主表实体类
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_invoice_main")
@Schema(description = "发票主表")
public class InvoiceMain {
@TableId(value = "invoice_id", type = IdType.AUTO)
@Schema(description = "发票ID")
private Long invoiceId;
@Schema(description = "发票编号")
private String invoiceNo;
@Schema(description = "关联订单编号")
private String orderNo;
@Schema(description = "关联出库单编号")
private String deliveryNo;
@Schema(description = "经销商编码")
private String dealerCode;
@Schema(description = "经销商名称")
private String dealerName;
@Schema(description = "发票总金额(含税)")
private BigDecimal totalAmount;
@Schema(description = "开票日期")
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate invoiceDate;
@Schema(description = "发票状态(0-未开票/1-已开票)")
private Integer invoiceStatus;
@Schema(description = "税率")
private BigDecimal taxRate;
@Schema(description = "数据来源")
private String dataSource;
@Schema(description = "数据上传时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime uploadTime;
@Schema(description = "创建者")
private String createBy;
@Schema(description = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@Schema(description = "更新者")
private String updateBy;
@Schema(description = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
@Schema(description = "删除标志(0代表存在 2代表删除)")
private String delFlag;
}
package com.apple.erp.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 订单商品明细表实体类
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_order_item")
@Schema(description = "订单商品明细表")
public class OrderItem {
@TableId(value = "item_id", type = IdType.AUTO)
@Schema(description = "明细ID")
private Long itemId;
@Schema(description = "订单ID")
private Long orderId;
@Schema(description = "订单编号")
private String orderNo;
@Schema(description = "产品编码")
private String productCode;
@Schema(description = "产品名称")
private String productName;
@Schema(description = "产品单价")
private BigDecimal unitPrice;
@Schema(description = "商品数量")
private Integer productQty;
@Schema(description = "商品明细金额")
private BigDecimal itemAmount;
@Schema(description = "创建者")
private String createBy;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "更新者")
private String updateBy;
@Schema(description = "更新时间")
private LocalDateTime updateTime;
@Schema(description = "删除标志(0代表存在 2代表删除)")
private String delFlag;
}
package com.apple.erp.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 订单主表实体类
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_order_main")
@Schema(description = "订单主表")
public class OrderMain {
@TableId(value = "order_id", type = IdType.AUTO)
@Schema(description = "订单ID")
private Long orderId;
@Schema(description = "订单编号")
private String orderNo;
@Schema(description = "经销商编码")
private String dealerCode;
@Schema(description = "经销商名称")
private String dealerName;
@Schema(description = "订单创建日期")
private LocalDateTime orderDate;
@Schema(description = "订单总金额")
private BigDecimal totalAmount;
@Schema(description = "订单返利金额")
private BigDecimal rebateAmount;
@Schema(description = "出库状态(0-未出库/1-已出库)")
private Integer deliveryStatus;
@Schema(description = "开票状态(0-未开票/1-已开票)")
private Integer invoiceStatus;
@Schema(description = "返利计算状态(0-未计算/1-已计算)")
private Integer rebateCalcFlag;
@Schema(description = "数据来源")
private String dataSource;
@Schema(description = "数据验证状态(0-待验证/1-验证通过/2-验证失败)")
private Integer verifyStatus;
@Schema(description = "数据上传时间")
private LocalDateTime uploadTime;
@Schema(description = "创建者")
private String createBy;
@Schema(description = "创建时间")
private LocalDateTime createTime;
@Schema(description = "更新者")
private String updateBy;
@Schema(description = "更新时间")
private LocalDateTime updateTime;
@Schema(description = "删除标志(0代表存在 2代表删除)")
private String delFlag;
}
package com.apple.erp.mapper;
import com.apple.erp.entity.DeliveryItem;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 出库明细表Mapper接口
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Mapper
public interface DeliveryItemMapper extends BaseMapper<DeliveryItem> {
}
package com.apple.erp.mapper;
import com.apple.erp.entity.DeliveryMain;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 出库主表Mapper接口
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Mapper
public interface DeliveryMainMapper extends BaseMapper<DeliveryMain> {
}
package com.apple.erp.mapper;
import com.apple.erp.entity.InvoiceItem;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 发票明细表Mapper接口
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Mapper
public interface InvoiceItemMapper extends BaseMapper<InvoiceItem> {
}
package com.apple.erp.mapper;
import com.apple.erp.entity.InvoiceMain;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 发票主表Mapper接口
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Mapper
public interface InvoiceMainMapper extends BaseMapper<InvoiceMain> {
}
package com.apple.erp.mapper;
import com.apple.erp.entity.OrderItem;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 订单明细表Mapper接口
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Mapper
public interface OrderItemMapper extends BaseMapper<OrderItem> {
}
package com.apple.erp.mapper;
import com.apple.erp.entity.OrderMain;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 订单主表Mapper接口
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Mapper
public interface OrderMainMapper extends BaseMapper<OrderMain> {
}
package com.apple.erp.service;
import com.apple.erp.dto.DeliveryAddReq;
import com.apple.erp.dto.DeliveryQueryReq;
import com.apple.erp.dto.DeliveryRes;
import com.apple.erp.dto.DeliveryUpdateReq;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.apple.erp.entity.DeliveryMain;
import java.util.List;
/**
* 出库主表Service接口
*
* @author Apple ERP System
* @since 2025-01-01
*/
public interface DeliveryMainService extends IService<DeliveryMain> {
/**
* 分页查询出库列表
* @param queryReq 查询条件
* @return 出库列表(带分页信息)
*/
Page<DeliveryRes> getDeliveryList(DeliveryQueryReq queryReq);
/**
* 获取出库详情
* @param deliveryId 出库单ID
* @return 出库详情(包含明细)
*/
DeliveryRes getDeliveryDetail(Long deliveryId);
/**
* 新增出库
* @param addReq 出库新增请求
* @return 是否成功
*/
boolean addDelivery(DeliveryAddReq addReq);
/**
* 修改出库
* @param updateReq 出库修改请求
* @return 是否成功
*/
boolean updateDelivery(DeliveryUpdateReq updateReq);
/**
* 删除出库(逻辑删除)
* @param deliveryId 出库单ID
* @return 是否成功
*/
boolean deleteDelivery(Long deliveryId);
/**
* 批量删除出库(逻辑删除)
* @param deliveryIds 出库单ID列表
* @return 是否成功
*/
boolean batchDeleteDeliveries(List<Long> deliveryIds);
/**
* 修改出库状态
* @param deliveryId 出库单ID
* @param deliveryStatus 出库状态
* @return 是否成功
*/
boolean updateDeliveryStatus(Long deliveryId, Integer deliveryStatus);
}
package com.apple.erp.service;
import com.apple.erp.dto.InvoiceAddReq;
import com.apple.erp.dto.InvoiceQueryReq;
import com.apple.erp.dto.InvoiceRes;
import com.apple.erp.dto.InvoiceUpdateReq;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.apple.erp.entity.InvoiceMain;
import java.util.List;
/**
* 发票主表Service接口
*
* @author Apple ERP System
* @since 2025-01-01
*/
public interface InvoiceMainService extends IService<InvoiceMain> {
/**
* 分页查询发票列表
* @param queryReq 查询条件
* @return 发票列表(带分页信息)
*/
Page<InvoiceRes> getInvoiceList(InvoiceQueryReq queryReq);
/**
* 获取发票详情
* @param invoiceId 发票ID
* @return 发票详情(包含明细)
*/
InvoiceRes getInvoiceDetail(Long invoiceId);
/**
* 新增发票
* @param addReq 发票新增请求
* @return 是否成功
*/
boolean addInvoice(InvoiceAddReq addReq);
/**
* 修改发票
* @param updateReq 发票修改请求
* @return 是否成功
*/
boolean updateInvoice(InvoiceUpdateReq updateReq);
/**
* 删除发票(逻辑删除)
* @param invoiceId 发票ID
* @return 是否成功
*/
boolean deleteInvoice(Long invoiceId);
/**
* 批量删除发票(逻辑删除)
* @param invoiceIds 发票ID列表
* @return 是否成功
*/
boolean batchDeleteInvoices(List<Long> invoiceIds);
/**
* 修改发票状态
* @param invoiceId 发票ID
* @param invoiceStatus 发票状态
* @return 是否成功
*/
boolean updateInvoiceStatus(Long invoiceId, Integer invoiceStatus);
}
package com.apple.erp.service;
import com.apple.erp.dto.OrderAddReq;
import com.apple.erp.dto.OrderQueryReq;
import com.apple.erp.dto.OrderRes;
import com.apple.erp.dto.OrderUpdateReq;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.apple.erp.entity.OrderMain;
import java.util.List;
/**
* 订单主表Service接口
*
* @author Apple ERP System
* @since 2025-01-01
*/
public interface OrderMainService extends IService<OrderMain> {
/**
* 分页查询订单列表
*
* @param queryReq 查询条件
* @return 订单分页数据
*/
Page<OrderRes> getOrderList(OrderQueryReq queryReq);
/**
* 获取订单详情
*
* @param orderId 订单ID
* @return 订单详情
*/
OrderRes getOrderDetail(Long orderId);
/**
* 新增订单
*
* @param addReq 新增请求
* @return 是否成功
*/
boolean addOrder(OrderAddReq addReq);
/**
* 修改订单
*
* @param updateReq 修改请求
* @return 是否成功
*/
boolean updateOrder(OrderUpdateReq updateReq);
/**
* 删除订单
*
* @param orderId 订单ID
* @return 是否成功
*/
boolean deleteOrder(Long orderId);
/**
* 批量删除订单
*
* @param orderIds 订单ID列表
* @return 是否成功
*/
boolean batchDeleteOrders(List<Long> orderIds);
/**
* 修改订单出库状态
*
* @param orderId 订单ID
* @param deliveryStatus 出库状态
* @return 是否成功
*/
boolean updateDeliveryStatus(Long orderId, Integer deliveryStatus);
/**
* 修改订单开票状态
*
* @param orderId 订单ID
* @param invoiceStatus 开票状态
* @return 是否成功
*/
boolean updateInvoiceStatus(Long orderId, Integer invoiceStatus);
/**
* 修改订单返利计算状态
*
* @param orderId 订单ID
* @param rebateCalcFlag 返利计算状态
* @return 是否成功
*/
boolean updateRebateCalcFlag(Long orderId, Integer rebateCalcFlag);
}
package com.apple.erp.service.impl;
import com.apple.erp.dto.*;
import com.apple.erp.entity.DeliveryItem;
import com.apple.erp.mapper.DeliveryItemMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.apple.erp.entity.DeliveryMain;
import com.apple.erp.mapper.DeliveryMainMapper;
import com.apple.erp.service.DeliveryMainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 出库主表Service实现类
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Slf4j
@Service
public class DeliveryMainServiceImpl extends ServiceImpl<DeliveryMainMapper, DeliveryMain> implements DeliveryMainService {
@Autowired
private DeliveryItemMapper deliveryItemMapper;
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public Page<DeliveryRes> getDeliveryList(DeliveryQueryReq queryReq) {
LambdaQueryWrapper<DeliveryMain> queryWrapper = Wrappers.lambdaQuery(DeliveryMain.class)
.eq(StringUtils.hasText(queryReq.getDeliveryNo()), DeliveryMain::getDeliveryNo, queryReq.getDeliveryNo())
.eq(StringUtils.hasText(queryReq.getDealerCode()), DeliveryMain::getDealerCode, queryReq.getDealerCode())
.like(StringUtils.hasText(queryReq.getDealerName()), DeliveryMain::getDealerName, queryReq.getDealerName())
.eq(StringUtils.hasText(queryReq.getOrderNo()), DeliveryMain::getOrderNo, queryReq.getOrderNo())
.eq(queryReq.getDeliveryStatus() != null, DeliveryMain::getDeliveryStatus, queryReq.getDeliveryStatus())
.eq(StringUtils.hasText(queryReq.getWarehouseCode()), DeliveryMain::getWarehouseCode, queryReq.getWarehouseCode())
.eq(StringUtils.hasText(queryReq.getDataSource()), DeliveryMain::getDataSource, queryReq.getDataSource())
.ge(StringUtils.hasText(queryReq.getDeliveryStartDate()), DeliveryMain::getDeliveryDate, parseDateTime(queryReq.getDeliveryStartDate()))
.le(StringUtils.hasText(queryReq.getDeliveryEndDate()), DeliveryMain::getDeliveryDate, parseDateTime(queryReq.getDeliveryEndDate()))
.eq(DeliveryMain::getDelFlag, "0")
.orderByDesc(DeliveryMain::getCreateTime);
Page<DeliveryMain> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize());
IPage<DeliveryMain> deliveryMainPage = baseMapper.selectPage(page, queryWrapper);
List<DeliveryRes> deliveryResList = deliveryMainPage.getRecords().stream().map(deliveryMain -> {
DeliveryRes deliveryRes = new DeliveryRes();
BeanUtils.copyProperties(deliveryMain, deliveryRes);
// 查找出库明细
LambdaQueryWrapper<DeliveryItem> itemQueryWrapper = Wrappers.lambdaQuery(DeliveryItem.class)
.eq(DeliveryItem::getDeliveryId, deliveryMain.getDeliveryId());
List<DeliveryItem> deliveryItems = deliveryItemMapper.selectList(itemQueryWrapper);
List<DeliveryItemRes> itemResList = deliveryItems.stream().map(deliveryItem -> {
DeliveryItemRes itemRes = new DeliveryItemRes();
BeanUtils.copyProperties(deliveryItem, itemRes);
return itemRes;
}).collect(Collectors.toList());
deliveryRes.setDeliveryItems(itemResList);
return deliveryRes;
}).collect(Collectors.toList());
Page<DeliveryRes> resultPage = new Page<>(deliveryMainPage.getCurrent(), deliveryMainPage.getSize(), deliveryMainPage.getTotal());
resultPage.setRecords(deliveryResList);
return resultPage;
}
@Override
public DeliveryRes getDeliveryDetail(Long deliveryId) {
DeliveryMain deliveryMain = baseMapper.selectById(deliveryId);
if (deliveryMain == null || "2".equals(deliveryMain.getDelFlag())) {
return null;
}
DeliveryRes deliveryRes = new DeliveryRes();
BeanUtils.copyProperties(deliveryMain, deliveryRes);
LambdaQueryWrapper<DeliveryItem> itemQueryWrapper = Wrappers.lambdaQuery(DeliveryItem.class)
.eq(DeliveryItem::getDeliveryId, deliveryId);
List<DeliveryItem> deliveryItems = deliveryItemMapper.selectList(itemQueryWrapper);
List<DeliveryItemRes> itemResList = deliveryItems.stream().map(deliveryItem -> {
DeliveryItemRes itemRes = new DeliveryItemRes();
BeanUtils.copyProperties(deliveryItem, itemRes);
return itemRes;
}).collect(Collectors.toList());
deliveryRes.setDeliveryItems(itemResList);
return deliveryRes;
}
@Override
@Transactional
public boolean addDelivery(DeliveryAddReq addReq) {
// 检查出库单编号是否已存在
if (isDeliveryNoExist(addReq.getDeliveryNo(), null)) {
throw new IllegalArgumentException("出库单编号已存在");
}
DeliveryMain deliveryMain = new DeliveryMain();
BeanUtils.copyProperties(addReq, deliveryMain);
deliveryMain.setCreateTime(LocalDateTime.now());
deliveryMain.setUpdateTime(LocalDateTime.now());
deliveryMain.setDelFlag("0");
int insert = baseMapper.insert(deliveryMain);
if (insert > 0) {
for (DeliveryItemAddReq itemAddReq : addReq.getDeliveryItems()) {
DeliveryItem deliveryItem = new DeliveryItem();
BeanUtils.copyProperties(itemAddReq, deliveryItem);
deliveryItem.setDeliveryId(deliveryMain.getDeliveryId());
deliveryItem.setDeliveryNo(deliveryMain.getDeliveryNo());
deliveryItem.setOrderNo(deliveryMain.getOrderNo());
deliveryItem.setCreateTime(LocalDateTime.now());
deliveryItem.setUpdateTime(LocalDateTime.now());
deliveryItem.setDelFlag("0");
deliveryItemMapper.insert(deliveryItem);
}
return true;
}
return false;
}
@Override
@Transactional
public boolean updateDelivery(DeliveryUpdateReq updateReq) {
// 检查出库单编号是否已存在且不属于当前出库单
if (isDeliveryNoExist(updateReq.getDeliveryNo(), updateReq.getDeliveryId())) {
throw new IllegalArgumentException("出库单编号已存在");
}
DeliveryMain deliveryMain = new DeliveryMain();
BeanUtils.copyProperties(updateReq, deliveryMain);
deliveryMain.setUpdateTime(LocalDateTime.now());
int update = baseMapper.updateById(deliveryMain);
if (update > 0) {
// 先删除旧的出库明细
LambdaQueryWrapper<DeliveryItem> deleteWrapper = Wrappers.lambdaQuery(DeliveryItem.class)
.eq(DeliveryItem::getDeliveryId, updateReq.getDeliveryId());
deliveryItemMapper.delete(deleteWrapper);
// 插入新的出库明细
for (DeliveryItemUpdateReq itemUpdateReq : updateReq.getDeliveryItems()) {
DeliveryItem deliveryItem = new DeliveryItem();
BeanUtils.copyProperties(itemUpdateReq, deliveryItem);
deliveryItem.setDeliveryId(updateReq.getDeliveryId());
deliveryItem.setDeliveryNo(updateReq.getDeliveryNo());
deliveryItem.setOrderNo(updateReq.getOrderNo());
deliveryItem.setCreateTime(LocalDateTime.now()); // 新增明细时设置创建时间
deliveryItem.setUpdateTime(LocalDateTime.now());
deliveryItem.setDelFlag("0");
deliveryItemMapper.insert(deliveryItem);
}
return true;
}
return false;
}
@Override
@Transactional
public boolean deleteDelivery(Long deliveryId) {
DeliveryMain deliveryMain = new DeliveryMain();
deliveryMain.setDeliveryId(deliveryId);
deliveryMain.setDelFlag("2"); // 逻辑删除
deliveryMain.setUpdateTime(LocalDateTime.now());
return baseMapper.updateById(deliveryMain) > 0;
}
@Override
@Transactional
public boolean batchDeleteDeliveries(List<Long> deliveryIds) {
List<DeliveryMain> deliveriesToUpdate = deliveryIds.stream().map(deliveryId -> {
DeliveryMain deliveryMain = new DeliveryMain();
deliveryMain.setDeliveryId(deliveryId);
deliveryMain.setDelFlag("2");
deliveryMain.setUpdateTime(LocalDateTime.now());
return deliveryMain;
}).collect(Collectors.toList());
return updateBatchById(deliveriesToUpdate);
}
@Override
public boolean updateDeliveryStatus(Long deliveryId, Integer deliveryStatus) {
DeliveryMain deliveryMain = new DeliveryMain();
deliveryMain.setDeliveryId(deliveryId);
deliveryMain.setDeliveryStatus(deliveryStatus);
deliveryMain.setUpdateTime(LocalDateTime.now());
return baseMapper.updateById(deliveryMain) > 0;
}
/**
* 检查出库单编号是否已存在
* @param deliveryNo 出库单编号
* @param excludeDeliveryId 排除的出库单ID(用于修改时)
* @return 是否存在
*/
private boolean isDeliveryNoExist(String deliveryNo, Long excludeDeliveryId) {
LambdaQueryWrapper<DeliveryMain> queryWrapper = Wrappers.lambdaQuery(DeliveryMain.class)
.eq(DeliveryMain::getDeliveryNo, deliveryNo)
.eq(DeliveryMain::getDelFlag, "0");
if (excludeDeliveryId != null) {
queryWrapper.ne(DeliveryMain::getDeliveryId, excludeDeliveryId);
}
return baseMapper.selectCount(queryWrapper) > 0;
}
private LocalDateTime parseDateTime(String dateTimeStr) {
if (!StringUtils.hasText(dateTimeStr)) {
return null;
}
try {
return LocalDateTime.parse(dateTimeStr, DATE_TIME_FORMATTER);
} catch (Exception e) {
log.warn("日期时间解析失败: {}", dateTimeStr, e);
return null;
}
}
}
package com.apple.erp.service.impl;
import com.apple.erp.dto.*;
import com.apple.erp.entity.InvoiceItem;
import com.apple.erp.mapper.InvoiceItemMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.apple.erp.entity.InvoiceMain;
import com.apple.erp.mapper.InvoiceMainMapper;
import com.apple.erp.service.InvoiceMainService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* 发票主表Service实现类
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Slf4j
@Service
public class InvoiceMainServiceImpl extends ServiceImpl<InvoiceMainMapper, InvoiceMain> implements InvoiceMainService {
@Autowired
private InvoiceItemMapper invoiceItemMapper;
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Override
public Page<InvoiceRes> getInvoiceList(InvoiceQueryReq queryReq) {
LambdaQueryWrapper<InvoiceMain> queryWrapper = Wrappers.lambdaQuery(InvoiceMain.class)
.eq(StringUtils.hasText(queryReq.getInvoiceNo()), InvoiceMain::getInvoiceNo, queryReq.getInvoiceNo())
.eq(StringUtils.hasText(queryReq.getOrderNo()), InvoiceMain::getOrderNo, queryReq.getOrderNo())
.eq(StringUtils.hasText(queryReq.getDeliveryNo()), InvoiceMain::getDeliveryNo, queryReq.getDeliveryNo())
.eq(StringUtils.hasText(queryReq.getDealerCode()), InvoiceMain::getDealerCode, queryReq.getDealerCode())
.like(StringUtils.hasText(queryReq.getDealerName()), InvoiceMain::getDealerName, queryReq.getDealerName())
.eq(queryReq.getInvoiceStatus() != null, InvoiceMain::getInvoiceStatus, queryReq.getInvoiceStatus())
.eq(StringUtils.hasText(queryReq.getDataSource()), InvoiceMain::getDataSource, queryReq.getDataSource())
.ge(StringUtils.hasText(queryReq.getInvoiceStartDate()), InvoiceMain::getInvoiceDate, parseDate(queryReq.getInvoiceStartDate()))
.le(StringUtils.hasText(queryReq.getInvoiceEndDate()), InvoiceMain::getInvoiceDate, parseDate(queryReq.getInvoiceEndDate()))
.ge(queryReq.getMinAmount() != null, InvoiceMain::getTotalAmount, queryReq.getMinAmount())
.le(queryReq.getMaxAmount() != null, InvoiceMain::getTotalAmount, queryReq.getMaxAmount())
.eq(InvoiceMain::getDelFlag, "0")
.orderByDesc(InvoiceMain::getCreateTime);
Page<InvoiceMain> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize());
IPage<InvoiceMain> invoiceMainPage = baseMapper.selectPage(page, queryWrapper);
List<InvoiceRes> invoiceResList = invoiceMainPage.getRecords().stream().map(invoiceMain -> {
InvoiceRes invoiceRes = new InvoiceRes();
BeanUtils.copyProperties(invoiceMain, invoiceRes);
// 查找发票明细
LambdaQueryWrapper<InvoiceItem> itemQueryWrapper = Wrappers.lambdaQuery(InvoiceItem.class)
.eq(InvoiceItem::getInvoiceId, invoiceMain.getInvoiceId());
List<InvoiceItem> invoiceItems = invoiceItemMapper.selectList(itemQueryWrapper);
List<InvoiceItemRes> itemResList = invoiceItems.stream().map(invoiceItem -> {
InvoiceItemRes itemRes = new InvoiceItemRes();
BeanUtils.copyProperties(invoiceItem, itemRes);
return itemRes;
}).collect(Collectors.toList());
invoiceRes.setInvoiceItems(itemResList);
return invoiceRes;
}).collect(Collectors.toList());
Page<InvoiceRes> resultPage = new Page<>(invoiceMainPage.getCurrent(), invoiceMainPage.getSize(), invoiceMainPage.getTotal());
resultPage.setRecords(invoiceResList);
return resultPage;
}
@Override
public InvoiceRes getInvoiceDetail(Long invoiceId) {
InvoiceMain invoiceMain = baseMapper.selectById(invoiceId);
if (invoiceMain == null || "2".equals(invoiceMain.getDelFlag())) {
return null;
}
InvoiceRes invoiceRes = new InvoiceRes();
BeanUtils.copyProperties(invoiceMain, invoiceRes);
LambdaQueryWrapper<InvoiceItem> itemQueryWrapper = Wrappers.lambdaQuery(InvoiceItem.class)
.eq(InvoiceItem::getInvoiceId, invoiceId);
List<InvoiceItem> invoiceItems = invoiceItemMapper.selectList(itemQueryWrapper);
List<InvoiceItemRes> itemResList = invoiceItems.stream().map(invoiceItem -> {
InvoiceItemRes itemRes = new InvoiceItemRes();
BeanUtils.copyProperties(invoiceItem, itemRes);
return itemRes;
}).collect(Collectors.toList());
invoiceRes.setInvoiceItems(itemResList);
return invoiceRes;
}
@Override
@Transactional
public boolean addInvoice(InvoiceAddReq addReq) {
// 检查发票编号是否已存在
if (isInvoiceNoExist(addReq.getInvoiceNo(), null)) {
throw new IllegalArgumentException("发票编号已存在");
}
InvoiceMain invoiceMain = new InvoiceMain();
BeanUtils.copyProperties(addReq, invoiceMain);
invoiceMain.setCreateTime(LocalDateTime.now());
invoiceMain.setUpdateTime(LocalDateTime.now());
invoiceMain.setDelFlag("0");
int insert = baseMapper.insert(invoiceMain);
if (insert > 0) {
for (InvoiceItemAddReq itemAddReq : addReq.getInvoiceItems()) {
InvoiceItem invoiceItem = new InvoiceItem();
BeanUtils.copyProperties(itemAddReq, invoiceItem);
invoiceItem.setInvoiceId(invoiceMain.getInvoiceId());
invoiceItem.setInvoiceNo(invoiceMain.getInvoiceNo());
invoiceItem.setOrderNo(invoiceMain.getOrderNo());
invoiceItem.setCreateTime(LocalDateTime.now());
invoiceItem.setUpdateTime(LocalDateTime.now());
invoiceItem.setDelFlag("0");
invoiceItemMapper.insert(invoiceItem);
}
return true;
}
return false;
}
@Override
@Transactional
public boolean updateInvoice(InvoiceUpdateReq updateReq) {
// 检查发票编号是否已存在且不属于当前发票
if (isInvoiceNoExist(updateReq.getInvoiceNo(), updateReq.getInvoiceId())) {
throw new IllegalArgumentException("发票编号已存在");
}
InvoiceMain invoiceMain = new InvoiceMain();
BeanUtils.copyProperties(updateReq, invoiceMain);
invoiceMain.setUpdateTime(LocalDateTime.now());
int update = baseMapper.updateById(invoiceMain);
if (update > 0) {
// 先删除旧的发票明细
LambdaQueryWrapper<InvoiceItem> deleteWrapper = Wrappers.lambdaQuery(InvoiceItem.class)
.eq(InvoiceItem::getInvoiceId, updateReq.getInvoiceId());
invoiceItemMapper.delete(deleteWrapper);
// 插入新的发票明细
for (InvoiceItemUpdateReq itemUpdateReq : updateReq.getInvoiceItems()) {
InvoiceItem invoiceItem = new InvoiceItem();
BeanUtils.copyProperties(itemUpdateReq, invoiceItem);
invoiceItem.setInvoiceId(updateReq.getInvoiceId());
invoiceItem.setInvoiceNo(updateReq.getInvoiceNo());
invoiceItem.setOrderNo(updateReq.getOrderNo());
invoiceItem.setCreateTime(LocalDateTime.now()); // 新增明细时设置创建时间
invoiceItem.setUpdateTime(LocalDateTime.now());
invoiceItem.setDelFlag("0");
invoiceItemMapper.insert(invoiceItem);
}
return true;
}
return false;
}
@Override
@Transactional
public boolean deleteInvoice(Long invoiceId) {
InvoiceMain invoiceMain = new InvoiceMain();
invoiceMain.setInvoiceId(invoiceId);
invoiceMain.setDelFlag("2"); // 逻辑删除
invoiceMain.setUpdateTime(LocalDateTime.now());
return baseMapper.updateById(invoiceMain) > 0;
}
@Override
@Transactional
public boolean batchDeleteInvoices(List<Long> invoiceIds) {
List<InvoiceMain> invoicesToUpdate = invoiceIds.stream().map(invoiceId -> {
InvoiceMain invoiceMain = new InvoiceMain();
invoiceMain.setInvoiceId(invoiceId);
invoiceMain.setDelFlag("2");
invoiceMain.setUpdateTime(LocalDateTime.now());
return invoiceMain;
}).collect(Collectors.toList());
return updateBatchById(invoicesToUpdate);
}
@Override
public boolean updateInvoiceStatus(Long invoiceId, Integer invoiceStatus) {
InvoiceMain invoiceMain = new InvoiceMain();
invoiceMain.setInvoiceId(invoiceId);
invoiceMain.setInvoiceStatus(invoiceStatus);
invoiceMain.setUpdateTime(LocalDateTime.now());
return baseMapper.updateById(invoiceMain) > 0;
}
/**
* 检查发票编号是否已存在
* @param invoiceNo 发票编号
* @param excludeInvoiceId 排除的发票ID(用于修改时)
* @return 是否存在
*/
private boolean isInvoiceNoExist(String invoiceNo, Long excludeInvoiceId) {
LambdaQueryWrapper<InvoiceMain> queryWrapper = Wrappers.lambdaQuery(InvoiceMain.class)
.eq(InvoiceMain::getInvoiceNo, invoiceNo)
.eq(InvoiceMain::getDelFlag, "0");
if (excludeInvoiceId != null) {
queryWrapper.ne(InvoiceMain::getInvoiceId, excludeInvoiceId);
}
return baseMapper.selectCount(queryWrapper) > 0;
}
private LocalDate parseDate(String dateStr) {
if (!StringUtils.hasText(dateStr)) {
return null;
}
try {
return LocalDate.parse(dateStr, DATE_FORMATTER);
} catch (Exception e) {
log.warn("日期解析失败: {}", dateStr, e);
return null;
}
}
}
package com.apple.erp.service.impl;
import com.apple.erp.dto.*;
import com.apple.erp.entity.OrderItem;
import com.apple.erp.entity.OrderMain;
import com.apple.erp.mapper.OrderItemMapper;
import com.apple.erp.mapper.OrderMainMapper;
import com.apple.erp.service.OrderMainService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
* 订单主表Service实现类
*
* @author Apple ERP System
* @since 2025-01-01
*/
@Service
public class OrderMainServiceImpl extends ServiceImpl<OrderMainMapper, OrderMain> implements OrderMainService {
@Autowired
private OrderMainMapper orderMainMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Override
public Page<OrderRes> getOrderList(OrderQueryReq queryReq) {
Page<OrderMain> page = new Page<>(queryReq.getPageNum(), queryReq.getPageSize());
LambdaQueryWrapper<OrderMain> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(OrderMain::getDelFlag, "0");
// 构建查询条件
if (StringUtils.hasText(queryReq.getOrderNo())) {
wrapper.like(OrderMain::getOrderNo, queryReq.getOrderNo());
}
if (StringUtils.hasText(queryReq.getDealerCode())) {
wrapper.like(OrderMain::getDealerCode, queryReq.getDealerCode());
}
if (StringUtils.hasText(queryReq.getDealerName())) {
wrapper.like(OrderMain::getDealerName, queryReq.getDealerName());
}
if (queryReq.getDeliveryStatus() != null) {
wrapper.eq(OrderMain::getDeliveryStatus, queryReq.getDeliveryStatus());
}
if (queryReq.getInvoiceStatus() != null) {
wrapper.eq(OrderMain::getInvoiceStatus, queryReq.getInvoiceStatus());
}
if (queryReq.getRebateCalcFlag() != null) {
wrapper.eq(OrderMain::getRebateCalcFlag, queryReq.getRebateCalcFlag());
}
if (StringUtils.hasText(queryReq.getDataSource())) {
wrapper.like(OrderMain::getDataSource, queryReq.getDataSource());
}
if (queryReq.getVerifyStatus() != null) {
wrapper.eq(OrderMain::getVerifyStatus, queryReq.getVerifyStatus());
}
if (queryReq.getOrderStartDate() != null) {
wrapper.ge(OrderMain::getOrderDate, queryReq.getOrderStartDate());
}
if (queryReq.getOrderEndDate() != null) {
wrapper.le(OrderMain::getOrderDate, queryReq.getOrderEndDate());
}
if (queryReq.getMinAmount() != null) {
wrapper.ge(OrderMain::getTotalAmount, queryReq.getMinAmount());
}
if (queryReq.getMaxAmount() != null) {
wrapper.le(OrderMain::getTotalAmount, queryReq.getMaxAmount());
}
wrapper.orderByDesc(OrderMain::getCreateTime);
Page<OrderMain> orderPage = orderMainMapper.selectPage(page, wrapper);
// 转换为响应DTO
Page<OrderRes> resultPage = new Page<>(orderPage.getCurrent(), orderPage.getSize(), orderPage.getTotal());
List<OrderRes> orderResList = orderPage.getRecords().stream().map(order -> {
OrderRes orderRes = new OrderRes();
BeanUtils.copyProperties(order, orderRes);
return orderRes;
}).collect(Collectors.toList());
resultPage.setRecords(orderResList);
return resultPage;
}
@Override
public OrderRes getOrderDetail(Long orderId) {
OrderMain order = orderMainMapper.selectById(orderId);
if (order == null || "2".equals(order.getDelFlag())) {
return null;
}
OrderRes orderRes = new OrderRes();
BeanUtils.copyProperties(order, orderRes);
// 查询订单明细
LambdaQueryWrapper<OrderItem> itemWrapper = new LambdaQueryWrapper<>();
itemWrapper.eq(OrderItem::getOrderId, orderId);
itemWrapper.eq(OrderItem::getDelFlag, "0");
itemWrapper.orderByAsc(OrderItem::getCreateTime);
List<OrderItem> orderItems = orderItemMapper.selectList(itemWrapper);
List<OrderItemRes> itemResList = orderItems.stream().map(item -> {
OrderItemRes itemRes = new OrderItemRes();
BeanUtils.copyProperties(item, itemRes);
return itemRes;
}).collect(Collectors.toList());
orderRes.setOrderItems(itemResList);
return orderRes;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean addOrder(OrderAddReq addReq) {
// 检查订单编号是否已存在
LambdaQueryWrapper<OrderMain> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(OrderMain::getOrderNo, addReq.getOrderNo());
wrapper.eq(OrderMain::getDelFlag, "0");
if (orderMainMapper.selectCount(wrapper) > 0) {
throw new RuntimeException("订单编号已存在");
}
// 保存订单主表
OrderMain order = new OrderMain();
BeanUtils.copyProperties(addReq, order);
order.setCreateTime(LocalDateTime.now());
order.setDelFlag("0");
int result = orderMainMapper.insert(order);
if (result <= 0) {
return false;
}
// 保存订单明细
for (OrderItemAddReq itemReq : addReq.getOrderItems()) {
OrderItem item = new OrderItem();
BeanUtils.copyProperties(itemReq, item);
item.setOrderId(order.getOrderId());
item.setOrderNo(order.getOrderNo());
item.setCreateTime(LocalDateTime.now());
item.setDelFlag("0");
// 计算明细金额
if (item.getItemAmount() == null) {
item.setItemAmount(item.getUnitPrice().multiply(java.math.BigDecimal.valueOf(item.getProductQty())));
}
orderItemMapper.insert(item);
}
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateOrder(OrderUpdateReq updateReq) {
OrderMain existingOrder = orderMainMapper.selectById(updateReq.getOrderId());
if (existingOrder == null || "2".equals(existingOrder.getDelFlag())) {
throw new RuntimeException("订单不存在");
}
// 检查订单编号是否被其他订单使用
if (!existingOrder.getOrderNo().equals(updateReq.getOrderNo())) {
LambdaQueryWrapper<OrderMain> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(OrderMain::getOrderNo, updateReq.getOrderNo());
wrapper.ne(OrderMain::getOrderId, updateReq.getOrderId());
wrapper.eq(OrderMain::getDelFlag, "0");
if (orderMainMapper.selectCount(wrapper) > 0) {
throw new RuntimeException("订单编号已被其他订单使用");
}
}
// 更新订单主表
OrderMain order = new OrderMain();
BeanUtils.copyProperties(updateReq, order);
order.setUpdateTime(LocalDateTime.now());
int result = orderMainMapper.updateById(order);
if (result <= 0) {
return false;
}
// 删除原有明细
LambdaQueryWrapper<OrderItem> deleteWrapper = new LambdaQueryWrapper<>();
deleteWrapper.eq(OrderItem::getOrderId, updateReq.getOrderId());
orderItemMapper.delete(deleteWrapper);
// 保存新明细
for (OrderItemUpdateReq itemReq : updateReq.getOrderItems()) {
OrderItem item = new OrderItem();
BeanUtils.copyProperties(itemReq, item);
item.setOrderId(updateReq.getOrderId());
item.setOrderNo(updateReq.getOrderNo());
item.setCreateTime(LocalDateTime.now());
item.setDelFlag("0");
// 计算明细金额
if (item.getItemAmount() == null) {
item.setItemAmount(item.getUnitPrice().multiply(java.math.BigDecimal.valueOf(item.getProductQty())));
}
orderItemMapper.insert(item);
}
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteOrder(Long orderId) {
OrderMain order = orderMainMapper.selectById(orderId);
if (order == null || "2".equals(order.getDelFlag())) {
throw new RuntimeException("订单不存在");
}
// 软删除订单主表
order.setDelFlag("2");
order.setUpdateTime(LocalDateTime.now());
orderMainMapper.updateById(order);
// 软删除订单明细
LambdaQueryWrapper<OrderItem> itemWrapper = new LambdaQueryWrapper<>();
itemWrapper.eq(OrderItem::getOrderId, orderId);
List<OrderItem> items = orderItemMapper.selectList(itemWrapper);
for (OrderItem item : items) {
item.setDelFlag("2");
item.setUpdateTime(LocalDateTime.now());
orderItemMapper.updateById(item);
}
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean batchDeleteOrders(List<Long> orderIds) {
for (Long orderId : orderIds) {
deleteOrder(orderId);
}
return true;
}
@Override
public boolean updateDeliveryStatus(Long orderId, Integer deliveryStatus) {
OrderMain order = orderMainMapper.selectById(orderId);
if (order == null || "2".equals(order.getDelFlag())) {
throw new RuntimeException("订单不存在");
}
order.setDeliveryStatus(deliveryStatus);
order.setUpdateTime(LocalDateTime.now());
return orderMainMapper.updateById(order) > 0;
}
@Override
public boolean updateInvoiceStatus(Long orderId, Integer invoiceStatus) {
OrderMain order = orderMainMapper.selectById(orderId);
if (order == null || "2".equals(order.getDelFlag())) {
throw new RuntimeException("订单不存在");
}
order.setInvoiceStatus(invoiceStatus);
order.setUpdateTime(LocalDateTime.now());
return orderMainMapper.updateById(order) > 0;
}
@Override
public boolean updateRebateCalcFlag(Long orderId, Integer rebateCalcFlag) {
OrderMain order = orderMainMapper.selectById(orderId);
if (order == null || "2".equals(order.getDelFlag())) {
throw new RuntimeException("订单不存在");
}
order.setRebateCalcFlag(rebateCalcFlag);
order.setUpdateTime(LocalDateTime.now());
return orderMainMapper.updateById(order) > 0;
}
}
-- Apple经销商ERP系统 - 订单管理、出库查询、发票管理按钮权限配置脚本
-- 创建时间: 2025-01-27
-- 版本: v1.0
-- 设置数据库和字符集
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- 使用数据库
USE apple_erp;
-- =========================================
-- 1. 插入订单管理相关按钮权限
-- =========================================
-- 订单管理按钮权限 (parent_id = 2)
INSERT INTO t_sys_menu (menu_id, parent_id, menu_name, menu_type, path, icon, sort, status, perms, create_by, create_time, update_by, update_time, del_flag)
VALUES
(201, 2, '订单查询', '2', '', '', 1, '1', 'order:list', 'admin', NOW(), 'admin', NOW(), '0'),
(202, 2, '订单新增', '2', '', '', 2, '1', 'order:add', 'admin', NOW(), 'admin', NOW(), '0'),
(203, 2, '订单修改', '2', '', '', 3, '1', 'order:edit', 'admin', NOW(), 'admin', NOW(), '0'),
(204, 2, '订单删除', '2', '', '', 4, '1', 'order:delete', 'admin', NOW(), 'admin', NOW(), '0'),
(205, 2, '订单详情', '2', '', '', 5, '1', 'order:detail', 'admin', NOW(), 'admin', NOW(), '0'),
(206, 2, '订单导出', '2', '', '', 6, '1', 'order:export', 'admin', NOW(), 'admin', NOW(), '0'),
(207, 2, '订单导入', '2', '', '', 7, '1', 'order:import', 'admin', NOW(), 'admin', NOW(), '0'),
(208, 2, '订单审核', '2', '', '', 8, '1', 'order:audit', 'admin', NOW(), 'admin', NOW(), '0'),
(209, 2, '订单取消', '2', '', '', 9, '1', 'order:cancel', 'admin', NOW(), 'admin', NOW(), '0'),
(210, 2, '订单完成', '2', '', '', 10, '1', 'order:complete', 'admin', NOW(), 'admin', NOW(), '0');
-- =========================================
-- 2. 插入出库查询相关按钮权限
-- =========================================
-- 出库查询按钮权限 (parent_id = 3)
INSERT INTO t_sys_menu (menu_id, parent_id, menu_name, menu_type, path, icon, sort, status, perms, create_by, create_time, update_by, update_time, del_flag)
VALUES
(301, 3, '出库查询', '2', '', '', 1, '1', 'delivery:list', 'admin', NOW(), 'admin', NOW(), '0'),
(302, 3, '出库详情', '2', '', '', 2, '1', 'delivery:query', 'admin', NOW(), 'admin', NOW(), '0'),
(303, 3, '出库导出', '2', '', '', 3, '1', 'delivery:export', 'admin', NOW(), 'admin', NOW(), '0'),
(304, 3, '出库统计', '2', '', '', 4, '1', 'delivery:statistics', 'admin', NOW(), 'admin', NOW(), '0'),
(305, 3, '出库打印', '2', '', '', 5, '1', 'delivery:print', 'admin', NOW(), 'admin', NOW(), '0'),
(306, 3, '出库确认', '2', '', '', 6, '1', 'delivery:confirm', 'admin', NOW(), 'admin', NOW(), '0'),
(307, 3, '出库撤销', '2', '', '', 7, '1', 'delivery:cancel', 'admin', NOW(), 'admin', NOW(), '0');
-- =========================================
-- 3. 插入发票管理相关按钮权限
-- =========================================
-- 发票管理按钮权限 (parent_id = 4)
INSERT INTO t_sys_menu (menu_id, parent_id, menu_name, menu_type, path, icon, sort, status, perms, create_by, create_time, update_by, update_time, del_flag)
VALUES
(401, 4, '发票查询', '2', '', '', 1, '1', 'invoice:list', 'admin', NOW(), 'admin', NOW(), '0'),
(402, 4, '发票新增', '2', '', '', 2, '1', 'invoice:add', 'admin', NOW(), 'admin', NOW(), '0'),
(403, 4, '发票修改', '2', '', '', 3, '1', 'invoice:edit', 'admin', NOW(), 'admin', NOW(), '0'),
(404, 4, '发票删除', '2', '', '', 4, '1', 'invoice:remove', 'admin', NOW(), 'admin', NOW(), '0'),
(405, 4, '发票详情', '2', '', '', 5, '1', 'invoice:query', 'admin', NOW(), 'admin', NOW(), '0'),
(406, 4, '发票导出', '2', '', '', 6, '1', 'invoice:export', 'admin', NOW(), 'admin', NOW(), '0'),
(407, 4, '发票打印', '2', '', '', 7, '1', 'invoice:print', 'admin', NOW(), 'admin', NOW(), '0'),
(408, 4, '发票审核', '2', '', '', 8, '1', 'invoice:audit', 'admin', NOW(), 'admin', NOW(), '0'),
(409, 4, '发票作废', '2', '', '', 9, '1', 'invoice:void', 'admin', NOW(), 'admin', NOW(), '0'),
(410, 4, '发票红冲', '2', '', '', 10, '1', 'invoice:reverse', 'admin', NOW(), 'admin', NOW(), '0');
-- =========================================
-- 4. 为ADMIN角色分配新权限
-- =========================================
-- 获取ADMIN角色ID
SET @admin_role_id = (SELECT role_id FROM t_sys_role WHERE role_code = 'ADMIN' AND del_flag = '0' LIMIT 1);
-- 为ADMIN角色分配订单管理按钮权限
INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @admin_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE menu_id IN (201, 202, 203, 204, 205, 206, 207, 208, 209, 210)
AND del_flag = '0';
-- 为ADMIN角色分配出库查询按钮权限
INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @admin_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE menu_id IN (301, 302, 303, 304, 305, 306, 307)
AND del_flag = '0';
-- 为ADMIN角色分配发票管理按钮权限
INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @admin_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE menu_id IN (401, 402, 403, 404, 405, 406, 407, 408, 409, 410)
AND del_flag = '0';
-- =========================================
-- 5. 为OPERATOR角色分配权限
-- =========================================
-- 获取OPERATOR角色ID
SET @operator_role_id = (SELECT role_id FROM t_sys_role WHERE role_code = 'OPERATOR' AND del_flag = '0' LIMIT 1);
-- 为OPERATOR角色分配订单管理按钮权限(除删除外)
INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @operator_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE menu_id IN (201, 202, 203, 205, 206, 207, 208, 209, 210)
AND del_flag = '0';
-- 为OPERATOR角色分配出库查询按钮权限
INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @operator_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE menu_id IN (301, 302, 303, 304, 305, 306, 307)
AND del_flag = '0';
-- 为OPERATOR角色分配发票管理按钮权限(除删除外)
INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @operator_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE menu_id IN (401, 402, 403, 405, 406, 407, 408, 409, 410)
AND del_flag = '0';
-- =========================================
-- 6. 为DEALER_MANAGER角色分配权限
-- =========================================
-- 获取DEALER_MANAGER角色ID
SET @dealer_manager_role_id = (SELECT role_id FROM t_sys_role WHERE role_code = 'DEALER_MANAGER' AND del_flag = '0' LIMIT 1);
-- 为DEALER_MANAGER角色分配订单管理按钮权限(只读权限)
INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @dealer_manager_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE menu_id IN (201, 205, 206)
AND del_flag = '0';
-- 为DEALER_MANAGER角色分配出库查询按钮权限
INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @dealer_manager_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE menu_id IN (301, 302, 303, 304, 305)
AND del_flag = '0';
-- 为DEALER_MANAGER角色分配发票管理按钮权限(只读权限)
INSERT INTO t_sys_role_menu (role_id, menu_id, create_by, create_time, update_by, update_time, del_flag)
SELECT @dealer_manager_role_id, menu_id, 'admin', NOW(), 'admin', NOW(), '0'
FROM t_sys_menu
WHERE menu_id IN (401, 405, 406, 407)
AND del_flag = '0';
-- 重置外键检查
SET FOREIGN_KEY_CHECKS = 1;
-- 输出完成信息
SELECT '订单管理、出库查询、发票管理按钮权限配置完成!' AS message;
......@@ -5,77 +5,5 @@
// Generated by unplugin-auto-import
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const createPinia: typeof import('pinia')['createPinia']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const defineStore: typeof import('pinia')['defineStore']
const effectScope: typeof import('vue')['effectScope']
const getActivePinia: typeof import('pinia')['getActivePinia']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const mapActions: typeof import('pinia')['mapActions']
const mapGetters: typeof import('pinia')['mapGetters']
const mapState: typeof import('pinia')['mapState']
const mapStores: typeof import('pinia')['mapStores']
const mapWritableState: typeof import('pinia')['mapWritableState']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const setActivePinia: typeof import('pinia')['setActivePinia']
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const storeToRefs: typeof import('pinia')['storeToRefs']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useId: typeof import('vue')['useId']
const useLink: typeof import('vue-router')['useLink']
const useModel: typeof import('vue')['useModel']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
const useSlots: typeof import('vue')['useSlots']
const useTemplateRef: typeof import('vue')['useTemplateRef']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
......
import { request } from '../utils/request'
// 订单相关类型定义
export interface OrderInfo {
orderId: number
orderNo: string
dealerCode: string
dealerName: string
orderDate: string
totalAmount: number
rebateAmount?: number
deliveryStatus: number
invoiceStatus: number
rebateCalcFlag: number
dataSource?: string
verifyStatus: number
uploadTime?: string
createBy?: string
createTime?: string
updateBy?: string
updateTime?: string
orderItems?: OrderItemInfo[]
}
export interface OrderItemInfo {
itemId?: number
orderId?: number
productCode: string
productName: string
productQty: number
unitPrice: number
itemAmount: number
}
export interface OrderQueryReq {
orderNo?: string
dealerCode?: string
dealerName?: string
deliveryStatus?: number
invoiceStatus?: number
rebateCalcFlag?: number
dataSource?: string
verifyStatus?: number
orderStartDate?: string
orderEndDate?: string
minAmount?: number
maxAmount?: number
pageNum?: number
pageSize?: number
}
export interface OrderAddReq {
orderNo: string
dealerCode: string
dealerName: string
orderDate: string
totalAmount: number
rebateAmount?: number
deliveryStatus?: number
invoiceStatus?: number
rebateCalcFlag?: number
dataSource?: string
verifyStatus?: number
uploadTime?: string
orderItems: OrderItemAddReq[]
}
export interface OrderItemAddReq {
productCode: string
productName: string
productQty: number
unitPrice: number
itemAmount: number
}
export interface OrderUpdateReq {
orderId: number
orderNo: string
dealerCode: string
dealerName: string
orderDate: string
totalAmount: number
rebateAmount?: number
deliveryStatus?: number
invoiceStatus?: number
rebateCalcFlag?: number
dataSource?: string
verifyStatus?: number
uploadTime?: string
orderItems: OrderItemUpdateReq[]
}
export interface OrderItemUpdateReq {
itemId?: number
productCode: string
productName: string
productQty: number
unitPrice: number
itemAmount: number
}
// 订单API接口
export const orderApi = {
// 分页查询订单列表
getOrderList: (params: OrderQueryReq) => {
return request.get('/order/list', { params })
},
// 获取订单详情
getOrderDetail: (orderId: number) => {
return request.get(`/order/${orderId}`)
},
// 新增订单
addOrder: (data: OrderAddReq) => {
return request.post('/order', data)
},
// 修改订单
updateOrder: (data: OrderUpdateReq) => {
return request.post('/order/update', data)
},
// 删除订单
deleteOrder: (orderId: number) => {
return request.delete(`/order/${orderId}`)
},
// 批量删除订单
batchDeleteOrders: (orderIds: number[]) => {
return request.post('/order/batchDelete', orderIds)
},
// 修改订单出库状态
updateDeliveryStatus: (orderId: number, deliveryStatus: number) => {
return request.post(`/order/${orderId}/deliveryStatus`, null, {
params: { deliveryStatus }
})
},
// 修改订单开票状态
updateInvoiceStatus: (orderId: number, invoiceStatus: number) => {
return request.post(`/order/${orderId}/invoiceStatus`, null, {
params: { invoiceStatus }
})
},
// 修改订单返利计算状态
updateRebateCalcFlag: (orderId: number, rebateCalcFlag: number) => {
return request.post(`/order/${orderId}/rebateCalcFlag`, null, {
params: { rebateCalcFlag }
})
}
}
......@@ -141,6 +141,9 @@ const tabContainer = ref<HTMLElement>()
// 菜单项配置
const menuItems = ref([
{ name: '首页', path: '/main/dashboard', icon: '🏠' },
{ name: '订单管理', path: '/main/order', icon: '📋' },
{ name: '出库查询', path: '/main/delivery', icon: '📦' },
{ name: '发票管理', path: '/main/invoice', icon: '🧾' },
{ name: '产品管理', path: '/main/product', icon: '📦' },
{ name: '经销商管理', path: '/main/dealer', icon: '🏢' },
{
......
......@@ -135,6 +135,33 @@ const staticRoutes: RouteRecordRaw[] = [
}
},
{
path: 'order',
name: 'Order',
component: () => import('@/views/order/index.vue'),
meta: {
title: '订单管理',
requiresAuth: true
}
},
{
path: 'delivery',
name: 'Delivery',
component: () => import('@/views/delivery/index.vue'),
meta: {
title: '出库查询',
requiresAuth: true
}
},
{
path: 'invoice',
name: 'Invoice',
component: () => import('@/views/invoice/index.vue'),
meta: {
title: '发票管理',
requiresAuth: true
}
},
{
path: 'settings',
name: 'Settings',
component: () => import('@/views/settings/index.vue'),
......
......@@ -3,16 +3,19 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import { useUserStore } from '@/stores/modules/user'
import router from '@/router'
// API基础配置
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8083'
// 创建axios实例
const service: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
baseURL: API_BASE_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
console.log('Axios实例创建,baseURL:', import.meta.env.VITE_API_BASE_URL)
console.log('Request工具实例创建,baseURL:', API_BASE_URL)
// 请求拦截器
service.interceptors.request.use(
......@@ -25,12 +28,11 @@ service.interceptors.request.use(
headers: config.headers
})
const userStore = useUserStore()
// 添加token到请求头
if (userStore.token) {
const token = localStorage.getItem('token')
if (token) {
config.headers = config.headers || {}
config.headers['Authorization'] = `Bearer ${userStore.token}`
config.headers['Authorization'] = `Bearer ${token}`
}
return config
......
<template>
<div class="delivery-container">
<!-- 搜索区域 -->
<div class="search-section">
<div class="search-row">
<div class="search-item">
<label>订单编号:</label>
<input
v-model="searchParams.orderNo"
class="search-input"
placeholder="请输入订单编号"
/>
</div>
<div class="search-item">
<label>经销商编码:</label>
<input
v-model="searchParams.dealerCode"
class="search-input"
placeholder="请输入经销商编码"
/>
</div>
<div class="search-item">
<label>经销商名称:</label>
<input
v-model="searchParams.dealerName"
class="search-input"
placeholder="请输入经销商名称"
/>
</div>
<div class="search-item">
<label>出库状态:</label>
<select v-model="searchParams.deliveryStatus" class="search-select">
<option value="">所有</option>
<option value="0">未出库</option>
<option value="1">已出库</option>
</select>
</div>
</div>
<div class="search-row">
<div class="search-item">
<label>开始日期:</label>
<input
v-model="searchParams.startDate"
class="search-input"
type="date"
/>
</div>
<div class="search-item">
<label>结束日期:</label>
<input
v-model="searchParams.endDate"
class="search-input"
type="date"
/>
</div>
<div class="search-actions">
<button @click="handleSearch" class="search-btn">🔍 搜索</button>
<button @click="handleReset" class="reset-btn">🔄 重置</button>
</div>
</div>
</div>
<!-- 操作按钮区域 -->
<div class="action-section">
<div class="action-buttons">
<button @click="handleAdd" class="action-btn primary">➕ 新增出库</button>
<button @click="handleExport" class="action-btn secondary">📋 导出</button>
</div>
</div>
<!-- 数据表格 -->
<div class="table-section">
<div class="table-header">
<div class="table-controls">
<button @click="handleTableSearch" class="control-btn">🔍</button>
<button @click="handleTableRefresh" class="control-btn">🔄</button>
<button @click="handleTableExport" class="control-btn">📋</button>
<button @click="handleTableViewToggle" class="control-btn">⊞</button>
</div>
</div>
<div class="table-container">
<div v-if="loading" class="loading-overlay">
<div class="loading-spinner">加载中...</div>
</div>
<table class="data-table">
<thead>
<tr>
<th>订单ID</th>
<th class="sortable">订单编号 ↕️</th>
<th>经销商编码</th>
<th>经销商名称</th>
<th>订单日期</th>
<th>订单金额</th>
<th>出库状态</th>
<th>出库时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="order in orderList" :key="order.orderId">
<td>{{ order.orderId }}</td>
<td>{{ order.orderNo }}</td>
<td>{{ order.dealerCode }}</td>
<td>{{ order.dealerName }}</td>
<td>{{ formatDate(order.orderDate) }}</td>
<td>{{ formatCurrency(order.totalAmount) }}</td>
<td>
<span :class="getDeliveryStatusClass(order.deliveryStatus)">
{{ getDeliveryStatusText(order.deliveryStatus) }}
</span>
</td>
<td>{{ formatTime(order.uploadTime || '') }}</td>
<td class="action-col">
<button @click="handleView(order)" class="view-btn">👁️ 查看</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页信息 -->
<div class="table-footer">
<div class="pagination-left">
<div class="pagination-info">
共 {{ total }} 条记录,第 {{ currentPage }} / {{ totalPages }} 页
</div>
<div class="page-size-selector">
<label>每页显示:</label>
<select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
<option value="10">10条</option>
<option value="20">20条</option>
<option value="50">50条</option>
<option value="100">100条</option>
</select>
</div>
</div>
<div class="pagination-container">
<button
@click="handlePageChange(currentPage - 1)"
:disabled="currentPage <= 1"
class="pagination-btn prev-btn"
>
上一页
</button>
<div class="pagination-pages">
<button
v-for="page in getPageNumbers()"
:key="page"
@click="handlePageChange(page)"
:class="['page-number', { active: page === currentPage }]"
>
{{ page }}
</button>
</div>
<button
@click="handlePageChange(currentPage + 1)"
:disabled="currentPage >= totalPages"
class="pagination-btn next-btn"
>
下一页
</button>
</div>
</div>
</div>
<!-- 订单详情对话框 -->
<div v-if="showDetailDialog" class="dialog-overlay" @click="closeDetailDialog">
<div class="dialog-content large-dialog" @click.stop>
<div class="dialog-header">
<h3>出库详情</h3>
<button @click="closeDetailDialog" class="close-btn">×</button>
</div>
<div class="dialog-body">
<div v-if="orderDetail" class="detail-content">
<div class="detail-section">
<h4>订单基本信息</h4>
<div class="detail-grid">
<div class="detail-item">
<label>订单编号:</label>
<span>{{ orderDetail.orderNo }}</span>
</div>
<div class="detail-item">
<label>经销商编码:</label>
<span>{{ orderDetail.dealerCode }}</span>
</div>
<div class="detail-item">
<label>经销商名称:</label>
<span>{{ orderDetail.dealerName }}</span>
</div>
<div class="detail-item">
<label>订单日期:</label>
<span>{{ formatDate(orderDetail.orderDate) }}</span>
</div>
<div class="detail-item">
<label>订单总金额:</label>
<span>{{ formatCurrency(orderDetail.totalAmount) }}</span>
</div>
<div class="detail-item">
<label>出库状态:</label>
<span :class="getDeliveryStatusClass(orderDetail.deliveryStatus)">
{{ getDeliveryStatusText(orderDetail.deliveryStatus) }}
</span>
</div>
<div class="detail-item">
<label>出库时间:</label>
<span>{{ formatTime(orderDetail.uploadTime || '') }}</span>
</div>
</div>
</div>
<div class="detail-section" v-if="orderDetail.orderItems && orderDetail.orderItems.length > 0">
<h4>出库明细</h4>
<table class="detail-table">
<thead>
<tr>
<th>产品编码</th>
<th>产品名称</th>
<th>产品型号</th>
<th>数量</th>
<th>单价</th>
<th>总价</th>
</tr>
</thead>
<tbody>
<tr v-for="item in orderDetail.orderItems" :key="item.itemId">
<td>{{ item.productCode }}</td>
<td>{{ item.productName }}</td>
<td>{{ item.productModel }}</td>
<td>{{ item.quantity }}</td>
<td>{{ formatCurrency(item.unitPrice) }}</td>
<td>{{ formatCurrency(item.totalPrice) }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- 新增出库对话框 -->
<div v-if="showAddDialog" class="dialog-overlay" @click="closeAddDialog">
<div class="dialog-content large-dialog" @click.stop>
<div class="dialog-header">
<h3>新增出库</h3>
<button @click="closeAddDialog" class="close-btn">×</button>
</div>
<div class="dialog-body">
<form @submit.prevent="handleSubmitAdd">
<!-- 出库基本信息 -->
<div class="form-section">
<h4>出库基本信息</h4>
<div class="form-row">
<div class="form-item">
<label class="required">出库单编号:</label>
<input
v-model="addFormData.deliveryNo"
class="form-input"
placeholder="请输入出库单编号"
required
/>
</div>
<div class="form-item">
<label class="required">经销商编码:</label>
<input
v-model="addFormData.dealerCode"
class="form-input"
placeholder="请输入经销商编码"
required
/>
</div>
</div>
<div class="form-row">
<div class="form-item">
<label class="required">经销商名称:</label>
<input
v-model="addFormData.dealerName"
class="form-input"
placeholder="请输入经销商名称"
required
/>
</div>
<div class="form-item">
<label class="required">关联订单编号:</label>
<input
v-model="addFormData.orderNo"
class="form-input"
placeholder="请输入关联订单编号"
required
/>
</div>
</div>
<div class="form-row">
<div class="form-item">
<label class="required">出库日期:</label>
<input
v-model="addFormData.deliveryDate"
class="form-input"
type="datetime-local"
required
/>
</div>
<div class="form-item">
<label>仓库编码:</label>
<input
v-model="addFormData.warehouseCode"
class="form-input"
placeholder="请输入仓库编码"
/>
</div>
</div>
<div class="form-row">
<div class="form-item">
<label>仓库名称:</label>
<input
v-model="addFormData.warehouseName"
class="form-input"
placeholder="请输入仓库名称"
/>
</div>
<div class="form-item">
<label>数据来源:</label>
<input
v-model="addFormData.dataSource"
class="form-input"
placeholder="请输入数据来源"
/>
</div>
</div>
</div>
<!-- 出库明细 -->
<div class="form-section">
<h4>出库明细</h4>
<div class="order-items-header">
<button type="button" @click="addDeliveryItem" class="add-item-btn">+ 添加明细</button>
</div>
<div class="order-items-container">
<div v-for="(item, index) in addFormData.deliveryItems" :key="index" class="order-item">
<div class="item-row-single">
<div class="form-item">
<label class="required">产品编码:</label>
<input
v-model="item.productCode"
class="form-input"
placeholder="请输入产品编码"
required
/>
</div>
<div class="form-item">
<label class="required">产品名称:</label>
<input
v-model="item.productName"
class="form-input"
placeholder="请输入产品名称"
required
/>
</div>
<div class="form-item">
<label class="required">出库数量:</label>
<input
v-model.number="item.deliveryQty"
class="form-input"
type="number"
placeholder="请输入出库数量"
@input="calculateDeliveryItemTotal(index)"
required
/>
</div>
<div class="form-item">
<label class="required">出库单价:</label>
<input
v-model.number="item.deliveryPrice"
class="form-input"
type="number"
step="0.01"
placeholder="请输入出库单价"
@input="calculateDeliveryItemTotal(index)"
required
/>
</div>
<div class="form-item">
<label>出库金额:</label>
<input
v-model.number="item.deliveryAmount"
class="form-input"
type="number"
step="0.01"
readonly
/>
</div>
<button type="button" @click="removeDeliveryItem(index)" class="remove-item-btn">删除</button>
</div>
</div>
</div>
</div>
<div class="form-actions">
<button type="button" @click="closeAddDialog" class="cancel-btn">取消</button>
<button type="submit" class="submit-btn">确定</button>
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { orderApi, type OrderInfo, type OrderQueryReq } from '../../api/order'
// 响应式数据
const loading = ref(false)
const orderList = ref<OrderInfo[]>([])
const showDetailDialog = ref(false)
const orderDetail = ref<OrderInfo | null>(null)
// 新增出库相关数据
const showAddDialog = ref(false)
const addFormData = reactive({
deliveryNo: '',
dealerCode: '',
dealerName: '',
orderNo: '',
deliveryDate: '',
warehouseCode: '',
warehouseName: '',
dataSource: '',
deliveryItems: [] as any[]
})
// 搜索参数
const searchParams = reactive({
orderNo: '',
dealerCode: '',
dealerName: '',
deliveryStatus: undefined,
startDate: '',
endDate: '',
pageNum: 1,
pageSize: 10
})
// 分页信息
const pagination = reactive({
total: 0,
pageNum: 1,
pageSize: 10
})
// 分页计算属性
const total = computed(() => pagination.total)
const currentPage = computed(() => pagination.pageNum)
const pageSize = computed({
get: () => pagination.pageSize,
set: (value: number) => {
pagination.pageSize = value
}
})
const totalPages = computed(() => Math.ceil(pagination.total / pagination.pageSize))
// 状态相关方法
const getDeliveryStatusText = (status: number) => {
return status === 1 ? '已出库' : '未出库'
}
const getDeliveryStatusClass = (status: number) => {
return status === 1 ? 'status-active' : 'status-inactive'
}
// 格式化方法
const formatDate = (date: string) => {
if (!date) return '-'
return new Date(date).toLocaleDateString()
}
const formatTime = (date: string) => {
if (!date) return '-'
return new Date(date).toLocaleString()
}
const formatCurrency = (amount: number) => {
if (amount === null || amount === undefined) return '-'
return '¥' + amount.toFixed(2)
}
// 消息提示
const showMessage = (message: string, type: 'success' | 'error' | 'warning' = 'success') => {
console.log(`${type}: ${message}`)
alert(message)
}
// 获取页码数组
const getPageNumbers = () => {
const totalPages = Math.ceil(pagination.total / pagination.pageSize)
const currentPage = pagination.pageNum
const maxVisible = 5
const start = Math.max(1, currentPage - Math.floor(maxVisible / 2))
const end = Math.min(totalPages, start + maxVisible - 1)
const pages = []
for (let i = start; i <= end; i++) {
pages.push(i)
}
return pages
}
// 方法
const fetchOrders = async () => {
try {
loading.value = true
const params = { ...searchParams, pageNum: pagination.pageNum, pageSize: pagination.pageSize }
const response = await orderApi.getOrderList(params) as any
console.log('出库查询API响应:', response)
// 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
if (response && response.records) {
orderList.value = response.records || []
pagination.total = response.total || 0
pagination.pageNum = response.current || 1
pagination.pageSize = response.size || 10
} else {
orderList.value = []
pagination.total = 0
showMessage('获取出库列表失败', 'error')
}
} catch (error) {
console.error('获取出库列表失败:', error)
showMessage('获取出库列表失败, 请重试', 'error')
} finally {
loading.value = false
}
}
const handleSearch = () => {
pagination.pageNum = 1
fetchOrders()
}
const handleReset = () => {
Object.assign(searchParams, {
orderNo: '',
dealerCode: '',
dealerName: '',
deliveryStatus: undefined,
startDate: '',
endDate: ''
})
pagination.pageNum = 1
fetchOrders()
}
const handleView = async (order: OrderInfo) => {
try {
const response = await orderApi.getOrderDetail(order.orderId) as any
console.log('出库查询详情API响应:', response)
// 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
if (response && response.orderId) {
orderDetail.value = response
showDetailDialog.value = true
} else {
showMessage('获取订单详情失败', 'error')
}
} catch (error) {
console.error('获取订单详情失败:', error)
showMessage('获取订单详情失败, 请重试', 'error')
}
}
const handleExport = () => {
showMessage('导出功能开发中...', 'warning')
}
// 新增出库相关函数
const handleAdd = () => {
// 重置表单数据
Object.assign(addFormData, {
deliveryNo: '',
dealerCode: '',
dealerName: '',
orderNo: '',
deliveryDate: '',
warehouseCode: '',
warehouseName: '',
dataSource: '',
deliveryItems: []
})
showAddDialog.value = true
}
const closeAddDialog = () => {
showAddDialog.value = false
}
const addDeliveryItem = () => {
addFormData.deliveryItems.push({
productCode: '',
productName: '',
deliveryQty: 1,
deliveryPrice: 0,
deliveryAmount: 0
})
}
const removeDeliveryItem = (index: number) => {
addFormData.deliveryItems.splice(index, 1)
}
const calculateDeliveryItemTotal = (index: number) => {
const item = addFormData.deliveryItems[index]
if (item.deliveryQty && item.deliveryPrice) {
item.deliveryAmount = item.deliveryQty * item.deliveryPrice
}
}
const handleSubmitAdd = async () => {
try {
// 这里应该调用新增出库的API
// await deliveryApi.addDelivery(addFormData)
showMessage('新增出库功能开发中...', 'warning')
closeAddDialog()
// fetchOrders() // 刷新列表
} catch (error) {
console.error('新增出库失败:', error)
showMessage('新增出库失败, 请重试', 'error')
}
}
const handlePageChange = (page: number) => {
if (page >= 1 && page <= totalPages.value) {
pagination.pageNum = page
fetchOrders()
}
}
const handlePageSizeChange = () => {
pagination.pageNum = 1
fetchOrders()
}
const handleTableSearch = () => {
handleSearch()
}
const handleTableRefresh = () => {
fetchOrders()
}
const handleTableExport = () => {
handleExport()
}
const handleTableViewToggle = () => {
showMessage('视图切换功能开发中...', 'warning')
}
const closeDetailDialog = () => {
showDetailDialog.value = false
orderDetail.value = null
}
// 生命周期
onMounted(() => {
fetchOrders()
})
</script>
<style scoped>
/* 复用订单管理页面的样式 */
.delivery-container {
padding: 0px;
background: #f5f5f5;
min-height: 100vh;
}
.search-section {
background: white;
padding: 16px 20px;
border-bottom: 1px solid #e0e0e0;
}
.search-row {
display: flex;
gap: 20px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 12px;
}
.search-item {
display: flex;
align-items: center;
gap: 8px;
}
.search-item label {
font-size: 12px;
color: #333;
white-space: nowrap;
}
.search-input, .search-select {
padding: 6px 8px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 12px;
width: 120px;
}
.search-input:focus, .search-select:focus {
outline: none;
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.search-actions {
display: flex;
gap: 10px;
align-items: center;
}
.search-btn {
background: #1890ff;
color: white;
border: none;
padding: 4px 8px;
border-radius: 3px;
font-size: 11px;
cursor: pointer;
height: 24px;
display: flex;
align-items: center;
gap: 4px;
}
.reset-btn {
background: #ff7875;
color: white;
border: none;
padding: 4px 8px;
border-radius: 3px;
font-size: 11px;
cursor: pointer;
height: 24px;
display: flex;
align-items: center;
gap: 4px;
}
.action-section {
background: white;
padding: 12px 16px;
border-bottom: 1px solid #e0e0e0;
}
.action-buttons {
display: flex;
gap: 6px;
}
.action-btn {
padding: 4px 8px;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 11px;
height: 24px;
display: flex;
align-items: center;
gap: 4px;
}
.action-btn.primary {
background: #007bff;
color: white;
}
.action-btn.secondary {
background: #6c757d;
color: white;
}
.table-section {
background: white;
margin: 16px;
border-radius: 6px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.table-header {
padding: 8px 16px;
background: #fafafa;
border-bottom: 1px solid #e0e0e0;
display: flex;
justify-content: flex-end;
}
.table-controls {
display: flex;
gap: 4px;
}
.control-btn {
width: 24px;
height: 24px;
border: 1px solid #d9d9d9;
background: white;
border-radius: 3px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
}
.table-container {
position: relative;
overflow-x: auto;
}
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255,255,255,0.8);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.loading-spinner {
padding: 20px;
background: white;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
}
.data-table th,
.data-table td {
padding: 8px 6px;
text-align: left;
border-bottom: 1px solid #e0e0e0;
}
.data-table th {
background-color: #fafafa;
font-weight: 500;
color: #333;
font-size: 11px;
}
.data-table tbody tr:hover {
background-color: #f5f5f5;
}
.action-col {
white-space: nowrap;
}
.action-col button {
margin-right: 5px;
padding: 2px 6px;
margin: 0 1px;
border: none;
border-radius: 2px;
cursor: pointer;
font-size: 10px;
height: 20px;
display: inline-flex;
align-items: center;
gap: 2px;
}
.view-btn {
background: #17a2b8;
color: white;
}
.status-btn {
background: #28a745;
color: white;
}
.print-btn {
background: #ffc107;
color: #333;
}
.status-active {
color: #28a745;
font-weight: 500;
}
.status-inactive {
color: #dc3545;
font-weight: 500;
}
.table-footer {
padding: 8px 16px;
border-top: 1px solid #e0e0e0;
display: flex;
justify-content: space-between;
align-items: center;
background: #fafafa;
}
.pagination-left {
display: flex;
align-items: center;
gap: 20px;
}
.pagination-info {
font-size: 12px;
color: #666;
}
.page-size-selector {
display: flex;
align-items: center;
gap: 8px;
}
.page-size-selector label {
font-size: 12px;
color: #666;
}
.page-size-select {
padding: 4px 8px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 12px;
background: white;
cursor: pointer;
transition: border-color 0.3s;
}
.pagination-container {
display: flex;
align-items: center;
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.pagination-btn {
padding: 2px 6px;
border: none;
background: white;
color: #6c757d;
font-size: 11px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
border-right: 1px solid #e9ecef;
height: 20px;
}
.pagination-btn:hover:not(:disabled) {
background: #e9ecef;
color: #495057;
}
.pagination-btn:disabled {
background: #f8f9fa;
color: #adb5bd;
cursor: not-allowed;
}
.pagination-pages {
display: flex;
align-items: center;
}
.page-number {
padding: 2px 6px;
border: none;
background: white;
color: #6c757d;
font-size: 11px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
border-right: 1px solid #e9ecef;
min-width: 28px;
text-align: center;
height: 20px;
}
.page-number:hover {
background: #e9ecef;
color: #495057;
}
.page-number.active {
background: #6c757d;
color: white;
font-weight: 600;
}
.page-number.active:hover {
background: #5a6268;
}
.dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.dialog-content {
background: white;
border-radius: 8px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.dialog-content.large-dialog {
max-width: 1000px;
}
.dialog-header {
padding: 20px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.dialog-header h3 {
margin: 0;
color: #333;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #999;
}
.dialog-body {
padding: 20px;
}
.detail-content {
max-height: 60vh;
overflow-y: auto;
}
.detail-section {
margin-bottom: 30px;
}
.detail-section h4 {
margin: 0 0 15px 0;
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 8px;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 15px;
}
.detail-item {
display: flex;
flex-direction: column;
gap: 5px;
}
.detail-item label {
font-weight: 500;
color: #666;
font-size: 14px;
}
.detail-item span {
color: #333;
font-size: 16px;
}
.detail-table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
.detail-table th,
.detail-table td {
padding: 10px;
text-align: left;
border: 1px solid #ddd;
}
.detail-table th {
background: #f8f9fa;
font-weight: 600;
color: #333;
}
.detail-table tr:nth-child(even) {
background: #f8f9fa;
}
/* 表单样式 */
.form-section {
margin-bottom: 20px;
}
.form-section h4 {
margin: 0 0 15px 0;
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 8px;
}
.form-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.form-item {
flex: 1;
display: flex;
flex-direction: column;
gap: 5px;
}
.form-item label {
font-weight: 500;
color: #333;
}
.form-item label.required::after {
content: ' *';
color: #dc3545;
}
.form-input {
padding: 6px 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 12px;
width: 100%;
box-sizing: border-box;
}
.form-input:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.2);
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.cancel-btn, .submit-btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
}
.cancel-btn {
background: #6c757d;
color: white;
}
.submit-btn {
background: #007bff;
color: white;
}
/* 出库明细样式 */
.order-items-header {
margin-bottom: 10px;
}
.add-item-btn {
padding: 8px 16px;
background: #28a745;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.order-items-container {
border: 1px solid #eee;
border-radius: 4px;
padding: 6px;
background: #f8f9fa;
}
.order-item {
background: white;
border: 1px solid #ddd;
border-radius: 4px;
padding: 6px;
margin-bottom: 6px;
}
.order-item:last-child {
margin-bottom: 0;
}
.item-row-single {
display: flex;
gap: 4px;
align-items: end;
flex-wrap: nowrap;
justify-content: space-between;
}
.order-item .form-input {
width: 100%;
padding: 3px 5px;
font-size: 11px;
box-sizing: border-box;
}
.order-item .form-item {
flex: 0 0 auto;
margin-bottom: 0;
min-width: 0;
}
.order-item .form-item:nth-child(1) {
flex: 0 0 120px;
}
.order-item .form-item:nth-child(2) {
flex: 1 1 200px;
min-width: 150px;
}
.order-item .form-item:nth-child(3) {
flex: 0 0 80px;
}
.order-item .form-item:nth-child(4) {
flex: 0 0 100px;
}
.order-item .form-item:nth-child(5) {
flex: 0 0 100px;
}
.order-item .form-item label {
font-size: 11px;
margin-bottom: 2px;
}
.remove-item-btn {
padding: 4px 8px;
background: #dc3545;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
height: 28px;
align-self: end;
}
</style>
<template>
<div class="invoice-container">
<!-- 搜索区域 -->
<div class="search-section">
<div class="search-row">
<div class="search-item">
<label>订单编号:</label>
<input
v-model="searchParams.orderNo"
class="search-input"
placeholder="请输入订单编号"
/>
</div>
<div class="search-item">
<label>经销商编码:</label>
<input
v-model="searchParams.dealerCode"
class="search-input"
placeholder="请输入经销商编码"
/>
</div>
<div class="search-item">
<label>经销商名称:</label>
<input
v-model="searchParams.dealerName"
class="search-input"
placeholder="请输入经销商名称"
/>
</div>
<div class="search-item">
<label>开票状态:</label>
<select v-model="searchParams.invoiceStatus" class="search-select">
<option value="">所有</option>
<option value="0">未开票</option>
<option value="1">已开票</option>
</select>
</div>
</div>
<div class="search-row">
<div class="search-item">
<label>开始日期:</label>
<input
v-model="searchParams.startDate"
class="search-input"
type="date"
/>
</div>
<div class="search-item">
<label>结束日期:</label>
<input
v-model="searchParams.endDate"
class="search-input"
type="date"
/>
</div>
<div class="search-actions">
<button @click="handleSearch" class="search-btn">🔍 搜索</button>
<button @click="handleReset" class="reset-btn">🔄 重置</button>
</div>
</div>
</div>
<!-- 操作按钮区域 -->
<div class="action-section">
<div class="action-buttons">
<button @click="handleAdd" class="action-btn primary">✨ 新增发票</button>
<button @click="handleExport" class="action-btn secondary">📋 导出</button>
<button @click="handlePrint" class="action-btn secondary">🖨️ 打印</button>
<button @click="handleStatistics" class="action-btn secondary">📊 统计</button>
</div>
</div>
<!-- 数据表格 -->
<div class="table-section">
<div class="table-header">
<div class="table-controls">
<button @click="handleTableSearch" class="control-btn">🔍</button>
<button @click="handleTableRefresh" class="control-btn">🔄</button>
<button @click="handleTableExport" class="control-btn">📋</button>
<button @click="handleTableViewToggle" class="control-btn">⊞</button>
</div>
</div>
<div class="table-container">
<div v-if="loading" class="loading-overlay">
<div class="loading-spinner">加载中...</div>
</div>
<table class="data-table">
<thead>
<tr>
<th>订单ID</th>
<th class="sortable">订单编号 ↕️</th>
<th>经销商编码</th>
<th>经销商名称</th>
<th>订单日期</th>
<th>订单金额</th>
<th>开票状态</th>
<th>开票时间</th>
<th>发票号码</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="order in orderList" :key="order.orderId">
<td>{{ order.orderId }}</td>
<td>{{ order.orderNo }}</td>
<td>{{ order.dealerCode }}</td>
<td>{{ order.dealerName }}</td>
<td>{{ formatDate(order.orderDate) }}</td>
<td>{{ formatCurrency(order.totalAmount) }}</td>
<td>
<span :class="getInvoiceStatusClass(order.invoiceStatus)">
{{ getInvoiceStatusText(order.invoiceStatus) }}
</span>
</td>
<td>{{ formatTime('') }}</td>
<td>{{ '-' }}</td>
<td class="action-col">
<button @click="handleView(order)" class="view-btn">👁️ 查看</button>
<button @click="handleEdit(order)" class="edit-btn">✏️ 编辑</button>
<button @click="handleInvoiceStatus(order)" class="status-btn">
{{ order.invoiceStatus === 0 ? '开票' : '撤销开票' }}
</button>
<button @click="handlePrintInvoice(order)" class="print-btn">🖨️ 打印</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页信息 -->
<div class="table-footer">
<div class="pagination-left">
<div class="pagination-info">
共 {{ total }} 条记录,第 {{ currentPage }} / {{ totalPages }} 页
</div>
<div class="page-size-selector">
<label>每页显示:</label>
<select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
<option value="10">10条</option>
<option value="20">20条</option>
<option value="50">50条</option>
<option value="100">100条</option>
</select>
</div>
</div>
<div class="pagination-container">
<button
@click="handlePageChange(currentPage - 1)"
:disabled="currentPage <= 1"
class="pagination-btn prev-btn"
>
上一页
</button>
<div class="pagination-pages">
<button
v-for="page in getPageNumbers()"
:key="page"
@click="handlePageChange(page)"
:class="['page-number', { active: page === currentPage }]"
>
{{ page }}
</button>
</div>
<button
@click="handlePageChange(currentPage + 1)"
:disabled="currentPage >= totalPages"
class="pagination-btn next-btn"
>
下一页
</button>
</div>
</div>
</div>
<!-- 新增/编辑发票对话框 -->
<div v-if="showDialog" class="dialog-overlay" @click="closeDialog">
<div class="dialog-content" @click.stop>
<div class="dialog-header">
<h3>{{ isEdit ? '编辑发票' : '新增发票' }}</h3>
<button @click="closeDialog" class="close-btn">×</button>
</div>
<div class="dialog-body">
<form @submit.prevent="handleSubmit">
<div class="form-row">
<div class="form-item">
<label class="required">订单编号:</label>
<input
v-model="formData.orderNo"
class="form-input"
placeholder="请输入订单编号"
required
/>
</div>
<div class="form-item">
<label class="required">发票号码:</label>
<input
v-model="formData.invoiceNo"
class="form-input"
placeholder="请输入发票号码"
required
/>
</div>
</div>
<div class="form-row">
<div class="form-item">
<label class="required">开票日期:</label>
<input
v-model="formData.invoiceDate"
class="form-input"
type="datetime-local"
required
/>
</div>
<div class="form-item">
<label class="required">开票金额:</label>
<input
v-model.number="formData.invoiceAmount"
class="form-input"
type="number"
step="0.01"
placeholder="请输入开票金额"
required
/>
</div>
</div>
<div class="form-row">
<div class="form-item">
<label>税率:</label>
<input
v-model.number="formData.taxRate"
class="form-input"
type="number"
step="0.01"
placeholder="请输入税率"
/>
</div>
<div class="form-item">
<label>税额:</label>
<input
v-model.number="formData.taxAmount"
class="form-input"
type="number"
step="0.01"
placeholder="请输入税额"
/>
</div>
</div>
<div class="form-row">
<div class="form-item full-width">
<label>备注:</label>
<textarea
v-model="formData.remark"
class="form-textarea"
placeholder="请输入备注"
rows="3"
></textarea>
</div>
</div>
<div class="form-actions">
<button type="button" @click="closeDialog" class="cancel-btn">取消</button>
<button type="submit" class="submit-btn">确定</button>
</div>
</form>
</div>
</div>
</div>
<!-- 发票详情对话框 -->
<div v-if="showDetailDialog" class="dialog-overlay" @click="closeDetailDialog">
<div class="dialog-content large-dialog" @click.stop>
<div class="dialog-header">
<h3>发票详情</h3>
<button @click="closeDetailDialog" class="close-btn">×</button>
</div>
<div class="dialog-body">
<div v-if="invoiceDetail" class="detail-content">
<div class="detail-section">
<h4>发票基本信息</h4>
<div class="detail-grid">
<div class="detail-item">
<label>订单编号:</label>
<span>{{ invoiceDetail.orderNo }}</span>
</div>
<div class="detail-item">
<label>发票号码:</label>
<span>{{ invoiceDetail.invoiceNo }}</span>
</div>
<div class="detail-item">
<label>开票日期:</label>
<span>{{ formatDate(invoiceDetail.invoiceDate) }}</span>
</div>
<div class="detail-item">
<label>开票金额:</label>
<span>{{ formatCurrency(invoiceDetail.invoiceAmount) }}</span>
</div>
<div class="detail-item">
<label>税率:</label>
<span>{{ invoiceDetail.taxRate ? (invoiceDetail.taxRate * 100).toFixed(2) + '%' : '-' }}</span>
</div>
<div class="detail-item">
<label>税额:</label>
<span>{{ formatCurrency(invoiceDetail.taxAmount) }}</span>
</div>
<div class="detail-item">
<label>开票状态:</label>
<span :class="getInvoiceStatusClass(invoiceDetail.invoiceStatus)">
{{ getInvoiceStatusText(invoiceDetail.invoiceStatus) }}
</span>
</div>
<div class="detail-item">
<label>备注:</label>
<span>{{ invoiceDetail.remark || '-' }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { orderApi, type OrderInfo, type OrderQueryReq } from '../../api/order'
// 响应式数据
const loading = ref(false)
const orderList = ref<OrderInfo[]>([])
const showDialog = ref(false)
const showDetailDialog = ref(false)
const isEdit = ref(false)
const invoiceDetail = ref<any>(null)
// 搜索参数
const searchParams = reactive({
orderNo: '',
dealerCode: '',
dealerName: '',
invoiceStatus: undefined,
startDate: '',
endDate: '',
pageNum: 1,
pageSize: 10
})
// 分页信息
const pagination = reactive({
total: 0,
pageNum: 1,
pageSize: 10
})
// 分页计算属性
const total = computed(() => pagination.total)
const currentPage = computed(() => pagination.pageNum)
const pageSize = computed({
get: () => pagination.pageSize,
set: (value: number) => {
pagination.pageSize = value
}
})
const totalPages = computed(() => Math.ceil(pagination.total / pagination.pageSize))
// 表单数据
const formData = reactive({
orderNo: '',
invoiceNo: '',
invoiceDate: '',
invoiceAmount: 0,
taxRate: 0,
taxAmount: 0,
remark: ''
})
// 状态相关方法
const getInvoiceStatusText = (status: number) => {
return status === 1 ? '已开票' : '未开票'
}
const getInvoiceStatusClass = (status: number) => {
return status === 1 ? 'status-active' : 'status-inactive'
}
// 格式化方法
const formatDate = (date: string) => {
if (!date) return '-'
return new Date(date).toLocaleDateString()
}
const formatTime = (date: string) => {
if (!date) return '-'
return new Date(date).toLocaleString()
}
const formatCurrency = (amount: number) => {
if (amount === null || amount === undefined) return '-'
return '¥' + amount.toFixed(2)
}
// 消息提示
const showMessage = (message: string, type: 'success' | 'error' | 'warning' = 'success') => {
console.log(`${type}: ${message}`)
alert(message)
}
// 获取页码数组
const getPageNumbers = () => {
const totalPages = Math.ceil(pagination.total / pagination.pageSize)
const currentPage = pagination.pageNum
const maxVisible = 5
const start = Math.max(1, currentPage - Math.floor(maxVisible / 2))
const end = Math.min(totalPages, start + maxVisible - 1)
const pages = []
for (let i = start; i <= end; i++) {
pages.push(i)
}
return pages
}
// 方法
const fetchOrders = async () => {
try {
loading.value = true
const params = { ...searchParams, pageNum: pagination.pageNum, pageSize: pagination.pageSize }
const response = await orderApi.getOrderList(params) as any
console.log('发票管理API响应:', response)
// 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
if (response && response.records) {
orderList.value = response.records || []
pagination.total = response.total || 0
pagination.pageNum = response.current || 1
pagination.pageSize = response.size || 10
} else {
orderList.value = []
pagination.total = 0
showMessage('获取发票列表失败', 'error')
}
} catch (error) {
console.error('获取发票列表失败:', error)
showMessage('获取发票列表失败, 请重试', 'error')
} finally {
loading.value = false
}
}
const handleSearch = () => {
pagination.pageNum = 1
fetchOrders()
}
const handleReset = () => {
Object.assign(searchParams, {
orderNo: '',
dealerCode: '',
dealerName: '',
invoiceStatus: undefined,
startDate: '',
endDate: ''
})
pagination.pageNum = 1
fetchOrders()
}
const handleAdd = () => {
isEdit.value = false
Object.assign(formData, {
orderNo: '',
invoiceNo: '',
invoiceDate: '',
invoiceAmount: 0,
taxRate: 0,
taxAmount: 0,
remark: ''
})
showDialog.value = true
}
const handleEdit = (order: OrderInfo) => {
isEdit.value = true
Object.assign(formData, {
orderNo: order.orderNo,
invoiceNo: (order as any).invoiceNo || '',
invoiceDate: (order as any).invoiceTime || '',
invoiceAmount: order.totalAmount,
taxRate: 0,
taxAmount: 0,
remark: ''
})
showDialog.value = true
}
const handleView = async (order: OrderInfo) => {
try {
const response = await orderApi.getOrderDetail(order.orderId) as any
console.log('发票管理详情API响应:', response)
// 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
if (response && response.orderId) {
invoiceDetail.value = response
showDetailDialog.value = true
} else {
showMessage('获取发票详情失败', 'error')
}
} catch (error) {
console.error('获取发票详情失败:', error)
showMessage('获取发票详情失败, 请重试', 'error')
}
}
const handleInvoiceStatus = async (order: OrderInfo) => {
const newStatus = order.invoiceStatus === 0 ? 1 : 0
const action = newStatus === 1 ? '开票' : '撤销开票'
if (confirm(`确定要${action}订单"${order.orderNo}"吗?`)) {
try {
await orderApi.updateInvoiceStatus(order.orderId, newStatus)
showMessage(`${action}成功`, 'success')
fetchOrders()
} catch (error) {
console.error(`${action}失败:`, error)
showMessage(`${action}失败, 请重试`, 'error')
}
}
}
const handleExport = () => {
showMessage('导出功能开发中...', 'warning')
}
const handlePrint = () => {
showMessage('打印功能开发中...', 'warning')
}
const handleStatistics = () => {
showMessage('统计功能开发中...', 'warning')
}
const handlePrintInvoice = (order: OrderInfo) => {
showMessage(`打印发票 ${order.orderNo} 功能开发中...`, 'warning')
}
const handleSubmit = async () => {
try {
showMessage('发票保存功能开发中...', 'warning')
closeDialog()
} catch (error) {
console.error('保存发票失败:', error)
showMessage('保存发票失败, 请重试', 'error')
}
}
const handlePageChange = (page: number) => {
if (page >= 1 && page <= totalPages.value) {
pagination.pageNum = page
fetchOrders()
}
}
const handlePageSizeChange = () => {
pagination.pageNum = 1
fetchOrders()
}
const handleTableSearch = () => {
handleSearch()
}
const handleTableRefresh = () => {
fetchOrders()
}
const handleTableExport = () => {
handleExport()
}
const handleTableViewToggle = () => {
showMessage('视图切换功能开发中...', 'warning')
}
const closeDialog = () => {
showDialog.value = false
}
const closeDetailDialog = () => {
showDetailDialog.value = false
invoiceDetail.value = null
}
// 生命周期
onMounted(() => {
fetchOrders()
})
</script>
<style scoped>
/* 复用订单管理页面的样式 */
.invoice-container {
padding: 0px;
background: #f5f5f5;
min-height: 100vh;
}
.search-section {
background: white;
padding: 16px 20px;
border-bottom: 1px solid #e0e0e0;
}
.search-row {
display: flex;
gap: 20px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 12px;
}
.search-item {
display: flex;
align-items: center;
gap: 8px;
}
.search-item label {
font-size: 12px;
color: #333;
white-space: nowrap;
}
.search-input, .search-select {
padding: 6px 8px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 12px;
width: 120px;
}
.search-input:focus, .search-select:focus {
outline: none;
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.search-actions {
display: flex;
gap: 10px;
align-items: center;
}
.search-btn {
background: #1890ff;
color: white;
border: none;
padding: 4px 8px;
border-radius: 3px;
font-size: 11px;
cursor: pointer;
height: 24px;
display: flex;
align-items: center;
gap: 4px;
}
.reset-btn {
background: #ff7875;
color: white;
border: none;
padding: 4px 8px;
border-radius: 3px;
font-size: 11px;
cursor: pointer;
height: 24px;
display: flex;
align-items: center;
gap: 4px;
}
.action-section {
background: white;
padding: 12px 16px;
border-bottom: 1px solid #e0e0e0;
}
.action-buttons {
display: flex;
gap: 6px;
}
.action-btn {
padding: 4px 8px;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 11px;
height: 24px;
display: flex;
align-items: center;
gap: 4px;
}
.action-btn.primary {
background: #28a745;
color: white;
}
.action-btn.secondary {
background: #6c757d;
color: white;
}
.table-section {
background: white;
margin: 16px;
border-radius: 6px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.table-header {
padding: 8px 16px;
background: #fafafa;
border-bottom: 1px solid #e0e0e0;
display: flex;
justify-content: flex-end;
}
.table-controls {
display: flex;
gap: 4px;
}
.control-btn {
width: 24px;
height: 24px;
border: 1px solid #d9d9d9;
background: white;
border-radius: 3px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
}
.table-container {
position: relative;
overflow-x: auto;
}
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255,255,255,0.8);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.loading-spinner {
padding: 20px;
background: white;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
}
.data-table th,
.data-table td {
padding: 8px 6px;
text-align: left;
border-bottom: 1px solid #e0e0e0;
}
.data-table th {
background-color: #fafafa;
font-weight: 500;
color: #333;
font-size: 11px;
}
.data-table tbody tr:hover {
background-color: #f5f5f5;
}
.action-col {
white-space: nowrap;
}
.action-col button {
margin-right: 5px;
padding: 2px 6px;
margin: 0 1px;
border: none;
border-radius: 2px;
cursor: pointer;
font-size: 10px;
height: 20px;
display: inline-flex;
align-items: center;
gap: 2px;
}
.view-btn {
background: #17a2b8;
color: white;
}
.edit-btn {
background: #ffc107;
color: #333;
}
.status-btn {
background: #28a745;
color: white;
}
.print-btn {
background: #6c757d;
color: white;
}
.status-active {
color: #28a745;
font-weight: 500;
}
.status-inactive {
color: #dc3545;
font-weight: 500;
}
.table-footer {
padding: 8px 16px;
border-top: 1px solid #e0e0e0;
display: flex;
justify-content: space-between;
align-items: center;
background: #fafafa;
}
.pagination-left {
display: flex;
align-items: center;
gap: 20px;
}
.pagination-info {
font-size: 12px;
color: #666;
}
.page-size-selector {
display: flex;
align-items: center;
gap: 8px;
}
.page-size-selector label {
font-size: 12px;
color: #666;
}
.page-size-select {
padding: 4px 8px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 12px;
background: white;
cursor: pointer;
transition: border-color 0.3s;
}
.pagination-container {
display: flex;
align-items: center;
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.pagination-btn {
padding: 2px 6px;
border: none;
background: white;
color: #6c757d;
font-size: 11px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
border-right: 1px solid #e9ecef;
height: 20px;
}
.pagination-btn:hover:not(:disabled) {
background: #e9ecef;
color: #495057;
}
.pagination-btn:disabled {
background: #f8f9fa;
color: #adb5bd;
cursor: not-allowed;
}
.pagination-pages {
display: flex;
align-items: center;
}
.page-number {
padding: 2px 6px;
border: none;
background: white;
color: #6c757d;
font-size: 11px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
border-right: 1px solid #e9ecef;
min-width: 28px;
text-align: center;
height: 20px;
}
.page-number:hover {
background: #e9ecef;
color: #495057;
}
.page-number.active {
background: #6c757d;
color: white;
font-weight: 600;
}
.page-number.active:hover {
background: #5a6268;
}
.dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.dialog-content {
background: white;
border-radius: 8px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.dialog-content.large-dialog {
max-width: 1000px;
}
.dialog-header {
padding: 20px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.dialog-header h3 {
margin: 0;
color: #333;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #999;
}
.dialog-body {
padding: 20px;
}
.form-row {
display: flex;
gap: 20px;
margin-bottom: 15px;
}
.form-item {
flex: 1;
display: flex;
flex-direction: column;
gap: 5px;
}
.form-item.full-width {
flex: 100%;
}
.form-item label {
font-weight: 500;
color: #333;
}
.form-item label.required::after {
content: ' *';
color: #dc3545;
}
.form-input, .form-select, .form-textarea {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.form-textarea {
resize: vertical;
min-height: 80px;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.cancel-btn, .submit-btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
}
.cancel-btn {
background: #6c757d;
color: white;
}
.submit-btn {
background: #007bff;
color: white;
}
.detail-content {
max-height: 60vh;
overflow-y: auto;
}
.detail-section {
margin-bottom: 30px;
}
.detail-section h4 {
margin: 0 0 15px 0;
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 8px;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 15px;
}
.detail-item {
display: flex;
flex-direction: column;
gap: 5px;
}
.detail-item label {
font-weight: 500;
color: #666;
font-size: 14px;
}
.detail-item span {
color: #333;
font-size: 16px;
}
</style>
<template>
<div class="order-container">
<!-- 搜索区域 -->
<div class="search-section">
<div class="search-row">
<div class="search-item">
<label>订单编号:</label>
<input
v-model="searchParams.orderNo"
class="search-input"
placeholder="请输入订单编号"
/>
</div>
<div class="search-item">
<label>经销商编码:</label>
<input
v-model="searchParams.dealerCode"
class="search-input"
placeholder="请输入经销商编码"
/>
</div>
<div class="search-item">
<label>经销商名称:</label>
<input
v-model="searchParams.dealerName"
class="search-input"
placeholder="请输入经销商名称"
/>
</div>
<div class="search-item">
<label>出库状态:</label>
<select v-model="searchParams.deliveryStatus" class="search-select">
<option value="">所有</option>
<option value="0">未出库</option>
<option value="1">已出库</option>
</select>
</div>
</div>
<div class="search-row">
<div class="search-item">
<label>开票状态:</label>
<select v-model="searchParams.invoiceStatus" class="search-select">
<option value="">所有</option>
<option value="0">未开票</option>
<option value="1">已开票</option>
</select>
</div>
<div class="search-item">
<label>返利计算状态:</label>
<select v-model="searchParams.rebateCalcFlag" class="search-select">
<option value="">所有</option>
<option value="0">未计算</option>
<option value="1">已计算</option>
</select>
</div>
<div class="search-item">
<label>数据验证状态:</label>
<select v-model="searchParams.verifyStatus" class="search-select">
<option value="">所有</option>
<option value="0">待验证</option>
<option value="1">验证通过</option>
<option value="2">验证失败</option>
</select>
</div>
<div class="search-actions">
<button @click="handleSearch" class="search-btn">🔍 搜索</button>
<button @click="handleReset" class="reset-btn">🔄 重置</button>
</div>
</div>
</div>
<!-- 操作按钮区域 -->
<div class="action-section">
<div class="action-buttons">
<button @click="handleAdd" class="action-btn primary">✨ 新增</button>
<button @click="handleBatchDelete" class="action-btn danger">🗑️ 删除</button>
<button @click="handleExport" class="action-btn secondary">📋 导出</button>
</div>
</div>
<!-- 数据表格 -->
<div class="table-section">
<div class="table-header">
<div class="table-controls">
<button @click="handleTableSearch" class="control-btn">🔍</button>
<button @click="handleTableRefresh" class="control-btn">🔄</button>
<button @click="handleTableExport" class="control-btn">📋</button>
<button @click="handleTableViewToggle" class="control-btn">⊞</button>
</div>
</div>
<div class="table-container">
<div v-if="loading" class="loading-overlay">
<div class="loading-spinner">加载中...</div>
</div>
<table class="data-table">
<thead>
<tr>
<th>
<input
type="checkbox"
class="select-all"
v-model="selectAll"
@change="handleSelectAll"
/>
</th>
<th>订单ID</th>
<th class="sortable">订单编号 ↕️</th>
<th>经销商编码</th>
<th>经销商名称</th>
<th>订单日期</th>
<th>订单金额</th>
<th>返利金额</th>
<th>出库状态</th>
<th>开票状态</th>
<th>返利计算</th>
<th>验证状态</th>
<th class="sortable">创建时间 ↕️</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="order in orderList" :key="order.orderId">
<td>
<input
type="checkbox"
class="row-checkbox"
:value="order.orderId"
v-model="selectedOrders"
/>
</td>
<td>{{ order.orderId }}</td>
<td>{{ order.orderNo }}</td>
<td>{{ order.dealerCode }}</td>
<td>{{ order.dealerName }}</td>
<td>{{ formatDate(order.orderDate) }}</td>
<td>{{ formatCurrency(order.totalAmount) }}</td>
<td>{{ formatCurrency(order.rebateAmount || 0) }}</td>
<td>
<span :class="getDeliveryStatusClass(order.deliveryStatus)">
{{ getDeliveryStatusText(order.deliveryStatus) }}
</span>
</td>
<td>
<span :class="getInvoiceStatusClass(order.invoiceStatus)">
{{ getInvoiceStatusText(order.invoiceStatus) }}
</span>
</td>
<td>
<span :class="getRebateCalcClass(order.rebateCalcFlag)">
{{ getRebateCalcText(order.rebateCalcFlag) }}
</span>
</td>
<td>
<span :class="getVerifyStatusClass(order.verifyStatus)">
{{ getVerifyStatusText(order.verifyStatus) }}
</span>
</td>
<td>{{ formatTime(order.createTime || '') }}</td>
<td class="action-col">
<button @click="handleView(order)" class="view-btn">👁️ 查看</button>
<button @click="handleEdit(order)" class="edit-btn">✏️ 编辑</button>
<button @click="handleDelete(order)" class="delete-btn">🗑️ 删除</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页信息 -->
<div class="table-footer">
<div class="pagination-left">
<div class="pagination-info">
共 {{ total }} 条记录,第 {{ currentPage }} / {{ totalPages }} 页
</div>
<div class="page-size-selector">
<label>每页显示:</label>
<select v-model="pageSize" @change="handlePageSizeChange" class="page-size-select">
<option value="10">10条</option>
<option value="20">20条</option>
<option value="50">50条</option>
<option value="100">100条</option>
</select>
</div>
</div>
<div class="pagination-container">
<button
@click="handlePageChange(currentPage - 1)"
:disabled="currentPage <= 1"
class="pagination-btn prev-btn"
>
上一页
</button>
<div class="pagination-pages">
<button
v-for="page in getPageNumbers()"
:key="page"
@click="handlePageChange(page)"
:class="['page-number', { active: page === currentPage }]"
>
{{ page }}
</button>
</div>
<button
@click="handlePageChange(currentPage + 1)"
:disabled="currentPage >= totalPages"
class="pagination-btn next-btn"
>
下一页
</button>
</div>
</div>
</div>
<!-- 新增/编辑对话框 -->
<div v-if="showDialog" class="dialog-overlay" @click="closeDialog">
<div class="dialog-content large-dialog" @click.stop>
<div class="dialog-header">
<h3>{{ isEdit ? '编辑订单' : '新增订单' }}</h3>
<button @click="closeDialog" class="close-btn">×</button>
</div>
<div class="dialog-body">
<form @submit.prevent="handleSubmit">
<!-- 订单基本信息 -->
<div class="form-section">
<h4>订单基本信息</h4>
<div class="form-row">
<div class="form-item">
<label class="required">订单编号:</label>
<input
v-model="formData.orderNo"
class="form-input"
placeholder="请输入订单编号"
required
/>
</div>
<div class="form-item">
<label class="required">经销商编码:</label>
<input
v-model="formData.dealerCode"
class="form-input"
placeholder="请输入经销商编码"
required
/>
</div>
</div>
<div class="form-row">
<div class="form-item">
<label class="required">经销商名称:</label>
<input
v-model="formData.dealerName"
class="form-input"
placeholder="请输入经销商名称"
required
/>
</div>
<div class="form-item">
<label class="required">订单日期:</label>
<input
v-model="formData.orderDate"
class="form-input"
type="datetime-local"
required
/>
</div>
</div>
<div class="form-row">
<div class="form-item">
<label class="required">订单总金额:</label>
<input
v-model.number="formData.totalAmount"
class="form-input"
type="number"
step="0.01"
placeholder="请输入订单总金额"
required
/>
</div>
<div class="form-item">
<label>返利金额:</label>
<input
v-model.number="formData.rebateAmount"
class="form-input"
type="number"
step="0.01"
placeholder="请输入返利金额"
/>
</div>
</div>
</div>
<!-- 订单明细 -->
<div class="form-section">
<h4>订单明细</h4>
<div class="order-items-header">
<button type="button" @click="addOrderItem" class="add-item-btn">+ 添加明细</button>
</div>
<div class="order-items-container">
<div v-for="(item, index) in formData.orderItems" :key="index" class="order-item">
<div class="item-row-single">
<div class="form-item">
<label class="required">产品编码:</label>
<input
v-model="item.productCode"
class="form-input"
placeholder="请输入产品编码"
required
/>
</div>
<div class="form-item">
<label class="required">产品名称:</label>
<input
v-model="item.productName"
class="form-input"
placeholder="请输入产品名称"
required
/>
</div>
<div class="form-item">
<label class="required">商品数量:</label>
<input
v-model.number="item.productQty"
class="form-input"
type="number"
placeholder="请输入商品数量"
@input="calculateItemTotal(index)"
required
/>
</div>
<div class="form-item">
<label class="required">单价:</label>
<input
v-model.number="item.unitPrice"
class="form-input"
type="number"
step="0.01"
placeholder="请输入单价"
@input="calculateItemTotal(index)"
required
/>
</div>
<div class="form-item">
<label>明细金额:</label>
<input
v-model.number="item.itemAmount"
class="form-input"
type="number"
step="0.01"
readonly
/>
</div>
<button type="button" @click="removeOrderItem(index)" class="remove-item-btn">删除</button>
</div>
</div>
</div>
</div>
<div class="form-actions">
<button type="button" @click="closeDialog" class="cancel-btn">取消</button>
<button type="submit" class="submit-btn">确定</button>
</div>
</form>
</div>
</div>
</div>
<!-- 订单详情对话框 -->
<div v-if="showDetailDialog" class="dialog-overlay" @click="closeDetailDialog">
<div class="dialog-content large-dialog" @click.stop>
<div class="dialog-header">
<h3>订单详情</h3>
<button @click="closeDetailDialog" class="close-btn">×</button>
</div>
<div class="dialog-body">
<div v-if="orderDetail" class="detail-content">
<div class="detail-section">
<h4>订单基本信息</h4>
<div class="detail-grid">
<div class="detail-item">
<label>订单编号:</label>
<span>{{ orderDetail.orderNo }}</span>
</div>
<div class="detail-item">
<label>经销商编码:</label>
<span>{{ orderDetail.dealerCode }}</span>
</div>
<div class="detail-item">
<label>经销商名称:</label>
<span>{{ orderDetail.dealerName }}</span>
</div>
<div class="detail-item">
<label>订单日期:</label>
<span>{{ formatDate(orderDetail.orderDate) }}</span>
</div>
<div class="detail-item">
<label>订单总金额:</label>
<span>{{ formatCurrency(orderDetail.totalAmount) }}</span>
</div>
<div class="detail-item">
<label>返利金额:</label>
<span>{{ formatCurrency(orderDetail.rebateAmount || 0) }}</span>
</div>
<div class="detail-item">
<label>出库状态:</label>
<span :class="getDeliveryStatusClass(orderDetail.deliveryStatus)">
{{ getDeliveryStatusText(orderDetail.deliveryStatus) }}
</span>
</div>
<div class="detail-item">
<label>开票状态:</label>
<span :class="getInvoiceStatusClass(orderDetail.invoiceStatus)">
{{ getInvoiceStatusText(orderDetail.invoiceStatus) }}
</span>
</div>
</div>
</div>
<div class="detail-section" v-if="orderDetail.orderItems && orderDetail.orderItems.length > 0">
<h4>订单明细</h4>
<table class="detail-table">
<thead>
<tr>
<th>产品编码</th>
<th>产品名称</th>
<th>商品数量</th>
<th>单价</th>
<th>明细金额</th>
</tr>
</thead>
<tbody>
<tr v-for="item in orderDetail.orderItems" :key="item.itemId">
<td>{{ item.productCode }}</td>
<td>{{ item.productName }}</td>
<td>{{ item.productQty }}</td>
<td>{{ formatCurrency(item.unitPrice) }}</td>
<td>{{ formatCurrency(item.itemAmount) }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { orderApi, type OrderInfo, type OrderQueryReq, type OrderAddReq, type OrderUpdateReq, type OrderItemAddReq } from '../../api/order'
// 响应式数据
const loading = ref(false)
const orderList = ref<OrderInfo[]>([])
const selectedOrders = ref<number[]>([])
const showDialog = ref(false)
const showDetailDialog = ref(false)
const isEdit = ref(false)
const orderDetail = ref<OrderInfo | null>(null)
// 搜索参数
const searchParams = reactive<OrderQueryReq>({
orderNo: '',
dealerCode: '',
dealerName: '',
deliveryStatus: undefined,
invoiceStatus: undefined,
rebateCalcFlag: undefined,
verifyStatus: undefined,
pageNum: 1,
pageSize: 10
})
// 分页信息
const pagination = reactive({
total: 0,
pageNum: 1,
pageSize: 10
})
// 分页计算属性
const total = computed(() => pagination.total)
const currentPage = computed(() => pagination.pageNum)
const pageSize = computed({
get: () => pagination.pageSize,
set: (value: number) => {
pagination.pageSize = value
}
})
const totalPages = computed(() => Math.ceil(pagination.total / pagination.pageSize))
// 表单数据
const formData = reactive<OrderAddReq & { orderId?: number }>({
orderNo: '',
dealerCode: '',
dealerName: '',
orderDate: '',
totalAmount: 0,
rebateAmount: 0,
deliveryStatus: 0,
invoiceStatus: 0,
rebateCalcFlag: 0,
dataSource: '',
verifyStatus: 0,
orderItems: []
})
// 计算属性
const selectAll = computed({
get: () => selectedOrders.value.length === orderList.value.length && orderList.value.length > 0,
set: (value: boolean) => {
if (value) {
selectedOrders.value = orderList.value.map(order => order.orderId)
} else {
selectedOrders.value = []
}
}
})
// 获取页码数组
const getPageNumbers = () => {
const totalPages = Math.ceil(pagination.total / pagination.pageSize)
const currentPage = pagination.pageNum
const maxVisible = 5
const start = Math.max(1, currentPage - Math.floor(maxVisible / 2))
const end = Math.min(totalPages, start + maxVisible - 1)
const pages = []
for (let i = start; i <= end; i++) {
pages.push(i)
}
return pages
}
// 状态相关方法
const getDeliveryStatusText = (status: number) => {
return status === 1 ? '已出库' : '未出库'
}
const getDeliveryStatusClass = (status: number) => {
return status === 1 ? 'status-active' : 'status-inactive'
}
const getInvoiceStatusText = (status: number) => {
return status === 1 ? '已开票' : '未开票'
}
const getInvoiceStatusClass = (status: number) => {
return status === 1 ? 'status-active' : 'status-inactive'
}
const getRebateCalcText = (status: number) => {
return status === 1 ? '已计算' : '未计算'
}
const getRebateCalcClass = (status: number) => {
return status === 1 ? 'status-active' : 'status-inactive'
}
const getVerifyStatusText = (status: number) => {
const statusMap = { 0: '待验证', 1: '验证通过', 2: '验证失败' }
return statusMap[status as keyof typeof statusMap] || '未知'
}
const getVerifyStatusClass = (status: number) => {
const classMap = { 0: 'status-pending', 1: 'status-active', 2: 'status-inactive' }
return classMap[status as keyof typeof classMap] || 'status-inactive'
}
// 格式化方法
const formatDate = (date: string) => {
if (!date) return '-'
return new Date(date).toLocaleDateString()
}
const formatTime = (date: string) => {
if (!date) return '-'
return new Date(date).toLocaleString()
}
const formatCurrency = (amount: number) => {
if (amount === null || amount === undefined) return '-'
return '¥' + amount.toFixed(2)
}
// 消息提示
const showMessage = (message: string, type: 'success' | 'error' | 'warning' = 'success') => {
// 这里可以集成消息提示组件
console.log(`${type}: ${message}`)
alert(message)
}
// 方法
const fetchOrders = async () => {
try {
loading.value = true
const params = { ...searchParams, pageNum: pagination.pageNum, pageSize: pagination.pageSize }
const response = await orderApi.getOrderList(params) as any
console.log('API响应:', response) // 添加调试日志
// 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
// 所以response就是data部分,包含records、total等字段
if (response && response.records) {
orderList.value = response.records || []
pagination.total = response.total || 0
pagination.pageNum = response.current || 1
pagination.pageSize = response.size || 10
console.log('数据加载成功:', orderList.value.length, '条记录')
} else {
orderList.value = []
pagination.total = 0
console.error('API响应错误:', response)
showMessage('获取订单列表失败', 'error')
}
} catch (error) {
console.error('获取订单列表失败:', error)
showMessage('获取订单列表失败, 请重试', 'error')
} finally {
loading.value = false
}
}
const handleSearch = () => {
pagination.pageNum = 1
fetchOrders()
}
const handleReset = () => {
Object.assign(searchParams, {
orderNo: '',
dealerCode: '',
dealerName: '',
deliveryStatus: undefined,
invoiceStatus: undefined,
rebateCalcFlag: undefined,
verifyStatus: undefined
})
pagination.pageNum = 1
fetchOrders()
}
const handleAdd = () => {
isEdit.value = false
Object.assign(formData, {
orderNo: '',
dealerCode: '',
dealerName: '',
orderDate: '',
totalAmount: 0,
rebateAmount: 0,
deliveryStatus: 0,
invoiceStatus: 0,
rebateCalcFlag: 0,
dataSource: '',
verifyStatus: 0,
orderItems: []
})
showDialog.value = true
}
const handleEdit = async (order: OrderInfo) => {
try {
// 调用后端接口获取完整的订单详情(包括订单明细)
const response = await orderApi.getOrderDetail(order.orderId) as any
console.log('编辑订单API响应:', response)
if (response && response.orderId) {
isEdit.value = true
Object.assign(formData, {
orderId: response.orderId,
orderNo: response.orderNo,
dealerCode: response.dealerCode,
dealerName: response.dealerName,
orderDate: response.orderDate,
totalAmount: response.totalAmount,
rebateAmount: response.rebateAmount || 0,
deliveryStatus: response.deliveryStatus,
invoiceStatus: response.invoiceStatus,
rebateCalcFlag: response.rebateCalcFlag,
dataSource: response.dataSource || '',
verifyStatus: response.verifyStatus,
orderItems: response.orderItems || []
})
showDialog.value = true
} else {
showMessage('获取订单详情失败', 'error')
}
} catch (error) {
console.error('获取订单详情失败:', error)
showMessage('获取订单详情失败, 请重试', 'error')
}
}
const handleView = async (order: OrderInfo) => {
try {
const response = await orderApi.getOrderDetail(order.orderId) as any
console.log('订单详情API响应:', response)
// 由于request.ts的响应拦截器已经处理了code=200的情况,直接返回data
if (response && response.orderId) {
orderDetail.value = response
showDetailDialog.value = true
} else {
showMessage('获取订单详情失败', 'error')
}
} catch (error) {
console.error('获取订单详情失败:', error)
showMessage('获取订单详情失败, 请重试', 'error')
}
}
const handleDelete = async (order: OrderInfo) => {
if (confirm(`确定要删除订单"${order.orderNo}"吗?`)) {
try {
await orderApi.deleteOrder(order.orderId)
showMessage('删除成功', 'success')
fetchOrders()
} catch (error) {
console.error('删除订单失败:', error)
showMessage('删除订单失败, 请重试', 'error')
}
}
}
const handleBatchDelete = async () => {
if (selectedOrders.value.length === 0) {
showMessage('请选择要删除的订单', 'warning')
return
}
if (confirm(`确定要删除选中的 ${selectedOrders.value.length} 个订单吗?`)) {
try {
await orderApi.batchDeleteOrders(selectedOrders.value)
showMessage('批量删除成功', 'success')
selectedOrders.value = []
fetchOrders()
} catch (error) {
console.error('批量删除订单失败:', error)
showMessage('批量删除订单失败, 请重试', 'error')
}
}
}
const handleExport = () => {
showMessage('导出功能开发中...', 'warning')
}
const handleSelectAll = () => {
// 已在计算属性中处理
}
const handlePageChange = (page: number) => {
if (page >= 1 && page <= totalPages.value) {
pagination.pageNum = page
fetchOrders()
}
}
const handlePageSizeChange = () => {
pagination.pageNum = 1
fetchOrders()
}
const handleTableSearch = () => {
handleSearch()
}
const handleTableRefresh = () => {
fetchOrders()
}
const handleTableExport = () => {
handleExport()
}
const handleTableViewToggle = () => {
showMessage('视图切换功能开发中...', 'warning')
}
// 订单明细相关方法
const addOrderItem = () => {
formData.orderItems.push({
productCode: '',
productName: '',
productQty: 1,
unitPrice: 0,
itemAmount: 0
})
}
const removeOrderItem = (index: number) => {
formData.orderItems.splice(index, 1)
}
const calculateItemTotal = (index: number) => {
const item = formData.orderItems[index]
if (item.productQty && item.unitPrice) {
item.itemAmount = item.productQty * item.unitPrice
}
}
const handleSubmit = async () => {
try {
if (isEdit.value) {
const updateData: OrderUpdateReq = {
orderId: formData.orderId!,
orderNo: formData.orderNo,
dealerCode: formData.dealerCode,
dealerName: formData.dealerName,
orderDate: formData.orderDate,
totalAmount: formData.totalAmount,
rebateAmount: formData.rebateAmount,
deliveryStatus: formData.deliveryStatus,
invoiceStatus: formData.invoiceStatus,
rebateCalcFlag: formData.rebateCalcFlag,
dataSource: formData.dataSource,
verifyStatus: formData.verifyStatus,
orderItems: formData.orderItems.map(item => ({
itemId: (item as any).itemId,
productCode: item.productCode,
productName: item.productName,
productQty: item.productQty,
unitPrice: item.unitPrice,
itemAmount: item.itemAmount
}))
}
await orderApi.updateOrder(updateData)
showMessage('修改订单成功', 'success')
} else {
const addData: OrderAddReq = {
orderNo: formData.orderNo,
dealerCode: formData.dealerCode,
dealerName: formData.dealerName,
orderDate: formData.orderDate,
totalAmount: formData.totalAmount,
rebateAmount: formData.rebateAmount,
deliveryStatus: formData.deliveryStatus,
invoiceStatus: formData.invoiceStatus,
rebateCalcFlag: formData.rebateCalcFlag,
dataSource: formData.dataSource,
verifyStatus: formData.verifyStatus,
orderItems: formData.orderItems.map(item => ({
productCode: item.productCode,
productName: item.productName,
productQty: item.productQty,
unitPrice: item.unitPrice,
itemAmount: item.itemAmount
}))
}
await orderApi.addOrder(addData)
showMessage('新增订单成功', 'success')
}
closeDialog()
fetchOrders()
} catch (error) {
console.error('保存订单失败:', error)
showMessage('保存订单失败, 请重试', 'error')
}
}
const closeDialog = () => {
showDialog.value = false
formData.orderItems = []
}
const closeDetailDialog = () => {
showDetailDialog.value = false
orderDetail.value = null
}
// 生命周期
onMounted(() => {
fetchOrders()
})
</script>
<style scoped>
.order-container {
padding: 0px;
background: #f5f5f5;
min-height: 100vh;
}
/* 搜索区域样式 */
.search-section {
background: white;
padding: 16px 20px;
border-bottom: 1px solid #e0e0e0;
}
.search-row {
display: flex;
gap: 20px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 12px;
}
.search-item {
display: flex;
align-items: center;
gap: 8px;
}
.search-item label {
font-size: 12px;
color: #333;
white-space: nowrap;
}
.search-input, .search-select {
padding: 6px 8px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 12px;
width: 120px;
}
.search-input:focus, .search-select:focus {
outline: none;
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.search-actions {
display: flex;
gap: 10px;
align-items: center;
}
.search-btn {
background: #1890ff;
color: white;
border: none;
padding: 4px 8px;
border-radius: 3px;
font-size: 11px;
cursor: pointer;
height: 24px;
display: flex;
align-items: center;
gap: 4px;
}
.reset-btn {
background: #ff7875;
color: white;
border: none;
padding: 4px 8px;
border-radius: 3px;
font-size: 11px;
cursor: pointer;
height: 24px;
display: flex;
align-items: center;
gap: 4px;
}
/* 操作按钮区域 */
.action-section {
background: white;
padding: 12px 16px;
border-bottom: 1px solid #e0e0e0;
}
.action-buttons {
display: flex;
gap: 6px;
}
.action-btn {
padding: 4px 8px;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 11px;
height: 24px;
display: flex;
align-items: center;
gap: 4px;
}
.action-btn.primary {
background: #28a745;
color: white;
}
.action-btn.danger {
background: #dc3545;
color: white;
}
.action-btn.secondary {
background: #6c757d;
color: white;
}
/* 表格样式 */
.table-section {
background: white;
margin: 16px;
border-radius: 6px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.table-header {
padding: 8px 16px;
background: #fafafa;
border-bottom: 1px solid #e0e0e0;
display: flex;
justify-content: flex-end;
}
.table-controls {
display: flex;
gap: 4px;
}
.control-btn {
width: 24px;
height: 24px;
border: 1px solid #d9d9d9;
background: white;
border-radius: 3px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
}
.table-container {
position: relative;
overflow-x: auto;
}
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255,255,255,0.8);
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.loading-spinner {
padding: 20px;
background: white;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
}
.data-table th,
.data-table td {
padding: 8px 6px;
text-align: left;
border-bottom: 1px solid #e0e0e0;
}
.data-table th {
background-color: #fafafa;
font-weight: 500;
color: #333;
font-size: 11px;
}
.data-table tbody tr:hover {
background-color: #f5f5f5;
}
.select-all, .row-checkbox {
margin: 0;
}
.action-col {
white-space: nowrap;
}
.action-col button {
margin-right: 5px;
padding: 2px 6px;
margin: 0 1px;
border: none;
border-radius: 2px;
cursor: pointer;
font-size: 10px;
height: 20px;
display: inline-flex;
align-items: center;
gap: 2px;
}
.view-btn {
background: #17a2b8;
color: white;
}
.edit-btn {
background: #ffc107;
color: #333;
}
.delete-btn {
background: #dc3545;
color: white;
}
.status-btn {
background: #28a745;
color: white;
}
/* 状态样式 */
.status-active {
color: #28a745;
font-weight: 500;
}
.status-inactive {
color: #dc3545;
font-weight: 500;
}
.status-pending {
color: #ffc107;
font-weight: 500;
}
/* 分页样式 */
.table-footer {
padding: 8px 16px;
border-top: 1px solid #e0e0e0;
display: flex;
justify-content: space-between;
align-items: center;
background: #fafafa;
}
.pagination-left {
display: flex;
align-items: center;
gap: 20px;
}
.pagination-info {
font-size: 12px;
color: #666;
}
.page-size-selector {
display: flex;
align-items: center;
gap: 8px;
}
.page-size-selector label {
font-size: 12px;
color: #666;
}
.page-size-select {
padding: 4px 8px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 12px;
background: white;
cursor: pointer;
transition: border-color 0.3s;
}
.pagination-container {
display: flex;
align-items: center;
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.pagination-btn {
padding: 2px 6px;
border: none;
background: white;
color: #6c757d;
font-size: 11px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
border-right: 1px solid #e9ecef;
height: 20px;
}
.pagination-btn:hover:not(:disabled) {
background: #e9ecef;
color: #495057;
}
.pagination-btn:disabled {
background: #f8f9fa;
color: #adb5bd;
cursor: not-allowed;
}
.pagination-pages {
display: flex;
align-items: center;
}
.page-number {
padding: 2px 6px;
border: none;
background: white;
color: #6c757d;
font-size: 11px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
border-right: 1px solid #e9ecef;
min-width: 28px;
text-align: center;
height: 20px;
}
.page-number:hover {
background: #e9ecef;
color: #495057;
}
.page-number.active {
background: #6c757d;
color: white;
font-weight: 600;
}
.page-number.active:hover {
background: #5a6268;
}
/* 对话框样式 */
.dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.dialog-content {
background: white;
border-radius: 8px;
max-width: 400px;
width: 70%;
max-height: 80vh;
overflow-y: auto;
}
.dialog-content.large-dialog {
max-width: 1000px;
}
.dialog-header {
padding: 20px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.dialog-header h3 {
margin: 0;
color: #333;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #999;
}
.dialog-body {
padding: 20px;
}
/* 表单样式 */
.form-section {
margin-bottom: 30px;
}
.form-section h4 {
margin: 0 0 15px 0;
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 8px;
}
.form-row {
display: flex;
gap: 20px;
margin-bottom: 15px;
}
.form-item {
flex: 1;
display: flex;
flex-direction: column;
gap: 5px;
}
.form-item.full-width {
flex: 100%;
}
.form-item label {
font-weight: 500;
color: #333;
}
.form-item label.required::after {
content: ' *';
color: #dc3545;
}
.form-input, .form-select, .form-textarea {
padding: 6px 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 12px;
width: 200px;
}
/* 订单明细中的输入框样式 - 优化空间利用 */
.order-item .form-input {
width: 100%;
padding: 3px 5px;
font-size: 11px;
box-sizing: border-box;
}
/* 订单明细中的表单项样式 */
.order-item .form-item {
flex: 0 0 auto;
margin-bottom: 0;
min-width: 0;
}
/* 产品编码字段 */
.order-item .form-item:nth-child(1) {
flex: 0 0 120px;
}
/* 产品名称字段 - 占用更多空间 */
.order-item .form-item:nth-child(2) {
flex: 1 1 200px;
min-width: 150px;
}
/* 数量字段 */
.order-item .form-item:nth-child(3) {
flex: 0 0 80px;
}
/* 单价字段 */
.order-item .form-item:nth-child(4) {
flex: 0 0 100px;
}
/* 明细金额字段 */
.order-item .form-item:nth-child(5) {
flex: 0 0 100px;
}
.order-item .form-item label {
font-size: 11px;
margin-bottom: 2px;
}
.form-textarea {
resize: vertical;
min-height: 80px;
}
/* 订单明细样式 */
.order-items-header {
margin-bottom: 10px;
}
.add-item-btn {
padding: 8px 16px;
background: #28a745;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.order-items-container {
border: 1px solid #eee;
border-radius: 4px;
padding: 6px;
background: #f8f9fa;
}
.order-item {
background: white;
border: 1px solid #ddd;
border-radius: 4px;
padding: 6px;
margin-bottom: 6px;
}
.order-item:last-child {
margin-bottom: 0;
}
.item-row {
display: flex;
gap: 8px;
margin-bottom: 6px;
align-items: end;
}
.item-row:last-child {
margin-bottom: 0;
}
/* 单行布局 - 所有字段在一行显示,优化空间利用 */
.item-row-single {
display: flex;
gap: 4px;
align-items: end;
flex-wrap: nowrap;
justify-content: space-between;
}
.remove-item-btn {
padding: 4px 8px;
background: #dc3545;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
height: 28px;
align-self: end;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.cancel-btn, .submit-btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
}
.cancel-btn {
background: #6c757d;
color: white;
}
.submit-btn {
background: #007bff;
color: white;
}
/* 详情样式 */
.detail-content {
max-height: 60vh;
overflow-y: auto;
}
.detail-section {
margin-bottom: 30px;
}
.detail-section h4 {
margin: 0 0 15px 0;
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 8px;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 15px;
}
.detail-item {
display: flex;
flex-direction: column;
gap: 5px;
}
.detail-item label {
font-weight: 500;
color: #666;
font-size: 14px;
}
.detail-item span {
color: #333;
font-size: 16px;
}
.detail-table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
.detail-table th,
.detail-table td {
padding: 10px;
text-align: left;
border: 1px solid #ddd;
}
.detail-table th {
background: #f8f9fa;
font-weight: 600;
color: #333;
}
.detail-table tr:nth-child(even) {
background: #f8f9fa;
}
</style>
......@@ -1026,8 +1026,9 @@ onMounted(() => {
.dialog-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
width: 600px;
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
......@@ -1095,10 +1096,9 @@ onMounted(() => {
.form-input, .form-select, .form-textarea {
padding: 8px 12px;
border: 1px solid #d9d9d9;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
transition: border-color 0.2s;
}
.form-input:focus, .form-select:focus, .form-textarea:focus {
......@@ -1122,25 +1122,20 @@ onMounted(() => {
}
.cancel-btn, .submit-btn {
padding: 8px 16px;
padding: 10px 20px;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
font-weight: 500;
}
.cancel-btn {
background: #f5f5f5;
color: #666;
}
.cancel-btn:hover {
background: #e6e6e6;
background: #6c757d;
color: white;
}
.submit-btn {
background: #1890ff;
background: #007bff;
color: white;
}
......