Commit 6e9d80c33d300675b7b519d3f415b6ab88c1ce41

Authored by 杜志良
2 parents 29701274 346196a5

Merge branch 'master' into d-cas

Showing 37 changed files with 1896 additions and 1067 deletions
afterbuild.js 0 → 100644
  1 +const fs = require("fs");
  2 +
  3 +const encoding = "UTF-8";
  4 +/**
  5 + * 写入version信息方便判断版本
  6 + */
  7 +function sendfile() {
  8 + try {
  9 + const pathFolder = "./dist/version.text";
  10 + deleteFolder(pathFolder);
  11 + fs.writeFileSync(pathFolder, new Date().getTime().toString(), { encoding });
  12 + } catch (e) {
  13 + console.error("sourcemap文件写入失败", e);
  14 + }
  15 +}
  16 +
  17 +/**删除文件或文件夹 */
  18 +function deleteFolder(path) {
  19 + let files = [];
  20 + if (fs.existsSync(path)) {
  21 + if (fs.statSync(path).isFile()) {
  22 + fs.unlinkSync(path); //删除文件
  23 + } else {
  24 + files = fs.readdirSync(path);
  25 + files.forEach((file, index) => {
  26 + let curPath = path + "/" + file;
  27 + if (fs.statSync(curPath).isDirectory()) {
  28 + // recurse
  29 + deleteFolder(curPath);
  30 + } else {
  31 + // delete file
  32 + fs.unlinkSync(curPath);
  33 + }
  34 + });
  35 + fs.rmdirSync(path);
  36 + }
  37 + }
  38 +}
  39 +
  40 +sendfile();
