-
mentioned in commit 56c332ee
Showing
11 changed files
src/pages/order3/SaleTask/api.ts
@@ -25,6 +25,7 @@ export interface GetSaleTaskApiRes { | @@ -25,6 +25,7 @@ export interface GetSaleTaskApiRes { | ||
25 | testDriveTaskCount: number; | 25 | testDriveTaskCount: number; |
26 | seriesTaskCount: number; | 26 | seriesTaskCount: number; |
27 | vehicleGrossProfitTask: number; | 27 | vehicleGrossProfitTask: number; |
28 | + totalGrossProfitTask: number; | ||
28 | } | 29 | } |
29 | 30 | ||
30 | export interface SeriesTaskItem { | 31 | export interface SeriesTaskItem { |
@@ -71,6 +72,8 @@ export interface ShopTaskItem { | @@ -71,6 +72,8 @@ export interface ShopTaskItem { | ||
71 | seriesTaskCount: number; | 72 | seriesTaskCount: number; |
72 | vehicleGrossProfitTask: number; | 73 | vehicleGrossProfitTask: number; |
73 | taskId?: number; | 74 | taskId?: number; |
75 | + orderTaskApplyId?: number; | ||
76 | + orderShopTaskId?: number; | ||
74 | } | 77 | } |
75 | 78 | ||
76 | /** 月度零售任务列表 */ | 79 | /** 月度零售任务列表 */ |
@@ -81,8 +84,8 @@ export function getSaleTaskApi( | @@ -81,8 +84,8 @@ export function getSaleTaskApi( | ||
81 | } | 84 | } |
82 | 85 | ||
83 | export interface GetShopSaleTaskReq { | 86 | export interface GetShopSaleTaskReq { |
84 | - shopId: number; | ||
85 | - taskDate: number; | 87 | + shopId?: number; |
88 | + taskDate?: number; | ||
86 | } | 89 | } |
87 | 90 | ||
88 | /** 门店零售任务详情 */ | 91 | /** 门店零售任务详情 */ |
@@ -109,6 +112,54 @@ export function submitSaleTask(params: { id: number }): PromiseResp<boolean> { | @@ -109,6 +112,54 @@ export function submitSaleTask(params: { id: number }): PromiseResp<boolean> { | ||
109 | return request.post(`${ORDER3_HOST}/erp/sales/task/submit`, params); | 112 | return request.post(`${ORDER3_HOST}/erp/sales/task/submit`, params); |
110 | } | 113 | } |
111 | 114 | ||
115 | +export interface AutoAssignItem { | ||
116 | + shopId: number; | ||
117 | + taskCount: number; | ||
118 | + newEnergyTaskCount: number; | ||
119 | + fuelVehicleTaskCount: number; | ||
120 | + tackCarTaskCount: number; | ||
121 | +} | ||
122 | + | ||
123 | +export interface AutoAssignSaleTaskReq { | ||
124 | + id: number; | ||
125 | + assignTask: boolean; | ||
126 | + shopTaskList: AutoAssignItem[]; | ||
127 | +} | ||
128 | + | ||
129 | +/** 自动分配零售任务 */ | ||
130 | +export function autoAssignSaleTask( | ||
131 | + params: AutoAssignSaleTaskReq | ||
132 | +): PromiseResp<boolean> { | ||
133 | + return request.post(`${ORDER3_HOST}/erp/sales/task/auto/assign`, params); | ||
134 | +} | ||
135 | + | ||
136 | +interface BatchSetSaleTaskItem { | ||
137 | + shopIdList: number[]; | ||
138 | + taskAims: number; | ||
139 | +} | ||
140 | + | ||
141 | +export interface BatchSetSaleTaskReq { | ||
142 | + grossProfitTaskList?: BatchSetSaleTaskItem[]; | ||
143 | + tackCarTaskList?: BatchSetSaleTaskItem[]; | ||
144 | + testDriveTaskList?: BatchSetSaleTaskItem[]; | ||
145 | + assignTask: boolean; | ||
146 | + orderTaskApplyId: number; | ||
147 | +} | ||
148 | + | ||
149 | +/** 批量设置零售任务 */ | ||
150 | +export function batchSetSaleTask( | ||
151 | + params: BatchSetSaleTaskReq | ||
152 | +): PromiseResp<boolean> { | ||
153 | + return request.post(`${ORDER3_HOST}/erp/sales/task/batch/shop/set`, params); | ||
154 | +} | ||
155 | + | ||
156 | +/** 自动分配单个门店的零售任务 */ | ||
157 | +export function autoAssignOneShop( | ||
158 | + params: ShopTaskItem | ||
159 | +): PromiseResp<boolean> { | ||
160 | + return request.post(`${ORDER3_HOST}/erp/sales/task/auto/assign/shop`, params); | ||
161 | +} | ||
162 | + | ||
112 | export interface BrandItem { | 163 | export interface BrandItem { |
113 | id: number; | 164 | id: number; |
114 | initial: string; | 165 | initial: string; |
@@ -148,9 +199,10 @@ export interface PreviewTaskReq { | @@ -148,9 +199,10 @@ export interface PreviewTaskReq { | ||
148 | secondManageId?: number; | 199 | secondManageId?: number; |
149 | thirdManageId?: number; | 200 | thirdManageId?: number; |
150 | taskId?: number; | 201 | taskId?: number; |
151 | - orderTaskApprovalType: number; | ||
152 | - id: number; | 202 | + orderTaskApprovalType?: number; |
203 | + id?: number; | ||
153 | token?: string; | 204 | token?: string; |
205 | + keywords?: string; | ||
154 | } | 206 | } |
155 | 207 | ||
156 | export interface TaskListItem { | 208 | export interface TaskListItem { |
@@ -188,7 +240,9 @@ export interface PreviewTaskRes { | @@ -188,7 +240,9 @@ export interface PreviewTaskRes { | ||
188 | } | 240 | } |
189 | 241 | ||
190 | /** 预览任务 */ | 242 | /** 预览任务 */ |
191 | -export function previewTask(params: PreviewTaskReq): PromiseResp<PreviewTaskRes> { | 243 | +export function previewTask( |
244 | + params: PreviewTaskReq | ||
245 | +): PromiseResp<PreviewTaskRes> { | ||
192 | return request.get(`${ORDER3_HOST}/erp/sales/task/approve/info`, { | 246 | return request.get(`${ORDER3_HOST}/erp/sales/task/approve/info`, { |
193 | params, | 247 | params, |
194 | }); | 248 | }); |
src/pages/order3/SaleTask/components/AdviserTaskPreview.tsx
@@ -4,8 +4,7 @@ import { observer } from "mobx-react-lite"; | @@ -4,8 +4,7 @@ import { observer } from "mobx-react-lite"; | ||
4 | import * as API from "../api"; | 4 | import * as API from "../api"; |
5 | import useInitial from "@/hooks/useInitail"; | 5 | import useInitial from "@/hooks/useInitail"; |
6 | import ModifiedTableCell from "./ModifiedTableCell"; | 6 | import ModifiedTableCell from "./ModifiedTableCell"; |
7 | - | ||
8 | -const { Column } = Table; | 7 | +import { ColumnsType } from "antd/es/table"; |
9 | 8 | ||
10 | // 查看销顾任务弹框 | 9 | // 查看销顾任务弹框 |
11 | interface AdviserTaskPreviewProps { | 10 | interface AdviserTaskPreviewProps { |
@@ -31,73 +30,100 @@ const AdviserTaskPreview = ({ | @@ -31,73 +30,100 @@ const AdviserTaskPreview = ({ | ||
31 | showSeriesModal(record); | 30 | showSeriesModal(record); |
32 | }; | 31 | }; |
33 | 32 | ||
33 | + const columns: ColumnsType<API.TaskListItem> = [ | ||
34 | + { | ||
35 | + title: "姓名", | ||
36 | + width: 100, | ||
37 | + dataIndex: "dataName", | ||
38 | + filterSearch: true, | ||
39 | + onFilter: ( | ||
40 | + value: string | number | boolean, | ||
41 | + record: API.TaskListItem | ||
42 | + ) => { | ||
43 | + return record.dataName.startsWith(value.toString()); | ||
44 | + }, | ||
45 | + }, | ||
46 | + { | ||
47 | + title: "零售任务(台)", | ||
48 | + children: [ | ||
49 | + { | ||
50 | + title: "合计", | ||
51 | + dataIndex: "taskCount", | ||
52 | + key: "taskCount", | ||
53 | + render: (text: string, record: API.TaskListItem) => { | ||
54 | + return ModifiedTableCell(record, "taskCount"); | ||
55 | + }, | ||
56 | + }, | ||
57 | + { | ||
58 | + title: "新能源车", | ||
59 | + dataIndex: "newEnergyTaskCount", | ||
60 | + key: "newEnergyTaskCount", | ||
61 | + render: (text: string, record: API.TaskListItem) => { | ||
62 | + return ModifiedTableCell(record, "newEnergyTaskCount"); | ||
63 | + }, | ||
64 | + }, | ||
65 | + { | ||
66 | + title: "传统燃油车", | ||
67 | + dataIndex: "fuelVehicleTaskCount", | ||
68 | + key: "fuelVehicleTaskCount", | ||
69 | + render: (text: string, record: API.TaskListItem) => { | ||
70 | + return ModifiedTableCell(record, "fuelVehicleTaskCount"); | ||
71 | + }, | ||
72 | + }, | ||
73 | + ], | ||
74 | + }, | ||
75 | + { | ||
76 | + title: "单车毛利任务(元)", | ||
77 | + dataIndex: "vehicleGrossProfitTask", | ||
78 | + render: (text: string, record: API.TaskListItem) => { | ||
79 | + return ModifiedTableCell(record, "vehicleGrossProfitTask"); | ||
80 | + }, | ||
81 | + }, | ||
82 | + { | ||
83 | + title: "线索到店成交(台)", | ||
84 | + width: 100, | ||
85 | + dataIndex: "clueDealTaskCount", | ||
86 | + render: (text: string, record: API.TaskListItem) => { | ||
87 | + return ModifiedTableCell(record, "clueDealTaskCount"); | ||
88 | + }, | ||
89 | + }, | ||
90 | + { | ||
91 | + title: "首客试驾成交(台)", | ||
92 | + width: 100, | ||
93 | + dataIndex: "testDriveTaskCount", | ||
94 | + render: (text: string, record: API.TaskListItem) => { | ||
95 | + return ModifiedTableCell(record, "testDriveTaskCount"); | ||
96 | + }, | ||
97 | + }, | ||
98 | + { | ||
99 | + title: "攻坚车任务(台)", | ||
100 | + width: 100, | ||
101 | + dataIndex: "tackCarTaskCount", | ||
102 | + render: (text: string, record: API.TaskListItem) => { | ||
103 | + return ModifiedTableCell(record, "tackCarTaskCount"); | ||
104 | + }, | ||
105 | + }, | ||
106 | + { | ||
107 | + title: "车系任务(台)", | ||
108 | + width: 100, | ||
109 | + dataIndex: "seriesTaskCount", | ||
110 | + render: (text: string, record: API.TaskListItem) => { | ||
111 | + if (record.dataId === -999) return text; | ||
112 | + return <a onClick={() => handlePreviewSeriesTask(record)}>{text}</a>; | ||
113 | + }, | ||
114 | + }, | ||
115 | + ]; | ||
116 | + | ||
34 | return ( | 117 | return ( |
35 | <Table | 118 | <Table |
119 | + bordered | ||
36 | rowKey="dataId" | 120 | rowKey="dataId" |
37 | loading={loading} | 121 | loading={loading} |
122 | + columns={columns} | ||
38 | dataSource={data.taskList} | 123 | dataSource={data.taskList} |
39 | pagination={false} | 124 | pagination={false} |
40 | scroll={{ y: 450 }} | 125 | scroll={{ y: 450 }} |
41 | - > | ||
42 | - <Column title="姓名" dataIndex="dataName" /> | ||
43 | - <Column | ||
44 | - title="零售任务(台)" | ||
45 | - dataIndex="taskCount" | ||
46 | - render={(text: string, record: API.TaskListItem) => { | ||
47 | - return ModifiedTableCell(record, "taskCount"); | ||
48 | - }} | ||
49 | - /> | ||
50 | - <Column | ||
51 | - title="新能源车任务(台)" | ||
52 | - dataIndex="newEnergyTaskCount" | ||
53 | - render={(text: string, record: API.TaskListItem) => { | ||
54 | - return ModifiedTableCell(record, "newEnergyTaskCount"); | ||
55 | - }} | ||
56 | - /> | ||
57 | - <Column | ||
58 | - title="传统燃油车任务(台)" | ||
59 | - dataIndex="fuelVehicleTaskCount" | ||
60 | - render={(text: string, record: API.TaskListItem) => { | ||
61 | - return ModifiedTableCell(record, "fuelVehicleTaskCount"); | ||
62 | - }} | ||
63 | - /> | ||
64 | - <Column | ||
65 | - title="车辆毛利任务(元)" | ||
66 | - dataIndex="vehicleGrossProfitTask" | ||
67 | - render={(text: string, record: API.TaskListItem) => { | ||
68 | - return ModifiedTableCell(record, "vehicleGrossProfitTask"); | ||
69 | - }} | ||
70 | - /> | ||
71 | - <Column | ||
72 | - title="线索到店零售台数(台)" | ||
73 | - dataIndex="clueDealTaskCount" | ||
74 | - render={(text: string, record: API.TaskListItem) => { | ||
75 | - return ModifiedTableCell(record, "clueDealTaskCount"); | ||
76 | - }} | ||
77 | - /> | ||
78 | - <Column | ||
79 | - title="首客试驾成交任务数(台)" | ||
80 | - dataIndex="testDriveTaskCount" | ||
81 | - render={(text: string, record: API.TaskListItem) => { | ||
82 | - return ModifiedTableCell(record, "testDriveTaskCount"); | ||
83 | - }} | ||
84 | - /> | ||
85 | - <Column | ||
86 | - title="攻坚车任务数(台)" | ||
87 | - dataIndex="tackCarTaskCount" | ||
88 | - render={(text: string, record: API.TaskListItem) => { | ||
89 | - return ModifiedTableCell(record, "tackCarTaskCount"); | ||
90 | - }} | ||
91 | - /> | ||
92 | - <Column | ||
93 | - title="车系任务数(台)" | ||
94 | - dataIndex="seriesTaskCount" | ||
95 | - render={(text: string, record: API.TaskListItem) => { | ||
96 | - if (record.dataId === -999) return text; | ||
97 | - return <a onClick={() => handlePreviewSeriesTask(record)}>{text}</a>; | ||
98 | - }} | ||
99 | - /> | ||
100 | - </Table> | 126 | + /> |
101 | ); | 127 | ); |
102 | }; | 128 | }; |
103 | 129 |
src/pages/order3/SaleTask/components/EntryTaskPreview.tsx
1 | import React, { useState } from "react"; | 1 | import React, { useState } from "react"; |
2 | -import { Card, Radio, RadioChangeEvent, Row, Table } from "antd"; | 2 | +import { |
3 | + Card, | ||
4 | + DatePicker, | ||
5 | + Input, | ||
6 | + Radio, | ||
7 | + RadioChangeEvent, | ||
8 | + Row, | ||
9 | + Table, | ||
10 | +} from "antd"; | ||
3 | import { observer } from "mobx-react-lite"; | 11 | import { observer } from "mobx-react-lite"; |
4 | import * as API from "../api"; | 12 | import * as API from "../api"; |
5 | import { OrderTaskApprovalType } from "../entity"; | 13 | import { OrderTaskApprovalType } from "../entity"; |
6 | import useInitial from "@/hooks/useInitail"; | 14 | import useInitial from "@/hooks/useInitail"; |
7 | import ModifiedTableCell from "./ModifiedTableCell"; | 15 | import ModifiedTableCell from "./ModifiedTableCell"; |
16 | +import { ColumnsType } from "antd/es/table"; | ||
17 | +import { Moment } from "moment"; | ||
8 | 18 | ||
9 | -const { Column } = Table; | ||
10 | const RadioButton = Radio.Button; | 19 | const RadioButton = Radio.Button; |
11 | const RadioGroup = Radio.Group; | 20 | const RadioGroup = Radio.Group; |
12 | 21 | ||
13 | // 预览任务入口弹框 | 22 | // 预览任务入口弹框 |
14 | interface EntryTaskPreviewProps { | 23 | interface EntryTaskPreviewProps { |
24 | + month: Moment; | ||
15 | params: any; // API.PreviewTaskReq | 25 | params: any; // API.PreviewTaskReq |
16 | showAdviserModal: ( | 26 | showAdviserModal: ( |
17 | record: API.TaskListItem, | 27 | record: API.TaskListItem, |
@@ -24,11 +34,13 @@ interface EntryTaskPreviewProps { | @@ -24,11 +34,13 @@ interface EntryTaskPreviewProps { | ||
24 | } | 34 | } |
25 | 35 | ||
26 | const EntryTaskPreview = ({ | 36 | const EntryTaskPreview = ({ |
37 | + month, | ||
27 | params, | 38 | params, |
28 | showAdviserModal, | 39 | showAdviserModal, |
29 | showSeriesModal, | 40 | showSeriesModal, |
30 | }: EntryTaskPreviewProps) => { | 41 | }: EntryTaskPreviewProps) => { |
31 | const [type, setType] = useState(OrderTaskApprovalType.门店维度); | 42 | const [type, setType] = useState(OrderTaskApprovalType.门店维度); |
43 | + const [keywords, setKeywords] = useState(""); | ||
32 | 44 | ||
33 | const { data, loading, setParams } = useInitial< | 45 | const { data, loading, setParams } = useInitial< |
34 | API.PreviewTaskRes, | 46 | API.PreviewTaskRes, |
@@ -40,15 +52,13 @@ const EntryTaskPreview = ({ | @@ -40,15 +52,13 @@ const EntryTaskPreview = ({ | ||
40 | if (value === 99) { | 52 | if (value === 99) { |
41 | setType(OrderTaskApprovalType.新车一级管理维度); | 53 | setType(OrderTaskApprovalType.新车一级管理维度); |
42 | setParams( | 54 | setParams( |
43 | - // @ts-ignore | ||
44 | { orderTaskApprovalType: OrderTaskApprovalType.新车一级管理维度 }, | 55 | { orderTaskApprovalType: OrderTaskApprovalType.新车一级管理维度 }, |
45 | true | 56 | true |
46 | ); | 57 | ); |
47 | return; | 58 | return; |
48 | } | 59 | } |
49 | setType(value); | 60 | setType(value); |
50 | - // @ts-ignore | ||
51 | - setParams({ orderTaskApprovalType: value }, true); | 61 | + setParams({ orderTaskApprovalType: value, keywords }, true); |
52 | }; | 62 | }; |
53 | 63 | ||
54 | // 查看顾问任务 | 64 | // 查看顾问任务 |
@@ -67,130 +77,171 @@ const EntryTaskPreview = ({ | @@ -67,130 +77,171 @@ const EntryTaskPreview = ({ | ||
67 | showSeriesModal(record, type); | 77 | showSeriesModal(record, type); |
68 | }; | 78 | }; |
69 | 79 | ||
70 | - return ( | ||
71 | - <Card | ||
72 | - title={ | ||
73 | - <Row align="middle" justify="start"> | ||
74 | - <RadioGroup onChange={handleChangeType} value={type}> | ||
75 | - <RadioButton value={OrderTaskApprovalType.门店维度}> | ||
76 | - 门店 | ||
77 | - </RadioButton> | ||
78 | - <RadioButton value={OrderTaskApprovalType.销售顾问维度}> | ||
79 | - 销顾 | ||
80 | - </RadioButton> | ||
81 | - <RadioButton value={99}>销售管理</RadioButton> | ||
82 | - </RadioGroup> | ||
83 | - {type !== OrderTaskApprovalType.门店维度 && | ||
84 | - type !== OrderTaskApprovalType.销售顾问维度 && ( | ||
85 | - <RadioGroup | ||
86 | - onChange={handleChangeType} | ||
87 | - value={type} | ||
88 | - style={{ marginLeft: 20 }} | ||
89 | - > | ||
90 | - <RadioButton value={OrderTaskApprovalType.新车一级管理维度}> | ||
91 | - 销售一级管理 | ||
92 | - </RadioButton> | ||
93 | - <RadioButton value={OrderTaskApprovalType.新车二级管理维度}> | ||
94 | - 销售二级管理 | ||
95 | - </RadioButton> | ||
96 | - <RadioButton value={OrderTaskApprovalType.新车三级管理维度}> | ||
97 | - 销售三级管理 | ||
98 | - </RadioButton> | ||
99 | - </RadioGroup> | ||
100 | - )} | ||
101 | - </Row> | ||
102 | - } | ||
103 | - > | ||
104 | - <Table | ||
105 | - rowKey="dataId" | ||
106 | - loading={loading} | ||
107 | - dataSource={data.taskList} | ||
108 | - pagination={false} | ||
109 | - scroll={{ y: 450 }} | ||
110 | - > | ||
111 | - <Column | ||
112 | - title={type === OrderTaskApprovalType.门店维度 ? "门店" : "姓名"} | ||
113 | - dataIndex="dataName" | ||
114 | - filterSearch | ||
115 | - onFilter={( | ||
116 | - value: string | number | boolean, | ||
117 | - record: API.TaskListItem | ||
118 | - ) => { | ||
119 | - return record.dataName.startsWith(value.toString()); | ||
120 | - }} | ||
121 | - /> | ||
122 | - <Column | ||
123 | - title="零售任务(台)" | ||
124 | - dataIndex="taskCount" | ||
125 | - render={(text: string, record: API.TaskListItem) => { | 80 | + const columns: ColumnsType<API.TaskListItem> = [ |
81 | + { | ||
82 | + title: type === OrderTaskApprovalType.门店维度 ? "门店" : "姓名", | ||
83 | + width: type === OrderTaskApprovalType.门店维度 ? 150 : 100, | ||
84 | + dataIndex: "dataName", | ||
85 | + filterSearch: true, | ||
86 | + onFilter: ( | ||
87 | + value: string | number | boolean, | ||
88 | + record: API.TaskListItem | ||
89 | + ) => { | ||
90 | + return record.dataName.startsWith(value.toString()); | ||
91 | + }, | ||
92 | + }, | ||
93 | + { | ||
94 | + title: "零售任务(台)", | ||
95 | + children: [ | ||
96 | + { | ||
97 | + title: "合计", | ||
98 | + dataIndex: "taskCount", | ||
99 | + key: "taskCount", | ||
100 | + render: (text: string, record: API.TaskListItem) => { | ||
126 | return ModifiedTableCell(record, "taskCount"); | 101 | return ModifiedTableCell(record, "taskCount"); |
127 | - }} | ||
128 | - /> | ||
129 | - <Column | ||
130 | - title="新能源车任务(台)" | ||
131 | - dataIndex="newEnergyTaskCount" | ||
132 | - render={(text: string, record: API.TaskListItem) => { | 102 | + }, |
103 | + }, | ||
104 | + { | ||
105 | + title: "新能源车", | ||
106 | + dataIndex: "newEnergyTaskCount", | ||
107 | + key: "newEnergyTaskCount", | ||
108 | + render: (text: string, record: API.TaskListItem) => { | ||
133 | return ModifiedTableCell(record, "newEnergyTaskCount"); | 109 | return ModifiedTableCell(record, "newEnergyTaskCount"); |
134 | - }} | ||
135 | - /> | ||
136 | - <Column | ||
137 | - title="传统燃油车任务(台)" | ||
138 | - dataIndex="fuelVehicleTaskCount" | ||
139 | - render={(text: string, record: API.TaskListItem) => { | 110 | + }, |
111 | + }, | ||
112 | + { | ||
113 | + title: "传统燃油车", | ||
114 | + dataIndex: "fuelVehicleTaskCount", | ||
115 | + key: "fuelVehicleTaskCount", | ||
116 | + render: (text: string, record: API.TaskListItem) => { | ||
140 | return ModifiedTableCell(record, "fuelVehicleTaskCount"); | 117 | return ModifiedTableCell(record, "fuelVehicleTaskCount"); |
141 | - }} | ||
142 | - /> | ||
143 | - <Column | ||
144 | - title="车辆毛利任务(元)" | ||
145 | - dataIndex="vehicleGrossProfitTask" | ||
146 | - render={(text: string, record: API.TaskListItem) => { | ||
147 | - return ModifiedTableCell(record, "vehicleGrossProfitTask"); | ||
148 | - }} | ||
149 | - /> | ||
150 | - <Column | ||
151 | - title="线索到店零售台数(台)" | ||
152 | - dataIndex="clueDealTaskCount" | ||
153 | - render={(text: string, record: API.TaskListItem) => { | ||
154 | - return ModifiedTableCell(record, "clueDealTaskCount"); | ||
155 | - }} | ||
156 | - /> | ||
157 | - <Column | ||
158 | - title="首客试驾成交任务数(台)" | ||
159 | - dataIndex="testDriveTaskCount" | ||
160 | - render={(text: string, record: API.TaskListItem) => { | ||
161 | - return ModifiedTableCell(record, "testDriveTaskCount"); | ||
162 | - }} | 118 | + }, |
119 | + }, | ||
120 | + ], | ||
121 | + }, | ||
122 | + { | ||
123 | + title: "单车毛利任务(元)", | ||
124 | + dataIndex: "vehicleGrossProfitTask", | ||
125 | + render: (text: string, record: API.TaskListItem) => { | ||
126 | + return ModifiedTableCell(record, "vehicleGrossProfitTask"); | ||
127 | + }, | ||
128 | + }, | ||
129 | + { | ||
130 | + title: "线索到店成交(台)", | ||
131 | + width: 100, | ||
132 | + dataIndex: "clueDealTaskCount", | ||
133 | + render: (text: string, record: API.TaskListItem) => { | ||
134 | + return ModifiedTableCell(record, "clueDealTaskCount"); | ||
135 | + }, | ||
136 | + }, | ||
137 | + { | ||
138 | + title: "首客试驾成交(台)", | ||
139 | + width: 100, | ||
140 | + dataIndex: "testDriveTaskCount", | ||
141 | + render: (text: string, record: API.TaskListItem) => { | ||
142 | + return ModifiedTableCell(record, "testDriveTaskCount"); | ||
143 | + }, | ||
144 | + }, | ||
145 | + { | ||
146 | + title: "攻坚车任务(台)", | ||
147 | + width: 100, | ||
148 | + dataIndex: "tackCarTaskCount", | ||
149 | + render: (text: string, record: API.TaskListItem) => { | ||
150 | + return ModifiedTableCell(record, "tackCarTaskCount"); | ||
151 | + }, | ||
152 | + }, | ||
153 | + { | ||
154 | + title: "车系任务(台)", | ||
155 | + width: 100, | ||
156 | + dataIndex: "seriesTaskCount", | ||
157 | + render: (text: string, record: API.TaskListItem) => { | ||
158 | + if (record.dataId === -999) return text; | ||
159 | + return <a onClick={() => handlePreviewSeriesTask(record)}>{text}</a>; | ||
160 | + }, | ||
161 | + }, | ||
162 | + ]; | ||
163 | + | ||
164 | + const extraColumns: ColumnsType<API.TaskListItem> = [ | ||
165 | + { | ||
166 | + title: "销顾任务", | ||
167 | + render: (text: string, record: API.TaskListItem) => { | ||
168 | + if (record.dataId === -999) { | ||
169 | + return "-"; | ||
170 | + } | ||
171 | + return <a onClick={() => handlePreviewAdviserTask(record)}>查看</a>; | ||
172 | + }, | ||
173 | + }, | ||
174 | + ]; | ||
175 | + | ||
176 | + return ( | ||
177 | + <> | ||
178 | + <Row align="middle" justify="start" style={{ marginBottom: 20 }}> | ||
179 | + <DatePicker | ||
180 | + style={{ width: 245 }} | ||
181 | + picker="month" | ||
182 | + value={month} | ||
183 | + allowClear={false} | ||
184 | + disabled | ||
163 | /> | 185 | /> |
164 | - <Column | ||
165 | - title="攻坚车任务数(台)" | ||
166 | - dataIndex="tackCarTaskCount" | ||
167 | - render={(text: string, record: API.TaskListItem) => { | ||
168 | - return ModifiedTableCell(record, "tackCarTaskCount"); | 186 | + <Input.Search |
187 | + allowClear | ||
188 | + placeholder="搜索门店或顾问" | ||
189 | + style={{ width: 263, marginLeft: 20 }} | ||
190 | + value={keywords} | ||
191 | + onChange={(e) => setKeywords(e.target.value)} | ||
192 | + onSearch={(v) => { | ||
193 | + setParams({ keywords: v }, true); | ||
169 | }} | 194 | }} |
170 | /> | 195 | /> |
171 | - <Column | ||
172 | - title="车系任务数(台)" | ||
173 | - dataIndex="seriesTaskCount" | ||
174 | - render={(text: string, record: API.TaskListItem) => { | ||
175 | - if (record.dataId === -999) return text; | ||
176 | - return ( | ||
177 | - <a onClick={() => handlePreviewSeriesTask(record)}>{text}</a> | ||
178 | - ); | ||
179 | - }} | 196 | + </Row> |
197 | + <Card | ||
198 | + title={ | ||
199 | + <Row align="middle" justify="start"> | ||
200 | + <RadioGroup onChange={handleChangeType} value={type}> | ||
201 | + <RadioButton value={OrderTaskApprovalType.门店维度}> | ||
202 | + 门店 | ||
203 | + </RadioButton> | ||
204 | + <RadioButton value={OrderTaskApprovalType.销售顾问维度}> | ||
205 | + 销顾 | ||
206 | + </RadioButton> | ||
207 | + <RadioButton value={99}>销售管理层</RadioButton> | ||
208 | + </RadioGroup> | ||
209 | + {type !== OrderTaskApprovalType.门店维度 && | ||
210 | + type !== OrderTaskApprovalType.销售顾问维度 && ( | ||
211 | + <RadioGroup | ||
212 | + onChange={handleChangeType} | ||
213 | + value={type} | ||
214 | + style={{ marginLeft: 20 }} | ||
215 | + > | ||
216 | + <RadioButton value={OrderTaskApprovalType.新车一级管理维度}> | ||
217 | + 销售一级管理 | ||
218 | + </RadioButton> | ||
219 | + <RadioButton value={OrderTaskApprovalType.新车二级管理维度}> | ||
220 | + 销售二级管理 | ||
221 | + </RadioButton> | ||
222 | + <RadioButton value={OrderTaskApprovalType.新车三级管理维度}> | ||
223 | + 销售三级管理 | ||
224 | + </RadioButton> | ||
225 | + </RadioGroup> | ||
226 | + )} | ||
227 | + </Row> | ||
228 | + } | ||
229 | + > | ||
230 | + <Table | ||
231 | + bordered | ||
232 | + rowKey="dataId" | ||
233 | + columns={ | ||
234 | + type === OrderTaskApprovalType.门店维度 | ||
235 | + ? columns.concat(extraColumns) | ||
236 | + : columns | ||
237 | + } | ||
238 | + loading={loading} | ||
239 | + dataSource={data.taskList} | ||
240 | + pagination={false} | ||
241 | + scroll={{ y: 450 }} | ||
180 | /> | 242 | /> |
181 | - {type === OrderTaskApprovalType.门店维度 && ( | ||
182 | - <Column | ||
183 | - title="销顾" | ||
184 | - render={(text: string, record: API.TaskListItem) => { | ||
185 | - if (record.dataId === -999) return "-"; | ||
186 | - return ( | ||
187 | - <a onClick={() => handlePreviewAdviserTask(record)}>查看</a> | ||
188 | - ); | ||
189 | - }} | ||
190 | - /> | ||
191 | - )} | ||
192 | - </Table> | ||
193 | - </Card> | 243 | + </Card> |
244 | + </> | ||
194 | ); | 245 | ); |
195 | }; | 246 | }; |
196 | 247 |
src/pages/order3/SaleTask/components/SaleTaskAutoAssign.tsx
0 → 100644
1 | +import React, { useContext, useEffect, useRef, useState } from "react"; | ||
2 | +import { | ||
3 | + Table, | ||
4 | + Form, | ||
5 | + InputRef, | ||
6 | + Row, | ||
7 | + Button, | ||
8 | + message, | ||
9 | + Modal, | ||
10 | + InputNumber, | ||
11 | +} from "antd"; | ||
12 | +import type { FormInstance } from "antd/es/form"; | ||
13 | +import * as API from "../api"; | ||
14 | +import "./index.less"; | ||
15 | +import { MAX_NUM } from "../entity"; | ||
16 | + | ||
17 | +type EditableTableProps = Parameters<typeof Table>[0]; | ||
18 | +type ColumnTypes = Exclude<EditableTableProps["columns"], undefined>; | ||
19 | +interface Item { | ||
20 | + id: string; | ||
21 | + shopName: string; | ||
22 | + taskCount: number; | ||
23 | + newEnergyTaskCount: number; | ||
24 | + fuelVehicleTaskCount: number; | ||
25 | + tackCarTaskCount: number; | ||
26 | +} | ||
27 | +interface EditableRowProps { | ||
28 | + index: number; | ||
29 | +} | ||
30 | +interface EditableCellProps { | ||
31 | + title: React.ReactNode; | ||
32 | + editable: boolean; | ||
33 | + children: React.ReactNode; | ||
34 | + dataIndex: keyof Item; | ||
35 | + record: Item; | ||
36 | + handleSave: (record: Item) => void; | ||
37 | +} | ||
38 | + | ||
39 | +const defaultColumns: (ColumnTypes[number] & { | ||
40 | + editable?: boolean; | ||
41 | + dataIndex: string; | ||
42 | +})[] = [ | ||
43 | + { | ||
44 | + title: "门店", | ||
45 | + dataIndex: "shopName", | ||
46 | + editable: false, | ||
47 | + }, | ||
48 | + { | ||
49 | + title: "零售任务(台)", | ||
50 | + dataIndex: "taskCount", | ||
51 | + editable: true, | ||
52 | + }, | ||
53 | + { | ||
54 | + title: "新能源车任务(台)", | ||
55 | + dataIndex: "newEnergyTaskCount", | ||
56 | + editable: true, | ||
57 | + }, | ||
58 | + { | ||
59 | + title: "传统燃油车任务(台)", | ||
60 | + dataIndex: "fuelVehicleTaskCount", | ||
61 | + editable: false, | ||
62 | + }, | ||
63 | + { | ||
64 | + title: "攻坚车任务(台)", | ||
65 | + dataIndex: "tackCarTaskCount", | ||
66 | + editable: true, | ||
67 | + }, | ||
68 | +]; | ||
69 | + | ||
70 | +interface SaleTaskAutoAssignProps { | ||
71 | + id: number; | ||
72 | + value?: API.ShopTaskItem[]; | ||
73 | + onCancel: () => void; | ||
74 | + onRefresh: () => void; | ||
75 | +} | ||
76 | + | ||
77 | +export default function SaleTaskAutoAssign(props: SaleTaskAutoAssignProps) { | ||
78 | + const EditableContext = React.createContext<FormInstance<any> | null>(null); | ||
79 | + const [dataSource, setDataSource] = useState<API.ShopTaskItem[]>([]); | ||
80 | + | ||
81 | + useEffect(() => { | ||
82 | + setDataSource(props.value ? [...props.value] : []); | ||
83 | + }, [props.value]); | ||
84 | + | ||
85 | + const EditableRow: React.FC<EditableRowProps> = ({ index, ...props }) => { | ||
86 | + const [form] = Form.useForm(); | ||
87 | + return ( | ||
88 | + <Form form={form} component={false}> | ||
89 | + <EditableContext.Provider value={form}> | ||
90 | + <tr {...props} /> | ||
91 | + </EditableContext.Provider> | ||
92 | + </Form> | ||
93 | + ); | ||
94 | + }; | ||
95 | + | ||
96 | + const EditableCell: React.FC<EditableCellProps> = ({ | ||
97 | + title, | ||
98 | + editable, | ||
99 | + children, | ||
100 | + dataIndex, | ||
101 | + record, | ||
102 | + handleSave, | ||
103 | + ...restProps | ||
104 | + }) => { | ||
105 | + const [editing, setEditing] = useState(false); | ||
106 | + const inputRef = useRef<InputRef>(null); | ||
107 | + const form = useContext(EditableContext)!; | ||
108 | + | ||
109 | + useEffect(() => { | ||
110 | + if (editing) { | ||
111 | + inputRef.current!.focus(); | ||
112 | + } | ||
113 | + }, [editing]); | ||
114 | + | ||
115 | + const toggleEdit = () => { | ||
116 | + setEditing(!editing); | ||
117 | + form.setFieldsValue({ [dataIndex]: record[dataIndex] }); | ||
118 | + }; | ||
119 | + | ||
120 | + const save = async () => { | ||
121 | + try { | ||
122 | + const values = await form.validateFields(); | ||
123 | + toggleEdit(); | ||
124 | + handleSave({ ...record, ...values }); | ||
125 | + } catch (errInfo) { | ||
126 | + console.log("Save failed:", errInfo); | ||
127 | + } | ||
128 | + }; | ||
129 | + | ||
130 | + let childNode = children; | ||
131 | + | ||
132 | + if (editable) { | ||
133 | + childNode = editing ? ( | ||
134 | + <Form.Item | ||
135 | + noStyle | ||
136 | + name={dataIndex} | ||
137 | + rules={[ | ||
138 | + { | ||
139 | + required: true, | ||
140 | + message: `请输入${title}`, | ||
141 | + }, | ||
142 | + ]} | ||
143 | + > | ||
144 | + <InputNumber | ||
145 | + ref={inputRef} | ||
146 | + min={0} | ||
147 | + max={MAX_NUM} | ||
148 | + style={{ width: "80px" }} | ||
149 | + onPressEnter={save} | ||
150 | + onBlur={save} | ||
151 | + /> | ||
152 | + </Form.Item> | ||
153 | + ) : ( | ||
154 | + <div className="editable-cell-value-wrap" onClick={toggleEdit}> | ||
155 | + {children} | ||
156 | + </div> | ||
157 | + ); | ||
158 | + } | ||
159 | + | ||
160 | + return <td {...restProps}>{childNode}</td>; | ||
161 | + }; | ||
162 | + | ||
163 | + const handleSave = (row: API.ShopTaskItem) => { | ||
164 | + const newData = [...dataSource]; | ||
165 | + const index = newData.findIndex((item) => row.id === item.id); | ||
166 | + const item = newData[index]; | ||
167 | + if (row.taskCount !== 0 && row.newEnergyTaskCount > row.taskCount) { | ||
168 | + message.warn("新能源车任务台数不得超过零售任务台数"); | ||
169 | + return; | ||
170 | + } | ||
171 | + const newRow = { | ||
172 | + ...item, | ||
173 | + ...row, | ||
174 | + fuelVehicleTaskCount: row.taskCount - row.newEnergyTaskCount, | ||
175 | + }; | ||
176 | + if (row.taskCount === 0) { | ||
177 | + newRow.taskCount = 0; | ||
178 | + newRow.newEnergyTaskCount = 0; | ||
179 | + newRow.fuelVehicleTaskCount = 0; | ||
180 | + } | ||
181 | + newData.splice(index, 1, newRow); | ||
182 | + setDataSource(newData); | ||
183 | + }; | ||
184 | + | ||
185 | + const autoAssignSaleTask = (isAssignToAdviser: boolean) => { | ||
186 | + Modal.confirm({ | ||
187 | + title: isAssignToAdviser ? ( | ||
188 | + <span> | ||
189 | + 确认分配到 | ||
190 | + <span className="tip">全部门店和顾问</span> | ||
191 | + 吗? | ||
192 | + </span> | ||
193 | + ) : ( | ||
194 | + <span> | ||
195 | + 确认分配到 | ||
196 | + <span className="tip">全部门店</span> | ||
197 | + 吗? | ||
198 | + </span> | ||
199 | + ), | ||
200 | + zIndex: 1002, | ||
201 | + onOk: async () => { | ||
202 | + const hide = message.loading("分配中,请稍候", 0); | ||
203 | + API.autoAssignSaleTask({ | ||
204 | + id: props.id, | ||
205 | + shopTaskList: dataSource.map((item) => ({ | ||
206 | + shopId: item.shopId, | ||
207 | + taskCount: item.taskCount, | ||
208 | + newEnergyTaskCount: item.newEnergyTaskCount, | ||
209 | + fuelVehicleTaskCount: item.fuelVehicleTaskCount, | ||
210 | + tackCarTaskCount: item.tackCarTaskCount, | ||
211 | + })), | ||
212 | + assignTask: isAssignToAdviser, | ||
213 | + }) | ||
214 | + .then((res) => { | ||
215 | + message.success("分配成功"); | ||
216 | + props.onRefresh(); | ||
217 | + }) | ||
218 | + .catch((error: any) => { | ||
219 | + message.error(error.message ?? "请求失败"); | ||
220 | + }) | ||
221 | + .finally(() => { | ||
222 | + hide(); | ||
223 | + }); | ||
224 | + }, | ||
225 | + }); | ||
226 | + }; | ||
227 | + | ||
228 | + const components = { | ||
229 | + body: { | ||
230 | + row: EditableRow, | ||
231 | + cell: EditableCell, | ||
232 | + }, | ||
233 | + }; | ||
234 | + | ||
235 | + const columns = defaultColumns.map((col) => { | ||
236 | + if (!col.editable) { | ||
237 | + return col; | ||
238 | + } | ||
239 | + return { | ||
240 | + ...col, | ||
241 | + onCell: (record: API.ShopTaskItem) => ({ | ||
242 | + record, | ||
243 | + editable: col.editable, | ||
244 | + dataIndex: col.dataIndex, | ||
245 | + title: col.title, | ||
246 | + handleSave, | ||
247 | + }), | ||
248 | + }; | ||
249 | + }); | ||
250 | + | ||
251 | + return ( | ||
252 | + <> | ||
253 | + <Table | ||
254 | + components={components} | ||
255 | + rowClassName={() => "editable-row"} | ||
256 | + bordered | ||
257 | + rowKey="id" | ||
258 | + dataSource={dataSource} | ||
259 | + columns={columns as ColumnTypes} | ||
260 | + /> | ||
261 | + <Row align="middle" justify="center" style={{ marginTop: 20 }}> | ||
262 | + <Button onClick={props.onCancel}>取消</Button> | ||
263 | + <Button | ||
264 | + type="primary" | ||
265 | + style={{ marginLeft: 10 }} | ||
266 | + onClick={() => autoAssignSaleTask(false)} | ||
267 | + > | ||
268 | + 分配到门店 | ||
269 | + </Button> | ||
270 | + <Button | ||
271 | + type="primary" | ||
272 | + style={{ marginLeft: 10 }} | ||
273 | + onClick={() => autoAssignSaleTask(true)} | ||
274 | + > | ||
275 | + 分配到门店和顾问 | ||
276 | + </Button> | ||
277 | + </Row> | ||
278 | + </> | ||
279 | + ); | ||
280 | +} |
src/pages/order3/SaleTask/components/SaleTaskBatchSet.tsx
0 → 100644
1 | +import { PlusOutlined } from "@ant-design/icons"; | ||
2 | +import { | ||
3 | + Button, | ||
4 | + Card, | ||
5 | + Col, | ||
6 | + Form, | ||
7 | + InputNumber, | ||
8 | + Modal, | ||
9 | + Row, | ||
10 | + message, | ||
11 | +} from "antd"; | ||
12 | +import "./index.less"; | ||
13 | +import React, { useState } from "react"; | ||
14 | +import { MAX_NUM } from "../entity"; | ||
15 | +import * as API from "../api"; | ||
16 | +import ShopSelectNew from "@/components/ShopSelectNew"; | ||
17 | +import { isArray } from "lodash"; | ||
18 | + | ||
19 | +interface SaleTaskBatchSetProps { | ||
20 | + id: number; | ||
21 | + onCancel: () => void; | ||
22 | + onRefresh: () => void; | ||
23 | +} | ||
24 | + | ||
25 | +export default function SaleTaskBatchSet(props: SaleTaskBatchSetProps) { | ||
26 | + const [form] = Form.useForm(); | ||
27 | + // 过滤各项已经选择的门店 | ||
28 | + // const [selectedShopIds, setSelectedShopIds] = useState({ | ||
29 | + // grossProfit: [], | ||
30 | + // tackCar: [], | ||
31 | + // testDrive: [], | ||
32 | + // }); | ||
33 | + | ||
34 | + const batchSetSaleTask = async (isAssignToAdviser: boolean) => { | ||
35 | + await form.validateFields(); | ||
36 | + const values = form.getFieldsValue(); | ||
37 | + if ( | ||
38 | + Object.values(values).every( | ||
39 | + (item) => !item || (isArray(item) && item.length === 0) | ||
40 | + ) | ||
41 | + ) { | ||
42 | + message.warn("请设置任务后再进行分配"); | ||
43 | + return; | ||
44 | + } | ||
45 | + const newValues = {}; | ||
46 | + Array.from(Object.keys(values)).forEach((valueKey: any) => { | ||
47 | + if (values[valueKey]) { | ||
48 | + newValues[valueKey] = values[valueKey].map((valueItem: any) => ({ | ||
49 | + taskAims: valueItem.taskAims, | ||
50 | + shopIdList: valueItem.shopIdList.map( | ||
51 | + (shopItem: any) => shopItem.shopId | ||
52 | + ), | ||
53 | + })); | ||
54 | + } | ||
55 | + }); | ||
56 | + Modal.confirm({ | ||
57 | + title: isAssignToAdviser ? ( | ||
58 | + <span> | ||
59 | + 确认分配到 | ||
60 | + <span className="tip">全部门店和顾问</span> | ||
61 | + 吗? | ||
62 | + </span> | ||
63 | + ) : ( | ||
64 | + <span> | ||
65 | + 确认分配到 | ||
66 | + <span className="tip">全部门店</span> | ||
67 | + 吗? | ||
68 | + </span> | ||
69 | + ), | ||
70 | + zIndex: 1002, | ||
71 | + onOk: async () => { | ||
72 | + const hide = message.loading("分配中,请稍候", 0); | ||
73 | + API.batchSetSaleTask({ | ||
74 | + assignTask: isAssignToAdviser, | ||
75 | + orderTaskApplyId: props.id, | ||
76 | + ...newValues, | ||
77 | + }) | ||
78 | + .then((res) => { | ||
79 | + message.success("分配成功"); | ||
80 | + props.onRefresh(); | ||
81 | + }) | ||
82 | + .catch((error: any) => { | ||
83 | + message.error(error.message ?? "请求失败"); | ||
84 | + }) | ||
85 | + .finally(() => { | ||
86 | + hide(); | ||
87 | + }); | ||
88 | + }, | ||
89 | + }); | ||
90 | + }; | ||
91 | + | ||
92 | + // const handleFormChange = (changedValues: any) => { | ||
93 | + // const labelKey: any = Object.keys(changedValues)[0]; | ||
94 | + // const list: any = Object.values(changedValues)[0]; | ||
95 | + // console.log(list); | ||
96 | + // if (!list[0]) return; | ||
97 | + // if (Object.keys(list[0])[0] === "shopIdList") { | ||
98 | + // const newSelectedIds = { ...selectedShopIds }; | ||
99 | + // newSelectedIds[labelKey] = Object.values(list[0])[0]; | ||
100 | + // setSelectedShopIds(newSelectedIds); | ||
101 | + // } | ||
102 | + // }; | ||
103 | + | ||
104 | + return ( | ||
105 | + <Form | ||
106 | + form={form} | ||
107 | + name="sale-task-batch-set-form" | ||
108 | + autoComplete="off" | ||
109 | + // onValuesChange={handleFormChange} | ||
110 | + > | ||
111 | + <Form.List name="grossProfitTaskList"> | ||
112 | + {(fields, { add, remove }) => ( | ||
113 | + <Card> | ||
114 | + <Row | ||
115 | + align="middle" | ||
116 | + justify="space-between" | ||
117 | + style={{ marginBottom: 15 }} | ||
118 | + > | ||
119 | + <h4 className="title">单车毛利任务</h4> | ||
120 | + <Button type="link" onClick={() => add()} icon={<PlusOutlined />}> | ||
121 | + 新增 | ||
122 | + </Button> | ||
123 | + </Row> | ||
124 | + {fields.map(({ key, name, ...restField }) => ( | ||
125 | + <Row gutter={16} key={key}> | ||
126 | + <Col className="gutter-row" span={6}> | ||
127 | + <Form.Item | ||
128 | + {...restField} | ||
129 | + name={[name, "taskAims"]} | ||
130 | + rules={[{ required: true, message: "请填写单车毛利任务" }]} | ||
131 | + > | ||
132 | + <InputNumber | ||
133 | + formatter={(value) => `${value}元`} | ||
134 | + parser={(value: any) => value.replace("元", "")} | ||
135 | + min={0} | ||
136 | + max={MAX_NUM} | ||
137 | + style={{ width: "100%" }} | ||
138 | + precision={2} | ||
139 | + placeholder="请填写单车毛利任务" | ||
140 | + /> | ||
141 | + </Form.Item> | ||
142 | + </Col> | ||
143 | + <Col className="gutter-row" span={15}> | ||
144 | + <Form.Item | ||
145 | + {...restField} | ||
146 | + name={[name, "shopIdList"]} | ||
147 | + rules={[{ required: true, message: "请选择适用门店" }]} | ||
148 | + > | ||
149 | + <ShopSelectNew | ||
150 | + multiple | ||
151 | + defaultOptions={{ bizTypes: "1" }} | ||
152 | + placeholder="请选择适用门店" | ||
153 | + // disabledShopIds={selectedShopIds.grossProfit} | ||
154 | + /> | ||
155 | + </Form.Item> | ||
156 | + </Col> | ||
157 | + <Col className="gutter-row" span={3}> | ||
158 | + <Button | ||
159 | + type="link" | ||
160 | + style={{ color: "#999" }} | ||
161 | + onClick={() => remove(name)} | ||
162 | + > | ||
163 | + 删除 | ||
164 | + </Button> | ||
165 | + </Col> | ||
166 | + </Row> | ||
167 | + ))} | ||
168 | + </Card> | ||
169 | + )} | ||
170 | + </Form.List> | ||
171 | + <Form.List name="testDriveTaskList"> | ||
172 | + {(fields, { add, remove }) => ( | ||
173 | + <Card style={{ marginTop: 20 }}> | ||
174 | + <Row | ||
175 | + align="middle" | ||
176 | + justify="space-between" | ||
177 | + style={{ marginBottom: 15 }} | ||
178 | + > | ||
179 | + <h4 className="title">首客试驾成交</h4> | ||
180 | + <Button type="link" onClick={() => add()} icon={<PlusOutlined />}> | ||
181 | + 新增 | ||
182 | + </Button> | ||
183 | + </Row> | ||
184 | + {fields.map(({ key, name, ...restField }) => ( | ||
185 | + <Row gutter={16} key={key}> | ||
186 | + <Col className="gutter-row" span={6}> | ||
187 | + <Form.Item | ||
188 | + {...restField} | ||
189 | + name={[name, "taskAims"]} | ||
190 | + rules={[{ required: true, message: "请填写首客试驾成交" }]} | ||
191 | + > | ||
192 | + <InputNumber | ||
193 | + formatter={(value) => `${value}台`} | ||
194 | + parser={(value: any) => value.replace("台", "")} | ||
195 | + min={0} | ||
196 | + max={MAX_NUM} | ||
197 | + style={{ width: "100%" }} | ||
198 | + precision={0} | ||
199 | + placeholder="请填写首客试驾成交" | ||
200 | + /> | ||
201 | + </Form.Item> | ||
202 | + </Col> | ||
203 | + <Col className="gutter-row" span={15}> | ||
204 | + <Form.Item | ||
205 | + {...restField} | ||
206 | + name={[name, "shopIdList"]} | ||
207 | + rules={[{ required: true, message: "请选择适用门店" }]} | ||
208 | + > | ||
209 | + <ShopSelectNew | ||
210 | + multiple | ||
211 | + defaultOptions={{ bizTypes: "1" }} | ||
212 | + placeholder="请选择适用门店" | ||
213 | + // disabledShopIds={selectedShopIds.testDrive} | ||
214 | + /> | ||
215 | + </Form.Item> | ||
216 | + </Col> | ||
217 | + <Col className="gutter-row" span={3}> | ||
218 | + <Button | ||
219 | + type="link" | ||
220 | + style={{ color: "#999" }} | ||
221 | + onClick={() => remove(name)} | ||
222 | + > | ||
223 | + 删除 | ||
224 | + </Button> | ||
225 | + </Col> | ||
226 | + </Row> | ||
227 | + ))} | ||
228 | + </Card> | ||
229 | + )} | ||
230 | + </Form.List> | ||
231 | + <Form.List name="tackCarTaskList"> | ||
232 | + {(fields, { add, remove }) => ( | ||
233 | + <Card style={{ marginTop: 20 }}> | ||
234 | + <Row | ||
235 | + align="middle" | ||
236 | + justify="space-between" | ||
237 | + style={{ marginBottom: 15 }} | ||
238 | + > | ||
239 | + <h4 className="title">攻坚车任务</h4> | ||
240 | + <Button type="link" onClick={() => add()} icon={<PlusOutlined />}> | ||
241 | + 新增 | ||
242 | + </Button> | ||
243 | + </Row> | ||
244 | + {fields.map(({ key, name, ...restField }) => ( | ||
245 | + <Row gutter={16} key={key}> | ||
246 | + <Col className="gutter-row" span={6}> | ||
247 | + <Form.Item | ||
248 | + {...restField} | ||
249 | + name={[name, "taskAims"]} | ||
250 | + rules={[{ required: true, message: "请填写攻坚车任务" }]} | ||
251 | + > | ||
252 | + <InputNumber | ||
253 | + formatter={(value) => `${value}台`} | ||
254 | + parser={(value: any) => value.replace("台", "")} | ||
255 | + min={0} | ||
256 | + max={MAX_NUM} | ||
257 | + style={{ width: "100%" }} | ||
258 | + precision={0} | ||
259 | + placeholder="请填写攻坚车任务" | ||
260 | + /> | ||
261 | + </Form.Item> | ||
262 | + </Col> | ||
263 | + <Col className="gutter-row" span={15}> | ||
264 | + <Form.Item | ||
265 | + {...restField} | ||
266 | + name={[name, "shopIdList"]} | ||
267 | + rules={[{ required: true, message: "请选择适用门店" }]} | ||
268 | + > | ||
269 | + <ShopSelectNew | ||
270 | + multiple | ||
271 | + defaultOptions={{ bizTypes: "1" }} | ||
272 | + placeholder="请选择适用门店" | ||
273 | + // disabledShopIds={selectedShopIds.tackCar} | ||
274 | + /> | ||
275 | + </Form.Item> | ||
276 | + </Col> | ||
277 | + <Col className="gutter-row" span={3}> | ||
278 | + <Button | ||
279 | + type="link" | ||
280 | + style={{ color: "#999" }} | ||
281 | + onClick={() => remove(name)} | ||
282 | + > | ||
283 | + 删除 | ||
284 | + </Button> | ||
285 | + </Col> | ||
286 | + </Row> | ||
287 | + ))} | ||
288 | + </Card> | ||
289 | + )} | ||
290 | + </Form.List> | ||
291 | + <Row align="middle" justify="center" style={{ marginTop: 20 }}> | ||
292 | + <Button onClick={props.onCancel}>取消</Button> | ||
293 | + <Button | ||
294 | + type="primary" | ||
295 | + style={{ marginLeft: 10 }} | ||
296 | + onClick={() => batchSetSaleTask(false)} | ||
297 | + > | ||
298 | + 分配到门店 | ||
299 | + </Button> | ||
300 | + <Button | ||
301 | + type="primary" | ||
302 | + style={{ marginLeft: 10 }} | ||
303 | + onClick={() => batchSetSaleTask(true)} | ||
304 | + > | ||
305 | + 分配到门店和顾问 | ||
306 | + </Button> | ||
307 | + </Row> | ||
308 | + </Form> | ||
309 | + ); | ||
310 | +} |
src/pages/order3/SaleTask/components/index.less
0 → 100644
src/pages/order3/SaleTask/index.tsx
@@ -15,14 +15,15 @@ import { history } from "umi"; | @@ -15,14 +15,15 @@ import { history } from "umi"; | ||
15 | import moment, { Moment } from "moment"; | 15 | import moment, { Moment } from "moment"; |
16 | import useInitial from "@/hooks/useInitail"; | 16 | import useInitial from "@/hooks/useInitail"; |
17 | import { Provider, useStore } from "./store"; | 17 | import { Provider, useStore } from "./store"; |
18 | -import { default as ApprovalProgressModal } from "@/pages/stock/AdvanceProgress/components/ApproveModal"; | 18 | +import ApprovalProgressModal from "@/pages/stock/AdvanceProgress/components/ApproveModal"; |
19 | import EntryTaskPreview from "./components/EntryTaskPreview"; | 19 | import EntryTaskPreview from "./components/EntryTaskPreview"; |
20 | import { OrderTaskApprovalType } from "./entity"; | 20 | import { OrderTaskApprovalType } from "./entity"; |
21 | import AdviserTaskPreview from "./components/AdviserTaskPreview"; | 21 | import AdviserTaskPreview from "./components/AdviserTaskPreview"; |
22 | import SeriesTaskPreview from "./components/SeriesTaskPreview"; | 22 | import SeriesTaskPreview from "./components/SeriesTaskPreview"; |
23 | import ApproveModal from "@/pages/order3/Common/ApproveModal"; | 23 | import ApproveModal from "@/pages/order3/Common/ApproveModal"; |
24 | - | ||
25 | -const { Column } = Table; | 24 | +import SaleTaskAutoAssign from "./components/SaleTaskAutoAssign"; |
25 | +import SaleTaskBatchSet from "./components/SaleTaskBatchSet"; | ||
26 | +import { ColumnsType } from "antd/es/table"; | ||
26 | 27 | ||
27 | export default () => ( | 28 | export default () => ( |
28 | <Provider> | 29 | <Provider> |
@@ -42,6 +43,9 @@ function SaleTaskList() { | @@ -42,6 +43,9 @@ function SaleTaskList() { | ||
42 | const [stpVisible, setStpVisible] = useState(false); | 43 | const [stpVisible, setStpVisible] = useState(false); |
43 | const [seriesTaskParams, setSeriesTaskParams] = useState({}); | 44 | const [seriesTaskParams, setSeriesTaskParams] = useState({}); |
44 | 45 | ||
46 | + const [autoVisible, setAutoVisible] = useState(false); | ||
47 | + const [batchVisible, setBatchVisible] = useState(false); | ||
48 | + | ||
45 | const { data, loading, setParams } = useInitial( | 49 | const { data, loading, setParams } = useInitial( |
46 | API.getSaleTaskApi, | 50 | API.getSaleTaskApi, |
47 | {} as API.GetSaleTaskApiRes, | 51 | {} as API.GetSaleTaskApiRes, |
@@ -71,6 +75,19 @@ function SaleTaskList() { | @@ -71,6 +75,19 @@ function SaleTaskList() { | ||
71 | }); | 75 | }); |
72 | }; | 76 | }; |
73 | 77 | ||
78 | + // 查看销顾任务 | ||
79 | + const goToAdviserPage = (record: API.ShopTaskItem) => { | ||
80 | + history.push({ | ||
81 | + pathname: "/order3/saleTask/edit", | ||
82 | + query: { | ||
83 | + readOnly: isReadOnly ? "1" : "0", | ||
84 | + shopId: String(record.shopId), | ||
85 | + taskDate: String(targetMonth.valueOf()), | ||
86 | + currTab: "2", | ||
87 | + }, | ||
88 | + }); | ||
89 | + }; | ||
90 | + | ||
74 | // 查看流程进度 | 91 | // 查看流程进度 |
75 | const viewProcess = () => { | 92 | const viewProcess = () => { |
76 | setApprove({ | 93 | setApprove({ |
@@ -128,30 +145,242 @@ function SaleTaskList() { | @@ -128,30 +145,242 @@ function SaleTaskList() { | ||
128 | setEtpVisible(true); | 145 | setEtpVisible(true); |
129 | }; | 146 | }; |
130 | 147 | ||
148 | + // 销顾任务 | ||
149 | + const showAdviserModal = ( | ||
150 | + record: API.TaskListItem, | ||
151 | + type: OrderTaskApprovalType | ||
152 | + ) => { | ||
153 | + const params: any = { | ||
154 | + id: data.id, | ||
155 | + taskId: record.id, | ||
156 | + orderTaskApprovalType: OrderTaskApprovalType.门店维度, // 只有门店有查看销顾任务 | ||
157 | + }; | ||
158 | + switch (type) { | ||
159 | + case OrderTaskApprovalType.门店维度: | ||
160 | + params.shopId = record.dataId; | ||
161 | + break; | ||
162 | + case OrderTaskApprovalType.销售顾问维度: | ||
163 | + params.staffId = record.dataId; | ||
164 | + break; | ||
165 | + case OrderTaskApprovalType.新车一级管理维度: | ||
166 | + params.firstManageId = record.dataId; | ||
167 | + break; | ||
168 | + case OrderTaskApprovalType.新车二级管理维度: | ||
169 | + params.secondManageId = record.dataId; | ||
170 | + break; | ||
171 | + case OrderTaskApprovalType.新车三级管理维度: | ||
172 | + params.thirdManageId = record.dataId; | ||
173 | + break; | ||
174 | + default: | ||
175 | + break; | ||
176 | + } | ||
177 | + setAdviserTaskParams(params); | ||
178 | + setAtpVisible(true); | ||
179 | + }; | ||
180 | + | ||
181 | + // 销顾任务--查看车系任务 | ||
182 | + const showSeriesModalByAdviser = (record: API.TaskListItem) => { | ||
183 | + const params = { ...adviserTaskParams } as any; | ||
184 | + params.taskId = record.id; | ||
185 | + params.orderTaskApprovalType = OrderTaskApprovalType.车系; | ||
186 | + params.staffId = record.dataId; | ||
187 | + setSeriesTaskParams(params); | ||
188 | + setStpVisible(true); | ||
189 | + }; | ||
190 | + | ||
191 | + // 车系任务 | ||
192 | + const showSeriesModal = ( | ||
193 | + record: API.TaskListItem, | ||
194 | + type: OrderTaskApprovalType | ||
195 | + ) => { | ||
196 | + const params: any = { | ||
197 | + id: data.id, | ||
198 | + taskId: record.id, | ||
199 | + orderTaskApprovalType: OrderTaskApprovalType.车系, | ||
200 | + }; | ||
201 | + switch (type) { | ||
202 | + case OrderTaskApprovalType.门店维度: | ||
203 | + params.shopId = record.dataId; | ||
204 | + break; | ||
205 | + case OrderTaskApprovalType.销售顾问维度: | ||
206 | + params.staffId = record.dataId; | ||
207 | + break; | ||
208 | + case OrderTaskApprovalType.新车一级管理维度: | ||
209 | + params.firstManageId = record.dataId; | ||
210 | + break; | ||
211 | + case OrderTaskApprovalType.新车二级管理维度: | ||
212 | + params.secondManageId = record.dataId; | ||
213 | + break; | ||
214 | + case OrderTaskApprovalType.新车三级管理维度: | ||
215 | + params.thirdManageId = record.dataId; | ||
216 | + break; | ||
217 | + default: | ||
218 | + break; | ||
219 | + } | ||
220 | + setSeriesTaskParams(params); | ||
221 | + setStpVisible(true); | ||
222 | + }; | ||
223 | + | ||
224 | + const handleAutoAssignRefresh = () => { | ||
225 | + setAutoVisible(false); | ||
226 | + setParams({}, true); | ||
227 | + }; | ||
228 | + const handleBatchSetRefresh = () => { | ||
229 | + setBatchVisible(false); | ||
230 | + setParams({}, true); | ||
231 | + }; | ||
232 | + | ||
233 | + const columns: ColumnsType<API.ShopTaskItem> = [ | ||
234 | + { | ||
235 | + title: "门店", | ||
236 | + width: 150, | ||
237 | + dataIndex: "shopName", | ||
238 | + }, | ||
239 | + { | ||
240 | + title: "零售任务(台)", | ||
241 | + children: [ | ||
242 | + { | ||
243 | + title: "合计", | ||
244 | + dataIndex: "taskCount", | ||
245 | + key: "taskCount", | ||
246 | + }, | ||
247 | + { | ||
248 | + title: "新能源车", | ||
249 | + dataIndex: "newEnergyTaskCount", | ||
250 | + key: "newEnergyTaskCount", | ||
251 | + }, | ||
252 | + { | ||
253 | + title: "传统燃油车", | ||
254 | + dataIndex: "fuelVehicleTaskCount", | ||
255 | + key: "fuelVehicleTaskCount", | ||
256 | + }, | ||
257 | + ], | ||
258 | + }, | ||
259 | + { | ||
260 | + title: "毛利任务(元)", | ||
261 | + children: [ | ||
262 | + { | ||
263 | + title: "合计", | ||
264 | + dataIndex: "grossProfitTaskTotal", | ||
265 | + key: "grossProfitTaskTotal", | ||
266 | + render: (text: string, record: API.ShopTaskItem) => { | ||
267 | + return (record.taskCount * record.vehicleGrossProfitTask).toFixed( | ||
268 | + 2 | ||
269 | + ); | ||
270 | + }, | ||
271 | + }, | ||
272 | + { | ||
273 | + title: "单车", | ||
274 | + dataIndex: "vehicleGrossProfitTask", | ||
275 | + key: "vehicleGrossProfitTask", | ||
276 | + }, | ||
277 | + ], | ||
278 | + }, | ||
279 | + { | ||
280 | + title: "线索到店成交(台)", | ||
281 | + width: 100, | ||
282 | + dataIndex: "clueDealTaskCount", | ||
283 | + key: "clueDealTaskCount", | ||
284 | + }, | ||
285 | + { | ||
286 | + title: "首客试驾成交(台)", | ||
287 | + width: 100, | ||
288 | + dataIndex: "testDriveTaskCount", | ||
289 | + key: "testDriveTaskCount", | ||
290 | + }, | ||
291 | + { | ||
292 | + title: "攻坚车任务(台)", | ||
293 | + width: 100, | ||
294 | + dataIndex: "tackCarTaskCount", | ||
295 | + key: "tackCarTaskCount", | ||
296 | + }, | ||
297 | + { | ||
298 | + title: "车系任务(台)", | ||
299 | + width: 100, | ||
300 | + dataIndex: "seriesTaskCount", | ||
301 | + key: "seriesTaskCount", | ||
302 | + }, | ||
303 | + { | ||
304 | + title: "销顾任务", | ||
305 | + render: (text: string, record: API.ShopTaskItem) => { | ||
306 | + return ( | ||
307 | + <a | ||
308 | + onClick={() => { | ||
309 | + goToAdviserPage(record); | ||
310 | + }} | ||
311 | + > | ||
312 | + 查看 | ||
313 | + </a> | ||
314 | + ); | ||
315 | + }, | ||
316 | + }, | ||
317 | + { | ||
318 | + title: "操作", | ||
319 | + render: (text: string, record: API.ShopTaskItem) => { | ||
320 | + return ( | ||
321 | + <a | ||
322 | + onClick={() => { | ||
323 | + goToEditPage(record); | ||
324 | + }} | ||
325 | + > | ||
326 | + {isReadOnly ? "查看" : "编辑"} | ||
327 | + </a> | ||
328 | + ); | ||
329 | + }, | ||
330 | + }, | ||
331 | + ]; | ||
332 | + | ||
131 | return ( | 333 | return ( |
132 | <PageHeaderWrapper title="零售任务分配"> | 334 | <PageHeaderWrapper title="零售任务分配"> |
133 | <Card> | 335 | <Card> |
134 | - <Row align="middle" justify="start" style={{ marginBottom: 20 }}> | ||
135 | - <DatePicker | ||
136 | - placeholder="月度" | ||
137 | - style={{ width: 260 }} | ||
138 | - picker="month" | ||
139 | - value={targetMonth} | ||
140 | - onChange={handleChangeMonth} | ||
141 | - allowClear={false} | ||
142 | - /> | ||
143 | - <Input.Search | ||
144 | - allowClear | ||
145 | - placeholder="门店名称" | ||
146 | - style={{ width: 260, marginLeft: 15 }} | ||
147 | - onSearch={(v) => { | ||
148 | - setParams({ shopName: v }, true); | ||
149 | - }} | ||
150 | - /> | 336 | + <Row |
337 | + align="middle" | ||
338 | + justify="space-between" | ||
339 | + style={{ marginBottom: 20 }} | ||
340 | + > | ||
341 | + <Row align="middle" justify="start" style={{ marginBottom: 20 }}> | ||
342 | + <DatePicker | ||
343 | + placeholder="月度" | ||
344 | + style={{ width: 260 }} | ||
345 | + picker="month" | ||
346 | + value={targetMonth} | ||
347 | + onChange={handleChangeMonth} | ||
348 | + allowClear={false} | ||
349 | + /> | ||
350 | + <Input.Search | ||
351 | + allowClear | ||
352 | + placeholder="门店名称" | ||
353 | + style={{ width: 260, marginLeft: 15 }} | ||
354 | + onSearch={(v) => { | ||
355 | + setParams({ shopName: v }, true); | ||
356 | + }} | ||
357 | + onBlur={(e) => { | ||
358 | + if (e.target.value.trim() === "") { | ||
359 | + setParams({ shopName: "" }, true); | ||
360 | + } | ||
361 | + }} | ||
362 | + /> | ||
363 | + </Row> | ||
364 | + {!isReadOnly && ( | ||
365 | + <Row align="middle" justify="start" style={{ marginBottom: 20 }}> | ||
366 | + <Button type="primary" onClick={() => setAutoVisible(true)}> | ||
367 | + 零售任务快捷分配 | ||
368 | + </Button> | ||
369 | + <Button | ||
370 | + type="primary" | ||
371 | + style={{ marginLeft: 10 }} | ||
372 | + onClick={() => setBatchVisible(true)} | ||
373 | + > | ||
374 | + 批量设置 | ||
375 | + </Button> | ||
376 | + </Row> | ||
377 | + )} | ||
151 | </Row> | 378 | </Row> |
152 | <Table | 379 | <Table |
153 | rowKey="id" | 380 | rowKey="id" |
381 | + bordered | ||
154 | loading={loading} | 382 | loading={loading} |
383 | + columns={columns} | ||
155 | dataSource={data.shopTaskList} | 384 | dataSource={data.shopTaskList} |
156 | pagination={false} | 385 | pagination={false} |
157 | scroll={{ y: 450 }} | 386 | scroll={{ y: 450 }} |
@@ -168,66 +397,39 @@ function SaleTaskList() { | @@ -168,66 +397,39 @@ function SaleTaskList() { | ||
168 | fontWeight: 500, | 397 | fontWeight: 500, |
169 | }} | 398 | }} |
170 | > | 399 | > |
171 | - <Table.Summary.Cell align="left" index={1}> | ||
172 | - 合计 | ||
173 | - </Table.Summary.Cell> | ||
174 | - <Table.Summary.Cell align="left" index={2}> | 400 | + <Table.Summary.Cell index={1}>合计</Table.Summary.Cell> |
401 | + <Table.Summary.Cell index={2}> | ||
175 | {data.totalTaskCount} | 402 | {data.totalTaskCount} |
176 | </Table.Summary.Cell> | 403 | </Table.Summary.Cell> |
177 | - <Table.Summary.Cell align="left" index={3}> | 404 | + <Table.Summary.Cell index={3}> |
178 | {data.newEnergyTaskCount} | 405 | {data.newEnergyTaskCount} |
179 | </Table.Summary.Cell> | 406 | </Table.Summary.Cell> |
180 | - <Table.Summary.Cell align="left" index={4}> | 407 | + <Table.Summary.Cell index={4}> |
181 | {data.fuelVehicleTaskCount} | 408 | {data.fuelVehicleTaskCount} |
182 | </Table.Summary.Cell> | 409 | </Table.Summary.Cell> |
183 | - <Table.Summary.Cell align="left" index={5}> | ||
184 | - {data.vehicleGrossProfitTask} | 410 | + <Table.Summary.Cell index={5}> |
411 | + {data.totalGrossProfitTask} | ||
185 | </Table.Summary.Cell> | 412 | </Table.Summary.Cell> |
186 | - <Table.Summary.Cell index={6}> | 413 | + <Table.Summary.Cell index={6}>-</Table.Summary.Cell> |
414 | + <Table.Summary.Cell index={7}> | ||
187 | {data.clueDealTaskCount} | 415 | {data.clueDealTaskCount} |
188 | </Table.Summary.Cell> | 416 | </Table.Summary.Cell> |
189 | - <Table.Summary.Cell index={7}> | 417 | + <Table.Summary.Cell index={8}> |
190 | {data.testDriveTaskCount} | 418 | {data.testDriveTaskCount} |
191 | </Table.Summary.Cell> | 419 | </Table.Summary.Cell> |
192 | - <Table.Summary.Cell index={8}> | 420 | + <Table.Summary.Cell index={9}> |
193 | {data.tackCarTaskCount} | 421 | {data.tackCarTaskCount} |
194 | </Table.Summary.Cell> | 422 | </Table.Summary.Cell> |
195 | - <Table.Summary.Cell index={9}> | 423 | + <Table.Summary.Cell index={10}> |
196 | {data.seriesTaskCount} | 424 | {data.seriesTaskCount} |
197 | </Table.Summary.Cell> | 425 | </Table.Summary.Cell> |
198 | - <Table.Summary.Cell index={10} /> | 426 | + <Table.Summary.Cell index={11} /> |
427 | + <Table.Summary.Cell index={12} /> | ||
199 | </Table.Summary.Row> | 428 | </Table.Summary.Row> |
200 | </Table.Summary> | 429 | </Table.Summary> |
201 | ); | 430 | ); |
202 | }} | 431 | }} |
203 | - > | ||
204 | - <Column title="门店" dataIndex="shopName" /> | ||
205 | - <Column title="零售任务(台)" dataIndex="taskCount" /> | ||
206 | - <Column title="新能源车任务(台)" dataIndex="newEnergyTaskCount" /> | ||
207 | - <Column title="传统燃油车任务(台)" dataIndex="fuelVehicleTaskCount" /> | ||
208 | - <Column title="车辆毛利任务(元)" dataIndex="vehicleGrossProfitTask" /> | ||
209 | - <Column title="线索到店零售台数(台)" dataIndex="clueDealTaskCount" /> | ||
210 | - <Column | ||
211 | - title="首客试驾成交任务数(台)" | ||
212 | - dataIndex="testDriveTaskCount" | ||
213 | - /> | ||
214 | - <Column title="攻坚车任务数(台)" dataIndex="tackCarTaskCount" /> | ||
215 | - <Column title="车系任务数(台)" dataIndex="seriesTaskCount" /> | ||
216 | - <Column | ||
217 | - title="操作" | ||
218 | - render={(text: string, record: API.ShopTaskItem) => { | ||
219 | - return ( | ||
220 | - <a | ||
221 | - onClick={() => { | ||
222 | - goToEditPage(record); | ||
223 | - }} | ||
224 | - > | ||
225 | - {isReadOnly ? "查看" : "编辑"} | ||
226 | - </a> | ||
227 | - ); | ||
228 | - }} | ||
229 | - /> | ||
230 | - </Table> | 432 | + /> |
231 | {data.revoke ? ( | 433 | {data.revoke ? ( |
232 | <Row align="middle" justify="center" style={{ marginTop: 50 }}> | 434 | <Row align="middle" justify="center" style={{ marginTop: 50 }}> |
233 | <Button onClick={cancelSaleTask}>撤销</Button> | 435 | <Button onClick={cancelSaleTask}>撤销</Button> |
@@ -271,82 +473,11 @@ function SaleTaskList() { | @@ -271,82 +473,11 @@ function SaleTaskList() { | ||
271 | destroyOnClose | 473 | destroyOnClose |
272 | footer={null} | 474 | footer={null} |
273 | > | 475 | > |
274 | - <Row align="middle" justify="start" style={{ marginBottom: 20 }}> | ||
275 | - <DatePicker | ||
276 | - placeholder="月度" | ||
277 | - style={{ width: 230 }} | ||
278 | - picker="month" | ||
279 | - value={targetMonth} | ||
280 | - allowClear={false} | ||
281 | - disabled | ||
282 | - /> | ||
283 | - {/* <Input.Search | ||
284 | - allowClear | ||
285 | - placeholder="门店名称" | ||
286 | - style={{ width: 263, marginLeft: 20 }} | ||
287 | - onSearch={(v) => { | ||
288 | - // todo setParams({ shopName: v }, true); | ||
289 | - }} | ||
290 | - /> */} | ||
291 | - </Row> | ||
292 | <EntryTaskPreview | 476 | <EntryTaskPreview |
477 | + month={targetMonth} | ||
293 | params={previewTaskParams} | 478 | params={previewTaskParams} |
294 | - showAdviserModal={(record, type) => { | ||
295 | - const params: any = { | ||
296 | - id: data.id, | ||
297 | - taskId: record.id, | ||
298 | - orderTaskApprovalType: OrderTaskApprovalType.门店维度, // 只有门店有查看销顾任务 | ||
299 | - }; | ||
300 | - switch (type) { | ||
301 | - case OrderTaskApprovalType.门店维度: | ||
302 | - params.shopId = record.dataId; | ||
303 | - break; | ||
304 | - case OrderTaskApprovalType.销售顾问维度: | ||
305 | - params.staffId = record.dataId; | ||
306 | - break; | ||
307 | - case OrderTaskApprovalType.新车一级管理维度: | ||
308 | - params.firstManageId = record.dataId; | ||
309 | - break; | ||
310 | - case OrderTaskApprovalType.新车二级管理维度: | ||
311 | - params.secondManageId = record.dataId; | ||
312 | - break; | ||
313 | - case OrderTaskApprovalType.新车三级管理维度: | ||
314 | - params.thirdManageId = record.dataId; | ||
315 | - break; | ||
316 | - default: | ||
317 | - break; | ||
318 | - } | ||
319 | - setAdviserTaskParams(params); | ||
320 | - setAtpVisible(true); | ||
321 | - }} | ||
322 | - showSeriesModal={(record, type) => { | ||
323 | - const params: any = { | ||
324 | - id: data.id, | ||
325 | - taskId: record.id, | ||
326 | - orderTaskApprovalType: OrderTaskApprovalType.车系, | ||
327 | - }; | ||
328 | - switch (type) { | ||
329 | - case OrderTaskApprovalType.门店维度: | ||
330 | - params.shopId = record.dataId; | ||
331 | - break; | ||
332 | - case OrderTaskApprovalType.销售顾问维度: | ||
333 | - params.staffId = record.dataId; | ||
334 | - break; | ||
335 | - case OrderTaskApprovalType.新车一级管理维度: | ||
336 | - params.firstManageId = record.dataId; | ||
337 | - break; | ||
338 | - case OrderTaskApprovalType.新车二级管理维度: | ||
339 | - params.secondManageId = record.dataId; | ||
340 | - break; | ||
341 | - case OrderTaskApprovalType.新车三级管理维度: | ||
342 | - params.thirdManageId = record.dataId; | ||
343 | - break; | ||
344 | - default: | ||
345 | - break; | ||
346 | - } | ||
347 | - setSeriesTaskParams(params); | ||
348 | - setStpVisible(true); | ||
349 | - }} | 479 | + showAdviserModal={showAdviserModal} |
480 | + showSeriesModal={showSeriesModal} | ||
350 | /> | 481 | /> |
351 | </Modal> | 482 | </Modal> |
352 | <Modal | 483 | <Modal |
@@ -359,14 +490,7 @@ function SaleTaskList() { | @@ -359,14 +490,7 @@ function SaleTaskList() { | ||
359 | > | 490 | > |
360 | <AdviserTaskPreview | 491 | <AdviserTaskPreview |
361 | params={adviserTaskParams} | 492 | params={adviserTaskParams} |
362 | - showSeriesModal={(record) => { | ||
363 | - const params = { ...adviserTaskParams } as any; | ||
364 | - params.taskId = record.id; | ||
365 | - params.orderTaskApprovalType = OrderTaskApprovalType.车系; | ||
366 | - params.staffId = record.dataId; | ||
367 | - setSeriesTaskParams(params); | ||
368 | - setStpVisible(true); | ||
369 | - }} | 493 | + showSeriesModal={showSeriesModalByAdviser} |
370 | /> | 494 | /> |
371 | </Modal> | 495 | </Modal> |
372 | <Modal | 496 | <Modal |
@@ -379,6 +503,37 @@ function SaleTaskList() { | @@ -379,6 +503,37 @@ function SaleTaskList() { | ||
379 | > | 503 | > |
380 | <SeriesTaskPreview params={seriesTaskParams} /> | 504 | <SeriesTaskPreview params={seriesTaskParams} /> |
381 | </Modal> | 505 | </Modal> |
506 | + <Modal | ||
507 | + width={800} | ||
508 | + title="零售任务快捷分配" | ||
509 | + open={autoVisible} | ||
510 | + onCancel={() => setAutoVisible(false)} | ||
511 | + destroyOnClose | ||
512 | + footer={null} | ||
513 | + maskClosable={false} | ||
514 | + > | ||
515 | + <SaleTaskAutoAssign | ||
516 | + id={data.id} | ||
517 | + value={data.shopTaskList} | ||
518 | + onCancel={() => setAutoVisible(false)} | ||
519 | + onRefresh={handleAutoAssignRefresh} | ||
520 | + /> | ||
521 | + </Modal> | ||
522 | + <Modal | ||
523 | + width={800} | ||
524 | + title="批量设置" | ||
525 | + open={batchVisible} | ||
526 | + onCancel={() => setBatchVisible(false)} | ||
527 | + destroyOnClose | ||
528 | + footer={null} | ||
529 | + maskClosable={false} | ||
530 | + > | ||
531 | + <SaleTaskBatchSet | ||
532 | + id={data.id} | ||
533 | + onCancel={() => setBatchVisible(false)} | ||
534 | + onRefresh={handleBatchSetRefresh} | ||
535 | + /> | ||
536 | + </Modal> | ||
382 | <ApprovalProgressModal | 537 | <ApprovalProgressModal |
383 | visible={approve.visible} | 538 | visible={approve.visible} |
384 | orderNo={approve.orderNo} | 539 | orderNo={approve.orderNo} |
src/pages/order3/SaleTask/subpages/TaskEdit/components/AdviserTask.tsx
@@ -5,8 +5,7 @@ import * as API from "../../../api"; | @@ -5,8 +5,7 @@ import * as API from "../../../api"; | ||
5 | import AdviserTaskEdit from "./AdviserTaskEdit"; | 5 | import AdviserTaskEdit from "./AdviserTaskEdit"; |
6 | import { history, useRequest } from "umi"; | 6 | import { history, useRequest } from "umi"; |
7 | import { cloneDeep } from "lodash"; | 7 | import { cloneDeep } from "lodash"; |
8 | - | ||
9 | -const { Column } = Table; | 8 | +import { ColumnsType } from "antd/es/table"; |
10 | 9 | ||
11 | interface AdviserTaskEditForm { | 10 | interface AdviserTaskEditForm { |
12 | submit: (callback: (data: any) => void) => void; | 11 | submit: (callback: (data: any) => void) => void; |
@@ -14,9 +13,10 @@ interface AdviserTaskEditForm { | @@ -14,9 +13,10 @@ interface AdviserTaskEditForm { | ||
14 | 13 | ||
15 | interface AdviserTaskProps { | 14 | interface AdviserTaskProps { |
16 | form: any; | 15 | form: any; |
16 | + onRefresh: () => void; | ||
17 | } | 17 | } |
18 | 18 | ||
19 | -export default function AdviserTask({ form }: AdviserTaskProps) { | 19 | +export default function AdviserTask({ form, onRefresh }: AdviserTaskProps) { |
20 | const adviserTaskEditRef = useRef<AdviserTaskEditForm>(null); | 20 | const adviserTaskEditRef = useRef<AdviserTaskEditForm>(null); |
21 | const { | 21 | const { |
22 | shopTaskItem, | 22 | shopTaskItem, |
@@ -48,6 +48,10 @@ export default function AdviserTask({ form }: AdviserTaskProps) { | @@ -48,6 +48,10 @@ export default function AdviserTask({ form }: AdviserTaskProps) { | ||
48 | 48 | ||
49 | // 前端更新编辑后的顾问分配任务 | 49 | // 前端更新编辑后的顾问分配任务 |
50 | const onOk = () => { | 50 | const onOk = () => { |
51 | + if (isReadOnly) { | ||
52 | + setVisible(false); | ||
53 | + return; | ||
54 | + } | ||
51 | adviserTaskEditRef.current?.submit((newTask) => { | 55 | adviserTaskEditRef.current?.submit((newTask) => { |
52 | setShopTaskItem(newTask); | 56 | setShopTaskItem(newTask); |
53 | setAdvisersFiltered(newTask?.staffTaskList); // 刷新列表 | 57 | setAdvisersFiltered(newTask?.staffTaskList); // 刷新列表 |
@@ -66,12 +70,96 @@ export default function AdviserTask({ form }: AdviserTaskProps) { | @@ -66,12 +70,96 @@ export default function AdviserTask({ form }: AdviserTaskProps) { | ||
66 | .run({ ...other, id: taskId, ...shopFormValue }) | 70 | .run({ ...other, id: taskId, ...shopFormValue }) |
67 | .then(() => { | 71 | .then(() => { |
68 | message.success("保存草稿成功"); | 72 | message.success("保存草稿成功"); |
73 | + onRefresh(); | ||
69 | }) | 74 | }) |
70 | .catch((error: any) => { | 75 | .catch((error: any) => { |
71 | message.error(error.message ?? "请求失败"); | 76 | message.error(error.message ?? "请求失败"); |
72 | }); | 77 | }); |
73 | }; | 78 | }; |
74 | 79 | ||
80 | + const columns: ColumnsType<API.StaffTaskItem> = [ | ||
81 | + { | ||
82 | + title: "销售顾问", | ||
83 | + width: 100, | ||
84 | + dataIndex: "staffName", | ||
85 | + }, | ||
86 | + { | ||
87 | + title: "零售任务(台)", | ||
88 | + children: [ | ||
89 | + { | ||
90 | + title: "合计", | ||
91 | + dataIndex: "taskCount", | ||
92 | + key: "taskCount", | ||
93 | + }, | ||
94 | + { | ||
95 | + title: "新能源车", | ||
96 | + dataIndex: "newEnergyTaskCount", | ||
97 | + key: "newEnergyTaskCount", | ||
98 | + }, | ||
99 | + { | ||
100 | + title: "传统燃油车", | ||
101 | + dataIndex: "fuelVehicleTaskCount", | ||
102 | + key: "fuelVehicleTaskCount", | ||
103 | + }, | ||
104 | + ], | ||
105 | + }, | ||
106 | + { | ||
107 | + title: "毛利任务(元)", | ||
108 | + children: [ | ||
109 | + { | ||
110 | + title: "合计", | ||
111 | + dataIndex: "grossProfitTaskTotal", | ||
112 | + key: "grossProfitTaskTotal", | ||
113 | + render: (text: string, record: API.StaffTaskItem) => { | ||
114 | + return (record.taskCount * record.vehicleGrossProfitTask).toFixed( | ||
115 | + 2 | ||
116 | + ); | ||
117 | + }, | ||
118 | + }, | ||
119 | + { | ||
120 | + title: "单车", | ||
121 | + dataIndex: "vehicleGrossProfitTask", | ||
122 | + key: "vehicleGrossProfitTask", | ||
123 | + }, | ||
124 | + ], | ||
125 | + }, | ||
126 | + { | ||
127 | + title: "线索到店成交(台)", | ||
128 | + width: 100, | ||
129 | + dataIndex: "clueDealTaskCount", | ||
130 | + }, | ||
131 | + { | ||
132 | + title: "首客试驾成交(台)", | ||
133 | + width: 100, | ||
134 | + dataIndex: "testDriveTaskCount", | ||
135 | + }, | ||
136 | + { | ||
137 | + title: "攻坚车任务(台)", | ||
138 | + width: 100, | ||
139 | + dataIndex: "tackCarTaskCount", | ||
140 | + }, | ||
141 | + { | ||
142 | + title: "车系任务(台)", | ||
143 | + width: 100, | ||
144 | + dataIndex: "seriesTaskCount", | ||
145 | + }, | ||
146 | + { | ||
147 | + title: "操作", | ||
148 | + render: (text: string, record: API.StaffTaskItem) => { | ||
149 | + return ( | ||
150 | + <a | ||
151 | + onClick={() => { | ||
152 | + setEditAdviser(cloneDeep(record)); // 注意对象引用 | ||
153 | + setVisible(true); | ||
154 | + }} | ||
155 | + > | ||
156 | + {isReadOnly ? "查看" : "编辑"} | ||
157 | + </a> | ||
158 | + ); | ||
159 | + }, | ||
160 | + }, | ||
161 | + ]; | ||
162 | + | ||
75 | return ( | 163 | return ( |
76 | <> | 164 | <> |
77 | <Row | 165 | <Row |
@@ -87,39 +175,13 @@ export default function AdviserTask({ form }: AdviserTaskProps) { | @@ -87,39 +175,13 @@ export default function AdviserTask({ form }: AdviserTaskProps) { | ||
87 | /> | 175 | /> |
88 | </Row> | 176 | </Row> |
89 | <Table | 177 | <Table |
178 | + bordered | ||
179 | + columns={columns} | ||
90 | dataSource={[...advisersFiltered]} | 180 | dataSource={[...advisersFiltered]} |
91 | pagination={false} | 181 | pagination={false} |
92 | rowKey="id" | 182 | rowKey="id" |
93 | loading={false} | 183 | loading={false} |
94 | - > | ||
95 | - <Column title="销售顾问" dataIndex="staffName" width={100} /> | ||
96 | - <Column title="零售任务(台)" dataIndex="taskCount" /> | ||
97 | - <Column title="新能源车任务(台)" dataIndex="newEnergyTaskCount" /> | ||
98 | - <Column title="传统燃油车任务(台)" dataIndex="fuelVehicleTaskCount" /> | ||
99 | - <Column title="车辆毛利任务(元)" dataIndex="vehicleGrossProfitTask" /> | ||
100 | - <Column title="线索到店零售台数(台)" dataIndex="clueDealTaskCount" /> | ||
101 | - <Column title="攻坚车任务数(台)" dataIndex="tackCarTaskCount" /> | ||
102 | - <Column title="首客试驾成交任务数(台)" dataIndex="testDriveTaskCount" /> | ||
103 | - <Column title="车系任务数(台)" dataIndex="seriesTaskCount" /> | ||
104 | - {!isReadOnly && ( | ||
105 | - <Column | ||
106 | - title="操作" | ||
107 | - width={100} | ||
108 | - render={(text: string, record: API.StaffTaskItem) => { | ||
109 | - return ( | ||
110 | - <a | ||
111 | - onClick={() => { | ||
112 | - setEditAdviser(cloneDeep(record)); // 注意对象引用 | ||
113 | - setVisible(true); | ||
114 | - }} | ||
115 | - > | ||
116 | - 编辑 | ||
117 | - </a> | ||
118 | - ); | ||
119 | - }} | ||
120 | - /> | ||
121 | - )} | ||
122 | - </Table> | 184 | + /> |
123 | <Row align="middle" justify="center" style={{ marginTop: 50 }}> | 185 | <Row align="middle" justify="center" style={{ marginTop: 50 }}> |
124 | <Button onClick={handleGoBack}>返回列表</Button> | 186 | <Button onClick={handleGoBack}>返回列表</Button> |
125 | {!isReadOnly && ( | 187 | {!isReadOnly && ( |
src/pages/order3/SaleTask/subpages/TaskEdit/components/AdviserTaskEdit.tsx
@@ -124,6 +124,7 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | @@ -124,6 +124,7 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | ||
124 | form={form} | 124 | form={form} |
125 | initialValues={editAdviser} | 125 | initialValues={editAdviser} |
126 | onFinish={onFinish} | 126 | onFinish={onFinish} |
127 | + disabled={isReadOnly} | ||
127 | > | 128 | > |
128 | <Form.Item name="taskCount" label="零售任务:" required> | 129 | <Form.Item name="taskCount" label="零售任务:" required> |
129 | <InputNumber | 130 | <InputNumber |
@@ -132,7 +133,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | @@ -132,7 +133,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | ||
132 | min={0} | 133 | min={0} |
133 | max={MAX_NUM} | 134 | max={MAX_NUM} |
134 | style={{ width: "100%" }} | 135 | style={{ width: "100%" }} |
135 | - disabled={isReadOnly} | ||
136 | precision={0} | 136 | precision={0} |
137 | /> | 137 | /> |
138 | </Form.Item> | 138 | </Form.Item> |
@@ -164,7 +164,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | @@ -164,7 +164,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | ||
164 | min={0} | 164 | min={0} |
165 | max={MAX_NUM} | 165 | max={MAX_NUM} |
166 | style={{ width: "100%" }} | 166 | style={{ width: "100%" }} |
167 | - disabled={isReadOnly} | ||
168 | precision={2} | 167 | precision={2} |
169 | /> | 168 | /> |
170 | </Form.Item> | 169 | </Form.Item> |
@@ -204,7 +203,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | @@ -204,7 +203,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | ||
204 | min={0} | 203 | min={0} |
205 | max={MAX_NUM} | 204 | max={MAX_NUM} |
206 | style={{ width: "100%" }} | 205 | style={{ width: "100%" }} |
207 | - disabled={isReadOnly} | ||
208 | precision={0} | 206 | precision={0} |
209 | /> | 207 | /> |
210 | </Form.Item> | 208 | </Form.Item> |
@@ -237,13 +235,12 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | @@ -237,13 +235,12 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | ||
237 | min={0} | 235 | min={0} |
238 | max={MAX_NUM} | 236 | max={MAX_NUM} |
239 | style={{ width: "100%" }} | 237 | style={{ width: "100%" }} |
240 | - disabled={isReadOnly} | ||
241 | precision={0} | 238 | precision={0} |
242 | /> | 239 | /> |
243 | </Form.Item> | 240 | </Form.Item> |
244 | <Form.Item | 241 | <Form.Item |
245 | name="vehicleGrossProfitTask" | 242 | name="vehicleGrossProfitTask" |
246 | - label="车辆毛利任务:" | 243 | + label="单车毛利任务:" |
247 | required | 244 | required |
248 | > | 245 | > |
249 | <InputNumber | 246 | <InputNumber |
@@ -252,7 +249,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | @@ -252,7 +249,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | ||
252 | min={0} | 249 | min={0} |
253 | max={MAX_NUM} | 250 | max={MAX_NUM} |
254 | style={{ width: "100%" }} | 251 | style={{ width: "100%" }} |
255 | - disabled={isReadOnly} | ||
256 | precision={2} | 252 | precision={2} |
257 | /> | 253 | /> |
258 | </Form.Item> | 254 | </Form.Item> |
@@ -267,7 +263,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | @@ -267,7 +263,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | ||
267 | min={0} | 263 | min={0} |
268 | max={MAX_NUM} | 264 | max={MAX_NUM} |
269 | style={{ width: "100%" }} | 265 | style={{ width: "100%" }} |
270 | - disabled={isReadOnly} | ||
271 | precision={0} | 266 | precision={0} |
272 | /> | 267 | /> |
273 | </Form.Item> | 268 | </Form.Item> |
@@ -278,7 +273,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | @@ -278,7 +273,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) { | ||
278 | min={0} | 273 | min={0} |
279 | max={MAX_NUM} | 274 | max={MAX_NUM} |
280 | style={{ width: "100%" }} | 275 | style={{ width: "100%" }} |
281 | - disabled={isReadOnly} | ||
282 | precision={0} | 276 | precision={0} |
283 | /> | 277 | /> |
284 | </Form.Item> | 278 | </Form.Item> |
src/pages/order3/SaleTask/subpages/TaskEdit/components/ShopTask.tsx
@@ -8,6 +8,7 @@ import { | @@ -8,6 +8,7 @@ import { | ||
8 | Button, | 8 | Button, |
9 | Row, | 9 | Row, |
10 | Tag, | 10 | Tag, |
11 | + Modal, | ||
11 | } from "antd"; | 12 | } from "antd"; |
12 | import { PlusOutlined } from "@ant-design/icons"; | 13 | import { PlusOutlined } from "@ant-design/icons"; |
13 | import * as API from "../../../api"; | 14 | import * as API from "../../../api"; |
@@ -18,14 +19,16 @@ import EditableCell from "./EditableCell"; | @@ -18,14 +19,16 @@ import EditableCell from "./EditableCell"; | ||
18 | import SeriesModal from "./SeriesModal"; | 19 | import SeriesModal from "./SeriesModal"; |
19 | import { history, useRequest } from "umi"; | 20 | import { history, useRequest } from "umi"; |
20 | import { MAX_NUM } from "../../../entity"; | 21 | import { MAX_NUM } from "../../../entity"; |
22 | +import "../../../components/index.less"; | ||
21 | 23 | ||
22 | const { Column } = Table; | 24 | const { Column } = Table; |
23 | 25 | ||
24 | interface ShopTaskProps { | 26 | interface ShopTaskProps { |
25 | form: any; | 27 | form: any; |
28 | + onRefresh: () => void; | ||
26 | } | 29 | } |
27 | 30 | ||
28 | -export default function ShopTask({ form }: ShopTaskProps) { | 31 | +export default function ShopTask({ form, onRefresh }: ShopTaskProps) { |
29 | const { | 32 | const { |
30 | shopTaskItem, | 33 | shopTaskItem, |
31 | isReadOnly, | 34 | isReadOnly, |
@@ -96,12 +99,49 @@ export default function ShopTask({ form }: ShopTaskProps) { | @@ -96,12 +99,49 @@ export default function ShopTask({ form }: ShopTaskProps) { | ||
96 | .run({ ...other, ...values, id: taskId }) | 99 | .run({ ...other, ...values, id: taskId }) |
97 | .then(() => { | 100 | .then(() => { |
98 | message.success("保存草稿成功"); | 101 | message.success("保存草稿成功"); |
102 | + onRefresh(); | ||
99 | }) | 103 | }) |
100 | .catch((error: any) => { | 104 | .catch((error: any) => { |
101 | message.error(error.message ?? "请求失败"); | 105 | message.error(error.message ?? "请求失败"); |
102 | }); | 106 | }); |
103 | }; | 107 | }; |
104 | 108 | ||
109 | + // 分配到门店和顾问 | ||
110 | + const autoAssignOneShop = async () => { | ||
111 | + await form.validateFields(); | ||
112 | + const values = form.getFieldsValue(); | ||
113 | + Modal.confirm({ | ||
114 | + title: ( | ||
115 | + <span> | ||
116 | + 确认分配到 | ||
117 | + <span className="tip">此门店和顾问</span> | ||
118 | + 吗? | ||
119 | + </span> | ||
120 | + ), | ||
121 | + zIndex: 1002, | ||
122 | + onOk: async () => { | ||
123 | + const hide = message.loading("分配中,请稍候", 0); | ||
124 | + const { taskId, id, ...other } = shopTaskItem!; | ||
125 | + API.autoAssignOneShop({ | ||
126 | + ...other, | ||
127 | + ...values, | ||
128 | + orderTaskApplyId: taskId, | ||
129 | + orderShopTaskId: id, | ||
130 | + }) | ||
131 | + .then((res) => { | ||
132 | + message.success("分配成功"); | ||
133 | + onRefresh(); | ||
134 | + }) | ||
135 | + .catch((error: any) => { | ||
136 | + message.error(error.message ?? "请求失败"); | ||
137 | + }) | ||
138 | + .finally(() => { | ||
139 | + hide(); | ||
140 | + }); | ||
141 | + }, | ||
142 | + }); | ||
143 | + }; | ||
144 | + | ||
105 | const layout = { | 145 | const layout = { |
106 | labelCol: { span: 5 }, | 146 | labelCol: { span: 5 }, |
107 | wrapperCol: { span: 19 }, | 147 | wrapperCol: { span: 19 }, |
@@ -121,6 +161,7 @@ export default function ShopTask({ form }: ShopTaskProps) { | @@ -121,6 +161,7 @@ export default function ShopTask({ form }: ShopTaskProps) { | ||
121 | labelAlign="left" | 161 | labelAlign="left" |
122 | form={form} | 162 | form={form} |
123 | initialValues={shopTaskItem!} | 163 | initialValues={shopTaskItem!} |
164 | + disabled={isReadOnly} | ||
124 | > | 165 | > |
125 | <Form.Item name="taskCount" label="零售任务:" required> | 166 | <Form.Item name="taskCount" label="零售任务:" required> |
126 | <InputNumber | 167 | <InputNumber |
@@ -129,7 +170,6 @@ export default function ShopTask({ form }: ShopTaskProps) { | @@ -129,7 +170,6 @@ export default function ShopTask({ form }: ShopTaskProps) { | ||
129 | min={0} | 170 | min={0} |
130 | max={MAX_NUM} | 171 | max={MAX_NUM} |
131 | style={{ width: "100%" }} | 172 | style={{ width: "100%" }} |
132 | - disabled={isReadOnly} | ||
133 | precision={0} | 173 | precision={0} |
134 | /> | 174 | /> |
135 | </Form.Item> | 175 | </Form.Item> |
@@ -161,7 +201,6 @@ export default function ShopTask({ form }: ShopTaskProps) { | @@ -161,7 +201,6 @@ export default function ShopTask({ form }: ShopTaskProps) { | ||
161 | min={0} | 201 | min={0} |
162 | max={MAX_NUM} | 202 | max={MAX_NUM} |
163 | style={{ width: "100%" }} | 203 | style={{ width: "100%" }} |
164 | - disabled={isReadOnly} | ||
165 | precision={2} | 204 | precision={2} |
166 | /> | 205 | /> |
167 | </Form.Item> | 206 | </Form.Item> |
@@ -201,7 +240,6 @@ export default function ShopTask({ form }: ShopTaskProps) { | @@ -201,7 +240,6 @@ export default function ShopTask({ form }: ShopTaskProps) { | ||
201 | min={0} | 240 | min={0} |
202 | max={MAX_NUM} | 241 | max={MAX_NUM} |
203 | style={{ width: "100%" }} | 242 | style={{ width: "100%" }} |
204 | - disabled={isReadOnly} | ||
205 | precision={0} | 243 | precision={0} |
206 | /> | 244 | /> |
207 | </Form.Item> | 245 | </Form.Item> |
@@ -235,13 +273,12 @@ export default function ShopTask({ form }: ShopTaskProps) { | @@ -235,13 +273,12 @@ export default function ShopTask({ form }: ShopTaskProps) { | ||
235 | min={0} | 273 | min={0} |
236 | max={MAX_NUM} | 274 | max={MAX_NUM} |
237 | style={{ width: "100%" }} | 275 | style={{ width: "100%" }} |
238 | - disabled={isReadOnly} | ||
239 | precision={0} | 276 | precision={0} |
240 | /> | 277 | /> |
241 | </Form.Item> | 278 | </Form.Item> |
242 | <Form.Item | 279 | <Form.Item |
243 | name="vehicleGrossProfitTask" | 280 | name="vehicleGrossProfitTask" |
244 | - label="车辆毛利任务:" | 281 | + label="单车毛利任务:" |
245 | required | 282 | required |
246 | > | 283 | > |
247 | <InputNumber | 284 | <InputNumber |
@@ -250,7 +287,6 @@ export default function ShopTask({ form }: ShopTaskProps) { | @@ -250,7 +287,6 @@ export default function ShopTask({ form }: ShopTaskProps) { | ||
250 | min={0} | 287 | min={0} |
251 | max={MAX_NUM} | 288 | max={MAX_NUM} |
252 | style={{ width: "100%" }} | 289 | style={{ width: "100%" }} |
253 | - disabled={isReadOnly} | ||
254 | precision={2} | 290 | precision={2} |
255 | /> | 291 | /> |
256 | </Form.Item> | 292 | </Form.Item> |
@@ -265,7 +301,6 @@ export default function ShopTask({ form }: ShopTaskProps) { | @@ -265,7 +301,6 @@ export default function ShopTask({ form }: ShopTaskProps) { | ||
265 | min={0} | 301 | min={0} |
266 | max={MAX_NUM} | 302 | max={MAX_NUM} |
267 | style={{ width: "100%" }} | 303 | style={{ width: "100%" }} |
268 | - disabled={isReadOnly} | ||
269 | precision={0} | 304 | precision={0} |
270 | /> | 305 | /> |
271 | </Form.Item> | 306 | </Form.Item> |
@@ -276,7 +311,6 @@ export default function ShopTask({ form }: ShopTaskProps) { | @@ -276,7 +311,6 @@ export default function ShopTask({ form }: ShopTaskProps) { | ||
276 | min={0} | 311 | min={0} |
277 | max={MAX_NUM} | 312 | max={MAX_NUM} |
278 | style={{ width: "100%" }} | 313 | style={{ width: "100%" }} |
279 | - disabled={isReadOnly} | ||
280 | precision={0} | 314 | precision={0} |
281 | /> | 315 | /> |
282 | </Form.Item> | 316 | </Form.Item> |
@@ -405,6 +439,15 @@ export default function ShopTask({ form }: ShopTaskProps) { | @@ -405,6 +439,15 @@ export default function ShopTask({ form }: ShopTaskProps) { | ||
405 | 保存草稿 | 439 | 保存草稿 |
406 | </Button> | 440 | </Button> |
407 | )} | 441 | )} |
442 | + {!isReadOnly && ( | ||
443 | + <Button | ||
444 | + type="primary" | ||
445 | + style={{ marginLeft: 10 }} | ||
446 | + onClick={autoAssignOneShop} | ||
447 | + > | ||
448 | + 分配到门店和顾问 | ||
449 | + </Button> | ||
450 | + )} | ||
408 | </Row> | 451 | </Row> |
409 | <SeriesModal | 452 | <SeriesModal |
410 | visible={seriesVisible} | 453 | visible={seriesVisible} |
src/pages/order3/SaleTask/subpages/TaskEdit/index.tsx
@@ -20,19 +20,19 @@ function TaskEdit() { | @@ -20,19 +20,19 @@ function TaskEdit() { | ||
20 | const readOnly = querys?.readOnly === "1"; | 20 | const readOnly = querys?.readOnly === "1"; |
21 | const shopId = querys?.shopId; | 21 | const shopId = querys?.shopId; |
22 | const taskDate = querys?.taskDate; | 22 | const taskDate = querys?.taskDate; |
23 | + const queryTab = querys?.currTab; | ||
23 | const { shopTaskItem, setShopTaskItem, setIsReadOnly } = useStore(); | 24 | const { shopTaskItem, setShopTaskItem, setIsReadOnly } = useStore(); |
24 | - const [currStep, setCurrStep] = useState("1"); | 25 | + const [currTab, setCurrTab] = useState(queryTab ?? "1"); |
25 | const [shopTaskForm] = Form.useForm(); | 26 | const [shopTaskForm] = Form.useForm(); |
26 | 27 | ||
27 | // 获取门店零售任务详情 | 28 | // 获取门店零售任务详情 |
28 | - const { data } = useInitial<API.ShopTaskItem, API.GetShopSaleTaskReq>( | ||
29 | - API.getShopSaleTask, | ||
30 | - {} as API.ShopTaskItem, | ||
31 | - { | ||
32 | - shopId, | ||
33 | - taskDate, | ||
34 | - } | ||
35 | - ); | 29 | + const { data, setParams } = useInitial< |
30 | + API.ShopTaskItem, | ||
31 | + API.GetShopSaleTaskReq | ||
32 | + >(API.getShopSaleTask, {} as API.ShopTaskItem, { | ||
33 | + shopId, | ||
34 | + taskDate, | ||
35 | + }); | ||
36 | 36 | ||
37 | useEffect(() => { | 37 | useEffect(() => { |
38 | setShopTaskItem(data); | 38 | setShopTaskItem(data); |
@@ -46,21 +46,27 @@ function TaskEdit() { | @@ -46,21 +46,27 @@ function TaskEdit() { | ||
46 | title={<span>当前选择门店:{shopTaskItem?.shopName}</span>} | 46 | title={<span>当前选择门店:{shopTaskItem?.shopName}</span>} |
47 | > | 47 | > |
48 | <Tabs | 48 | <Tabs |
49 | - defaultActiveKey={currStep} | ||
50 | - onChange={(activeKey) => setCurrStep(activeKey)} | 49 | + defaultActiveKey={currTab} |
50 | + onChange={(activeKey) => setCurrTab(activeKey)} | ||
51 | items={[ | 51 | items={[ |
52 | { | 52 | { |
53 | label: `门店任务分配${readOnly ? "详情" : ""}`, | 53 | label: `门店任务分配${readOnly ? "详情" : ""}`, |
54 | key: "1", | 54 | key: "1", |
55 | children: !isEmpty(shopTaskItem) && ( | 55 | children: !isEmpty(shopTaskItem) && ( |
56 | - <ShopTask form={shopTaskForm} /> | 56 | + <ShopTask |
57 | + form={shopTaskForm} | ||
58 | + onRefresh={() => setParams({}, true)} | ||
59 | + /> | ||
57 | ), | 60 | ), |
58 | }, | 61 | }, |
59 | { | 62 | { |
60 | label: `销售顾问任务分配${readOnly ? "详情" : ""}`, | 63 | label: `销售顾问任务分配${readOnly ? "详情" : ""}`, |
61 | key: "2", | 64 | key: "2", |
62 | children: !isEmpty(shopTaskItem) && ( | 65 | children: !isEmpty(shopTaskItem) && ( |
63 | - <AdviserTask form={shopTaskForm} /> | 66 | + <AdviserTask |
67 | + form={shopTaskForm} | ||
68 | + onRefresh={() => setParams({}, true)} | ||
69 | + /> | ||
64 | ), | 70 | ), |
65 | }, | 71 | }, |
66 | ]} | 72 | ]} |