Merged
Merge Request #264 · created by 徐欣


零售任务分配优化功能


From Shinner-sale-task-batch-set into master

Merged by 莫红玲

Source branch has been removed
2 participants
src/pages/order3/SaleTask/api.ts
... ... @@ -25,6 +25,7 @@ export interface GetSaleTaskApiRes {
25 25 testDriveTaskCount: number;
26 26 seriesTaskCount: number;
27 27 vehicleGrossProfitTask: number;
  28 + totalGrossProfitTask: number;
28 29 }
29 30  
30 31 export interface SeriesTaskItem {
... ... @@ -71,6 +72,8 @@ export interface ShopTaskItem {
71 72 seriesTaskCount: number;
72 73 vehicleGrossProfitTask: number;
73 74 taskId?: number;
  75 + orderTaskApplyId?: number;
  76 + orderShopTaskId?: number;
74 77 }
75 78  
76 79 /** 月度零售任务列表 */
... ... @@ -81,8 +84,8 @@ export function getSaleTaskApi(
81 84 }
82 85  
83 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 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 163 export interface BrandItem {
113 164 id: number;
114 165 initial: string;
... ... @@ -148,9 +199,10 @@ export interface PreviewTaskReq {
148 199 secondManageId?: number;
149 200 thirdManageId?: number;
150 201 taskId?: number;
151   - orderTaskApprovalType: number;
152   - id: number;
  202 + orderTaskApprovalType?: number;
  203 + id?: number;
153 204 token?: string;
  205 + keywords?: string;
154 206 }
155 207  
156 208 export interface TaskListItem {
... ... @@ -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 246 return request.get(`${ORDER3_HOST}/erp/sales/task/approve/info`, {
193 247 params,
194 248 });
... ...
src/pages/order3/SaleTask/components/AdviserTaskPreview.tsx
... ... @@ -4,8 +4,7 @@ import { observer } from &quot;mobx-react-lite&quot;;
4 4 import * as API from "../api";
5 5 import useInitial from "@/hooks/useInitail";
6 6 import ModifiedTableCell from "./ModifiedTableCell";
7   -
8   -const { Column } = Table;
  7 +import { ColumnsType } from "antd/es/table";
9 8  
10 9 // 查看销顾任务弹框
11 10 interface AdviserTaskPreviewProps {
... ... @@ -31,73 +30,100 @@ const AdviserTaskPreview = ({
31 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 117 return (
35 118 <Table
  119 + bordered
36 120 rowKey="dataId"
37 121 loading={loading}
  122 + columns={columns}
38 123 dataSource={data.taskList}
39 124 pagination={false}
40 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 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 11 import { observer } from "mobx-react-lite";
4 12 import * as API from "../api";
5 13 import { OrderTaskApprovalType } from "../entity";
6 14 import useInitial from "@/hooks/useInitail";
7 15 import ModifiedTableCell from "./ModifiedTableCell";
  16 +import { ColumnsType } from "antd/es/table";
  17 +import { Moment } from "moment";
8 18  
9   -const { Column } = Table;
10 19 const RadioButton = Radio.Button;
11 20 const RadioGroup = Radio.Group;
12 21  
13 22 // 预览任务入口弹框
14 23 interface EntryTaskPreviewProps {
  24 + month: Moment;
15 25 params: any; // API.PreviewTaskReq
16 26 showAdviserModal: (
17 27 record: API.TaskListItem,
... ... @@ -24,11 +34,13 @@ interface EntryTaskPreviewProps {
24 34 }
25 35  
26 36 const EntryTaskPreview = ({
  37 + month,
27 38 params,
28 39 showAdviserModal,
29 40 showSeriesModal,
30 41 }: EntryTaskPreviewProps) => {
31 42 const [type, setType] = useState(OrderTaskApprovalType.门店维度);
  43 + const [keywords, setKeywords] = useState("");
32 44  
33 45 const { data, loading, setParams } = useInitial<
34 46 API.PreviewTaskRes,
... ... @@ -40,15 +52,13 @@ const EntryTaskPreview = ({
40 52 if (value === 99) {
41 53 setType(OrderTaskApprovalType.新车一级管理维度);
42 54 setParams(
43   - // @ts-ignore
44 55 { orderTaskApprovalType: OrderTaskApprovalType.新车一级管理维度 },
45 56 true
46 57 );
47 58 return;
48 59 }
49 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 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 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 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 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 15 import moment, { Moment } from "moment";
16 16 import useInitial from "@/hooks/useInitail";
17 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 19 import EntryTaskPreview from "./components/EntryTaskPreview";
20 20 import { OrderTaskApprovalType } from "./entity";
21 21 import AdviserTaskPreview from "./components/AdviserTaskPreview";
22 22 import SeriesTaskPreview from "./components/SeriesTaskPreview";
23 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 28 export default () => (
28 29 <Provider>
... ... @@ -42,6 +43,9 @@ function SaleTaskList() {
42 43 const [stpVisible, setStpVisible] = useState(false);
43 44 const [seriesTaskParams, setSeriesTaskParams] = useState({});
44 45  
  46 + const [autoVisible, setAutoVisible] = useState(false);
  47 + const [batchVisible, setBatchVisible] = useState(false);
  48 +
45 49 const { data, loading, setParams } = useInitial(
46 50 API.getSaleTaskApi,
47 51 {} as API.GetSaleTaskApiRes,
... ... @@ -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 92 const viewProcess = () => {
76 93 setApprove({
... ... @@ -128,30 +145,242 @@ function SaleTaskList() {
128 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 333 return (
132 334 <PageHeaderWrapper title="零售任务分配">
133 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 378 </Row>
152 379 <Table
153 380 rowKey="id"
  381 + bordered
154 382 loading={loading}
  383 + columns={columns}
155 384 dataSource={data.shopTaskList}
156 385 pagination={false}
157 386 scroll={{ y: 450 }}
... ... @@ -168,66 +397,39 @@ function SaleTaskList() {
168 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 402 {data.totalTaskCount}
176 403 </Table.Summary.Cell>
177   - <Table.Summary.Cell align="left" index={3}>
  404 + <Table.Summary.Cell index={3}>
178 405 {data.newEnergyTaskCount}
179 406 </Table.Summary.Cell>
180   - <Table.Summary.Cell align="left" index={4}>
  407 + <Table.Summary.Cell index={4}>
181 408 {data.fuelVehicleTaskCount}
182 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 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 415 {data.clueDealTaskCount}
188 416 </Table.Summary.Cell>
189   - <Table.Summary.Cell index={7}>
  417 + <Table.Summary.Cell index={8}>
190 418 {data.testDriveTaskCount}
191 419 </Table.Summary.Cell>
192   - <Table.Summary.Cell index={8}>
  420 + <Table.Summary.Cell index={9}>
193 421 {data.tackCarTaskCount}
194 422 </Table.Summary.Cell>
195   - <Table.Summary.Cell index={9}>
  423 + <Table.Summary.Cell index={10}>
196 424 {data.seriesTaskCount}
197 425 </Table.Summary.Cell>
198   - <Table.Summary.Cell index={10} />
  426 + <Table.Summary.Cell index={11} />
  427 + <Table.Summary.Cell index={12} />
199 428 </Table.Summary.Row>
200 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 433 {data.revoke ? (
232 434 <Row align="middle" justify="center" style={{ marginTop: 50 }}>
233 435 <Button onClick={cancelSaleTask}>撤销</Button>
... ... @@ -271,82 +473,11 @@ function SaleTaskList() {
271 473 destroyOnClose
272 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 476 <EntryTaskPreview
  477 + month={targetMonth}
293 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 482 </Modal>
352 483 <Modal
... ... @@ -359,14 +490,7 @@ function SaleTaskList() {
359 490 >
360 491 <AdviserTaskPreview
361 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 495 </Modal>
372 496 <Modal
... ... @@ -379,6 +503,37 @@ function SaleTaskList() {
379 503 >
380 504 <SeriesTaskPreview params={seriesTaskParams} />
381 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 537 <ApprovalProgressModal
383 538 visible={approve.visible}
384 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 5 import AdviserTaskEdit from "./AdviserTaskEdit";
6 6 import { history, useRequest } from "umi";
7 7 import { cloneDeep } from "lodash";
8   -
9   -const { Column } = Table;
  8 +import { ColumnsType } from "antd/es/table";
10 9  
11 10 interface AdviserTaskEditForm {
12 11 submit: (callback: (data: any) => void) => void;
... ... @@ -14,9 +13,10 @@ interface AdviserTaskEditForm {
14 13  
15 14 interface AdviserTaskProps {
16 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 20 const adviserTaskEditRef = useRef<AdviserTaskEditForm>(null);
21 21 const {
22 22 shopTaskItem,
... ... @@ -48,6 +48,10 @@ export default function AdviserTask({ form }: AdviserTaskProps) {
48 48  
49 49 // 前端更新编辑后的顾问分配任务
50 50 const onOk = () => {
  51 + if (isReadOnly) {
  52 + setVisible(false);
  53 + return;
  54 + }
51 55 adviserTaskEditRef.current?.submit((newTask) => {
52 56 setShopTaskItem(newTask);
53 57 setAdvisersFiltered(newTask?.staffTaskList); // 刷新列表
... ... @@ -66,12 +70,96 @@ export default function AdviserTask({ form }: AdviserTaskProps) {
66 70 .run({ ...other, id: taskId, ...shopFormValue })
67 71 .then(() => {
68 72 message.success("保存草稿成功");
  73 + onRefresh();
69 74 })
70 75 .catch((error: any) => {
71 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 163 return (
76 164 <>
77 165 <Row
... ... @@ -87,39 +175,13 @@ export default function AdviserTask({ form }: AdviserTaskProps) {
87 175 />
88 176 </Row>
89 177 <Table
  178 + bordered
  179 + columns={columns}
90 180 dataSource={[...advisersFiltered]}
91 181 pagination={false}
92 182 rowKey="id"
93 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 185 <Row align="middle" justify="center" style={{ marginTop: 50 }}>
124 186 <Button onClick={handleGoBack}>返回列表</Button>
125 187 {!isReadOnly && (
... ...
src/pages/order3/SaleTask/subpages/TaskEdit/components/AdviserTaskEdit.tsx
... ... @@ -124,6 +124,7 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) {
124 124 form={form}
125 125 initialValues={editAdviser}
126 126 onFinish={onFinish}
  127 + disabled={isReadOnly}
127 128 >
128 129 <Form.Item name="taskCount" label="零售任务:" required>
129 130 <InputNumber
... ... @@ -132,7 +133,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) {
132 133 min={0}
133 134 max={MAX_NUM}
134 135 style={{ width: "100%" }}
135   - disabled={isReadOnly}
136 136 precision={0}
137 137 />
138 138 </Form.Item>
... ... @@ -164,7 +164,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) {
164 164 min={0}
165 165 max={MAX_NUM}
166 166 style={{ width: "100%" }}
167   - disabled={isReadOnly}
168 167 precision={2}
169 168 />
170 169 </Form.Item>
... ... @@ -204,7 +203,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) {
204 203 min={0}
205 204 max={MAX_NUM}
206 205 style={{ width: "100%" }}
207   - disabled={isReadOnly}
208 206 precision={0}
209 207 />
210 208 </Form.Item>
... ... @@ -237,13 +235,12 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) {
237 235 min={0}
238 236 max={MAX_NUM}
239 237 style={{ width: "100%" }}
240   - disabled={isReadOnly}
241 238 precision={0}
242 239 />
243 240 </Form.Item>
244 241 <Form.Item
245 242 name="vehicleGrossProfitTask"
246   - label="车辆毛利任务:"
  243 + label="单车毛利任务:"
247 244 required
248 245 >
249 246 <InputNumber
... ... @@ -252,7 +249,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) {
252 249 min={0}
253 250 max={MAX_NUM}
254 251 style={{ width: "100%" }}
255   - disabled={isReadOnly}
256 252 precision={2}
257 253 />
258 254 </Form.Item>
... ... @@ -267,7 +263,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) {
267 263 min={0}
268 264 max={MAX_NUM}
269 265 style={{ width: "100%" }}
270   - disabled={isReadOnly}
271 266 precision={0}
272 267 />
273 268 </Form.Item>
... ... @@ -278,7 +273,6 @@ function AdviserTaskEdit(props: AdviserTaskEditProps, ref: any) {
278 273 min={0}
279 274 max={MAX_NUM}
280 275 style={{ width: "100%" }}
281   - disabled={isReadOnly}
282 276 precision={0}
283 277 />
284 278 </Form.Item>
... ...
src/pages/order3/SaleTask/subpages/TaskEdit/components/ShopTask.tsx
... ... @@ -8,6 +8,7 @@ import {
8 8 Button,
9 9 Row,
10 10 Tag,
  11 + Modal,
11 12 } from "antd";
12 13 import { PlusOutlined } from "@ant-design/icons";
13 14 import * as API from "../../../api";
... ... @@ -18,14 +19,16 @@ import EditableCell from &quot;./EditableCell&quot;;
18 19 import SeriesModal from "./SeriesModal";
19 20 import { history, useRequest } from "umi";
20 21 import { MAX_NUM } from "../../../entity";
  22 +import "../../../components/index.less";
21 23  
22 24 const { Column } = Table;
23 25  
24 26 interface ShopTaskProps {
25 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 32 const {
30 33 shopTaskItem,
31 34 isReadOnly,
... ... @@ -96,12 +99,49 @@ export default function ShopTask({ form }: ShopTaskProps) {
96 99 .run({ ...other, ...values, id: taskId })
97 100 .then(() => {
98 101 message.success("保存草稿成功");
  102 + onRefresh();
99 103 })
100 104 .catch((error: any) => {
101 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 145 const layout = {
106 146 labelCol: { span: 5 },
107 147 wrapperCol: { span: 19 },
... ... @@ -121,6 +161,7 @@ export default function ShopTask({ form }: ShopTaskProps) {
121 161 labelAlign="left"
122 162 form={form}
123 163 initialValues={shopTaskItem!}
  164 + disabled={isReadOnly}
124 165 >
125 166 <Form.Item name="taskCount" label="零售任务:" required>
126 167 <InputNumber
... ... @@ -129,7 +170,6 @@ export default function ShopTask({ form }: ShopTaskProps) {
129 170 min={0}
130 171 max={MAX_NUM}
131 172 style={{ width: "100%" }}
132   - disabled={isReadOnly}
133 173 precision={0}
134 174 />
135 175 </Form.Item>
... ... @@ -161,7 +201,6 @@ export default function ShopTask({ form }: ShopTaskProps) {
161 201 min={0}
162 202 max={MAX_NUM}
163 203 style={{ width: "100%" }}
164   - disabled={isReadOnly}
165 204 precision={2}
166 205 />
167 206 </Form.Item>
... ... @@ -201,7 +240,6 @@ export default function ShopTask({ form }: ShopTaskProps) {
201 240 min={0}
202 241 max={MAX_NUM}
203 242 style={{ width: "100%" }}
204   - disabled={isReadOnly}
205 243 precision={0}
206 244 />
207 245 </Form.Item>
... ... @@ -235,13 +273,12 @@ export default function ShopTask({ form }: ShopTaskProps) {
235 273 min={0}
236 274 max={MAX_NUM}
237 275 style={{ width: "100%" }}
238   - disabled={isReadOnly}
239 276 precision={0}
240 277 />
241 278 </Form.Item>
242 279 <Form.Item
243 280 name="vehicleGrossProfitTask"
244   - label="车辆毛利任务:"
  281 + label="单车毛利任务:"
245 282 required
246 283 >
247 284 <InputNumber
... ... @@ -250,7 +287,6 @@ export default function ShopTask({ form }: ShopTaskProps) {
250 287 min={0}
251 288 max={MAX_NUM}
252 289 style={{ width: "100%" }}
253   - disabled={isReadOnly}
254 290 precision={2}
255 291 />
256 292 </Form.Item>
... ... @@ -265,7 +301,6 @@ export default function ShopTask({ form }: ShopTaskProps) {
265 301 min={0}
266 302 max={MAX_NUM}
267 303 style={{ width: "100%" }}
268   - disabled={isReadOnly}
269 304 precision={0}
270 305 />
271 306 </Form.Item>
... ... @@ -276,7 +311,6 @@ export default function ShopTask({ form }: ShopTaskProps) {
276 311 min={0}
277 312 max={MAX_NUM}
278 313 style={{ width: "100%" }}
279   - disabled={isReadOnly}
280 314 precision={0}
281 315 />
282 316 </Form.Item>
... ... @@ -405,6 +439,15 @@ export default function ShopTask({ form }: ShopTaskProps) {
405 439 保存草稿
406 440 </Button>
407 441 )}
  442 + {!isReadOnly && (
  443 + <Button
  444 + type="primary"
  445 + style={{ marginLeft: 10 }}
  446 + onClick={autoAssignOneShop}
  447 + >
  448 + 分配到门店和顾问
  449 + </Button>
  450 + )}
408 451 </Row>
409 452 <SeriesModal
410 453 visible={seriesVisible}
... ...
src/pages/order3/SaleTask/subpages/TaskEdit/index.tsx
... ... @@ -20,19 +20,19 @@ function TaskEdit() {
20 20 const readOnly = querys?.readOnly === "1";
21 21 const shopId = querys?.shopId;
22 22 const taskDate = querys?.taskDate;
  23 + const queryTab = querys?.currTab;
23 24 const { shopTaskItem, setShopTaskItem, setIsReadOnly } = useStore();
24   - const [currStep, setCurrStep] = useState("1");
  25 + const [currTab, setCurrTab] = useState(queryTab ?? "1");
25 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 37 useEffect(() => {
38 38 setShopTaskItem(data);
... ... @@ -46,21 +46,27 @@ function TaskEdit() {
46 46 title={<span>当前选择门店:{shopTaskItem?.shopName}</span>}
47 47 >
48 48 <Tabs
49   - defaultActiveKey={currStep}
50   - onChange={(activeKey) => setCurrStep(activeKey)}
  49 + defaultActiveKey={currTab}
  50 + onChange={(activeKey) => setCurrTab(activeKey)}
51 51 items={[
52 52 {
53 53 label: `门店任务分配${readOnly ? "详情" : ""}`,
54 54 key: "1",
55 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 63 label: `销售顾问任务分配${readOnly ? "详情" : ""}`,
61 64 key: "2",
62 65 children: !isEmpty(shopTaskItem) && (
63   - <AdviserTask form={shopTaskForm} />
  66 + <AdviserTask
  67 + form={shopTaskForm}
  68 + onRefresh={() => setParams({}, true)}
  69 + />
64 70 ),
65 71 },
66 72 ]}
... ...