... ...
config/routers/pms.ts
... ... @@ -44,6 +44,10 @@ export default [
44 44 component: './pms/storage/partShop'
45 45 },
46 46 {
  47 + path: '/pms/storage/fwStockPartShop', // 服务站配件(霏微(库存))
  48 + component: './pms/storage/partShop'
  49 + },
  50 + {
47 51 path: '/pms/storage/areaStorage', // 区域库设置
48 52 component: './pms/storage/areaStorageSetting'
49 53 },
... ...
package.json
... ... @@ -5,8 +5,8 @@
5 5 "description": "霏微汽车云平台",
6 6 "scripts": {
7 7 "analyze": "cross-env ANALYZE=1 umi build",
8   - "build:prod": "cross-env REACT_APP_ENV=prod umi build",
9   - "build:unset": "cross-env REACT_APP_ENV=dev umi build",
  8 + "build:prod": "cross-env REACT_APP_ENV=prod umi build && node afterbuild.js",
  9 + "build:unset": "cross-env REACT_APP_ENV=dev umi build && node afterbuild.js",
10 10 "lint": "npm run lint:js && npm run lint:style && npm run lint:prettier",
11 11 "lint-staged": "lint-staged",
12 12 "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
... ... @@ -14,16 +14,16 @@
14 14 "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./",
15 15 "lint:prettier": "check-prettier lint",
16 16 "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
  17 + "package": "chmod +x ./build.sh && ./build.sh",
17 18 "prettier": "prettier -c --write \"**/*\"",
18 19 "site": "cross-env npm run fetch:blocks && npm run build && npm run functions:build",
19 20 "start": "cross-env HOST=devlocal.feewee.cn umi dev",
20 21 "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_UI=none HOST=devlocal.feewee.cn umi dev",
21   - "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_UI=none HOST=testlocal.feewee.cn umi dev",
22 22 "start:prod": "cross-env REACT_APP_ENV=prod MOCK=none UMI_UI=none HOST=local.feewee.cn umi dev",
  23 + "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_UI=none HOST=testlocal.feewee.cn umi dev",
23 24 "test": "umi test",
24 25 "test:all": "node ./tests/run-tests.js",
25   - "test:component": "umi test ./src/components",
26   - "package": "chmod +x ./build.sh && ./build.sh"
  26 + "test:component": "umi test ./src/components"
27 27 },
28 28 "husky": {
29 29 "hooks": {}
... ...
src/components/GlobalFooter/index.tsx
1   -import React from 'react';
2   -import { CopyrightOutlined } from '@ant-design/icons';
3   -import { Layout } from 'antd';
4   -import classNames from 'classnames';
5   -import styles from './index.less';
  1 +import React, { useCallback, useEffect, useRef } from "react";
  2 +import { CopyrightOutlined } from "@ant-design/icons";
  3 +import { Button, Layout, Space, notification } from "antd";
  4 +import classNames from "classnames";
  5 +import styles from "./index.less";
  6 +import { useIntl } from "umi";
6 7  
7 8 export interface IGlobalFooterProps {
8 9 links?: Array<{
... ... @@ -17,25 +18,74 @@ const { Footer } = Layout;
17 18 const COPYRIGHT = `${new Date().getFullYear()} 重庆霏微科技有限公司 渝ICP备17016156号-3`;
18 19  
19 20 const GlobalFooter = ({ links }: IGlobalFooterProps) => {
  21 + const notifiShowRef = useRef<boolean>(false);
  22 +
  23 + const intl = useIntl();
  24 +
  25 + useEffect(() => {
  26 + document.getElementById("fw_updater")?.addEventListener("click", showNotification);
  27 + return () => {
  28 + document.getElementById("fw_updater")?.removeEventListener("click", showNotification);
  29 + };
  30 + }, []);
  31 +
  32 + const showNotification = useCallback(() => {
  33 + if (notifiShowRef?.current) {
  34 + return;
  35 + }
  36 + const key = `open${Date.now()}`;
  37 + const btn = (
  38 + <Space>
  39 + <Button
  40 + size="small"
  41 + onClick={() => {
  42 + notification.close(key);
  43 + notifiShowRef.current = false;
  44 + }}
  45 + >
  46 + {intl.formatMessage({ id: "app.update.tip.close" })}
  47 + </Button>
  48 + <Button type="primary" size="small" onClick={() => window.location.reload()}>
  49 + {intl.formatMessage({ id: "app.update.tip.confirm" })}
  50 + </Button>
  51 + </Space>
  52 + );
  53 + notification.open({
  54 + message: intl.formatMessage({ id: "app.update.tip.title" }),
  55 + description: intl.formatMessage({ id: "app.update.tip.message" }),
  56 + btn,
  57 + placement: "bottomRight",
  58 + key,
  59 + duration: 0,
  60 + onClose: () => {
  61 + notifiShowRef.current = false;
  62 + },
  63 + });
  64 + notifiShowRef.current = true;
  65 + }, []);
  66 +
20 67 const clsString = classNames(styles.globalFooter);
21 68 return (
22 69 <Footer style={{ padding: 0 }}>
23 70 <div className={clsString}>
24 71 {links && (
25 72 <div className={styles.links}>
26   - {links.map(link => (
  73 + {links.map((link) => (
27 74 <a
28 75 key={link.key}
29 76 title={link.key}
30   - target={link.blankTarget ? '_blank' : '_self'}
  77 + target={link.blankTarget ? "_blank" : "_self"}
31 78 href={link.href}
  79 + rel="noreferrer"
32 80 >
33 81 {link.title}
34 82 </a>
35 83 ))}
36 84 </div>
37 85 )}
38   - <div className={styles.copyright}>Copyright <CopyrightOutlined /> {COPYRIGHT}</div>
  86 + <div className={styles.copyright}>
  87 + Copyright <CopyrightOutlined /> {COPYRIGHT}
  88 + </div>
39 89 </div>
40 90 </Footer>
41 91 );
... ...
src/global.tsx
... ... @@ -82,7 +82,6 @@ if (pwa) {
82 82 }
83 83 }
84 84  
85   -
86 85 //@ts-ignore
87 86 if (!window.Number.prototype._toFixed) {
88 87 window.Number.prototype._toFixed = window.Number.prototype.toFixed;
... ...
src/locales/en-US.ts
... ... @@ -6,11 +6,16 @@ import settingDrawer from &#39;./en-US/settingDrawer&#39;;
6 6 import settings from './en-US/settings';
7 7  
8 8 export default {
9   - 'navBar.lang': 'Languages',
10   - 'layout.user.link.help': 'Help',
11   - 'layout.user.link.privacy': 'Privacy',
12   - 'layout.user.link.terms': 'Terms',
13   - 'app.preview.down.block': 'Download this page to your local project',
  9 + "navBar.lang": "Languages",
  10 + "layout.user.link.help": "Help",
  11 + "layout.user.link.privacy": "Privacy",
  12 + "layout.user.link.terms": "Terms",
  13 + "app.preview.down.block": "Download this page to your local project",
  14 + "app.update.tip.title": "Update Tips",
  15 + "app.update.tip.message":
  16 + "It is detected that the content of the website has been updated. Do you want to refresh the page to load the latest version?",
  17 + "app.update.tip.confirm": "Confirm",
  18 + "app.update.tip.close": "Close",
14 19 ...globalHeader,
15 20 ...menu,
16 21 ...settingDrawer,
... ...
src/locales/pt-BR.ts
... ... @@ -6,11 +6,16 @@ import settingDrawer from &#39;./pt-BR/settingDrawer&#39;;
6 6 import settings from './pt-BR/settings';
7 7  
8 8 export default {
9   - 'navBar.lang': 'Idiomas',
10   - 'layout.user.link.help': 'ajuda',
11   - 'layout.user.link.privacy': 'política de privacidade',
12   - 'layout.user.link.terms': 'termos de serviços',
13   - 'app.preview.down.block': 'Download this page to your local project',
  9 + "navBar.lang": "Idiomas",
  10 + "layout.user.link.help": "ajuda",
  11 + "layout.user.link.privacy": "política de privacidade",
  12 + "layout.user.link.terms": "termos de serviços",
  13 + "app.preview.down.block": "Download this page to your local project",
  14 + "app.update.tip.title": "Update Tips",
  15 + "app.update.tip.message":
  16 + "It is detected that the content of the website has been updated. Do you want to refresh the page to load the latest version?",
  17 + "app.update.tip.confirm": "Confirm",
  18 + "app.update.tip.close": "Close",
14 19 ...globalHeader,
15 20 ...menu,
16 21 ...settingDrawer,
... ...
src/locales/zh-CN.ts
... ... @@ -6,11 +6,15 @@ import settingDrawer from &#39;./zh-CN/settingDrawer&#39;;
6 6 import settings from './zh-CN/settings';
7 7  
8 8 export default {
9   - 'navBar.lang': '语言',
10   - 'layout.user.link.help': '帮助',
11   - 'layout.user.link.privacy': '隐私',
12   - 'layout.user.link.terms': '条款',
13   - 'app.preview.down.block': '下载此页面到本地项目',
  9 + "navBar.lang": "语言",
  10 + "layout.user.link.help": "帮助",
  11 + "layout.user.link.privacy": "隐私",
  12 + "layout.user.link.terms": "条款",
  13 + "app.preview.down.block": "下载此页面到本地项目",
  14 + "app.update.tip.title": "更新提示",
  15 + "app.update.tip.message": "检测到网站内容有更新,是否刷新页面加载最新版本?",
  16 + "app.update.tip.confirm": "确认",
  17 + "app.update.tip.close": "关闭",
14 18 ...globalHeader,
15 19 ...menu,
16 20 ...settingDrawer,
... ...
src/locales/zh-TW.ts
... ... @@ -6,11 +6,15 @@ import settingDrawer from &#39;./zh-TW/settingDrawer&#39;;
6 6 import settings from './zh-TW/settings';
7 7  
8 8 export default {
9   - 'navBar.lang': '語言',
10   - 'layout.user.link.help': '幫助',
11   - 'layout.user.link.privacy': '隱私',
12   - 'layout.user.link.terms': '條款',
13   - 'app.preview.down.block': '下載此頁面到本地項目',
  9 + "navBar.lang": "語言",
  10 + "layout.user.link.help": "幫助",
  11 + "layout.user.link.privacy": "隱私",
  12 + "layout.user.link.terms": "條款",
  13 + "app.preview.down.block": "下載此頁面到本地項目",
  14 + "app.update.tip.title": "更新提示",
  15 + "app.update.tip.message": "檢測到網站內容有更新,是否刷新頁面加載最新版本?",
  16 + "app.update.tip.confirm": "確認",
  17 + "app.update.tip.close": "關閉",
14 18 ...globalHeader,
15 19 ...menu,
16 20 ...settingDrawer,
... ...
src/pages/document.ejs
... ... @@ -3,19 +3,16 @@
3 3 <head>
4 4 <meta charset="UTF-8" />
5 5 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6   - <meta
7   - name="viewport"
8   - content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
9   - />
  6 + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
10 7 <title>霏微汽车服务平台</title>
11 8 <script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.data-set-0.9.6/dist/data-set.min.js"></script>
12 9 <!--加载鼠标绘制工具-->
13 10 <link rel="icon" href="/favicon.png" type="image/x-icon" />
14 11 <!-- 高德地图 -->
15 12 <script type="text/javascript">
16   - window._AMapSecurityConfig = {
17   - securityJsCode: '235e8683072929e38b792ded30736d2d',
18   - }
  13 + window._AMapSecurityConfig = {
  14 + securityJsCode: "235e8683072929e38b792ded30736d2d",
  15 + };
19 16 </script>
20 17 </head>
21 18 <body>
... ... @@ -23,34 +20,34 @@
23 20 <div id="root">
24 21 <style>
25 22 .page-loading-warp {
26   - padding: 120px;
27 23 display: flex;
28   - justify-content: center;
29 24 align-items: center;
  25 + justify-content: center;
  26 + padding: 120px;
30 27 }
31 28 .ant-spin {
  29 + position: absolute;
  30 + display: none;
32 31 -webkit-box-sizing: border-box;
33 32 box-sizing: border-box;
34 33 margin: 0;
35 34 padding: 0;
36 35 color: rgba(0, 0, 0, 0.65);
  36 + color: #1890ff;
37 37 font-size: 14px;
38 38 font-variant: tabular-nums;
39 39 line-height: 1.5;
40   - list-style: none;
41   - -webkit-font-feature-settings: 'tnum';
42   - font-feature-settings: 'tnum';
43   - position: absolute;
44   - display: none;
45   - color: #1890ff;
46 40 text-align: center;
47 41 vertical-align: middle;
  42 + list-style: none;
48 43 opacity: 0;
49 44 -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
50 45 transition: -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
51 46 transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
52 47 transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
53 48 -webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
  49 + -webkit-font-feature-settings: "tnum";
  50 + font-feature-settings: "tnum";
54 51 }
55 52  
56 53 .ant-spin-spinning {
... ... @@ -62,9 +59,9 @@
62 59 .ant-spin-dot {
63 60 position: relative;
64 61 display: inline-block;
65   - font-size: 20px;
66 62 width: 20px;
67 63 height: 20px;
  64 + font-size: 20px;
68 65 }
69 66  
70 67 .ant-spin-dot-item {
... ... @@ -120,9 +117,9 @@
120 117 }
121 118  
122 119 .ant-spin-lg .ant-spin-dot {
123   - font-size: 32px;
124 120 width: 32px;
125 121 height: 32px;
  122 + font-size: 32px;
126 123 }
127 124  
128 125 .ant-spin-lg .ant-spin-dot i {
... ... @@ -166,11 +163,12 @@
166 163 <div class="page-loading-warp">
167 164 <div class="ant-spin ant-spin-lg ant-spin-spinning">
168 165 <span class="ant-spin-dot ant-spin-dot-spin"
169   - ><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i
170   - ><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i
  166 + ><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i
  167 + ><i class="ant-spin-dot-item"></i
171 168 ></span>
172 169 </div>
173 170 </div>
174 171 </div>
  172 + <button id="fw_updater" style="display: none"></button>
175 173 </body>
176 174 </html>
... ...
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&lt;boolean&gt; {
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,103 @@ 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 + if (record.dataId === -999) {
  80 + return <div style={{ textAlign: "center" }}>-</div>;
  81 + }
  82 + return ModifiedTableCell(record, "vehicleGrossProfitTask");
  83 + },
  84 + },
  85 + {
  86 + title: "线索到店成交(台)",
  87 + width: 100,
  88 + dataIndex: "clueDealTaskCount",
  89 + render: (text: string, record: API.TaskListItem) => {
  90 + return ModifiedTableCell(record, "clueDealTaskCount");
  91 + },
  92 + },
  93 + {
  94 + title: "首客试驾成交(台)",
  95 + width: 100,
  96 + dataIndex: "testDriveTaskCount",
  97 + render: (text: string, record: API.TaskListItem) => {
  98 + return ModifiedTableCell(record, "testDriveTaskCount");
  99 + },
  100 + },
  101 + {
  102 + title: "攻坚车任务(台)",
  103 + width: 100,
  104 + dataIndex: "tackCarTaskCount",
  105 + render: (text: string, record: API.TaskListItem) => {
  106 + return ModifiedTableCell(record, "tackCarTaskCount");
  107 + },
  108 + },
  109 + {
  110 + title: "车系任务(台)",
  111 + width: 100,
  112 + dataIndex: "seriesTaskCount",
  113 + render: (text: string, record: API.TaskListItem) => {
  114 + if (record.dataId === -999) return text;
  115 + return <a onClick={() => handlePreviewSeriesTask(record)}>{text}</a>;
  116 + },
  117 + },
  118 + ];
  119 +
34 120 return (
35 121 <Table
  122 + bordered
36 123 rowKey="dataId"
37 124 loading={loading}
  125 + columns={columns}
38 126 dataSource={data.taskList}
39 127 pagination={false}
40 128 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>
  129 + />
101 130 );
102 131 };
103 132  
... ...
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,174 @@ 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 + if (record.dataId === -999) {
  127 + return <div style={{ textAlign: "center" }}>-</div>;
  128 + }
  129 + return ModifiedTableCell(record, "vehicleGrossProfitTask");
  130 + },
  131 + },
  132 + {
  133 + title: "线索到店成交(台)",
  134 + width: 100,
  135 + dataIndex: "clueDealTaskCount",
  136 + render: (text: string, record: API.TaskListItem) => {
  137 + return ModifiedTableCell(record, "clueDealTaskCount");
  138 + },
  139 + },
  140 + {
  141 + title: "首客试驾成交(台)",
  142 + width: 100,
  143 + dataIndex: "testDriveTaskCount",
  144 + render: (text: string, record: API.TaskListItem) => {
  145 + return ModifiedTableCell(record, "testDriveTaskCount");
  146 + },
  147 + },
  148 + {
  149 + title: "攻坚车任务(台)",
  150 + width: 100,
  151 + dataIndex: "tackCarTaskCount",
  152 + render: (text: string, record: API.TaskListItem) => {
  153 + return ModifiedTableCell(record, "tackCarTaskCount");
  154 + },
  155 + },
  156 + {
  157 + title: "车系任务(台)",
  158 + width: 100,
  159 + dataIndex: "seriesTaskCount",
  160 + render: (text: string, record: API.TaskListItem) => {
  161 + if (record.dataId === -999) return text;
  162 + return <a onClick={() => handlePreviewSeriesTask(record)}>{text}</a>;
  163 + },
  164 + },
  165 + ];
  166 +
  167 + const extraColumns: ColumnsType<API.TaskListItem> = [
  168 + {
  169 + title: "销顾任务",
  170 + render: (text: string, record: API.TaskListItem) => {
  171 + if (record.dataId === -999) {
  172 + return "-";
  173 + }
  174 + return <a onClick={() => handlePreviewAdviserTask(record)}>查看</a>;
  175 + },
  176 + },
  177 + ];
  178 +
  179 + return (
  180 + <>
  181 + <Row align="middle" justify="start" style={{ marginBottom: 20 }}>
  182 + <DatePicker
  183 + style={{ width: 245 }}
  184 + picker="month"
  185 + value={month}
  186 + allowClear={false}
  187 + disabled
163 188 />
164   - <Column
165   - title="攻坚车任务数(台)"
166   - dataIndex="tackCarTaskCount"
167   - render={(text: string, record: API.TaskListItem) => {
168   - return ModifiedTableCell(record, "tackCarTaskCount");
  189 + <Input.Search
  190 + allowClear
  191 + placeholder="搜索门店或顾问"
  192 + style={{ width: 263, marginLeft: 20 }}
  193 + value={keywords}
  194 + onChange={(e) => setKeywords(e.target.value)}
  195 + onSearch={(v) => {
  196 + setParams({ keywords: v }, true);
169 197 }}
170 198 />
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   - }}
  199 + </Row>
  200 + <Card
  201 + title={
  202 + <Row align="middle" justify="start">
  203 + <RadioGroup onChange={handleChangeType} value={type}>
  204 + <RadioButton value={OrderTaskApprovalType.门店维度}>
  205 + 门店
  206 + </RadioButton>
  207 + <RadioButton value={OrderTaskApprovalType.销售顾问维度}>
  208 + 销顾
  209 + </RadioButton>
  210 + <RadioButton value={99}>销售管理层</RadioButton>
  211 + </RadioGroup>
  212 + {type !== OrderTaskApprovalType.门店维度 &&
  213 + type !== OrderTaskApprovalType.销售顾问维度 && (
  214 + <RadioGroup
  215 + onChange={handleChangeType}
  216 + value={type}
  217 + style={{ marginLeft: 20 }}
  218 + >
  219 + <RadioButton value={OrderTaskApprovalType.新车一级管理维度}>
  220 + 销售一级管理
  221 + </RadioButton>
  222 + <RadioButton value={OrderTaskApprovalType.新车二级管理维度}>
  223 + 销售二级管理
  224 + </RadioButton>
  225 + <RadioButton value={OrderTaskApprovalType.新车三级管理维度}>
  226 + 销售三级管理
  227 + </RadioButton>
  228 + </RadioGroup>
  229 + )}
  230 + </Row>
  231 + }
  232 + >
  233 + <Table
  234 + bordered
  235 + rowKey="dataId"
  236 + columns={
  237 + type === OrderTaskApprovalType.门店维度
  238 + ? columns.concat(extraColumns)
  239 + : columns
  240 + }
  241 + loading={loading}
  242 + dataSource={data.taskList}
  243 + pagination={false}
  244 + scroll={{ y: 450 }}
180 245 />
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>
  246 + </Card>
  247 + </>
194 248 );
195 249 };
196 250  
... ...
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 ]}
... ...
src/pages/performance/EvaGroupSetting/EditComfirm/components/AddCommissionParamsModal.tsx
... ... @@ -110,11 +110,7 @@ export default function AddCommissionParamsModal(props: Props) {
110 110 }
111 111 function handSubmit(fieldsValue: any) {
112 112 const pa: any = transformDTO(fieldsValue);
113   - // if (pa.targetValue) {
114 113 pa.targetType = targetType;
115   - // } else {
116   - // pa.targetType = 1;
117   - // }
118 114 pa.codeType = codeType;
119 115 pa.dataType = dataType;
120 116 console.log("100pa", pa);
... ... @@ -123,7 +119,6 @@ export default function AddCommissionParamsModal(props: Props) {
123 119 if (!comItem.code) {
124 120 const tmpIds = [...selectedIndicators];
125 121 tmpIds.push(newItemId);
126   - // setSelectedIndicators([...tmpIds]);
127 122 } else {
128 123 pa.code = comItem.code;
129 124 pa.name = comItem.name;
... ...
src/pages/performance/EvaGroupSetting/EditComfirm/components/AddCommissionParamsModalSal.tsx
... ... @@ -110,7 +110,11 @@ export default function AddCommissionParamsModal(props: Props) {
110 110 }
111 111 function handSubmit(fieldsValue: any) {
112 112 const pa: any = transformDTO(fieldsValue);
113   - pa.targetType = targetType;
  113 + if (pa.targetValue !== undefined) {
  114 + pa.targetType = targetType;
  115 + } else {
  116 + pa.targetType = 1;
  117 + }
114 118 pa.codeType = codeType;
115 119 pa.dataType = dataType;
116 120 console.log("100pa", pa);
... ... @@ -124,21 +128,6 @@ export default function AddCommissionParamsModal(props: Props) {
124 128 pa.code = comItem.code;
125 129 pa.name = comItem.name;
126 130 }
127   - pa.extraTargetType = pa.targetType;
128   - if (comItem.codeType == 1 && multiStage) {
129   - if (pa.targetValue) {
130   - pa.targetType = targetType;
131   - pa.targetCalcType = 3;
132   - } else {
133   - pa.targetType = 1;
134   - }
135   - if (pa.extraTargetValue) {
136   - pa.extraTargetType = targetType;
137   - pa.extraTargetCalcType = 4;
138   - } else {
139   - pa.extraTargetType = 1;
140   - }
141   - }
142 131 onOk(pa);
143 132 onCancel && onCancel();
144 133 }
... ... @@ -156,13 +145,7 @@ export default function AddCommissionParamsModal(props: Props) {
156 145 width={1000}
157 146 >
158 147 <Spin spinning={loading}>
159   - <Form
160   - form={form}
161   - // labelCol={{ span: 6 }}
162   - // wrapperCol={{ span: 18 }}
163   - onFinish={handSubmit}
164   - style={{ width: "70%", marginLeft: 150 }}
165   - >
  148 + <Form form={form} onFinish={handSubmit} style={{ width: "70%", marginLeft: 150 }}>
166 149 <Form.Item name="commissionParams" label="计分指标" rules={[{ required: true, message: "计分指标" }]}>
167 150 <Select
168 151 placeholder="选择指标(*为考评指标,无*为绩效指标)"
... ... @@ -233,127 +216,38 @@ export default function AddCommissionParamsModal(props: Props) {
233 216 <Form.Item label="考核目标名称" name="targetName">
234 217 <Select disabled defaultValue={indicatorName} />
235 218 </Form.Item>
236   - {comItem.codeType == 1 && multiStage ? (
237   - <>
238   - <Form.Item
239   - noStyle
240   - shouldUpdate={(prevValues, currentValues) =>
241   - prevValues.extraTargetValue !== currentValues.extraTargetValue
242   - }
243   - >
244   - {({ getFieldValue }) => {
245   - const extraTargetValue = getFieldValue("extraTargetValue");
246   - return (
247   - <>
248   - <Form.Item
249   - label="阶段目标考核值"
250   - name="targetValue"
251   - rules={[
252   - { required: !extraTargetValue, message: "请输入阶段目标考核值" },
253   - {
254   - pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
255   - message:
256   - targetType === TargetTypeEnum["百分比"]
257   - ? "请输入大于0小于等于100的数(保留两位小数)"
258   - : "请输入大于0的数(保留两位小数)",
259   - },
260   - ]}
261   - >
262   - <InputNumber
263   - placeholder="请输入阶段目标考核值"
264   - style={{ width: "100%" }}
265   - addonAfter={
266   - targetType === TargetTypeEnum["百分比"]
267   - ? "%"
268   - : targetType === TargetTypeEnum["金额"]
269   - ? "元"
270   - : "台"
271   - }
272   - />
273   - </Form.Item>
274   - </>
275   - );
276   - }}
277   - </Form.Item>
278   - <Form.Item
279   - noStyle
280   - shouldUpdate={(prevValues, currentValues) => prevValues.targetValue !== currentValues.targetValue}
281   - >
282   - {({ getFieldValue }) => {
283   - const targetValue = getFieldValue("targetValue");
284   - return (
285   - <>
286   - <Form.Item
287   - label="时间进度目标值"
288   - name="extraTargetValue"
289   - rules={[
290   - { required: !targetValue, message: "请输入时间进度目标值" },
291   - {
292   - pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
293   - message:
294   - targetType === TargetTypeEnum["百分比"]
295   - ? "请输入大于0小于等于100的数(保留两位小数)"
296   - : "请输入大于0的数(保留两位小数)",
297   - },
298   - ]}
299   - >
300   - <InputNumber
301   - placeholder="请输入时间进度目标值"
302   - style={{ width: "100%" }}
303   - addonAfter={
304   - targetType === TargetTypeEnum["百分比"]
305   - ? "%"
306   - : targetType === TargetTypeEnum["金额"]
307   - ? "元"
308   - : "台"
309   - }
310   - />
311   - </Form.Item>
312   - </>
313   - );
314   - }}
315   - </Form.Item>
316   - </>
317   - ) : (
318   - <>
319   - <Form.Item
320   - label="考核目标计算类型"
321   - name="targetCalcType"
322   - rules={[{ required: true, message: "请选择考核目标计算类型" }]}
323   - >
324   - <Radio.Group>
325   - <Radio value={1}>考核目标值计算 </Radio>
326   - <Radio value={2}>最低要求计算</Radio>
327   - </Radio.Group>
328   - </Form.Item>
329   - <Form.Item
330   - label="考核目标值"
331   - name="targetValue"
332   - rules={[
333   - { required: true, message: "请输入考核目标值" },
334   - {
335   - pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
336   - message:
337   - targetType === TargetTypeEnum["百分比"]
338   - ? "请输入大于0小于等于100的数(保留两位小数)"
339   - : "请输入大于0的数(保留两位小数)",
340   - },
341   - ]}
342   - >
343   - <InputNumber
344   - placeholder="请输入考核目标值"
345   - style={{ width: "100%" }}
346   - addonAfter={
347   - targetType === TargetTypeEnum["百分比"]
348   - ? "%"
349   - : targetType === TargetTypeEnum["金额"]
350   - ? "元"
351   - : "台"
352   - }
353   - />
354   - </Form.Item>
355   - </>
356   - )}
  219 + <Form.Item
  220 + label="考核目标计算类型"
  221 + name="targetCalcType"
  222 + rules={[{ required: true, message: "请选择考核目标计算类型" }]}
  223 + >
  224 + <Radio.Group>
  225 + <Radio value={1}>考核目标值计算 </Radio>
  226 + <Radio value={2}>最低要求计算</Radio>
  227 + </Radio.Group>
  228 + </Form.Item>
  229 + <Form.Item
  230 + label="考核目标值"
  231 + name="targetValue"
  232 + rules={[
  233 + { required: true, message: "请输入考核目标值" },
  234 + {
  235 + pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
  236 + message:
  237 + targetType === TargetTypeEnum["百分比"]
  238 + ? "请输入大于0小于等于100的数(保留两位小数)"
  239 + : "请输入大于0的数(保留两位小数)",
  240 + },
  241 + ]}
  242 + >
  243 + <InputNumber
  244 + placeholder="请输入考核目标值"
  245 + style={{ width: "100%" }}
  246 + addonAfter={
  247 + targetType === TargetTypeEnum["百分比"] ? "%" : targetType === TargetTypeEnum["金额"] ? "元" : "台"
  248 + }
  249 + />
  250 + </Form.Item>
357 251 </>
358 252 )}
359 253 </Form>
... ...
src/pages/performance/EvaGroupSetting/EditComfirm/components/AddCommissionParamsModalSalShop.tsx
... ... @@ -111,7 +111,11 @@ export default function AddCommissionParamsModal(props: Props) {
111 111 }
112 112 function handSubmit(fieldsValue: any) {
113 113 const pa: any = transformDTO(fieldsValue);
114   - pa.targetType = targetType;
  114 + if (pa.targetValue !== undefined) {
  115 + pa.targetType = targetType;
  116 + } else {
  117 + pa.targetType = 1;
  118 + }
115 119 pa.codeType = codeType;
116 120 pa.dataType = dataType;
117 121 console.log("100pa", pa);
... ... @@ -125,21 +129,6 @@ export default function AddCommissionParamsModal(props: Props) {
125 129 pa.code = comItem.code;
126 130 pa.name = comItem.name;
127 131 }
128   - pa.extraTargetType = pa.targetType;
129   - if (comItem.codeType == 1 && multiStage) {
130   - if (pa.targetValue) {
131   - pa.targetType = targetType;
132   - pa.targetCalcType = 3;
133   - } else {
134   - pa.targetType = 1;
135   - }
136   - if (pa.extraTargetValue) {
137   - pa.extraTargetType = targetType;
138   - pa.extraTargetCalcType = 4;
139   - } else {
140   - pa.extraTargetType = 1;
141   - }
142   - }
143 132 onOk(pa);
144 133 onCancel && onCancel();
145 134 }
... ... @@ -157,13 +146,7 @@ export default function AddCommissionParamsModal(props: Props) {
157 146 width={1000}
158 147 >
159 148 <Spin spinning={loading}>
160   - <Form
161   - form={form}
162   - // labelCol={{ span: 6 }}
163   - // wrapperCol={{ span: 18 }}
164   - onFinish={handSubmit}
165   - style={{ width: "70%", marginLeft: 150 }}
166   - >
  149 + <Form form={form} onFinish={handSubmit} style={{ width: "70%", marginLeft: 150 }}>
167 150 <Form.Item name="commissionParams" label="计分指标" rules={[{ required: true, message: "计分指标" }]}>
168 151 <Select
169 152 placeholder="选择指标(*为考评指标,无*为绩效指标)"
... ... @@ -234,127 +217,38 @@ export default function AddCommissionParamsModal(props: Props) {
234 217 <Form.Item label="考核目标名称" name="targetName">
235 218 <Select disabled defaultValue={indicatorName} />
236 219 </Form.Item>
237   - {comItem.codeType == 1 && multiStage ? (
238   - <>
239   - <Form.Item
240   - noStyle
241   - shouldUpdate={(prevValues, currentValues) =>
242   - prevValues.extraTargetValue !== currentValues.extraTargetValue
243   - }
244   - >
245   - {({ getFieldValue }) => {
246   - const extraTargetValue = getFieldValue("extraTargetValue");
247   - return (
248   - <>
249   - <Form.Item
250   - label="阶段目标考核值"
251   - name="targetValue"
252   - rules={[
253   - { required: !extraTargetValue, message: "请输入阶段目标考核值" },
254   - {
255   - pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
256   - message:
257   - targetType === TargetTypeEnum["百分比"]
258   - ? "请输入大于0小于等于100的数(保留两位小数)"
259   - : "请输入大于0的数(保留两位小数)",
260   - },
261   - ]}
262   - >
263   - <InputNumber
264   - placeholder="请输入阶段目标考核值"
265   - style={{ width: "100%" }}
266   - addonAfter={
267   - targetType === TargetTypeEnum["百分比"]
268   - ? "%"
269   - : targetType === TargetTypeEnum["金额"]
270   - ? "元"
271   - : "台"
272   - }
273   - />
274   - </Form.Item>
275   - </>
276   - );
277   - }}
278   - </Form.Item>
279   - <Form.Item
280   - noStyle
281   - shouldUpdate={(prevValues, currentValues) => prevValues.targetValue !== currentValues.targetValue}
282   - >
283   - {({ getFieldValue }) => {
284   - const targetValue = getFieldValue("targetValue");
285   - return (
286   - <>
287   - <Form.Item
288   - label="时间进度目标值"
289   - name="extraTargetValue"
290   - rules={[
291   - { required: !targetValue, message: "请输入时间进度目标值" },
292   - {
293   - pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
294   - message:
295   - targetType === TargetTypeEnum["百分比"]
296   - ? "请输入大于0小于等于100的数(保留两位小数)"
297   - : "请输入大于0的数(保留两位小数)",
298   - },
299   - ]}
300   - >
301   - <InputNumber
302   - placeholder="请输入时间进度目标值"
303   - style={{ width: "100%" }}
304   - addonAfter={
305   - targetType === TargetTypeEnum["百分比"]
306   - ? "%"
307   - : targetType === TargetTypeEnum["金额"]
308   - ? "元"
309   - : "台"
310   - }
311   - />
312   - </Form.Item>
313   - </>
314   - );
315   - }}
316   - </Form.Item>
317   - </>
318   - ) : (
319   - <>
320   - <Form.Item
321   - label="考核目标计算类型"
322   - name="targetCalcType"
323   - rules={[{ required: true, message: "请选择考核目标计算类型" }]}
324   - >
325   - <Radio.Group>
326   - <Radio value={1}>考核目标值计算 </Radio>
327   - <Radio value={2}>最低要求计算</Radio>
328   - </Radio.Group>
329   - </Form.Item>
330   - <Form.Item
331   - label="考核目标值"
332   - name="targetValue"
333   - rules={[
334   - { required: true, message: "请输入考核目标值" },
335   - {
336   - pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
337   - message:
338   - targetType === TargetTypeEnum["百分比"]
339   - ? "请输入大于0小于等于100的数(保留两位小数)"
340   - : "请输入大于0的数(保留两位小数)",
341   - },
342   - ]}
343   - >
344   - <InputNumber
345   - placeholder="请输入考核目标值"
346   - style={{ width: "100%" }}
347   - addonAfter={
348   - targetType === TargetTypeEnum["百分比"]
349   - ? "%"
350   - : targetType === TargetTypeEnum["金额"]
351   - ? "元"
352   - : "台"
353   - }
354   - />
355   - </Form.Item>
356   - </>
357   - )}
  220 + <Form.Item
  221 + label="考核目标计算类型"
  222 + name="targetCalcType"
  223 + rules={[{ required: true, message: "请选择考核目标计算类型" }]}
  224 + >
  225 + <Radio.Group>
  226 + <Radio value={1}>考核目标值计算 </Radio>
  227 + <Radio value={2}>最低要求计算</Radio>
  228 + </Radio.Group>
  229 + </Form.Item>
  230 + <Form.Item
  231 + label="考核目标值"
  232 + name="targetValue"
  233 + rules={[
  234 + { required: true, message: "请输入考核目标值" },
  235 + {
  236 + pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
  237 + message:
  238 + targetType === TargetTypeEnum["百分比"]
  239 + ? "请输入大于0小于等于100的数(保留两位小数)"
  240 + : "请输入大于0的数(保留两位小数)",
  241 + },
  242 + ]}
  243 + >
  244 + <InputNumber
  245 + placeholder="请输入考核目标值"
  246 + style={{ width: "100%" }}
  247 + addonAfter={
  248 + targetType === TargetTypeEnum["百分比"] ? "%" : targetType === TargetTypeEnum["金额"] ? "元" : "台"
  249 + }
  250 + />
  251 + </Form.Item>
358 252 </>
359 253 )}
360 254 </Form>
... ...
src/pages/performance/EvaGroupSetting/EditComfirm/components/AddLadderParamsModalSal.tsx
... ... @@ -103,7 +103,11 @@ export default function AddLadderParamsModal(props: Props) {
103 103 }
104 104 function handSubmit(fieldsValue: any) {
105 105 const pa: any = transformDTO(fieldsValue);
106   - pa.targetType = targetType;
  106 + if (pa.targetValue !== undefined) {
  107 + pa.targetType = targetType;
  108 + } else {
  109 + pa.targetType = 1;
  110 + }
107 111 pa.codeType = codeType;
108 112 if (comItem.dataType) {
109 113 pa.dataType = comItem.dataType;
... ... @@ -122,21 +126,6 @@ export default function AddLadderParamsModal(props: Props) {
122 126 pa.code = comItem.code;
123 127 pa.name = comItem.name;
124 128 }
125   - pa.extraTargetType = pa.targetType;
126   - if (comItem.codeType == 1 && multiStage) {
127   - if (pa.targetValue) {
128   - pa.targetType = targetType;
129   - pa.targetCalcType = 3;
130   - } else {
131   - pa.targetType = 1;
132   - }
133   - if (pa.extraTargetValue) {
134   - pa.extraTargetType = targetType;
135   - pa.extraTargetCalcType = 4;
136   - } else {
137   - pa.extraTargetType = 1;
138   - }
139   - }
140 129 onOk(pa);
141 130 onCancel && onCancel();
142 131 }
... ... @@ -154,13 +143,7 @@ export default function AddLadderParamsModal(props: Props) {
154 143 width={1000}
155 144 >
156 145 <Spin spinning={loading}>
157   - <Form
158   - form={form}
159   - // labelCol={{ span: 6 }}
160   - // wrapperCol={{ span: 18 }}
161   - onFinish={handSubmit}
162   - style={{ width: "70%", marginLeft: 150 }}
163   - >
  146 + <Form form={form} onFinish={handSubmit} style={{ width: "70%", marginLeft: 150 }}>
164 147 <Form.Item name="ladderParams" label="计分指标" rules={[{ required: true, message: "计分指标" }]}>
165 148 <Select
166 149 placeholder="选择指标(*为考评指标,无*为绩效指标)"
... ... @@ -231,127 +214,38 @@ export default function AddLadderParamsModal(props: Props) {
231 214 <Form.Item label="考核目标名称" name="targetName">
232 215 <Select disabled defaultValue={name} />
233 216 </Form.Item>
234   - {comItem.codeType == 1 && multiStage ? (
235   - <>
236   - <Form.Item
237   - noStyle
238   - shouldUpdate={(prevValues, currentValues) =>
239   - prevValues.extraTargetValue !== currentValues.extraTargetValue
240   - }
241   - >
242   - {({ getFieldValue }) => {
243   - const extraTargetValue = getFieldValue("extraTargetValue");
244   - return (
245   - <>
246   - <Form.Item
247   - label="阶段目标考核值"
248   - name="targetValue"
249   - rules={[
250   - { required: !extraTargetValue, message: "请输入阶段目标考核值" },
251   - {
252   - pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
253   - message:
254   - targetType === TargetTypeEnum["百分比"]
255   - ? "请输入大于0小于等于100的数(保留两位小数)"
256   - : "请输入大于0的数(保留两位小数)",
257   - },
258   - ]}
259   - >
260   - <InputNumber
261   - placeholder="请输入阶段目标考核值"
262   - style={{ width: "100%" }}
263   - addonAfter={
264   - targetType === TargetTypeEnum["百分比"]
265   - ? "%"
266   - : targetType === TargetTypeEnum["金额"]
267   - ? "元"
268   - : "台"
269   - }
270   - />
271   - </Form.Item>
272   - </>
273   - );
274   - }}
275   - </Form.Item>
276   - <Form.Item
277   - noStyle
278   - shouldUpdate={(prevValues, currentValues) => prevValues.targetValue !== currentValues.targetValue}
279   - >
280   - {({ getFieldValue }) => {
281   - const targetValue = getFieldValue("targetValue");
282   - return (
283   - <>
284   - <Form.Item
285   - label="时间进度目标值"
286   - name="extraTargetValue"
287   - rules={[
288   - { required: !targetValue, message: "请输入时间进度目标值" },
289   - {
290   - pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
291   - message:
292   - targetType === TargetTypeEnum["百分比"]
293   - ? "请输入大于0小于等于100的数(保留两位小数)"
294   - : "请输入大于0的数(保留两位小数)",
295   - },
296   - ]}
297   - >
298   - <InputNumber
299   - placeholder="请输入时间进度目标值"
300   - style={{ width: "100%" }}
301   - addonAfter={
302   - targetType === TargetTypeEnum["百分比"]
303   - ? "%"
304   - : targetType === TargetTypeEnum["金额"]
305   - ? "元"
306   - : "台"
307   - }
308   - />
309   - </Form.Item>
310   - </>
311   - );
312   - }}
313   - </Form.Item>
314   - </>
315   - ) : (
316   - <>
317   - <Form.Item
318   - label="考核目标计算类型"
319   - name="targetCalcType"
320   - rules={[{ required: true, message: "请选择考核目标计算类型" }]}
321   - >
322   - <Radio.Group>
323   - <Radio value={1}>考核目标值计算 </Radio>
324   - <Radio value={2}>最低要求计算</Radio>
325   - </Radio.Group>
326   - </Form.Item>
327   - <Form.Item
328   - label="考核目标值"
329   - name="targetValue"
330   - rules={[
331   - { required: true, message: "请输入考核目标值" },
332   - {
333   - pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
334   - message:
335   - targetType === TargetTypeEnum["百分比"]
336   - ? "请输入大于0小于等于100的数(保留两位小数)"
337   - : "请输入大于0的数(保留两位小数)",
338   - },
339   - ]}
340   - >
341   - <InputNumber
342   - placeholder="请输入考核目标值"
343   - style={{ width: "100%" }}
344   - addonAfter={
345   - targetType === TargetTypeEnum["百分比"]
346   - ? "%"
347   - : targetType === TargetTypeEnum["金额"]
348   - ? "元"
349   - : "台"
350   - }
351   - />
352   - </Form.Item>
353   - </>
354   - )}
  217 + <Form.Item
  218 + label="考核目标计算类型"
  219 + name="targetCalcType"
  220 + rules={[{ required: true, message: "请选择考核目标计算类型" }]}
  221 + >
  222 + <Radio.Group>
  223 + <Radio value={1}>考核目标值计算 </Radio>
  224 + <Radio value={2}>最低要求计算</Radio>
  225 + </Radio.Group>
  226 + </Form.Item>
  227 + <Form.Item
  228 + label="考核目标值"
  229 + name="targetValue"
  230 + rules={[
  231 + { required: true, message: "请输入考核目标值" },
  232 + {
  233 + pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
  234 + message:
  235 + targetType === TargetTypeEnum["百分比"]
  236 + ? "请输入大于0小于等于100的数(保留两位小数)"
  237 + : "请输入大于0的数(保留两位小数)",
  238 + },
  239 + ]}
  240 + >
  241 + <InputNumber
  242 + placeholder="请输入考核目标值"
  243 + style={{ width: "100%" }}
  244 + addonAfter={
  245 + targetType === TargetTypeEnum["百分比"] ? "%" : targetType === TargetTypeEnum["金额"] ? "元" : "台"
  246 + }
  247 + />
  248 + </Form.Item>
355 249 </>
356 250 )}
357 251 </Form>
... ...
src/pages/performance/EvaGroupSetting/EditComfirm/components/AddLadderParamsModalSalShop.tsx
... ... @@ -104,7 +104,11 @@ export default function AddLadderParamsModal(props: Props) {
104 104 }
105 105 function handSubmit(fieldsValue: any) {
106 106 const pa: any = transformDTO(fieldsValue);
107   - pa.targetType = targetType;
  107 + if (pa.targetValue !== undefined) {
  108 + pa.targetType = targetType;
  109 + } else {
  110 + pa.targetType = 1;
  111 + }
108 112 pa.codeType = codeType;
109 113 if (comItem.dataType) {
110 114 pa.dataType = comItem.dataType;
... ... @@ -123,21 +127,6 @@ export default function AddLadderParamsModal(props: Props) {
123 127 pa.code = comItem.code;
124 128 pa.name = comItem.name;
125 129 }
126   - pa.extraTargetType = pa.targetType;
127   - if (comItem.codeType == 1 && multiStage) {
128   - if (pa.targetValue) {
129   - pa.targetType = targetType;
130   - pa.targetCalcType = 3;
131   - } else {
132   - pa.targetType = 1;
133   - }
134   - if (pa.extraTargetValue) {
135   - pa.extraTargetType = targetType;
136   - pa.extraTargetCalcType = 4;
137   - } else {
138   - pa.extraTargetType = 1;
139   - }
140   - }
141 130 onOk(pa);
142 131 onCancel && onCancel();
143 132 }
... ... @@ -155,13 +144,7 @@ export default function AddLadderParamsModal(props: Props) {
155 144 width={1000}
156 145 >
157 146 <Spin spinning={loading}>
158   - <Form
159   - form={form}
160   - // labelCol={{ span: 6 }}
161   - // wrapperCol={{ span: 18 }}
162   - onFinish={handSubmit}
163   - style={{ width: "70%", marginLeft: 150 }}
164   - >
  147 + <Form form={form} onFinish={handSubmit} style={{ width: "70%", marginLeft: 150 }}>
165 148 <Form.Item name="ladderParams" label="计分指标" rules={[{ required: true, message: "计分指标" }]}>
166 149 <Select
167 150 placeholder="选择指标(*为考评指标,无*为绩效指标)"
... ... @@ -232,127 +215,38 @@ export default function AddLadderParamsModal(props: Props) {
232 215 <Form.Item label="考核目标名称" name="targetName">
233 216 <Select disabled defaultValue={name} />
234 217 </Form.Item>
235   - {comItem.codeType == 1 && multiStage ? (
236   - <>
237   - <Form.Item
238   - noStyle
239   - shouldUpdate={(prevValues, currentValues) =>
240   - prevValues.extraTargetValue !== currentValues.extraTargetValue
241   - }
242   - >
243   - {({ getFieldValue }) => {
244   - const extraTargetValue = getFieldValue("extraTargetValue");
245   - return (
246   - <>
247   - <Form.Item
248   - label="阶段目标考核值"
249   - name="targetValue"
250   - rules={[
251   - { required: !extraTargetValue, message: "请输入阶段目标考核值" },
252   - {
253   - pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
254   - message:
255   - targetType === TargetTypeEnum["百分比"]
256   - ? "请输入大于0小于等于100的数(保留两位小数)"
257   - : "请输入大于0的数(保留两位小数)",
258   - },
259   - ]}
260   - >
261   - <InputNumber
262   - placeholder="请输入阶段目标考核值"
263   - style={{ width: "100%" }}
264   - addonAfter={
265   - targetType === TargetTypeEnum["百分比"]
266   - ? "%"
267   - : targetType === TargetTypeEnum["金额"]
268   - ? "元"
269   - : "台"
270   - }
271   - />
272   - </Form.Item>
273   - </>
274   - );
275   - }}
276   - </Form.Item>
277   - <Form.Item
278   - noStyle
279   - shouldUpdate={(prevValues, currentValues) => prevValues.targetValue !== currentValues.targetValue}
280   - >
281   - {({ getFieldValue }) => {
282   - const targetValue = getFieldValue("targetValue");
283   - return (
284   - <>
285   - <Form.Item
286   - label="时间进度目标值"
287   - name="extraTargetValue"
288   - rules={[
289   - { required: !targetValue, message: "请输入时间进度目标值" },
290   - {
291   - pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
292   - message:
293   - targetType === TargetTypeEnum["百分比"]
294   - ? "请输入大于0小于等于100的数(保留两位小数)"
295   - : "请输入大于0的数(保留两位小数)",
296   - },
297   - ]}
298   - >
299   - <InputNumber
300   - placeholder="请输入时间进度目标值"
301   - style={{ width: "100%" }}
302   - addonAfter={
303   - targetType === TargetTypeEnum["百分比"]
304   - ? "%"
305   - : targetType === TargetTypeEnum["金额"]
306   - ? "元"
307   - : "台"
308   - }
309   - />
310   - </Form.Item>
311   - </>
312   - );
313   - }}
314   - </Form.Item>
315   - </>
316   - ) : (
317   - <>
318   - <Form.Item
319   - label="考核目标计算类型"
320   - name="targetCalcType"
321   - rules={[{ required: true, message: "请选择考核目标计算类型" }]}
322   - >
323   - <Radio.Group>
324   - <Radio value={1}>考核目标值计算 </Radio>
325   - <Radio value={2}>最低要求计算</Radio>
326   - </Radio.Group>
327   - </Form.Item>
328   - <Form.Item
329   - label="考核目标值"
330   - name="targetValue"
331   - rules={[
332   - { required: true, message: "请输入考核目标值" },
333   - {
334   - pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
335   - message:
336   - targetType === TargetTypeEnum["百分比"]
337   - ? "请输入大于0小于等于100的数(保留两位小数)"
338   - : "请输入大于0的数(保留两位小数)",
339   - },
340   - ]}
341   - >
342   - <InputNumber
343   - placeholder="请输入考核目标值"
344   - style={{ width: "100%" }}
345   - addonAfter={
346   - targetType === TargetTypeEnum["百分比"]
347   - ? "%"
348   - : targetType === TargetTypeEnum["金额"]
349   - ? "元"
350   - : "台"
351   - }
352   - />
353   - </Form.Item>
354   - </>
355   - )}
  218 + <Form.Item
  219 + label="考核目标计算类型"
  220 + name="targetCalcType"
  221 + rules={[{ required: true, message: "请选择考核目标计算类型" }]}
  222 + >
  223 + <Radio.Group>
  224 + <Radio value={1}>考核目标值计算 </Radio>
  225 + <Radio value={2}>最低要求计算</Radio>
  226 + </Radio.Group>
  227 + </Form.Item>
  228 + <Form.Item
  229 + label="考核目标值"
  230 + name="targetValue"
  231 + rules={[
  232 + { required: true, message: "请输入考核目标值" },
  233 + {
  234 + pattern: targetType === TargetTypeEnum["百分比"] ? percent : Momney,
  235 + message:
  236 + targetType === TargetTypeEnum["百分比"]
  237 + ? "请输入大于0小于等于100的数(保留两位小数)"
  238 + : "请输入大于0的数(保留两位小数)",
  239 + },
  240 + ]}
  241 + >
  242 + <InputNumber
  243 + placeholder="请输入考核目标值"
  244 + style={{ width: "100%" }}
  245 + addonAfter={
  246 + targetType === TargetTypeEnum["百分比"] ? "%" : targetType === TargetTypeEnum["金额"] ? "元" : "台"
  247 + }
  248 + />
  249 + </Form.Item>
356 250 </>
357 251 )}
358 252 </Form>
... ...
src/pages/pms/partPlan/PlanManage/subpages/Apply/components/PartModal.tsx
... ... @@ -13,12 +13,15 @@ interface Props {
13 13 parts: any[]
14 14 onOk: (parts: any[]) => any
15 15 setParams: Function
  16 + _supplierId: number | undefined
16 17 }
17 18 const {Option} = Select;
18   -export default function Index({ onCancel, visible, parts=[], onOk, setParams }: Props) {
  19 +export default function Index({ onCancel, visible, parts = [], onOk, setParams, _supplierId }: Props) {
19 20 const [selectedParts, setSelectedParts] = useState<any[]>([]);
20 21 const [dfParam, setDfParam] = useState<any>({keywords: ''});
21 22 const [partList, setPartList] = useState(parts);
  23 + const [info, setInfo] = useState<{open: boolean, partName: string, poolId?: number}>({open: false, partName: '', poolId: undefined});
  24 + const [partArr, setPartArr] = useState<any[]>([]);
22 25  
23 26 const shopNames = useMemo(() => {
24 27 return Array.from(new Set(parts.map(it => it.shopName || '').filter(it => !!it)));
... ... @@ -37,6 +40,7 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }:
37 40 useEffect(() => {
38 41 if (!visible) {
39 42 setSelectedParts([]);
  43 + setPartArr([]);
40 44 setPartList(parts);
41 45 }
42 46 }, [visible, parts]);
... ... @@ -46,6 +50,14 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }:
46 50 message.error("请选择配件");
47 51 return;
48 52 }
  53 + if (partArr.some(it => !!it.supplierId)) {
  54 + for (const item of partArr) {
  55 + if (!!item.supplierId && item.supplierId != _supplierId) {
  56 + setInfo({ open: true, partName: item.partName, poolId: item.poolId });
  57 + return;
  58 + }
  59 + }
  60 + }
49 61 onOk && onOk(selectedParts);
50 62 onCancel && onCancel();
51 63 }
... ... @@ -81,6 +93,7 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }:
81 93 visible={visible}
82 94 title="配件采购明细"
83 95 onCancel={onCancel}
  96 + maskClosable={false}
84 97 footer={[
85 98 <Button key="cancel" onClick={onCancel}>取消</Button>,
86 99 <Button
... ... @@ -195,6 +208,7 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }:
195 208 } else if (index > -1) {
196 209 newData.splice(index, 1);
197 210 }
  211 + setPartArr(newData);
198 212 setSelectedParts([...newData]);
199 213 },
200 214 onSelectAll: (selected, selectedRows, changeRows) => {
... ... @@ -206,6 +220,7 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }:
206 220 } else {
207 221 newData = selectedParts.filter(row => !changedKeys.includes(`${row.poolId}`));
208 222 }
  223 + setPartArr(newData);
209 224 setSelectedParts(newData);
210 225 },
211 226 }}
... ... @@ -220,6 +235,25 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }:
220 235 <Column title="配件来源类型" dataIndex="typeName" />
221 236 <Column title="上次采购供应商" dataIndex="supplierName" />
222 237 </Table>
  238 + <Modal
  239 + title="提示"
  240 + open={info.open}
  241 + maskClosable={false}
  242 + closable={false}
  243 + cancelText="重新选择"
  244 + okText="继续"
  245 + onCancel={() => {
  246 + setPartArr(partArr.filter((i => i.poolId != info.poolId)));
  247 + setSelectedParts(selectedParts.filter((i => i.poolId != info.poolId)));
  248 + setInfo({open: false, partName: '', poolId: undefined});
  249 + }}
  250 + onOk={() => {
  251 + setPartArr(partArr.filter((i => i.poolId != info.poolId)));
  252 + setInfo({ open: false, partName: '', poolId: undefined });
  253 + }}
  254 + >
  255 + <p style={{ color: 'red' }}><span style={{ color: '#333333', fontWeight: '500', marginRight: 3}}>【{info.partName}】</span>本次采购供应商和上次采购供应商不一致,是否继续?</p>
  256 + </Modal>
223 257 </Modal>
224 258 );
225 259 }
... ...
src/pages/pms/partPlan/PlanManage/subpages/Apply/index.tsx
... ... @@ -41,6 +41,7 @@ export default function Index() {
41 41 const partList = flattenDeep(dealerList.map(it => (it.suppliers || []).map((su: any) => (su.storages || []).map((st: any) => (st.parts || [])))));
42 42 const poolIds = partList.map((it: any) => it.poolId);
43 43 const [info, setInfo] = useState<{ remark?: string, fids?: any }>();
  44 + const [_supplierId, setSupplierId] = useState<number| undefined>(undefined);
44 45  
45 46 useEffect(() => {
46 47 if (planId?.planId) {
... ... @@ -254,7 +255,7 @@ export default function Index() {
254 255 <div key={`supplier${supplier.supplierId}`} style={{ marginTop: 10, marginLeft: 40 }}>
255 256 <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center'}}>
256 257 <div style={{ fontWeight: "bold" }}>{`供应商: ${supplier.supplierName || ''}`}</div>
257   - <a style={{marginLeft: 20}} onClick={() => { setVisiblePart(true); setDealer({...dealer, ...supplier}); }}>
  258 + <a style={{ marginLeft: 20 }} onClick={() => { setVisiblePart(true); setDealer({ ...dealer, ...supplier }); setSupplierId(supplier.supplierId); }}>
258 259 添加采购配件
259 260 </a>
260 261 <Popconfirm
... ... @@ -322,6 +323,7 @@ export default function Index() {
322 323 parts={parts.filter(it => !poolIds.includes(it.poolId))}
323 324 onOk={onOk}
324 325 setParams={setParams}
  326 + _supplierId={_supplierId}
325 327 />
326 328 <PartDetailModal
327 329 visible={visiblePartDetail}
... ...
src/pages/pms/partPlan/PlanShipping/api.ts
... ... @@ -36,10 +36,91 @@ export interface Item {
36 36 otherRatio?: number; // 对方责任占比(几成)
37 37 againCnt?: number; // 补发数量
38 38 }
  39 +export interface Detail {
  40 + brandId?:number
  41 + // int64
  42 + // 品牌ID
  43 + // 510
  44 + brandName?:
  45 + string
  46 + // 品牌名称
  47 +// opal.prohaska
  48 +supplierId?:number
  49 +// int64
  50 +// 供应商id
  51 +// 950
  52 +partKind?:number
  53 +// int32
  54 +// 配件种类
  55 +// 863
  56 +supplierName?:
  57 +string
  58 +// 供应商名称
  59 +// opal.prohaska
  60 +shopId?:number
  61 +// int64
  62 +// 服务站id
  63 +// 705/
  64 +shopName?:
  65 +string
  66 +// 门店名称
  67 +// opal.prohaska
  68 +storageId?:number
  69 +// int64
  70 +// 库房id
  71 +// 563
  72 +storageName?:
  73 +string
  74 +// 库房名称
  75 +// opal.prohaska
  76 +groupId?:number
  77 +// int64
  78 +// 集团ID
  79 +// 33/
  80 +shippingNo?:
  81 +string
  82 +// 发运单号
  83 +// zt910g
  84 +shippingDate?:
  85 +string
  86 +// 发运日期?;
  87 +// 2023 - 05 - 23
  88 +totalAmount?:
  89 +number
  90 +// 总金额
  91 +// 904
  92 +payToken?:
  93 +string
  94 +// 待付token
  95 +// wywo3x
  96 +importUserName?:
  97 +string
  98 +// 导入人员
  99 +// opal.prohaska
  100 +inStorageUserName?:
  101 +string
  102 +// 入库人员
  103 +// opal.prohaska
  104 +status?:number
  105 +// enum
  106 + // 状态0待确认1待入库2已完成9作废(See: 配件发运单状态
  107 +// create at 2020-03 - 18)
  108 +
  109 +settleDealerId?:number
  110 +// int64
  111 +// 结算商家/Id
  112 +// 115
  113 +settleDealerName?:
  114 +string
  115 +// 结算商家名称
  116 + settleShopId?:number
  117 + settleShopName?:string
  118 + list?: DetailItem[]
  119 +}
39 120 /**
40 121 * 明细
41 122 */
42   -export interface Detail {
  123 +export interface DetailItem {
43 124 supplierName?: string; // 供应商名称
44 125 partId?: number; // 配件ID
45 126 partName?: string; // 配件名称
... ... @@ -68,8 +149,8 @@ export function getList(params?: Params): http.PromisePageResp&lt;Item&gt; {
68 149 /**
69 150 * 查询明细
70 151 */
71   -export function getDetail(shippingNo?: string): http.PromiseResp<Detail[]> {
72   - return request.get(`${PMS_HOST}/app/part/shipping/detail`, { params: {shippingNo} });
  152 +export function getDetail(shippingNo?: string): http.PromiseResp<Detail> {
  153 + return request.get(`${PMS_HOST}/app/part/shipping/part/detail`, { params: {shippingNo} });
73 154 }
74 155  
75 156 /**
... ...
src/pages/pms/partPlan/PlanShipping/components/ConfirmDetailModal.tsx
1 1 import React, { useState, useEffect } from 'react';
2   -import { Button, message, Modal, Table } from 'antd';
3   -import { confirmApi, getDetail, Detail, Item } from "../api";
  2 +import { Button, message, Modal, Table, Descriptions } from 'antd';
  3 +import { confirmApi, getDetail, Detail, Item, DetailItem } from "../api";
4 4 import useInitail from "@/hooks/useInitail";
5 5 import _ from "lodash";
6 6  
... ... @@ -16,7 +16,7 @@ const { Column } = Table;
16 16 export default function Index(props: Props) {
17 17 const { visible, onCancel, fetchList, item, confirm } = props;
18 18 const [delay, setDelay] = useState(true);
19   - const { data, setParams, loading: aloading} = useInitail<Detail[], string | undefined>(getDetail, [], item.shippingNo, delay);
  19 + const { data, setParams, loading: aloading} = useInitail<Detail, string | undefined>(getDetail, {}, item.shippingNo, delay);
20 20 const [loading, setLoading] = useState(false);
21 21  
22 22 useEffect(() => {
... ... @@ -54,13 +54,19 @@ export default function Index(props: Props) {
54 54 <Button key="2" type="primary" loading={loading} disabled={loading} onClick={() => submit(true)}>确认</Button>
55 55 ] : [<Button key="1" loading={loading} onClick={() => onCancel()}>取消</Button>]}
56 56 >
57   - <Table loading={aloading} rowKey={(v: Detail) => `${v.partId}`} scroll={{y: 500, x: 1200}} dataSource={data || []} pagination={false}>
  57 + <Descriptions column={3}>
  58 + <Descriptions.Item label="品牌">{data.brandName}</Descriptions.Item>
  59 + <Descriptions.Item label="供应商" span={2}>{data.supplierName}</Descriptions.Item>
  60 + <Descriptions.Item label="库房">{data.storageName}</Descriptions.Item>
  61 + <Descriptions.Item label="提报门店" span={2}>{data.settleShopName}</Descriptions.Item>
  62 + </Descriptions>
  63 + <Table loading={aloading} rowKey={(v: DetailItem) => `${v.partId}`} scroll={{y: 500, x: 1200}} dataSource={data.list || []} pagination={false}>
58 64 <Column title="配件名称" dataIndex="partName" />
59 65 <Column title="配件编码" dataIndex="partCode" />
60 66 <Column title="配件件号" dataIndex="partNo" />
61 67 <Column title="发运数量" dataIndex="partCount" />
62 68 <Column title="采购单价" dataIndex="price" />
63   - <Column title="总金额(元)" dataIndex="totalAmount" render={(t: number, _:Detail) => ((_.price || 0) * (_.partCount || 0) || '--')} />
  69 + <Column title="总金额(元)" dataIndex="totalAmount" render={(t: number, _: DetailItem) => ((_.price || 0) * (_.partCount || 0) || '--')} />
64 70 {!confirm && <Column title="入库数量" dataIndex="storageCnt" render={(t: number) => (t || '--')} />}
65 71 {!confirm && <Column title="遗漏数量" dataIndex="omitCnt" render={(t: number) => (t || '--')} />}
66 72 {!confirm && <Column title="破损数量" dataIndex="damagedCnt" render={(t: number) => (t || '--')} />}
... ...
src/pages/pms/partPlan/PlanShipping/components/UploadExcel.tsx
... ... @@ -107,7 +107,7 @@ export default function UploadExcel({ getList, importVisible, setImportVisible,
107 107 onChange={supplierId => setParam({ ...param, supplierId })}
108 108 />
109 109 <SelectRow
110   - title="库房"
  110 + title="发运库房"
111 111 value={param.storageId}
112 112 data={storages}
113 113 id="id"
... ... @@ -115,7 +115,7 @@ export default function UploadExcel({ getList, importVisible, setImportVisible,
115 115 onChange={storageId => setParam({ ...param, storageId })}
116 116 />
117 117 <SelectRow
118   - title="结算门店"
  118 + title="提报门店"
119 119 value={param.settleShopId}
120 120 data={shops}
121 121 id="id"
... ...
src/pages/pms/partPlan/PlanShipping/index.tsx
1 1 import React, { useState } from "react";
2   -import { Card, ConfigProvider, DatePicker, Divider, Table } from 'antd';
  2 +import { Card, ConfigProvider, DatePicker, Divider, Table, Dropdown, Menu } from 'antd';
  3 +import { DownOutlined } from '@ant-design/icons';
3 4 import { PageHeaderWrapper } from '@ant-design/pro-layout';
4 5 import usePagination from "@/hooks/usePagination";
5 6 import DetailModal from './components/ConfirmDetailModal';
... ... @@ -23,6 +24,23 @@ export default function PartPriceCoefficient() {
23 24 const [item, setItem] = useState<Item>({});
24 25 const { data: shops } = useInitial<PmsStoragePartShop.Option[], {}>(api.getShopApi, [], {});
25 26  
  27 + const menu = (
  28 + <Menu
  29 + items={
  30 + [
  31 + {
  32 + label: <a href="/api/pms/erp/part/template/shipping">通用模板</a>,
  33 + key: '0',
  34 + },
  35 + {
  36 + label: <a href="/api/pms/erp/part/template/shipping/ca">长安模板</a>,
  37 + key: '1',
  38 + },
  39 + ]
  40 + }
  41 + />
  42 +);
  43 +
26 44 return (
27 45 <PageHeaderWrapper title="发运单">
28 46 <ConfigProvider locale={zhCN}>
... ... @@ -56,13 +74,10 @@ export default function PartPriceCoefficient() {
56 74 />
57 75 </div>
58 76 <div style={{ display: 'flex', flexDirection: 'row'}}>
59   - <a
60   - href={`/api/pms/erp/part/template/shipping?t=${moment().valueOf()}`}
61   - style={{ marginRight: 20 }}
62   - >
63   - 下载模板
64   - </a>
65   - <a onClick={() => { setImportVisible(true); }}>导入数据</a>
  77 + <Dropdown overlay={menu} trigger={['click']} placement="bottomLeft" arrow overlayStyle={{width: 120}}>
  78 + <a style={{display: 'flex', alignItems: 'center'}}>导入模板<DownOutlined style={{fontSize: 14, marginLeft: 3}} /></a>
  79 + </Dropdown>
  80 + <div style={{marginLeft: 20}}><a onClick={() => { setImportVisible(true); }}>导入数据</a></div>
66 81 </div>
67 82 </div>
68 83 <Table
... ...
src/pages/pms/storage/partShop/api.ts
... ... @@ -8,9 +8,17 @@ export function getPartShopPageApi(params: PmsStoragePartShop.QueryParams): http
8 8 }
9 9  
10 10 /** 编辑服务站配件 */
11   -export function savePartShopApi(params: PmsStoragePartShop.Params): http.PromiseResp<string> {
  11 +export function savePartShopeApi(params: PmsStoragePartShop.Params): http.PromiseResp<string> {
12 12 return request.post(`${PMS_HOST}/erp/part/shop/save`, { ...params });
13 13 }
  14 +/** 编辑服务站配件 */
  15 +export function savePartStockApi(params: PmsStoragePartShop.Params): http.PromiseResp<string> {
  16 + return request.post(`${PMS_HOST}/erp/part/shop/update/stock`, { ...params });
  17 +}
  18 +/** 编辑服务站配件价格 */
  19 +export function savePartPriceApi(params: PmsStoragePartShop.Params): http.PromiseResp<string> {
  20 + return request.post(`${PMS_HOST}/erp/part/shop/update/price`, { ...params });
  21 +}
14 22  
15 23 /** 获取服务站列表 */
16 24 export function getShopApi(): http.PromiseResp<PmsStoragePartShop.Option[]> {
... ...
src/pages/pms/storage/partShop/components/List.tsx
... ... @@ -9,7 +9,7 @@ import LockFlowModal from &#39;./LockFlowModal&#39;;
9 9 const { Column } = Table;
10 10  
11 11 export default function Filter() {
12   - const { setParams, loading, paginationConfig, list, setVisible, setItem, item, fw, setIsprice} = useStore();
  12 + const { setParams, loading, paginationConfig, list, setVisible, setItem, item, fw, setIsprice, fwS} = useStore();
13 13 const [visibleLockDetail, setVisibleLockDetail] = useState(false);
14 14 const [visibleFlowDetail, setVisibleFlowDetail] = useState(false);
15 15 const [visibleLockFlow, setVisibleLockFlow] = useState(false);
... ... @@ -59,19 +59,16 @@ export default function Filter() {
59 59 render={(text, _item: PartRepertorySpace.Item) => (
60 60 <div style={{display: 'flex', alignItems: 'center'}}>
61 61 <a onClick={() => { setVisible(true); setItem(_item); }}>修改</a>
62   - <Divider type="vertical" />
63   - <a onClick={() => { setVisible(true); setItem(_item); setIsprice(true); }}>修改价格</a>
  62 + {fwS ? null : (
  63 + <>
  64 + <Divider type="vertical" />
  65 + <a onClick={() => { setVisible(true); setItem(_item); setIsprice(true); }}>修改价格</a>
  66 + </>
  67 + )}
64 68 </div>
65 69 )}
66 70 />
67   - ) : (
68   - <Column
69   - title="操作"
70   - render={(text, _item: PartRepertorySpace.Item) => (
71   - <a onClick={() => { setVisible(true); setItem(_item); setIsprice(true); }}>修改价格</a>
72   - )}
73   - />
74   - )}
  71 + ) : null}
75 72 </Table>
76 73 <LockDetailModal item={item} visible={visibleLockDetail} onCancel={() => setVisibleLockDetail(false)} />
77 74 <FlowDetailModal item={item} visible={visibleFlowDetail} onCancel={() => setVisibleFlowDetail(false)} />
... ...
src/pages/pms/storage/partShop/components/PartShopModal.tsx
1 1 import React, { useEffect, useState } from 'react';
2   -import { Modal, Button, Form, Select, InputNumber, message, Input } from 'antd';
  2 +import { Modal, Button, Form, Select, InputNumber, message, Input, MessageArgsProps } from 'antd';
3 3 import { useStore } from '../index';
4 4 import ChoosePart from './ChoosePart';
5   -import { savePartShopApi } from '../api';
  5 +import { savePartStockApi, savePartPriceApi, savePartShopeApi } from '../api';
6 6  
7 7 const { Item } = Form;
8 8 const { Option } = Select;
9 9  
  10 +const apiObj: { [key: number]: any } = {
  11 + 1: savePartShopeApi,
  12 + 2: savePartStockApi,
  13 + 3: savePartPriceApi
  14 +};
10 15 export default function PartShopModal() {
11 16 const { visible, setVisible, shops, setLoading, item = {}, setItem, isprice, setIsprice, isadd, setIsadd, fw } = useStore();
12 17 const [form] = Form.useForm();
13 18 const [confirmLoading, setConfirmLoading] = useState(false);
  19 + const apiType = isadd ? 1 : isprice ? 3 : 2;
14 20  
15 21 useEffect(() => {
16 22 if (visible) {
... ... @@ -37,7 +43,7 @@ export default function PartShopModal() {
37 43 type: isadd ? 1 : isprice ? 3 : 2
38 44 };
39 45 setConfirmLoading(true);
40   - savePartShopApi(params).then(res => {
  46 + apiObj[apiType](params).then((res: { success: any; result: {} | null | undefined; }) => {
41 47 if (res.success) {
42 48 message.success(res.result);
43 49 setVisible(false);
... ... @@ -47,7 +53,7 @@ export default function PartShopModal() {
47 53 setConfirmLoading(false);
48 54 message.error(res.result);
49 55 }
50   - }).catch(error => {
  56 + }).catch((error: { message: boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | MessageArgsProps | null | undefined; }) => {
51 57 setConfirmLoading(false);
52 58 message.error(error.message);
53 59 });
... ...
src/pages/pms/storage/partShop/index.tsx
... ... @@ -13,14 +13,14 @@ import StorageFlowModal from &#39;./components/StorageFlowModal&#39;;
13 13 export const { Provider, useStore } = createStore(store);
14 14  
15 15 function PartShop() {
16   - const { setImportVisible, fw, setVisible, setIsadd } = useStore();
  16 + const { setImportVisible, fw, setVisible, setIsadd, fwS } = useStore();
17 17 const [more, setMore] = useState(false);
18 18 const [stock, setStock] = useState(false);
19 19 const [islock, setIslock] = useState(false);
20 20 const [storageFlowVisible, setStorageFlowVisible] = useState(false);
21 21  
22 22 return (
23   - <PageHeaderWrapper title={`服务站配件${fw ? '(霏微)': ''}`}>
  23 + <PageHeaderWrapper title={`服务站配件${fw ? `${fwS ? '(库存)' : '(霏微)'}`: ''}`}>
24 24 <Card>
25 25 <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 20 }}>
26 26 <Filter />
... ...
src/pages/pms/storage/partShop/store.ts
... ... @@ -29,7 +29,8 @@ export default function useStore() {
29 29 const [releaseVisible, setReleaseVisible] = useState(false);
30 30 const [override, setOverride] = useState(false);
31 31 const [item, setItem] = useState<PmsStoragePartShop.Item>({});
32   - const [fw] = useState(location.href.includes('fwPartShop'));
  32 + const [fw] = useState(location.href.includes('fw'));
  33 + const [fwS] = useState(location.href.includes('fwStock'));
33 34 const [isprice, setIsprice] = useState<boolean>(false);
34 35 const [isadd, setIsadd] = useState<boolean>(false);
35 36  
... ... @@ -55,6 +56,7 @@ export default function useStore() {
55 56 override,
56 57 setOverride,
57 58 fw,
  59 + fwS,
58 60 isprice,
59 61 setIsprice,
60 62 isadd,
... ...