1c9bc538
Shinner
修复问题
|
1
|
import React, { useEffect, useMemo, useState } from "react";
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
2
3
|
import {
InputNumber,
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
4
5
6
7
8
|
Divider,
Form,
message,
Table,
Button,
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
9
10
|
Row,
Tag,
|
d8aedefd
Shinner
单个门店支持分配到门店和顾问
|
11
|
Modal,
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
12
13
14
|
} from "antd";
import { PlusOutlined } from "@ant-design/icons";
import * as API from "../../../api";
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
15
|
import styles from "../index.less";
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
16
|
import { useStore } from "../../../store";
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
17
|
import _ from "lodash";
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
18
|
import EditableCell from "./EditableCell";
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
19
|
import SeriesModal from "./SeriesModal";
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
20
|
import { history, useRequest } from "umi";
|
1c9bc538
Shinner
修复问题
|
21
|
import { MAX_NUM } from "../../../entity";
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
22
|
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
23
24
|
const { Column } = Table;
|
a8fb2c8c
Shinner
优化销顾任务保存草稿逻辑
|
25
26
27
28
29
|
interface ShopTaskProps {
form: any;
}
export default function ShopTask({ form }: ShopTaskProps) {
|
263b8790
Shinner
优化分配任务前端数据更新逻辑
|
30
31
32
33
34
35
36
|
const {
shopTaskItem,
isReadOnly,
setShopSeriesRow,
deleteShopSeriesRow,
addShopSeriesRow,
} = useStore();
|
a1e7edf3
Shinner
调试零售任务分配接口
|
37
|
const [seriesForm] = Form.useForm();
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
38
|
const [editingKey, setEditingKey] = useState(-1); // 编辑表格 key
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
39
|
const [seriesVisible, setSeriesVisible] = useState(false);
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
40
|
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
41
42
43
44
45
|
const saveShopSaleTaskHook = useRequest(API.saveShopSaleTask, {
manual: true,
throwOnError: true,
});
|
1c9bc538
Shinner
修复问题
|
46
47
48
49
50
51
|
// 车系去重
const selectedIds = useMemo(() => {
if (!shopTaskItem) return [];
return shopTaskItem.seriesTaskList.map((item) => String(item.seriesId));
}, [shopTaskItem]);
|
4ddb4140
Shinner
调试零售任务分配接口
|
52
53
54
55
56
57
|
// 计算车系任务总数自动填写
useEffect(() => {
const total = shopTaskItem?.seriesTaskList.reduce(
(total, currItem) => total + currItem.taskCount,
0
);
|
eb77f47f
Shinner
增加表单交互
|
58
|
form.setFieldValue("seriesTaskCount", total);
|
4ddb4140
Shinner
调试零售任务分配接口
|
59
60
|
}, [shopTaskItem]);
|
a1e7edf3
Shinner
调试零售任务分配接口
|
61
62
|
const isEditing = (record: API.SeriesTaskItem) => {
return record.seriesId === Number(editingKey);
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
63
64
|
};
|
a1e7edf3
Shinner
调试零售任务分配接口
|
65
66
|
const setSeriesRow = (id: number) => {
seriesForm
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
67
68
69
|
.validateFields()
.then((row: any) => {
setEditingKey(-1);
|
263b8790
Shinner
优化分配任务前端数据更新逻辑
|
70
|
setShopSeriesRow(id, row);
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
71
72
73
74
75
76
|
})
.catch((error: any) => {
message.error(error.message ?? "表单校验失败");
});
};
|
a1e7edf3
Shinner
调试零售任务分配接口
|
77
78
79
|
const editSeriesRow = (record: API.SeriesTaskItem) => {
seriesForm.setFieldsValue({ ...record });
setEditingKey(record.seriesId);
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
80
81
|
};
|
1c9bc538
Shinner
修复问题
|
82
|
// 添加车系
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
83
|
const handleSelectSeries = (values: any) => {
|
263b8790
Shinner
优化分配任务前端数据更新逻辑
|
84
|
addShopSeriesRow(values);
|
a1e7edf3
Shinner
调试零售任务分配接口
|
85
|
setSeriesVisible(false);
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
86
87
|
};
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
88
89
90
91
92
93
94
|
const handleGoBack = () => {
history.goBack(); // todo 提示是否有未保存的修改
};
const handleSaveTask = async () => {
await form.validateFields();
const values = form.getFieldsValue();
|
df7d79e7
Shinner
调试零售任务分配接口
|
95
|
const { taskId, ...other } = shopTaskItem!;
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
96
|
saveShopSaleTaskHook
|
df7d79e7
Shinner
调试零售任务分配接口
|
97
|
.run({ ...other, ...values, id: taskId })
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
98
99
|
.then(() => {
message.success("保存草稿成功");
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
100
101
|
})
.catch((error: any) => {
|
436c6c9d
Shinner
报错显示后端提示信息
|
102
|
message.error(error.message ?? "请求失败");
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
103
104
105
|
});
};
|
d8aedefd
Shinner
单个门店支持分配到门店和顾问
|
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
// 分配到门店和顾问
const autoAssignOneShop = async () => {
await form.validateFields();
const values = form.getFieldsValue();
Modal.confirm({
title: (
<span>
确认分配到
<span className="tip">全部门店和顾问</span>
吗?
</span>
),
zIndex: 1002,
onOk: async () => {
const hide = message.loading("分配中,请稍候", 0);
const { taskId, id, ...other } = shopTaskItem!;
API.autoAssignOneShop({
...other,
...values,
orderTaskApplyId: taskId,
orderShopTaskId: id,
})
.then((res) => {
message.success("分配成功");
})
.catch((error: any) => {
message.error(error.message ?? "请求失败");
})
.finally(() => {
hide();
});
},
});
};
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
141
142
143
144
145
146
147
148
149
150
151
152
|
const layout = {
labelCol: { span: 5 },
wrapperCol: { span: 19 },
};
const components = {
body: {
cell: EditableCell,
},
};
return (
|
4c5bad19
Shinner
预览任务按钮显示与是否审批无关;调整样式
|
153
|
<>
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
154
|
<div style={{ width: 880, margin: "0 auto", paddingTop: 24 }}>
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
155
156
157
158
|
<Form
{...layout}
labelAlign="left"
form={form}
|
11ea8143
Shinner
调试零售任务分配接口
|
159
|
initialValues={shopTaskItem!}
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
160
|
>
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
161
|
<Form.Item name="taskCount" label="零售任务:" required>
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
162
163
164
165
|
<InputNumber
formatter={(value) => `${value}台`}
parser={(value: any) => value.replace("台", "")}
min={0}
|
1c9bc538
Shinner
修复问题
|
166
|
max={MAX_NUM}
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
167
|
style={{ width: "100%" }}
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
168
|
disabled={isReadOnly}
|
1c9bc538
Shinner
修复问题
|
169
|
precision={0}
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
170
171
|
/>
</Form.Item>
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
172
173
174
|
<Form.Item
noStyle
shouldUpdate={(prevValues, currentValues) => {
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
175
176
177
|
if (prevValues.taskCount !== currentValues.taskCount) {
form.setFieldValue(
"clueDealTaskCount",
|
22729ea6
Shinner
修复编辑顾问任务分配
|
178
179
|
(
currentValues.taskCount *
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
180
|
((shopTaskItem?.clueDealTaskRate ?? 100) / 100)
|
22729ea6
Shinner
修复编辑顾问任务分配
|
181
|
).toFixed(2)
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
182
183
|
);
}
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
184
185
186
|
return prevValues.taskCount !== currentValues.taskCount;
}}
>
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
187
|
{() => {
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
188
|
return (
|
eb77f47f
Shinner
增加表单交互
|
189
190
191
192
193
|
<Form.Item
name="clueDealTaskCount"
label="线索到店零售台数:"
required
>
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
194
195
196
197
|
<InputNumber
formatter={(value) => `${value}台`}
parser={(value: any) => value.replace("台", "")}
min={0}
|
1c9bc538
Shinner
修复问题
|
198
|
max={MAX_NUM}
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
199
200
|
style={{ width: "100%" }}
disabled={isReadOnly}
|
1c9bc538
Shinner
修复问题
|
201
|
precision={2}
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
202
203
204
205
|
/>
</Form.Item>
);
}}
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
206
|
</Form.Item>
|
eb77f47f
Shinner
增加表单交互
|
207
|
<Form.Item
|
9d18f86b
Shinner
回退台数交互
|
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
|
name="newEnergyTaskCount"
label="新能源车任务台数:"
required
dependencies={["taskCount", "fuelVehicleTaskCount"]}
rules={[
({ getFieldValue }) => ({
validator(_, value) {
const taskCount = getFieldValue("taskCount");
const fuelVehicleTaskCount = getFieldValue(
"fuelVehicleTaskCount"
);
if (
value >= 0 &&
fuelVehicleTaskCount + value === taskCount
) {
return Promise.resolve();
}
return Promise.reject(
new Error(
"规则:新能源车任务台数 + 传统燃油车任务台数 = 零售任务台数"
)
);
},
}),
]}
|
eb77f47f
Shinner
增加表单交互
|
233
|
>
|
9d18f86b
Shinner
回退台数交互
|
234
235
236
237
238
239
240
241
242
|
<InputNumber
formatter={(value) => `${value}台`}
parser={(value: any) => value.replace("台", "")}
min={0}
max={MAX_NUM}
style={{ width: "100%" }}
disabled={isReadOnly}
precision={0}
/>
|
eb77f47f
Shinner
增加表单交互
|
243
|
</Form.Item>
|
9d18f86b
Shinner
回退台数交互
|
244
|
|
eb77f47f
Shinner
增加表单交互
|
245
|
<Form.Item
|
9d18f86b
Shinner
回退台数交互
|
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
|
name="fuelVehicleTaskCount"
label="传统燃油车任务台数:"
required
dependencies={["taskCount", "newEnergyTaskCount"]}
rules={[
({ getFieldValue }) => ({
validator(_, value) {
const taskCount = getFieldValue("taskCount");
const newEnergyTaskCount =
getFieldValue("newEnergyTaskCount");
if (value >= 0 && newEnergyTaskCount + value === taskCount) {
return Promise.resolve();
}
return Promise.reject(
new Error(
"规则:新能源车任务台数 + 传统燃油车任务台数 = 零售任务台数"
)
);
},
}),
]}
|
eb77f47f
Shinner
增加表单交互
|
267
|
>
|
9d18f86b
Shinner
回退台数交互
|
268
269
270
271
272
273
274
275
276
|
<InputNumber
formatter={(value) => `${value}台`}
parser={(value: any) => value.replace("台", "")}
min={0}
max={MAX_NUM}
style={{ width: "100%" }}
disabled={isReadOnly}
precision={0}
/>
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
277
|
</Form.Item>
|
eb77f47f
Shinner
增加表单交互
|
278
279
|
<Form.Item
name="vehicleGrossProfitTask"
|
7c25275f
Shinner
[车辆毛利任务]修改为[单车毛利任务]
|
280
|
label="单车毛利任务:"
|
eb77f47f
Shinner
增加表单交互
|
281
282
|
required
>
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
283
|
<InputNumber
|
22729ea6
Shinner
修复编辑顾问任务分配
|
284
285
|
formatter={(value) => `${value}元`}
parser={(value: any) => value.replace("元", "")}
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
286
|
min={0}
|
1c9bc538
Shinner
修复问题
|
287
|
max={MAX_NUM}
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
288
|
style={{ width: "100%" }}
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
289
|
disabled={isReadOnly}
|
b04b25da
Shinner
车辆毛利任务小数点两位
|
290
|
precision={2}
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
291
292
|
/>
</Form.Item>
|
eb77f47f
Shinner
增加表单交互
|
293
294
295
296
297
|
<Form.Item
name="testDriveTaskCount"
label="首客试驾成交任务台数:"
required
>
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
298
299
300
301
|
<InputNumber
formatter={(value) => `${value}台`}
parser={(value: any) => value.replace("台", "")}
min={0}
|
1c9bc538
Shinner
修复问题
|
302
|
max={MAX_NUM}
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
303
304
|
style={{ width: "100%" }}
disabled={isReadOnly}
|
1c9bc538
Shinner
修复问题
|
305
|
precision={0}
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
306
307
|
/>
</Form.Item>
|
eb77f47f
Shinner
增加表单交互
|
308
|
<Form.Item name="tackCarTaskCount" label="攻坚车任务台数:" required>
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
309
310
311
312
|
<InputNumber
formatter={(value) => `${value}台`}
parser={(value: any) => value.replace("台", "")}
min={0}
|
1c9bc538
Shinner
修复问题
|
313
|
max={MAX_NUM}
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
314
315
|
style={{ width: "100%" }}
disabled={isReadOnly}
|
1c9bc538
Shinner
修复问题
|
316
|
precision={0}
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
317
318
|
/>
</Form.Item>
|
eb77f47f
Shinner
增加表单交互
|
319
|
<Form.Item name="seriesTaskCount" label="车系任务台数:" required>
|
4ddb4140
Shinner
调试零售任务分配接口
|
320
321
322
323
|
<InputNumber
formatter={(value) => `${value}台`}
parser={(value: any) => value.replace("台", "")}
min={0}
|
1c9bc538
Shinner
修复问题
|
324
|
max={MAX_NUM}
|
4ddb4140
Shinner
调试零售任务分配接口
|
325
|
style={{ width: "100%" }}
|
1c9bc538
Shinner
修复问题
|
326
|
precision={0}
|
aeeb3492
Shinner
增加预览任务功能
|
327
|
disabled
|
4ddb4140
Shinner
调试零售任务分配接口
|
328
329
|
/>
</Form.Item>
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
330
331
332
333
334
335
336
337
338
339
|
</Form>
</div>
<Divider />
<div style={{ width: 880, margin: "0 auto" }}>
<Row
align="middle"
justify="space-between"
style={{ marginBottom: 20 }}
>
<h2 className={styles.carTask}>车系任务</h2>
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
340
341
342
343
344
345
346
347
348
349
350
|
{!isReadOnly && (
<Button
icon={<PlusOutlined />}
type="primary"
onClick={() => {
setSeriesVisible(true);
}}
>
添加车系
</Button>
)}
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
351
|
</Row>
|
a1e7edf3
Shinner
调试零售任务分配接口
|
352
|
<Form form={seriesForm} component={false}>
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
353
354
|
<Table
scroll={{ y: 400 }}
|
a1e7edf3
Shinner
调试零售任务分配接口
|
355
|
rowKey="seriesId"
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
356
357
358
|
rowClassName={() => "editable-cell"}
pagination={false}
components={components}
|
a1e7edf3
Shinner
调试零售任务分配接口
|
359
|
dataSource={[...shopTaskItem?.seriesTaskList!]}
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
360
|
>
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
361
|
<Column
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
|
title="车系"
dataIndex="seriesName"
render={(value, record: API.SeriesTaskItem) => {
return (
<Row align="middle" justify="start">
<span>{value}</span>
{record.newEnergy && (
<Tag
style={{ marginBottom: 5, marginLeft: 7 }}
color="green"
>
新能源
</Tag>
)}
</Row>
);
}}
/>
<Column
title="零售任务(台)"
|
a1e7edf3
Shinner
调试零售任务分配接口
|
382
383
|
dataIndex="taskCount"
onCell={(record: API.SeriesTaskItem) => ({
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
384
385
|
record,
inputType: "number",
|
a1e7edf3
Shinner
调试零售任务分配接口
|
386
|
dataIndex: "taskCount",
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
387
|
title: "零售任务(台)",
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
388
389
390
|
editing: isEditing(record),
})}
/>
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
391
392
393
394
|
{!isReadOnly && (
<Column
width={120}
title="操作"
|
a1e7edf3
Shinner
调试零售任务分配接口
|
395
|
render={(text, record: API.SeriesTaskItem) => {
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
396
397
398
399
400
|
const editable = isEditing(record);
return (
<div>
{editable ? (
<span>
|
a1e7edf3
Shinner
调试零售任务分配接口
|
401
402
403
|
<a onClick={() => setSeriesRow(record.seriesId)}>
确认
</a>
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
404
405
406
407
408
409
410
411
412
|
<Divider type="vertical" />
<a
style={{ color: "#999" }}
onClick={() => setEditingKey(-1)}
>
取消
</a>
</span>
) : (
|
1c9bc538
Shinner
修复问题
|
413
|
<span>
|
a1e7edf3
Shinner
调试零售任务分配接口
|
414
|
<a onClick={() => editSeriesRow(record)}>编辑</a>
|
1c9bc538
Shinner
修复问题
|
415
416
417
|
<Divider type="vertical" />
<a
style={{ color: "#999" }}
|
263b8790
Shinner
优化分配任务前端数据更新逻辑
|
418
|
onClick={() => deleteShopSeriesRow(record.seriesId)}
|
1c9bc538
Shinner
修复问题
|
419
420
421
422
|
>
删除
</a>
</span>
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
423
424
425
426
427
428
|
)}
</div>
);
}}
/>
)}
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
429
430
431
|
</Table>
</Form>
</div>
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
432
433
434
435
436
437
438
439
440
441
442
443
|
<Row align="middle" justify="center" style={{ marginTop: 50 }}>
<Button onClick={handleGoBack}>返回列表</Button>
{!isReadOnly && (
<Button
type="primary"
style={{ marginLeft: 10 }}
loading={saveShopSaleTaskHook.loading}
onClick={handleSaveTask}
>
保存草稿
</Button>
)}
|
d8aedefd
Shinner
单个门店支持分配到门店和顾问
|
444
445
446
447
448
449
450
451
452
|
{!isReadOnly && (
<Button
type="primary"
style={{ marginLeft: 10 }}
onClick={autoAssignOneShop}
>
分配到门店和顾问
</Button>
)}
|
1aa6dd0a
Shinner
调试零售任务分配接口
|
453
|
</Row>
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
454
455
|
<SeriesModal
visible={seriesVisible}
|
1c9bc538
Shinner
修复问题
|
456
|
selectedIds={selectedIds}
|
3b0d1ee6
Shinner
增加零售任务分配菜单静态页面
|
457
458
459
460
461
|
onOk={handleSelectSeries}
onCancel={() => {
setSeriesVisible(false);
}}
/>
|
4c5bad19
Shinner
预览任务按钮显示与是否审批无关;调整样式
|
462
|
</>
|
32942bc1
Shinner
增加零售任务分配菜单静态页面
|
463
|
);
|
11ea8143
Shinner
调试零售任务分配接口
|
464
|
}
|