Commit 56c332ee5fd987e711687040955bdae2d97efdb8

Authored by 莫红玲
2 parents a73d2b93 75f10597

Merge branch 'Shinner-sale-task-batch-set' into 'master'

零售任务分配优化功能



See merge request !264
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 &quot;mobx-react-lite&quot;; @@ -4,8 +4,7 @@ import { observer } from &quot;mobx-react-lite&quot;;
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
  1 +.editable-row:hover .editable-cell-value-wrap {
  2 + border: 1px solid #d9d9d9;
  3 +}
  4 +.editable-cell-value-wrap {
  5 + border: 1px solid transparent;
  6 + cursor: pointer;
  7 +}
  8 +.tip {
  9 + color: #ff4d4f;
  10 + font-weight: bold;
  11 +}
src/pages/order3/SaleTask/index.tsx
@@ -15,14 +15,15 @@ import { history } from &quot;umi&quot;; @@ -15,14 +15,15 @@ import { history } from &quot;umi&quot;;
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 &quot;../../../api&quot;; @@ -5,8 +5,7 @@ import * as API from &quot;../../../api&quot;;
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 &quot;./EditableCell&quot;; @@ -18,14 +19,16 @@ import EditableCell from &quot;./EditableCell&quot;;
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 ]}